Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix MQTT spam #17

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions firmware/dlockoslo.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,17 @@ def __init__(self, *args, **kwargs):

class States:
def __init__(self,
lock : Lock = Locked(0),
opener : Opener = Inactive(0),
lock : Lock = None,
opener : Opener = None,
bolt_present : bool = False,
bolt_present_updated : float = 0.0,
connected_light : bool = False) -> None:

if lock is None:
lock = Locked(0)
if opener is None:
opener = Inactive(0)

self.lock = lock
self.opener = opener
self.connected_light = connected_light
Expand Down Expand Up @@ -133,7 +138,7 @@ def ensure_unlocked_for_opener():
lock = TemporarilyUnlocked(since=i.current_time, until=i.current_time+temp_unlock_time)

# unlock switch
if i.holdopen_button == True:
if i.holdopen_button == True and lock.state != 'Unlocked':
lock = Unlocked(since=i.current_time, reason='switch')
elif i.holdopen_button == False and lock.state == 'Unlocked' and lock.reason == 'switch':
lock = Locked(since=i.current_time, reason='switch')
Expand Down Expand Up @@ -293,12 +298,19 @@ def process(self, inport, msg):
self.send('error', 'Unknown port {}'.format(inport))
self.ack(msg)

def is_state_change(current, next):
state_changed = repr(next.__dict__) != repr(current.__dict__)
return state_changed


class LockParticipant(msgflo.Participant):
def __init__(self, role):
d = copy.deepcopy(participant_definition)
msgflo.Participant.__init__(self, d, role)

self.role = role
self.discovery_period = float(os.environ.get("DLOCK_DISCOVERY_PERIOD", "60"))
self.poll_interval = float(os.environ.get("DLOCK_POLL_INTERVAL", "0.2"))

pin_mapping = {
# in
Expand Down Expand Up @@ -345,7 +357,7 @@ def process(self, inport, msg):
def loop(self):
while True:
self.recalculate_state()
gevent.sleep(0.2)
gevent.sleep(self.poll_interval)

def recalculate_state(self, mqtt_request=None):
# Retrieve current inputs
Expand All @@ -366,9 +378,9 @@ def recalculate_state(self, mqtt_request=None):
next = next_state(self.state, Inputs(**inputs))
set_outputs(self.state, self.output_files)

state_changed = next.__dict__ != self.state.__dict__
state_changed = is_state_change(self.state, next)
if state_changed:
entry = { 'inputs': inputs, 'state': next.__dict__ }
entry = { 'role': self.role, 'inputs': inputs, 'state': next.__dict__ }
log.info(entry)

if connected:
Expand Down
42 changes: 42 additions & 0 deletions firmware/test_doorsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,27 @@ def test_dooropener_after_unlock():
assert states.opener.state == 'TemporarilyActive'


def test_holdopen_since_updates():
states = dlockoslo.States()
assert states.lock.state == 'Locked'
assert states.opener.state == 'Inactive'
inputs = dict(
holdopen_button=True,
current_time=100,
)
states = dlockoslo.next_state(states, dlockoslo.Inputs(**inputs))
assert states.lock.state == 'Unlocked', 'unlocks'
assert states.lock.since == 100, 'update since on initial transition'

inputs = dict(
holdopen_button=True,
current_time=222,
)
states = dlockoslo.next_state(states, dlockoslo.Inputs(**inputs))
assert states.lock.state == 'Unlocked', 'still unlocked'
assert states.lock.since == 100, 'dont update since when no state transition'


def test_bolt_present_reflects_input():
states = dlockoslo.States()
assert states.lock.state == 'Locked'
Expand Down Expand Up @@ -226,4 +247,25 @@ def test_bolt_present_periodic_update(bolt_present):
assert states.bolt_present_updated > last_updated, 'should update'
assert states.bolt_present_updated == 90.0

def test_state_change_identity():
states = dlockoslo.States()
change = dlockoslo.is_state_change(states, states)
assert change == False

def test_state_change_equivalence():
state1 = dlockoslo.States()
state2 = dlockoslo.States()
change = dlockoslo.is_state_change(state1, state2)
assert change == False


def test_state_change_unlocking():
state1 = dlockoslo.States()
state1.lock = dlockoslo.Locked(since=10)
state2 = dlockoslo.States()
state2.lock = dlockoslo.Unlocked(since=20)

change = dlockoslo.is_state_change(state1, state2)
assert change == True, 'unlocking is state change'


18 changes: 1 addition & 17 deletions gateway/test_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,23 +213,7 @@ def set_bolt_present(state : bool):
assert bolt['last_updated'] > update_time


# TODO: keep this info in database
not_running = [
'notresponding-1',
'sorenga-1',
'fubiak-1',
'dev-0',
'origo-1',
'origo-2',
'deichman-toyen',
'lindeberg-1',
'deichman-majorstuen',
'loren-1',
'deichman-stovner',
'unused-4',
'unused-12',
'unused-13',
]
not_running = list(set(gateway.doors.keys()) - set(testdevices.devices))
ignore = '&'.join('ignore={}'.format(d) for d in not_running)

def test_status_all_devices_ok(devices):
Expand Down
7 changes: 7 additions & 0 deletions gateway/testdevices.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ def create_virtual_lock(name):
return virtual


devices = [
'virtual-1',
'virtual-2',
'erroring-1',
'dlock-2',
]

def get_participants():
participants = [
create_virtual_lock('doors/virtual-1'),
Expand Down