foodoord/debian/usr/sbin/foodoord_oben

202 lines
5.4 KiB
Plaintext
Raw Normal View History

2021-11-13 16:14:15 +01:00
#!/usr/bin/env python3
# vim: ts=2 sw=2 et
2024-06-07 22:17:06 +02:00
import grp
import json
import os
import signal
2024-06-07 22:17:06 +02:00
import stat
import subprocess
import sys
2025-01-10 01:10:31 +01:00
import threading
2024-06-07 22:17:06 +02:00
import time
2021-11-13 16:14:15 +01:00
from configparser import ConfigParser
2024-07-04 22:57:17 +02:00
from dataclasses import dataclass
2025-01-10 01:10:31 +01:00
import paho.mqtt.client as mqtt
2024-06-07 22:17:06 +02:00
import pifacedigitalio
2021-11-07 19:46:13 +01:00
2025-01-10 01:10:31 +01:00
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)
2024-06-07 22:17:06 +02:00
# Read config
parser = ConfigParser()
parser.read('/etc/foodoord.conf')
2024-07-04 22:57:17 +02:00
@dataclass
class API:
2025-01-10 01:10:31 +01:00
location: str
2024-07-04 22:57:17 +02:00
api_url: str
consumer_key: str
consumer_secret: str
2025-01-10 01:10:31 +01:00
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
])
2024-07-04 22:57:17 +02:00
2025-01-10 01:10:31 +01:00
APIv1 = API("aerie",
2024-07-04 22:57:17 +02:00
parser.get('doorstatus', 'status_url'),
parser.get('doorstatus', 'key'),
parser.get('doorstatus', 'secret'),
)
2025-01-10 01:10:31 +01:00
APIv2 = API("aerie",
2024-07-04 22:57:17 +02:00
parser.get('doorstatusv2', 'status_url'),
parser.get('doorstatusv2', 'key'),
parser.get('doorstatusv2', 'secret'),
)
2024-06-07 22:17:06 +02:00
class Foodoord:
2025-01-10 01:10:31 +01:00
# 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
2024-06-07 22:17:06 +02:00
def __init__(self):
self.status_open = False
2025-01-10 01:10:31 +01:00
self.mqtt = FoodoorMQTT("oben")
2025-01-14 19:04:13 +01:00
self.pifacedigital = pifacedigitalio.PiFaceDigital()
2024-06-07 22:17:06 +02:00
self.listener = pifacedigitalio.InputEventListener()
2025-01-10 01:10:31 +01:00
self.listener.register(self.DOOR_BELL, pifacedigitalio.IODIR_RISING_EDGE, self.doorbell, settle_time=10)
self.listener.register(self.CLOSE_BUTTON, pifacedigitalio.IODIR_RISING_EDGE, self.close_button, settle_time=5)
2024-06-07 22:17:06 +02:00
def signal_handler(self, _signal, _frame):
self.listener.deactivate()
os.remove("/var/run/foodoord.pipe")
2025-01-10 01:10:31 +01:00
self.update_api(True)
self.set_led(self.RED)
2024-06-07 22:17:06 +02:00
sys.exit(0)
2025-01-10 01:10:31 +01:00
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:
2025-01-14 19:04:13 +01:00
self.pifacedigital.leds[gpio].turn_on()
2025-01-10 01:10:31 +01:00
else:
2025-01-14 19:04:13 +01:00
self.pifacedigital.leds[gpio].turn_off()
2025-01-10 01:10:31 +01:00
2024-06-07 22:17:06 +02:00
def doorbell(self, event):
if self.status_open:
2025-01-14 19:04:13 +01:00
self.pifacedigital.relays[self.RELAYS_UNLOCK].toggle()
2024-06-07 22:17:06 +02:00
time.sleep(2)
2025-01-14 19:04:13 +01:00
self.pifacedigital.relays[self.RELAYS_UNLOCK].toggle()
2024-06-07 22:17:06 +02:00
def close_button(self, _event):
self.status_open = False
2025-01-10 01:10:31 +01:00
self.update_api(True)
self.set_led(self.RED)
2024-06-07 22:17:06 +02:00
def main(self):
2025-01-10 01:10:31 +01:00
self.mqtt.connect()
2024-06-07 22:17:06 +02:00
self.listener.activate()
signal.signal(signal.SIGTERM, self.signal_handler)
2024-06-07 22:17:06 +02:00
# Start settings
2025-01-10 01:10:31 +01:00
self.set_led(self.RED)
2024-06-07 22:17:06 +02:00
# Setting up FiFo to get sshd-output
try:
2024-06-07 22:17:06 +02:00
os.mkfifo("/var/run/foodoord.pipe")
os.chown("/var/run/foodoord.pipe", -1, grp.getgrnam('foodoor')[2])
os.chmod("/var/run/foodoord.pipe", stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP)
except OSError:
pass
2024-06-07 22:17:06 +02:00
ssh_input = open("/var/run/foodoord.pipe", "r")
while True:
# Read sshd-output from pipe
pipe_cmd = ssh_input.readline().strip()
2024-06-07 22:17:06 +02:00
if pipe_cmd == "close" and self.status_open:
2025-01-14 19:04:13 +01:00
self.pifacedigital.relays[self.RELAYS_LOCK].toggle()
2024-06-07 22:17:06 +02:00
time.sleep(1)
2025-01-14 19:04:13 +01:00
self.pifacedigital.relays[self.RELAYS_LOCK].toggle()
2024-06-07 22:17:06 +02:00
self.status_open = False
2025-01-10 01:10:31 +01:00
self.update_api(True)
self.set_led(self.RED)
2024-06-07 22:17:06 +02:00
elif pipe_cmd == "open":
2025-01-14 19:04:13 +01:00
self.pifacedigital.relays[self.RELAYS_UNLOCK].toggle()
2024-06-07 22:17:06 +02:00
time.sleep(2)
2025-01-14 19:04:13 +01:00
self.pifacedigital.relays[self.RELAYS_UNLOCK].toggle()
2024-06-07 22:17:06 +02:00
if not self.status_open:
2025-01-10 01:10:31 +01:00
self.update_api(False)
2024-06-07 22:17:06 +02:00
self.status_open = True
2025-01-10 01:10:31 +01:00
self.set_led(self.GREEN)
2024-06-07 22:17:06 +02:00
time.sleep(0.1)
if __name__ == "__main__":
Foodoord().main()