mirror of
https://github.com/binary-kitchen/doorlockd
synced 2024-12-22 02:14:26 +01:00
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:
parent
8bc75bc0f6
commit
07a0ac5307
7
Makefile
7
Makefile
@ -8,7 +8,7 @@ ETC = $(DESTDIR)/etc/
|
|||||||
SHARE = $(USR)/share/
|
SHARE = $(USR)/share/
|
||||||
SYSTEMD_UNITS = $(ETC)/systemd/system/
|
SYSTEMD_UNITS = $(ETC)/systemd/system/
|
||||||
|
|
||||||
all: gpio-wait pydoorlock/Protocol.py
|
all: pydoorlock/Protocol.py
|
||||||
|
|
||||||
package:
|
package:
|
||||||
sed -i -r -e "s@(^SYSCONFDIR = ').*('$$)@\1$(SYSCONFDIR)\2@" pydoorlock/Config.py
|
sed -i -r -e "s@(^SYSCONFDIR = ').*('$$)@\1$(SYSCONFDIR)\2@" pydoorlock/Config.py
|
||||||
@ -18,15 +18,13 @@ package:
|
|||||||
pydoorlock/Protocol.py: avr-code/protocol.h
|
pydoorlock/Protocol.py: avr-code/protocol.h
|
||||||
./scripts/gen_protocol.sh $^ > $@
|
./scripts/gen_protocol.sh $^ > $@
|
||||||
|
|
||||||
gpio-wait: gpio-wait.c
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
mkdir -p $(BIN)
|
mkdir -p $(BIN)
|
||||||
mkdir -p $(SHARE)
|
mkdir -p $(SHARE)
|
||||||
mkdir -p $(SYSTEMD_UNITS)
|
mkdir -p $(SYSTEMD_UNITS)
|
||||||
mkdir -p $(ETC)
|
mkdir -p $(ETC)
|
||||||
|
|
||||||
install doorlockd gpio-wait doorstate $(BIN)
|
install doorlockd doorstate $(BIN)
|
||||||
install doorlockd-passwd $(BIN)
|
install doorlockd-passwd $(BIN)
|
||||||
install -m 0644 etc/doorlockd.cfg $(ETC)
|
install -m 0644 etc/doorlockd.cfg $(ETC)
|
||||||
install -m 0644 systemd/doorlockd.service systemd/doorstate.service $(SYSTEMD_UNITS)
|
install -m 0644 systemd/doorlockd.service systemd/doorstate.service $(SYSTEMD_UNITS)
|
||||||
@ -36,6 +34,5 @@ install:
|
|||||||
cp -av share/* $(SHARE)
|
cp -av share/* $(SHARE)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f gpio-wait
|
|
||||||
rm -rf pydoorlock/__pycache__
|
rm -rf pydoorlock/__pycache__
|
||||||
rm -f pydoorlock/Protocol.py
|
rm -f pydoorlock/Protocol.py
|
||||||
|
@ -12,9 +12,12 @@ depends=('python3'
|
|||||||
'python-ldap'
|
'python-ldap'
|
||||||
'python-pip'
|
'python-pip'
|
||||||
'alsa-utils'
|
'alsa-utils'
|
||||||
|
'libgpiod'
|
||||||
'mosquitto'
|
'mosquitto'
|
||||||
|
'mpg123'
|
||||||
'python-flask-wtf'
|
'python-flask-wtf'
|
||||||
'python-flask-socketio'
|
'python-flask-socketio'
|
||||||
|
'python-paho-mqtt'
|
||||||
'chromium'
|
'chromium'
|
||||||
'xf86-video-fbdev'
|
'xf86-video-fbdev'
|
||||||
'fluxbox'
|
'fluxbox'
|
||||||
|
186
doorstate
186
doorstate
@ -1,23 +1,173 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
GPIO=22
|
"""
|
||||||
|
Doorlockd -- Binary Kitchen's smart door opener
|
||||||
|
|
||||||
function publish {
|
Copyright (c) Binary Kitchen e.V., 2019
|
||||||
mosquitto_pub -r -t kitchen/doorlock/frontdoor/doorstate -m "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "$(id -u)" != "0" ]; then
|
Author:
|
||||||
echo "Please run as root!"
|
Ralf Ramsauer <ralf@binary-kitchen.de>
|
||||||
exit -1
|
|
||||||
fi
|
|
||||||
|
|
||||||
while true; do
|
This work is licensed under the terms of the GNU GPL, version 2. See
|
||||||
gpio-wait /dev/gpiochip0 $GPIO
|
the LICENSE file in the top-level directory.
|
||||||
ret=$?
|
|
||||||
|
|
||||||
if [ $ret -eq 0 ]; then
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
publish "closed"
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
elif [ $ret -eq 1 ]; then
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
publish "open"
|
details.
|
||||||
fi
|
"""
|
||||||
done
|
|
||||||
|
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()
|
||||||
|
@ -22,3 +22,18 @@ WELCOME = Willkommen in der Binary Kitchen
|
|||||||
SERIAL_PORT = /dev/ttyAMA0
|
SERIAL_PORT = /dev/ttyAMA0
|
||||||
|
|
||||||
SECRET_KEY = foobar
|
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 =
|
||||||
|
100
gpio-wait.c
100
gpio-wait.c
@ -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;
|
|
||||||
}
|
|
BIN
share/doorlockd/sounds/alarm.mp3
Normal file
BIN
share/doorlockd/sounds/alarm.mp3
Normal file
Binary file not shown.
BIN
share/doorlockd/sounds/door_call.mp3
Normal file
BIN
share/doorlockd/sounds/door_call.mp3
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user