def run_discovery(self): self.debug('discover devices') try: # publish devices publishedDevices = [self.publish_hub_device()] deviceManager = DeviceManager(self.context) devices = deviceManager.retrieveDevices() for device in devices: try: self.debug(json.dumps({ 'deviceId': device.id(), 'type': self.getDeviceType(device), 'name': device.name(), 'isDevice': device.isDevice(), 'isSensor': device.isSensor(), 'methods': device.methods(), 'battery': device.battery(), 'parameters': device.allParameters() if hasattr(device, 'allParameters') else device.parameters(), 'typeStr': device.typeString(), 'sensors': device.sensorValues(), 'state': device.state() })) publishedDevices.extend(self.discovery(device)) except Exception as e: self.debug('run_discovery device exception %s' % str(e)) for type, devId, fullId in list(set(self.getKnownDevices()) - set(publishedDevices)): self.remove_discovery(type, devId, fullId) self.setKnownDevices(publishedDevices) except Exception as e: self.debug('run_discovery exception %s' % str(e))
def loadAccessories(self): self.accessories[1] = HapBridgeAccessory() deviceManager = DeviceManager(HapConnection.HTTPDServer.context) for device in deviceManager.retrieveDevices(): if not device.confirmed(): continue self.deviceAdded(device)
class Group(Plugin): implements(ITelldusLiveObserver) def __init__(self): self.devices = [] self.deviceManager = DeviceManager(self.context) for d in self.deviceManager.retrieveDevices('group'): p = d.params() device = GroupDevice() self.devices.append(device) device.setNodeId(d.id()) device.setParams(p) self.deviceManager.addDevice(device) self.deviceManager.finishedLoading('group') self.live = TelldusLive(self.context) def addDevice(self, name, devices): if type(devices) != list: return device = GroupDevice() device.setName(name) device.setParams({ 'devices': devices }) self.devices.append(device) self.deviceManager.addDevice(device) @TelldusLive.handler('group') def __handleCommand(self, msg): data = msg.argument(0).toNative() action = data['action'] if action == 'addGroup': self.addDevice(data['name'], data['devices']) elif action == 'editGroup': deviceId = data['device'] for device in self.devices: if device.id() == deviceId: device.setParams({ 'devices': data['devices'], }) device.paramUpdated('') break elif action == 'groupInfo': deviceId = data['device'] for device in self.devices: if device.id() == deviceId: params = device.params() params['deviceId'] = deviceId self.live.pushToWeb('group', 'groupInfo', params) return elif action == 'remove': deviceId = data['device'] for device in self.devices: if device.id() == deviceId: self.deviceManager.removeDevice(deviceId) self.devices.remove(device) return
def discover(self): self.discovered_flag = False self._debug('Discovering devices ...') self.devices = self.staticDevices + [] devMgr = DeviceManager(self.context) for device in devMgr.retrieveDevices(): haDevs = devs.createDevices(device, self.hub, self._buildTopic, self.config('use_via')) self._debug('Discovered %s' % json.dumps(self._debugDevice(device, haDevs))) for haDev in haDevs: self.devices.append(haDev) self.discovered_flag = True self._debug('Discovered %s devices' % len(self.devices)) Application().queue(self.cleanupDevices)
def __loadCached(self): deviceManager = DeviceManager(self.context) l = deviceManager.retrieveDevices('sonos') if len(l) == 0: # No cached, retry search in 10 minutes timer = Timer(600, self.__scanDevices) timer.daemon = True timer.start() return for dev in l: ip = dev.params().get('ip_address', None) if ip is None: continue try: s = soco.SoCo(ip) s.uid # Will throw exception if not available deviceManager.addDevice(SonosDevice(s)) except ConnectionError: continue
class Broadlink(Plugin): def __init__(self): self.deviceManager = DeviceManager(self.context) self.devices = None thread = Thread(target=self.detectBroadlink, name="Detect Broadlink Devices") thread.start() def detectBroadlink(self): self.devices = broadlink.discover(timeout=5) for device in self.devices: self.deviceManager.addDevice(BroadDevice(device)) self.deviceManager.finishedLoading('broadlink') Application().registerScheduledTask(self.updateValues, seconds=300, runAtOnce=True) def tearDown(self): self.deviceManager.removeDevicesByType('broadlink') def updateValues(self): for device in self.deviceManager.retrieveDevices("broadlink"): device.updateValue()
class RF433(Plugin): implements(ITelldusLiveObserver) fwVersions = {'18F25K50': 1} def __init__(self): self.version = 0 self.hwVersion = None self.devices = [] self.sensors = [] self.rawEnabled = False self.rawEnabledAt = 0 self.dev = Adapter(self, Board.rf433Port()) deviceNode = DeviceNode(self.dev) self.deviceManager = DeviceManager(self.context) self.registerSensorCleanup() for d in self.deviceManager.retrieveDevices('433'): p = d.params() if 'type' not in p: continue if p['type'] == 'sensor': device = SensorNode() self.sensors.append(device) elif p['type'] == 'device': device = DeviceNode(self.dev) self.devices.append(device) else: continue device.setNodeId(d.id()) device.setParams(p) if p['type'] == 'sensor': device._packageCount = 7 # already loaded, keep it that way! device._sensorValues = d._sensorValues device.batteryLevel = d.batteryLevel self.deviceManager.addDevice(device) self.deviceManager.finishedLoading('433') self.dev.queue( RF433Msg('V', success=self.__version, failure=self.__noVersion)) self.dev.queue( RF433Msg('H', success=self.__hwVersion, failure=self.__noHWVersion)) self.live = TelldusLive(self.context) def addDevice(self, protocol, model, name, params): device = DeviceNode(self.dev) device.setName(name) device.setParams({ 'protocol': protocol, 'model': model, 'protocolParams': params }) self.devices.append(device) self.deviceManager.addDevice(device) def cleanupSensors(self): numberOfSensorsBefore = len(self.sensors) for i, sensor in enumerate(self.sensors): if not sensor.isValid(): self.deviceManager.removeDevice(sensor.id()) del self.sensors[i] self.deviceManager.sensorsUpdated() @TelldusLive.handler('rf433') def __handleCommand(self, msg): data = msg.argument(0).toNative() action = data['action'] if action == 'addDevice': self.addDevice(data['protocol'], data['model'], data['name'], data['parameters']) elif action == 'deviceInfo': deviceId = data['device'] for device in self.devices: if device.id() == deviceId: params = device.params() params['deviceId'] = deviceId self.live.pushToWeb('rf433', 'deviceInfo', params) return elif action == 'editDevice': deviceId = data['device'] for device in self.devices: if device.id() == deviceId: device.setParams({ 'protocol': data['protocol'], 'model': data['model'], 'protocolParams': data['parameters'] }) device.paramUpdated('') break elif action == 'remove': deviceId = data['device'] for device in self.devices: if device.id() == deviceId: self.deviceManager.removeDevice(deviceId) self.devices.remove(device) return elif action == 'rawEnabled': if data['value']: self.rawEnabled = True self.rawEnabledAt = time.time() else: self.rawEnabled = False else: logging.warning("Unknown rf433 command %s", action) @signal('rf433RawData') def decode(self, msg): """ Signal send on any raw data received from 433 receiver. Please note that the TellStick must contain a receiver for this signal to be sent. Not all models contains a receiver. """ if 'class' in msg and msg['class'] == 'sensor': self.decodeSensor(msg) return msg = Protocol.decodeData(msg) for m in msg: self.decodeCommandData(m) if self.rawEnabled: if self.rawEnabledAt < (time.time() - 600): # timeout, only allow scan for 10 minutes at a time self.rawEnabled = False continue self.live.pushToWeb('client', 'rawData', m) def decodeCommandData(self, msg): protocol = msg['protocol'] model = msg['model'] method = msg['method'] methods = Protocol.methodsForProtocol(protocol, model) if not method & methods: return for device in self.devices: params = device.params() if params['protocol'] != protocol: continue if not method & device.methods(): continue deviceParams = params['protocolParams'] thisDevice = True for parameter in Protocol.parametersForProtocol(protocol, model): if parameter not in msg: thisDevice = False break if parameter not in deviceParams: thisDevice = False break if msg[parameter] != deviceParams[parameter]: thisDevice = False break if thisDevice: device.setState(method, None) def decodeData(self, cmd, params): if cmd == 'W': self.decode(params) elif cmd == 'V': # New version received, probably after firmware upload self.__version(params) else: logging.debug("Unknown data: %s", str(cmd)) def decodeSensor(self, msg): protocol = Protocol.protocolInstance(msg['protocol']) if not protocol: logging.error("No known protocol for %s", msg['protocol']) return data = protocol.decodeData(msg) if not data: return p = data['protocol'] m = data['model'] sensorId = data['id'] sensorData = data['values'] sensor = None for s in self.sensors: if s.compare(p, m, sensorId): sensor = s break if sensor is None: sensor = SensorNode() sensor.setParams({'protocol': p, 'model': m, 'sensorId': sensorId}) sensor.setManager(self.deviceManager) self.sensors.append(sensor) if 'battery' in data: sensor.batteryLevel = data['battery'] sensor.updateValues(sensorData) """ Register scheduled job to clean up sensors that have not been updated for a while""" def registerSensorCleanup(self): Application().registerScheduledTask(self.cleanupSensors, hours=12) # every 12th hour t = Timer(10, self.cleanupSensors) # run a first time after 10 minutes t.daemon = True t.name = 'Sensor cleanup' t.start() def __noVersion(self): logging.warning( "Could not get firmware version for RF433, force upgrade") def __noHWVersion(self): logging.warning("Could not get hw version for RF433") def __hwVersion(self, version): logging.debug("Got HW version %s", version) self.hwVersion = version if version not in RF433.fwVersions: return fwVersion = RF433.fwVersions[self.hwVersion] if fwVersion != self.version: logging.info("Version %i is to old, update firmware", self.version) # TODO: implement def __version(self, version): self.version = version logging.info("RF433 version: %i", self.version)
class Shelly(Plugin): implements(IWebReactHandler) implements(IWebRequestHandler) def __init__(self): self.last_sent_data = None self.stop_ping_loop = threading.Event() self.deviceManager = DeviceManager(self.context) Application().registerShutdown(self.shutdown) settings = Settings('tellduslive.config') self.uuid = settings['uuid'] self.logHandler = ShellyLogger(self.uuid) LOGGER.addHandler(self.logHandler) self.setupPing() LOGGER.info('Init Shelly ' + __version__) self._initPyShelly() def setupPing(self): self.ping_count = 0 self.ping_interval = PING_INTERVAL self.ping() def loop(): while not self.stop_ping_loop.wait(self.ping_interval): self.ping() self.ping_thread = threading.Thread(target=loop) self.ping_thread.daemon = True self.ping_thread.start() def _read_settings(self): settings = Settings(CONFIG) pys = self.pyShelly pys.set_cloud_settings(settings["cloud_server"], settings["cloud_auth_key"]) pys.update_status_interval = timedelta(seconds=30) pys.only_device_id = settings["only_device_id"] pys.mdns_enabled = False #settings.get('mdns', True) if not settings['logger']: LOGGER.setLevel(logging.WARNING) def _initPyShelly(self): try: self.pyShelly.close() except: pass pys = self.pyShelly = pyShelly() pys.igmpFixEnabled = True #Enable IGMP fix for ZNet pys.cb_device_added.append(self._device_added) pys.update_status_interval = timedelta(seconds=30) self._read_settings() ###### # pys.cb_block_added.append(self._block_added) # pys.cb_device_added.append(self._device_added) # pys.cb_device_removed.append(self._device_removed) pys.cb_save_cache = self._save_cache pys.cb_load_cache = self._load_cache # pys.username = conf.get(CONF_USERNAME) # pys.password = conf.get(CONF_PASSWORD) # pys.cloud_auth_key = conf.get(CONF_CLOUD_AUTH_KEY) # pys.cloud_server = conf.get(CONF_CLOUD_SERVER) # if zeroconf_async_get_instance: # pys.zeroconf = await zeroconf_async_get_instance(self.hass) # tmpl_name = conf.get(CONF_TMPL_NAME) # if tmpl_name: # pys.tmpl_name = tmpl_name # if additional_info: # pys.update_status_interval = timedelta(seconds=update_interval) # if pys.only_device_id: # pys.only_device_id = pys.only_device_id.upper() # pys.igmp_fix_enabled = conf.get(CONF_IGMPFIX) # pys.mdns_enabled = conf.get(CONF_MDNS) ### pys.start() pys.discover() def _save_cache(self, name, data): settings = Settings('Shelly.cache') settings[name] = data def _load_cache(self, name): settings = Settings('Shelly.cache') return json.loads(settings[name]) def ping(self): try: headers = { "Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain", "Connection": "close" } self.ping_count += 1 params = urllib.urlencode({ 'shelly': __version__, 'pyShelly': self.pyShelly.version(), 'uuid': self.uuid, 'pluginid': 1, 'ping': self.ping_count, 'devices': len(self.pyShelly.blocks), 'level': self.logHandler.logLevel, 'interval': self.ping_interval }) conn = httplib.HTTPConnection("api.tarra.se") conn.request("POST", "/telldus/ping", params, headers) resp = conn.getresponse() body = resp.read() resp = json.loads(body) self.logHandler.logLevel = resp['level'] self.ping_interval = resp['interval'] conn.close() except: pass @staticmethod def getReactComponents(): return { 'shelly': { 'title': 'Shelly', 'script': 'shelly/shelly.js', 'tags': ['menu'], } } def matchRequest(self, plugin, path): LOGGER.debug("MATCH %s %s", plugin, path) if plugin != 'shelly': return False #if path in ['reset', 'state']: #return True return True def _getConfig(self): settings = Settings(CONFIG) return { 'cloud_server': settings["cloud_server"], 'cloud_auth_key': settings["cloud_auth_key"] } def _getData(self, all_devs=False): shellyDevices = \ self.deviceManager.retrieveDevices(None if all_devs else "Shelly") devices = [] for d in shellyDevices: try: buttons = {} methods = d.methods() if methods & Device.TURNON: buttons["on"] = True buttons["off"] = True if methods & Device.UP: buttons["up"] = True buttons["down"] = True buttons["stop"] = True buttons["firmware"] = getattr(d, "has_firmware_update", False) dev = { 'id': d.id(), 'localid': d.localId(), 'name': d.name(), 'isDevice': d.isDevice(), 'state': d.state()[0], 'params': d.params(), 'available': False, 'buttons': buttons, 'typeName': getattr(d, 'type_name', '') } if hasattr(d, 'dev'): _dev = d.dev dev["available"] = _dev.available() if (hasattr(_dev, 'rgb') and _dev.rgb is not None): dev['rgb'] = '#' + ''.join('%02x' % v for v in _dev.rgb) if hasattr(_dev, "get_dim_value"): dev["brightness"] = _dev.get_dim_value() sensors = {} values = d.sensorValues() if 1 in values: sensors['temp'] = \ "%.1f" % float(values[1][0]['value']) if 2 in values: sensors['hum'] = \ "%.1f" % float(values[2][0]['value']) if 256 in values: sensors['consumption'] = \ "%.1f" % float(values[256][0]['value']) if sensors: dev["sensors"] = sensors devices.append(dev) except Exception as ex: LOGGER.exception("Error reading cache") devices.sort(key=lambda x: x['name']) return { 'devices': devices, 'pyShellyVer': self.pyShelly.version() if self.pyShelly else "", 'ver': __version__, 'id': self.uuid } def refreshClient(self): data = self._getData() if self.last_sent_data != data: self.last_sent_data = data Server(self.context).webSocketSend('shelly', 'refresh', data) def handleRequest(self, plugin, path, __params, **__kwargs): if path == 'list': return WebResponseJson(self._getData()) if path == "config": if __params: settings = Settings(CONFIG) for param in __params: if param in ['cloud_server', 'cloud_auth_key']: settings[param] = __params[param] self._read_settings() return WebResponseJson(self._getConfig()) if path == 'devices': devices = list( map( lambda d: { 'id': d.id, 'name': d.friendly_name(), 'unit_id': d.unit_id, 'type': d.type, 'ip_addr': d.ip_addr, 'is_device': d.is_device, 'is_sensor': d.is_sensor, 'sub_name': d.sub_name, 'state_values': d.state_values, 'state': d.state, 'device_type': d.device_type, 'device_sub_type': d.device_sub_type, 'device_nr': d.device_nr, 'master_unit': d.master_unit, 'ext_sensor': d.ext_sensor, 'info_values': d.info_values, 'friendly_name': d.friendly_name() }, self.pyShelly.devices)) return WebResponseJson(devices) if path == 'blocks': blocks = list( map( lambda d: { 'id': d.id, 'unit_id': d.unit_id, 'type': d.type, 'ip_addr': d.ip_addr, 'info_values': d.info_values }, self.pyShelly.blocks.values())) return WebResponseJson(blocks) if path == 'dump': shellyDevices = self.deviceManager.retrieveDevices() devices = list( map( lambda d: { 'id': d.id(), 'localid': d.localId(), 'name': d.name(), 'state': d.state(), 'params': d.params(), 'stateValues': d.stateValues(), 'sensorValues': d.sensorValues(), 'isDevice': d.isDevice(), 'isSensor': d.isSensor(), 'methods': d.methods(), 'parameters': d.parameters(), 'metadata': d.metadata(), 'type': d.typeString() }, shellyDevices)) return WebResponseJson({'devices': devices}) if path in [ 'turnon', 'turnoff', 'up', 'down', 'stop', 'firmware_update' ]: LOGGER.info('Request ' + path) id = __params['id'] device = self.deviceManager.device(int(id)) if path == 'turnon': if hasattr(device.dev, 'brightness'): device.dev.turn_on(brightness=100) else: device.dev.turn_on() elif path == 'turnoff': device.dev.turn_off() elif path == 'up': device.dev.up() elif path == 'down': device.dev.down() elif path == 'stop': device.dev.stop() elif path == 'firmware_update': if device.dev.block: device.dev.block.update_firmware() return WebResponseJson({}) if path == "rgb": id = __params['id'] r = __params['r'] g = __params['g'] b = __params['b'] device = self.deviceManager.device(int(id)) device.dev.set_values(rgb=[r, g, b]) self.refreshClient() return WebResponseJson({}) if path == "rename": id = __params['id'] name = __params['name'] device = self.deviceManager.device(int(id)) device.local_name = name device.update_name() self.refreshClient() return WebResponseJson({}) if path == "clean": self.deviceManager.removeDevicesByType('Shelly') self._initPyShelly() self.refreshClient() return WebResponseJson({'msg': 'Clean done'}) if path == "discover": self.pyShelly.discover() return WebResponseJson({}) if path == "addMember": LOGGER.debug("Add membership") import socket import struct mreq = struct.pack("=4sl", socket.inet_aton("224.0.1.187"), socket.INADDR_ANY) self.pyShelly._socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) return WebResponseJson({}) if path == "dropMember": LOGGER.debug("Drop membership") import socket import struct mreq = struct.pack("=4sl", socket.inet_aton("224.0.1.187"), socket.INADDR_ANY) self.pyShelly._socket.setsockopt(socket.IPPROTO_IP, socket.IP_DROP_MEMBERSHIP, mreq) return WebResponseJson({}) if path == "initSocket": self.pyShelly.init_socket() return WebResponseJson({'msg': 'init socket done'}) def _device_added(self, dev, code): LOGGER.info('Add device ' + dev.id + ' ' + str(code)) if (dev.device_type != "POWERMETER" and dev.device_type != "SWITCH" \ and dev.device_sub_type != "humidity") \ or dev.master_unit or dev.major_unit: device = ShellyDevice(dev, self) self.deviceManager.addDevice(device) def shutdown(self): if self.pyShelly is not None: self.pyShelly.close() self.pyShelly = None if self.stop_ping_loop is not None: self.stop_ping_loop.set() def tearDown(self): deviceManager = DeviceManager(self.context) deviceManager.removeDevicesByType('shelly')
class Client(Plugin): implements(ISignalObserver) def __init__(self): self.deviceManager = DeviceManager(self.context) self.client = mqtt.Client('telldus') self.client.on_connect = self.onConnect self.client.on_message = self.onMessage self.client.on_publish = self.onPublish self.client.on_subscribe = self.onSubscribe if self.config('hostname') != '': self.connect() def configWasUpdated(self, key, __value): # TODO: handle other changes if key == 'hostname': self.connect() def connect(self): if self.config('username') != '': self.client.username_pw_set(self.config('username'), self.config('password')) self.client.will_set('%s/status' % self.config('topic'), payload='Offline', qos=0, retain=True) self.client.connect_async(self.config('hostname'), self.config('port')) self.client.loop_start() def subscribeDevice(self, deviceId): self.client.subscribe('%s/device/%s/cmd' % (self.config('topic'), deviceId)) def unsubscribeDevice(self, deviceId): self.client.unsubscribe('%s/device/%s/cmd' % (self.config('topic'), deviceId)) @slot('deviceAdded') def onDeviceAdded(self, device): self.subscribeDevice(device.id()) @slot('deviceRemoved') def onDeviceRemoved(self, deviceId): self.unsubscribeDevice(deviceId) @slot('deviceStateChanged') def onDeviceStateChanged(self, device, state, stateValue, origin=None): del origin self.client.publish( '%s/device/%s/state' % (self.config('topic'), device.id()), json.dumps({ 'name': device.name(), 'state': state, 'stateValue': stateValue, })) @slot('sensorValueUpdated') def onSensorValueUpdated(self, device, valueType, value, scale): self.client.publish( '%s/sensor/%s/value' % (self.config('topic'), device.id()), json.dumps({ 'name': device.name(), 'value': FloatWrapper(value), 'valueType': valueType, 'scale': scale, })) def onConnect(self, client, userdata, flags, result): for device in self.deviceManager.retrieveDevices(): self.subscribeDevice(device.id()) self.client.publish('%s/status' % self.config('topic'), payload='Online', qos=0, retain=True) def onMessage(self, client, userdata, msg): try: data = json.loads(str(msg.payload.decode('utf-8'))) deviceId = msg.topic.split('/')[-2] # _topic_/device/_id_/cmd device = self.deviceManager.device(int(deviceId)) if device: device.command(data.get('action'), data.get('value')) except ValueError as e: logging.error('Could not decode JSON payload %s', e) except Exception as e: logging.error('Could not perform a command %s', e) def onPublish(self, client, obj, mid): pass def onSubscribe(self, client, obj, mid, granted_qos): pass
class Group(Plugin): implements(ITelldusLiveObserver) def __init__(self): self.devices = [] self.deviceManager = DeviceManager(self.context) # pylint: disable=too-many-function-args for oldDevice in self.deviceManager.retrieveDevices('group'): params = oldDevice.params() device = GroupDevice() self.devices.append(device) device.setNodeId(oldDevice.id()) device.setParams(params) self.deviceManager.addDevice(device) self.deviceManager.finishedLoading('group') self.live = TelldusLive(self.context) # pylint: disable=too-many-function-args def addDevice(self, uuid, name, devices): if not isinstance(devices, list): return device = GroupDevice() if uuid: device.setUuid(uuid) device.setName(name) device.setParams({'devices': devices}) self.devices.append(device) self.deviceManager.addDevice(device) @TelldusLive.handler('group') def __handleCommand(self, msg): data = msg.argument(0).toNative() action = data['action'] if action == 'addGroup': # Sent from web-v2 application self.addDevice(None, data['name'], data['devices']) elif action == 'addDevice': # Sent from Telldus API self.addDevice( data.get('id', None), data['name'], data.get('parameters', {}).get('devices', []) ) elif action == 'editGroup': deviceId = data['device'] for device in self.devices: if device.id() == deviceId: device.setParams({ 'devices': data['devices'], }) device.paramUpdated('devices') break elif action == 'groupInfo': deviceId = data['device'] for device in self.devices: if device.id() == deviceId: params = device.params() params['deviceId'] = deviceId self.live.pushToWeb('group', 'groupInfo', params) return elif action == 'remove': deviceId = data['device'] for device in self.devices: if device.id() == deviceId: self.deviceManager.removeDevice(deviceId) self.devices.remove(device) return