import asyncio import serial import aioserial import aiomqtt from tse_serial_interpreter import * from dataclasses import dataclass room = 'p01' #TODO make be do get fronm file of configuration aser: aioserial.AioSerial = aioserial.AioSerial( port='/dev/serial/by-id/usb-Prolific_Technology_Inc._USB-Serial_Controller_D-if00-port0', #id say not hardcode it ampak kinda nimamo izbire baudrate=1200, parity=serial.PARITY_NONE, bytesize=serial.EIGHTBITS, stopbits=serial.STOPBITS_ONE ) #TODO get this from file da ni hardcoded mapping_toggles = { "master": 1, "audio": 2, "projectors": 3, } platno_mapping = { "glavni": { "dol": 5, "gor": 6 }, "stranski": { "dol": 7, "gor": 8 } } shades_mapping = { "dol": 9, "gor": 10 } #reverse_lookup = {v: k for k, v in mapping.items()} #fujto # TODO simple reverse lookup za ko kripa pove kaj # in vse tole 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) #command = reverse_lookup[relState.relay_id] #action = relState.state #TODO havent figured out a clean way to # get out the action from topic yet as they are # not always on the same level # REWORK # probably just do it the most straight forward way # with some more code #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())