This commit is contained in:
T 2025-01-10 01:10:31 +01:00
parent e2e5878630
commit bd7532a080
5 changed files with 169 additions and 104 deletions

View File

@ -1,3 +1,3 @@
#!/bin/bash #!/bin/bash
VERSION=3.2.0 VERSION=3.3.0
dpkg-deb --root-owner-group -b debian "foodoord_${VERSION}_all.deb" dpkg-deb --root-owner-group -b debian "foodoord_${VERSION}_all.deb"

View File

@ -1,6 +1,6 @@
Package: foodoord Package: foodoord
Version: 3.2.0 Version: 3.3.0
Maintainer: Bandie <bandie@chaospott.de> Maintainer: Bandie <bandie@chaospott.de>, Tobi <tobi@chaospott.de>
Architecture: all Architecture: all
Description: Control the doors of the club, ja! Description: Control the doors of the club, ja!
Depends: dash, git, python3, pip, tmux Depends: dash, git, python3, pip, tmux

View File

@ -27,7 +27,7 @@ done
echo "##################" echo "##################"
echo "Installing dependencies via pip: pifacecommon pifacedigitalio" echo "Installing dependencies via pip: pifacecommon pifacedigitalio"
pip install pifacecommon pifacedigitalio pip install pifacecommon pifacedigitalio paho-mqtt
echo "Enabling and starting systemd-Services" echo "Enabling and starting systemd-Services"
systemctl daemon-reload systemctl daemon-reload

View File

@ -8,26 +8,49 @@ import signal
import stat import stat
import subprocess import subprocess
import sys import sys
import threading
import time import time
from configparser import ConfigParser from configparser import ConfigParser
from dataclasses import dataclass from dataclasses import dataclass
import paho.mqtt.client as mqtt
import pifacedigitalio import pifacedigitalio
# Definitions for output
LED_RED = 6
LED_GREEN = 7
RELAYS_LOCK = 0
RELAYS_UNLOCK = 1
# Definitions for input class FoodoorMQTT:
DOOR_BELL = 0 def __init__(self, area):
REED_RELAYS = 1 # not implemented yet self.area = area
self.client = mqtt.Client()
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self._connect_lock = threading.Condition()
def connect(self):
try:
self.client.connect("mqtt.chaospott.de")
self.client.loop_start()
with self._connect_lock:
self._connect_lock.wait()
except Exception as e:
print(f"Verbindungsfehler zu MQTT-Server: {e}")
def disconnect(self):
self.client.loop_stop()
def on_connect(self, client, userdata, flags, rc):
with self._connect_lock:
self._connect_lock.notify()
def on_message(self, client, userdata, msg):
print(f"MQTT-Server Message: {msg.topic} {msg.payload}")
def send_state(self, locked: bool):
self.client.publish(f"foobar/{self.area}/foodoor/status", {
False: "open",
True: "closed",
}[locked], qos=0, retain=True)
# Definitions for LED color
RED = 1
GREEN = 2
ORANGE = 3
# Read config # Read config
parser = ConfigParser() parser = ConfigParser()
@ -36,95 +59,108 @@ parser.read('/etc/foodoord.conf')
@dataclass @dataclass
class API: class API:
location: str
api_url: str api_url: str
consumer_key: str consumer_key: str
consumer_secret: str consumer_secret: str
def update_state(self, locked):
subprocess.check_call([
"/usr/bin/curl", "-XPOST",
"--header", "Content-Type: application/json",
"--data",
json.dumps({"consumer_key": self.consumer_key, "consumer_secret": self.consumer_secret, self.location: locked}),
self.api_url
])
APIv1 = API(
APIv1 = API("aerie",
parser.get('doorstatus', 'status_url'), parser.get('doorstatus', 'status_url'),
parser.get('doorstatus', 'key'), parser.get('doorstatus', 'key'),
parser.get('doorstatus', 'secret'), parser.get('doorstatus', 'secret'),
) )
APIv2 = API( APIv2 = API("aerie",
parser.get('doorstatusv2', 'status_url'), parser.get('doorstatusv2', 'status_url'),
parser.get('doorstatusv2', 'key'), parser.get('doorstatusv2', 'key'),
parser.get('doorstatusv2', 'secret'), parser.get('doorstatusv2', 'secret'),
) )
def update_api(locked):
try:
# API v1
subprocess.check_call([
"/usr/bin/curl", "-XPOST",
"--header", "Content-Type: application/json",
"--data",
json.dumps({"consumer_key": APIv1.consumer_key, "consumer_secret": APIv1.consumer_secret, "aerie": locked}),
APIv1.api_url
])
except:
pass
try:
# API v2
subprocess.check_call([
"/usr/bin/curl", "-XPOST",
"--header", "Content-Type: application/json",
"--data",
json.dumps({"consumer_key": APIv2.consumer_key, "consumer_secret": APIv2.consumer_secret, "aerie": locked}),
APIv2.api_url
])
except:
pass
def set_led(color):
if color == RED:
pifacedigital.leds[LED_RED].turn_on()
pifacedigital.leds[LED_GREEN].turn_off()
elif color == GREEN:
pifacedigital.leds[LED_GREEN].turn_on()
pifacedigital.leds[LED_RED].turn_off()
elif color == ORANGE:
pifacedigital.leds[LED_RED].turn_on()
pifacedigital.leds[LED_GREEN].turn_on()
class Foodoord: class Foodoord:
# Definitions for LED color
RED = 0b1
GREEN = 0b10
ORANGE = RED | GREEN
# Definitions for output
LEDS = {
RED: 6,
GREEN: 7,
}
RELAYS_LOCK = 0
RELAYS_UNLOCK = 1
# Definitions for input
DOOR_BELL = 0
CLOSE_BUTTON = 1
def __init__(self): def __init__(self):
self.status_open = False self.status_open = False
self.mqtt = FoodoorMQTT("oben")
self.listener = pifacedigitalio.InputEventListener() self.listener = pifacedigitalio.InputEventListener()
self.listener.register(0, pifacedigitalio.IODIR_RISING_EDGE, self.doorbell, settle_time=10) self.listener.register(self.DOOR_BELL, pifacedigitalio.IODIR_RISING_EDGE, self.doorbell, settle_time=10)
self.listener.register(1, pifacedigitalio.IODIR_RISING_EDGE, self.close_button, settle_time=5) self.listener.register(self.CLOSE_BUTTON, pifacedigitalio.IODIR_RISING_EDGE, self.close_button, settle_time=5)
def signal_handler(self, _signal, _frame): def signal_handler(self, _signal, _frame):
self.listener.deactivate() self.listener.deactivate()
os.remove("/var/run/foodoord.pipe") os.remove("/var/run/foodoord.pipe")
update_api(True) self.update_api(True)
set_led(RED) self.set_led(self.RED)
sys.exit(0) sys.exit(0)
def update_api(self, locked):
try:
self.mqtt.send_state(locked)
except:
pass
try:
APIv1.update_state(locked)
except:
pass
try:
APIv2.update_state(locked)
except:
pass
def set_led(self, color):
for led, gpio in self.LEDS.items():
if color & led:
pifacedigital.leds[gpio].turn_on()
else:
pifacedigital.leds[gpio].turn_off()
def doorbell(self, event): def doorbell(self, event):
if self.status_open: if self.status_open:
pifacedigital.relays[RELAYS_UNLOCK].toggle() pifacedigital.relays[self.RELAYS_UNLOCK].toggle()
time.sleep(2) time.sleep(2)
pifacedigital.relays[RELAYS_UNLOCK].toggle() pifacedigital.relays[self.RELAYS_UNLOCK].toggle()
def close_button(self, _event): def close_button(self, _event):
self.status_open = False self.status_open = False
update_api(True) self.update_api(True)
set_led(RED) self.set_led(self.RED)
def main(self): def main(self):
self.mqtt.connect()
self.listener.activate() self.listener.activate()
pifacedigital = pifacedigitalio.PiFaceDigital() pifacedigital = pifacedigitalio.PiFaceDigital()
signal.signal(signal.SIGTERM, self.signal_handler) signal.signal(signal.SIGTERM, self.signal_handler)
# Start settings # Start settings
pifacedigital.leds[LED_RED].turn_on() self.set_led(self.RED)
# Setting up FiFo to get sshd-output # Setting up FiFo to get sshd-output
try: try:
@ -140,23 +176,23 @@ class Foodoord:
pipe_cmd = ssh_input.readline().strip() pipe_cmd = ssh_input.readline().strip()
if pipe_cmd == "close" and self.status_open: if pipe_cmd == "close" and self.status_open:
pifacedigital.relays[RELAYS_LOCK].toggle() pifacedigital.relays[self.RELAYS_LOCK].toggle()
time.sleep(1) time.sleep(1)
pifacedigital.relays[RELAYS_LOCK].toggle() pifacedigital.relays[self.RELAYS_LOCK].toggle()
self.status_open = False self.status_open = False
update_api(True) self.update_api(True)
set_led(RED) self.set_led(self.RED)
elif pipe_cmd == "open": elif pipe_cmd == "open":
pifacedigital.relays[RELAYS_UNLOCK].toggle() pifacedigital.relays[self.RELAYS_UNLOCK].toggle()
time.sleep(2) time.sleep(2)
pifacedigital.relays[RELAYS_UNLOCK].toggle() pifacedigital.relays[self.RELAYS_UNLOCK].toggle()
if not self.status_open: if not self.status_open:
update_api(False) self.update_api(False)
self.status_open = True self.status_open = True
set_led(GREEN) self.set_led(self.GREEN)
time.sleep(0.1) time.sleep(0.1)

View File

@ -6,27 +6,53 @@ import json
import os import os
import stat import stat
import subprocess import subprocess
import threading
import time import time
from configparser import ConfigParser from configparser import ConfigParser
from dataclasses import dataclass from dataclasses import dataclass
import RPi.GPIO as gpio import RPi.GPIO as gpio
import paho.mqtt.client as mqtt
class FoodoorMQTT:
def __init__(self, area):
self.area = area
self.client = mqtt.Client()
self.client.on_connect = self.on_connect
self.client.on_message = self.on_message
self._connect_lock = threading.Condition()
def connect(self):
try:
self.client.connect("mqtt.chaospott.de")
self.client.loop_start()
with self._connect_lock:
self._connect_lock.wait()
except Exception as e:
print(f"Verbindungsfehler zu MQTT-Server: {e}")
def disconnect(self):
self.client.loop_stop()
def on_connect(self, client, userdata, flags, rc):
with self._connect_lock:
self._connect_lock.notify()
def on_message(self, client, userdata, msg):
print(f"MQTT-Server Message: {msg.topic} {msg.payload}")
def send_state(self, locked: bool):
self.client.publish(f"foobar/{self.area}/foodoor/status", {
False: "open",
True: "closed",
}[locked], qos=0, retain=True)
# Definitions for output # Definitions for output
LED_RED = 6
LED_GREEN = 7
RELAYS_LOCK = 0
RELAYS_UNLOCK = 1
PIN_OPEN = 24 PIN_OPEN = 24
PIN_CLOSE = 27 PIN_CLOSE = 27
# Definitions for input
DOOR_BELL = 0
REED_RELAYS = 1 # not implemented yet
# Definitions for LED color
RED = 1
GREEN = 2
ORANGE = 3
# Read config # Read config
parser = ConfigParser() parser = ConfigParser()
@ -35,17 +61,28 @@ parser.read('/etc/foodoord.conf')
@dataclass @dataclass
class API: class API:
location: str
api_url: str api_url: str
consumer_key: str consumer_key: str
consumer_secret: str consumer_secret: str
def update_state(self, locked):
subprocess.check_call([
"/usr/bin/curl", "-XPOST",
"--header", "Content-Type: application/json",
"--data",
json.dumps({"consumer_key": self.consumer_key, "consumer_secret": self.consumer_secret, self.location: locked}),
self.api_url
])
APIv1 = API(
MQTT = FoodoorMQTT("unten")
APIv1 = API("cellar",
parser.get('doorstatus', 'status_url'), parser.get('doorstatus', 'status_url'),
parser.get('doorstatus', 'key'), parser.get('doorstatus', 'key'),
parser.get('doorstatus', 'secret'), parser.get('doorstatus', 'secret'),
) )
APIv2 = API( APIv2 = API("cellar",
parser.get('doorstatusv2', 'status_url'), parser.get('doorstatusv2', 'status_url'),
parser.get('doorstatusv2', 'key'), parser.get('doorstatusv2', 'key'),
parser.get('doorstatusv2', 'secret'), parser.get('doorstatusv2', 'secret'),
@ -54,33 +91,23 @@ APIv2 = API(
def write_state(state): def write_state(state):
try: try:
with open("/tmp/door_state", "w") as handle: with open("/tmp/door_state", "w") as f:
handle.write(state) f.write(state)
except: except:
pass pass
def update_api(locked): def update_api(locked):
try: try:
# API v1 MQTT.send_state(locked)
subprocess.check_call([
"/usr/bin/curl", "-XPOST",
"--header", "Content-Type: application/json",
"--data",
json.dumps({"consumer_key": APIv1.consumer_key, "consumer_secret": APIv1.consumer_secret, "cellar": locked}),
APIv1.api_url
])
except: except:
pass pass
try: try:
# API v2 APIv1.update_state(locked)
subprocess.check_call([ except:
"/usr/bin/curl", "-XPOST", pass
"--header", "Content-Type: application/json", try:
"--data", APIv2.update_state(locked)
json.dumps({"consumer_key": APIv2.consumer_key, "consumer_secret": APIv2.consumer_secret, "cellar": locked}),
APIv2.api_url
])
except: except:
pass pass
@ -99,6 +126,8 @@ def main():
except OSError: except OSError:
pass pass
MQTT.connect()
ssh_input = open("/var/run/foodoord.pipe", "r") ssh_input = open("/var/run/foodoord.pipe", "r")
while True: while True:
# Read sshd output from pipe # Read sshd output from pipe