diff --git a/src/Logger.py b/src/Logger.py index 46c5129..9157c64 100644 --- a/src/Logger.py +++ b/src/Logger.py @@ -45,7 +45,7 @@ current = None # capture any data that we want - #print(msg) + ##trace(msg) for rec in msg['recs']: paramid = rec['paramid'] try: @@ -80,6 +80,6 @@ csv = "%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s" % (timestamp, mfrid, productid, sensorid, flags, switch, voltage, freq, reactive, real, apparent, current) log_file.write(csv + '\n') log_file.flush() - #trace(csv) # testing + ##trace(csv) # testing # END diff --git a/src/energenie/Devices.py b/src/energenie/Devices.py index 2730f70..dcd0b13 100644 --- a/src/energenie/Devices.py +++ b/src/energenie/Devices.py @@ -1,72 +1,84 @@ # Devices.py 30/09/2015 D.J.Whale # # Information about specific Energenie devices +# This table is mostly reverse-engineered from various websites and web catalogues. MFRID = 0x04 +# Deprecated, these are old device names, do not use. +#PRODUCTID_C1_MONITOR = 0x01 # MIHO004 Monitor +#PRODUCTID_R1_MONITOR_AND_CONTROL = 0x02 # MIHO005 Adaptor Plus + #PRODUCTID_MIHO001 = 0x0? # Home Hub #PRODUCTID_MIHO002 = 0x0? # Control only #PRODUCTID_MIHO003 = 0x0? # Hand Controller -PRODUCTID_C1_MONITOR = 0x01 # MIHO004 Monitor -PRODUCTID_R1_MONITOR_AND_CONTROL = 0x02 # MIHO005 Adaptor Plus +PRODUCTID_MIHO004 = 0x01 # Monitor only +PRODUCTID_MIHO005 = 0x02 # Adaptor Plus PRODUCTID_MIHO006 = 0x05 # House Monitor #PRODUCTID_MIHO007 = 0x0? # Double Wall Socket White #PRODUCTID_MIHO008 = 0x0? # Single light switch -#009 not used -#010 not used -#011 not used -#012 not used +#PRODUCTID_MIHO009 not used +#PRODUCTID_MIHO010 not used +#PRODUCTID_MIHO011 not used +#PRODUCTID_MIHO012 not used PRODUCTID_MIHO013 = 0x03 # eTRV #PRODUCTID_MIHO014 = 0x0? # In-line Relay -#015 not used -#016 not used -#017 -#018 -#019 -#020 +#PRODUCTID_MIHO015 not used +#PRODUCTID_MIHO016 not used +#PRODUCTID_MIHO017 +#PRODUCTID_MIHO018 +#PRODUCTID_MIHO019 +#PRODUCTID_MIHO020 #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 -#027 starter pack bundle -#028 eco starter pack -#029 heating bundle -#030-036 not used -#037 Adaptor Plus Bundle -#038 2-gang socket Bundle -#039 2-gang socket Bundle black nickel -#040 2-gang socket Bundle chrome -#041 2-gang socket Bundle stainless 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_MIHO034 not used +#PRODUCTID_MIHO035 not used +#PRODUCTID_MIHO036 not used +#PRODUCTID_MIHO037 Adaptor Plus Bundle +#PRODUCTID_MIHO038 2-gang socket Bundle +#PRODUCTID_MIHO039 2-gang socket Bundle black nickel +#PRODUCTID_MIHO040 2-gang socket Bundle chrome +#PRODUCTID_MIHO041 2-gang socket Bundle stainless steel CRYPT_PID = 242 CRYPT_PIP = 0x0100 -# OpenHEMS does not support a broadcast id, but Energenie added one for their -# MiHome Adaptors. This makes simple discovery possible. -BROADCAST_ID = 0xFFFFFF # energenie broadcast +# OpenThings does not support a broadcast id, +# but Energenie added one for their MiHome Adaptors. +# This makes simple discovery possible. +BROADCAST_ID = 0xFFFFFF # Energenie broadcast -# TODO put additional products in here from the Energenie directory -# TODO make this table based +#TODO: put additional products in here from the Energenie directory +#TODO: make this table based def getDescription(mfrid, productid): if mfrid == MFRID: mfr = "Energenie" - if productid == PRODUCTID_C1_MONITOR: - product = "C1 MONITOR" - elif productid == PRODUCTID_R1_MONITOR_AND_CONTROL: + if productid == PRODUCTID_MIHO004: + product = "MIHO004 MONITOR" + elif productid == PRODUCTID_MIHO005: product = "MIHO005 ADAPTOR PLUS" elif productid == PRODUCTID_MIHO006: product = "MIHO006 HOUSE MONITOR" elif productid == PRODUCTID_MIHO013: product = "MIHO013 ETRV" else: - product = "UNKNOWN" + product = "UNKNOWN_%s" % str(hex(productid)) else: - mfr = "UNKNOWN" - product = "UNKNOWN" + mfr = "UNKNOWN_%s" % str(hex(mfrid)) + product = "UNKNOWN_%s" % str(hex(productid)) return "Manufacturer:%s Product:%s" % (mfr, product) diff --git a/src/energenie/Messages.py b/src/energenie/Messages.py index aaac787..397c3ba 100644 --- a/src/energenie/Messages.py +++ b/src/energenie/Messages.py @@ -42,6 +42,16 @@ } +REGISTERED_SENSOR = { + "header": { + "mfrid": 0, # FILL IN + "productid": 0, # FILL IN + "encryptPIP": Devices.CRYPT_PIP, + "sensorid": 0 # FILL IN + } +} + + def send_join_ack(radio, mfrid, productid, sensorid): # send back a JOIN ACK, so that join light stops flashing response = OpenThings.alterMessage(JOIN_ACK, diff --git a/src/energenie/OpenThings.py b/src/energenie/OpenThings.py index fbf935b..7dc3862 100644 --- a/src/energenie/OpenThings.py +++ b/src/energenie/OpenThings.py @@ -622,7 +622,7 @@ def getFromMessage(message, keypath): - """Get a field from a message, given a keypath to the item""" + """Get a field from a message, given an underscored keypath to the item""" path = keypath.split("_") for p in path[:-1]: @@ -630,8 +630,8 @@ p = int(p) except: pass - m = m[p] - return m[path[-1]] + message = message[p] + return message[path[-1]] #----- TEST HARNESS ----------------------------------------------------------- diff --git a/src/legacy.py b/src/legacy.py index f92e06c..af86081 100644 --- a/src/legacy.py +++ b/src/legacy.py @@ -34,7 +34,7 @@ # Prebuild all possible message up front, to make switching code faster HOUSE_ADDRESS = None # Use default energenie quasi-random address 0x6C6C6 -#HOUSE_ADDRESS = 0xA0170 # Captured address of David's RF hand controller +##HOUSE_ADDRESS = 0xA0170 # Captured address of David's RF hand controller ALL_ON = encoder.build_switch_msg(True, house_address=HOUSE_ADDRESS) ONE_ON = encoder.build_switch_msg(True, device_address=1, house_address=HOUSE_ADDRESS) @@ -138,10 +138,10 @@ radio.modulation(ook=True) try: - #pattern_test() + ##pattern_test() legacy_learn_mode() legacy_switch_loop() - #switch1_loop() + ##switch1_loop() finally: radio.finished() diff --git a/src/monitor.py b/src/monitor.py index 8ac2945..8d8d92b 100644 --- a/src/monitor.py +++ b/src/monitor.py @@ -32,7 +32,7 @@ while True: # See if there is a payload, and if there is, process it if radio.isReceiveWaiting(): - #trace("receiving payload") + ##trace("receiving payload") payload = radio.receive() try: decoded = OpenThings.decode(payload) @@ -43,7 +43,7 @@ OpenThings.showMessage(decoded) # Any device that reports will be added to the non-persistent directory Registry.update(decoded) - #trace(decoded) + ##trace(decoded) Logger.logMessage(decoded) # Process any JOIN messages by sending back a JOIN-ACK to turn the LED off @@ -52,12 +52,10 @@ print("Empty record:%s" % decoded) else: # assume only 1 rec in a join, for now - #TODO: use OpenThings.getFromMessage("header_mfrid") if decoded["recs"][0]["paramid"] == OpenThings.PARAM_JOIN: - header = decoded["header"] - mfrid = header["mfrid"] - productid = header["productid"] - sensorid = header["sensorid"] + mfrid = OpenThings.getFromMessage(decoded, "header_mfrid") + productid = OpenThings.getFromMessage(decoded, "header_productid") + sensorid = OpenThings.getFromMessage(decoded, "header_sensorid") Messages.send_join_ack(radio, mfrid, productid, sensorid) diff --git a/src/switch.py b/src/switch.py index 9a19a39..78e5962 100644 --- a/src/switch.py +++ b/src/switch.py @@ -18,7 +18,7 @@ # Increase this if you have lots of switches, so that the receiver has enough # time to receive update messages, otherwise your devices won't make it into # the device directory. -TX_RATE = 10 # seconds between each switch change cycle +TX_RATE = 2 # seconds between each switch change cycle def warning(msg): @@ -36,7 +36,7 @@ # See if there is a payload, and if there is, process it if radio.isReceiveWaiting(): - #trace("receiving payload") + ##trace("receiving payload") payload = radio.receive() try: decoded = OpenThings.decode(payload) @@ -47,7 +47,7 @@ OpenThings.showMessage(decoded) # Any device that reports will be added to the non-persistent directory Registry.update(decoded) - #trace(decoded) + ##trace(decoded) # Process any JOIN messages by sending back a JOIN-ACK to turn the LED off if len(decoded["recs"]) == 0: @@ -55,12 +55,10 @@ print("Empty record:%s" % decoded) else: # assume only 1 rec in a join, for now - #TODO: use OpenThings.getFromMessage("header_mfrid") if decoded["recs"][0]["paramid"] == OpenThings.PARAM_JOIN: - header = decoded["header"] - mfrid = header["mfrid"] - productid = header["productid"] - sensorid = header["sensorid"] + mfrid = OpenThings.getFromMessage(decoded, "header_mfrid") + productid = OpenThings.getFromMessage(decoded, "header_productid") + sensorid = OpenThings.getFromMessage(decoded, "header_sensorid") Messages.send_join_ack(radio, mfrid, productid, sensorid) @@ -96,17 +94,27 @@ if __name__ == "__main__": - trace("starting switch tester") + trace("starting switch tester - (in TX-only mode)") radio.init() OpenThings.init(Devices.CRYPT_PID) + # Seed the registry with a known device, to simplify tx-only testing + SENSOR_ID = 0x6AB # TODO capture this from a known device before testing + device_header = OpenThings.alterMessage(Messages.REGISTERED_SENSOR, + header_mfrid = Devices.MFRID, + header_productid = Devices.PRODUCTID_MIHO005, # adaptor plus + header_sensorid = SENSOR_ID) + Registry.updateDirectory(device_header) + + sendSwitchTimer = Timer(TX_RATE, 1) # every n seconds offset by initial 1 switch_state = 0 # OFF - radio.receiver() + ##radio.receiver() + radio.transmitter() try: while True: - switch_sniff_loop() + ##switch_sniff_loop() switch_toggle_loop() finally: