predavalnice-kontroler/tse_serial/tse_serial_controler.py

222 lines
No EOL
6.9 KiB
Python

import asyncio
import serial
import aioserial
import aiomqtt
from tse_serial_interpreter import *
from dataclasses import dataclass
import os
import sys
import toml
import serial.tools.list_ports
room = 'p01' #TODO make be do get fronm file of configuration
# serPath = '/dev/serial/by-id/'
# devLst = os.listdir(serPath)
# if len(devLst) < 1:
# sys.exit("No serial device found.")
# serDev = devLst[0]
portList = serial.tools.list_ports.comports()
if portList < 1:
sys.exit("No serial port found")
#TODO if multiple ports idk, shouldn't ever happen but still
(serport, serdesc, serhwid) = portList[0]
aser: aioserial.AioSerial = aioserial.AioSerial(
port=serport,
baudrate=1200,
parity=serial.PARITY_NONE,
bytesize=serial.EIGHTBITS,
stopbits=serial.STOPBITS_ONE
)
#TODO get this from file da ni hardcoded?
# altho itak je ta script za tist specific tse box in so vsi isti
mapping_toggles = {
"master": 1,
"audio": 2,
"projectors": 3,
}
# 4 is not connected to anything
platno_mapping = {
"glavni": {
"dol": 5,
"gor": 6
},
"stranski": {
"dol": 7,
"gor": 8
}
}
shades_mapping = {
"dol": 9,
"gor": 10
}
reverse_lookup = {
1: "master",
2: "audio",
3: "projectors",
5: "glavni_dol",
6: "glavni_gor",
7: "glavni_dol",
8: "glavni_gor",
9: "shades_dol",
10: "shades_gor"
}
async def task_status2mqtt(statusClient: aiomqtt.Client):
while True:
data = await aser.read_until_async()
data = data.decode(errors='ignore').strip()
print("TSE box sent: " + data)
relState = resp_to_relay_state(data)
if relState.relay_id == None:
continue # TODO handling
command = reverse_lookup[relState.relay_id]
action = relState.state
#publishTopic = f"{room}/"
#publishPayload = "ON" if relState.state else "OFF"
#print("Publishing [" + publishPayload + "] to topic [" + publishTopic + "]")
#await statusClient.publish(publishTopic, payload=publishPayload)
await asyncio.sleep(0.01)
async def executeAndPublish(mqttClient, pubTopic, pubPayload, relStat):
setRelayCmd = relay_state_to_cmd(relStat)
#print("Received: [" + topicVal + "] payload: [" + pubPayload + "]")
print("Sending to TSE box: " + setRelayCmd)
print(f"Also publishing topic [{pubTopic}] with status [{pubPayload}]")
print()
await aser.write_async(bytes(setRelayCmd + '\r\n', "ascii"))
await mqttClient.publish(pubTopic, payload=pubPayload)
async def handleTsePower(client, sysSelect, cmd):
rel = RelayState(mapping_toggles[sysSelect], cmd == '1')
await executeAndPublish(client, f'{room}/power/{sysSelect}/platno/status', cmd, rel)
async def handleTseSencilo(client, cmd):
#relName = tval.split('/')[3]
topicPub = f'{room}/firanki/status'
if cmd == "MOVE_UP":
rel = RelayState(shades_mapping['gor'], True)
await executeAndPublish(client, topicPub, "MOVING_UP", rel)
elif cmd == "MOVE_DOWN":
rel = RelayState(shades_mapping['dol'], True)
await executeAndPublish(client, topicPub, "MOVING_DOWN", rel)
else:
await executeAndPublish(client, topicPub, "STOPPED", RelayState(shades_mapping['gor'], False))
await executeAndPublish(client, topicPub, "STOPPED", RelayState(shades_mapping['dol'], False))
platnoBckgdMoving = False # mucho importante variable prav zares dedoles
async def platnoTimeout(mqttClient, pubTopic, pubPayload, relStat: RelayState, intent):
global platnoBckgdMoving
await asyncio.sleep(3) #TODO time actual delay
relStat.state = False
await executeAndPublish(mqttClient, pubTopic, intent, relStat)
platnoBckgdMoving = False
async def handleTsePlatno(client, proj, cmdType, cmd):
global platnoBckgdMoving
#topicSplit = tval.split('/')
# {room} {projectors} {[select]} {platno} {move/goto}
#projector = topicSplit[2]
#command = topicSplit[4]
pubTop = f'{room}/projectors/{proj}/status'
if platnoBckgdMoving:
if cmd == 'STOP':
pubPld = 'UNKNOWN'
rel1 = RelayState(platno_mapping[proj]['gor'], False)
rel2 = RelayState(platno_mapping[proj]['dol'], False)
await executeAndPublish(client,pubTop, pubPld, rel1)
await executeAndPublish(client,pubTop, pubPld, rel2)
platnoBckgdMoving = False
else:
return # ignore any other move commands while moving
#movement cmds
elif cmdType == 'move':
rel: RelayState
if cmd == 'UP':
rel = RelayState(platno_mapping[proj]['gor'], True)
elif cmd == 'DOWN':
rel = RelayState(platno_mapping[proj]['dol'], True)
else:
return # in case of invalid input skip
platnoBckgdMoving = True
pubPld = 'MOVING'
await executeAndPublish(client, pubTop, pubPld, rel)
elif cmdType == 'goto':
# print('received GOTO')
rel: RelayState
if cmd == 'UP':
rel = RelayState(platno_mapping[proj]['gor'], True)
intent = 'UP'
elif cmd == 'DOWN':
rel = RelayState(platno_mapping[proj]['dol'], True)
intent = 'DOWN'
else:
return # in case of invalid input skip
platnoBckgdMoving = True
pubPld = 'MOVING'
await executeAndPublish(client, pubTop, pubPld, rel)
asyncio.create_task(platnoTimeout(client, pubTop, pubPld, rel, intent))
return
async def task_command2serial(controlClient: aiomqtt.Client):
#print('oge')
await controlClient.subscribe(f"{room}/#")
#print('ogee')
#async with controlClient.messages as msgs:
async for mesg in controlClient.messages:
#print('oge')
#mesg: aiomqtt.Message
topicVal = mesg.topic.value
msgTopic = mesg.topic.value.split('/')
cmnd = mesg.payload.decode()
#print('Received on: ', topicVal, ' with:', cmnd)
#print('bfr')
if mesg.topic.matches(f'{room}/projectors/+/platno/#'):
proj = msgTopic[2] # glavni / stranski
cmdType = msgTopic[4] # move / goto
await handleTsePlatno(controlClient, proj, cmdType, cmnd)
elif mesg.topic.matches(f'{room}/power/#'):
systype = msgTopic[2]
await handleTsePower(controlClient, systype, cmnd)
elif mesg.topic.matches(f'{room}/firanki/move/#'):
await handleTseSencilo(controlClient, cmnd)
else:
continue
# code after if block doesnt execute in this case
#print("after")
await asyncio.sleep(0.01)
async def main():
async with aiomqtt.Client('localhost', 1883) as client:
task_status = asyncio.create_task(task_status2mqtt(client))
task_control = asyncio.create_task(task_command2serial(client))
await asyncio.gather(task_status, task_control)
if __name__ == '__main__':
asyncio.run(main())