Newer
Older
pyenergenie / doc / classes.txt
DEVICE CLASSES

A device class is a scheme where user devices plugged into Energenie
product, can be referred to as objects within a user application.

It is a way of abstracting the on-air radio interface and Energenie
device specifics from a user application, such that the user can
code 'in the land of their devices'.


--------------------------------------------------------------------------------
REQUIREMENTS

1. To be able to write expressive and compact applications,
   that talk in the vocabulary of physical devices.

   a. All known Energenie devices to be modelled as classes inside a
      device database, and the capabilities and operations on those devices
      pre-written so they can be reused by a user application.

   b. An easy way for users to map energenie device intents to user
      device intents (such as by wrapping custom object vocabulary around
      the standard energenie device vocabulary) - e.g. room.heat() rather
      than plug.on()

  e.g. first level device names such as my_radiator or my_plug,
  or second level device names such as bedroom_radiator and kitchen_kettle
  (things plugged into devices)

  This will hide the detail of how messages get encoded and transported,
  and allows users to focus ore on the intents of the application, rather
  than the implementation details.


2. To be able to build a local registry of devices and their configurations,
   and refer to devices by name inside the application.

   a. to be configurable by learning (e.g. listen for messages such as
      a join message, and add the device to the registry)

   b. to be configurable by hand (e.g. hand entering the sensor id of
      a known device into the registry)

   c. to automatically build variables for the user program from the
      registry, so that users don't have to bother with lots of
      wiring up code every time their app starts.

   d. this registry must be persistable, e.g. save and restore to a disk file,
      so that on application startup, the device database is automatically loaded
      and objects created.

   e. the registry can be queried, such as 'find me all devices that are of
      type x' or 'find me all devices in location kitchen'.


3. To be able to command and query devices in a way that represents
   meaningful device-based intents (such as tv.on() and tv.get_power())

   a. received data values to be cached for deferred query, such as get_power()

   b. the last receipt time of data from a transmitting device to be known

   c. the next expected receipt time of data from a transmitting device to be known

   d. the last known state of a transmitting device to be known (e.g. switch state
      both by commanded state and retrieved state)


4. To be able to refer to user devices in an Energenie device agnostic way.

   e.g. it doesn't matter if the TV is plugged into a green button device,
   or a MiHome device. It is always tv.on() in the code.


5. To be able to instigate and manage learn mode from within an app

   a. To send specific commands to green button devices so they can
      learn the pattern

   b. To sniff for any messages from MiHome devices and capture them
      for later analysis and turning into device objects

   c. To process MiHome join requests, and send MiHome join acks


6. To completely hide the user from the on-air radio interface

   a. choosing the correct radio frequency and modulation automatically

   b. choosing the correct physical layer configuration automatically,
      such as message repeats for certain devices


Not as part of this work, but this should at least be enabled
by the design

7. To be able to build a well performing system
   with very few message collisions and message losses

   a. by dynamically learning report patterns of MiHome devices

   b. by intelligently deferring and schedulling transmit messages
      to avoid transmit slots of reporting devices

   c. to query device characteristics such as modulation scheme and msg repeats.
      also to estimate the transmit time of a particular message to help
      with message scheduling.


--------------------------------------------------------------------------------
DESIGN Devices.py

  to have a device class for each supported Energenie product.

  These classes to define the operations on that device, such as on() off()
  get_power().

  Radio interface configuration parameters to be associated with each
  device class, such as the modulation scheme and message repeat requirements.

  commands modelled by function calls such as turn_on()

  commanded state? (did we ask it to be on, when did we ask?)
  reported state? (did it tell us it is on, when did we learn it?)

  overall device state
    can this device receive commands?
    can this device transmit reports?
    have we seen this device this run?
    when did we last hear from it?
    when did we last talk to it?
    when do we expect to next hear from it?

  common device features
    it's manufacturer id
    it's product id
    it's sensor id

  yet unmodelled devices still to be usable to some degree
    for MiHome devices, a proxy class generated dynamically based on received message parameters.
    e.g. if it reports a TEMPERATURE field, then there should be an automatic get_temperature() method
    generated.

  an incoming message callback
    this already knows it is for the device, but it is up to the device to decode and action

  an outgoing message sender
    to be knitted to the on air interface proxy, but no radio handler code in the device class or instance.

  TODO
    possibly add callbacks such as when_turned_on() when_turned_off() etc.

    Where do unknown incoming messages get routed to? Need to at least log them somewhere.
    Although they won't necessarily route to a device class. But useful for learning semantics.
    Perhaps there is a single 'UnknownDevice' that is just a Device() base class, that
    captures all of these messages? But there might be multiple devices, so perhaps
    we could generate UnknownDevice instances (optionally) when we receive messages
    from something that we don't know what it is yet?


(do we need to know what our last sent request is, vs last known reported state?
e.g. if we have sent a request but not heard a response yet, this means we think we asked
it to turn on, but we don't yet know if it has done that. Some devices can't report
back, but some can, so it would be nice to have a four stage state machine for on/off)

(note, would be good to be able to persist the last message received on disk,
so that when code restarts, it knows the last send/receive time that was last processed.
i.e. a resumable state machine persisted to disk)

(note, a message scheduler if inserted in the middle, would do callbacks to say
that the request has been processed, so timestamps can be updated. Also same scheduler
could handle retries perhaps, if the device is tx and rx, then when you send a switch
change, it would normally report back that the switch had changed, so if you don't
get it, or if it is in the wrong state, could retry a send again until it changes)

(note, inner variables might have two versions for some devices, the requested
value and the confirmed value. If they are different, it means might still be
waiting for a reply, so can't guarantee the command was received yet)

Device
  get_manufacturer_id
  get_sensor_id
  get_product_id

  (these may need an ack-back from radio module to know it happened)
  ?get_last_receive_time
  ?get_last_send_time
  ?get_next_receive_time
  ?get_next_send_time

  incoming_message (OOK or OpenThings as appropriate, stripped of header? pydict?)
  send_message (a link out to the transport, could be mocked, for example)

  EnergenieDevice
    get_radio_config -> config_selector? (freq, modulation) config_parameters? (inner_repeats, delay, outer_repeats)
    has_switch
    can_send
    can_receive

    LegacyDevice
      ENER002
        turn_on
        turn_off

    MiHomeDevice
      MIHO005 (AdaptorPlus)
        turn_on
        turn_off
        is_on
        is_off
        get_switch
        get_voltage
        get_freq
        get_apparent
        get_reactive
        get_real
      MIHO006 (HomeMonitor)
        get_battery_voltage
        get_current
      MIHO012 (eTRV)
        ?get_battery_voltage
        ?get_ambient_temperature
        ?get_pipe_temperature
        set_setpoint_temperature
        ?get_valve_position ?is_on ?is_off
        ?set_valve_position ?turn_on ?turn_off

--------------------------------------------------------------------------------
DESIGN Registry.py



--------------------------------------------------------------------------------
GENERAL NOTES

(when configuring the system)
print("Press the learn button on the TV device")
energenie.start_learn(house_code=0xABCDE, index=2)
raw_input("Press enter when done")
energenie.stop_learn()

energenie.create_device("tv", energenie.device.ENER002, index=2, house_code=0xABCDE)
energenie.create_device("aquarium", energenie.device.MIHO005, address=0x1234)

(when running the system)

tv = energenie.get("tv")
aquarium = energenie.get("aquarium")

tv.off()
aquarium.on()
time.sleep(10)
if aquarium.power > 20:
print("Has the pump motor stalled??")

def just_turned_off(device):
print("Your user just turned off %s" % device)
print("I'm turning it back on!")
device.on()

aquarium.when_turned_off(just_turned_off)


--------------------------------------------------------------------------------

END