class SqueezeBox(Plugin): def __init__(self): self.deviceManager = DeviceManager(self.context) self.loaded = False if self.config('hostname') != '': self.setHostname(self.config('hostname')) def configWasUpdated(self, key, value): if key == 'hostname': self.setHostname(value) def setHostname(self, hostname): if self.loaded: logging.warning('Cannot change hostname, without a restart') return self.sc = Server(hostname=hostname) try: self.sc.connect() except: logging.error("Cannot connect to squeezebox server") return for player in self.sc.players: self.deviceManager.addDevice(Player(player)) self.deviceManager.finishedLoading('squeezebox') self.loaded = True
class Eliq(Plugin): def __init__(self): self.deviceManager = DeviceManager(self.context) self.sensor = None Application().registerScheduledTask(self.__requestNewValue, minutes=1, runAtOnce=True) def __requestNewValue(self): accessToken = self.config('accessToken') if accessToken == '': return try: eliq = eliqonline.API(accessToken) data_now = eliq.get_data_now() except HTTPError as e: # Something wrong with our request apparantly logging.error('Could not request Eliq value %s', e) return except Exception as e: # Something wrong with our request apparantly logging.error('Could not request Eliq value %s', e) return if self.sensor is None: self.sensor = EliqSensor(data_now.channelid) self.sensor.setSensorValue(Sensor.WATT, data_now.power, Sensor.SCALE_POWER_WATT) self.deviceManager.addDevice(self.sensor) self.deviceManager.finishedLoading('eliq') else: self.sensor.setSensorValue(Sensor.WATT, data_now.power, Sensor.SCALE_POWER_WATT)
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 __init__(self): self.loaded = False self.armState = None self.alarmDevice = AlarmDevice() deviceManager = DeviceManager(self.context) deviceManager.addDevice(self.alarmDevice) self.devices = {} Application().registerScheduledTask(fn=self.__fetch, minutes=10, runAtOnce=True)
def __init__(self): self.sensor = YRSensor() deviceManager = DeviceManager(self.context) deviceManager.addDevice(self.sensor) deviceManager.finishedLoading('yr') Application().registerScheduledTask(self.__requestWeather, hours=1, runAtOnce=True)
def __init__(self): self.lastCheckedDate = None self.device = HolidayDevice() deviceManager = DeviceManager(self.context) deviceManager.addDevice(self.device) deviceManager.finishedLoading('holiday') Application().registerScheduledTask(self.checkDay, minutes=1, runAtOnce=False)
def discover(self): deviceManager = DeviceManager(self.context) for light in self.lifxClient.get_devices(): try: device = LifxDevice(light) except Exception: continue deviceManager.addDevice(device) deviceManager.finishedLoading('lifx')
def __scanDevices(self): l = soco.discover() if l is None: # No found, this could be an error. Don't remove all old. Try to check them manually self.__loadCached() return deviceManager = DeviceManager(self.context) for s in l: deviceManager.addDevice(SonosDevice(s)) deviceManager.finishedLoading('sonos')
class Dummy(Plugin): '''This is the plugins main entry point and is a singleton Manage and load the plugins here ''' def __init__(self): # The devicemanager is a globally manager handling all device types self.deviceManager = DeviceManager(self.context) # Load all devices this plugin handles here. Individual settings for the devices # are handled by the devicemanager self.deviceManager.addDevice(DummyDevice()) # When all devices has been loaded we need to call finishedLoading() to tell # the manager we are finished. This clears old devices and caches self.deviceManager.finishedLoading('dummy')
class Lifx(Plugin): def __init__(self): self.deviceManager = DeviceManager(self.context) self.lifxClient = lifx.Client() Timer(2.0, self.discover).start() @mainthread def discover(self): for light in self.lifxClient.get_devices(): try: d = LifxDevice(light) except Exception: continue self.deviceManager.addDevice(d) self.deviceManager.finishedLoading('lifx')
def parseValues(overview): # Running in the main thread deviceManager = DeviceManager(self.context) self.armState = overview.get('armState', {}).get('statusType', '') # Main box if self.alarmDevice.updateStatus(overview['armState']): # Send signal self.verisureArmStateChanged(overview['armState']['date'], overview['armState']['statusType'], overview['armState']['name'], overview['armState']['changedVia']) # Door/Window sensors for door in overview.get('doorWindow', {}).get('doorWindowDevice', []): deviceLabel = door['deviceLabel'] if deviceLabel not in self.devices: device = DoorWindowDevice(door) self.devices[deviceLabel] = device deviceManager.addDevice(device) else: device = self.devices[deviceLabel] device.updateStatus(door) # Climate values for climate in overview.get('climateValues', []): deviceLabel = climate['deviceLabel'] if deviceLabel not in self.devices: device = ClimateDevice(climate) self.devices[deviceLabel] = device deviceManager.addDevice(device) else: device = self.devices[deviceLabel] device.updateValues(climate) # Smart plugs for plug in overview.get('smartPlugs', []): deviceLabel = plug['deviceLabel'] if deviceLabel not in self.devices: device = SmartPlugDevice(plug, self) self.devices[deviceLabel] = device deviceManager.addDevice(device) else: device = self.devices[deviceLabel] device.updateValues(plug) # Door locks for lock in overview.get('doorLockStatusList', []): deviceLabel = lock['deviceLabel'] if deviceLabel not in self.devices: device = DoorLockDevice(lock) self.devices[deviceLabel] = device deviceManager.addDevice(device) else: device = self.devices[deviceLabel] device.updateStatus(lock) if self.loaded == False: deviceManager.finishedLoading('verisure') self.loaded = True
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 WeMo(Plugin): def __init__(self): self.devices = {} self.deviceManager = DeviceManager(self.context) Thread(target=self.discover).start() def addDevice(self, DeviceType, device): if device.serialnumber in self.devices: return d = DeviceType(device) self.deviceManager.addDevice(d) self.devices[device.serialnumber] = d def bridgeFound(self, bridge): if bridge.serialnumber in self.devices: return self.devices[bridge.serialnumber] = bridge for light in bridge.Lights: d = WeMoLight(bridge.Lights[light]) self.deviceManager.addDevice(d) def discover(self): devices = pywemo.discover_devices() for device in devices: if isinstance(device, pywemo.Bridge): Application().queue(self.bridgeFound, device) elif isinstance(device, pywemo.Switch): Application().queue(self.switchFound, device) elif isinstance(device, pywemo.Motion): Application().queue(self.motionFound, device) Application().queue(self.deviceManager.finishedLoading, 'wemo') def motionFound(self, motion): logging.info("WeMo motion found: %s", motion) # TODO(micke): Implement this when we have a sample def switchFound(self, switch): self.addDevice(WeMoSwitch, switch)
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 Temperature(Plugin): '''This is the plugins main entry point and is a singleton Manage and load the plugins here ''' def __init__(self): # The devicemanager is a globally manager handling all device types self.deviceManager = DeviceManager(self.context) # Load all devices this plugin handles here. Individual settings for the devices # are handled by the devicemanager self.sensor = TemperatureSensor() self.deviceManager.addDevice(self.sensor) # When all devices has been loaded we need to call finishedLoading() to tell # the manager we are finished. This clears old devices and caches self.deviceManager.finishedLoading('temperature') Application().registerScheduledTask(self.updateValues, minutes=1, runAtOnce=True) def updateValues(self): self.sensor.updateValue()
def loadDevices(self): deviceManager = DeviceManager(self.context) for dev in TPLinkSmartHomeProtocol.discover(): plug = SmartPlug(dev['ip']) deviceManager.addDevice(TPLinkDevice(plug)) deviceManager.finishedLoading('tplink')
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 Netatmo(Plugin): implements(IWebRequestHandler) implements(IWebReactHandler) supportedTypes = { 'Temperature': (Sensor.TEMPERATURE, Sensor.SCALE_TEMPERATURE_CELCIUS), 'Humidity': (Sensor.HUMIDITY, Sensor.SCALE_HUMIDITY_PERCENT), #'CO2': (Sensor.UNKNOWN, Sensor.SCALE_UNKNOWN), #'Noise':, 'Pressure': (Sensor.BAROMETRIC_PRESSURE, Sensor.SCALE_BAROMETRIC_PRESSURE_KPA), 'Rain': (Sensor.RAINRATE, Sensor.SCALE_RAINRATE_MMH), 'sum_rain_24': (Sensor.RAINTOTAL, Sensor.SCALE_RAINTOTAL_MM), 'WindAngle': (Sensor.WINDDIRECTION, Sensor.SCALE_WIND_DIRECTION), 'WindStrength': (Sensor.WINDAVERAGE, Sensor.SCALE_WIND_VELOCITY_MS), 'GustStrength': (Sensor.WINDGUST, Sensor.SCALE_WIND_VELOCITY_MS), } products = { # 'NAMain': {} # Base station 'NAModule1': { 'batteryMax': 6000, 'batteryMin': 3600 }, # Outdoor module 'NAModule4': { 'batteryMax': 6000, 'batteryMin': 4200 }, # Additional indoor module 'NAModule3': { 'batteryMax': 6000, 'batteryMin': 3600 }, # Rain gauge 'NAModule2': { 'batteryMax': 6000, 'batteryMin': 3950 }, # Wind gauge # 'NAPlug': {}, # Thermostat relay/plug # 'NATherm1': {}, # Thermostat module } def __init__(self): self.deviceManager = DeviceManager(self.context) self.sensors = {} self.loaded = False self.clientId = '' self.clientSecret = '' config = self.config('oauth') self.accessToken = config.get('accessToken', '') self.refreshToken = config.get('refreshToken', '') if self.accessToken is not '': self.configuration['oauth'].activated = True self.tokenTTL = config.get('tokenTTL', 0) Application().registerScheduledTask(self.__requestNewValues, minutes=10, runAtOnce=True) def getReactComponents(self): return { 'netatmo': { 'title': 'Netatmo', 'script': 'netatmo/netatmo.js', } } def matchRequest(self, plugin, path): if plugin != 'netatmo': return False if path in ['activate', 'code', 'logout']: return True return False def handleRequest(self, plugin, path, params, request, **kwargs): # Web requests if path in ['activate', 'code']: service = rauth.OAuth2Service( client_id=self.clientId, client_secret=self.clientSecret, access_token_url='https://api.netatmo.net/oauth2/token', authorize_url='https://api.netatmo.net/oauth2/authorize') if path == 'activate': params = { 'redirect_uri': '%s/netatmo/code' % request.base(), 'response_type': 'code' } url = service.get_authorize_url(**params) return WebResponseJson({'url': url}) if path == 'code': data = { 'code': params['code'], 'grant_type': 'authorization_code', 'redirect_uri': '%s/netatmo/code' % request.base() } session = service.get_auth_session( data=data, decoder=self.__decodeAccessToken) return WebResponseRedirect('%s/plugins?settings=netatmo' % request.base()) if path == 'logout': self.accessToken = '' self.refreshToken = '' self.tokenTTL = 0 self.setConfig( 'oauth', { 'accessToken': self.accessToken, 'refreshToken': self.refreshToken, 'tokenTTL': self.tokenTTL, }) self.configuration['oauth'].activated = False return WebResponseJson({'success': True}) return None def __addUpdateDevice(self, data): if data['_id'] not in self.sensors: sensor = NetatmoModule(data['_id'], data['module_name'], data['type']) self.deviceManager.addDevice(sensor) self.sensors[data['_id']] = sensor else: sensor = self.sensors[data['_id']] for dataType in Netatmo.supportedTypes: if dataType not in data['dashboard_data']: continue valueType, scale = Netatmo.supportedTypes[dataType] value = data['dashboard_data'][dataType] if dataType == 'WindStrength' or dataType == 'GustStrength': value = round(value / 3.6, 2) # Data is reported in km/h, we want m/s elif dataType == 'Pressure': value = round(value / 10.0) # Data is reported in mbar, we want kPa sensor.setSensorValue(valueType, value, scale) if 'battery_vp' in data and data['type'] in Netatmo.products: product = Netatmo.products[data['type']] battery = 1.0 * max(min(data['battery_vp'], product['batteryMax']), product['batteryMin']) sensor.batteryLevel = int( (battery - product['batteryMin']) / (product['batteryMax'] - product['batteryMin']) * 100) @mainthread def __parseValues(self, data): if 'body' not in data: return body = data['body'] if 'devices' not in body: return devices = body['devices'] for device in devices: self.__addUpdateDevice(device) for module in device['modules']: self.__addUpdateDevice(module) if self.loaded == False: self.loaded = True self.deviceManager.finishedLoading('netatmo') def __requestNewValues(self): if self.accessToken == '': return def backgroundTask(): service = rauth.OAuth2Service( client_id=self.clientId, client_secret=self.clientSecret, access_token_url='https://api.netatmo.net/oauth2/token') if time.time() > self.tokenTTL: session = self.__requestSession(service) else: session = rauth.OAuth2Session(self.clientId, self.clientSecret, access_token=self.accessToken, service=service) response = session.get( 'https://api.netatmo.com/api/getstationsdata') data = response.json() if 'error' in data and data['error']['code'] in [2, 3]: # Token is expired. Request new session = self.__requestSession(service) response = session.get( 'https://api.netatmo.com/api/getstationsdata') data = response.json() self.__parseValues(data) Thread(target=backgroundTask).start() def __requestSession(self, service): data = { 'grant_type': 'refresh_token', 'refresh_token': self.refreshToken } session = service.get_auth_session(data=data, decoder=self.__decodeAccessToken) return session def __decodeAccessToken(self, data): response = json.loads(data) self.accessToken = response['access_token'] self.refreshToken = response['refresh_token'] self.tokenTTL = int(time.time()) + response['expires_in'] self.setConfig( 'oauth', { 'accessToken': self.accessToken, 'refreshToken': self.refreshToken, 'tokenTTL': self.tokenTTL, }) self.configuration['oauth'].activated = True return response
class SceneManager(Plugin): implements(ITelldusLiveObserver) def __init__(self): self.scenes = {} self.deviceManager = DeviceManager(self.context) for sceneId in self.config('scenes'): device = SceneDevice(sceneId) self.scenes[sceneId] = device self.deviceManager.addDevice(device) self.deviceManager.finishedLoading('scene') def addDevice(self, name, devices): if type(devices) != dict: return sceneId = str(uuid.uuid4()) device = SceneDevice(sceneId) device.setName(name) device.setParams({'devices': devices}) self.scenes[sceneId] = device self.deviceManager.addDevice(device) scenes = self.config('scenes') scenes.append(sceneId) self.setConfig('scenes', scenes) @TelldusLive.handler('scene') def __handleCommand(self, msg): data = msg.argument(0).toNative() action = data['action'] if action == 'addScene': self.addDevice(data['name'], data['devices']) elif action == 'editScene': deviceId = data['device'] for sceneId in self.scenes: device = self.scenes[sceneId] if device.id() == deviceId: device.setParams({ 'devices': data['devices'], }) device.paramUpdated('') break elif action == 'sceneInfo': deviceId = data['device'] for sceneId in self.scenes: device = self.scenes[sceneId] if device.id() == deviceId: params = device.params() params['deviceId'] = deviceId live = TelldusLive(self.context) live.pushToWeb('scene', 'sceneInfo', params) return elif action == 'remove': deviceId = data['device'] for sceneId in self.scenes: device = self.scenes[sceneId] if device.id() == deviceId: self.deviceManager.removeDevice(deviceId) del self.scenes[sceneId] return
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
class Hue(Plugin): implements(IWebRequestHandler) implements(IWebReactHandler) implements(ISSDPNotifier) STATE_NO_BRIDGE, STATE_UNAUTHORIZED, STATE_AUTHORIZED = range(3) def __init__(self): self.deviceManager = DeviceManager(self.context) self.username = None self.ssdp = None config = self.config('bridge') self.activated = config.get('activated', False) self.username = config.get('username', '') self.bridge = config.get('bridge', '') self.state = Hue.STATE_NO_BRIDGE self.lights = {} self.ssdp = SSDP(self.context) if self.activated: Application().queue(self.selectBridge, config.get('bridge')) Application().registerScheduledTask(self.update, minutes=1) @mainthread def authorize(self): if self.username is None or self.username == '': data = self.doCall('POST', '/api', '{"devicetype": "Telldus#TellStick"}') resp = data[0] if resp.get('error', {}).get('type', None) == 101: # Unauthorized, the user needs to press the button. Try again in 5 seconds thread = threading.Timer(5.0, self.authorize) thread.name = 'Philips Hue authorization poll timer' thread.daemon = True thread.start() return if 'success' in resp: self.username = resp['success']['username'] self.activated = True self.saveConfig() self.setState(Hue.STATE_AUTHORIZED) else: return # Check if username is ok data = self.doCall('GET', '/api/%s/lights' % self.username) self.setState(Hue.STATE_AUTHORIZED) self.parseLights(data) self.deviceManager.finishedLoading('hue') def doCall(self, requestType, endpoint, body='', bridge=None): if bridge is None: bridge = self.bridge conn = httplib.HTTPConnection(bridge) try: conn.request(requestType, endpoint, body) except Exception: return [{'error': 'Could not connect'}] response = conn.getresponse() try: rawData = response.read() data = json.loads(rawData) except Exception: logging.warning("Could not parse JSON") logging.warning("%s", rawData) return [{'error': 'Could not parse JSON'}] return data def getReactComponents(self): # pylint: disable=R0201 return { 'hue': { 'title': 'Philips Hue', 'script': 'hue/hue.js', } } def matchRequest(self, plugin, path): # pylint: disable=R0201 if plugin != 'hue': return False if path in ['reset', 'state']: return True return False def handleRequest(self, plugin, path, __params, **__kwargs): if plugin != 'hue': return None if path == 'state': if self.state == Hue.STATE_NO_BRIDGE: # If ssdp fails to detect, use the hue remote service self.searchNupnp() return WebResponseJson({'state': self.state}) if path == 'reset': self.setState(Hue.STATE_NO_BRIDGE) return WebResponseJson({'success': True}) def parseLights(self, lights): if isinstance(lights, list) and len(lights) and 'error' in lights[0]: return False oldDevices = self.lights.keys() for i in lights: lightData = lights[i] if 'uniqueid' not in lightData: continue lightId = lightData['uniqueid'] if lightId in oldDevices: # Find any removed lights oldDevices.remove(lightId) name = lightData.get('name', '') if lightId in self.lights: light = self.lights[lightId] if light.name() != name: light.setName(name) else: light = Light(lightId, i, self) self.lights[lightId] = light if 'type' in lightData: light.setType(lightData['type']) self.deviceManager.addDevice(light) light.setName(name) if 'state' in lightData: state = lightData['state'] ourState, ourStateValue = light.state() if state['on'] is False: hueState = Device.TURNOFF hueStateValue = ourStateValue elif state['bri'] == 254: hueState = Device.TURNON hueStateValue = ourStateValue else: hueState = Device.DIM hueStateValue = state['bri'] if ourState != hueState or ourStateValue != hueStateValue: light.setState(hueState, hueStateValue) for lightId in oldDevices: light = self.lights[lightId] self.deviceManager.removeDevice(light.id()) del self.lights[lightId] def saveConfig(self): self.setConfig( 'bridge', { 'bridge': self.bridge, 'username': self.username, 'activated': self.activated, }) def searchNupnp(self): conn = httplib.HTTPSConnection('www.meethue.com') conn.request('GET', '/api/nupnp') response = conn.getresponse() try: rawData = response.read() data = json.loads(rawData) except Exception: logging.warning("Could not parse JSON") logging.warning("%s", rawData) return for bridge in data: if 'internalipaddress' not in bridge: continue self.selectBridge(bridge['internalipaddress']) return def setState(self, newState): if newState == Hue.STATE_NO_BRIDGE: self.bridge = None self.username = None self.activated = False self.saveConfig() elif newState == Hue.STATE_UNAUTHORIZED: Application().queue(self.authorize) elif newState == Hue.STATE_AUTHORIZED: pass self.state = newState # Notify websocket Server(self.context).webSocketSend('hue', 'status', {'state': self.state}) def ssdpDeviceFound(self, device): if device.type != 'basic:1': return url = urlparse.urlparse(device.location) if self.state == Hue.STATE_NO_BRIDGE: self.selectBridge(url.netloc) elif self.state == Hue.STATE_AUTHORIZED: if url.netloc == self.bridge: return # Ip to bridge may have has changed data = self.doCall('GET', '/api/%s/lights' % self.username, bridge=url.netloc) try: if 'error' in data[0]: return except Exception as __error: pass # Save new address self.bridge = url.netloc self.saveConfig() self.parseLights(data) def selectBridge(self, urlbase): if urlbase == '' or urlbase is None: self.setState(Hue.STATE_NO_BRIDGE) return self.bridge = urlbase self.saveConfig() self.setState(Hue.STATE_UNAUTHORIZED) def tearDown(self): self.deviceManager.removeDevicesByType('hue') def update(self): if self.state != Hue.STATE_AUTHORIZED: # Skip return data = self.doCall('GET', '/api/%s/lights' % self.username) self.parseLights(data)