mirror of
https://github.com/bitlair/bitlair_doorduino.git
synced 2025-05-13 12:20:07 +02:00
Add spacestate support
This commit is contained in:
parent
fd0e6bfcc0
commit
02be0a1921
4 changed files with 198 additions and 65 deletions
2
pi-config/.gitignore
vendored
Normal file
2
pi-config/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
toegang
|
||||||
|
config/settings
|
|
@ -4,6 +4,9 @@ dooropen.subject=bitlair/doorduino/dooropen
|
||||||
lockstate.subject=bitlair/doorduino/lockstate
|
lockstate.subject=bitlair/doorduino/lockstate
|
||||||
server=mqtt.bitlair.nl
|
server=mqtt.bitlair.nl
|
||||||
mqtt-simple=/usr/local/bin/mqtt-simple
|
mqtt-simple=/usr/local/bin/mqtt-simple
|
||||||
|
client-id=doorpi
|
||||||
|
state-bitlair.subject=bitlair/state
|
||||||
|
state-djo.subject=bitlair/state/djo
|
||||||
|
|
||||||
[serial]
|
[serial]
|
||||||
device=/dev/ttyS2
|
device=/dev/ttyS2
|
|
@ -16,6 +16,84 @@ import configparser
|
||||||
import re
|
import re
|
||||||
import threading
|
import threading
|
||||||
import syslog
|
import syslog
|
||||||
|
import csv
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from paho.mqtt import client as mqtt_client
|
||||||
|
|
||||||
|
def connect_mqtt(config, broker, port, client_id):
|
||||||
|
# def on_connect(client, userdata, flags, rc):
|
||||||
|
# For paho-mqtt 2.0.0, you need to add the properties parameter.
|
||||||
|
def on_connect(client, userdata, flags, rc, properties):
|
||||||
|
if rc == 0:
|
||||||
|
print("Connected to MQTT Broker!")
|
||||||
|
subscribe(client, config.get('mqtt', 'state-bitlair.subject'))
|
||||||
|
subscribe(client, config.get('mqtt', 'state-djo.subject'))
|
||||||
|
else:
|
||||||
|
print("Failed to connect, return code %d\n", rc)
|
||||||
|
# Set Connecting Client ID
|
||||||
|
# For paho-mqtt 2.0.0, you need to set callback_api_version.
|
||||||
|
client = mqtt_client.Client(client_id=client_id, callback_api_version=mqtt_client.CallbackAPIVersion.VERSION2)
|
||||||
|
|
||||||
|
# client.username_pw_set(username, password)
|
||||||
|
client.on_connect = on_connect
|
||||||
|
client.connect(broker, port)
|
||||||
|
return client
|
||||||
|
|
||||||
|
FIRST_RECONNECT_DELAY = 1
|
||||||
|
RECONNECT_RATE = 2
|
||||||
|
MAX_RECONNECT_COUNT = 12
|
||||||
|
MAX_RECONNECT_DELAY = 60
|
||||||
|
|
||||||
|
def on_disconnect(client, userdata, rc):
|
||||||
|
logging.info("Disconnected with result code: %s", rc)
|
||||||
|
reconnect_count, reconnect_delay = 0, FIRST_RECONNECT_DELAY
|
||||||
|
while reconnect_count < MAX_RECONNECT_COUNT:
|
||||||
|
logging.info("Reconnecting in %d seconds...", reconnect_delay)
|
||||||
|
time.sleep(reconnect_delay)
|
||||||
|
|
||||||
|
try:
|
||||||
|
client.reconnect()
|
||||||
|
logging.info("Reconnected successfully!")
|
||||||
|
return
|
||||||
|
except Exception as err:
|
||||||
|
logging.error("%s. Reconnect failed. Retrying...", err)
|
||||||
|
|
||||||
|
reconnect_delay *= RECONNECT_RATE
|
||||||
|
reconnect_delay = min(reconnect_delay, MAX_RECONNECT_DELAY)
|
||||||
|
reconnect_count += 1
|
||||||
|
logging.info("Reconnect failed after %s attempts. Exiting...", reconnect_count)
|
||||||
|
|
||||||
|
states = {}
|
||||||
|
def update_spacestate():
|
||||||
|
global ser
|
||||||
|
open = False
|
||||||
|
for key in states:
|
||||||
|
if states[key] == "open":
|
||||||
|
open = True
|
||||||
|
try:
|
||||||
|
if open:
|
||||||
|
print("Send spacestate open")
|
||||||
|
ser.write(b"\n")
|
||||||
|
ser.write(b"spacestate open\n");
|
||||||
|
else:
|
||||||
|
print("Send spacestate closed")
|
||||||
|
ser.write(b"\n")
|
||||||
|
ser.write(b"spacestate closed\n")
|
||||||
|
except serial.SerialException:
|
||||||
|
print("Serial connection error")
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
|
||||||
|
def subscribe(client: mqtt_client, topic):
|
||||||
|
def on_message(client, userdata, msg):
|
||||||
|
states[msg.topic] = msg.payload.decode()
|
||||||
|
print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic")
|
||||||
|
update_spacestate()
|
||||||
|
|
||||||
|
client.subscribe(topic)
|
||||||
|
client.on_message = on_message
|
||||||
|
|
||||||
|
|
||||||
def read_configuration(configdir):
|
def read_configuration(configdir):
|
||||||
if (configdir == ''):
|
if (configdir == ''):
|
||||||
|
@ -32,7 +110,7 @@ def read_configuration(configdir):
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
config.readfp(configfile.open())
|
config.read_file(configfile.open())
|
||||||
|
|
||||||
expected_config_options = { 'mqtt': [ 'doorbell.subject', 'dooropen.subject', 'lockstate.subject', 'server', 'mqtt-simple' ],
|
expected_config_options = { 'mqtt': [ 'doorbell.subject', 'dooropen.subject', 'lockstate.subject', 'server', 'mqtt-simple' ],
|
||||||
'serial': [ 'device' ] }
|
'serial': [ 'device' ] }
|
||||||
|
@ -45,46 +123,36 @@ def read_configuration(configdir):
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
def mqtt_thread(config, subject, value, persistent):
|
def mqtt_send_thread(config, subject, value, persistent):
|
||||||
if persistent:
|
if persistent:
|
||||||
subprocess.call([config.get('mqtt', 'mqtt-simple'), "-h", config.get('mqtt', 'server'), "-r", "-p", subject, "-m", value])
|
subprocess.call([config.get('mqtt', 'mqtt-simple'), "-h", config.get('mqtt', 'server'), "-r", "-p", subject, "-m", value])
|
||||||
else:
|
else:
|
||||||
subprocess.call([config.get('mqtt', 'mqtt-simple'), "-h", config.get('mqtt', 'server'), "-p", subject, "-m", value])
|
subprocess.call([config.get('mqtt', 'mqtt-simple'), "-h", config.get('mqtt', 'server'), "-p", subject, "-m", value])
|
||||||
|
|
||||||
def mqtt(config, subject, value, persistent=False):
|
def mqtt(config, subject, value, persistent=False):
|
||||||
threading.Thread(target = mqtt_thread, args = (config, subject, value, persistent)).start()
|
threading.Thread(target = mqtt_send_thread, args = (config, subject, value, persistent)).start()
|
||||||
|
|
||||||
def log(message):
|
def log(message):
|
||||||
print("LOG " + message)
|
print("LOG " + message)
|
||||||
syslog.syslog(message)
|
syslog.syslog(message)
|
||||||
|
|
||||||
def main(argv):
|
def mqtt_thread():
|
||||||
global users
|
global client
|
||||||
|
global config
|
||||||
|
client = connect_mqtt(config, config.get('mqtt', 'server'), 1883, config.get('mqtt', 'client-id'))
|
||||||
|
|
||||||
configdir = ''
|
client.loop_forever()
|
||||||
|
|
||||||
syslog.openlog('doorduino')
|
|
||||||
|
|
||||||
try:
|
|
||||||
opts, args = getopt.getopt(argv,"c:",["config="])
|
|
||||||
except getopt.GetoptError:
|
|
||||||
print('doorduino.py -c <configdir>')
|
|
||||||
sys.exit(2)
|
|
||||||
|
|
||||||
for opt, arg in opts:
|
|
||||||
if opt == "-c" or opt == "--config":
|
|
||||||
configdir = arg
|
|
||||||
|
|
||||||
config = read_configuration(configdir)
|
|
||||||
|
|
||||||
|
buttons = []
|
||||||
|
def serial_monitor_thread():
|
||||||
|
global ser
|
||||||
|
global config
|
||||||
|
global buttons
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
#cdc = next(list_ports.grep(config.get('serial', 'device')))
|
ser = serial.Serial(config.get('serial', 'device'), 115200, rtscts=False, dsrdtr=True)
|
||||||
#ser = serial.Serial(cdc[0])
|
|
||||||
ser = serial.Serial(config.get('serial', 'device'), 115200)
|
|
||||||
|
|
||||||
time.sleep(2);
|
time.sleep(2);
|
||||||
# ser.write(b"R\n");
|
|
||||||
print("Doorduino started");
|
print("Doorduino started");
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
@ -115,47 +183,14 @@ def main(argv):
|
||||||
print("lock closed")
|
print("lock closed")
|
||||||
log("lock closed")
|
log("lock closed")
|
||||||
mqtt(config, config.get('mqtt','lockstate.subject'), 'closed', True)
|
mqtt(config, config.get('mqtt','lockstate.subject'), 'closed', True)
|
||||||
|
elif action[:7] == "button:":
|
||||||
# if action == 'B':
|
# print("got button ")
|
||||||
# print("Arduino ready")
|
# print(action[8:])
|
||||||
# ser.write(b"R\n")
|
if not action[8:] in buttons:
|
||||||
# elif action == 'A':
|
buttons.append(action[8:])
|
||||||
# print("Authenticating", value)
|
elif action == "DEBUG: Board started":
|
||||||
# try:
|
print("Arduino was reset, sending spacestate")
|
||||||
# if users[value]:
|
update_spacestate()
|
||||||
# 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:
|
except serial.SerialException:
|
||||||
print("Serial connection error")
|
print("Serial connection error")
|
||||||
|
@ -164,6 +199,97 @@ def main(argv):
|
||||||
print("No device found")
|
print("No device found")
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
|
|
||||||
|
def git_update(git_binary, git_dir):
|
||||||
|
log("Updating git")
|
||||||
|
subprocess.call([git_binary, "-C", git_dir, "pull"])
|
||||||
|
|
||||||
|
|
||||||
|
def git_thread():
|
||||||
|
global ser
|
||||||
|
global buttons
|
||||||
|
print("GIT init")
|
||||||
|
git_update("git", "toegang")
|
||||||
|
git_buttons = {}
|
||||||
|
with open('toegang/toegang.csv', newline='') as csvfile:
|
||||||
|
data = csvfile.read()
|
||||||
|
data = data.replace(' ', '')
|
||||||
|
reader = csv.DictReader(data.splitlines(), delimiter=',')
|
||||||
|
for row in reader:
|
||||||
|
ibutton = row['ibutton'].split(':')
|
||||||
|
git_buttons[ibutton[0].lower()] = ibutton[1].lower()
|
||||||
|
# print(row['naam'] + " " + row['ibutton'])
|
||||||
|
|
||||||
|
if len(git_buttons) < 25:
|
||||||
|
print("Something wrong, not enough buttons in git")
|
||||||
|
return
|
||||||
|
|
||||||
|
buttons = []
|
||||||
|
ser.write(b"\n")
|
||||||
|
ser.write(b"list_buttons\n");
|
||||||
|
time.sleep(10)
|
||||||
|
if len(buttons) < 5:
|
||||||
|
print("Something wrong, not enough buttons in doorduino")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(buttons)
|
||||||
|
for button in git_buttons:
|
||||||
|
if button not in buttons:
|
||||||
|
print("should add " + button)
|
||||||
|
ser.write(b"\n")
|
||||||
|
ser.write(b"add_button "+button.encode('ascii')+b" "+git_buttons[button].encode('ascii')+b"\n")
|
||||||
|
time.sleep(2)
|
||||||
|
# else:
|
||||||
|
# print("already there " + button)
|
||||||
|
|
||||||
|
for button in buttons:
|
||||||
|
if button not in git_buttons:
|
||||||
|
print("should remove " + button)
|
||||||
|
ser.write(b"\n")
|
||||||
|
ser.write(b"remove_button "+button.encode('ascii')+b"\n")
|
||||||
|
time.sleep(2)
|
||||||
|
# else:
|
||||||
|
# print("should be there " + button)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def threadwrap(threadfunc):
|
||||||
|
def wrapper():
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
threadfunc()
|
||||||
|
except BaseException as e:
|
||||||
|
print('{!r}; restarting thread'.format(e))
|
||||||
|
else:
|
||||||
|
print('exited normally, bad thread; restarting')
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
global config
|
||||||
|
configdir = ''
|
||||||
|
|
||||||
|
syslog.openlog('doorduino')
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(argv,"c:",["config="])
|
||||||
|
except getopt.GetoptError:
|
||||||
|
print('doorduino.py -c <configdir>')
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
for opt, arg in opts:
|
||||||
|
if opt == "-c" or opt == "--config":
|
||||||
|
configdir = arg
|
||||||
|
|
||||||
|
config = read_configuration(configdir)
|
||||||
|
|
||||||
|
threading.Thread(target = threadwrap(serial_monitor_thread)).start()
|
||||||
|
time.sleep(5) # Give doorduino time to start before sending spacestate data
|
||||||
|
threading.Thread(target = threadwrap(mqtt_thread)).start()
|
||||||
|
# threading.Thread(target = threadwrap(git_thread)).start()
|
||||||
|
git_thread()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
|
||||||
|
|
2
pi-config/requirements.txt
Normal file
2
pi-config/requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
paho-mqtt
|
||||||
|
paramiko
|
Loading…
Add table
Add a link
Reference in a new issue