Skip to content

Commit

Permalink
Improves integration of mqtt
Browse files Browse the repository at this point in the history
  • Loading branch information
Dennis Muth committed Mar 13, 2019
1 parent f58e327 commit dab4eee
Show file tree
Hide file tree
Showing 12 changed files with 388 additions and 274 deletions.
17 changes: 3 additions & 14 deletions rpi433rc/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import os

from flask_restplus import Api

from ..business.devices import DeviceDict, MemoryState, DeviceRegistry
from ..business.rc433 import RC433
from ..business.publish import from_config

from ..config import VERSION

api = Api(
title='RPi433',
version=VERSION,
Expand All @@ -22,11 +17,5 @@
from .send import api as ns_send
api.add_namespace(ns_send)

from ..config import GPIO_OUT, CONFIG_DIR
config_file = os.path.join(CONFIG_DIR, 'devices.json')
device_store = DeviceDict.from_json(config_file)
device_state = MemoryState()
device_db = DeviceRegistry(device_store, device_state)

rc433 = RC433(gpio_out=GPIO_OUT)
publisher = from_config()
from ..factories import create_registry
device_db = create_registry()
9 changes: 1 addition & 8 deletions rpi433rc/api/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,6 @@ class DeviceSwitch(Resource):
@api.marshal_with(state)
def get(self, device_name, on_off):
from . import device_db
from . import rc433
from . import publisher

device = device_db.lookup(device_name)
res = rc433.switch_device(device, on_off)
if res:
# Mark the device as on resp. off
device_db.switch(device_name, on_off)
publisher.publish(device_name, on_off)
res = device_db.switch(device_name, on_off)
return {'state': on_off, 'result': res}
4 changes: 2 additions & 2 deletions rpi433rc/api/send.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ class SendCode(Resource):
@requires_auth
@api.marshal_with(code)
def get(self, code):
from . import rc433
return {'code': code, 'result': rc433.send_code(code)}
from . import device_db
return {'code': code, 'result': device_db.rc433.send_code(code)}
147 changes: 0 additions & 147 deletions rpi433rc/business/devices.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import json
from abc import abstractmethod
from collections import defaultdict

import attr

from schema import Schema, Or, Use, Optional

from .util import LogMixin
Expand Down Expand Up @@ -82,21 +80,6 @@ class SystemDevice(Device):
resend = attr.ib(converter=int, validator=lambda i, a, v: v > 0, default=3)


@attr.s
class StatefulDevice(object):
"""
Adds a state (on resp. off) to a device entity.
Example:
>>> device = Device('device1')
>>> StatefulDevice(device, True)
StatefulDevice(device=Device(device_name='device1'), state=True)
"""
device = attr.ib(validator=attr.validators.instance_of(Device))
state = attr.ib(validator=attr.validators.instance_of(bool))


__ALL_DEVICES__ = [CodeDevice, SystemDevice]


Expand Down Expand Up @@ -244,133 +227,3 @@ def lookup(self, device_name):
if res is None:
raise UnknownDeviceError("The requested device '{}' is unknown".format(device_name))
return res


class DeviceState(object):
"""
Abstract base class for a device state tracker.
"""
def _device_name(self, device_or_name):
if isinstance(device_or_name, Device):
return device_or_name.device_name

return device_or_name

@abstractmethod
def lookup(self, device_or_name):
"""
Lookup the state of a single device by the specified name or entity.
Args:
device_or_name: A real device entity (Device) or it's name
Returns:
Returns True if the device is currently on; otherwise False.
"""
pass

@abstractmethod
def switch(self, device_or_name, on):
"""
Switch on / off the specified device.
Args:
device_or_name: A real device entity (Device) or it's name
on: If True the device will be marked as on; otherwise off.
Returns:
None
"""
pass


class MemoryState(DeviceState):
"""
In-memory implementation of a device state mapping.
Example:
>>> dut = MemoryState()
>>> dut.lookup(Device('device1'))
False
>>> dut.switch(Device('device2'), True)
>>> dut.lookup(Device('device1')), dut.lookup(Device('device2'))
(False, True)
"""
def __init__(self):
self.states = defaultdict(bool)

def lookup(self, device_or_name):
"""
Lookup the state of a single device by the specified name or entity.
Args:
device_or_name: A real device entity (Device) or it's name
Returns:
Returns True if the device is currently on; otherwise False.
"""
return self.states.get(self._device_name(device_or_name), False)

def switch(self, device_or_name, on):
"""
Switch on / off the specified device.
Args:
device_or_name: A real device entity (Device) or it's name
on: If True the device will be marked as on; otherwise off.
Returns:
None
"""
self.states[self._device_name(device_or_name)] = on


@attr.s
class DeviceRegistry(DeviceStore, DeviceState):
"""
The device registry associates a DeviceStore (where the actual devices are stored / configured) and
a DeviceState (where the state of the devices are tracked).
Example:
>>> device_dict = {
... 'device1': {"code_on": 12345, 'code_off': "23456"},
... 'device2': {"system_code": "00010", "device_code": "2"}
... }
>>> dstore = DeviceDict(device_dict) # Instantiate the device store
>>> dstate = MemoryState() # Instantiate the device state
>>> dut = DeviceRegistry(dstore, dstate)
>>> (sorted(dut.list(), key=lambda e: e.device.device_name) ==
... [StatefulDevice(device=CodeDevice(device_name='device1', code_on=12345, code_off=23456), state=False),
... StatefulDevice(device=SystemDevice(device_name='device2', system_code='00010', device_code=2)
... , state=False)])
True
>>> ((dut.lookup('device1'), dut.switch('device1', True), dut.lookup('device1')) ==
... (StatefulDevice(device=CodeDevice(device_name='device1', code_on=12345, code_off=23456), state=False),
... None,
... StatefulDevice(device=CodeDevice(device_name='device1', code_on=12345, code_off=23456), state=True)))
True
"""
device_store = attr.ib(validator=attr.validators.instance_of(DeviceStore))
state = attr.ib(validator=attr.validators.instance_of(DeviceState))

def lookup(self, device):
if not isinstance(device, Device):
# Assuming a device name instead of a real device
device = self.device_store.lookup(device)

if device is None:
raise UnknownDeviceError("The requested device '{}' is unknown".format(str(device)))

return StatefulDevice(device=device, state=self.state.lookup(device.device_name))

def list(self):
return [self.lookup(device) for device in self.device_store.list()]

def switch(self, device_or_name, on):
self.state.switch(device_or_name, on)
77 changes: 0 additions & 77 deletions rpi433rc/business/publish.py

This file was deleted.

31 changes: 18 additions & 13 deletions rpi433rc/business/rc433.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import attr

from .devices import CodeDevice, StatefulDevice
from .devices import CodeDevice
from .util import LogMixin


class RFDeviceMock(object):
def __init__(self, *args, **kwargs):
pass

def enable_tx(self):
pass

def cleanup(self):
pass

def tx_code(self, code, **kwargs):
return True


try:
import rpi_rf
RFDevice = rpi_rf.RFDevice
except ImportError:
# Mock it on non-rpi machines
class RFDevice(object):
def __init__(self, *args, **kwargs):
pass

def enable_tx(self):
pass

def cleanup(self):
pass

def tx_code(self, code, **kwargs):
return True
RFDevice = RFDeviceMock


class UnsupportedDeviceError(Exception):
Expand Down Expand Up @@ -85,6 +89,7 @@ def switch_device(self, device, on):
Returns True if the underlying RFDevice acknowledged; otherwise False.
"""
self.logger.debug("Device switch for '{}' to '{}' requested".format(str(device), str(on)))
from .registry import StatefulDevice
if isinstance(device, StatefulDevice):
# Unpack the actual device from the Stateful device wrapper
device = device.device
Expand Down
Loading

0 comments on commit dab4eee

Please sign in to comment.