Add files via upload

This commit is contained in:
LukeSpad 2021-12-08 13:03:50 +00:00 committed by GitHub
parent 7d7fcc9f5e
commit 4688f11252
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 366 additions and 112 deletions

111
Resources/ASBridge.py Normal file
View file

@ -0,0 +1,111 @@
#!/usr/bin/python
import os
from Foundation import NSAppleScript
from ScriptingBridge import SBApplication
''' Module defining a MusicController class for Apple Music, enables:
basic transport control of the player,
query of player status,
playing existing playlists, and
running external appleScripts for more complex control
reporting messages in the notification centre'''
PLAYSTATE = dict([
(1800426320, "Play"),
(1800426352, "Pause"),
(1800426323, "Stop"),
(1800426310, "Wind"),
(1800426322, "Rewind")
])
class MusicController(object):
def __init__(self):
self.app = SBApplication.applicationWithBundleIdentifier_("com.apple.Music")
# Player information
def get_current_track_info(self):
name = str(self.app.currentTrack().name())
album = str(self.app.currentTrack().album())
artist = str(self.app.currentTrack().artist())
return [name, album, artist]
def get_current_play_state(self):
return PLAYSTATE.get(self.app.playerState())
def get_current_track_position(self):
return self.app.playerPosition()
# ########################################################################################
# Transport Controls
def playpause(self):
self.app.playpause()
def play(self):
if PLAYSTATE.get(self.app.playerState()) in ['Wind', 'Rewind']:
self.app.resume()
elif PLAYSTATE.get(self.app.playerState()) == 'Pause':
self.app.playpause()
elif PLAYSTATE.get(self.app.playerState()) == 'Stop':
playlist = self.app.sources().objectWithName_("Library")
playlist.playOnce_(None)
def pause(self):
if PLAYSTATE.get(self.app.playerState()) == 'Play':
self.app.pause()
def stop(self):
if PLAYSTATE.get(self.app.playerState()) != 'Stop':
self.app.stop()
def next_track(self):
self.app.nextTrack()
def previous_track(self):
self.app.previousTrack()
def wind(self, time):
# Native wind function can be a bit annoying
# I provide an alternative below that skips a set number of seconds forwards
# self.app.wind()
self.set_current_track_position(time)
def rewind(self, time):
# Native rewind function can be a bit annoying
# I provide an alternative below that skips a set number of seconds back
# self.app.rewind()
self.set_current_track_position(time)
# ########################################################################################
# More complex playback control functions
def set_current_track_position(self, time, mode='Relative'):
if mode == 'Relative':
# Set playback position in seconds relative to current position
self.app.setPlayerPosition_(self.app.playerPosition() + time)
elif mode == 'Absolute':
# Set playback position in seconds from the start of the track
self.app.setPlayerPosition_(time)
def play_playlist(self, playlist):
self.app.stop()
playlist = self.app.sources().objectWithName_("Library").playlists().objectWithName_(playlist)
playlist.playOnce_(None)
# ########################################################################################
# Accessory functions
@staticmethod
def run_script(script):
# Run an external applescript file
script = 'run script ("' + script + '" as POSIX file)'
s = NSAppleScript.alloc().initWithSource_(script)
s.executeAndReturnError_(None)
@staticmethod
def notify(message):
# Post message in notification center
path = os.path.dirname(os.path.abspath(__file__))
script = 'tell application "' + path + '/Notify.app" to notify("BeoGateway", "' + \
message + '")'
s = NSAppleScript.alloc().initWithSource_(script)
s.executeAndReturnError_(None)

View file

@ -173,7 +173,7 @@ class BLHIPClient(asynchat.async_chat):
self.log.info("\tAttempting to Authenticate...") self.log.info("\tAttempting to Authenticate...")
self.send_cmd(self._user) self.send_cmd(self._user)
self.send_cmd(self._pwd) self.send_cmd(self._pwd)
self.statefiler() self.statefilter()
def handle_close(self): def handle_close(self):
self.log.info(self.name + ": Closing socket") self.log.info(self.name + ": Closing socket")
@ -219,7 +219,7 @@ class BLHIPClient(asynchat.async_chat):
self.log.info(self.name + ": sending state update request for" + device + dev_type + room + zone) self.log.info(self.name + ": sending state update request for" + device + dev_type + room + zone)
self.send_cmd(query) self.send_cmd(query)
def statefiler(self, zone='*', room='*', dev_type='*', device='*'): def statefilter(self, zone='*', room='*', dev_type='*', device='*'):
s_filter = "f " + zone + "/" + room + "/" + dev_type + '/' + device s_filter = "f " + zone + "/" + room + "/" + dev_type + '/' + device
self.send_cmd(s_filter) self.send_cmd(s_filter)
@ -241,12 +241,16 @@ class BLHIPClient(asynchat.async_chat):
@staticmethod @staticmethod
def _get_channel_track(message): def _get_channel_track(message):
# Check device list for channel name information try:
if CONST.devices: # Check device list for channel name information
for device in CONST.devices: if CONST.devices:
if device['Device'] == message['Device']: for device in CONST.devices:
if 'channels' in device['Sources'][message["State_Update"]["source"]]: if device['Device'] == message['Device']:
for channel in device['Sources'][message["State_Update"]["source"]]['channels']: if 'channels' in device['Sources'][message["State_Update"]["source"]]:
if channel['number'] == int(message["State_Update"]['nowPlayingDetails']["channel_track"]): for channel in device['Sources'][message["State_Update"]["source"]]['channels']:
message["State_Update"]["nowPlaying"] = channel['name'] if channel['number'] == int(
break message["State_Update"]['nowPlayingDetails']["channel_track"]):
message["State_Update"]["nowPlaying"] = channel['name']
break
except KeyError:
pass

View file

@ -1,11 +1,31 @@
# Constants for B&O telegram protocols # Constants for B&O telegram protocols
# ######################################################################################## # ########################################################################################
# Config data (set on initialisation) # Config data (set on initialisation)
gateway = dict() gateway = dict([("Current Audio Source", ''), ("Current Video Source", '')])
rooms = [] rooms = []
devices = [] devices = []
available_sources = [] available_sources = []
# ########################################################################################
# Source Types
source_type_dict = dict(
[
("Video Sources", ("TV", "V.AUX/DTV2", "MEDIA", "V.TAPE/V.MEM/DVD2", "DVD", "CAMERA",
"SAT/DTV", "PC", "WEB", "DOORCAM", "PHOTO", "USB2", "WEBMEDIA", "AV.IN",
"HOMEMEDIA", "DNLA", "RECORDINGS", "CAMERA", "USB", "DNLA-DMR", "YOUTUBE",
"HOME.APP", "HDMI_1", "HDMI_2", "HDMI_3", "HDMI_4", "HDMI_5", "HDMI_6",
"HDMI_7", "HDMI_8", "MATRIX_1", "MATRIX_2", "MATRIX_3", "MATRIX_4", "MATRIX_5",
"MATRIX_6", "MATRIX_7", "MATRIX_8", "MATRIX_9", "MATRIX_10", "MATRIX_11",
"MATRIX_12", "MATRIX_13", "MATRIX_14", "MATRIX_15", "MATRIX_16", "PERSONAL_1",
"PERSONAL_2", "PERSONAL_3", "PERSONAL_4", "PERSONAL_5", "PERSONAL_6", "PERSONAL_7",
"PERSONAL_8")),
("Audio Sources", ("RADIO", "A.AUX", "A.TAPE/A.MEM", "CD", "PHONO/N.RADIO", "A.TAPE2/N.MUSIC",
"SERVER", "SPOTIFY", "CD2/JOIN", "TUNEIN", "DVB_RADIO", "LINE.IN", "BLUETOOTH",
"MUSIC", "AIRPLAY", "SPOTIFY", "DEEZER", "QPLAY"))
]
)
# ######################################################################################## # ########################################################################################
# Beo4 Commands # Beo4 Commands
beo4_commanddict = dict( beo4_commanddict = dict(
@ -18,7 +38,7 @@ beo4_commanddict = dict(
(0x82, "V.Aux/DTV2"), (0x82, "V.Aux/DTV2"),
(0x83, "A.Aux"), (0x83, "A.Aux"),
(0x84, "Media"), (0x84, "Media"),
(0x85, "V.Tape/V.Mem/DVD2"), (0x85, "V.Tape/V.Mem"),
(0x86, "DVD"), (0x86, "DVD"),
(0x87, "Camera"), (0x87, "Camera"),
(0x88, "Text"), (0x88, "Text"),
@ -350,9 +370,9 @@ ml_selectedsourcedict = dict(
(0x0B, "TV"), (0x0B, "TV"),
(0x15, "V.TAPE/V.MEM"), (0x15, "V.TAPE/V.MEM"),
(0x16, "DVD2"), (0x16, "DVD2"),
(0x1F, "DTV"), (0x1F, "SAT/DTV"),
(0x29, "DVD"), (0x29, "DVD"),
(0x33, "V.AUX"), (0x33, "V.AUX/DTV2"),
(0x3E, "DOORCAM"), (0x3E, "DOORCAM"),
(0x47, "PC"), (0x47, "PC"),
(0x6F, "RADIO"), (0x6F, "RADIO"),
@ -369,6 +389,8 @@ ml_selectedsourcedict = dict(
ml_trackinfo_subtype_dict = dict([(0x05, "Current Source"), (0x07, "Change Source"), ]) ml_trackinfo_subtype_dict = dict([(0x05, "Current Source"), (0x07, "Change Source"), ])
ml_sourcekind_dict = dict([(0x01, "audio source"), (0x02, "video source"), (0xFF, "undefined")])
ml_selectedsource_type_dict = dict( ml_selectedsource_type_dict = dict(
[ [
("VIDEO", (0x0B, 0x1F)), ("VIDEO", (0x0B, 0x1F)),

View file

@ -84,7 +84,7 @@ class MLCLIClient(asynchat.async_chat):
for item in items: for item in items:
try: try:
telegram.append(int(item[:-1], base=16)) telegram.append(int(item[:-1], base=16))
except TypeError: except (ValueError, TypeError):
# abort if invalid character found # abort if invalid character found
self.log.debug('Invalid character ' + str(item) + ' found in telegram: ' + self.log.debug('Invalid character ' + str(item) + ' found in telegram: ' +
''.join(items) + '\nAborting!') ''.join(items) + '\nAborting!')
@ -192,37 +192,35 @@ class MLCLIClient(asynchat.async_chat):
def _decode(self, telegram): def _decode(self, telegram):
# Decode header # Decode header
message = OrderedDict() message = OrderedDict()
if CONST.devices: self._get_device_info(message, telegram)
for device in CONST.devices: message["from_device"] = self._get_device_name(self._dictsanitize(CONST.ml_device_dict, telegram[1]))
if device['ML_ID'] == telegram[1]:
message["Zone"] = device["Zone"].upper()
message["Room"] = device["Room"].uppr()
message["Type"] = "AV RENDERER"
message["Device"] = device["Device"]
break
message["from_device"] = self._dictsanitize(CONST.ml_device_dict, telegram[1])
message["from_source"] = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[5]) message["from_source"] = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[5])
message["to_device"] = self._dictsanitize(CONST.ml_device_dict, telegram[0]) message["to_device"] = self._get_device_name(self._dictsanitize(CONST.ml_device_dict, telegram[0]))
message["to_source"] = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[4]) message["to_source"] = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[4])
message["type"] = self._dictsanitize(CONST.ml_telegram_type_dict, telegram[3]) message["type"] = self._dictsanitize(CONST.ml_telegram_type_dict, telegram[3])
message["payload_type"] = self._dictsanitize(CONST.ml_command_type_dict, telegram[7]) message["payload_type"] = self._dictsanitize(CONST.ml_command_type_dict, telegram[7])
message["payload_len"] = telegram[8] + 1 message["payload_len"] = telegram[8] + 1
message["State_Update"] = OrderedDict() message["State_Update"] = OrderedDict()
# RELEASE command signifies product standby
if message.get("payload_type") in ["RELEASE", "STANDBY"]:
message["State_Update"]["state"] = 'Standby'
# source status info # source status info
# TTFF__TYDSOS__PTLLPS SR____LS______SLSHTR__ACSTPI________________________TRTR______ # TTFF__TYDSOS__PTLLPS SR____LS______SLSHTR__ACSTPI________________________TRTR______
if message.get("payload_type") == "STATUS_INFO": if message.get("payload_type") == "STATUS_INFO":
message["State_Update"]["nowPlaying"] = '' message["State_Update"]["nowPlaying"] = 'Unknown'
message["State_Update"]["nowPlayingDetails"] = OrderedDict() message["State_Update"]["nowPlayingDetails"] = OrderedDict()
message["State_Update"]["nowPlayingDetails"]["local_source"] = telegram[13] message["State_Update"]["nowPlayingDetails"]["local_source"] = telegram[13]
message["State_Update"]["nowPlayingDetails"]["type"] = telegram[22] message["State_Update"]["nowPlayingDetails"]["type"] = \
self._dictsanitize(CONST.ml_sourcekind_dict, telegram[22])
if telegram[8] < 27: if telegram[8] < 27:
message["State_Update"]["nowPlayingDetails"]["channel_track"] = telegram[19] message["State_Update"]["nowPlayingDetails"]["channel_track"] = telegram[19]
else: else:
message["State_Update"]["nowPlayingDetails"]["channel_track"] = telegram[36] * 256 + telegram[37] message["State_Update"]["nowPlayingDetails"]["channel_track"] = telegram[36] * 256 + telegram[37]
message["State_Update"]["nowPlayingDetails"]["source_medium_position"] = \ message["State_Update"]["nowPlayingDetails"]["source_medium_position"] = \
self._hexword(telegram[18], telegram[17]) self._hexword(telegram[18], telegram[17])
message["State_Update"]["nowPlayingDetails"]["picture_identifier"] = \ message["State_Update"]["nowPlayingDetails"]["picture_format"] = \
self._dictsanitize(CONST.ml_pictureformatdict, telegram[23]) self._dictsanitize(CONST.ml_pictureformatdict, telegram[23])
source = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[10]) source = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[10])
self._get_source_name(source, message) self._get_source_name(source, message)
@ -257,7 +255,7 @@ class MLCLIClient(asynchat.async_chat):
# audio track info long # audio track info long
if message.get("payload_type") == "TRACK_INFO_LONG": if message.get("payload_type") == "TRACK_INFO_LONG":
message["State_Update"]["nowPlaying"] = '' message["State_Update"]["nowPlaying"] = 'Unknown'
message["State_Update"]["nowPlayingDetails"] = OrderedDict() message["State_Update"]["nowPlayingDetails"] = OrderedDict()
message["State_Update"]["nowPlayingDetails"]["type"] = \ message["State_Update"]["nowPlayingDetails"]["type"] = \
self._get_type(CONST.ml_selectedsource_type_dict, telegram[11]) self._get_type(CONST.ml_selectedsource_type_dict, telegram[11])
@ -271,7 +269,7 @@ class MLCLIClient(asynchat.async_chat):
# video track info # video track info
if message.get("payload_type") == "VIDEO_TRACK_INFO": if message.get("payload_type") == "VIDEO_TRACK_INFO":
message["State_Update"]["nowPlaying"] = '' message["State_Update"]["nowPlaying"] = 'Unknown'
message["State_Update"]["nowPlayingDetails"] = OrderedDict() message["State_Update"]["nowPlayingDetails"] = OrderedDict()
message["State_Update"]["nowPlayingDetails"]["source_type"] = \ message["State_Update"]["nowPlayingDetails"]["source_type"] = \
self._get_type(CONST.ml_selectedsource_type_dict, telegram[13]) self._get_type(CONST.ml_selectedsource_type_dict, telegram[13])
@ -286,6 +284,8 @@ class MLCLIClient(asynchat.async_chat):
# track change info # track change info
if message.get("payload_type") == "TRACK_INFO": if message.get("payload_type") == "TRACK_INFO":
message["State_Update"]["subtype"] = self._dictsanitize(CONST.ml_trackinfo_subtype_dict, telegram[9]) message["State_Update"]["subtype"] = self._dictsanitize(CONST.ml_trackinfo_subtype_dict, telegram[9])
# Change source
if message["State_Update"].get("subtype") == "Change Source": if message["State_Update"].get("subtype") == "Change Source":
message["State_Update"]["prev_source"] = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[11]) message["State_Update"]["prev_source"] = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[11])
message["State_Update"]["prev_sourceID"] = telegram[11] message["State_Update"]["prev_sourceID"] = telegram[11]
@ -297,6 +297,7 @@ class MLCLIClient(asynchat.async_chat):
message["State_Update"]["source"] = source message["State_Update"]["source"] = source
message["State_Update"]["sourceID"] = telegram[22] message["State_Update"]["sourceID"] = telegram[22]
# Current Source
if message["State_Update"].get("subtype") == "Current Source": if message["State_Update"].get("subtype") == "Current Source":
source = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[11]) source = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[11])
self._get_source_name(source, message) self._get_source_name(source, message)
@ -308,7 +309,7 @@ class MLCLIClient(asynchat.async_chat):
# goto source # goto source
if message.get("payload_type") == "GOTO_SOURCE": if message.get("payload_type") == "GOTO_SOURCE":
message["State_Update"]["nowPlaying"] = '' message["State_Update"]["nowPlaying"] = 'Unknown'
message["State_Update"]["nowPlayingDetails"] = OrderedDict() message["State_Update"]["nowPlayingDetails"] = OrderedDict()
message["State_Update"]["nowPlayingDetails"]["source_type"] = \ message["State_Update"]["nowPlayingDetails"]["source_type"] = \
self._get_type(CONST.ml_selectedsource_type_dict, telegram[11]) self._get_type(CONST.ml_selectedsource_type_dict, telegram[11])
@ -374,6 +375,27 @@ class MLCLIClient(asynchat.async_chat):
message["State_Update"]["nowPlaying"] = channel['name'] message["State_Update"]["nowPlaying"] = channel['name']
return return
def _get_device_info(self, message, telegram):
if CONST.devices:
for device in CONST.devices:
if device['ML_ID'] == self._dictsanitize(CONST.ml_device_dict, telegram[1]):
try:
message["Zone"] = device['Zone'].upper()
except KeyError:
pass
message["Room"] = device["Room"].upper()
message["Type"] = "AV RENDERER"
message["Device"] = device["Device"]
break
@staticmethod
def _get_device_name(dev):
if CONST.devices:
for device in CONST.devices:
if device['ML_ID'] == dev:
return device['Device']
return dev
@staticmethod @staticmethod
def _get_source_name(source, message): def _get_source_name(source, message):
if CONST.available_sources: if CONST.available_sources:

View file

@ -1,7 +1,9 @@
import logging import logging
import requests
import asyncore import asyncore
import json import json
import time
import requests
from requests.auth import HTTPDigestAuth, HTTPBasicAuth
from collections import OrderedDict from collections import OrderedDict
import Resources.CONSTANTS as CONST import Resources.CONSTANTS as CONST
@ -20,48 +22,95 @@ class MLConfig:
self._download_data() self._download_data()
def _download_data(self): def _download_data(self):
self.log.info('Downloading configuration data from Gateway...') try:
url = 'http://' + self._host + '/mlgwpservices.json' self.log.info('Downloading configuration data from Gateway...')
auth = (self._user, self._pwd) url = 'http://' + self._host + '/mlgwpservices.json'
response = requests.get(url, auth=auth) # try Basic Auth next (this is needed for the BLGW)
configurationdata = json.loads(response.text) response = requests.get(url, auth=HTTPBasicAuth(self._user, self._pwd))
self.configure_mlgw(configurationdata)
if response.status_code == 401:
# try Digest Auth first (this is needed for the MLGW)
response = requests.get(url, auth=HTTPDigestAuth(self._user, self._pwd))
if response.status_code == 401:
return
else:
# Once logged in successfully download and process the configuration data
configurationdata = json.loads(response.text)
self.log.debug(json.dumps(configurationdata, indent=4))
self.configure_mlgw(configurationdata)
except ValueError:
pass
def configure_mlgw(self, data): def configure_mlgw(self, data):
self.log.info('Processing Gateway configuration data...\n') self.log.info('Processing Gateway configuration data...\n')
CONST.gateway['Serial_Number'] = data['sn'] CONST.gateway['Serial_Number'] = data['sn']
CONST.gateway['Project'] = data['project'] CONST.gateway['Project'] = data['project']
CONST.gateway['Installer'] = str(data['installer']['name']) try:
CONST.gateway['Contact'] = str(data['installer']['contact']) CONST.gateway['Installer'] = str(data['installer']['name'])
CONST.gateway['Contact'] = str(data['installer']['contact'])
gateway_type = 'blgw'
except KeyError:
gateway_type = 'mlgw'
for zone in data["zones"]: for zone in data["zones"]:
if int(zone['number']) == 240: if int(zone['number']) == 240:
continue continue
room = OrderedDict() room = OrderedDict()
room['Room_Number'] = zone['number'] room['Room_Number'] = zone['number']
room['Zone'] = str(zone['name']).split('/')[0] if gateway_type == 'blgw':
room['Room_Name'] = str(zone['name']).split('/')[1] # BLGW arranges rooms within zones
room['Zone'] = str(zone['name']).split('/')[0]
room['Room_Name'] = str(zone['name']).split('/')[1]
elif gateway_type == 'mlgw':
# MLGW has no zoning concept - devices are arranged in rooms only
room['Room_Name'] = str(zone['name'])
room['Products'] = [] room['Products'] = []
for product in zone["products"]: for product in zone["products"]:
device = OrderedDict() device = OrderedDict()
room['Products'].append(str(product["name"])) room['Products'].append(str(product["name"]))
# Device identification
device['Device'] = str(product["name"]) device['Device'] = str(product["name"])
device['MLN'] = product["MLN"] device['MLN'] = product["MLN"]
device['ML_ID'] = '' device['ML_ID'] = ''
device['Serial_num'] = str(product["sn"]) try:
device['Zone'] = str(zone["name"]).split('/')[0] device['Serial_num'] = str(product["sn"])
device['Room'] = str(zone["name"]).split('/')[1] except KeyError:
device['Serial_num'] = ''
# Physical location
if gateway_type == 'blgw':
# BLGW arranges rooms within zones
device['Zone'] = str(zone['name']).split('/')[0]
device['Room'] = str(zone['name']).split('/')[1]
elif gateway_type == 'mlgw':
# MLGW has no zoning concept - devices are arranged in rooms only
device['Room'] = str(zone['name'])
device['Room_Number'] = str(zone["number"]) device['Room_Number'] = str(zone["number"])
# Source logging parameters for managing notifications
device['Sources'] = OrderedDict() device['Sources'] = OrderedDict()
device['Current Source'] = 'None'
device['Current Source Type'] = 'None'
device['Now Playing'] = 'None'
device['Channel/Track'] = '0'
device['State'] = 'Standby'
device['last update'] = time.time()
for source in product["sources"]: for source in product["sources"]:
device['Sources'][str(source["name"])] = OrderedDict() device['Sources'][str(source["name"])] = OrderedDict()
for selectCmd in source["selectCmds"]: for selectCmd in source["selectCmds"]:
source_id = str(source['sourceId'].split(':')[0]) if gateway_type == 'blgw':
source_id = self._srcdictsanitize(CONST.blgw_srcdict, source_id).upper() # get source information from the BLGW config file
device['Sources'][str(source["name"])]['source'] = source_id source_id = str(source['sourceId'].split(':')[0])
device['Sources'][str(source["name"])]['uniqueID'] = str(source['sourceId']) source_id = self._srcdictsanitize(CONST.blgw_srcdict, source_id).upper()
device['Sources'][str(source["name"])]['source'] = source_id
device['Sources'][str(source["name"])]['uniqueID'] = str(source['sourceId'])
else:
# MLGW config file is structured differently
source_id = self._srcdictsanitize(CONST.beo4_commanddict, source['selectID']).upper()
device['Sources'][str(source["name"])]['source'] = source_id
source_tuple = (str(source["name"]), source_id) source_tuple = (str(source["name"]), source_id)
cmd_tuple = (source_id, (int(selectCmd["cmd"]), int(selectCmd["unit"]))) cmd_tuple = (source_id, (int(selectCmd["cmd"]), int(selectCmd["unit"])))
device['Sources'][str(source["name"])]['BR1_cmd'] = cmd_tuple device['Sources'][str(source["name"])]['BR1_cmd'] = cmd_tuple
@ -70,7 +119,10 @@ class MLConfig:
for channel in source['channels']: for channel in source['channels']:
c = OrderedDict() c = OrderedDict()
c_num = '' c_num = ''
num = channel['selectSEQ'][::2] if gateway_type == 'blgw':
num = channel['selectSEQ'][::2]
else:
num = channel['selectSEQ'][:-1]
for n in num: for n in num:
c_num += str(n) c_num += str(n)
c['number'] = int(c_num) c['number'] = int(c_num)
@ -98,7 +150,7 @@ class MLConfig:
def get_masterlink_id(self, mlgw, mlcli): def get_masterlink_id(self, mlgw, mlcli):
self.log.info("Finding MasterLink ID of products:") self.log.info("Finding MasterLink ID of products:")
if mlgw.is_connected and mlcli.is_connected: if mlgw.is_connected and mlcli.is_connected and CONST.devices:
for device in CONST.devices: for device in CONST.devices:
self.log.info("Finding MasterLink ID of product " + device.get('Device')) self.log.info("Finding MasterLink ID of product " + device.get('Device'))
# Ping the device with a light timeout to elicit a ML telegram containing its ML_ID # Ping the device with a light timeout to elicit a ML telegram containing its ML_ID
@ -106,23 +158,33 @@ class MLConfig:
CONST.CMDS_DEST.get("AUDIO SOURCE"), CONST.CMDS_DEST.get("AUDIO SOURCE"),
CONST.BEO4_CMDS.get("LIGHT TIMEOUT")) CONST.BEO4_CMDS.get("LIGHT TIMEOUT"))
if device.get('Serial_num') is None: if device.get('Serial_num') in [None, '']:
# If this is a MasterLink product it has no serial number... # If this is a MasterLink product it has no serial number...
# loop to until expected response received from ML Command Line Interface # loop to until expected response received from ML Command Line Interface
while 'to_device' not in mlcli.last_message and mlcli.last_message['from_device'] == \ test = True
"MLGW" and mlcli.last_message['payload_type'] == \ while test:
"MLGW_REMOTE_BEO4" and mlcli.last_message['payload']['command'] == "LIGHT TIMEOUT": try:
asyncore.loop(count=1, timeout=0.2) if mlcli.last_message['from_device'] == "MLGW" and \
mlcli.last_message['payload_type'] == "MLGW_REMOTE_BEO4" and \
mlcli.last_message['State_Update']['command'] == "Light Timeout":
device['ML_ID'] = mlcli.last_message.get('to_device')
self.log.info("\tMasterLink ID of product " +
device.get('Device') + " is " + device.get('ML_ID') + ".\n")
test = False
except KeyError:
asyncore.loop(count=1, timeout=0.2)
device['ML_ID'] = mlcli.last_message.get('to_device')
self.log.info("\tMasterLink ID of product " +
device.get('Device') + " is " + device.get('ML_ID') + ".\n")
else: else:
# If this is a NetLink product then it has a serial number and no ML_ID # If this is a NetLink product then it has a serial number and no ML_ID
device['ML_ID'] = 'NA' device['ML_ID'] = 'NA'
self.log.info("\tNetworkLink ID of product " + device.get('Device') + " is " + self.log.info("\tNetworkLink ID of product " + device.get('Device') + " is " +
device.get('Serial_num') + ". No MasterLink ID assigned.\n") device.get('Serial_num') + ". No MasterLink ID assigned.\n")
self.log.debug(json.dumps(device, indent=4))
# ########################################################################################
# Utility Functions
@staticmethod @staticmethod
def _srcdictsanitize(d, s): def _srcdictsanitize(d, s):
result = d.get(s) result = d.get(s)

View file

@ -118,20 +118,11 @@ class MLGWClient(asynchat.async_chat):
message['serial_Num'] = sn message['serial_Num'] = sn
elif payload_type == "Source Status": elif payload_type == "Source Status":
if CONST.rooms and CONST.devices: self._get_device_info(message, payload)
for device in CONST.devices:
if device['MLN'] == payload[0]:
name = device['Device']
for room in CONST.rooms:
if name in room['Products']:
message["Zone"] = room['Zone'].upper()
message["Room"] = room['Room_Name'].upper()
message["Type"] = 'AV RENDERER'
message["Device"] = name
message["payload_type"] = payload_type message["payload_type"] = payload_type
message["MLN"] = payload[0] message["MLN"] = payload[0]
message["State_Update"] = OrderedDict() message["State_Update"] = OrderedDict()
message["State_Update"]["nowPlaying"] = '' message["State_Update"]["nowPlaying"] = 'Unknown'
message["State_Update"]["nowPlayingDetails"] = OrderedDict() message["State_Update"]["nowPlayingDetails"] = OrderedDict()
message["State_Update"]["nowPlayingDetails"]["channel_track"] = \ message["State_Update"]["nowPlayingDetails"]["channel_track"] = \
self._hexword(payload[4], payload[5]) self._hexword(payload[4], payload[5])
@ -146,27 +137,26 @@ class MLGWClient(asynchat.async_chat):
message["State_Update"]["state"] = self._getdictstr(CONST.sourceactivitydict, payload[6]) message["State_Update"]["state"] = self._getdictstr(CONST.sourceactivitydict, payload[6])
elif payload_type == "Picture and Sound Status": elif payload_type == "Picture and Sound Status":
if CONST.rooms and CONST.devices: self._get_device_info(message, payload)
for device in CONST.devices:
if device['MLN'] == payload[0]:
name = device['Device']
for room in CONST.rooms:
if name in room['Products']:
message["Zone"] = room['Zone'].upper()
message["Room"] = room['Room_Name'].upper()
message["Type"] = 'AV RENDERER'
message["Device"] = name
message["payload_type"] = payload_type message["payload_type"] = payload_type
message["MLN"] = payload[0] message["MLN"] = payload[0]
message["State_Update"] = OrderedDict() message["State_Update"] = OrderedDict([("sound_status", OrderedDict()), ("picture_status", OrderedDict())])
message["State_Update"]["sound_status"] = self._getdictstr(CONST.mlgw_soundstatusdict, payload[1]) message["State_Update"]["sound_status"]["mute_status"] = \
message["State_Update"]["speakermode"] = self._getdictstr(CONST.mlgw_speakermodedict, payload[2]) self._getdictstr(CONST.mlgw_soundstatusdict, payload[1])
message["State_Update"]["stereo_mode"] = self._getdictstr(CONST.mlgw_stereoindicatordict, payload[9]) message["State_Update"]["sound_status"]["speakermode"] = \
message["State_Update"]["screen1_mute"] = self._getdictstr(CONST.mlgw_screenmutedict, payload[4]) self._getdictstr(CONST.mlgw_speakermodedict, payload[2])
message["State_Update"]["screen1_active"] = self._getdictstr(CONST.mlgw_screenactivedict, payload[5]) message["State_Update"]["sound_status"]["stereo_mode"] = \
message["State_Update"]["screen2_mute"] = self._getdictstr(CONST.mlgw_screenmutedict, payload[6]) self._getdictstr(CONST.mlgw_stereoindicatordict, payload[9])
message["State_Update"]["screen2_active"] = self._getdictstr(CONST.mlgw_screenactivedict, payload[7]) message["State_Update"]["picture_status"]["screen1_mute"] = \
message["State_Update"]["cinema_mode"] = self._getdictstr(CONST.mlgw_cinemamodedict, payload[8]) self._getdictstr(CONST.mlgw_screenmutedict, payload[4])
message["State_Update"]["picture_status"]["screen1_active"] = \
self._getdictstr(CONST.mlgw_screenactivedict, payload[5])
message["State_Update"]["picture_status"]["screen2_mute"] = \
self._getdictstr(CONST.mlgw_screenmutedict, payload[6])
message["State_Update"]["picture_status"]["screen2_active"] = \
self._getdictstr(CONST.mlgw_screenactivedict, payload[7])
message["State_Update"]["picture_status"]["cinema_mode"] = \
self._getdictstr(CONST.mlgw_cinemamodedict, payload[8])
message["volume"] = int(payload[3]) message["volume"] = int(payload[3])
elif payload_type == "All standby notification": elif payload_type == "All standby notification":
@ -177,7 +167,10 @@ class MLGWClient(asynchat.async_chat):
if CONST.rooms: if CONST.rooms:
for room in CONST.rooms: for room in CONST.rooms:
if room['Room_Number'] == payload[0]: if room['Room_Number'] == payload[0]:
message["Zone"] = room['Zone'].upper() try:
message["Zone"] = room['Zone'].upper()
except KeyError:
pass
message["Room"] = room['Room_Name'].upper() message["Room"] = room['Room_Name'].upper()
message["Type"] = self._getdictstr(CONST.mlgw_lctypedict, payload[1]).upper() + " COMMAND" message["Type"] = self._getdictstr(CONST.mlgw_lctypedict, payload[1]).upper() + " COMMAND"
message["Device"] = 'Beo4/BeoRemote One' message["Device"] = 'Beo4/BeoRemote One'
@ -236,7 +229,7 @@ class MLGWClient(asynchat.async_chat):
self.messageCallBack(self.name, str(list(header)), str(list(payload)), message) self.messageCallBack(self.name, str(list(header)), str(list(payload)), message)
# ######################################################################################## # ########################################################################################
# ##### mlgw send_cmder functions # ##### mlgw send functions
# send_cmd command to mlgw # send_cmd command to mlgw
def _send_cmd(self, msg_type, payload): def _send_cmd(self, msg_type, payload):
@ -267,6 +260,7 @@ class MLGWClient(asynchat.async_chat):
# Sleep to allow msg to arrive # Sleep to allow msg to arrive
time.sleep(0.2) time.sleep(0.2)
# Ping the gateway
def ping(self): def ping(self):
self._send_cmd(CONST.MLGW_PL.get("PING"), "") self._send_cmd(CONST.MLGW_PL.get("PING"), "")
@ -337,31 +331,31 @@ class MLGWClient(asynchat.async_chat):
def _getpayloadtypestr(self, payloadtype): def _getpayloadtypestr(self, payloadtype):
result = CONST.mlgw_payloadtypedict.get(payloadtype) result = CONST.mlgw_payloadtypedict.get(payloadtype)
if result is None: if result is None:
result = "UNKNOWN (type=" + self._hexbyte(payloadtype) + ")" result = "UNKNOWN (type = " + self._hexbyte(payloadtype) + ")"
return str(result) return str(result)
def _getbeo4commandstr(self, command): def _getbeo4commandstr(self, command):
result = CONST.beo4_commanddict.get(command) result = CONST.beo4_commanddict.get(command)
if result is None: if result is None:
result = "Cmd=" + self._hexbyte(command) result = "CMD = " + self._hexbyte(command)
return result return result
def _getvirtualactionstr(self, action): def _getvirtualactionstr(self, action):
result = CONST.mlgw_virtualactiondict.get(action) result = CONST.mlgw_virtualactiondict.get(action)
if result is None: if result is None:
result = "Action=" + self._hexbyte(action) result = "Action = " + self._hexbyte(action)
return result return result
def _getselectedsourcestr(self, source): def _getselectedsourcestr(self, source):
result = CONST.ml_selectedsourcedict.get(source) result = CONST.ml_selectedsourcedict.get(source)
if result is None: if result is None:
result = "Src=" + self._hexbyte(source) result = "SRC = " + self._hexbyte(source)
return result return result
def _getspeakermodestr(self, source): def _getspeakermodestr(self, source):
result = CONST.mlgw_speakermodedict.get(source) result = CONST.mlgw_speakermodedict.get(source)
if result is None: if result is None:
result = "mode=" + self._hexbyte(source) result = "mode = " + self._hexbyte(source)
return result return result
def _getdictstr(self, mydict, mykey): def _getdictstr(self, mydict, mykey):
@ -376,16 +370,40 @@ class MLGWClient(asynchat.async_chat):
for src in CONST.available_sources: for src in CONST.available_sources:
if src[1] == source: if src[1] == source:
message["State_Update"]["sourceName"] = src[0] message["State_Update"]["sourceName"] = src[0]
break return
@staticmethod
def _get_device_info(message, payload):
if CONST.rooms and CONST.devices:
for device in CONST.devices:
try:
if device['MLN'] == payload[0]:
name = device['Device']
for room in CONST.rooms:
if name in room['Products']:
try:
message["Zone"] = room['Zone'].upper()
except KeyError:
pass
message["Room"] = room['Room_Name'].upper()
message["Type"] = 'AV RENDERER'
message["Device"] = name
return
except KeyError:
pass
@staticmethod @staticmethod
def _get_channel_track(message): def _get_channel_track(message):
# Check device list for channel name information # Check device list for channel name information
if CONST.devices: if CONST.devices:
for device in CONST.devices: for device in CONST.devices:
if device['Device'] == message['Device']: try:
if 'channels' in device['Sources'][message["State_Update"]["source"]]: if device['Device'] == message['Device']:
for channel in device['Sources'][message["State_Update"]["source"]]['channels']: if 'channels' in device['Sources'][message["State_Update"]["source"]]:
if channel['number'] == int(message["State_Update"]['nowPlayingDetails']["channel_track"]): for channel in device['Sources'][message["State_Update"]["source"]]['channels']:
message["State_Update"]["nowPlaying"] = channel['name'] if channel['number'] == int(
break message["State_Update"]['nowPlayingDetails']["channel_track"]):
message["State_Update"]["nowPlaying"] = channel['name']
return
except KeyError:
pass

View file

@ -66,13 +66,12 @@ class MLtnClient(asynchat.async_chat):
self.handle_close() self.handle_close()
else: else:
self._decode(items) try:
self._decode(items)
except IndexError:
self.log.debug(str(list(items)))
self._received_data = "" self._received_data = ""
self.last_sent = ''
self.last_sent_at = 0
self.last_received = ''
self.last_received_at = 0
def _decode(self, items): def _decode(self, items):
header = items[3][:-1] header = items[3][:-1]
@ -80,6 +79,10 @@ class MLtnClient(asynchat.async_chat):
telegram = self._received_data[telegram_starts:].replace('!', '').split('/') telegram = self._received_data[telegram_starts:].replace('!', '').split('/')
message = OrderedDict() message = OrderedDict()
if telegram[0] == 'Monitor events ( keys: M, E, C, (spc), Q ) ----':
self.toggle_commands()
self.toggle_macros()
if header == 'integration_protocol': if header == 'integration_protocol':
message = self._decode_ip(telegram, message) message = self._decode_ip(telegram, message)
@ -322,13 +325,25 @@ class MLtnClient(asynchat.async_chat):
time.sleep(0.2) time.sleep(0.2)
def toggle_events(self): def toggle_events(self):
self._send_cmd('e') try:
self.push('e')
except socket.error, e:
self.log.info("Error sending data: %s" % e)
self.handle_close()
def toggle_macros(self): def toggle_macros(self):
self._send_cmd('m') try:
self.push('m')
except socket.error, e:
self.log.info("Error sending data: %s" % e)
self.handle_close()
def toggle_commands(self): def toggle_commands(self):
self._send_cmd('c') try:
self.push('c')
except socket.error, e:
self.log.info("Error sending data: %s" % e)
self.handle_close()
def ping(self): def ping(self):
self._send_cmd('') self._send_cmd('')