From 5727a9aa7176f9e1f85c755c2d0f20e60b230fed Mon Sep 17 00:00:00 2001 From: Jeroen Stroeve Date: Wed, 21 Aug 2024 14:16:04 +0200 Subject: [PATCH] Initial pi-configs from running system --- manage.py | 4 +- pi-config/config/settings | 10 +++ pi-config/doorduino-init | 111 ++++++++++++++++++++++++ pi-config/doorduino.py | 169 +++++++++++++++++++++++++++++++++++++ pi-config/reset_arduino.sh | 8 ++ 5 files changed, 300 insertions(+), 2 deletions(-) create mode 100644 pi-config/config/settings create mode 100644 pi-config/doorduino-init create mode 100644 pi-config/doorduino.py create mode 100644 pi-config/reset_arduino.sh diff --git a/manage.py b/manage.py index ee9cbc9..dea27fb 100755 --- a/manage.py +++ b/manage.py @@ -4,7 +4,7 @@ import serial import sys import time -SERIAL_PORTS = [ '/dev/ttyS1', '/dev/ttyS2' ] +SERIAL_PORTS = [ '/dev/ttyS2' ] def open_port(tty): @@ -56,7 +56,7 @@ def remove_button(ports, button): break print(line) -def add_button(ser, button, secret): +def add_button(ports, button, secret): for port in ports: with open_port(port) as ser: ser.write(b'\r\n') diff --git a/pi-config/config/settings b/pi-config/config/settings new file mode 100644 index 0000000..58c6a5b --- /dev/null +++ b/pi-config/config/settings @@ -0,0 +1,10 @@ +[mqtt] +doorbell.subject=bitlair/doorduino/doorbell +dooropen.subject=bitlair/doorduino/dooropen +lockstate.subject=bitlair/doorduino/lockstate +server=mqtt.bitlair.nl +mqtt-simple=/usr/local/bin/mqtt-simple + +[serial] +device=/dev/ttyS2 + diff --git a/pi-config/doorduino-init b/pi-config/doorduino-init new file mode 100644 index 0000000..19f1acc --- /dev/null +++ b/pi-config/doorduino-init @@ -0,0 +1,111 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: generic-prog +# Required-Start: $local_fs $remote_fs $network +# Required-Stop: $local_fs $remote_fs $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Generic Program +# Description: Generic Program is a generic program to do generic things with +### END INIT INFO + + +# Documentation available at +# http://refspecs.linuxfoundation.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptfunc.html +# Debian provides some extra functions though +. /lib/lsb/init-functions + + +DAEMON_NAME="root" +DAEMON_USER="root" +DAEMON_PATH="/root/bitlair_doorduino/pi-config/doorduino.py" +DAEMON_OPTS="-c /root/bitlair_doorduino/pi-config/config" +DAEMON_PWD="${PWD}" +DAEMON_DESC=$(get_lsb_header_val $0 "Short-Description") +DAEMON_PID="/var/run/${DAEMON_NAME}.pid" +DAEMON_NICE=0 +DAEMON_LOG='/var/log/doorduino' + +[ -r "/etc/default/${DAEMON_NAME}" ] && . "/etc/default/${DAEMON_NAME}" + +do_start() { + local result + + pidofproc -p "${DAEMON_PID}" "${DAEMON_PATH}" > /dev/null + if [ $? -eq 0 ]; then + log_warning_msg "${DAEMON_NAME} is already started" + result=0 + else + log_daemon_msg "Starting ${DAEMON_DESC}" "${DAEMON_NAME}" + touch "${DAEMON_LOG}" + chown $DAEMON_USER "${DAEMON_LOG}" + chmod u+rw "${DAEMON_LOG}" + if [ -z "${DAEMON_USER}" ]; then + start-stop-daemon --start --quiet --oknodo --background \ + --nicelevel $DAEMON_NICE \ + --chdir "${DAEMON_PWD}" \ + --pidfile "${DAEMON_PID}" --make-pidfile \ + --exec "${DAEMON_PATH}" -- $DAEMON_OPTS + result=$? + else + start-stop-daemon --start --quiet --oknodo --background \ + --nicelevel $DAEMON_NICE \ + --chdir "${DAEMON_PWD}" \ + --pidfile "${DAEMON_PID}" --make-pidfile \ + --chuid "${DAEMON_USER}" \ + --exec "${DAEMON_PATH}" -- $DAEMON_OPTS + result=$? + fi + log_end_msg $result + fi + return $result +} + +do_stop() { + local result + + pidofproc -p "${DAEMON_PID}" "${DAEMON_PATH}" > /dev/null + if [ $? -ne 0 ]; then + log_warning_msg "${DAEMON_NAME} is not started" + result=0 + else + log_daemon_msg "Stopping ${DAEMON_DESC}" "${DAEMON_NAME}" + killproc -p "${DAEMON_PID}" "${DAEMON_PATH}" + result=$? + log_end_msg $result + rm "${DAEMON_PID}" + fi + return $result +} + +do_restart() { + local result + do_stop + result=$? + if [ $result = 0 ]; then + do_start + result=$? + fi + return $result +} + +do_status() { + local result + status_of_proc -p "${DAEMON_PID}" "${DAEMON_PATH}" "${DAEMON_NAME}" + result=$? + return $result +} + +do_usage() { + echo $"Usage: $0 {start | stop | restart | status}" + exit 1 +} + +case "$1" in +start) do_start; exit $? ;; +stop) do_stop; exit $? ;; +restart) do_restart; exit $? ;; +status) do_status; exit $? ;; +*) do_usage; exit 1 ;; +esac + diff --git a/pi-config/doorduino.py b/pi-config/doorduino.py new file mode 100644 index 0000000..5143440 --- /dev/null +++ b/pi-config/doorduino.py @@ -0,0 +1,169 @@ +#!/usr/bin/python3 + +from serial.tools import list_ports +import serial +import io +import time +import subprocess +import csv +import getopt +import sys +import os +import math +import paramiko +from pathlib import Path +import configparser +import re +import threading +import syslog + +def read_configuration(configdir): + if (configdir == ''): + print("Missing configdir.") + sys.exit(2) + + if not os.path.exists(configdir): + print("Directory ", configdir, " does not exist"); + sys.exit(2) + + configfile = Path(os.path.join(configdir, 'settings')) + if not configfile.is_file(): + print(configfile, " does not exist") + sys.exit(2) + + config = configparser.ConfigParser() + config.readfp(configfile.open()) + + expected_config_options = { 'mqtt': [ 'doorbell.subject', 'dooropen.subject', 'lockstate.subject', 'server', 'mqtt-simple' ], + 'serial': [ 'device' ] } + + for section, options in expected_config_options.items(): + for option in options: + if not config.has_option(section, option): + print("Missing config option ", option, "in section", section) + sys.exit(2) + + return config + +def mqtt_thread(config, subject, value, persistent): + if persistent: + subprocess.call([config.get('mqtt', 'mqtt-simple'), "-h", config.get('mqtt', 'server'), "-r", "-p", subject, "-m", value]) + else: + subprocess.call([config.get('mqtt', 'mqtt-simple'), "-h", config.get('mqtt', 'server'), "-p", subject, "-m", value]) + +def mqtt(config, subject, value, persistent=False): + threading.Thread(target = mqtt_thread, args = (config, subject, value, persistent)).start() + +def log(message): + print("LOG " + message) + syslog.syslog(message) + +def main(argv): + global users + + configdir = '' + + syslog.openlog('doorduino') + + try: + opts, args = getopt.getopt(argv,"c:",["config="]) + except getopt.GetoptError: + print('doorduino.py -c ') + sys.exit(2) + + for opt, arg in opts: + if opt == "-c" or opt == "--config": + configdir = arg + + config = read_configuration(configdir) + + while True: + try: + #cdc = next(list_ports.grep(config.get('serial', 'device'))) + #ser = serial.Serial(cdc[0]) + ser = serial.Serial(config.get('serial', 'device'), 115200) + + time.sleep(2); +# ser.write(b"R\n"); + print("Doorduino started"); + + while True: + data = ser.readline() + action = data.decode("iso-8859-1").strip() + + print("Data:" + action) + if action == "Horn activated": + print("Horn activated") + log("Horn activated") + mqtt(config, config.get('mqtt','doorbell.subject'), '1', False) + time.sleep(2) + mqtt(config, config.get('mqtt','doorbell.subject'), '0', False) + elif action == "Solenoid activated": + print("Solenoid activated") + log("Solenoid activated") + mqtt(config, config.get('mqtt','dooropen.subject'), '1', False) + time.sleep(2) + mqtt(config, config.get('mqtt','dooropen.subject'), '0', False) + elif action == "iButton authenticated": + print("iButton authenticated") + log("iButton authenticated") + elif action == "opening lock": + print("lock open") + log("lock open") + mqtt(config, config.get('mqtt','lockstate.subject'), 'open', True) + elif action == "closing lock": + print("lock closed") + log("lock closed") + mqtt(config, config.get('mqtt','lockstate.subject'), 'closed', True) + +# if action == 'B': +# print("Arduino ready") +# ser.write(b"R\n") +# elif action == 'A': +# print("Authenticating", value) +# try: +# if users[value]: +# ser.write(b"U"); +# if users[value]['maintenance']: +# ser.write(b'T') +# else: +# ser.write(b'F') +# +# ser.write(users[value]['price'].encode('utf-8')) +# ser.write(users[value]['revbank'].encode('utf-8')) +# ser.write(b"\n") +# +# log("Laser unlock by " + users[value]['revbank'] + " (" + value + ")") +# +# active_user = users[value] +# except KeyError: +# log("Laser unlock attempt by unknown iButton " + value) +# ser.write(b"FNiet gevonden\n"); +# threading.Thread(target = git_update, args = (config.get('git', 'git'), configdir)).start() +# +# elif action == 'W': +# mqtt(config, config.get('mqtt','water.subject'), value, False); +# elif action == 'T': +# mqtt(config, config.get('mqtt', 'temperature.subject'), value, False); +# elif action == 'S': +# log("Laser job started for " + active_user['revbank']) +# mqtt(config, config.get('mqtt', 'laseractive.subject'), '1', True) +# elif action == 'E': +# log("Laser job finished for " + active_user['revbank'] + " in " + value + "ms") +# mqtt(config, config.get('mqtt', 'laseractive.subject'), '0', True) +# payment(config, active_user, float(value)) +# elif action == 'M': +# log("Maintenance state entered by " + active_user['revbank']) +# elif action == 'L': +# log("Left maintenance state by " + active_user['revbank']) + + except serial.SerialException: + print("Serial connection error") + time.sleep(2) + except StopIteration: + print("No device found") + time.sleep(2) + +if __name__ == "__main__": + main(sys.argv[1:]) + diff --git a/pi-config/reset_arduino.sh b/pi-config/reset_arduino.sh new file mode 100644 index 0000000..efd7823 --- /dev/null +++ b/pi-config/reset_arduino.sh @@ -0,0 +1,8 @@ +#!/bin/bash +cd /sys/class/gpio/ +echo 6 > export +echo out > gpio6/direction +echo 0 > gpio6/value +echo 1 > gpio6/value +echo 6 > unexport +