DoorAdmin/imaginaerraum_door_admin/door_handle.py

112 lines
4.3 KiB
Python

import paho.mqtt.client as mqtt
import socket
from pathlib import Path
import logging
from datetime import datetime
class DoorHandle:
def __init__(self, token_file, mqtt_host, mqtt_port=1883, nfc_socket='/tmp/nfc.sock', logger=None):
self.state = None
self.encoder_position = None
if not Path(token_file).exists():
raise FileNotFoundError(f"File with door tokens could not be found at {Path(token_file).absolute()}")
self.token_file = token_file
self.last_invalid = {}
self.mqtt_client = mqtt.Client()
self.mqtt_client.on_connect = self.on_connect
self.mqtt_client.on_message = self.on_message
self.mqtt_client.connect_async(host=mqtt_host, port=mqtt_port)
self.mqtt_client.loop_start()
if logger:
self.logger = logger
else:
self.logger = logging
self.nfc_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
self.nfc_sock.connect(nfc_socket)
self.logger.info(f"Connected to NFC socket at {nfc_socket}.")
except Exception as e:
print(f"Could not connect to NFC socket at {nfc_socket}. Exception: {e}")
self.nfc_sock = None
#raise
self.data_fields = ['name', 'organization', 'email', 'valid_thru']
# The callback for when the client receives a CONNACK response from the server.
def on_connect(self, client, userdata, flags, rc):
self.logger.info("Connected to MQTT broker with result code " + str(rc))
# Subscribing in on_connect() means that if we lose the connection and
# reconnect then subscriptions will be renewed.
client.subscribe("#")
# The callback for when a PUBLISH message is received from the server.
def on_message(self, client, userdata, msg):
#print(msg.topic + " " + str(msg.payload))
if msg.topic == 'door/state/value':
self.state = msg.payload.decode()
elif msg.topic == 'door/position/value':
self.encoder_position = int(msg.payload)
elif msg.topic == 'door/token/last_invalid':
timestamp, token = msg.payload.decode().split(";")
timestamp = datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S')
self.last_invalid = {'timestamp': timestamp, 'token': token}
def get_tokens(self):
tokens = {}
with open(self.token_file) as f:
token_lines = f.readlines()
for line in token_lines[1:]:
data = line.split('|')
inactive = '#' in data[0]
token = data[0].strip().strip('#')
name = data[1].strip() if len(data) > 1 else None
organization = data[2].strip() if len(data) > 2 else None
email = data[3].strip() if len(data) > 3 else None
valid_thru = data[4].strip() if len(data) > 4 else None
tokens[token] = {'name': name,
'organization': organization,
'email': email,
'valid_thru': valid_thru,
'inactive': inactive}
return tokens
def store_tokens(self, tokens):
output = '# token | ' + ' | '.join(self.data_fields) + '\n'
for t, data in tokens.items():
output += '#' if data['inactive'] else ''
output += t
for key in self.data_fields:
output += '|'
output += data[key] if data[key] else ''
output += '\n'
# write new tokens to file and trigger reload
with open(self.token_file, 'w') as f:
f.write(output)
if self.nfc_sock is not None:
self.nfc_sock.send(b'rld\n')
def open_door(self, user=''):
if self.nfc_sock is not None:
self.nfc_sock.send(b'open ' + user.encode() + b'\n')
else:
raise Exception("No connection to NFC socket. Cannot close door!")
def close_door(self, user=''):
if self.nfc_sock is not None:
self.nfc_sock.send(b'close ' + user.encode() + b'\n')
else:
raise Exception("No connection to NFC socket. Cannot close door!")
def get_most_recent_token(self):
# read last invalid token from logfile
return self.last_invalid