doorstate: reimplement doorstate in python

Furthermore, add initial support for the alarm topic.

Signed-off-by: Ralf Ramsauer <ralf@binary-kitchen.de>
This commit is contained in:
Ralf Ramsauer 2019-06-15 22:27:36 +02:00
parent 8bc75bc0f6
commit 07a0ac5307
7 changed files with 188 additions and 123 deletions

View File

@ -8,7 +8,7 @@ ETC = $(DESTDIR)/etc/
SHARE = $(USR)/share/
SYSTEMD_UNITS = $(ETC)/systemd/system/
all: gpio-wait pydoorlock/Protocol.py
all: pydoorlock/Protocol.py
package:
sed -i -r -e "s@(^SYSCONFDIR = ').*('$$)@\1$(SYSCONFDIR)\2@" pydoorlock/Config.py
@ -18,15 +18,13 @@ package:
pydoorlock/Protocol.py: avr-code/protocol.h
./scripts/gen_protocol.sh $^ > $@
gpio-wait: gpio-wait.c
install:
mkdir -p $(BIN)
mkdir -p $(SHARE)
mkdir -p $(SYSTEMD_UNITS)
mkdir -p $(ETC)
install doorlockd gpio-wait doorstate $(BIN)
install doorlockd doorstate $(BIN)
install doorlockd-passwd $(BIN)
install -m 0644 etc/doorlockd.cfg $(ETC)
install -m 0644 systemd/doorlockd.service systemd/doorstate.service $(SYSTEMD_UNITS)
@ -36,6 +34,5 @@ install:
cp -av share/* $(SHARE)
clean:
rm -f gpio-wait
rm -rf pydoorlock/__pycache__
rm -f pydoorlock/Protocol.py

View File

@ -12,9 +12,12 @@ depends=('python3'
'python-ldap'
'python-pip'
'alsa-utils'
'libgpiod'
'mosquitto'
'mpg123'
'python-flask-wtf'
'python-flask-socketio'
'python-paho-mqtt'
'chromium'
'xf86-video-fbdev'
'fluxbox'

186
doorstate
View File

@ -1,23 +1,173 @@
#!/bin/bash
#!/usr/bin/env python3
GPIO=22
"""
Doorlockd -- Binary Kitchen's smart door opener
function publish {
mosquitto_pub -r -t kitchen/doorlock/frontdoor/doorstate -m "$1"
}
Copyright (c) Binary Kitchen e.V., 2019
if [ "$(id -u)" != "0" ]; then
echo "Please run as root!"
exit -1
fi
Author:
Ralf Ramsauer <ralf@binary-kitchen.de>
while true; do
gpio-wait /dev/gpiochip0 $GPIO
ret=$?
This work is licensed under the terms of the GNU GPL, version 2. See
the LICENSE file in the top-level directory.
if [ $ret -eq 0 ]; then
publish "closed"
elif [ $ret -eq 1 ]; then
publish "open"
fi
done
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
"""
import gpiod
import os
import sys
from subprocess import Popen
from threading import Timer, Thread, Event
import paho.mqtt.client as mqtt
from pydoorlock.Config import Config, sounds_prefix
prog = sys.argv[0]
global is_locked
is_locked = None
class AudioPlayer(Thread):
def __init__(self, filename):
super(AudioPlayer, self).__init__()
self._filename = filename
self._event = Event()
self._shutdown = False
self._process = None
self._state = False
def set(self, state):
self._state = state
self._event.set()
def shutdown(self):
self._state = False
self._shutdown = True
self._event.set()
def run(self):
print('Run')
while not self._shutdown:
self._event.wait(1)
if self._event.is_set():
print('Came by event')
self._event.clear()
if self._process:
if self._process.poll() is not None:
print('Cleaning up...')
self._process.wait()
self._process = None
self._state = False
elif not self._state:
print('Killing...')
self._process.terminate()
self._process.wait()
self._process = None
self._state = False
else:
if self._state:
print('Starting...')
self._process = Popen(['mpg123', self._filename])
def mqtt_on_connect(client, userdata, flags, rc):
if rc == 0:
print("MqTT: Connection returned result: " + mqtt.connack_string(rc))
client.subscribe(topic_alarm)
client.subscribe(topic_lockstate)
else:
print("MqTT: Bad connection Returned code=", rc)
def mqtt_on_lockstate(client, userdata, message):
global is_locked
is_locked = message.payload.decode('utf-8') == 'locked'
print('is_locked: %s' % is_locked)
def mqtt_on_alarm(client, userdata, message):
alarm_set = message.payload.decode('utf-8') == '1'
print('MQTT: alarm: %u' % alarm_set)
player_alarm.set(alarm_set)
def publish_doorstate():
global door_open
message = 'open' if door_open else 'closed'
mqttc.publish(topic_doorstate, message, retain=True)
cfg = Config('dooralarm')
topic_alarm = cfg.str('TOPIC_ALARM')
topic_doorstate = cfg.str('TOPIC_DOORSTATE')
topic_lockstate = cfg.str('TOPIC_LOCKSTATE')
doorstate_alarm_timeout = int(cfg.str('DOORSTATE_ALARM_TIMEOUT'))
f_alarm = os.path.join(sounds_prefix, 'alarm.mp3')
f_door_call = os.path.join(sounds_prefix, 'door_call.mp3')
mqttc = mqtt.Client(client_id=prog)
mqttc.username_pw_set(cfg.str('MQTT_USERNAME'), cfg.str('MQTT_PASSWORD'))
mqttc.on_connect = mqtt_on_connect
mqttc.message_callback_add(topic_alarm, mqtt_on_alarm)
mqttc.message_callback_add(topic_lockstate, mqtt_on_lockstate)
mqttc.connect(cfg.str('MQTT_HOST'))
chip = gpiod.Chip(cfg.str('GPIO_CHIP'))
pin = cfg.int('GPIO_PIN')
line = chip.get_line(pin)
line.request(consumer=prog, type=gpiod.LINE_REQ_EV_BOTH_EDGES)
player_alarm = AudioPlayer(f_alarm)
player_alarm.start()
player_door_call = AudioPlayer(f_door_call)
player_door_call.start()
global door_open
door_open = line.get_value() == 1
door_alarm_set = False
while True:
# Synchronous loops: MQTT + GPIO
mqttc.loop(timeout=0.5, max_packets=1)
ev_line = line.event_wait(sec=1)
if ev_line:
event = line.event_read()
door_open = event.type == gpiod.LineEvent.RISING_EDGE
publish_doorstate()
print('Loop: door_open: %s' % door_open)
print('Loop: is_locked: %s' % is_locked)
print('Loop: door_alarm_set: %s' % door_alarm_set)
print()
# Door State stuff
if is_locked is not None:
if is_locked is False:
if door_alarm_set:
player_door_call.set(False)
door_alarm_set = False
elif door_open is True:
if not door_alarm_set:
door_alarm_set = True
player_door_call.set(True)
elif door_open is False:
if door_alarm_set:
player_door_call.set(False)
door_alarm_set = False
player_alarm.shutdown()
player_alarm.join()
player_door_call.shutdown()
player_door_call.join()

View File

@ -22,3 +22,18 @@ WELCOME = Willkommen in der Binary Kitchen
SERIAL_PORT = /dev/ttyAMA0
SECRET_KEY = foobar
[dooralarm]
GPIO_CHIP = /dev/gpiochip0
GPIO_PIN = 22
TOPIC_ALARM = kitchen/alarm
TOPIC_DOORSTATE = kitchen/doorlock/frontdoor/doorstate
TOPIC_LOCKSTATE = kitchen/doorlock/frontdoor/lockstate
DOORSTATE_ALARM_TIMEOUT = 2
MQTT_HOST = pizza.binary.kitchen
MQTT_USERNAME = doorlock
MQTT_PASSWORD =

View File

@ -1,100 +0,0 @@
/*
* Doorlockd -- Binary Kitchen's smart door opener
*
* Copyright (c) Binary Kitchen e.V., 2018
*
* Author: Ralf Ramsauer <ralf@binary-kitchen.de>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the LICENSE file in the top-level directory.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/gpio.h>
int main(int argc, const char **argv) {
int gpiochip_fd, ret;
struct gpioevent_request req;
struct gpiohandle_data data;
struct gpioevent_data event;
if (argc != 3) {
fprintf(stderr, "Usage: %s gpiochip pin\n", argv[0]);
return -1;
}
gpiochip_fd = open(argv[1], 0);
if (gpiochip_fd == -1) {
perror("open");
return -1;
}
req.lineoffset = atoi(argv[2]);
req.handleflags = GPIOHANDLE_REQUEST_INPUT;
req.eventflags = GPIOEVENT_REQUEST_RISING_EDGE | GPIOEVENT_REQUEST_FALLING_EDGE;
req.consumer_label[0] = 0;
ret = ioctl(gpiochip_fd, GPIO_GET_LINEEVENT_IOCTL, &req);
if (ret == -1) {
perror("ioctl");
goto gpiochip_out;
}
ret = ioctl(req.fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &data);
if (ret == -1) {
perror("ioctl");
goto req_fd_out;
}
printf("Initial Value: %u\n", data.values[0]);
retry:
ret = read(req.fd, &event, sizeof(event));
if (ret == -1) {
if (errno == -EAGAIN) {
printf("Nothing available, trying again\n");
goto retry;
}
}
if (ret != sizeof(event)) {
ret = -EIO;
goto req_fd_out;
}
switch (event.id) {
case GPIOEVENT_EVENT_RISING_EDGE:
printf("Detected rising edge\n");
ret = 1;
break;
case GPIOEVENT_EVENT_FALLING_EDGE:
printf("Detected falling edge\n");
ret = 0;
break;
default:
printf("Unknown event\n");
ret = -EIO;
break;
}
req_fd_out:
close(req.fd);
gpiochip_out:
close(gpiochip_fd);
return ret;
}

Binary file not shown.

Binary file not shown.