diff --git a/doc/devices_classes_branch.txt b/doc/devices_classes_branch.txt index 5c5a748..8aaadd8 100644 --- a/doc/devices_classes_branch.txt +++ b/doc/devices_classes_branch.txt @@ -410,9 +410,6 @@ TODO NEXT DONE -* see if we can rewrite legacy.py using the new device classes - (this is tx only with a seeded registry) - * see if we can rewrite switch.py using the new device classes (might use a seeded registry and remove the join logic) diff --git a/src/cleanup.py b/src/cleanup.py deleted file mode 100644 index 2c430e6..0000000 --- a/src/cleanup.py +++ /dev/null @@ -1,18 +0,0 @@ -# cleanup.py 05/04/2016 D.J.Whale -# -# Put all used GPIO pins into an input state -# Useful to recover from a crash - -import RPi.GPIO as GPIO -GPIO.setmode(GPIO.BCM) - -GPIO.setup(27, GPIO.IN) # Green LED -GPIO.setup(22, GPIO.IN) # Red LED -GPIO.setup(7, GPIO.IN) # CS -GPIO.setup(8, GPIO.IN) # CS -GPIO.setup(11, GPIO.IN) # SCLK -GPIO.setup(10, GPIO.IN) # MOSI -GPIO.setup(9, GPIO.IN) # MISO -GPIO.setup(25, GPIO.IN) # RESET - -GPIO.cleanup() diff --git a/src/cleanup_GPIO.py b/src/cleanup_GPIO.py new file mode 100644 index 0000000..2c430e6 --- /dev/null +++ b/src/cleanup_GPIO.py @@ -0,0 +1,18 @@ +# cleanup.py 05/04/2016 D.J.Whale +# +# Put all used GPIO pins into an input state +# Useful to recover from a crash + +import RPi.GPIO as GPIO +GPIO.setmode(GPIO.BCM) + +GPIO.setup(27, GPIO.IN) # Green LED +GPIO.setup(22, GPIO.IN) # Red LED +GPIO.setup(7, GPIO.IN) # CS +GPIO.setup(8, GPIO.IN) # CS +GPIO.setup(11, GPIO.IN) # SCLK +GPIO.setup(10, GPIO.IN) # MOSI +GPIO.setup(9, GPIO.IN) # MISO +GPIO.setup(25, GPIO.IN) # RESET + +GPIO.cleanup() diff --git a/src/combined.py b/src/combined.py deleted file mode 100644 index b3be277..0000000 --- a/src/combined.py +++ /dev/null @@ -1,67 +0,0 @@ -# combined.py 15/05/2016 D.J.Whale -# -# A simple demo of combining both FSK (MiHome) and OOK (green button legacy) -# -# NOTE: This is only a test harness. -# If you really want a nice way to control these devices, wait for the 'device classes' -# issues to be implemented and tested on top of the raw radio interface, as these -# will be much nicer to use. - -import time -from energenie import OpenThings, radio, TwoBit, Devices - -# build FSK messages for MiHome purple - -OpenThings.init(Devices.CRYPT_PID) - -PURPLE_ID = 0x68B # captured from a real device using Monitor.py -m = OpenThings.Message(Devices.SWITCH) -m.set(header_sensorid=PURPLE_ID, recs_SWITCH_STATE_value=1) -purple_on = OpenThings.encode(m) - -m = OpenThings.Message(Devices.SWITCH) -m.set(header_sensorid=PURPLE_ID, recs_SWITCH_STATE_value=0) -purple_off = OpenThings.encode(m) - -# build OOK messages for legacy green button - -GREEN_ON = TwoBit.encode_switch_message(True, device_address=1) -GREEN_OFF = TwoBit.encode_switch_message(False, device_address=1) - - -def switch_loop(): - print("Turning green ON") - radio.modulation(ook=True) - radio.transmit(GREEN_ON) - time.sleep(0.5) - - print("Turning purple ON") - radio.modulation(fsk=True) - radio.transmit(purple_on, inner_times=2) - time.sleep(2) - - print("Turning green OFF") - radio.modulation(ook=True) - radio.transmit(GREEN_OFF) - time.sleep(0.5) - - print("Turning purple OFF") - radio.modulation(fsk=True) - radio.transmit(purple_off, inner_times=2) - time.sleep(2) - - -if __name__ == "__main__": - - print("starting combined switch tester") - print("radio init") - radio.init() - - try: - while True: - switch_loop() - - finally: - radio.finished() - -# END diff --git a/src/control_both.py b/src/control_both.py new file mode 100644 index 0000000..cfff59c --- /dev/null +++ b/src/control_both.py @@ -0,0 +1,67 @@ +# control_both.py 15/05/2016 D.J.Whale +# +# A simple demo of combining both FSK (MiHome) and OOK (green button legacy) +# +# NOTE: This is only a test harness. +# If you really want a nice way to control these devices, wait for the 'device classes' +# issues to be implemented and tested on top of the raw radio interface, as these +# will be much nicer to use. + +import time +from energenie import OpenThings, radio, TwoBit, Devices + +# build FSK messages for MiHome purple + +OpenThings.init(Devices.CRYPT_PID) + +PURPLE_ID = 0x68B # captured from a real device using Monitor.py +m = OpenThings.Message(Devices.SWITCH) +m.set(header_sensorid=PURPLE_ID, recs_SWITCH_STATE_value=1) +purple_on = OpenThings.encode(m) + +m = OpenThings.Message(Devices.SWITCH) +m.set(header_sensorid=PURPLE_ID, recs_SWITCH_STATE_value=0) +purple_off = OpenThings.encode(m) + +# build OOK messages for legacy green button + +GREEN_ON = TwoBit.encode_switch_message(True, device_address=1) +GREEN_OFF = TwoBit.encode_switch_message(False, device_address=1) + + +def switch_loop(): + print("Turning green ON") + radio.modulation(ook=True) + radio.transmit(GREEN_ON) + time.sleep(0.5) + + print("Turning purple ON") + radio.modulation(fsk=True) + radio.transmit(purple_on, inner_times=2) + time.sleep(2) + + print("Turning green OFF") + radio.modulation(ook=True) + radio.transmit(GREEN_OFF) + time.sleep(0.5) + + print("Turning purple OFF") + radio.modulation(fsk=True) + radio.transmit(purple_off, inner_times=2) + time.sleep(2) + + +if __name__ == "__main__": + + print("starting combined switch tester") + print("radio init") + radio.init() + + try: + while True: + switch_loop() + + finally: + radio.finished() + +# END diff --git a/src/control_legacy.py b/src/control_legacy.py new file mode 100644 index 0000000..c51ea99 --- /dev/null +++ b/src/control_legacy.py @@ -0,0 +1,106 @@ +# legacy.py 17/03/2016 D.J.Whale +# +# Control up to 4 legacy green-button sockets (or MiHome control-only sockets) + +import time +import energenie + +APP_DELAY = 1 +##energenie.radio.DEBUG = True + +all_sockets = energenie.Devices.ENER002(0) +socket1 = energenie.Devices.ENER002(1) +socket2 = energenie.Devices.ENER002(2) +socket3 = energenie.Devices.ENER002(3) +socket4 = energenie.Devices.ENER002(4) +sockets = [all_sockets, socket1, socket2, socket3, socket4] + + +try: + readin = raw_input # Python 2 +except NameError: + readin = input # Python 3 + + +def get_yes_no(): + """Get a simple yes or no answer""" + answer = readin() + if answer.upper() in ['Y', 'YES']: + return True + return False + + +def legacy_learn_mode(): + """Give the user a chance to learn any sockets""" + print("Do you want to program any sockets?") + y = get_yes_no() + if not y: + return + + for socket_no in range(1,5): + print("Learn socket %d?" % socket_no) + y = get_yes_no() + if y: + print("Press the LEARN button on any socket %d for 5 secs until LED flashes" % socket_no) + readin("press ENTER when LED is flashing") + + print("ON") + sockets[1].turn_on() + time.sleep(APP_DELAY) + + print("Device should now be programmed") + + print("Testing....") + for i in range(4): + time.sleep(APP_DELAY) + print("OFF") + sockets[socket_no].turn_off() + + time.sleep(APP_DELAY) + print("ON") + sockets[socket_no].turn_on() + + print("Test completed") + + +def legacy_socket_loop(): + """Turn all sockets on or off every few seconds""" + + while True: + for socket_no in range(5): + # socket_no 0 is ALL, then 1=1, 2=2, 3=3, 4=4 + # ON + print("socket %d ON" % socket_no) + sockets[socket_no].turn_on() + time.sleep(APP_DELAY) + + # OFF + print("socket %d OFF" % socket_no) + sockets[socket_no].turn_off() + time.sleep(APP_DELAY) + + +def socket1_loop(): + """Repeatedly turn socket 1 ON then OFF""" + while True: + print("Plug 1 ON") + sockets[1].turn_on() + time.sleep(APP_DELAY) + + print("Plug 1 OFF") + sockets[1].turn_off() + time.sleep(APP_DELAY) + +if __name__ == "__main__": + print("starting legacy socket tester") + energenie.init() + + try: + legacy_learn_mode() + legacy_socket_loop() + ##socket1_loop() + finally: + energenie.finished() + +# END + diff --git a/src/control_mihome.py b/src/control_mihome.py new file mode 100644 index 0000000..96a9f82 --- /dev/null +++ b/src/control_mihome.py @@ -0,0 +1,57 @@ +# control_mihome.py 17/03/2016 D.J.Whale +# +# Control Energenie MiHome Adaptor or AdaptorPlus sockets + +import energenie +from Timer import Timer + +# Define this if you want to seed the registry with know device_id's for testing +# Alternatively, remove this line, and use discover_mihome.py to fill your registry +MY_SOCKET_IDS = [0x68b] + +TX_RATE = 2 # number of seconds to toggle the socket switches + +#----- TEST APPLICATION ------------------------------------------------------- + +def socket_toggle_loop(): + """Toggle the switch on all devices in the directory""" + + global socket_state + + if energenie.registry.size() > 0 and send_timer.check(): + print("Setting socket switches to %s" % str(socket_state)) + + for device in energenie.registry.devices(): #TODO: Make energenie.registry iterable + # Only try to toggle the switch for devices that actually have a switch + + if device.has_switch(): + print(" socket id %s" % device) + device.set_switch(socket_state) + + socket_state = not socket_state + + +if __name__ == "__main__": + + print("starting socket tester") + energenie.init() + + # Seed the registry with known devices to simplify tx-only testing + try: + for id in MY_SOCKET_IDS: + socket = energenie.Devices.MIHO005(id) + energenie.registry.add(socket, "socket_%s" % str(hex(id))) + except: + pass + + send_timer = Timer(TX_RATE, 1) # every n seconds offset by initial 1 + socket_state = False + + try: + while True: + socket_toggle_loop() + + finally: + energenie.finished() + +# END diff --git a/src/discover_mihome.py b/src/discover_mihome.py new file mode 100644 index 0000000..0605f94 --- /dev/null +++ b/src/discover_mihome.py @@ -0,0 +1,19 @@ +# discover_mihome.py 24/05/2016 D.J.Whale + +#TODO: Placeholder for mihome discovery example app. +# +# This will sit in permanent receive mode and listen for any device id reports, +# and when it sees one that is not in the user registry, it will add it. +# +# This will be useful for building up a user registry that can then be used +# with the control_mihome tester to actually toggle all the switches, +# and with the monitor_mihome to dump all message reports. + +# For the moment, you have to seed the registry in control_mihome with a known +# device_id to run the test (perhaps using an older version of the code to learn it). + +# This is only a temporary situation until the complete receive pipeline is written. +# But I want to implement this properly, rather than half-hearted. + +# END + diff --git a/src/energenie/Devices.py b/src/energenie/Devices.py index efff42a..b6191ff 100644 --- a/src/energenie/Devices.py +++ b/src/energenie/Devices.py @@ -438,6 +438,10 @@ } self.send_message(payload) + def __repr__(self): + return "ENER002(%s,%s)" % (str(hex(self.device_id[0]), str(self.device_id[1]))) + + class MIHO005(MiHomeDevice): """An Energenie MiHome Adaptor Plus""" @@ -458,6 +462,10 @@ self.capabilities.receive = True self.capabilities.switch = True + def __repr__(self): + return "MIHO005(%s)" % str(hex(self.device_id)) + + def incoming_message(self, payload): for rec in payload["recs"]: paramid = rec["paramid"] @@ -505,6 +513,12 @@ recs_SWITCH_STATE_value=False) self.send_message(payload) + def set_switch(self, state): + if state: + self.turn_on() + else: + self.turn_off() + #TODO: difference between 'is on and 'is requested on' #TODO: difference between 'is off' and 'is requested off' #TODO: switch state might be 'unknown' if not heard. diff --git a/src/energenie/Registry.py b/src/energenie/Registry.py index d62072e..ee4e8dd 100644 --- a/src/energenie/Registry.py +++ b/src/energenie/Registry.py @@ -93,6 +93,9 @@ def keys(self): return self.store.keys() + def size(self): + return len(self.store) + class DeviceRegistry(): # this is actions, so is this the 'RegistRAR'?? """A persistent registry for device class instance configurations""" @@ -147,6 +150,21 @@ print("DEVICE %s" % k) print(" %s" % self.store[k]) + def size(self): + """How many entries are there in the registry?""" + return self.store.size() + + + def devices(self): + """Get a list of all device classes in the registry""" + #TODO: Temporary method until we read up about iterable, so we can say + # for devices in energenie.registry + dl = [] + for k in self.store.keys(): + d = self.store[k] + dl.append(d) + return dl + registry = DeviceRegistry("registry.txt") diff --git a/src/energenie/__init__.py b/src/energenie/__init__.py index a2227b1..9e89d80 100644 --- a/src/energenie/__init__.py +++ b/src/energenie/__init__.py @@ -11,16 +11,20 @@ from . import radio from . import Devices from . import Registry + from . import OpenThings except ImportError: # Python 2 import radio import Devices import Registry + import OpenThings +registry = Registry.registry def init(): """Start the Energenie system running""" radio.init() + OpenThings.init(Devices.CRYPT_PID) def finished(): diff --git a/src/legacy.py b/src/legacy.py deleted file mode 100644 index 17db0a2..0000000 --- a/src/legacy.py +++ /dev/null @@ -1,106 +0,0 @@ -# legacy.py 17/03/2016 D.J.Whale -# -# Control up to 4 legacy green-button sockets - -import time -import energenie - -APP_DELAY = 1 -energenie.radio.DEBUG = True - -all_sockets = energenie.Devices.ENER002(0) -socket1 = energenie.Devices.ENER002(1) -socket2 = energenie.Devices.ENER002(2) -socket3 = energenie.Devices.ENER002(3) -socket4 = energenie.Devices.ENER002(4) -sockets = [all_sockets, socket1, socket2, socket3, socket4] - - -try: - readin = raw_input # Python 2 -except NameError: - readin = input # Python 3 - - -def get_yes_no(): - """Get a simple yes or no answer""" - answer = readin() - if answer.upper() in ['Y', 'YES']: - return True - return False - - -def legacy_learn_mode(): - """Give the user a chance to learn any sockets""" - print("Do you want to program any sockets?") - y = get_yes_no() - if not y: - return - - for socket_no in range(1,5): - print("Learn socket %d?" % socket_no) - y = get_yes_no() - if y: - print("Press the LEARN button on any socket %d for 5 secs until LED flashes" % socket_no) - readin("press ENTER when LED is flashing") - - print("ON") - sockets[1].turn_on() - time.sleep(APP_DELAY) - - print("Device should now be programmed") - - print("Testing....") - for i in range(4): - time.sleep(APP_DELAY) - print("OFF") - sockets[socket_no].turn_off() - - time.sleep(APP_DELAY) - print("ON") - sockets[socket_no].turn_on() - - print("Test completed") - - -def legacy_socket_loop(): - """Turn all sockets on or off every few seconds""" - - while True: - for socket_no in range(5): - # socket_no 0 is ALL, then 1=1, 2=2, 3=3, 4=4 - # ON - print("socket %d ON" % socket_no) - sockets[socket_no].turn_on() - time.sleep(APP_DELAY) - - # OFF - print("socket %d OFF" % socket_no) - sockets[socket_no].turn_off() - time.sleep(APP_DELAY) - - -def socket1_loop(): - """Repeatedly turn socket 1 ON then OFF""" - while True: - print("Plug 1 ON") - sockets[1].turn_on() - time.sleep(APP_DELAY) - - print("Plug 1 OFF") - sockets[1].turn_off() - time.sleep(APP_DELAY) - -if __name__ == "__main__": - print("starting legacy socket tester") - energenie.init() - - try: - legacy_learn_mode() - legacy_socket_loop() - ##socket1_loop() - finally: - energenie.finished() - -# END - diff --git a/src/monitor.py b/src/monitor.py deleted file mode 100644 index 365607a..0000000 --- a/src/monitor.py +++ /dev/null @@ -1,78 +0,0 @@ -# monitor.py 27/09/2015 D.J.Whale -# -# Monitor Energine MiHome plugs - -# Note, this is *only* a test program, to exercise the lower level code. -# Don't expect this to be a good starting point for an application. -# Consider waiting for me to finish developing the device object interface first. -# -# However, it will log all messages from MiHome monitor, adaptor plus and house monitor -# to a CSV log file, so could be the basis for a non-controlling energy logging app. - -from energenie import Registry, Devices, OpenThings, radio -import time -import Logger - -def warning(msg): - print("warning:%s" % str(msg)) - - -def trace(msg): - print("monitor:%s" % str(msg)) - - -#----- TEST APPLICATION ------------------------------------------------------- - -def monitor_loop(): - """Capture any incoming messages and log to CSV file""" - - radio.receiver() - - while True: - # See if there is a payload, and if there is, process it - if radio.is_receive_waiting(): - trace("receiving payload") - payload = radio.receive() - now = time.time() - try: - decoded = OpenThings.decode(payload, receive_timestamp=now) - except OpenThings.OpenThingsException as e: - warning("Can't decode payload:" + str(e)) - continue - - #TODO: Consider putting a 'timestamp' in a received decoded message - print(now) #TODO: improve formatting of timestamp - ##print(decoded) - decoded.dump() - # Any device that reports will be added to the non-persistent directory - Registry.update(decoded) - ##trace(decoded) - Logger.logMessage(decoded) - - # Process any JOIN messages by sending back a JOIN-ACK to turn the LED off - if len(decoded["recs"]) == 0: - # handle messages with zero recs in them silently - print("Empty record:%s" % decoded) - else: - # assume only 1 rec in a join, for now - #TODO: 'if OpenThings.PARAM_JOIN in decoded:' - special magic handling for param_id - if decoded["recs"][0]["paramid"] == OpenThings.PARAM_JOIN: - mfrid = decoded.get("header_mfrid") - productid = decoded.get("header_productid") - sensorid = decoded.get("header_sensorid") - Devices.send_join_ack(radio, mfrid, productid, sensorid) - - -if __name__ == "__main__": - - trace("starting monitor tester") - radio.init() - OpenThings.init(Devices.CRYPT_PID) - - try: - monitor_loop() - - finally: - radio.finished() - -# END diff --git a/src/monitor_mihome.py b/src/monitor_mihome.py new file mode 100644 index 0000000..c36e694 --- /dev/null +++ b/src/monitor_mihome.py @@ -0,0 +1,78 @@ +# monitor_mihome.py 27/09/2015 D.J.Whale +# +# Monitor Energine MiHome sockets + +# Note, this is *only* a test program, to exercise the lower level code. +# Don't expect this to be a good starting point for an application. +# Consider waiting for me to finish developing the device object interface first. +# +# However, it will log all messages from MiHome monitor, adaptor plus and house monitor +# to a CSV log file, so could be the basis for a non-controlling energy logging app. + +from energenie import Registry, Devices, OpenThings, radio +import time +import Logger + +def warning(msg): + print("warning:%s" % str(msg)) + + +def trace(msg): + print("monitor:%s" % str(msg)) + + +#----- TEST APPLICATION ------------------------------------------------------- + +def monitor_loop(): + """Capture any incoming messages and log to CSV file""" + + radio.receiver() + + while True: + # See if there is a payload, and if there is, process it + if radio.is_receive_waiting(): + trace("receiving payload") + payload = radio.receive() + now = time.time() + try: + decoded = OpenThings.decode(payload, receive_timestamp=now) + except OpenThings.OpenThingsException as e: + warning("Can't decode payload:" + str(e)) + continue + + #TODO: Consider putting a 'timestamp' in a received decoded message + print(now) #TODO: improve formatting of timestamp + ##print(decoded) + decoded.dump() + # Any device that reports will be added to the non-persistent directory + Registry.update(decoded) + ##trace(decoded) + Logger.logMessage(decoded) + + # Process any JOIN messages by sending back a JOIN-ACK to turn the LED off + if len(decoded["recs"]) == 0: + # handle messages with zero recs in them silently + print("Empty record:%s" % decoded) + else: + # assume only 1 rec in a join, for now + #TODO: 'if OpenThings.PARAM_JOIN in decoded:' - special magic handling for param_id + if decoded["recs"][0]["paramid"] == OpenThings.PARAM_JOIN: + mfrid = decoded.get("header_mfrid") + productid = decoded.get("header_productid") + sensorid = decoded.get("header_sensorid") + Devices.send_join_ack(radio, mfrid, productid, sensorid) + + +if __name__ == "__main__": + + trace("starting monitor tester") + radio.init() + OpenThings.init(Devices.CRYPT_PID) + + try: + monitor_loop() + + finally: + radio.finished() + +# END diff --git a/src/switch.py b/src/switch.py deleted file mode 100644 index 80aa693..0000000 --- a/src/switch.py +++ /dev/null @@ -1,121 +0,0 @@ -# switch.py 17/03/2016 D.J.Whale -# -# Control Energenie switches. -# Note, at the moment, this only works with MiHome Adaptor Plus devices -# because the 'sensorid' is discovered via the monitor message. -# You could probably fake it by preloading the directory with your sensorid -# if you know what it is. - -# Note, this is *only* a test program, to exercise the lower level code. -# Don't expect this to be a good starting point for an application. -# Consider waiting for me to finish developing the device object interface first. - -import time -from energenie import Devices, Registry, OpenThings, radio -from Timer import Timer - - -# 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 = 2 # seconds between each switch change cycle - - -def warning(msg): - print("warning:%s" % str(msg)) - - -def trace(msg): - print("monitor:%s" % str(msg)) - - -#----- TEST APPLICATION ------------------------------------------------------- - -def switch_sniff_loop(): - """Listen to sensor messages and add them to the Registry""" - - # See if there is a payload, and if there is, process it - if radio.is_receive_waiting(): - ##trace("receiving payload") - payload = radio.receive() - now = time.time() - try: - decoded = OpenThings.decode(payload, receive_timestamp=now) - except OpenThings.OpenThingsException as e: - warning("Can't decode payload:" + str(e)) - return - - print(now) #TODO: better timestamp handling - decoded.dump() - # Any device that reports will be added to the non-persistent directory - Registry.update(decoded) - ##trace(decoded) - - # Process any JOIN messages by sending back a JOIN-ACK to turn the LED off - if len(decoded["recs"]) == 0: - # handle messages with zero recs in them silently - print("Empty record:%s" % decoded) - else: - # assume only 1 rec in a join, for now - if decoded["recs"][0]["paramid"] == OpenThings.PARAM_JOIN: - mfrid = decoded.get("header_mfrid") - productid = decoded.get("header_productid") - sensorid = decoded.get("header_sensorid") - Devices.send_join_ack(radio, mfrid, productid, sensorid) - - -def switch_toggle_loop(): - """Toggle the switch on all devices in the directory""" - - global switch_state - - if Registry.size() > 0 and sendSwitchTimer.check(): - print("transmit") - radio.transmitter() - - for sensorid in Registry.get_sensorids(): - # Only try to toggle the switch for devices that actually have a switch - header = Registry.get_info(sensorid)["header"] - mfrid = header["mfrid"] - productid = header["productid"] - - if Devices.hasSwitch(mfrid, productid): - request = OpenThings.Message(Devices.SWITCH) - request.set(header_sensorid=sensorid, recs_SWITCH_STATE_value=switch_state) - p = OpenThings.encode(request) - print("Sending switch message to %s %s" % (hex(productid), hex(sensorid))) - # Transmit multiple times, hope one of them gets through - radio.transmit(p, inner_times=2) - - radio.receiver() - switch_state = (switch_state+1) % 2 # toggle - - -if __name__ == "__main__": - - trace("starting switch tester") - radio.init() - OpenThings.init(Devices.CRYPT_PID) - - # Seed the registry with a known device, to simplify tx-only testing - SENSOR_ID = 0x68B # captured from a real device - device_header = OpenThings.Message(header_mfrid=Devices.MFRID, - header_productid=Devices.PRODUCTID_MIHO005, - header_sensorid=SENSOR_ID, - recs=[]) - Registry.update(device_header) - - - sendSwitchTimer = Timer(TX_RATE, 1) # every n seconds offset by initial 1 - switch_state = 0 # OFF - radio.receiver() - - try: - while True: - switch_sniff_loop() - switch_toggle_loop() - - finally: - radio.finished() - -# END