class Event(object): class Types(object): OUTPUT = 'OUTPUT' INPUT = 'INPUT' SENSOR = 'SENSOR' THERMOSTAT = 'THERMOSTAT' SYSTEM = 'SYSTEM' POWER = 'POWER' BUTTON_PRESS = 'BUTTON_PRESS' LED_ON = 'LED_ON' LED_BLINK = 'LED_BLINK' UNKNOWN = 'UNKNOWN' class SensorType(object): TEMPERATURE = 'TEMPERATURE' HUMIDITY = 'HUMIDITY' BRIGHTNESS = 'BRIGHTNESS' UNKNOWN = 'UNKNOWN' class SystemEventTypes(object): EEPROM_ACTIVATE = 'EEPROM_ACTIVATE' ONBOARD_TEMP_CHANGED = 'ONBOARD_TEMP_CHANGED' UNKNOWN = 'UNKNOWN' class ThermostatOrigins(object): SLAVE = 'SLAVE' MASTER = 'MASTER' UNKNOWN = 'UNKNOWN' class Bus(object): RS485 = 'RS485' CAN = 'CAN' class Leds(object): LED_0 = 0 LED_1 = 1 LED_2 = 2 LED_3 = 3 LED_4 = 4 LED_5 = 5 LED_6 = 6 LED_7 = 7 LED_8 = 8 LED_9 = 9 LED_10 = 10 LED_11 = 11 LED_12 = 12 LED_13 = 13 LED_14 = 14 LED_15 = 15 class LedStates(object): OFF = 'OFF' ON = 'ON' class LedFrequencies(object): BLINKING_25 = 'BLINKING_25' BLINKING_50 = 'BLINKING_50' BLINKING_75 = 'BLINKING_75' SOLID = 'SOLID' class Buttons(object): SETUP = 0 ACTION = 1 CAN_POWER = 2 SELECT = 3 class ButtonStates(object): RELEASED = 0 PRESSED = 1 PRESSED_5S = 2 PRESSED_LONG = 3 def __init__(self, data): self._type = data['type'] self._action = data['action'] self._device_nr = data['device_nr'] self._data = data['data'] self._word_helper = WordField('') @property def type(self): type_map = { 0: Event.Types.OUTPUT, 1: Event.Types.INPUT, 2: Event.Types.SENSOR, 20: Event.Types.THERMOSTAT, 250: Event.Types.BUTTON_PRESS, 251: Event.Types.LED_BLINK, 252: Event.Types.LED_ON, 253: Event.Types.POWER, 254: Event.Types.SYSTEM } return type_map.get(self._type, Event.Types.UNKNOWN) @property def data(self): if self.type == Event.Types.OUTPUT: timer_factor = None timer_value = self._word_decode( self._data[2:]) # type: Optional[int] if self._data[1] == 0: timer_value = None elif self._data[1] == 1: timer_factor = 0.1 elif self._data[1] == 2: timer_factor = 1 elif self._data[2] == 3: timer_factor = 60 return { 'output': self._device_nr, 'status': self._action == 1, 'dimmer_value': self._data[0], 'timer_factor': timer_factor, 'timer_value': timer_value } if self.type == Event.Types.INPUT: return {'input': self._device_nr, 'status': self._action == 1} if self.type == Event.Types.SENSOR: sensor_type = Event.SensorType.UNKNOWN sensor_value = None if self._action == 0: sensor_type = Event.SensorType.TEMPERATURE sensor_value = Temperature.system_value_to_temperature( self._data[1]) elif self._action == 1: sensor_type = Event.SensorType.HUMIDITY sensor_value = Humidity.system_value_to_humidity(self._data[1]) elif self._action == 2: sensor_type = Event.SensorType.BRIGHTNESS sensor_value = self._word_decode(self._data[0:2]) return { 'sensor': self._device_nr, 'type': sensor_type, 'value': sensor_value } if self.type == Event.Types.THERMOSTAT: origin_map = { 0: Event.ThermostatOrigins.SLAVE, 1: Event.ThermostatOrigins.MASTER } return { 'origin': origin_map.get(self._action, Event.ThermostatOrigins.UNKNOWN), 'thermostat': self._device_nr, 'mode': self._data[0], 'setpoint': self._data[1] } if self.type == Event.Types.BUTTON_PRESS: return {'button': self._device_nr, 'state': self._data[0]} if self.type == Event.Types.LED_BLINK: word_25 = self._device_nr word_50 = self._word_decode(self._data[0:2]) word_75 = self._word_decode(self._data[2:4]) leds = {} for i in range(16): if word_25 & (1 << i): leds[i] = Event.LedFrequencies.BLINKING_25 elif word_50 & (1 << i): leds[i] = Event.LedFrequencies.BLINKING_50 elif word_75 & (1 << i): leds[i] = Event.LedFrequencies.BLINKING_75 else: leds[i] = Event.LedFrequencies.SOLID return {'chip': self._device_nr, 'leds': leds} if self.type == Event.Types.LED_ON: word_on = self._word_decode(self._data[0:2]) leds = {} for i in range(16): leds[i] = Event.LedStates.ON if word_on & ( 1 << i) else Event.LedStates.OFF return {'chip': self._device_nr, 'leds': leds} if self.type == Event.Types.POWER: return { 'bus': Event.Bus.RS485 if self._device_nr == 0 else Event.Bus.CAN, 'power': self._data[0 > 1] } if self.type == Event.Types.SYSTEM: type_map = { 0: Event.SystemEventTypes.EEPROM_ACTIVATE, 1: Event.SystemEventTypes.ONBOARD_TEMP_CHANGED } event_type = type_map.get(self._action, Event.SystemEventTypes.UNKNOWN) event_data = {'type': event_type} if event_type == Event.SystemEventTypes.ONBOARD_TEMP_CHANGED: event_data['temperature'] = self._data[0] return event_data return None def _word_decode(self, data): # type: (List[int]) -> int return self._word_helper.decode(bytearray(data[0:2])) def __str__(self): return '{0} ({1})'.format( self.type, self.data if self.type != Event.Types.UNKNOWN else self._type)
class Event(object): class Types(object): OUTPUT = 'OUTPUT' INPUT = 'INPUT' SENSOR = 'SENSOR' THERMOSTAT = 'THERMOSTAT' SYSTEM = 'SYSTEM' POWER = 'POWER' EXECUTED_BA = 'EXECUTED_BA' BUTTON_PRESS = 'BUTTON_PRESS' LED_ON = 'LED_ON' LED_BLINK = 'LED_BLINK' UCAN = 'UCAN' EXECUTE_GATEWAY_API = 'EXECUTE_GATEWAY_API' UNKNOWN = 'UNKNOWN' class OutputEventTypes(object): STATUS = 'STATUS' LOCKING = 'LOCKING' class UCANEventTypes(object): POWER_OUT_ERROR = 'POWER_OUT_ERROR' POWER_OUT_RESTORED = 'POWER_OUT_RESTORED' POWER_OUT_ON = 'POWER_OUT_ON' POWER_OUT_OFF = 'POWER_OUT_OFF' I2C_ERROR = 'I2C_ERROR' UNKNOWN = 'UNKNOWN' class SensorType(object): TEMPERATURE = 'TEMPERATURE' HUMIDITY = 'HUMIDITY' BRIGHTNESS = 'BRIGHTNESS' UNKNOWN = 'UNKNOWN' class SystemEventTypes(object): EEPROM_ACTIVATE = 'EEPROM_ACTIVATE' ONBOARD_TEMP_CHANGED = 'ONBOARD_TEMP_CHANGED' UNKNOWN = 'UNKNOWN' class ThermostatOrigins(object): SLAVE = 'SLAVE' MASTER = 'MASTER' UNKNOWN = 'UNKNOWN' class Bus(object): RS485 = 'RS485' CAN = 'CAN' class Leds(object): LED_0 = 0 LED_1 = 1 LED_2 = 2 LED_3 = 3 LED_4 = 4 LED_5 = 5 LED_6 = 6 LED_7 = 7 LED_8 = 8 LED_9 = 9 LED_10 = 10 LED_11 = 11 LED_12 = 12 LED_13 = 13 LED_14 = 14 LED_15 = 15 class LedStates(object): OFF = 'OFF' ON = 'ON' class LedFrequencies(object): BLINKING_25 = 'BLINKING_25' BLINKING_50 = 'BLINKING_50' BLINKING_75 = 'BLINKING_75' SOLID = 'SOLID' class Buttons(object): SETUP = 0 ACTION = 1 CAN_POWER = 2 SELECT = 3 class ButtonStates(object): RELEASED = 0 PRESSED = 1 PRESSED_5S = 2 PRESSED_LONG = 3 def __init__(self, data): self._type = data['type'] self._action = data['action'] self._device_nr = data['device_nr'] self._data = data['data'] self._word_helper = WordField('') self._address_helper = AddressField('', length=3) @property def type(self): type_map = {0: Event.Types.OUTPUT, 1: Event.Types.INPUT, 2: Event.Types.SENSOR, 20: Event.Types.THERMOSTAT, 21: Event.Types.UCAN, 22: Event.Types.EXECUTED_BA, 249: Event.Types.EXECUTE_GATEWAY_API, 250: Event.Types.BUTTON_PRESS, 251: Event.Types.LED_BLINK, 252: Event.Types.LED_ON, 253: Event.Types.POWER, 254: Event.Types.SYSTEM} return type_map.get(self._type, Event.Types.UNKNOWN) @property def data(self): if self.type == Event.Types.OUTPUT: data = {'output': self._device_nr} if self._action in [0, 1]: timer_type = self._data[1] # type: int timer_value = self._word_decode(self._data[2:]) or 0 # type: int timer = Timer.event_timer_type_to_seconds(timer_type, timer_value) data.update({'type': Event.OutputEventTypes.STATUS, 'status': self._action == 1, 'dimmer_value': self._data[0], 'timer': timer}) else: data.update({'type': Event.OutputEventTypes.LOCKING, 'locked': self._data[0] == 1}) return data if self.type == Event.Types.INPUT: return {'input': self._device_nr, 'status': self._action == 1} if self.type == Event.Types.SENSOR: sensor_type = Event.SensorType.UNKNOWN sensor_value = None if self._action == 0: sensor_type = Event.SensorType.TEMPERATURE sensor_value = Temperature.system_value_to_temperature(self._data[1]) elif self._action == 1: sensor_type = Event.SensorType.HUMIDITY sensor_value = Humidity.system_value_to_humidity(self._data[1]) elif self._action == 2: sensor_type = Event.SensorType.BRIGHTNESS sensor_value = self._word_decode(self._data[0:2]) return {'sensor': self._device_nr, 'type': sensor_type, 'value': sensor_value} if self.type == Event.Types.THERMOSTAT: origin_map = {0: Event.ThermostatOrigins.SLAVE, 1: Event.ThermostatOrigins.MASTER} return {'origin': origin_map.get(self._action, Event.ThermostatOrigins.UNKNOWN), 'thermostat': self._device_nr, 'mode': self._data[0], 'setpoint': self._data[1]} if self.type == Event.Types.BUTTON_PRESS: return {'button': self._device_nr, 'state': self._data[0]} if self.type == Event.Types.LED_BLINK: word_25 = self._device_nr word_50 = self._word_decode(self._data[0:2]) word_75 = self._word_decode(self._data[2:4]) leds = {} for i in range(16): if word_25 & (1 << i): leds[i] = Event.LedFrequencies.BLINKING_25 elif word_50 & (1 << i): leds[i] = Event.LedFrequencies.BLINKING_50 elif word_75 & (1 << i): leds[i] = Event.LedFrequencies.BLINKING_75 else: leds[i] = Event.LedFrequencies.SOLID return {'chip': self._device_nr, 'leds': leds} if self.type == Event.Types.LED_ON: word_on = self._word_decode(self._data[0:2]) leds = {} for i in range(16): leds[i] = Event.LedStates.ON if word_on & (1 << i) else Event.LedStates.OFF return {'chip': self._device_nr, 'leds': leds} if self.type == Event.Types.POWER: return {'bus': Event.Bus.RS485 if self._device_nr == 0 else Event.Bus.CAN, 'power': self._data[0 > 1]} if self.type == Event.Types.SYSTEM: type_map = {0: Event.SystemEventTypes.EEPROM_ACTIVATE, 1: Event.SystemEventTypes.ONBOARD_TEMP_CHANGED} event_type = type_map.get(self._action, Event.SystemEventTypes.UNKNOWN) event_data = {'type': event_type} if event_type == Event.SystemEventTypes.ONBOARD_TEMP_CHANGED: event_data['temperature'] = self._data[0] return event_data if self.type == Event.Types.UCAN: event_data = {'address': self._address_helper.decode(bytearray([self._device_nr & 0xFF]) + self._data[0:2])} if self._data[2] == 0 and self._data[3] == 0: event_data['type'] = Event.UCANEventTypes.POWER_OUT_ERROR elif self._data[2] == 0 and self._data[3] == 1: event_data['type'] = Event.UCANEventTypes.POWER_OUT_RESTORED elif self._data[2] == 0 and self._data[3] == 2: event_data['type'] = Event.UCANEventTypes.POWER_OUT_OFF elif self._data[2] == 2 and self._data[3] == 3: event_data['type'] = Event.UCANEventTypes.POWER_OUT_ON elif self._data[2] == 1: event_data.update({'type': Event.UCANEventTypes.I2C_ERROR, 'i2c_address': self._data[3]}) else: event_data.update({'type': Event.UCANEventTypes.UNKNOWN, 'data': self._data[2:4]}) return event_data if self.type == Event.Types.EXECUTED_BA: return {'basic_action': BasicAction(action_type=self._data[0], action=self._data[1], device_nr=self._device_nr, extra_parameter=self._word_decode(self._data[2:4]))} if self.type == Event.Types.EXECUTE_GATEWAY_API: return {'action': self._data[3], 'device_nr': self._device_nr, 'extra_parameter': self._word_decode(self._data[0:2])} return None def _word_decode(self, data): # type: (List[int]) -> int return self._word_helper.decode(bytearray(data[0:2])) def __str__(self): return '{0} ({1})'.format(self.type, self.data if self.type != Event.Types.UNKNOWN else self._type)
class BasicAction(object): def __init__(self, action_type, action, device_nr=None, extra_parameter=None ): # type: (int, int, Optional[int], Optional[int]) -> None self._word_helper = WordField('') self._byte_helper = ByteField('') self._action_type = self._byte_helper.encode( action_type) # type: bytearray self._action = self._byte_helper.encode(action) # type: bytearray self._device_nr = self._word_helper.encode( device_nr if device_nr is not None else 0) # type: bytearray self._extra_parameter = self._word_helper.encode( extra_parameter if extra_parameter is not None else 0) # type: bytearray @property def action_type(self): # type: () -> int return self._byte_helper.decode(self._action_type) @property def action(self): # type: () -> int return self._byte_helper.decode(self._action) @property def device_nr(self): # type: () -> int return self._word_helper.decode(self._device_nr) @property def extra_parameter(self): # type: () -> int return self._word_helper.decode(self._extra_parameter) def encode(self): # type: () -> bytearray return self._action_type + self._action + self._device_nr + self._extra_parameter @property def in_use(self): # type: () -> bool return self.action_type != 255 and self.action != 255 @property def is_execute_group_action(self): # type: () -> bool return self.action_type == 19 and self.action == 0 @staticmethod def decode(data): # type: (bytearray) -> BasicAction basic_action = BasicAction(action_type=data[0], action=data[1]) basic_action._device_nr = data[2:4] basic_action._extra_parameter = data[4:6] return basic_action def __repr__(self): return 'BA({0},{1},{2},{3})'.format(self.action_type, self.action, self.device_nr, self.extra_parameter) def __eq__(self, other): # type: (Any) -> bool if not isinstance(other, BasicAction): return False return self.encode() == other.encode()