Skip to content

Commit

Permalink
Add new commands 'stream' and 'wifi' to configure camera settings. (#53)
Browse files Browse the repository at this point in the history
New commands 'stream' and 'wifi' allow control of the Streaming and WiFi functions of
the attached camera. These settings can also be written to and read from files.

For Streaming settings, the official tools should be used to allocate the appropriate
crypto-tokens, the other values can be changed at will. Ustream documentation explains
how to obtain the OAuth2 tokens, the others are available from user's profile.
  • Loading branch information
mungewell authored and ma1co committed Nov 28, 2017
1 parent 5f35301 commit 60f8ea1
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 0 deletions.
13 changes: 13 additions & 0 deletions pmca-console.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ def main():
gps = subparsers.add_parser('gps', description='Update GPS assist data')
gps.add_argument('-d', dest='driver', choices=['libusb', 'native'], help='specify the driver')
gps.add_argument('-f', dest='file', type=argparse.FileType('rb'), help='assistme.dat file')
stream = subparsers.add_parser('stream', description='Update Streaming configuration')
stream.add_argument('-d', dest='driver', choices=['libusb', 'native'], help='specify the driver')
stream.add_argument('-f', dest='file', type=argparse.FileType('w'), help='store current settings to file')
stream.add_argument('-w', dest='write', type=argparse.FileType('r'), help='program camera settings from file')
wifi = subparsers.add_parser('wifi', description='Update WiFi configuration')
wifi.add_argument('-d', dest='driver', choices=['libusb', 'native'], help='specify the driver')
wifi.add_argument('-m', dest='multi', action='store_true', help='Read/Write "Multi-WiFi" settings')
wifi.add_argument('-f', dest='file', type=argparse.FileType('w'), help='store current settings to file')
wifi.add_argument('-w', dest='write', type=argparse.FileType('r'), help='program camera settings from file')

args = parser.parse_args()
if args.command == 'info':
Expand All @@ -70,6 +79,10 @@ def main():
updaterShellCommand(args.model, args.fdatFile, args.driver)
elif args.command == 'gps':
gpsUpdateCommand(args.file, args.driver)
elif args.command == 'stream':
streamingCommand(args.write, args.file, args.driver)
elif args.command == 'wifi':
wifiCommand(args.write, args.file, args.multi, args.driver)
else:
parser.print_usage()

Expand Down
181 changes: 181 additions & 0 deletions pmca/commands/usb.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import sys
import time
import struct

if sys.version_info < (3,):
# Python 2
Expand Down Expand Up @@ -392,3 +393,183 @@ def gpsUpdateCommand(file=None, driverName=None):
print('Writing GPS data')
SonyExtCmdCamera(device).writeGpsData(file)
print('Done')


def streamingCommand(write=None, file=None, driverName=None):
"""Read/Write Streaming information for the camera connected via usb"""
with importDriver(driverName) as driver:
device = getDevice(driver)
if device:
if isinstance(device, SonyMtpAppInstaller):
print('Error: Cannot configure camera in app install mode. Please restart the device.')
else:
dev = SonyExtCmdCamera(device)

if write:
incoming = json.load(write)

# assemble Social (first 9 items in file)
mydict = {}
for key in incoming[:9]:
if key[0] in ['twitterEnabled', 'facebookEnabled']:
mydict[key[0]] = key[1] # Integer
else:
mydict[key[0]] = key[1].encode('ascii')

data = SonyExtCmdCamera.LiveStreamingSNSInfo.pack(
twitterEnabled = mydict['twitterEnabled'],
twitterConsumerKey = mydict['twitterConsumerKey'].ljust(1025, b'\x00'),
twitterConsumerSecret = mydict['twitterConsumerSecret'].ljust(1025, b'\x00'),
twitterAccessToken1 = mydict['twitterAccessToken1'].ljust(1025, b'\x00'),
twitterAccessTokenSecret = mydict['twitterAccessTokenSecret'].ljust(1025, b'\x00'),
twitterMessage = mydict['twitterMessage'].ljust(401, b'\x00'),
facebookEnabled = mydict['facebookEnabled'],
facebookAccessToken = mydict['facebookAccessToken'].ljust(1025, b'\x00'),
facebookMessage = mydict['facebookMessage'].ljust(401, b'\x00'),
)
dev.setLiveStreamingSocialInfo(data)

# assemble Streaming, file may contain multiple sets (of 14 items)
data = b'\x01\x00\x00\x00'
data += struct.pack('<i', int((len(incoming)-9)/14))
mydict = {}
count = 1
for key in incoming[9:]:
if key[0] in ['service', 'enabled', 'videoFormat', 'videoFormat', 'unknown', \
'enableRecordMode', 'channels', 'supportedFormats']:
mydict[key[0]] = key[1]
elif key[0] == 'macIssueTime':
mydict[key[0]] = binascii.a2b_hex(key[1])
else:
mydict[key[0]] = key[1].encode('ascii')

if count == 14:
# reassemble Structs
data += SonyExtCmdCamera.LiveStreamingServiceInfo1.pack(
service = mydict['service'],
enabled = mydict['enabled'],
macId = mydict['macId'].ljust(41, b'\x00'),
macSecret = mydict['macSecret'].ljust(41, b'\x00'),
macIssueTime = mydict['macIssueTime'],
unknown = 0, # mydict['unknown'],
)

data += struct.pack('<i', len(mydict['channels']))
for j in range(len(mydict['channels'])):
data += struct.pack('<i', mydict['channels'][j])

data += SonyExtCmdCamera.LiveStreamingServiceInfo2.pack(
shortURL = mydict['shortURL'].ljust(101, b'\x00'),
videoFormat = mydict['videoFormat'],
)

data += struct.pack('<i', len(mydict['supportedFormats']))
for j in range(len(mydict['supportedFormats'])):
data += struct.pack('<i', mydict['supportedFormats'][j])

data += SonyExtCmdCamera.LiveStreamingServiceInfo3.pack(
enableRecordMode = mydict['enableRecordMode'],
videoTitle = mydict['videoTitle'].ljust(401, b'\x00'),
videoDescription = mydict['videoDescription'].ljust(401, b'\x00'),
videoTag = mydict['videoTag'].ljust(401, b'\x00'),
)
count = 1
else:
count += 1

dev.setLiveStreamingServiceInfo(data)
return

# Read settings from camera (do this first so we know channels/supportedFormats)
settings = dev.getLiveStreamingServiceInfo()
social = dev.getLiveStreamingSocialInfo()

data = []
# Social settings
for key in (social._asdict()).items():
if key[0] in ['twitterEnabled', 'facebookEnabled']:
data.append([key[0], key[1]])
else:
data.append([key[0], key[1].decode('ascii').split('\x00')[0]])

# Streaming settings, file may contain muliple sets of data
try:
for key in next(settings).items():
if key[0] in ['service', 'enabled', 'videoFormat', 'enableRecordMode', \
'unknown', 'channels', 'supportedFormats']:
data.append([key[0], key[1]])
elif key[0] == 'macIssueTime':
data.append([key[0], binascii.b2a_hex(key[1]).decode('ascii')])
else:
data.append([key[0], key[1].decode('ascii').split('\x00')[0]])
except StopIteration:
pass

if file:
file.write(json.dumps(data, indent=4))
else:
for k, v in data:
print('%-20s%s' % (k + ': ', v))


def wifiCommand(write=None, file=None, multi=False, driverName=None):
"""Read/Write WiFi information for the camera connected via usb"""
with importDriver(driverName) as driver:
device = getDevice(driver)
if device:
if isinstance(device, SonyMtpAppInstaller):
print('Error: Cannot configure camera in app install mode. Please restart the device.')
else:
dev = SonyExtCmdCamera(device)

if write:
incoming = json.load(write)
data = struct.pack('<i', int(len(incoming)/3))

mydict = {}
count = 1
for key in incoming:
if key[0] == 'keyType':
mydict[key[0]] = key[1] # Integer
else:
mydict[key[0]] = key[1].encode('ascii')

if count == 3:
# reassemble Struct
apinfo = SonyExtCmdCamera.APInfo.pack(
keyType = mydict['keyType'],
sid = mydict['sid'].ljust(33, b'\x00'),
key = mydict['key'].ljust(65, b'\x00'),
)
data += apinfo
count = 1
else:
count += 1

if multi:
dev.setMultiWifiAPInfo(data)
else:
dev.setWifiAPInfo(data)
return

# Read settings from camera
if multi:
settings = dev.getMultiWifiAPInfo()
else:
settings = dev.getWifiAPInfo()

data = []
try:
for key in next(settings)._asdict().items():
if key[0] == 'keyType':
data.append([key[0], key[1]]) # Integer
else:
data.append([key[0],key[1].decode('ascii').split('\x00')[0]])
except StopIteration:
pass

if file:
file.write(json.dumps(data, indent=4))
else:
for k, v in data:
print('%-20s%s' % (k + ': ', v))
16 changes: 16 additions & 0 deletions pmca/usb/sony.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,18 @@ def getLiveStreamingServiceInfo(self):
info3._asdict(),
] for e in d.items())

def setLiveStreamingServiceInfo(self, data):
"""Sets the live streaming ustream configuration"""
return self._sendCommand(self.SONY_CMD_NetworkServiceInfo_SetLiveStreamingServiceInfo, data)

def getLiveStreamingSocialInfo(self):
"""Returns the live streaming social media configuration"""
return self.LiveStreamingSNSInfo.unpack(self._sendCommand(self.SONY_CMD_NetworkServiceInfo_GetLiveStreamingSNSInfo))

def setLiveStreamingSocialInfo(self, data):
"""Sets the live streaming social media configuration"""
return self._sendCommand(self.SONY_CMD_NetworkServiceInfo_SetLiveStreamingSNSInfo, data)

def _parseAPs(self, data):
for i in range(parse32le(data.read(4))):
yield self.APInfo.unpack(data.read(self.APInfo.size))
Expand All @@ -350,11 +358,19 @@ def getWifiAPInfo(self):
for ap in self._parseAPs(BytesIO(self._sendCommand(self.SONY_CMD_NetworkServiceInfo_GetWifiAPInfo))):
yield ap

def setWifiAPInfo(self, data):
"""Sets the live streaming access point configuration"""
return self._sendCommand(self.SONY_CMD_NetworkServiceInfo_SetWifiAPInfo, data)

def getMultiWifiAPInfo(self):
"""Returns the live streaming multi access point configuration"""
for ap in self._parseAPs(BytesIO(self._sendCommand(self.SONY_CMD_NetworkServiceInfo_GetMultiWifiAPInfo))):
yield ap

def setMultiWifiAPInfo(self, data):
"""Sets the live streaming multi access point configuration"""
return self._sendCommand(self.SONY_CMD_NetworkServiceInfo_SetMultiWifiAPInfo, data)


class SonyUpdaterSequenceError(Exception):
def __init__(self):
Expand Down

0 comments on commit 60f8ea1

Please sign in to comment.