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.send_cmd(self._user)
self.send_cmd(self._pwd)
self.statefiler()
self.statefilter()
def handle_close(self):
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.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
self.send_cmd(s_filter)
@ -241,12 +241,16 @@ class BLHIPClient(asynchat.async_chat):
@staticmethod
def _get_channel_track(message):
# Check device list for channel name information
if CONST.devices:
for device in CONST.devices:
if device['Device'] == message['Device']:
if 'channels' in device['Sources'][message["State_Update"]["source"]]:
for channel in device['Sources'][message["State_Update"]["source"]]['channels']:
if channel['number'] == int(message["State_Update"]['nowPlayingDetails']["channel_track"]):
message["State_Update"]["nowPlaying"] = channel['name']
break
try:
# Check device list for channel name information
if CONST.devices:
for device in CONST.devices:
if device['Device'] == message['Device']:
if 'channels' in device['Sources'][message["State_Update"]["source"]]:
for channel in device['Sources'][message["State_Update"]["source"]]['channels']:
if channel['number'] == int(
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
# ########################################################################################
# Config data (set on initialisation)
gateway = dict()
gateway = dict([("Current Audio Source", ''), ("Current Video Source", '')])
rooms = []
devices = []
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_commanddict = dict(
@ -18,7 +38,7 @@ beo4_commanddict = dict(
(0x82, "V.Aux/DTV2"),
(0x83, "A.Aux"),
(0x84, "Media"),
(0x85, "V.Tape/V.Mem/DVD2"),
(0x85, "V.Tape/V.Mem"),
(0x86, "DVD"),
(0x87, "Camera"),
(0x88, "Text"),
@ -350,9 +370,9 @@ ml_selectedsourcedict = dict(
(0x0B, "TV"),
(0x15, "V.TAPE/V.MEM"),
(0x16, "DVD2"),
(0x1F, "DTV"),
(0x1F, "SAT/DTV"),
(0x29, "DVD"),
(0x33, "V.AUX"),
(0x33, "V.AUX/DTV2"),
(0x3E, "DOORCAM"),
(0x47, "PC"),
(0x6F, "RADIO"),
@ -369,6 +389,8 @@ ml_selectedsourcedict = dict(
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(
[
("VIDEO", (0x0B, 0x1F)),

View file

@ -84,7 +84,7 @@ class MLCLIClient(asynchat.async_chat):
for item in items:
try:
telegram.append(int(item[:-1], base=16))
except TypeError:
except (ValueError, TypeError):
# abort if invalid character found
self.log.debug('Invalid character ' + str(item) + ' found in telegram: ' +
''.join(items) + '\nAborting!')
@ -192,37 +192,35 @@ class MLCLIClient(asynchat.async_chat):
def _decode(self, telegram):
# Decode header
message = OrderedDict()
if CONST.devices:
for device in CONST.devices:
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])
self._get_device_info(message, telegram)
message["from_device"] = self._get_device_name(self._dictsanitize(CONST.ml_device_dict, telegram[1]))
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["type"] = self._dictsanitize(CONST.ml_telegram_type_dict, telegram[3])
message["payload_type"] = self._dictsanitize(CONST.ml_command_type_dict, telegram[7])
message["payload_len"] = telegram[8] + 1
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
# TTFF__TYDSOS__PTLLPS SR____LS______SLSHTR__ACSTPI________________________TRTR______
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"]["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:
message["State_Update"]["nowPlayingDetails"]["channel_track"] = telegram[19]
else:
message["State_Update"]["nowPlayingDetails"]["channel_track"] = telegram[36] * 256 + telegram[37]
message["State_Update"]["nowPlayingDetails"]["source_medium_position"] = \
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])
source = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[10])
self._get_source_name(source, message)
@ -257,7 +255,7 @@ class MLCLIClient(asynchat.async_chat):
# audio 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"]["type"] = \
self._get_type(CONST.ml_selectedsource_type_dict, telegram[11])
@ -271,7 +269,7 @@ class MLCLIClient(asynchat.async_chat):
# 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"]["source_type"] = \
self._get_type(CONST.ml_selectedsource_type_dict, telegram[13])
@ -286,6 +284,8 @@ class MLCLIClient(asynchat.async_chat):
# track change info
if message.get("payload_type") == "TRACK_INFO":
message["State_Update"]["subtype"] = self._dictsanitize(CONST.ml_trackinfo_subtype_dict, telegram[9])
# 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_sourceID"] = telegram[11]
@ -297,6 +297,7 @@ class MLCLIClient(asynchat.async_chat):
message["State_Update"]["source"] = source
message["State_Update"]["sourceID"] = telegram[22]
# Current Source
if message["State_Update"].get("subtype") == "Current Source":
source = self._dictsanitize(CONST.ml_selectedsourcedict, telegram[11])
self._get_source_name(source, message)
@ -308,7 +309,7 @@ class MLCLIClient(asynchat.async_chat):
# 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"]["source_type"] = \
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']
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
def _get_source_name(source, message):
if CONST.available_sources:

View file

@ -1,7 +1,9 @@
import logging
import requests
import asyncore
import json
import time
import requests
from requests.auth import HTTPDigestAuth, HTTPBasicAuth
from collections import OrderedDict
import Resources.CONSTANTS as CONST
@ -20,48 +22,95 @@ class MLConfig:
self._download_data()
def _download_data(self):
self.log.info('Downloading configuration data from Gateway...')
url = 'http://' + self._host + '/mlgwpservices.json'
auth = (self._user, self._pwd)
response = requests.get(url, auth=auth)
configurationdata = json.loads(response.text)
self.configure_mlgw(configurationdata)
try:
self.log.info('Downloading configuration data from Gateway...')
url = 'http://' + self._host + '/mlgwpservices.json'
# try Basic Auth next (this is needed for the BLGW)
response = requests.get(url, auth=HTTPBasicAuth(self._user, self._pwd))
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):
self.log.info('Processing Gateway configuration data...\n')
CONST.gateway['Serial_Number'] = data['sn']
CONST.gateway['Project'] = data['project']
CONST.gateway['Installer'] = str(data['installer']['name'])
CONST.gateway['Contact'] = str(data['installer']['contact'])
try:
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"]:
if int(zone['number']) == 240:
continue
room = OrderedDict()
room['Room_Number'] = zone['number']
room['Zone'] = str(zone['name']).split('/')[0]
room['Room_Name'] = str(zone['name']).split('/')[1]
if gateway_type == 'blgw':
# 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'] = []
for product in zone["products"]:
device = OrderedDict()
room['Products'].append(str(product["name"]))
# Device identification
device['Device'] = str(product["name"])
device['MLN'] = product["MLN"]
device['ML_ID'] = ''
device['Serial_num'] = str(product["sn"])
device['Zone'] = str(zone["name"]).split('/')[0]
device['Room'] = str(zone["name"]).split('/')[1]
try:
device['Serial_num'] = str(product["sn"])
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"])
# Source logging parameters for managing notifications
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"]:
device['Sources'][str(source["name"])] = OrderedDict()
for selectCmd in source["selectCmds"]:
source_id = str(source['sourceId'].split(':')[0])
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'])
if gateway_type == 'blgw':
# get source information from the BLGW config file
source_id = str(source['sourceId'].split(':')[0])
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)
cmd_tuple = (source_id, (int(selectCmd["cmd"]), int(selectCmd["unit"])))
device['Sources'][str(source["name"])]['BR1_cmd'] = cmd_tuple
@ -70,7 +119,10 @@ class MLConfig:
for channel in source['channels']:
c = OrderedDict()
c_num = ''
num = channel['selectSEQ'][::2]
if gateway_type == 'blgw':
num = channel['selectSEQ'][::2]
else:
num = channel['selectSEQ'][:-1]
for n in num:
c_num += str(n)
c['number'] = int(c_num)
@ -98,7 +150,7 @@ class MLConfig:
def get_masterlink_id(self, mlgw, mlcli):
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:
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
@ -106,23 +158,33 @@ class MLConfig:
CONST.CMDS_DEST.get("AUDIO SOURCE"),
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...
# 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'] == \
"MLGW" and mlcli.last_message['payload_type'] == \
"MLGW_REMOTE_BEO4" and mlcli.last_message['payload']['command'] == "LIGHT TIMEOUT":
asyncore.loop(count=1, timeout=0.2)
test = True
while test:
try:
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:
# If this is a NetLink product then it has a serial number and no ML_ID
device['ML_ID'] = 'NA'
self.log.info("\tNetworkLink ID of product " + device.get('Device') + " is " +
device.get('Serial_num') + ". No MasterLink ID assigned.\n")
self.log.debug(json.dumps(device, indent=4))
# ########################################################################################
# Utility Functions
@staticmethod
def _srcdictsanitize(d, s):
result = d.get(s)

View file

@ -118,20 +118,11 @@ class MLGWClient(asynchat.async_chat):
message['serial_Num'] = sn
elif payload_type == "Source Status":
if CONST.rooms and CONST.devices:
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
self._get_device_info(message, payload)
message["payload_type"] = payload_type
message["MLN"] = payload[0]
message["State_Update"] = OrderedDict()
message["State_Update"]["nowPlaying"] = ''
message["State_Update"]["nowPlaying"] = 'Unknown'
message["State_Update"]["nowPlayingDetails"] = OrderedDict()
message["State_Update"]["nowPlayingDetails"]["channel_track"] = \
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])
elif payload_type == "Picture and Sound Status":
if CONST.rooms and CONST.devices:
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
self._get_device_info(message, payload)
message["payload_type"] = payload_type
message["MLN"] = payload[0]
message["State_Update"] = OrderedDict()
message["State_Update"]["sound_status"] = self._getdictstr(CONST.mlgw_soundstatusdict, payload[1])
message["State_Update"]["speakermode"] = self._getdictstr(CONST.mlgw_speakermodedict, payload[2])
message["State_Update"]["stereo_mode"] = self._getdictstr(CONST.mlgw_stereoindicatordict, payload[9])
message["State_Update"]["screen1_mute"] = self._getdictstr(CONST.mlgw_screenmutedict, payload[4])
message["State_Update"]["screen1_active"] = self._getdictstr(CONST.mlgw_screenactivedict, payload[5])
message["State_Update"]["screen2_mute"] = self._getdictstr(CONST.mlgw_screenmutedict, payload[6])
message["State_Update"]["screen2_active"] = self._getdictstr(CONST.mlgw_screenactivedict, payload[7])
message["State_Update"]["cinema_mode"] = self._getdictstr(CONST.mlgw_cinemamodedict, payload[8])
message["State_Update"] = OrderedDict([("sound_status", OrderedDict()), ("picture_status", OrderedDict())])
message["State_Update"]["sound_status"]["mute_status"] = \
self._getdictstr(CONST.mlgw_soundstatusdict, payload[1])
message["State_Update"]["sound_status"]["speakermode"] = \
self._getdictstr(CONST.mlgw_speakermodedict, payload[2])
message["State_Update"]["sound_status"]["stereo_mode"] = \
self._getdictstr(CONST.mlgw_stereoindicatordict, payload[9])
message["State_Update"]["picture_status"]["screen1_mute"] = \
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])
elif payload_type == "All standby notification":
@ -177,7 +167,10 @@ class MLGWClient(asynchat.async_chat):
if CONST.rooms:
for room in CONST.rooms:
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["Type"] = self._getdictstr(CONST.mlgw_lctypedict, payload[1]).upper() + " COMMAND"
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)
# ########################################################################################
# ##### mlgw send_cmder functions
# ##### mlgw send functions
# send_cmd command to mlgw
def _send_cmd(self, msg_type, payload):
@ -267,6 +260,7 @@ class MLGWClient(asynchat.async_chat):
# Sleep to allow msg to arrive
time.sleep(0.2)
# Ping the gateway
def ping(self):
self._send_cmd(CONST.MLGW_PL.get("PING"), "")
@ -337,31 +331,31 @@ class MLGWClient(asynchat.async_chat):
def _getpayloadtypestr(self, payloadtype):
result = CONST.mlgw_payloadtypedict.get(payloadtype)
if result is None:
result = "UNKNOWN (type=" + self._hexbyte(payloadtype) + ")"
result = "UNKNOWN (type = " + self._hexbyte(payloadtype) + ")"
return str(result)
def _getbeo4commandstr(self, command):
result = CONST.beo4_commanddict.get(command)
if result is None:
result = "Cmd=" + self._hexbyte(command)
result = "CMD = " + self._hexbyte(command)
return result
def _getvirtualactionstr(self, action):
result = CONST.mlgw_virtualactiondict.get(action)
if result is None:
result = "Action=" + self._hexbyte(action)
result = "Action = " + self._hexbyte(action)
return result
def _getselectedsourcestr(self, source):
result = CONST.ml_selectedsourcedict.get(source)
if result is None:
result = "Src=" + self._hexbyte(source)
result = "SRC = " + self._hexbyte(source)
return result
def _getspeakermodestr(self, source):
result = CONST.mlgw_speakermodedict.get(source)
if result is None:
result = "mode=" + self._hexbyte(source)
result = "mode = " + self._hexbyte(source)
return result
def _getdictstr(self, mydict, mykey):
@ -376,16 +370,40 @@ class MLGWClient(asynchat.async_chat):
for src in CONST.available_sources:
if src[1] == source:
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
def _get_channel_track(message):
# Check device list for channel name information
if CONST.devices:
for device in CONST.devices:
if device['Device'] == message['Device']:
if 'channels' in device['Sources'][message["State_Update"]["source"]]:
for channel in device['Sources'][message["State_Update"]["source"]]['channels']:
if channel['number'] == int(message["State_Update"]['nowPlayingDetails']["channel_track"]):
message["State_Update"]["nowPlaying"] = channel['name']
break
try:
if device['Device'] == message['Device']:
if 'channels' in device['Sources'][message["State_Update"]["source"]]:
for channel in device['Sources'][message["State_Update"]["source"]]['channels']:
if channel['number'] == int(
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()
else:
self._decode(items)
try:
self._decode(items)
except IndexError:
self.log.debug(str(list(items)))
self._received_data = ""
self.last_sent = ''
self.last_sent_at = 0
self.last_received = ''
self.last_received_at = 0
def _decode(self, items):
header = items[3][:-1]
@ -80,6 +79,10 @@ class MLtnClient(asynchat.async_chat):
telegram = self._received_data[telegram_starts:].replace('!', '').split('/')
message = OrderedDict()
if telegram[0] == 'Monitor events ( keys: M, E, C, (spc), Q ) ----':
self.toggle_commands()
self.toggle_macros()
if header == 'integration_protocol':
message = self._decode_ip(telegram, message)
@ -322,13 +325,25 @@ class MLtnClient(asynchat.async_chat):
time.sleep(0.2)
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):
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):
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):
self._send_cmd('')