Skip to content

Commit

Permalink
Merge pull request #14 from alexmohr/dev
Browse files Browse the repository at this point in the history
Misc Fixes and imrovements
  • Loading branch information
alexmohr authored Apr 2, 2019
2 parents f6b9654 + 3349000 commit 367490d
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 44 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ if __name__ == "__main__":
device.play()
```

More examples can be found in the examples folder.

# URL list

https://github.com/chr15m/media-remote/blob/master/SNIFF.md

https://gist.github.com/kalleth/e10e8f3b8b7cb1bac21463b0073a65fb

# Compatibility List

LCD TV BRAVIA
Expand Down
File renamed without changes.
File renamed without changes.
37 changes: 37 additions & 0 deletions examples/pair_and_apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from sonyapilib.device import SonyDevice

def save_device():
data = device.save_to_json()
text_file = open("bluray.json", "w")
text_file.write(data)
text_file.close()

if __name__ == "__main__":

stored_config = "bluray.json"
device = None
import os.path
if os.path.exists(stored_config):
with open(stored_config, 'r') as content_file:
json_data = content_file.read()
device = SonyDevice.load_from_json(json_data)
else:
# device must be on for registration
host = "10.0.0.102"
device = SonyDevice(host, "SonyApiLib Python Test")
device.register()
pin = input("Enter the PIN displayed at your device: ")
device.send_authentication(pin)
save_device()

# wake device
is_on = device.get_power_status()
if not is_on:
device.power(True)

apps = device.get_apps()

device.start_app(apps[0])

# Play media
device.play()
11 changes: 6 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,19 @@
# and be sure to test it firstly using "python setup.py register sdist upload -r pypitest"
setup(name='sonyapilib',
packages = ['sonyapilib'], # this must be the same as the name above
version = '0.3.10',
description = 'Lib to control sony devices with theier soap api',
version = '0.3.11',
description = 'Lib to control sony devices with their soap api',
author = 'Alexander Mohr',
author_email = '[email protected]',
url = 'https://github.com/alexmohr/sonyapilib', # use the URL to the github repo
download_url = 'https://codeload.github.com/alexmohr/sonyapilib/tar.gz/0.3.7',
download_url = 'https://codeload.github.com/alexmohr/sonyapilib/tar.gz/0.3.11',
keywords = ['soap', 'sony', 'api'], # arbitrary keywords
classifiers = [],
install_requires=[
'jsonpickle',
'setuptools',
'requests'
'requests',
'wakeonlan'
],

)
)
77 changes: 38 additions & 39 deletions sonyapilib/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
import requests
import urllib.parse
import xml.etree.ElementTree
import logging
import requests
from enum import Enum


import wakeonlan
import jsonpickle

from sonyapilib import ssdp
Expand All @@ -23,7 +22,7 @@
TIMEOUT = 5


class AuthenicationResult(Enum):
class AuthenticationResult(Enum):
SUCCESS = 0
ERROR = 1
PIN_NEEDED = 2
Expand Down Expand Up @@ -54,7 +53,7 @@ def __init__(self, xml_data):
if (arg == "mode"):
setattr(self, arg, int(xml_data[arg]))
else:
setattr(self, arg, xml_data[arg])
setattr(self, arg, xml_data[arg])

class SonyDevice():
"""
Expand Down Expand Up @@ -95,7 +94,7 @@ def __init__(self, host, nickname, port=50001, dmr_port=52323, app_port=50202, i

if len(self.actions) == 0 and self.pin is not None:
self.update_service_urls()

@staticmethod
def discover():
"""
Expand All @@ -115,7 +114,7 @@ def load_from_json(data):
return jsonpickle.decode(data)

def save_to_json(self):

return jsonpickle.dumps(self)

def create_json_v4(self, method, params=None):
Expand All @@ -130,19 +129,7 @@ def create_json_v4(self, method, params=None):

def wakeonlan(self):
if self.mac is not None:
addr_byte = self.mac.split('-')
hw_addr = struct.pack('BBBBBB', int(addr_byte[0], 16),
int(addr_byte[1], 16),
int(addr_byte[2], 16),
int(addr_byte[3], 16),
int(addr_byte[4], 16),
int(addr_byte[5], 16))
msg = b'\xff' * 6 + hw_addr * 16
socket_instance = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
socket_instance.setsockopt(
socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
socket_instance.sendto(msg, ('<broadcast>', 9))
socket_instance.close()
wakeonlan.send_magic_packet(self.mac, ip_address=self.host)

def update_service_urls(self):
""" Initalizes the device by reading the necessary resources from it """
Expand Down Expand Up @@ -175,7 +162,7 @@ def update_service_urls(self):
for element in xml_data.findall("action"):
action = XmlApiObject(element.attrib)
self.actions[action.name] = action

# some data has to overwritten for the registration to work properly
if action.name == "register":
if action.mode < 4:
Expand All @@ -191,10 +178,10 @@ def update_service_urls(self):
# if self.actions["register"].mode == 4:
# self.actions["getRemoteCommandList"].url = "http://{0}/sony/system".format(
# lirc_url.netloc.split(":")[0])

# make sure we are authenticated before
self.recreate_authentication()

if services is not None:
# read service list
for service in services:
Expand Down Expand Up @@ -243,8 +230,8 @@ def update_service_urls(self):
self.update_applist()

def update_commands(self):
# needs to be registred to do that

# needs to be registred to do that
if self.pin is None:
return

Expand Down Expand Up @@ -297,16 +284,16 @@ def recreate_authentication(self):
cookies = None
#cookies = requests.cookies.RequestsCookieJar()
#cookies.set("auth", self.cookies.get("auth"))

username = ''
base64string = base64.encodebytes(('%s:%s' % (username, self.pin)).encode()) \
.decode().replace('\n', '')

registration_action = self.get_action("register")

self.headers['Authorization'] = "Basic %s" % base64string
if registration_action.mode == 3:
self.headers['X-CERS-DEVICE-ID'] = self.nickname
self.headers['X-CERS-DEVICE-ID'] = self.get_device_id()
elif registration_action.mode == 4:
self.headers['Connection'] = "keep-alive"

Expand All @@ -315,18 +302,21 @@ def recreate_authentication(self):
def register(self):
"""
Register at the api.50001
:param str name: The name which will be displayed in the UI of the device. Make sure this name does not exist yet
Register at the api. The name which will be displayed in the UI of the device. Make sure this name does not exist yet
For this the device must be put in registration mode.
The tested sd5500 has no separte mode but allows registration in the overview "
"""
registrataion_result = AuthenicationResult.ERROR
registration_result = AuthenticationResult.ERROR
registration_action = registration_action = self.get_action("register")

# protocoll version 1 and 2
if registration_action.mode < 3:
self.send_http(
registration_response = self.send_http(
registration_action.url, method=HttpMethod.GET, raise_errors=True)
registrataion_result = AuthenicationResult.SUCCESS
if registration_response.text == "":
registration_result = AuthenticationResult.SUCCESS
else:
registration_result = AuthenticationResult.ERROR

# protocoll version 3
elif registration_action.mode == 3:
Expand All @@ -335,7 +325,7 @@ def register(self):
method=HttpMethod.GET, raise_errors=True)
except requests.exceptions.HTTPError as ex:
_LOGGER.error("[W] HTTPError: " + str(ex))
registrataion_result = AuthenicationResult.PIN_NEEDED
registration_result = AuthenticationResult.PIN_NEEDED

# newest protocoll version 4 this is the same method as braviarc uses
elif registration_action.mode == 4:
Expand All @@ -356,7 +346,7 @@ def register(self):
data=authorization, raise_errors=True)
except requests.exceptions.HTTPError as ex:
_LOGGER.error("[W] HTTPError: " + str(ex))
registrataion_result = AuthenicationResult.PIN_NEEDED
registration_result = AuthenticationResult.PIN_NEEDED

except Exception as ex: # pylint: disable=broad-except
_LOGGER.error("[W] Exception: " + str(ex))
Expand All @@ -365,16 +355,16 @@ def register(self):
_LOGGER.debug(json.dumps(resp, indent=4))
if resp is None or not resp.get('error'):
self.cookies = response.cookies
registrataion_result = AuthenicationResult.SUCCESS
registration_result = AuthenticationResult.SUCCESS

else:
raise ValueError(
"Regisration mode {0} is not supported".format(registration_action.mode))

return registrataion_result
return registration_result

def send_authentication(self, pin):

registration_action = self.get_action("register")

# they do not need a pin
Expand Down Expand Up @@ -503,6 +493,9 @@ def send_req_ircc(self, params, log_errors=True):
url=self.control_url, params=data, action=action)
return content

def get_device_id(self):
return "TVSideView:{0}".format(self.mac)

def get_playing_status(self):
data = '<m:GetTransportInfo xmlns:m="urn:schemas-upnp-org:service:AVTransport:1">' + \
'<InstanceID>0</InstanceID>' + \
Expand Down Expand Up @@ -544,10 +537,16 @@ def send_command(self, name):
if len(self.commands) == 0:
self.update_commands()

self.send_req_ircc(self.commands[name].value)
if len(self.commands) > 0:
if name in self.commands:
self.send_req_ircc(self.commands[name].value)
else:
raise ValueError('Unknown command: %s', name)
else:
raise ValueError('Failed to read command list from device.')

def get_action(self, name):
if not name in self.actions and len(self.actions) == 0:
if not name in self.actions and len(self.actions) == 0:
self.update_service_urls()
if not name in self.actions and len(self.actions) == 0:
raise ValueError('Failed to read action list from device.')
Expand All @@ -563,7 +562,7 @@ def power(self, on):
self.send_command('Power')

# Try using the power on command incase the WOL doesn't work


def get_apps(self):
return list(self.apps.keys())
Expand Down

0 comments on commit 367490d

Please sign in to comment.