diff --git a/doc/devices_classes_branch.txt b/doc/devices_classes_branch.txt index 6550120..5c5a748 100644 --- a/doc/devices_classes_branch.txt +++ b/doc/devices_classes_branch.txt @@ -409,22 +409,17 @@ -------------------------------------------------------------------------------- TODO NEXT -* test on real hardware to make sure existing examples still work - with the new Message abstraction: - - legacy.py monitor.py switch.py combined.py - ----- - +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 combined.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) +* see if we can rewrite combined.py using the new device classes + (this is tx only with a seeded registry) + + ---- * write the Router() as a table that routes messages to device class instances in the registry @@ -434,7 +429,6 @@ incoming messages - probably a Router.loop() that is called by the main app loop (or wrapped in a thread later) - * test a synthetic receive route to make sure receive messages can be routed and decoded by different device classes, pump the Router.loop() manually diff --git a/src/energenie/Devices.py b/src/energenie/Devices.py index 53d3f7f..efff42a 100644 --- a/src/energenie/Devices.py +++ b/src/energenie/Devices.py @@ -252,7 +252,7 @@ class Device(): """A generic connected device abstraction""" - def __init__(self, air_interface=None, device_id=None): + def __init__(self, device_id=None, air_interface=None): self.air_interface = air_interface self.device_id = device_id class Config(): pass @@ -295,8 +295,8 @@ class EnergenieDevice(Device): """An abstraction for any kind of Energenie connected device""" - def __init__(self, air_interface=None, device_id=None): - Device.__init__(self, air_interface, device_id) + def __init__(self, device_id=None, air_interface=None): + Device.__init__(self, device_id, air_interface) def get_device_id(self): # -> id:int return self.device_id @@ -306,11 +306,18 @@ class LegacyDevice(EnergenieDevice): + DEFAULT_HOUSE_ADDRESS = 0x6C6C6 + """An abstraction for Energenie green button legacy OOK devices""" - def __init__(self, air_interface=None, device_id=None): + def __init__(self, device_id=None, air_interface=None): if air_interface == None: air_interface == ook_interface - EnergenieDevice.__init__(self, ook_interface, device_id) + if device_id == None: + device_id = (LegacyDevice.DEFAULT_HOUSE_ADDRESS, 1) + elif type(device_id) == int: + device_id = (LegacyDevice.DEFAULT_HOUSE_ADDRESS, device_id) + EnergenieDevice.__init__(self, device_id, ook_interface) + #TODO: These might now just be implied by the ook_interface adaptor self.config.frequency = 433.92 self.config.modulation = "OOK" self.config.codec = "4bit" @@ -338,10 +345,10 @@ class MiHomeDevice(EnergenieDevice): """An abstraction for Energenie new style MiHome FSK devices""" - def __init__(self, air_interface=None, device_id=None): + def __init__(self, device_id=None, air_interface=None): if air_interface == None: air_interface = fsk_interface - EnergenieDevice.__init__(self, air_interface, device_id) + EnergenieDevice.__init__(self, device_id, air_interface) self.config.frequency = 433.92 self.config.modulation = "FSK" self.config.codec = "OpenThings" @@ -403,9 +410,8 @@ class ENER002(LegacyDevice): """A green-button switch""" - def __init__(self, air_interface=None, device_id=None): - LegacyDevice.__init__(self, air_interface, device_id) - self.device_id = device_id # (house_address, device_index) + def __init__(self, device_id, air_interface=None): + LegacyDevice.__init__(self, device_id, air_interface) self.config.tx_repeats = 8 self.capabilities.switch = True self.capabilities.receive = True @@ -435,8 +441,8 @@ class MIHO005(MiHomeDevice): """An Energenie MiHome Adaptor Plus""" - def __init__(self, air_interface=None, device_id=None): - MiHomeDevice.__init__(self, air_interface, device_id) + def __init__(self, device_id, air_interface=None): + MiHomeDevice.__init__(self, device_id, air_interface) self.product_id = PRODUCTID_MIHO005 class Readings(): switch = None @@ -553,8 +559,8 @@ class MIHO006(MiHomeDevice): """An Energenie MiHome Home Monitor""" - def __init__(self, air_interface=None, device_id=None): - MiHomeDevice.__init__(self, air_interface, device_id) + def __init__(self, device_id, air_interface=None): + MiHomeDevice.__init__(self, device_id, air_interface) self.product_id = PRODUCTID_MIHO006 class Readings(): battery_voltage = None @@ -571,8 +577,8 @@ class MIHO013(MiHomeDevice): """An Energenie MiHome eTRV Radiator Valve""" - def __init__(self, air_interface=None, device_id=None): - MiHomeDevice.__init__(self, air_interface, device_id) + def __init__(self, device_id, air_interface=None): + MiHomeDevice.__init__(self, device_id, air_interface) self.product_id = PRODUCTID_MIHO013 class Readings(): battery_voltage = None diff --git a/src/energenie/__init__.py b/src/energenie/__init__.py index e69de29..a2227b1 100644 --- a/src/energenie/__init__.py +++ b/src/energenie/__init__.py @@ -0,0 +1,31 @@ +# energenie.py 24/05/2016 D.J.Whale +# +# Provides the app writer with a simple single module interface to everything. +# At the moment, this just hides the fact that the radio module needs to be initialised +# at the start and cleaned up at the end. +# +# Future versions of this *might* also start receive monitor or scheduler threads. + +try: + # Python 3 + from . import radio + from . import Devices + from . import Registry +except ImportError: + # Python 2 + import radio + import Devices + import Registry + + +def init(): + """Start the Energenie system running""" + radio.init() + + +def finished(): + """Cleanly close the Energenie system when finished""" + radio.finished() + + +# END \ No newline at end of file diff --git a/src/energenie/radio.py b/src/energenie/radio.py index 3384a6e..dcaf44c 100644 --- a/src/energenie/radio.py +++ b/src/energenie/radio.py @@ -20,8 +20,8 @@ #TODO: Would like to add RSSI measurements and reporting to the metadata that #comes back with received packets. -LIBNAME = "drv/radio_rpi.so" -##LIBNAME = "drv/radio_mac.so" # testing +##LIBNAME = "drv/radio_rpi.so" +LIBNAME = "drv/radio_mac.so" # testing import time import ctypes diff --git a/src/legacy.py b/src/legacy.py index f4f693f..17db0a2 100644 --- a/src/legacy.py +++ b/src/legacy.py @@ -1,57 +1,25 @@ # legacy.py 17/03/2016 D.J.Whale # -# Note: This is a test harness only, to prove that the underlying OOK -# radio support for legacy plugs is working. -# Please don't use this as a basis for building your applications from. -# Another higher level device API will be designed once this has been -# completely verified. +# Control up to 4 legacy green-button sockets import time +import energenie -from energenie import TwoBit, radio - -# How many times to send messages in the driver fast loop -# Present version of driver limits to 15 -# but this restriction will be lifted soon -# 4800bps, burst transmit time at 15 repeats is 400mS -# 1 payload takes 26ms -# 75 payloads takes 2s -INNER_TIMES = 16 - -# how many times to send messages in the API slow loop -# this is slower than using the driver, and will introduce tiny -# inter-burst delays -OUTER_TIMES = 1 - -# delay in seconds between each application switch message 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 + readin = raw_input # Python 2 except NameError: - readin = input # python 3 - - -#----- TEST APPLICATION ------------------------------------------------------- - -# 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 - -ALL_ON = TwoBit.encode_switch_message(True, house_address=HOUSE_ADDRESS) -ONE_ON = TwoBit.encode_switch_message(True, device_address=1, house_address=HOUSE_ADDRESS) -TWO_ON = TwoBit.encode_switch_message(True, device_address=2, house_address=HOUSE_ADDRESS) -THREE_ON = TwoBit.encode_switch_message(True, device_address=3, house_address=HOUSE_ADDRESS) -FOUR_ON = TwoBit.encode_switch_message(True, device_address=4, house_address=HOUSE_ADDRESS) -ON_MSGS = [ALL_ON, ONE_ON, TWO_ON, THREE_ON, FOUR_ON] - -ALL_OFF = TwoBit.encode_switch_message(False, house_address=HOUSE_ADDRESS) -ONE_OFF = TwoBit.encode_switch_message(False, device_address=1, house_address=HOUSE_ADDRESS) -TWO_OFF = TwoBit.encode_switch_message(False, device_address=2, house_address=HOUSE_ADDRESS) -THREE_OFF = TwoBit.encode_switch_message(False, device_address=3, house_address=HOUSE_ADDRESS) -FOUR_OFF = TwoBit.encode_switch_message(False, device_address=4, house_address=HOUSE_ADDRESS) -OFF_MSGS = [ALL_OFF, ONE_OFF, TWO_OFF, THREE_OFF, FOUR_OFF] + readin = input # Python 3 def get_yes_no(): @@ -63,21 +31,21 @@ def legacy_learn_mode(): - """Give the user a chance to learn any switches""" - print("Do you want to program any switches?") + """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 switch_no in range(1,5): - print("Learn switch %d?" % switch_no) + 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 switch %d for 5 secs until LED flashes" % switch_no) + 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") - radio.transmit(ON_MSGS[switch_no], OUTER_TIMES, INNER_TIMES) + sockets[1].turn_on() time.sleep(APP_DELAY) print("Device should now be programmed") @@ -86,67 +54,53 @@ for i in range(4): time.sleep(APP_DELAY) print("OFF") - radio.transmit(OFF_MSGS[switch_no], OUTER_TIMES, INNER_TIMES) + sockets[socket_no].turn_off() + time.sleep(APP_DELAY) print("ON") - radio.transmit(ON_MSGS[switch_no], OUTER_TIMES, INNER_TIMES) + sockets[socket_no].turn_on() + print("Test completed") -def legacy_switch_loop(): - """Turn all switches on or off every few seconds""" +def legacy_socket_loop(): + """Turn all sockets on or off every few seconds""" while True: - for switch_no in range(5): - # switch_no 0 is ALL, then 1=1, 2=2, 3=3, 4=4 + for socket_no in range(5): + # socket_no 0 is ALL, then 1=1, 2=2, 3=3, 4=4 # ON - print("switch %d ON" % switch_no) - radio.transmit(ON_MSGS[switch_no], OUTER_TIMES, INNER_TIMES) + print("socket %d ON" % socket_no) + sockets[socket_no].turn_on() time.sleep(APP_DELAY) # OFF - print("switch %d OFF" % switch_no) - radio.transmit(OFF_MSGS[switch_no], OUTER_TIMES, INNER_TIMES) + print("socket %d OFF" % socket_no) + sockets[socket_no].turn_off() time.sleep(APP_DELAY) -def switch1_loop(): - """Repeatedly turn switch 1 ON then OFF""" +def socket1_loop(): + """Repeatedly turn socket 1 ON then OFF""" while True: - print("Switch 1 ON") - radio.transmit(ON_MSGS[1], OUTER_TIMES, INNER_TIMES) + print("Plug 1 ON") + sockets[1].turn_on() time.sleep(APP_DELAY) - print("Switch 1 OFF") - radio.transmit(OFF_MSGS[1], OUTER_TIMES, INNER_TIMES) + print("Plug 1 OFF") + sockets[1].turn_off() time.sleep(APP_DELAY) - -def pattern_test(): - """Test all patterns""" - while True: - p = readin("number 0..F") - p = int(p, 16) - msg = TwoBit.encode_test_message(p) - print("pattern %s payload %s" % (str(hex(p)), TwoBit.ashex(msg))) - radio.send_payload(msg, OUTER_TIMES, INNER_TIMES) - - if __name__ == "__main__": - - print("starting legacy switch tester") - print("radio init") - radio.init() - print("radio as OOK") - radio.modulation(ook=True) + print("starting legacy socket tester") + energenie.init() try: - ##pattern_test() legacy_learn_mode() - legacy_switch_loop() - ##switch1_loop() + legacy_socket_loop() + ##socket1_loop() finally: - radio.finished() + energenie.finished() # END