From 1a31cdca0041f93f268cfc4259795f5c0430a11b Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Thu, 13 Dec 2018 09:53:01 +0100 Subject: [PATCH 01/31] Add support for rooms Closes #218 --- telldus/src/telldus/Device.py | 29 +++++++- telldus/src/telldus/DeviceManager.py | 100 +++++++++++++++++++++------ 2 files changed, 106 insertions(+), 23 deletions(-) diff --git a/telldus/src/telldus/Device.py b/telldus/src/telldus/Device.py index 3c4c29b..29293a8 100644 --- a/telldus/src/telldus/Device.py +++ b/telldus/src/telldus/Device.py @@ -116,6 +116,7 @@ def __init__(self): self._loadCount = 0 self._name = None self._manager = None + self._room = None self._state = Device.TURNOFF self._stateValue = '' self._sensorValues = {} @@ -129,7 +130,7 @@ def id(self): def allParameters(self): """ - Similar as parameters() but this returnes more values such as the device type + Similar as parameters() but this returnes more values such as the device type and the room """ params = self.parameters() if isinstance(params, dict): @@ -144,6 +145,11 @@ def allParameters(self): except Exception as error: params['devicetype'] = Device.TYPE_UNKNOWN Application.printException(error) + if self._room is None: + # Make sure it's removed + params.pop('room', None) + else: + params['room'] = self._room return params def battery(self): # pylint: disable=R0201 @@ -261,6 +267,7 @@ def loadCached(self, olddevice): self._loadCount = 0 self.setParams(olddevice.params()) (state, stateValue) = olddevice.state() + self._room = olddevice._room self._state = state self._stateValue = stateValue self._ignored = olddevice._ignored @@ -276,6 +283,8 @@ def load(self, settings): self._name = settings['name'] if 'params' in settings: self.setParams(settings['params']) + if 'room' in settings: + self._room = settings['room'] #if 'state' in settings and 'stateValue' in settings: # self.setState(settings['state'], settings['stateValue']) @@ -350,6 +359,12 @@ def paramUpdated(self, param): def protocol(self): return self.typeString() + def room(self): + """ + :returns: The current room this device belongs to + """ + return self._room + def sensorElement(self, valueType, scale): """ :returns: a sensor value and lastUpdated-time, as a dict, of a the specified @@ -401,6 +416,18 @@ def setName(self, name): def setParams(self, params): pass + def setRoom(self, room): + """ + Adds the device to a room. + Set to None or empty string to remove from room + """ + room = None if room == '' else room + if self._room == room: + # Don't fire update if not changed + return + self._room = room + self.paramUpdated('room') + def setSensorValue(self, valueType, value, scale): self.setSensorValues([{'type': valueType, 'value':value, 'scale': scale}]) diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index d22e676..43af255 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -52,6 +52,7 @@ class DeviceManager(Plugin): def __init__(self): self.devices = [] + self.rooms = {} self.settings = Settings('telldus.devicemanager') self.nextId = self.settings.get('nextId', 0) self.live = TelldusLive(self.context) @@ -135,6 +136,8 @@ def deviceParamUpdated(self, device, param): self.__sendDeviceReport() if device.isSensor: self.__sendSensorReport() + if param == 'room': + self.__sendDeviceParameterReport(device, sendParameters=True, sendMetadata=False) def findByName(self, name): for device in self.devices: @@ -368,6 +371,19 @@ def __handleDeviceCommand(self, msg): else: dev.setName(args['name'].decode('UTF-8')) return + elif args['action'] == 'setParameter': + device = None + for dev in self.devices: + if dev.id() == args['device']: + device = dev + break + if device is None: + return + name = args.get('name', '') + value = args.get('value', '') + if name == 'room': + device.setRoom(value) + return @TelldusLive.handler('device-requestdata') def __handleDeviceParametersRequest(self, msg): @@ -375,28 +391,9 @@ def __handleDeviceParametersRequest(self, msg): device = self.device(args.get('id', 0)) if not device: return - reply = LiveMessage('device-datareport') - data = { - 'id': args['id'] - } - if args.get('parameters', 0) == 1: - parameters = json.dumps( - device.allParameters(), - separators=(',', ':'), - sort_keys=True - ) - data['parameters'] = parameters - data['parametersHash'] = hashlib.sha1(parameters).hexdigest() - if args.get('metadata', 0) == 1: - metadata = json.dumps( - device.metadata(), - separators=(',', ':'), - sort_keys=True - ) - data['metadata'] = metadata - data['metadataHash'] = hashlib.sha1(metadata).hexdigest() - reply.append(data) - self.live.send(reply) + sendParameters = True if args.get('parameters', 0) == 1 else False + sendMetadata = True if args.get('metadata', 0) == 1 else False + self.__sendDeviceParameterReport(device, sendParameters, sendMetadata) @TelldusLive.handler('reload') def __handleSensorUpdate(self, msg): @@ -429,12 +426,47 @@ def __handleSensorUpdate(self, msg): # cleaned up self.__sendSensorReport() + @TelldusLive.handler('room') + def handleRoom(self, msg): + data = msg.argument(0).toNative() + if data['action'] == 'add': + isOwner = bool(data.get('isOwner', False)) + self.rooms[data['id']] = { + 'name': data.get('name', ''), + 'parent': data.get('parent', ''), + 'color': data.get('color', ''), + 'icon': data.get('icon', ''), + 'isOwner': isOwner, + 'mode': '', # Not implemented yet + } + if self.live.registered and isOwner: + msg = LiveMessage('RoomAdded') + msg.append(self.rooms[data['id']]) + self.live.send(msg) + self.settings['rooms'] = self.rooms + return + + if data['action'] == 'remove': + room = self.rooms.pop(data['id'], None) + if room is None: + logging.warning('Room %s was not found', data['id']) + return + if not room['isOwner']: + return + if self.live.registered: + msg = LiveMessage('RoomRemoved') + msg.append({'id': data['id']}) + self.live.send(msg) + self.settings['rooms'] = self.rooms + return + def liveRegistered(self, __msg): self.registered = True self.__sendDeviceReport() self.__sendSensorReport() def __load(self): + self.rooms = self.settings.get('rooms', {}) self.store = self.settings.get('devices', []) for dev in self.store: if 'type' not in dev or 'localId' not in dev: @@ -562,6 +594,30 @@ def __sendSensorChange(self, sensorid, valueType, value): msg.append(value) self.live.send(msg) + def __sendDeviceParameterReport(self, device, sendParameters, sendMetadata): + reply = LiveMessage('device-datareport') + data = { + 'id': device.id() + } + if sendParameters: + parameters = json.dumps( + device.allParameters(), + separators=(',', ':'), + sort_keys=True + ) + data['parameters'] = parameters + data['parametersHash'] = hashlib.sha1(parameters).hexdigest() + if sendMetadata: + metadata = json.dumps( + device.metadata(), + separators=(',', ':'), + sort_keys=True + ) + data['metadata'] = metadata + data['metadataHash'] = hashlib.sha1(metadata).hexdigest() + reply.append(data) + self.live.send(reply) + def __sendSensorReport(self): if not self.live.registered: return From b31650162eaa38f4438c1811d9fc410fb3955306 Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Mon, 17 Dec 2018 12:45:19 +0100 Subject: [PATCH 02/31] Make it possible to set device specific parameters --- rf433/src/rf433/RF433.py | 6 ++++++ telldus/src/telldus/Device.py | 7 +++++++ telldus/src/telldus/DeviceManager.py | 11 +++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/rf433/src/rf433/RF433.py b/rf433/src/rf433/RF433.py index fad8efc..8f503df 100644 --- a/rf433/src/rf433/RF433.py +++ b/rf433/src/rf433/RF433.py @@ -193,6 +193,12 @@ def parameters(self): def protocol(self): return self._protocol + def setParameter(self, name, value): + if name not in ('code', 'fade', 'house', 'system', 'unit', 'units'): + return + self._protocolParams[name] = value + self.paramUpdated(name) + def setParams(self, params): self._protocol = params.setdefault('protocol', '') self._model = params.setdefault('model', '') diff --git a/telldus/src/telldus/Device.py b/telldus/src/telldus/Device.py index 29293a8..9e6121b 100644 --- a/telldus/src/telldus/Device.py +++ b/telldus/src/telldus/Device.py @@ -413,6 +413,13 @@ def setName(self, name): self._name = name self.paramUpdated('name') + def setParameter(self, name, value): + """ + Set a device specific parameter. What kind of paramters to set is dependent on the device + type + """ + pass + def setParams(self, params): pass diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index 43af255..153535e 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -136,7 +136,8 @@ def deviceParamUpdated(self, device, param): self.__sendDeviceReport() if device.isSensor: self.__sendSensorReport() - if param == 'room': + return + if param and param != '': self.__sendDeviceParameterReport(device, sendParameters=True, sendMetadata=False) def findByName(self, name): @@ -381,9 +382,11 @@ def __handleDeviceCommand(self, msg): return name = args.get('name', '') value = args.get('value', '') - if name == 'room': - device.setRoom(value) - return + if args['action'] == 'setParameter': + if name == 'room': + device.setRoom(value) + else: + device.setParameter(name, value) @TelldusLive.handler('device-requestdata') def __handleDeviceParametersRequest(self, msg): From 670b8e9398a94b3c321529034b03122eb2090bca Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Mon, 17 Dec 2018 12:46:27 +0100 Subject: [PATCH 03/31] Make it possible to edit rooms See #218 --- telldus/src/telldus/DeviceManager.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index 153535e..3addd89 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -463,6 +463,27 @@ def handleRoom(self, msg): self.settings['rooms'] = self.rooms return + if data['action'] == 'edit': + room = self.rooms.get(data['id'], None) + if not room: + logging.warning('Room %s was not found', data['id']) + return + name = data.get('name', '') + if name != '': + room['name'] = name + color = data.get('color', '') + if color != '': + room['color'] = color + icon = data.get('icon', '') + if icon != '': + room['icon'] = icon + if self.live.registered and room['isOwner']: + msg = LiveMessage('RoomAdded') + msg.append(room) + self.live.send(msg) + self.settings['rooms'] = self.rooms + return + def liveRegistered(self, __msg): self.registered = True self.__sendDeviceReport() From d2deb44ac5ebfc422b24d8ac5726140edb3d9cfa Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Mon, 17 Dec 2018 12:50:23 +0100 Subject: [PATCH 04/31] Implement setting metadata Closes #154 --- telldus/src/telldus/Device.py | 20 ++++++++++++++++++-- telldus/src/telldus/DeviceManager.py | 10 +++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/telldus/src/telldus/Device.py b/telldus/src/telldus/Device.py index 9e6121b..c4f6a9b 100644 --- a/telldus/src/telldus/Device.py +++ b/telldus/src/telldus/Device.py @@ -115,6 +115,7 @@ def __init__(self): self._ignored = None self._loadCount = 0 self._name = None + self._metadata = {} self._manager = None self._room = None self._state = Device.TURNOFF @@ -267,6 +268,7 @@ def loadCached(self, olddevice): self._loadCount = 0 self.setParams(olddevice.params()) (state, stateValue) = olddevice.state() + self._metadata = olddevice._metadata self._room = olddevice._room self._state = state self._stateValue = stateValue @@ -279,6 +281,8 @@ def loadCount(self): def load(self, settings): if 'id' in settings: self._id = settings['id'] + if 'metadata' in settings: + self._metadata = settings['metadata'] if 'name' in settings: self._name = settings['name'] if 'params' in settings: @@ -319,8 +323,8 @@ def metadata(self, key=None, default=None): a dictionary. """ if key is None: - return {} - return default + return self._metadata.copy() + return self._metadata.get(key, default) def methods(self): """ @@ -409,6 +413,18 @@ def setIgnored(self, ignored): def setManager(self, manager): self._manager = manager + def setMetadata(self, name, value): + if self._metadata.get(name, None) == value: + # Identical, do nothing + return + if value is None or value == '': + # Remove it + self._metadata.pop(name, None) + else: + self._metadata[name] = value + if self._manager: + self._manager.deviceMetadataUpdated(self, name) + def setName(self, name): self._name = name self.paramUpdated('name') diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index 3addd89..3d0198f 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -128,6 +128,11 @@ def device(self, deviceId): return device return None + def deviceMetadataUpdated(self, device, param): + self.save() + if param and param != '': + self.__sendDeviceParameterReport(device, sendParameters=False, sendMetadata=True) + def deviceParamUpdated(self, device, param): self.save() self.__deviceUpdated(device, [param]) @@ -372,7 +377,7 @@ def __handleDeviceCommand(self, msg): else: dev.setName(args['name'].decode('UTF-8')) return - elif args['action'] == 'setParameter': + elif args['action'] in ('setParameter', 'setMetadata'): device = None for dev in self.devices: if dev.id() == args['device']: @@ -387,6 +392,8 @@ def __handleDeviceCommand(self, msg): device.setRoom(value) else: device.setParameter(name, value) + else: + device.setMetadata(name, value) @TelldusLive.handler('device-requestdata') def __handleDeviceParametersRequest(self, msg): @@ -542,6 +549,7 @@ def save(self): "type": device.typeString(), "name": device.name(), "params": device.params(), + "metadata": device.metadata(), "methods": device.methods(), "state": state, "stateValue": stateValue, From 9e2d8ce2b6128f192cfb874caa0b711519e2323a Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Mon, 17 Dec 2018 12:50:57 +0100 Subject: [PATCH 05/31] Let metadata devicetype override device metadata --- telldus/src/telldus/Device.py | 15 ++++++++++----- telldus/src/telldus/DeviceManager.py | 6 +++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/telldus/src/telldus/Device.py b/telldus/src/telldus/Device.py index c4f6a9b..cdb2a40 100644 --- a/telldus/src/telldus/Device.py +++ b/telldus/src/telldus/Device.py @@ -141,11 +141,16 @@ def allParameters(self): # parameters() must return a dict params = {} - try: - params['devicetype'] = self.deviceType() - except Exception as error: - params['devicetype'] = Device.TYPE_UNKNOWN - Application.printException(error) + devicetype = self.metadata('devicetype', None) + if devicetype is not None: + # Devicetype in metadata overrides the devicetype + params['devicetype'] = devicetype + else: + try: + params['devicetype'] = self.deviceType() + except Exception as error: + params['devicetype'] = Device.TYPE_UNKNOWN + Application.printException(error) if self._room is None: # Make sure it's removed params.pop('room', None) diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index 3d0198f..c1dfade 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -131,7 +131,11 @@ def device(self, deviceId): def deviceMetadataUpdated(self, device, param): self.save() if param and param != '': - self.__sendDeviceParameterReport(device, sendParameters=False, sendMetadata=True) + sendParameters = False + if param == 'devicetype': + # This effects device parameters also + sendParameters = True + self.__sendDeviceParameterReport(device, sendParameters=sendParameters, sendMetadata=True) def deviceParamUpdated(self, device, param): self.save() From 4d919d65f7cd03bf8852388ee6ad5970c4cb6a59 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Mon, 18 Feb 2019 13:57:41 +0100 Subject: [PATCH 06/31] Replace add and edit room with only set --- telldus/src/telldus/DeviceManager.py | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index c1dfade..13b5e64 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -443,8 +443,8 @@ def __handleSensorUpdate(self, msg): @TelldusLive.handler('room') def handleRoom(self, msg): data = msg.argument(0).toNative() - if data['action'] == 'add': isOwner = bool(data.get('isOwner', False)) + if data['action'] == 'set': self.rooms[data['id']] = { 'name': data.get('name', ''), 'parent': data.get('parent', ''), @@ -454,7 +454,7 @@ def handleRoom(self, msg): 'mode': '', # Not implemented yet } if self.live.registered and isOwner: - msg = LiveMessage('RoomAdded') + msg = LiveMessage('RoomSet') msg.append(self.rooms[data['id']]) self.live.send(msg) self.settings['rooms'] = self.rooms @@ -474,27 +474,6 @@ def handleRoom(self, msg): self.settings['rooms'] = self.rooms return - if data['action'] == 'edit': - room = self.rooms.get(data['id'], None) - if not room: - logging.warning('Room %s was not found', data['id']) - return - name = data.get('name', '') - if name != '': - room['name'] = name - color = data.get('color', '') - if color != '': - room['color'] = color - icon = data.get('icon', '') - if icon != '': - room['icon'] = icon - if self.live.registered and room['isOwner']: - msg = LiveMessage('RoomAdded') - msg.append(room) - self.live.send(msg) - self.settings['rooms'] = self.rooms - return - def liveRegistered(self, __msg): self.registered = True self.__sendDeviceReport() From fe3ad2678610ab51ce744aa84bff7e3b830b4e43 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Mon, 18 Feb 2019 14:13:06 +0100 Subject: [PATCH 07/31] Remove isOwner from Room Instead compare with Responsible to find out who is the owner. --- telldus/src/telldus/DeviceManager.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index 13b5e64..8f8fe68 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -443,17 +443,19 @@ def __handleSensorUpdate(self, msg): @TelldusLive.handler('room') def handleRoom(self, msg): data = msg.argument(0).toNative() - isOwner = bool(data.get('isOwner', False)) if data['action'] == 'set': + oldResponsible = '' + if data['id'] in self.rooms: + oldResponsible = self.rooms[data['id']]['responsible'] self.rooms[data['id']] = { 'name': data.get('name', ''), 'parent': data.get('parent', ''), 'color': data.get('color', ''), 'icon': data.get('icon', ''), - 'isOwner': isOwner, 'mode': '', # Not implemented yet + 'responsible': data['responsible'], } - if self.live.registered and isOwner: + if self.live.registered and (data['responsible'] == self.live.uuid or oldResponsible == self.live.uuid): msg = LiveMessage('RoomSet') msg.append(self.rooms[data['id']]) self.live.send(msg) @@ -465,9 +467,7 @@ def handleRoom(self, msg): if room is None: logging.warning('Room %s was not found', data['id']) return - if not room['isOwner']: - return - if self.live.registered: + if self.live.registered and room['responsible'] == self.live.uuid: msg = LiveMessage('RoomRemoved') msg.append({'id': data['id']}) self.live.send(msg) From fac4626d4c67c5ce5816f9d9650be50f35f2da24 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Mon, 18 Feb 2019 14:15:27 +0100 Subject: [PATCH 08/31] Handle UTF-8 encoded room names --- telldus/src/telldus/DeviceManager.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index 8f8fe68..8c24a2c 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -443,6 +443,11 @@ def __handleSensorUpdate(self, msg): @TelldusLive.handler('room') def handleRoom(self, msg): data = msg.argument(0).toNative() + if 'name' in data: + if isinstance(data['name'], int): + data['name'] = str(data['name']) + else: + data['name'] = data['name'].decode('UTF-8') if data['action'] == 'set': oldResponsible = '' if data['id'] in self.rooms: From 391c0838e1258cc4d1757b28e0d410821421490a Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Mon, 18 Feb 2019 14:17:03 +0100 Subject: [PATCH 09/31] Prepare for mode/content in room data --- telldus/src/telldus/DeviceManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index 8c24a2c..d777720 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -456,9 +456,10 @@ def handleRoom(self, msg): 'name': data.get('name', ''), 'parent': data.get('parent', ''), 'color': data.get('color', ''), + 'content': data.get('content', ''), 'icon': data.get('icon', ''), - 'mode': '', # Not implemented yet 'responsible': data['responsible'], + 'mode': data.get('mode', ''), } if self.live.registered and (data['responsible'] == self.live.uuid or oldResponsible == self.live.uuid): msg = LiveMessage('RoomSet') From f55bfa41efc0de0371b855fe3ea3ef7646bd6568 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Mon, 18 Feb 2019 14:17:52 +0100 Subject: [PATCH 10/31] Add room id in parameters sent to server --- telldus/src/telldus/DeviceManager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index d777720..9a02f96 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -463,6 +463,7 @@ def handleRoom(self, msg): } if self.live.registered and (data['responsible'] == self.live.uuid or oldResponsible == self.live.uuid): msg = LiveMessage('RoomSet') + msg.append({'id': data['id']}) msg.append(self.rooms[data['id']]) self.live.send(msg) self.settings['rooms'] = self.rooms From eae6a236f634e22582931acf9eff5ca207baf1d5 Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Wed, 13 Feb 2019 15:01:10 +0100 Subject: [PATCH 11/31] Move rooms to own manager --- telldus/src/telldus/DeviceManager.py | 43 ------------------- telldus/src/telldus/RoomManager.py | 63 ++++++++++++++++++++++++++++ telldus/src/telldus/__init__.py | 1 + 3 files changed, 64 insertions(+), 43 deletions(-) create mode 100644 telldus/src/telldus/RoomManager.py diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index 9a02f96..f0eaab3 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -52,7 +52,6 @@ class DeviceManager(Plugin): def __init__(self): self.devices = [] - self.rooms = {} self.settings = Settings('telldus.devicemanager') self.nextId = self.settings.get('nextId', 0) self.live = TelldusLive(self.context) @@ -440,54 +439,12 @@ def __handleSensorUpdate(self, msg): # cleaned up self.__sendSensorReport() - @TelldusLive.handler('room') - def handleRoom(self, msg): - data = msg.argument(0).toNative() - if 'name' in data: - if isinstance(data['name'], int): - data['name'] = str(data['name']) - else: - data['name'] = data['name'].decode('UTF-8') - if data['action'] == 'set': - oldResponsible = '' - if data['id'] in self.rooms: - oldResponsible = self.rooms[data['id']]['responsible'] - self.rooms[data['id']] = { - 'name': data.get('name', ''), - 'parent': data.get('parent', ''), - 'color': data.get('color', ''), - 'content': data.get('content', ''), - 'icon': data.get('icon', ''), - 'responsible': data['responsible'], - 'mode': data.get('mode', ''), - } - if self.live.registered and (data['responsible'] == self.live.uuid or oldResponsible == self.live.uuid): - msg = LiveMessage('RoomSet') - msg.append({'id': data['id']}) - msg.append(self.rooms[data['id']]) - self.live.send(msg) - self.settings['rooms'] = self.rooms - return - - if data['action'] == 'remove': - room = self.rooms.pop(data['id'], None) - if room is None: - logging.warning('Room %s was not found', data['id']) - return - if self.live.registered and room['responsible'] == self.live.uuid: - msg = LiveMessage('RoomRemoved') - msg.append({'id': data['id']}) - self.live.send(msg) - self.settings['rooms'] = self.rooms - return - def liveRegistered(self, __msg): self.registered = True self.__sendDeviceReport() self.__sendSensorReport() def __load(self): - self.rooms = self.settings.get('rooms', {}) self.store = self.settings.get('devices', []) for dev in self.store: if 'type' not in dev or 'localId' not in dev: diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py new file mode 100644 index 0000000..58351b4 --- /dev/null +++ b/telldus/src/telldus/RoomManager.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +from base import \ + ISignalObserver, \ + Plugin, \ + Settings, \ + implements, \ + signal +from tellduslive.base import TelldusLive, LiveMessage, ITelldusLiveObserver + +__name__ = 'telldus' # pylint: disable=W0622 + +class RoomManager(Plugin): + """The roommanager holds and manages all the rooms in the server""" + implements(ISignalObserver) + implements(ITelldusLiveObserver) + public = True + + def __init__(self): + self.rooms = {} + self.settings = Settings('telldus.rooms') + self.rooms = self.settings.get('rooms', {}) + + @TelldusLive.handler('room') + def __handleRoom(self, msg): + data = msg.argument(0).toNative() + if 'name' in data: + if isinstance(data['name'], int): + data['name'] = str(data['name']) + else: + data['name'] = data['name'].decode('UTF-8') + live = TelldusLive(self.context) + if data['action'] == 'set': + oldResponsible = '' + if data['id'] in self.rooms: + oldResponsible = self.rooms[data['id']]['responsible'] + self.rooms[data['id']] = { + 'name': data.get('name', ''), + 'parent': data.get('parent', ''), + 'color': data.get('color', ''), + 'content': data.get('content', ''), + 'icon': data.get('icon', ''), + 'responsible': data['responsible'], + 'mode': '', + } + if self.live.registered and (data['responsible'] == self.live.uuid or oldResponsible == self.live.uuid): + msg = LiveMessage('RoomSet') + msg.append({'id': data['id']}) + msg.append(self.rooms[data['id']]) + live.send(msg) + self.settings['rooms'] = self.rooms + return + + if data['action'] == 'remove': + room = self.rooms.pop(data['id'], None) + if room is None: + return + if live.registered and room['responsible'] == self.live.uuid: + msg = LiveMessage('RoomRemoved') + msg.append({'id': data['id']}) + live.send(msg) + self.settings['rooms'] = self.rooms + return diff --git a/telldus/src/telldus/__init__.py b/telldus/src/telldus/__init__.py index 92918df..b3462d5 100644 --- a/telldus/src/telldus/__init__.py +++ b/telldus/src/telldus/__init__.py @@ -2,6 +2,7 @@ from .Device import Device, DeviceAbortException, Sensor from .DeviceManager import DeviceManager, IDeviceChange +from .RoomManager import RoomManager try: from .DeviceEventFactory import DeviceEventFactory except ImportError: From e5840bedfc1e8b186721a3962443a14821b39b56 Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Tue, 19 Feb 2019 15:18:12 +0100 Subject: [PATCH 12/31] Keep all parameters in one dictionary for RoomSet --- telldus/src/telldus/RoomManager.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index 58351b4..7c79e4c 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -45,8 +45,16 @@ def __handleRoom(self, msg): } if self.live.registered and (data['responsible'] == self.live.uuid or oldResponsible == self.live.uuid): msg = LiveMessage('RoomSet') - msg.append({'id': data['id']}) - msg.append(self.rooms[data['id']]) + msg.append({ + 'id': data['id'], + 'name': data.get('name', ''), + 'parent': data.get('parent', ''), + 'color': data.get('color', ''), + 'content': data.get('content', ''), + 'icon': data.get('icon', ''), + 'responsible': data.get('responsible', ''), + 'mode': data.get('mode', ''), + }) live.send(msg) self.settings['rooms'] = self.rooms return From d07d977e857c48e159478ad498b69d9b6128f426 Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Tue, 19 Feb 2019 15:23:14 +0100 Subject: [PATCH 13/31] Do not specify default values twice --- telldus/src/telldus/RoomManager.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index 7c79e4c..4d5da66 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -44,16 +44,19 @@ def __handleRoom(self, msg): 'mode': '', } if self.live.registered and (data['responsible'] == self.live.uuid or oldResponsible == self.live.uuid): + room = self.rooms[data['id']] msg = LiveMessage('RoomSet') msg.append({ + # No need to call get() on room here since we know every value has at least a + # default value above 'id': data['id'], - 'name': data.get('name', ''), - 'parent': data.get('parent', ''), - 'color': data.get('color', ''), - 'content': data.get('content', ''), - 'icon': data.get('icon', ''), - 'responsible': data.get('responsible', ''), - 'mode': data.get('mode', ''), + 'name': room['name'], + 'parent': room['parent'], + 'color': room['color'], + 'content': room['content'], + 'icon': room['icon'], + 'responsible': room['responsible'], + 'mode': room['mode'], }) live.send(msg) self.settings['rooms'] = self.rooms From 8200c3e1fbe75d9bad639acb580862084db4c91c Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Tue, 12 Feb 2019 15:30:44 +0100 Subject: [PATCH 14/31] Implement mode trigger for rooms --- telldus/src/telldus/DeviceEventFactory.py | 35 ++++++++++++++++++++++- telldus/src/telldus/RoomManager.py | 17 +++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/telldus/src/telldus/DeviceEventFactory.py b/telldus/src/telldus/DeviceEventFactory.py index 23b6ce2..55387fe 100644 --- a/telldus/src/telldus/DeviceEventFactory.py +++ b/telldus/src/telldus/DeviceEventFactory.py @@ -2,7 +2,7 @@ from threading import Timer -from base import Plugin, implements +from base import Plugin, implements, ISignalObserver, slot from events.base import IEventFactory, Action, Condition, Trigger from .Device import Device from .DeviceManager import DeviceManager, IDeviceChange @@ -10,14 +10,17 @@ class DeviceEventFactory(Plugin): implements(IEventFactory) implements(IDeviceChange) + implements(ISignalObserver) def __init__(self): self.deviceTriggers = [] + self.modeTriggers = [] self.sensorTriggers = [] self.deviceManager = DeviceManager(self.context) # pylint: disable=E1121 def clearAll(self): self.deviceTriggers = [] + self.modeTriggers = [] self.sensorTriggers = [] def createAction(self, type, params, **kwargs): # pylint: disable=W0622 @@ -41,6 +44,10 @@ def createTrigger(self, type, **kwargs): # pylint: disable=W0622 deviceTrigger = DeviceTrigger(self, **kwargs) self.deviceTriggers.append(deviceTrigger) return deviceTrigger + if type == 'mode': + modeTrigger = ModeTrigger(**kwargs) + self.modeTriggers.append(modeTrigger) + return modeTrigger if type == 'sensor': sensorTrigger = SensorTrigger(self, **kwargs) self.sensorTriggers.append(sensorTrigger) @@ -68,6 +75,20 @@ def stateChanged(self, device, method, statevalue): 'method': int(method) }) + @slot('modeChanged') + def __modeChanged(self, objectId, mode, objectType, objectName): + for trigger in self.modeTriggers: + if trigger.objectId != objectId: + continue + if trigger.mode != mode: + continue + trigger.triggered({ + 'objectId': objectId, + 'mode': mode, + 'objectType': objectType, + 'objectName': objectName, + }) + class DeviceActionExecutor(object): def __init__(self, device, method, value, repeats, description): self.device = device @@ -176,6 +197,18 @@ def parseParam(self, name, value): elif name == 'method': self.method = int(value) +class ModeTrigger(Trigger): + def __init__(self, **kwargs): + super(ModeTrigger, self).__init__(**kwargs) + self.objectId = None + self.mode = None + + def parseParam(self, name, value): + if name == 'modeId': + self.mode = value + elif name == 'objectId': + self.objectId = value + class SensorCondition(Condition): def __init__(self, manager, **kwargs): super(SensorCondition, self).__init__(**kwargs) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index 4d5da66..5f1623d 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -21,6 +21,13 @@ def __init__(self): self.settings = Settings('telldus.rooms') self.rooms = self.settings.get('rooms', {}) + @signal('modeChanged') + def __modeChanged(self, objectId, modeId, objectType, objectName): + """ + Called every time the mode changes for a room + """ + pass + @TelldusLive.handler('room') def __handleRoom(self, msg): data = msg.argument(0).toNative() @@ -72,3 +79,13 @@ def __handleRoom(self, msg): live.send(msg) self.settings['rooms'] = self.rooms return + + if data['action'] == 'setMode': + room = self.rooms.get(data['id'], None) + if not room: + return + room['mode'] = data.get('mode', '') + self.settings['rooms'] = self.rooms + self.__modeChanged(data['id'], data['mode'], 'room', data.get('name', '')) + # TODO, notify live + return From 4efd4f5e6f3b322681d88715fb3c78b0b042aae3 Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Wed, 13 Feb 2019 13:35:23 +0100 Subject: [PATCH 15/31] Implement ModeCondition --- telldus/src/telldus/DeviceEventFactory.py | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/telldus/src/telldus/DeviceEventFactory.py b/telldus/src/telldus/DeviceEventFactory.py index 55387fe..028d2e2 100644 --- a/telldus/src/telldus/DeviceEventFactory.py +++ b/telldus/src/telldus/DeviceEventFactory.py @@ -34,6 +34,8 @@ def createCondition(self, type, params, **kwargs): # pylint: disable=W0622 if 'local' in params and params['local'] == 1: return DeviceCondition(manager=self.deviceManager, **kwargs) return None + if type == 'mode': + return ModeCondition(manager=self.deviceManager, **kwargs) if type == 'sensor': if 'local' in params and params['local'] == 1: return SensorCondition(manager=self.deviceManager, **kwargs) @@ -209,6 +211,29 @@ def parseParam(self, name, value): elif name == 'objectId': self.objectId = value +class ModeCondition(Condition): + def __init__(self, manager, **kwargs): + super(ModeCondition, self).__init__(**kwargs) + self.objectId = None + self.manager = manager + self.modeId = None + + def parseParam(self, name, value): + if name == 'modeId': + self.modeId = value + elif name == 'objectId': + self.objectId = value + + def validate(self, success, failure): + room = self.manager.rooms.get(self.objectId, None) + if not room: + failure() + return + if room.get('mode', '') == self.modeId: + success() + return + failure() + class SensorCondition(Condition): def __init__(self, manager, **kwargs): super(SensorCondition, self).__init__(**kwargs) From 8a983f25a984153ab8e059e8d96012df3306b041 Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Thu, 14 Feb 2019 13:14:42 +0100 Subject: [PATCH 16/31] Implement ModeAction --- telldus/src/telldus/DeviceEventFactory.py | 28 +++++++++++++++++++++++ telldus/src/telldus/RoomManager.py | 20 ++++++++++------ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/telldus/src/telldus/DeviceEventFactory.py b/telldus/src/telldus/DeviceEventFactory.py index 028d2e2..4619735 100644 --- a/telldus/src/telldus/DeviceEventFactory.py +++ b/telldus/src/telldus/DeviceEventFactory.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- +import logging from threading import Timer from base import Plugin, implements, ISignalObserver, slot from events.base import IEventFactory, Action, Condition, Trigger from .Device import Device from .DeviceManager import DeviceManager, IDeviceChange +from .RoomManager import RoomManager class DeviceEventFactory(Plugin): implements(IEventFactory) @@ -27,6 +29,9 @@ def createAction(self, type, params, **kwargs): # pylint: disable=W0622 if type == 'device': if 'local' in params and params['local'] == 1: return DeviceAction(manager=self.deviceManager, **kwargs) + if type == 'mode': + roomManager = RoomManager(self.context) # pylint: disable=E1121 + return ModeAction(manager=roomManager, **kwargs) return None def createCondition(self, type, params, **kwargs): # pylint: disable=W0622 @@ -199,6 +204,29 @@ def parseParam(self, name, value): elif name == 'method': self.method = int(value) +class ModeAction(Action): + def __init__(self, manager, **kwargs): + self.objectId = '' + self.objectType = '' + self.modeId = '' + self.roomManager = manager + super(ModeAction, self).__init__(**kwargs) + + def parseParam(self, name, value): + if name == 'objectId': + self.objectId = value + elif name == 'objectType': + self.objectType = value + elif name == 'modeId': + self.modeId = value + + def execute(self, triggerInfo=None): + del triggerInfo + if self.objectType == 'room': + self.roomManager.setMode(self.objectId, self.modeId) + else: + logging.error('Cannot handle mode change for type %s', self.objectType) + class ModeTrigger(Trigger): def __init__(self, **kwargs): super(ModeTrigger, self).__init__(**kwargs) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index 5f1623d..b4c3699 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -21,6 +21,18 @@ def __init__(self): self.settings = Settings('telldus.rooms') self.rooms = self.settings.get('rooms', {}) + def setMode(self, roomId, mode): + """ + Set a room to a new mode + """ + room = self.rooms.get(roomId, None) + if not room: + return + room['mode'] = mode + self.settings['rooms'] = self.rooms + self.__modeChanged(roomId, mode, 'room', room.get('name', '')) + # TODO, notify live + @signal('modeChanged') def __modeChanged(self, objectId, modeId, objectType, objectName): """ @@ -81,11 +93,5 @@ def __handleRoom(self, msg): return if data['action'] == 'setMode': - room = self.rooms.get(data['id'], None) - if not room: - return - room['mode'] = data.get('mode', '') - self.settings['rooms'] = self.rooms - self.__modeChanged(data['id'], data['mode'], 'room', data.get('name', '')) - # TODO, notify live + self.setMode(data.get('id', None), data.get('mode', '')) return From 14aedea4d147da8a6adb1cf43e521377a7d36303 Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Thu, 14 Feb 2019 13:29:30 +0100 Subject: [PATCH 17/31] Only store the mode if it was changed --- telldus/src/telldus/RoomManager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index b4c3699..ee688cc 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -28,8 +28,9 @@ def setMode(self, roomId, mode): room = self.rooms.get(roomId, None) if not room: return - room['mode'] = mode - self.settings['rooms'] = self.rooms + if room['mode'] != mode: + room['mode'] = mode + self.settings['rooms'] = self.rooms self.__modeChanged(roomId, mode, 'room', room.get('name', '')) # TODO, notify live From 1779e27ded5f90c8aaf4688035f624e661792d6e Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Thu, 14 Feb 2019 13:42:24 +0100 Subject: [PATCH 18/31] Notify live when the mode on a room was changed --- telldus/src/telldus/RoomManager.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index ee688cc..e188416 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -31,8 +31,16 @@ def setMode(self, roomId, mode): if room['mode'] != mode: room['mode'] = mode self.settings['rooms'] = self.rooms + live = TelldusLive(self.context) + if live.registered and room.get('isOwner', False): + # Notify live if we are the owner + msg = LiveMessage('RoomModeSet') + msg.append({ + 'id': roomId, + 'mode': mode + }) + live.send(msg) self.__modeChanged(roomId, mode, 'room', room.get('name', '')) - # TODO, notify live @signal('modeChanged') def __modeChanged(self, objectId, modeId, objectType, objectName): From 3886bd837c721ca6fe04136a873f14fd8ece9520 Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Tue, 19 Feb 2019 15:24:51 +0100 Subject: [PATCH 19/31] Pylint fix --- telldus/src/telldus/RoomManager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index e188416..00fd4b8 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -71,7 +71,8 @@ def __handleRoom(self, msg): 'responsible': data['responsible'], 'mode': '', } - if self.live.registered and (data['responsible'] == self.live.uuid or oldResponsible == self.live.uuid): + if self.live.registered and \ + (data['responsible'] == self.live.uuid or oldResponsible == self.live.uuid): room = self.rooms[data['id']] msg = LiveMessage('RoomSet') msg.append({ From 2e11f61d4521cf9c3d94ef8915acd233972e0fbe Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Wed, 20 Feb 2019 11:20:39 +0100 Subject: [PATCH 20/31] Compare room responsible instead of isOwner --- telldus/src/telldus/RoomManager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index 00fd4b8..e50c896 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -32,7 +32,7 @@ def setMode(self, roomId, mode): room['mode'] = mode self.settings['rooms'] = self.rooms live = TelldusLive(self.context) - if live.registered and room.get('isOwner', False): + if live.registered and room.get('responsible', '') == self.live.uuid: # Notify live if we are the owner msg = LiveMessage('RoomModeSet') msg.append({ From 01214b9c402449be04a3a1b22e444d58a11d45d0 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Wed, 20 Feb 2019 16:02:33 +0100 Subject: [PATCH 21/31] Fix variables not declared on init anymore --- telldus/src/telldus/RoomManager.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index e50c896..3987aaf 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -32,7 +32,7 @@ def setMode(self, roomId, mode): room['mode'] = mode self.settings['rooms'] = self.rooms live = TelldusLive(self.context) - if live.registered and room.get('responsible', '') == self.live.uuid: + if live.registered and room.get('responsible', '') == live.uuid: # Notify live if we are the owner msg = LiveMessage('RoomModeSet') msg.append({ @@ -71,8 +71,8 @@ def __handleRoom(self, msg): 'responsible': data['responsible'], 'mode': '', } - if self.live.registered and \ - (data['responsible'] == self.live.uuid or oldResponsible == self.live.uuid): + if live.registered and \ + (data['responsible'] == live.uuid or oldResponsible == live.uuid): room = self.rooms[data['id']] msg = LiveMessage('RoomSet') msg.append({ @@ -95,7 +95,8 @@ def __handleRoom(self, msg): room = self.rooms.pop(data['id'], None) if room is None: return - if live.registered and room['responsible'] == self.live.uuid: + live = TelldusLive(self.context) + if live.registered and room['responsible'] == live.uuid: msg = LiveMessage('RoomRemoved') msg.append({'id': data['id']}) live.send(msg) From 3e88f94e5380138c5d39ca66ee158dab6b8a69e6 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Thu, 21 Feb 2019 10:46:41 +0100 Subject: [PATCH 22/31] Handle room edits without all parameters --- telldus/src/telldus/RoomManager.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index 3987aaf..aa19b1d 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -61,16 +61,25 @@ def __handleRoom(self, msg): if data['action'] == 'set': oldResponsible = '' if data['id'] in self.rooms: - oldResponsible = self.rooms[data['id']]['responsible'] - self.rooms[data['id']] = { - 'name': data.get('name', ''), - 'parent': data.get('parent', ''), - 'color': data.get('color', ''), - 'content': data.get('content', ''), - 'icon': data.get('icon', ''), - 'responsible': data['responsible'], - 'mode': '', - } + # existing room + room = self.rooms[data['id']] + oldResponsible = room['responsible'] + validKeys = ['name', 'color', 'content', 'icon', 'responsible'] + for key in validKeys: + if key in data: + room[key] = data.get(key, '') + self.rooms[data['id']] = room + else: + # new room + self.rooms[data['id']] = { + 'name': data.get('name', ''), + 'parent': data.get('parent', ''), + 'color': data.get('color', ''), + 'content': data.get('content', ''), + 'icon': data.get('icon', ''), + 'responsible': data['responsible'], + 'mode': data.get('mode', ''), + } if live.registered and \ (data['responsible'] == live.uuid or oldResponsible == live.uuid): room = self.rooms[data['id']] From 89abd5be48729ae5fe4c27b4943ce1d7b0944cf6 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Thu, 21 Feb 2019 11:06:35 +0100 Subject: [PATCH 23/31] Send signal if mode is changed in room edit --- telldus/src/telldus/RoomManager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index aa19b1d..87bfe93 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -68,6 +68,9 @@ def __handleRoom(self, msg): for key in validKeys: if key in data: room[key] = data.get(key, '') + if 'mode' in data and room['mode'] != data.get('mode', ''): + room['mode'] = data.get('mode', '') + self.__modeChanged(data['id'], room['mode'], 'room', room['name']) self.rooms[data['id']] = room else: # new room From 742d51739df2b472796337102e09ffc027204f06 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Fri, 22 Feb 2019 14:15:24 +0100 Subject: [PATCH 24/31] Differentiate between server reconnects and reboots Some reports do not have to be sent or received on each server reconnect. The new refreshRequired-argument in liveRegistered indicates if it's a restart/has been offline for a long time, or if it just a switch from one server to another. --- events/src/events/base/EventManager.py | 2 +- led/src/led/Led.py | 2 +- live/src/tellduslive/base/TelldusLive.py | 13 +++++++++---- scheduler/src/scheduler/base/Scheduler.py | 6 ++---- telldus/src/telldus/DeviceManager.py | 2 +- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/events/src/events/base/EventManager.py b/events/src/events/base/EventManager.py index a83725f..2e66440 100644 --- a/events/src/events/base/EventManager.py +++ b/events/src/events/base/EventManager.py @@ -40,7 +40,7 @@ def loadEvent(self, eventId, data, storeddata): event.loadTriggers(data['triggers']) self.events[eventId] = event - def liveRegistered(self, msg): + def liveRegistered(self, msg, refreshRequired): changed = False if 'latitude' in msg and msg['latitude'] != self.latitude: changed = True diff --git a/led/src/led/Led.py b/led/src/led/Led.py index ff23cee..366e223 100644 --- a/led/src/led/Led.py +++ b/led/src/led/Led.py @@ -20,7 +20,7 @@ def __init__(self): def liveConnected(self): self.setNetworkLed() - def liveRegistered(self, msg): + def liveRegistered(self, msg, refreshRequired): self.setNetworkLed() def liveDisconnected(self): diff --git a/live/src/tellduslive/base/TelldusLive.py b/live/src/tellduslive/base/TelldusLive.py index 17d703b..9ba952a 100644 --- a/live/src/tellduslive/base/TelldusLive.py +++ b/live/src/tellduslive/base/TelldusLive.py @@ -37,7 +37,7 @@ class ITelldusLiveObserver(IInterface): """ def liveConnected(): """This method is called when we have succesfully connected to a Live! server""" - def liveRegistered(params): + def liveRegistered(params, refreshRequired): """This method is called when we have succesfully registered with a Live! server""" def liveDisconnected(): """This method is call when we are disconnected""" @@ -51,6 +51,7 @@ def __init__(self): self.email = '' self.supportedMethods = 0 self.connected = False + self.refreshRequired = True self.registered = False self.running = False self.serverList = ServerList() @@ -108,7 +109,8 @@ def handleMessage(self, message): if 'uuid' in data and data['uuid'] != self.uuid: self.uuid = data['uuid'] self.settings['uuid'] = self.uuid - self.liveRegistered(data) + self.liveRegistered(data, self.refreshRequired) + self.refreshRequired = False return if (message.name() == "command"): @@ -155,9 +157,9 @@ def liveDisconnected(self): self.observers.liveDisconnected() @signal - def liveRegistered(self, options): + def liveRegistered(self, options, refreshRequired): """This signal is sent when we have succesfully registered with a Live! server""" - self.observers.liveRegistered(options) + self.observers.liveRegistered(options, refreshRequired) def run(self): self.running = True @@ -181,6 +183,9 @@ def run(self): logging.warning("Could not connect, retry in %i seconds", wait) elif state == ServerConnection.CONNECTED: + if (pongTimer + 43200) < time.time(): + # 12 hours since last online + self.refreshRequired = True pongTimer, self.pingTimer = (time.time(), time.time()) self.__sendRegisterMessage() diff --git a/scheduler/src/scheduler/base/Scheduler.py b/scheduler/src/scheduler/base/Scheduler.py index 380b043..e961639 100644 --- a/scheduler/src/scheduler/base/Scheduler.py +++ b/scheduler/src/scheduler/base/Scheduler.py @@ -32,7 +32,6 @@ def __init__(self): self.latitude = self.settings.get('latitude', '55.699592') self.longitude = self.settings.get('longitude', '13.187836') self.jobs = [] - self.jobsFetchedFromServer = False self.fetchLocalJobs() self.live = TelldusLive(self.context) self.deviceManager = DeviceManager(self.context) @@ -227,7 +226,7 @@ def fetchLocalJobs(self): print("WARNING: Could not fetch schedules from local storage") self.calculateJobs(jobs) - def liveRegistered(self, msg): + def liveRegistered(self, msg, refreshRequired): if 'latitude' in msg: self.latitude = msg['latitude'] if 'longitude' in msg: @@ -235,7 +234,7 @@ def liveRegistered(self, msg): if 'tz' in msg: self.timezone = msg['tz'] - if not self.jobsFetchedFromServer: + if refreshRequired: self.requestJobsFromServer() @TelldusLive.handler('scheduler-remove') @@ -279,7 +278,6 @@ def receiveOneJobFromServer(self, msg): # self.live.pushToWeb('scheduler', 'updated', job['id']) def requestJobsFromServer(self): - self.jobsFetchedFromServer = True self.live.send(LiveMessage("scheduler-requestjob")) def run(self): diff --git a/telldus/src/telldus/DeviceManager.py b/telldus/src/telldus/DeviceManager.py index f0eaab3..51a7fe6 100644 --- a/telldus/src/telldus/DeviceManager.py +++ b/telldus/src/telldus/DeviceManager.py @@ -439,7 +439,7 @@ def __handleSensorUpdate(self, msg): # cleaned up self.__sendSensorReport() - def liveRegistered(self, __msg): + def liveRegistered(self, __msg, refreshRequired): self.registered = True self.__sendDeviceReport() self.__sendSensorReport() From bb8c7ae4e38c75ca0cfef7ea7dd6c6647d4a984e Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Fri, 1 Mar 2019 14:06:46 +0100 Subject: [PATCH 25/31] Allow for syncronization of rooms --- telldus/src/telldus/RoomManager.py | 56 +++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index 87bfe93..10314d7 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -21,6 +21,34 @@ def __init__(self): self.settings = Settings('telldus.rooms') self.rooms = self.settings.get('rooms', {}) + def getResponsibleRooms(self, responsible=None): + if not responsible: + live = TelldusLive(self.context) + responsible = live.uuid + rooms = {} + for roomUUID in self.rooms: + room = self.rooms[roomUUID] + if room['responsible'] == responsible: + rooms[roomUUID] = room + return rooms + + def liveRegistered(self, msg, refreshRequired): + if refreshRequired: + self.syncRoom() + + def reportRooms(self, rooms): + if not rooms: + return + msg = LiveMessage('RoomReport') + msg.append({'rooms': rooms}) + TelldusLive(self.context).send(msg) + + def roomChanged(self, room1, room2): + for prop in room1: + if not room1[prop] == room2[prop]: + return True + return False + def setMode(self, roomId, mode): """ Set a room to a new mode @@ -42,6 +70,9 @@ def setMode(self, roomId, mode): live.send(msg) self.__modeChanged(roomId, mode, 'room', room.get('name', '')) + def syncRoom(self): + TelldusLive(self.context).send(LiveMessage("roomsync-request")) + @signal('modeChanged') def __modeChanged(self, objectId, modeId, objectType, objectName): """ @@ -107,7 +138,6 @@ def __handleRoom(self, msg): room = self.rooms.pop(data['id'], None) if room is None: return - live = TelldusLive(self.context) if live.registered and room['responsible'] == live.uuid: msg = LiveMessage('RoomRemoved') msg.append({'id': data['id']}) @@ -118,3 +148,27 @@ def __handleRoom(self, msg): if data['action'] == 'setMode': self.setMode(data.get('id', None), data.get('mode', '')) return + + if data['action'] == 'sync': + rooms = data['rooms'] + responsibleRooms = self.getResponsibleRooms() + if not rooms and responsibleRooms: + # list from server was completely empty but we have rooms locally, + # this might be an error in the fetching, or we have added rooms locally + # when offline. In any case, don't sync this time, just post our rooms + # for next time + self.reportRooms(responsibleRooms) + return + changedRooms = {} + for roomUUID in rooms: + room = rooms[roomUUID] + if room['responsible'] == live.uuid: + # we are responsible for this room + localRoom = self.rooms[roomUUID] + if self.roomChanged(room, localRoom): + changedRooms[roomUUID] = localRoom + else: + self.rooms[roomUUID] = room + + self.reportRooms(changedRooms) + self.settings['rooms'] = self.rooms From 5c664839c7667a34885a1caac7ffdb994bf60937 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Thu, 7 Mar 2019 16:46:08 +0100 Subject: [PATCH 26/31] Don't sync empty room list if we know it's supposed to be empty --- telldus/src/telldus/RoomManager.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index 10314d7..6c7339a 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -20,6 +20,7 @@ def __init__(self): self.rooms = {} self.settings = Settings('telldus.rooms') self.rooms = self.settings.get('rooms', {}) + self.roomlistEmpty = self.settings.get('roomlistEmpty', False) def getResponsibleRooms(self, responsible=None): if not responsible: @@ -37,7 +38,9 @@ def liveRegistered(self, msg, refreshRequired): self.syncRoom() def reportRooms(self, rooms): - if not rooms: + if not rooms and not self.roomlistEmpty: + # only allow empty room reports if we know it has been + # explicitly emptied return msg = LiveMessage('RoomReport') msg.append({'rooms': rooms}) @@ -142,6 +145,10 @@ def __handleRoom(self, msg): msg = LiveMessage('RoomRemoved') msg.append({'id': data['id']}) live.send(msg) + if len(self.rooms) == 0: + self.settings['roomlistEmpty'] = True + self.roomlistEmpty = True + self.settings['rooms'] = self.rooms return From 5bd4f6c638c0fe226137737e73c4c84fec2f9f90 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Fri, 8 Mar 2019 11:30:45 +0100 Subject: [PATCH 27/31] Also handle removed rooms in room sync --- telldus/src/telldus/RoomManager.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index 6c7339a..3466436 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -37,13 +37,18 @@ def liveRegistered(self, msg, refreshRequired): if refreshRequired: self.syncRoom() - def reportRooms(self, rooms): - if not rooms and not self.roomlistEmpty: + def reportRooms(self, rooms, removedRooms = None): + report = {} + if not rooms and not self.roomlistEmpty and not removedRooms: # only allow empty room reports if we know it has been # explicitly emptied return + if rooms or self.roomlistEmpty: + report['rooms'] = rooms + if removedRooms: + report['removedRooms'] = removedRooms msg = LiveMessage('RoomReport') - msg.append({'rooms': rooms}) + msg.append(report) TelldusLive(self.context).send(msg) def roomChanged(self, room1, room2): @@ -145,7 +150,7 @@ def __handleRoom(self, msg): msg = LiveMessage('RoomRemoved') msg.append({'id': data['id']}) live.send(msg) - if len(self.rooms) == 0: + if len(self.getResponsibleRooms()) == 0: self.settings['roomlistEmpty'] = True self.roomlistEmpty = True @@ -167,15 +172,23 @@ def __handleRoom(self, msg): self.reportRooms(responsibleRooms) return changedRooms = {} + newRooms = {} + removedRooms = [] for roomUUID in rooms: room = rooms[roomUUID] if room['responsible'] == live.uuid: # we are responsible for this room + if roomUUID not in self.rooms: + # this room does not exist locally anymore + removedRooms.append(roomUUID) + continue localRoom = self.rooms[roomUUID] if self.roomChanged(room, localRoom): changedRooms[roomUUID] = localRoom else: - self.rooms[roomUUID] = room + newRooms[roomUUID] = room - self.reportRooms(changedRooms) + newRooms.update(responsibleRooms) + self.rooms = newRooms + self.reportRooms(changedRooms, removedRooms) self.settings['rooms'] = self.rooms From f060d5c80168c297d5c97d675dc84bd7a07644c3 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Fri, 5 Apr 2019 11:17:37 +0200 Subject: [PATCH 28/31] Add possibility to re-set same mode again Possible to decide if mode should be set even if the new mode is the same as already set mode. --- telldus/src/telldus/DeviceEventFactory.py | 5 ++++- telldus/src/telldus/RoomManager.py | 14 ++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/telldus/src/telldus/DeviceEventFactory.py b/telldus/src/telldus/DeviceEventFactory.py index 4619735..c5b4d2b 100644 --- a/telldus/src/telldus/DeviceEventFactory.py +++ b/telldus/src/telldus/DeviceEventFactory.py @@ -206,6 +206,7 @@ def parseParam(self, name, value): class ModeAction(Action): def __init__(self, manager, **kwargs): + self.setAlways = True self.objectId = '' self.objectType = '' self.modeId = '' @@ -213,7 +214,9 @@ def __init__(self, manager, **kwargs): super(ModeAction, self).__init__(**kwargs) def parseParam(self, name, value): - if name == 'objectId': + if name == 'setAlways': + self.setAlways = value + elif name == 'objectId': self.objectId = value elif name == 'objectType': self.objectType = value diff --git a/telldus/src/telldus/RoomManager.py b/telldus/src/telldus/RoomManager.py index 3466436..6bfc092 100644 --- a/telldus/src/telldus/RoomManager.py +++ b/telldus/src/telldus/RoomManager.py @@ -57,16 +57,18 @@ def roomChanged(self, room1, room2): return True return False - def setMode(self, roomId, mode): + def setMode(self, roomId, mode, setAlways = 1): """ Set a room to a new mode """ room = self.rooms.get(roomId, None) if not room: return - if room['mode'] != mode: - room['mode'] = mode - self.settings['rooms'] = self.rooms + setAlways = int(setAlways) + if setAlways or room['mode'] != mode: + if room['mode'] != mode: + room['mode'] = mode + self.settings['rooms'] = self.rooms live = TelldusLive(self.context) if live.registered and room.get('responsible', '') == live.uuid: # Notify live if we are the owner @@ -76,7 +78,7 @@ def setMode(self, roomId, mode): 'mode': mode }) live.send(msg) - self.__modeChanged(roomId, mode, 'room', room.get('name', '')) + self.__modeChanged(roomId, mode, 'room', room.get('name', '')) def syncRoom(self): TelldusLive(self.context).send(LiveMessage("roomsync-request")) @@ -158,7 +160,7 @@ def __handleRoom(self, msg): return if data['action'] == 'setMode': - self.setMode(data.get('id', None), data.get('mode', '')) + self.setMode(data.get('id', None), data.get('mode', ''), data.get('setAlways', 1)) return if data['action'] == 'sync': From 9642d01517fc8439b2b1b315bb93d838ec00b21a Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Fri, 5 Apr 2019 11:22:14 +0200 Subject: [PATCH 29/31] Only run mode change action on rooms for which we are responsible --- telldus/src/telldus/DeviceEventFactory.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/telldus/src/telldus/DeviceEventFactory.py b/telldus/src/telldus/DeviceEventFactory.py index c5b4d2b..4e37bb1 100644 --- a/telldus/src/telldus/DeviceEventFactory.py +++ b/telldus/src/telldus/DeviceEventFactory.py @@ -8,6 +8,7 @@ from .Device import Device from .DeviceManager import DeviceManager, IDeviceChange from .RoomManager import RoomManager +from tellduslive.base import LiveMessage, TelldusLive class DeviceEventFactory(Plugin): implements(IEventFactory) @@ -226,7 +227,13 @@ def parseParam(self, name, value): def execute(self, triggerInfo=None): del triggerInfo if self.objectType == 'room': - self.roomManager.setMode(self.objectId, self.modeId) + room = self.roomManager.rooms.get(self.objectId, None) + if room and room.get('responsible', '') == TelldusLive(self.roomManager.context).uuid: + self.roomManager.setMode(self.objectId, self.modeId, self.setAlways) + else: + msg = LiveMessage('RequestRoomModeSet') + msg.append({'id': self.objectId, 'mode': self.modeId, 'setAlways': self.setAlways}) + TelldusLive(self.roomManager.context).send(msg) else: logging.error('Cannot handle mode change for type %s', self.objectType) From 4bdf106f47f3585e76affbc59d0f203acaf9dc5f Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Fri, 5 Apr 2019 14:32:04 +0200 Subject: [PATCH 30/31] Use correct manager for ModeCondition --- telldus/src/telldus/DeviceEventFactory.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/telldus/src/telldus/DeviceEventFactory.py b/telldus/src/telldus/DeviceEventFactory.py index 4e37bb1..c2c54d9 100644 --- a/telldus/src/telldus/DeviceEventFactory.py +++ b/telldus/src/telldus/DeviceEventFactory.py @@ -41,7 +41,8 @@ def createCondition(self, type, params, **kwargs): # pylint: disable=W0622 return DeviceCondition(manager=self.deviceManager, **kwargs) return None if type == 'mode': - return ModeCondition(manager=self.deviceManager, **kwargs) + roomManager = RoomManager(self.context) # pylint: disable=E1121 + return ModeCondition(manager=roomManager, **kwargs) if type == 'sensor': if 'local' in params and params['local'] == 1: return SensorCondition(manager=self.deviceManager, **kwargs) From d2bc7c044d937f00b8cc36833721883546ea6568 Mon Sep 17 00:00:00 2001 From: Stefan Persson Date: Fri, 5 Apr 2019 14:33:01 +0200 Subject: [PATCH 31/31] Possible to choose if ModeCondition should match or not Possible to choose if ModeCondition should match a mode, or differ from a certain mode. --- telldus/src/telldus/DeviceEventFactory.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/telldus/src/telldus/DeviceEventFactory.py b/telldus/src/telldus/DeviceEventFactory.py index c2c54d9..2eb940b 100644 --- a/telldus/src/telldus/DeviceEventFactory.py +++ b/telldus/src/telldus/DeviceEventFactory.py @@ -253,12 +253,15 @@ def parseParam(self, name, value): class ModeCondition(Condition): def __init__(self, manager, **kwargs): super(ModeCondition, self).__init__(**kwargs) + self.equalTo = True self.objectId = None self.manager = manager self.modeId = None def parseParam(self, name, value): - if name == 'modeId': + if name == 'equalTo': + self.equalTo = int(value) + elif name == 'modeId': self.modeId = value elif name == 'objectId': self.objectId = value @@ -268,7 +271,7 @@ def validate(self, success, failure): if not room: failure() return - if room.get('mode', '') == self.modeId: + if (self.equalTo and room.get('mode', '') == self.modeId) or (not self.equalTo and room.get('mode', '') != self.modeId): success() return failure()