diff --git a/doc/branch_new_devices_june16.txt b/doc/branch_new_devices_june16.txt new file mode 100644 index 0000000..2b5f247 --- /dev/null +++ b/doc/branch_new_devices_june16.txt @@ -0,0 +1,26 @@ +New devices to add: + +-------------------------------------------------------------------------------- + +MiHome Light + MIHO008 OOK White + MIHO024 OOK Black Nickel + MIHO025 OOK Chrome + MIHO026 OOK Brushed Steel + + RX ONLY + OOK, OUTER_TIMES=1, OUTER_DELAY=0, INNER_TIMES=75 + + +-------------------------------------------------------------------------------- + +KNOCK ON TODOs +- need to pass forward radio config parameters to get INNER_TIMES=75 for the MiHome Light. + - need to think about config.tx_repeats vs get_config() - these are different concepts + + +-------------------------------------------------------------------------------- + + + + diff --git a/doc/user_guides.txt b/doc/user_guides.txt new file mode 100644 index 0000000..222e2dc --- /dev/null +++ b/doc/user_guides.txt @@ -0,0 +1,22 @@ +LINKS TO USER GUIDES +==================== + +MIHOME LIGHT + MIHO008 White + MIHO024 Black Nickel + MIHO025 Chrome + MIHO025 Brushed Steel + + https://energenie4u.co.uk/res/pdfs/MIHome-Light-User-Guide-v1.4.pdf + + +MIHOME OPEN SENSOR + MIHO033 + + https://energenie4u.co.uk/res/pdfs/MIHO033-Open-Sensor-User-Guide-v1.1.pdf + + +MIHOME MOTION SENSOR + MIHO032 + + https://energenie4u.co.uk/res/pdfs/MIHO032-Motion-Sensor-User-Guide-v1.3-outlines.pdf diff --git a/src/Logger.py b/src/Logger.py index 1dab788..b748690 100644 --- a/src/Logger.py +++ b/src/Logger.py @@ -57,6 +57,9 @@ if paramid == OpenThings.PARAM_SWITCH_STATE: switch = value flags[0] = 1 + elif paramid == OpenThings.PARAM_DOOR_SENSOR: + switch = value + flags[0] = 1 elif paramid == OpenThings.PARAM_VOLTAGE: flags[1] = 1 voltage = value diff --git a/src/energenie/Devices.py b/src/energenie/Devices.py index 78ba11d..0202c23 100644 --- a/src/energenie/Devices.py +++ b/src/energenie/Devices.py @@ -28,7 +28,7 @@ PRODUCTID_MIHO005 = 0x02 # Adaptor Plus PRODUCTID_MIHO006 = 0x05 # House Monitor ##PRODUCTID_MIHO007 = 0x0? # Double Wall Socket White -##PRODUCTID_MIHO008 = 0x0? # Single light switch +##PRODUCTID_MIHO008 = 0x0? # OOK: Single light switch white ##PRODUCTID_MIHO009 not used ##PRODUCTID_MIHO010 not used ##PRODUCTID_MIHO011 not used @@ -44,16 +44,16 @@ ##PRODUCTID_MIHO021 = 0x0? # Double Wall Socket Nickel ##PRODUCTID_MIHO022 = 0x0? # Double Wall Socket Chrome ##PRODUCTID_MIHO023 = 0x0? # Double Wall Socket Brushed Steel -##PRODUCTID_MIHO024 = 0x0? # Style Light Nickel -##PRODUCTID_MIHO025 = 0x0? # Style Light Chrome -##PRODUCTID_MIHO026 = 0x0? # Style Light Steel +##PRODUCTID_MIHO024 = 0x0? # OOK:Style Light Nickel +##PRODUCTID_MIHO025 = 0x0? # OOK:Style Light Chrome +##PRODUCTID_MIHO026 = 0x0? # OOK:Style Light Steel ##PRODUCTID_MIHO027 starter pack bundle ##PRODUCTID_MIHO028 eco starter pack ##PRODUCTID_MIHO029 heating bundle ##PRODUCTID_MIHO030 not used ##PRODUCTID_MIHO031 not used -##PRODUCTID_MIHO032 not used -##PRODUCTID_MIHO033 not used +PRODUCTID_MIHO032 = 0x0C # FSK motion sensor +PRODUCTID_MIHO033 = 0x0D # FSK open sensor ##PRODUCTID_MIHO034 not used ##PRODUCTID_MIHO035 not used ##PRODUCTID_MIHO036 not used @@ -75,7 +75,6 @@ #----- DEFINED MESSAGE TEMPLATES ---------------------------------------------- - SWITCH = { "header": { "mfrid": MFRID_ENERGENIE, @@ -498,6 +497,8 @@ print("send_message(mock[%s %s %s]):%s" % (str(m), str(p), str(d), payload)) +#------------------------------------------------------------------------------ + class ENER002(LegacyDevice): """A green-button switch""" def __init__(self, device_id, air_interface=None): @@ -539,11 +540,82 @@ self.turn_off() +#------------------------------------------------------------------------------ + +class MiHomeLight(LegacyDevice): + """Base for all MiHomeLight variants. Receive only OOK device""" + def __init__(self, device_id, air_interface=None): + LegacyDevice.__init__(self, device_id, air_interface) + self.config.tx_repeats = 75 + self.capabilities.switch = True + self.capabilities.receive = True + + def __repr__(self): + return "MiHomeLight(%s,%s)" % (str(hex(self.device_id[0])), str(hex(self.device_id[1]))) + + def turn_on(self): + #TODO: should this be here, or in LegacyDevice?? + #addressing should probably be in LegacyDevice + #child devices might interpret the command differently + payload = { + "house_address": self.device_id[0], + "device_index": self.device_id[1], + "on": True + } + #TODO: Need to pass forward the new radio config OUTER_TIMES=1 OUTER_DELAY=1 INNER_TIMES=75 + #using self.config.tx_repeats + self.send_message(payload) + + def turn_off(self): + #TODO: should this be here, or in LegacyDevice??? + #addressing should probably be in LegacyDevice + #child devices might interpret the command differently + payload = { + "house_address": self.device_id[0], + "device_index": self.device_id[1], + "on": False + } + #TODO: Need to pass forward the new radio config OUTER_TIMES=1 OUTER_DELAY=1 INNER_TIMES=75 + #using self.config.tx_repeats + self.send_message(payload) + + def set_switch(self, state): + if state: + self.turn_on() + else: + self.turn_off() + + +class MIHO008(MiHomeLight): + """White finish""" + def __repr__(self): + return "MIHO008(%s,%s)" % (str(hex(self.device_id[0])), str(hex(self.device_id[1]))) + +class MIHO024(MiHomeLight): + """Black Nickel Finish""" + def __repr__(self): + return "MIHO024(%s,%s)" % (str(hex(self.device_id[0])), str(hex(self.device_id[1]))) + +class MIHO025(MiHomeLight): + """Chrome Finish""" + def __repr__(self): + return "MIHO025(%s,%s)" % (str(hex(self.device_id[0])), str(hex(self.device_id[1]))) + +class MIHO026(MiHomeLight): + """Brushed Steel Finish""" + def __repr__(self): + return "MIHO026(%s,%s)" % (str(hex(self.device_id[0])), str(hex(self.device_id[1]))) + + +#------------------------------------------------------------------------------ + class MIHO004(MiHomeDevice): """Monitor only Adaptor""" pass #TODO +#------------------------------------------------------------------------------ + class MIHO005(MiHomeDevice): """An Energenie MiHome Adaptor Plus""" def __init__(self, device_id, air_interface=None): @@ -677,6 +749,8 @@ return self.readings.real_power +#------------------------------------------------------------------------------ + class MIHO006(MiHomeDevice): """An Energenie MiHome Home Monitor""" def __init__(self, device_id, air_interface=None): @@ -695,6 +769,8 @@ return self.readings.current +#------------------------------------------------------------------------------ + class MIHO013(MiHomeDevice): """An Energenie MiHome eTRV Radiator Valve""" def __init__(self, device_id, air_interface=None): @@ -753,6 +829,92 @@ pass #TODO: i.e. valve is completely closed? +#------------------------------------------------------------------------------ + +class MIHO032(MiHomeDevice): + """An Energenie Motion Sensor""" + def __init__(self, device_id, air_interface=None): + MiHomeDevice.__init__(self, device_id, air_interface) + self.product_id = PRODUCTID_MIHO032 + class Readings(): + switch_state = None + battery_alarm = None + self.readings = Readings() + self.capabilities.send = True + + def __repr__(self): + return "MIHO032(%s)" % str(hex(self.device_id)) + + def handle_message(self, payload): + ##print("MIHO032 new data %s %s" % (self.device_id, payload)) + for rec in payload["recs"]: + paramid = rec["paramid"] + #TODO: consider making this table driven and allowing our base class to fill our readings in for us + #TODO: consider using @OpenThings.parameter as a decorator to the receive function + #it will then register a handler for that message for itself as a handler + #we still need Readings() defined too as a cache. The decorator could add + #an entry into the cache too for us perhaps? + if "value" in rec: + value = rec["value"] + if paramid == OpenThings.PARAM_MOTION_DETECTOR: + self.readings.switch_state = ((value == True) or (value != 0)) + elif paramid == OpenThings.PARAM_ALARM: + if value == 0x42: # battery alarming + self.readings.battery_alarm = True + elif value == 0x62: # battery not alarming + self.readings.battery_alarm = False + else: + try: + param_name = OpenThings.param_info[paramid]['n'] # name + except: + param_name = "UNKNOWN_%s" % str(hex(paramid)) + print("unwanted paramid: %s" % param_name) + + def get_switch_state(self): # -> switch:bool + return self.readings.switch_state + + def get_battery_alarm(self): # -> alarm:bool + return self.readings.battery_alarm + +#------------------------------------------------------------------------------ + +class MIHO033(MiHomeDevice): + """An Energenie Open Sensor""" + def __init__(self, device_id, air_interface=None): + MiHomeDevice.__init__(self, device_id, air_interface) + self.product_id = PRODUCTID_MIHO033 + class Readings(): + switch_state = None + self.readings = Readings() + self.capabilities.send = True + + def __repr__(self): + return "MIHO033(%s)" % str(hex(self.device_id)) + + def handle_message(self, payload): + ##print("MIHO033 new data %s %s" % (self.device_id, payload)) + for rec in payload["recs"]: + paramid = rec["paramid"] + #TODO: consider making this table driven and allowing our base class to fill our readings in for us + #TODO: consider using @OpenThings.parameter as a decorator to the receive function + #it will then register a handler for that message for itself as a handler + #we still need Readings() defined too as a cache. The decorator could add + #an entry into the cache too for us perhaps? + if "value" in rec: + value = rec["value"] + if paramid == OpenThings.PARAM_DOOR_SENSOR: + self.readings.switch_state = ((value == True) or (value != 0)) + else: + try: + param_name = OpenThings.param_info[paramid]['n'] # name + except: + param_name = "UNKNOWN_%s" % str(hex(paramid)) + print("unwanted paramid: %s" % param_name) + + def get_switch_state(self): # -> switch:bool + return self.readings.switch_state + + #----- DEVICE FACTORY --------------------------------------------------------- # This is a singleton, but might not be in the future. @@ -767,10 +929,12 @@ # If you know the name of the device, use this table device_from_name = { # official name friendly name - "ENER002": ENER002, "GreenButton": ENER002, - "MIHO005": MIHO005, "AdaptorPlus": MIHO005, - "MIHO006": MIHO006, "HomeMonitor": MIHO006, - "MIHO013": MIHO013, "eTRV": MIHO013, + "ENER002": ENER002, "GreenButton": ENER002, + "MIHO005": MIHO005, "AdaptorPlus": MIHO005, + "MIHO006": MIHO006, "HomeMonitor": MIHO006, + "MIHO013": MIHO013, "eTRV": MIHO013, + "MIHO032": MIHO032, "MotionSensor": MIHO032, + "MIHO033": MIHO033, "OpenSensor": MIHO033 } #TODO: These are MiHome devices only, but might add in mfrid prefix too @@ -779,7 +943,9 @@ PRODUCTID_MIHO004: MIHO004, PRODUCTID_MIHO005: MIHO005, PRODUCTID_MIHO006: MIHO006, - PRODUCTID_MIHO013: MIHO013 + PRODUCTID_MIHO013: MIHO013, + PRODUCTID_MIHO032: MIHO032, + PRODUCTID_MIHO033: MIHO033 #ENER product range does not have deviceid, as it does not transmit } diff --git a/src/energenie/Registry.py b/src/energenie/Registry.py index 46a1a71..de0c10e 100644 --- a/src/energenie/Registry.py +++ b/src/energenie/Registry.py @@ -63,9 +63,9 @@ c = self.store[name] if self.fsk_router != None: - if c.can_receive(): + if c.can_send(): # if can transmit, we can receive from it if isinstance(c, Devices.MiHomeDevice): - ##print("Adding rx route for receive enabled device %s" % c) + print("Adding rx route for transmit enabled device %s" % c) address = (c.manufacturer_id, c.product_id, c.device_id) self.fsk_router.add(address, c) return c diff --git a/src/registry.kvs b/src/registry.kvs index bbacec5..678732d 100644 --- a/src/registry.kvs +++ b/src/registry.kvs @@ -6,3 +6,11 @@ type=MIHO005 device_id=1675 +ADD door +type=MIHO033 +device_id=2817 + +ADD PIR +type=MIHO032 +device_id=4042 + diff --git a/src/setup_tool.py b/src/setup_tool.py index 5d3457f..30f5b66 100644 --- a/src/setup_tool.py +++ b/src/setup_tool.py @@ -147,6 +147,7 @@ print("REGISTRY:") show_registry() + energenie.registry.fsk_router.list() def do_switch_device():