diff --git a/docs/MQTT_struktura.md b/docs/MQTT_struktura.md index 9d3d73a..89b8c16 100644 --- a/docs/MQTT_struktura.md +++ b/docs/MQTT_struktura.md @@ -31,4 +31,8 @@ - `set` - set the power status (`0` or `1`) - `/projectors` - Projectors and lifts - `status` - current power status (`0` or `1`) - - `set` - set the power status (`0` or `1`) \ No newline at end of file + - `set` - set the power status (`0` or `1`) + + - `/firanki/` + - `status` - `STATIONARY`, `MOVING_UP`, `MOVING_DOWN` + - `command` - `NONE`, `MOVE_UP`, `MOVE_DOWN` \ No newline at end of file diff --git a/tse_serial/requirements.txt b/tse_serial/requirements.txt new file mode 100644 index 0000000..1d9b897 --- /dev/null +++ b/tse_serial/requirements.txt @@ -0,0 +1,4 @@ +import asyncio +import serial +import aioserial +import aiomqtt \ No newline at end of file diff --git a/tse_serial/tse_serial_controler.py b/tse_serial/tse_serial_controler.py index 484cd40..4432535 100644 --- a/tse_serial/tse_serial_controler.py +++ b/tse_serial/tse_serial_controler.py @@ -3,6 +3,7 @@ import serial import aioserial import aiomqtt from tse_serial_interpreter import * +from dataclasses import dataclass room = 'p1' #TODO make be do get fronm file of configuration @@ -15,22 +16,33 @@ aser: aioserial.AioSerial = aioserial.AioSerial( ) # TODO adjust serial on actual TSE interface -mapping = { +#TODO get this from file da ni hardcoded + +mapping_toggles = { "master": 1, "audio": 2, "projectors": 3, - - "platno_glavni_dol": 5, - "platno_glavni_gor": 6, - "platno_stranski_dol": 7, - "platno_stranski_gor": 8, - "sencilo_dol": 9, - "sencilo_gor": 10 - - #ostalo reserved } -reverse_lookup = {v: k for k, v in mapping.items()} +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 + def parse_topic_from_mqtt(topic: str): topicArr = topic.split() @@ -42,43 +54,130 @@ async def task_status2mqtt(statusClient: aiomqtt.Client): 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 + #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) + #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(topicVal, 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}]") + #await aser.write_async(bytes(setRelayCmd + '\r\n', "ascii")) + await mqttClient.publish(pubTopic, payload=pubPayload) + + +async def handleTsePower(tval, client, sysSelect, cmd): + rel = RelayState(mapping_toggles[sysSelect], cmd == '1') + await executeAndPublish(tval, client, f'{room}/power/{sysSelect}/status', cmd, rel) + +async def handleTseSencilo(tval, client, cmd): + #relName = tval.split('/')[3] + topicPub = f'{room}/firanki/status' + if cmd == "MOVE_UP": + rel = RelayState(shades_mapping['gor'], True) + await executeAndPublish(tval, client, topicPub, "MOVING_UP", rel) + elif cmd == "MOVE_DOWN": + rel = RelayState(shades_mapping['dol'], True) + await executeAndPublish(tval, client, topicPub, "MOVING_DOWN", rel) + else: + await executeAndPublish(tval, client, topicPub, "STATIONARY", RelayState(shades_mapping['gor'], False)) + await executeAndPublish(tval, client, topicPub, "STATIONARY", RelayState(shades_mapping['dol'], False)) + + +platnoBckgdMoving = False # mucho importante variable prav zares dedoles + +async def platnoTimeout(topicVal, mqttClient, pubTopic, pubPayload, relStat): + global platnoBckgdMoving + await asyncio.sleep(3) #TODO time actual delay + relStat.state = False + executeAndPublish(topicVal, mqttClient, pubTopic, pubPayload, relStat) + platnoBckgdMoving = False + +async def handleTsePlatno(tval, 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/{projector}/status' + + if platnoBckgdMoving: + if cmd == 'STOP': + pubPld = 'UNKNOWN' + rel1 = RelayState(platno_mapping[projector]['gor'], False) + rel2 = RelayState(platno_mapping[projector]['dol'], False) + executeAndPublish(tval,client,pubTop, pubPld, rel1) + executeAndPublish(tval,client,pubTop, pubPld, rel2) + else: + return # ignore any other move commands while moving + + #movement cmds + if 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 + platnoBckgdState = True + pubPld = 'MOVING' + executeAndPublish(tval, client, pubTop, pubPld, rel) + + elif cmdType == 'goto': + 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 + platnoBckgdState = True + pubPld = 'MOVING' + executeAndPublish(tval, client, pubTop, pubPld, rel) + asyncio.create_task(platnoTimeout(tval, client, pubTop, pubPld, rel)) + + return + async def task_command2serial(controlClient: aiomqtt.Client): - await controlClient.subscribe("p1/tseRelays/#") + await controlClient.subscribe(f"{room}/#") async with controlClient.messages as msgs: async for mesg in msgs: mesg: aiomqtt.Message - if mesg.topic.matches('p1/tse_box/+/ukaz'): - msgTopic = mesg.topic.value - cmnd = mesg.payload.decode() - print("Received: [" + msgTopic + "] payload: [" + cmnd + "]") - relay = int(mesg.topic.value.split("/")[-2]) #TODO different by case - cmnd = cmnd == "ON" - relState = RelayState(relay, cmnd) - setRelay = relay_state_to_cmd(relState) - print("Sending to TSE box: " + setRelay) - await aser.write_async(bytes(setRelay + '\r\n', "ascii")) + topicVal = mesg.topic.value + msgTopic = mesg.topic.value.split('/') + cmnd = mesg.payload.decode() - publishTopic = f"p1/tseRelays/{relState.relay_id}/status" - publishPayload = "1" if relState.state else "0" - print("Also publishing topic [" + publishTopic + "] with status [" + publishPayload + "]") - await controlClient.publish(publishTopic, payload=publishPayload) + if mesg.topic.matches(f'{room}/projectors/+/platno/#'): + proj = msgTopic[2] # glavni / stranski + cmdType = msgTopic[4] # move / goto + await handleTsePlatno(topicVal, controlClient, proj, cmdType, cmnd) - await asyncio.sleep(0.01) + elif mesg.topic.matches(f'{room}/power/#'): + systype = msgTopic[2] + await handleTsePower(topicVal, controlClient, systype, cmnd) + + elif mesg.topic.matches(f'{room}/firanki/command/#'): + await handleTseSencilo(topicVal, controlClient, cmnd) + + else: + continue + # code after if block doesnt execute in this case + + await asyncio.sleep(0.01) async def main():