def configure(self, data=None): if data is not None: self.config.update(data) self.paramParser = ParamParser(self.config, self.use_numeric_key) self.connectionDescription = (self.config.get('hostName', '') + ':' + str(self.config.get('port', 0)))
def __init__(self, config, logger, use_numeric_key=False): super(WebOS, self).__init__("ws://" + config['hostName'] + ':' + str(config['port']), exclude_headers=["Origin"]) self.config = config self.connected = False self.connectEvent = Event() self.curID = 0 self.callbacks = {} self.clientKey = None self.logger = logger self.paramParser = ParamParser(config, use_numeric_key) try: with open(config['clientKeyFile'], 'r') as clientKeyInput: self.clientKey = yaml.load(clientKeyInput) except: pass try: with open(config['macFile'], 'r') as macInput: self.config.update(yaml.load(macInput)) except: pass self.sock.settimeout(5) try: self.connect() except: logger.debug('Websocket timeout. Possibly device is off') logger.info('Loaded %s driver', __name__)
def __init__(self, config, logger, use_numeric_key=False): self.config = config self.executable = config['executable'] self.logger = logger self.paramParser = ParamParser(config, use_numeric_key) self.connectStr = self.config['hostName'] + ':' + str( self.config['port']) self.connected = False logger.info('Loaded %s driver', __name__)
def create(self): # Write controller labels self.nlsData.append('# controller labels') self.nlsData.append('') self.nlsData.append( self.NODE_NAME.format( 'controller', self.config['controller']['name'] + ' Controller')) self.nlsData.append( self.NODE_ICON.format( 'controller', self.config['controller'].get('icon', 'GenericCtl'))) self.nlsData.append('') self.nlsData.append( self.COMMAND_NAME.format('ctl-DISCOVER', 'Re-Discover')) self.nlsData.append( self.STATUS_NAME.format( 'ctl', 'ST', self.config['controller']['name'] + ' Online')) self.nlsData.append('') # Write controller node nodeDef = ET.SubElement(self.nodeTree, 'nodeDef', id='controller', nls='ctl') sts = ET.SubElement(nodeDef, 'sts') ET.SubElement(sts, 'st', id='ST', editor='bool') cmds = ET.SubElement(nodeDef, 'cmds') ET.SubElement(cmds, 'sends') accepts = ET.SubElement(cmds, 'accepts') ET.SubElement(accepts, 'cmd', id='DISCOVER') # Write primary devices for driverName, driverData in self.config['drivers'].items(): polyCommandsData = self.config['poly']['drivers'].get( driverName, {}).get('commands', {}) paramParser = ParamParser(driverData, True) nodeDesc = driverData.get('description', utils.name_to_desc(driverName)) nlsName = utils.name_to_nls(driverName) nlsData = [ self.STATUS_NAME.format(nlsName, 'ST', nodeDesc + ' Online') ] states = self.write_node_info(polyCommandsData, paramParser, driverName, nodeDesc, nlsName, driverData, nlsData) ET.SubElement(states, 'st', id='ST', editor='bool') # Write sub devices for groupName, groupData in driverData.get('commandGroups', {}).items(): polyGroupData = self.config['poly']['commandGroups'].get( groupName) if polyGroupData: self.write_node_info( polyCommandsData, paramParser, driverName + '_' + groupName, nodeDesc + ' ' + utils.name_to_desc(groupName), utils.name_to_nls(driverName + polyGroupData.get('nls', groupName)), groupData)
def ISYExport(config, destination, output, input, host, temp): configData = yaml.load(config) configData['host'] = host if input: print("Extracting existing resources from", input.name) with ZipFile(input) as inputFile: inputFile.extractall(destination) global curResourceID resources = {} commands = {} maxResourceID = 0 outputFileName = os.path.join(destination, ISY_CONF_FOLDER, ISY_NET_FOLDER, 'RES.CFG') try: with open(outputFileName, 'r') as resourceFile: resourceTree = ET.parse(resourceFile).getroot() for resource in resourceTree: curResourceID = 0 for id in resource.iter('id'): curResourceID = int(id.text) if maxResourceID < curResourceID: maxResourceID = curResourceID for name in resource.iter('name'): resources[name.text] = resource except: resourceTree = ET.Element('NetConfig') print('Found', maxResourceID, 'existing resources') curResourceID = maxResourceID + 1 print("Exporting ISY network resources from", config.name, "to", destination) for deviceName, deviceData in configData['devices'].items(): deviceData.update(configData['drivers'][deviceData['driver']]) paramParser = ParamParser(deviceData) utils.flatten_commands(deviceData) for commandName, commandData in deviceData['commands'].items(): if not commandData.get('result'): simpleCommand = True resourceName = deviceName + '.' + commandName command = deviceName + '/' + commandName if commandData.get('acceptsNumber'): print('Create state variable for', resourceName) resourceID = addResource(configData, resources, resourceTree, resourceName) commands[resourceID] = command + STATE_VAR_MESSAGE simpleCommand = False if 'value_set' in commandData: for value in paramParser.value_sets[commandData['value_set']].keys(): resourceID = addResource(configData, resources, resourceTree, resourceName + '/' + value) commands[resourceID] = command + '/' + value simpleCommand = False if simpleCommand: resourceID = addResource(configData, resources, resourceTree, resourceName) commands[resourceID] = command outputFileName = os.path.join(destination, ISY_CONF_FOLDER, ISY_NET_FOLDER, 'RES.CFG') if not os.path.exists(os.path.dirname(outputFileName)): try: os.makedirs(os.path.dirname(outputFileName)) except OSError as error: if error.errno != errno.EEXIST: raise with ZipFile(os.path.join(destination, output), 'w') as outputFile: with open(outputFileName, 'w') as output: output.write(ET.tostring(resourceTree).decode()) # Add main resources file to zip outputFile.write(outputFileName, os.path.relpath(outputFileName, destination)) # Add RES files to zip for resourceID in range(1, curResourceID): command = commands.get(resourceID) outputFileName = os.path.join(destination, ISY_CONF_FOLDER, ISY_NET_FOLDER, str(resourceID) + '.RES') if command: with open(outputFileName, 'w') as output: output.write(REQUEST.format(DEFAULT_METHOD, command, configData['host'], configData['port'])) outputFile.write(outputFileName, os.path.relpath(outputFileName, destination)) if not temp: shutil.rmtree(os.path.join(destination, ISY_CONF_FOLDER))
class BaseDriver(object): def __init__(self, config, logger, use_numeric_key=False): self.config = config self.use_numeric_key = use_numeric_key self.connected = False self.logger = logger self.configure() def configure(self, data=None): if data is not None: self.config.update(data) self.paramParser = ParamParser(self.config, self.use_numeric_key) self.connectionDescription = (self.config.get('hostName', '') + ':' + str(self.config.get('port', 0))) def start(self): try: if not self.connected: self.connect() except: self.logger.exception('Connection to %s failed', self.connectionDescription) def connect(self): pass def is_connected(self): try: if not self.connected: self.connect() except: pass return self.connected def hasCommand(self, commandName): return commandName in self.config['commands'] def getCommand(self, commandName): return self.config['commands'].get(commandName) def getData(self, commandName, args=None): if commandName == 'commands': commandList = [] for commandName, command in self.config['commands'].items(): commandList.append({ 'name': commandName, 'method': 'GET' if command.get('result', False) else 'PUT' }) return {'driver': self.__class__.__name__, 'commands': commandList} command = self.config['commands'][commandName] if not command.get('result'): raise Exception('Invalid command for ' + __name__ + ' and method: ' + commandName) result = { 'driver': self.__class__.__name__, 'command': commandName, } try: result['output'] = self.sendCommandRaw(commandName, command) self.process_result(commandName, command, result) except: self.connected = False raise return result def sendCommandRaw(self, commandName, command, args=None): pass def executeCommand(self, commandName, args=None): command = self.config['commands'][commandName] if args is not None: args = self.paramParser.translate_param(command, str(args)) if command.get('acceptsBool') and type(args) is not bool: args = args == 'true' or args == 'on' elif command.get('acceptsNumber'): args = str(int(args)) elif command.get('acceptsPct'): args = float(args) / 100 elif command.get('acceptsFloat'): args = '{0:g}'.format(float(args)) elif command.get('acceptsHex'): args = hex(int(args))[2:].upper() result = { 'driver': __name__, 'command': commandName, } try: result['output'] = self.sendCommandRaw(commandName, command, args) self.process_result(commandName, command, result) except: self.connected = False raise if args is not None: result['args'] = args return result def process_result(self, commandName, command, result): output = None try: if command.get('acceptsNumber'): output = int(result['output']) elif command.get('acceptsFloat'): output = float(result['output']) elif command.get('acceptsPct'): output = int(float(result['output']) * 100) elif command.get('acceptsHex'): output = int(result['output'], 16) else: output = self.paramParser.translate_param( command, result['output'], None, False) except: pass if output is not None: result['result'] = output @staticmethod def processParams(config, param): return False @staticmethod def discoverDevices(config): return None
class Android(object): ACTIVITY_RECORD = 'ActivityRecord' def __init__(self, config, logger, use_numeric_key=False): self.config = config self.executable = config['executable'] self.logger = logger self.paramParser = ParamParser(config, use_numeric_key) self.connectStr = self.config['hostName'] + ':' + str( self.config['port']) self.connected = False logger.info('Loaded %s driver', __name__) def start(self): try: self.connect() except: self.logger.exception('Error connecting to ' + self.connectStr) def connect(self): self.logger.debug( 'ADB status %s', subprocess.check_output( [self.executable, 'connect', self.connectStr]).decode()) self.logger.debug( 'ADB status %s', subprocess.check_output( [self.executable, 'connect', self.connectStr]).decode()) output = subprocess.check_output([self.executable, 'devices']).decode().split('\n') for line in output: if line.startswith(self.connectStr): if line.split('\t')[1] == 'device': self.connected = True return raise IOError('Connection to ' + self.connectStr + ' failed. If device is accessible, try restarting it') def is_connected(self): return self.connected def getData(self, commandName, args=None): output = None if commandName == 'commands': commandList = [] for commandName, commandData in self.config['commands'].items(): commandList.append({ 'name': commandName, 'method': 'GET' if commandData.get('result') else 'PUT' }) output = {'driver': __name__, 'commands': commandList} elif commandName == 'get_app_list': output = { 'driver': __name__, 'command': 'get_app_list', 'output': self.getCommandList() } elif commandName == 'get_current_activity': output = { 'driver': __name__, 'command': 'get_current_activity', 'output': self.getCurrentActivity() } if output: output['result'] = self.paramParser.translate_param( self.config['commands'][commandName], output['output'], '') return output raise Exception('Invalid command for ' + __name__ + ': ' + commandName) def executeCommand(self, commandName, args=None): if not self.connected: self.connect() if commandName == 'start_app': args = self.paramParser.translate_param( self.config['commands'][commandName], args) output = subprocess.check_output( [self.executable, 'shell', 'am', 'start', '-n', args]).decode() else: output = subprocess.check_output([ self.executable, 'shell', 'input', 'keyevent', str(self.config['commands'][commandName]['code']) ]).decode() result = {'driver': __name__, 'command': commandName, 'output': output} if args: result['args'] = args return result def process_result(self, commandName, command, result): if 'argKey' in command: param = result['output']['payload'][command['argKey']] result['result'] = self.paramParser.translate_param(command, param) def getCurrentActivity(self): output = subprocess.check_output( [self.executable, 'shell', 'dumpsys', 'window', 'windows']).decode().split('\n') for line in output: pos = line.find('mFocusedApp') if pos > 0: pos = line.find(Android.ACTIVITY_RECORD) if pos > 0: values = line[pos + len(Android.ACTIVITY_RECORD):].split(' ') return values[2].split('/')[0] return '' def getCommandList(self): if not self.connected: self.connect() output = subprocess.check_output( [self.executable, 'shell', 'pm', 'list', 'packages', '-f']).decode().split('\n') appList = [] for line in output: pos = line.find('=') if pos > 0: appList.append({'appName': line[pos + 1:], 'activity': []}) for appInfo in appList: self.logger.debug("Processing %s app", appInfo['appName']) output = subprocess.check_output( [self.executable, 'shell', 'pm', 'dump', appInfo['appName']]).decode().split('\n') for index, line in enumerate(output): if line.find('MAIN') > 0: activityLine = output[index - 1] pos = activityLine.find(appInfo['appName'] + '/') if pos > 0: appInfo['activity'].append( activityLine[activityLine.find('/', pos) + 1:activityLine.find(' ', pos)]) return appList
class WebOS(WebSocketClient): def __init__(self, config, logger, use_numeric_key=False): super(WebOS, self).__init__("ws://" + config['hostName'] + ':' + str(config['port']), exclude_headers=["Origin"]) self.config = config self.connected = False self.connectEvent = Event() self.curID = 0 self.callbacks = {} self.clientKey = None self.logger = logger self.paramParser = ParamParser(config, use_numeric_key) try: with open(config['clientKeyFile'], 'r') as clientKeyInput: self.clientKey = yaml.load(clientKeyInput) except: pass try: with open(config['macFile'], 'r') as macInput: self.config.update(yaml.load(macInput)) except: pass self.sock.settimeout(5) try: self.connect() except: logger.debug('Websocket timeout. Possibly device is off') logger.info('Loaded %s driver', __name__) def start(self): pass def saveClientKey(self): with open(self.config['clientKeyFile'], 'w') as clientKeyOutput: yaml.safe_dump(self.clientKey, clientKeyOutput, allow_unicode=True, encoding='utf-8') clientKeyOutput.close() def opened(self): self.connected = True if self.clientKey: self.config['registerCommand'].update(self.clientKey) self.sendCommand('register', 'register', None, self.config['registerCommand'], False) def closed(self, code, reason=None): self.connected = False self.connectEvent.clear() self.logger.warn('LG TV Connection closed %s, %s', code, reason) def received_message(self, data): message = json.loads(str(data)) if message['id'] == 'register0': if message['type'] == 'error': self.logger.error('Connection issue %s', message['error']) elif message['type'] == 'registered': if not self.clientKey: self.clientKey = message['payload'] self.saveClientKey() self.connectEvent.set() callback = self.callbacks.get(message['id']) if callback: callback['data'] = message callback['event'].set() def is_connected(self): if not self.connected: try: self.connect() self.connectEvent.wait(self.config['timeout'] if self.clientKey else self.config['promptTimeout']) except: pass return self.connected def sendCommand(self, prefix, type, uri, payload=None, shouldWait=True): if not self.connected: try: self.connect() self.connectEvent.wait(self.config['timeout'] if self.clientKey else self.config['promptTimeout']) except: raise Exception('Driver ' + __name__ + ' cannot connect to device') id = prefix + str(self.curID) message = {'type': type, 'id': id} if payload: message['payload'] = payload if uri: message['uri'] = 'ssap://' + uri self.send(json.dumps(message)) self.curID += 1 event = Event() callback = {'event': event, 'data': {}} self.callbacks[id] = callback if shouldWait: event.wait(self.config['timeout']) return callback['data'] return '' def getData(self, commandName, args=None): if commandName == 'commands': commandList = [] for commandName, command in self.config['commands'].items(): commandList.append({ 'name': commandName, 'method': 'GET' if command.get('result', False) else 'PUT' }) return {'driver': __name__, 'commands': commandList} command = self.config['commands'][commandName] if not command.get('result'): raise Exception('Invalid command for ' + __name__ + ' and method: ' + commandName) result = { 'driver': __name__, 'command': commandName, 'output': self.sendCommand('', 'request', command['uri']) } self.process_result(commandName, command, result) return result def executeCommand(self, commandName, args=None): if commandName == 'toggle_mute': output = self.getData('status') if 'error' in output['output']: return output return self.executeCommand( 'mute', 'off' if output['output']['payload']['mute'] else 'on') elif commandName == 'power_on': self.logger.debug('Sending wake up command to %s', self.config['mac']) wakeonlan.send_magic_packet(self.config['mac']) return {'driver': __name__, 'command': commandName, 'output': ''} command = self.config['commands'][commandName] if command.get('result'): raise Exception('Invalid command for ' + __name__ + ' and method: ' + commandName) argKey = command.get('argKey') argData = None if args: if not argKey: raise Exception('Command in ' + __name__ + ': ' + commandName + ' isn' 't configured for arguments') args = self.paramParser.translate_param(command, args) argData = {argKey: args} if command.get('acceptsBool') and type(args) is not bool: argData[argKey] = args == 'true' or args == 'on' elif command.get('acceptsNumber'): try: argData[argKey] = int(args) except ValueError: pass elif argKey: raise Exception('Command in ' + __name__ + ': ' + commandName + ' expects for arguments') result = { 'driver': __name__, 'command': commandName, 'output': self.sendCommand('', 'request', command['uri'], argData), 'args': argData } return result def process_result(self, commandName, command, result): if 'argKey' in command: param = result['output']['payload'][command['argKey']] result['result'] = self.paramParser.translate_param(command, param)
def __init__(self, config, logger, use_numeric_key=False): self.config = config self.logger = logger self.paramParser = ParamParser(config, use_numeric_key) logger.info('Loaded %s driver', __name__)
class TivoIP(object): def __init__(self, config, logger, use_numeric_key=False): self.config = config self.logger = logger self.paramParser = ParamParser(config, use_numeric_key) logger.info('Loaded %s driver', __name__) def start(self): pass def getData(self, commandName, args=None): if commandName == 'commands': commandList = [] for commandName in self.config['commands'].keys(): commandList.append({'name': commandName, 'method': 'PUT'}) return {'driver': __name__, 'commands': commandList} raise Exception('Invalid command for ' + __name__ + ': ' + commandName) def is_connected(self): try: conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) conn.connect((self.config['hostName'], self.config['port'])) conn.settimeout(self.config['timeout']) conn.close() return True except: pass return False def sendCommand(self, command, args=None): result = '' conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: conn.connect((self.config['hostName'], self.config['port'])) conn.settimeout(self.config['timeout']) conn.send(command['code'].encode()) if (command.get('argument') or 'value_set' in command) and args: conn.send( self.paramParser.translate_param(command, args).encode()) conn.send('\r\n'.encode()) if command.get('response', False): result = conn.recv(1024).decode() delay = command.get('delay') if delay: time.sleep(delay) except socket.timeout: pass conn.close() return result def executeCommand(self, commandName, args=None): self.logger.debug('Driver %s executing command %s', __name__, commandName) output = [] command = self.config['commands'][commandName] if 'commands' in command: for item in command['commands']: output.append(self.sendCommand(item, args)) else: output.append(self.sendCommand(command, args)) result = {'driver': __name__, 'command': commandName, 'output': output} if args: result['args'] = args return result