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/
|
||||
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
|
||||
|
@ -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
186
doorstate
@ -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()
|
||||
|
@ -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 =
|
||||
|
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