Narete stvari da delajo
This commit is contained in:
parent
64a10b0512
commit
29b2beca5a
45 changed files with 809 additions and 1600 deletions
11
controller/Pipfile
Normal file
11
controller/Pipfile
Normal file
|
@ -0,0 +1,11 @@
|
|||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.12"
|
20
controller/Pipfile.lock
generated
Normal file
20
controller/Pipfile.lock
generated
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "702ad05de9bc9de99a4807c8dde1686f31e0041d7b5f6f6b74861195a52110f5"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.12"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {},
|
||||
"develop": {}
|
||||
}
|
|
@ -1,114 +1,122 @@
|
|||
import asyncio
|
||||
import socket
|
||||
from collections import defaultdict
|
||||
import aiomqtt
|
||||
import telnetlib3
|
||||
import toml
|
||||
import sys
|
||||
import sys
|
||||
import os
|
||||
|
||||
#GLOBALS
|
||||
#GLOBALS
|
||||
|
||||
room: str
|
||||
barcoPosition: str
|
||||
barcoIP: str
|
||||
telnetPort: int
|
||||
mqttPort: int
|
||||
mqttIP: str
|
||||
barcoReached: bool
|
||||
|
||||
lastState = defaultdict(lambda: None)
|
||||
|
||||
cmdMap = {
|
||||
'power': 'POWR',
|
||||
'shutter': 'PMUT',
|
||||
'freeze': 'FRZE'
|
||||
'freeze': 'FRZE',
|
||||
#'test_pattern': 'TPRN',
|
||||
}
|
||||
reverseCmdMap = {v: k for k, v in cmdMap.items()}
|
||||
|
||||
# There needs to be a minimum time between writes. Since we have two "threads", we use a lock and a sleep in barco_send() to enforce it
|
||||
lock = asyncio.Lock()
|
||||
|
||||
def parse_barco_response(raw: str):
|
||||
global room, barcoPosition, barcoReached
|
||||
raw = raw[1:-1] # strip square brackets
|
||||
#print(raw)
|
||||
|
||||
if raw.startswith("ERR"):
|
||||
print("Projector" + room + " " + barcoPosition + " returned" + raw)
|
||||
print("ERROR:", raw)
|
||||
return None # TODO parse type error - "disabled control" is special case which shouldnt normally happen
|
||||
|
||||
cmd, status = raw.split("!", maxsplit=2)
|
||||
#print(cmd + " " + status)
|
||||
|
||||
cmd = reverseCmdMap[cmd]
|
||||
status = '1' if status == '01' else '0'
|
||||
status = int(status)
|
||||
|
||||
barcoReached = True
|
||||
return cmd, status
|
||||
|
||||
|
||||
async def barco_telnet_command(client, writer, select: str):
|
||||
global room
|
||||
onSub = f"{room}/projectors/{select}/#"
|
||||
#print('TEST', onSub)
|
||||
onMatch = f"{room}/projectors/{select}/command/+" #TODO should be set?
|
||||
await client.subscribe(onSub)
|
||||
#async with client.messages as msgs:
|
||||
async for mesg in client.messages:
|
||||
# print(mesg.topic.value)
|
||||
# print(mesg.payload.decode())
|
||||
# print('on', select)
|
||||
async def barco_send(writer, value):
|
||||
async with lock:
|
||||
writer.write(value + '\r\n')
|
||||
print("Writing", value)
|
||||
await asyncio.sleep(0.2) # sleep between writes necessary, otherwise it gets confused.
|
||||
|
||||
async def barco_telnet_command(client, writer, select: str):
|
||||
"""Receive commands from MQTT and send them to the projector"""
|
||||
await client.subscribe(f"{room}/projectors/{select}/#")
|
||||
|
||||
async for mesg in client.messages:
|
||||
|
||||
if mesg.topic.matches(f"{room}/projectors/{select}/set/+"):
|
||||
cmd = mesg.topic.value.rsplit("/", maxsplit=1)[-1]
|
||||
val = mesg.payload.decode()
|
||||
|
||||
if val not in ("0", "1") or cmd not in cmdMap:
|
||||
print("INVALID COMMAND OR VALUE:", cmd, val)
|
||||
continue
|
||||
|
||||
barcoCmd = cmdMap[cmd]
|
||||
# Send command to projector
|
||||
await barco_send(writer, f"[{barcoCmd}{val}]")
|
||||
# Immediately ask for a status
|
||||
await barco_send(writer, f"[{barcoCmd}?]")
|
||||
|
||||
if mesg.topic.matches(onMatch):
|
||||
# print("test")
|
||||
cmd = mesg.topic.value.split("/")[-1]
|
||||
#val = '1' if mesg.payload.decode() == 'ON' else '0'
|
||||
val = '1' if mesg.payload.decode() == '1' else '0' # refactor to direct 0 and 1
|
||||
barcoCmd = f"[{cmdMap[cmd]}{val}]"
|
||||
print("Received: [" + mesg.topic.value + "] payload: [" + mesg.payload.decode() + "]")
|
||||
print("Sending command to Barco: " + barcoCmd)
|
||||
writer.write(barcoCmd)
|
||||
|
||||
|
||||
async def barco_telnet_read_status(client, reader, select: str):
|
||||
global room
|
||||
"""Read status reports (we trigger them in the polling task as well as whenver sending a command)"""
|
||||
while True:
|
||||
output = await reader.readuntil(b']')
|
||||
raw_response: str = output.decode().strip() # strip not necessary? needed for local netcat testing though
|
||||
raw_response: str = output.decode()
|
||||
print("Received: " + raw_response + " from Barco (" + select + ')')
|
||||
parsed = parse_barco_response(raw_response)
|
||||
if parsed is None:
|
||||
continue #TODO alert for errors
|
||||
print(f"Updating topic [{parsed[0]}] with value [{parsed[1]}]")
|
||||
await client.publish(f"{room}/projectors/{select}/status/{parsed[0]}", payload=parsed[1])
|
||||
|
||||
try:
|
||||
key, val = parse_barco_response(raw_response)
|
||||
except:
|
||||
print("NOT PARSED:", raw_response)
|
||||
continue
|
||||
|
||||
await client.publish(f"{room}/projectors/{select}/status/{key}", payload=val)
|
||||
|
||||
|
||||
async def barco_telnet_query_status(writer, select: str):
|
||||
"""Periodically ask the projector for its status"""
|
||||
while True:
|
||||
for val in cmdMap.values():
|
||||
print(f"Querying Barco {select} with: [{val}?]")
|
||||
writer.write(f"[{val}?]" + '\r\n') # TODO test if funny CRLF necessary (it probably gets ignored)
|
||||
await asyncio.sleep(0.2) # sleep between writes necessary, otherwise it gets confused.
|
||||
# simultaneous commands from control could break this? TODO fix later
|
||||
await asyncio.sleep(10) # TODO find appropriate period
|
||||
# Most queries only work when turned on, so if we're not sure, only query power
|
||||
if lastState[cmdMap["power"]] == "01":
|
||||
queries = cmdMap.values()
|
||||
else:
|
||||
queries = [cmdMap["power"]]
|
||||
|
||||
for val in queries:
|
||||
await barco_send(writer, f"[{val}?]")
|
||||
|
||||
# async def shell(reader, writer):
|
||||
# async with aiomqtt.Client('localhost', 1883) as client:
|
||||
# task_status_query = asyncio.create_task(barco_telnet_query_status(writer))
|
||||
# task_status_reader = asyncio.create_task(barco_telnet_read_status(client, reader))
|
||||
# task_control = asyncio.create_task(barco_telnet_command(client, writer))
|
||||
# await asyncio.gather(task_status_query, task_status_reader, task_control)
|
||||
await asyncio.sleep(2) # TODO find appropriate period
|
||||
|
||||
|
||||
async def main():
|
||||
global room, barcoReached
|
||||
global barcoPosition, barcoIP, telnetPort, mqttIP, mqttPort
|
||||
global room, barcoReached, barcoPosition
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit("No position provided")
|
||||
else:
|
||||
barcoPosition = sys.argv[1]
|
||||
|
||||
conf = toml.load('./malinaConfig.toml')
|
||||
config_file = os.getenv('MM_CONFIG_PATH', './malinaConfig.toml')
|
||||
conf = toml.load(config_file)
|
||||
room = conf['global']['room']
|
||||
mqttHost = conf['global']['mqttHost']
|
||||
mqttPort = conf['global']['mqttPort']
|
||||
|
||||
g62Barcos = {k: v for k,v in conf["barco_G62"].items()}
|
||||
currentBarco = g62Barcos[barcoPosition]
|
||||
barcoIP = currentBarco['ip']
|
||||
telnetPort = int(currentBarco["port"])
|
||||
|
||||
room = conf["global"]["room"]
|
||||
mqttPort = int(conf["global"]["mqttPort"])
|
||||
mqttIP = conf["global"]["mqttIp"]
|
||||
barcoReached = False
|
||||
try:
|
||||
barcoReader, barcoWriter = await telnetlib3.open_connection(barcoIP, telnetPort)
|
||||
|
@ -116,8 +124,8 @@ async def main():
|
|||
except Exception as e:
|
||||
print("Connection failed: " + barcoIP + ": " + str(e))
|
||||
barcoReached = False
|
||||
finally:
|
||||
async with aiomqtt.Client(mqttIP, mqttPort) as client:
|
||||
else:
|
||||
async with aiomqtt.Client(mqttHost, mqttPort) as client:
|
||||
task_status_query_barco = asyncio.create_task(barco_telnet_query_status(barcoWriter, barcoPosition))
|
||||
task_status_reader_barco = asyncio.create_task(barco_telnet_read_status(client, barcoReader, barcoPosition))
|
||||
task_control_barco = asyncio.create_task(barco_telnet_command(client, barcoWriter, barcoPosition))
|
||||
|
@ -125,18 +133,6 @@ async def main():
|
|||
await asyncio.gather(task_status_query_barco, task_status_reader_barco, task_control_barco)
|
||||
await client.publish(f"{room}/projectors/{barcoPosition}/error", payload=("UNREACHABLE" if not barcoReached else "OK"))
|
||||
|
||||
### fuj to, ne tk delat
|
||||
|
||||
# if __name__ == '__main__':
|
||||
|
||||
# loop = asyncio.get_event_loop()
|
||||
# coro = telnetlib3.open_connection(mainBarcoIP, 3023, shell=shell)
|
||||
# coro = telnetlib3.open_connection(mainBarcoIP, 3023, shell=shell)
|
||||
# # coro = telnetlib3.open_connection('localhost', 1234, shell=shell)
|
||||
# reader, writer = loop.run_until_complete(coro)
|
||||
# loop.run_until_complete(writer.protocol.waiter_closed)
|
||||
|
||||
#mqttIP = sys.argv[1]
|
||||
#barcoIP = sys.argv[2]
|
||||
|
||||
asyncio.run(main())
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
|
|
|
@ -47,12 +47,17 @@ async def shell(reader, writer):
|
|||
async with aiomqtt.Client('localhost', 1883) as client:
|
||||
task_status_query = asyncio.create_task(extron_audio_telnet_status(client, writer, reader))
|
||||
task_control = asyncio.create_task(extron_audio_telnet_control(client, writer))
|
||||
await asyncio.gather(task_control)
|
||||
await asyncio.gather(task_status_query, task_control)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
config_file = os.getenv('MM_CONFIG_PATH', './malinaConfig.toml')
|
||||
conf = toml.load(config_file)
|
||||
room = conf['global']['room']
|
||||
mqttHost = conf['global']['mqttHost']
|
||||
mqttPort = conf['global']['mqttPort']
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
#coro = telnetlib3.open_connection('localhost', 1234, shell=shell)
|
||||
coro = telnetlib3.open_connection('192.168.192.14', 23, shell=shell)
|
||||
reader, writer = loop.run_until_complete(coro)
|
||||
loop.run_until_complete(writer.protocol.waiter_closed)
|
||||
|
|
|
@ -4,47 +4,94 @@ import aiomqtt
|
|||
import asyncio
|
||||
import toml
|
||||
import aiohttp
|
||||
import os
|
||||
|
||||
lucke_bearer_token = "" #TODO only set types
|
||||
room = ""
|
||||
url = ""
|
||||
roomId = 0
|
||||
lucke_bearer_token: str
|
||||
room: str
|
||||
url: str
|
||||
roomId: int|str
|
||||
|
||||
async def sendSceneRequest(client, scene):
|
||||
global roomId
|
||||
endpoint = url.format(roomId=roomId, sceneId=scene)
|
||||
# Content-Type needs to be JSON, but the content itself is ignored
|
||||
async with aiohttp.request("POST", endpoint, headers={"Authorization": "Bearer " + lucke_bearer_token}, json={}) as resp:
|
||||
#if 204 change was made
|
||||
if resp.status != 204:
|
||||
endpoint = url + f"/rest/v2/fri-fkkt/lecture-halls/{roomId}/scenes/{scene}/activate"
|
||||
async with aiohttp.request("GET", endpoint, headers={"Authorization": "Bearer " + lucke_bearer_token}) as resp:
|
||||
if resp.status == 204:
|
||||
await client.publish(f'{room}/lucke/preset/current', payload=scene, qos=1, retain=True)
|
||||
else:
|
||||
print(resp.status, await resp.text())
|
||||
|
||||
await client.publish(f'{room}/lucke/preset/current', payload=scene, qos=1, retain=True)
|
||||
|
||||
async def setLight(client, lightNum, intensity: int):
|
||||
endpoint = url + f"/rest/v2/fri-fkkt/lecture-halls/{roomId}/lights/{lightNum}"
|
||||
async with aiohttp.request("PUT", endpoint, headers={"Authorization": "Bearer " + lucke_bearer_token}, json={
|
||||
"stateOn": intensity != 0,
|
||||
"dimmValue": intensity
|
||||
}) as resp:
|
||||
if resp.status == 204:
|
||||
await client.publish(f'{room}/lucke/brightness/{lightNum}', payload=intensity, qos=1, retain=True)
|
||||
else:
|
||||
print("setLight error:", resp.status, await resp.text())
|
||||
|
||||
async def task_luckeCommand(controlClient):
|
||||
await controlClient.subscribe(f"{room}/lucke/preset/recall")
|
||||
msgs = controlClient.messages
|
||||
async for mesg in msgs:
|
||||
await controlClient.subscribe(f"{room}/lucke/#")
|
||||
|
||||
async for mesg in controlClient.messages:
|
||||
mesg: aiomqtt.Message
|
||||
msgTopicArr = mesg.topic.value.split('/')
|
||||
sceneNum = mesg.payload.decode()
|
||||
print("Received: " + str(msgTopicArr) + " payload: [" + sceneNum + "]")
|
||||
#print('sending request to endpoint..')
|
||||
await sendSceneRequest(controlClient, sceneNum)
|
||||
|
||||
if mesg.topic.matches(f"{room}/lucke/preset/recall"):
|
||||
sceneNum = mesg.payload.decode()
|
||||
print("Received: " + str(mesg.topic) + " payload: [" + sceneNum + "]")
|
||||
await sendSceneRequest(controlClient, sceneNum)
|
||||
|
||||
elif mesg.topic.matches(f"{room}/lucke/set/+"):
|
||||
lightNum = mesg.topic.value.rsplit("/", maxsplit=1)[-1]
|
||||
try:
|
||||
intensity = int(mesg.payload.decode())
|
||||
assert 0 <= intensity <= 100
|
||||
except:
|
||||
print("Invalid message:", mesg)
|
||||
else:
|
||||
await setLight(controlClient, lightNum, intensity)
|
||||
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
async def task_luckePoll(client):
|
||||
"""Polls the API and sends light brightness to the API"""
|
||||
while True:
|
||||
endpoint = url + f"/rest/v2/fri-fkkt/lecture-halls/{roomId}/lights/"
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.get(endpoint, headers={"Authorization": f"Bearer {lucke_bearer_token}"}) as response:
|
||||
if response.status == 200:
|
||||
lights = await response.json()
|
||||
for light in lights:
|
||||
await client.publish(f'{room}/lucke/is_dimmable/{light["id"]}', payload=light["dimmable"], qos=1, retain=True)
|
||||
# TODO: find a better way to handle non-dimmable lights
|
||||
if light["stateOn"]:
|
||||
dimValue = light.get("dimmValue", 100)
|
||||
else:
|
||||
dimValue = 0
|
||||
await client.publish(f'{room}/lucke/brightness/{light["id"]}', payload=dimValue, qos=1, retain=True)
|
||||
|
||||
else:
|
||||
print(f"Failed to fetch lights: {response.status}", await response.text())
|
||||
await asyncio.sleep(.5)
|
||||
|
||||
async def main():
|
||||
global room, lucke_bearer_token, url, roomId
|
||||
conf = toml.load('./malinaConfig.toml')
|
||||
room = conf.get("global")['room'] #TODO use brackets everywhere (also other files)
|
||||
url = conf.get("lucke")['url']
|
||||
roomId = conf.get("lucke")['roomId']
|
||||
lucke_bearer_token = conf.get("lucke")['bearer_token']
|
||||
|
||||
async with aiomqtt.Client('localhost', 1883) as client:
|
||||
config_file = os.getenv('MM_CONFIG_PATH', './malinaConfig.toml')
|
||||
conf = toml.load(config_file)
|
||||
room = conf['global']['room']
|
||||
mqttHost = conf['global']['mqttHost']
|
||||
mqttPort = conf['global']['mqttPort']
|
||||
|
||||
url = conf["lucke"]['url']
|
||||
roomId = conf["lucke"]['roomId']
|
||||
lucke_bearer_token = conf["lucke"]['bearer_token']
|
||||
|
||||
async with aiomqtt.Client(mqttHost, mqttPort) as client:
|
||||
task_control = asyncio.create_task(task_luckeCommand(client))
|
||||
task_control = asyncio.create_task(task_luckePoll(client))
|
||||
await asyncio.gather(task_control)
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
asyncio.run(main())
|
||||
|
|
17
controller/malinaConfig.toml
Normal file
17
controller/malinaConfig.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[global]
|
||||
mqttHost = '192.168.192.42'
|
||||
mqttPort = 1883
|
||||
room = 'P01'
|
||||
|
||||
[lucke]
|
||||
url = 'http://192.168.190.90'
|
||||
roomId = 1
|
||||
bearer_token = '0954afe1-4111-4f89-a123-fea08a55dc46'
|
||||
|
||||
[barco_G62.main]
|
||||
ip = '192.168.192.12'
|
||||
port = 3023
|
||||
|
||||
[barco_G62.side]
|
||||
ip = '192.168.192.13'
|
||||
port = 3023
|
|
@ -1,30 +1,23 @@
|
|||
#import gpiozero.pins.mock
|
||||
#from gpiozero import *
|
||||
#from grove.factory import Factory
|
||||
#from grove.grove_relay import GroveRelay
|
||||
MOCK = False
|
||||
import aiomqtt
|
||||
import asyncio
|
||||
#from i2cpy import I2C
|
||||
from smbus2 import SMBus
|
||||
import toml
|
||||
#import i2cpy
|
||||
#i2cset -y 1 0x11 0x11 0x42
|
||||
#set i2c address from 0x11 to 0x42
|
||||
import os
|
||||
|
||||
# ONLY FOR TESTING ON NON RPI
|
||||
#Device.pin_factory = gpiozero.pins.mock.MockFactory()
|
||||
|
||||
#relays = [LED(17), LED(27), LED(22), LED(23), LED(5), LED(6), LED(24), LED(25)]
|
||||
main_I2C_addr = 0 #default
|
||||
side_I2C_addr = 0 #TODO get actual projector motor things from config (dont hardcode main/side)
|
||||
room = ""
|
||||
use_offset = False
|
||||
room: str
|
||||
use_offset = False
|
||||
|
||||
""" 0 1 2 3 """
|
||||
relayMasks = [0b0001, 0b0010, 0b0100, 0b1000] #probably ne rabim
|
||||
|
||||
bus = SMBus(1)
|
||||
|
||||
i2c_map = {
|
||||
'main': -1,
|
||||
'side': -1,
|
||||
}
|
||||
|
||||
relMapping = {
|
||||
'service_down': 0,
|
||||
'service_up': 1,
|
||||
|
@ -38,36 +31,26 @@ currentState = {
|
|||
}
|
||||
|
||||
async def msgRelayBoard(projSelect, command, state: bool):
|
||||
#i2cAddr = relayBoardMain if projSelect == 'glavni' else relayBoardSide
|
||||
#TODO this is not optimal, check for more crap
|
||||
|
||||
# register 0x10 za releje
|
||||
|
||||
I2CAddr = 0 #glavni
|
||||
match projSelect:
|
||||
case 'main':
|
||||
I2CAddr = main_I2C_addr
|
||||
case 'side':
|
||||
I2CAddr = side_I2C_addr
|
||||
#return #TODO TEMPORARY, REMOVE LATER#
|
||||
case default:
|
||||
return #ignore if unknown position
|
||||
# Select the correct relay board
|
||||
i2c_addr = i2c_map[projSelect]
|
||||
|
||||
# Get the relay position for the given command
|
||||
maskShift = relMapping[command]
|
||||
# Set the corresponding bit
|
||||
mask = (1 << maskShift)
|
||||
if state:
|
||||
currentState[projSelect] = currentState[projSelect] | mask
|
||||
else:
|
||||
currentState[projSelect] = currentState[projSelect] & ~mask
|
||||
|
||||
bus.write_byte_data(I2CAddr, 0x10, currentState[projSelect])
|
||||
print("Command sent")
|
||||
#print('testirovano jako')
|
||||
|
||||
# Write it to the I2C bus (0x10 is the register for relay states)
|
||||
bus.write_byte_data(i2c_addr, 0x10, currentState[projSelect])
|
||||
print(projSelect, "{:04b}".format(currentState[projSelect]))
|
||||
|
||||
|
||||
"""
|
||||
SrvDwn SrvUp OpDwn OpUp
|
||||
MAIN: 0x42 0001 0010 0100 1000
|
||||
MAIN: 0x42 0001 0010 0100 1000
|
||||
SIDE: 0x43 0001 0010 0100 1000
|
||||
"""
|
||||
|
||||
|
@ -81,45 +64,84 @@ SIDE: 4 5 6 7
|
|||
#dej like bolš to podukumentiraj or smth
|
||||
|
||||
async def task_command2relays(controlClient: aiomqtt.Client):
|
||||
global room
|
||||
#relayCtrl = lambda cmd, relay: relays[relay].on() if cmd == 1 else relays[relay].off()
|
||||
#relayCtrl = lambda cmd, relay: [relays[r].on() if cmd == 1 and r == relay else relays[r].off for r in range(len(relays))]
|
||||
|
||||
relayCtrl = lambda x, y: print(x, y)
|
||||
"""Read commands from MQTT and send them to the relays"""
|
||||
|
||||
await controlClient.subscribe(f"{room}/projectors/+/lift/#")
|
||||
msgs = controlClient.messages
|
||||
async for mesg in msgs:
|
||||
mesg: aiomqtt.Message
|
||||
if mesg.topic.matches(f'{room}/projectors/+/lift/move/+'):
|
||||
msgTopicArr = mesg.topic.value.split('/')
|
||||
state = mesg.payload.decode()
|
||||
print("Received: " + str(msgTopicArr) + " payload: [" + state + "]")
|
||||
#testCase = (msgTopicArr[2], msgTopicArr[4])
|
||||
projSel = msgTopicArr[2] #TODO projselect odzadaj indexed (just works tm)
|
||||
if projSel != 'main' and projSel != 'side':
|
||||
continue #TODO error hnadling?
|
||||
command = msgTopicArr[5] #TODO same
|
||||
await msgRelayBoard(projSel, command, state == '1')
|
||||
await controlClient.publish(f'{room}/projectors/{projSel}/lift/status', payload="", qos=1, retain=True)
|
||||
|
||||
async for mesg in controlClient.messages:
|
||||
msgTopicArr = mesg.topic.value.split('/')
|
||||
value = mesg.payload.decode()
|
||||
|
||||
if mesg.topic.matches(f'{room}/projectors/+/lift/manual/+'):
|
||||
command = msgTopicArr[-1]
|
||||
projSel = msgTopicArr[-4]
|
||||
|
||||
if projSel not in ("main", "side") or command not in relMapping.keys() or value not in ("0", "1"):
|
||||
print("Invalid manual command:", projSel, command, value)
|
||||
continue
|
||||
|
||||
await msgRelayBoard(projSel, command, value == '1')
|
||||
|
||||
# Service move
|
||||
if "service" in command:
|
||||
# Manual control makes the position unknown, so we clear it
|
||||
if value == "1":
|
||||
status = "MOVING"
|
||||
else:
|
||||
status = "STOPPED"
|
||||
await controlClient.publish(f'{room}/projectors/{projSel}/lift/status', payload=status, qos=1, retain=True)
|
||||
# Normal move
|
||||
else:
|
||||
# HACK: if the press is too short it doesn't register, so we sleep for a bit
|
||||
if value == "1":
|
||||
await asyncio.sleep(.2)
|
||||
|
||||
#print("Pushing \'off\' to other relays to prevent conflicts")
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
elif mesg.topic.matches(f'{room}/projectors/+/lift/goto'):
|
||||
projSel = msgTopicArr[-3]
|
||||
|
||||
if projSel not in ("main", "side") or value not in ("UP", "DOWN"):
|
||||
print("Invalid goto command:", projSel, value)
|
||||
continue
|
||||
|
||||
# Clear manual control
|
||||
await msgRelayBoard(projSel, "service_down", False)
|
||||
await msgRelayBoard(projSel, "service_up", False)
|
||||
|
||||
if value == "UP":
|
||||
other = "down"
|
||||
elif value == "DOWN":
|
||||
other = "up"
|
||||
await msgRelayBoard(projSel, other, False)
|
||||
|
||||
# Click the buttom for a bit and release it, then publish that the lift has moved
|
||||
await msgRelayBoard(projSel, value.lower(), True)
|
||||
await asyncio.sleep(1)
|
||||
await msgRelayBoard(projSel, value.lower(), False)
|
||||
|
||||
await controlClient.publish(f'{room}/projectors/{projSel}/lift/status', payload=value, qos=1, retain=True)
|
||||
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
|
||||
|
||||
async def main():
|
||||
global main_I2C_addr, side_I2C_addr, room
|
||||
conf = toml.load('./malinaConfig.toml')
|
||||
projMotors = conf.get("projector_motors")
|
||||
mainMotor = projMotors.get("main")
|
||||
sideMotor = projMotors.get("side")
|
||||
main_I2C_addr = mainMotor['i2c_address']
|
||||
side_I2C_addr = sideMotor['i2c_address'] #TODO spremen v dict
|
||||
global i2c_map, room
|
||||
|
||||
room = conf["global"]["room"]
|
||||
config_file = os.getenv('MM_CONFIG_PATH', './malinaConfig.toml')
|
||||
conf = toml.load(config_file)
|
||||
room = conf['global']['room']
|
||||
mqttHost = conf['global']['mqttHost']
|
||||
mqttPort = conf['global']['mqttPort']
|
||||
|
||||
async with aiomqtt.Client('localhost', 1883) as client:
|
||||
projMotors = conf["projector_motors"]
|
||||
i2c_map['main'] = projMotors["main"]['i2c_address']
|
||||
i2c_map['side'] = projMotors["side"]['i2c_address']
|
||||
|
||||
async with aiomqtt.Client(mqttHost, mqttPort) as client:
|
||||
task_control = asyncio.create_task(task_command2relays(client))
|
||||
await asyncio.gather(task_control)
|
||||
|
||||
if __name__ == '__main__':
|
||||
asyncio.run(main())
|
||||
asyncio.run(main())
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
import asyncio
|
||||
import serial
|
||||
import aioserial
|
||||
import aiomqtt
|
|
@ -4,14 +4,17 @@ import aioserial
|
|||
import aiomqtt
|
||||
from tse_serial_interpreter import *
|
||||
from dataclasses import dataclass
|
||||
import os
|
||||
import os
|
||||
import sys
|
||||
import toml
|
||||
import toml
|
||||
import serial.tools.list_ports
|
||||
|
||||
#GLOBALS
|
||||
|
||||
room = 'undefined' #TODO make be do get fronm file of configuration
|
||||
# GLOBALS
|
||||
room: str
|
||||
platnoBckgdMoving = {
|
||||
'main': False,
|
||||
'side': False,
|
||||
}
|
||||
|
||||
# serPath = '/dev/serial/by-id/'
|
||||
# devLst = os.listdir(serPath)
|
||||
|
@ -20,11 +23,16 @@ room = 'undefined' #TODO make be do get fronm file of configuration
|
|||
# serDev = devLst[0]
|
||||
|
||||
portList = serial.tools.list_ports.comports()
|
||||
if len(portList) < 1:
|
||||
sys.exit("No serial port found")
|
||||
#TODO if multiple ports idk, shouldn't ever happen but still
|
||||
(serport, serdesc, serhwid) = portList[0] #TODO CHECK FOR TTYUSB0
|
||||
#TODO if port provided from conf, set, otherwise autodetect magical thing just works
|
||||
candidates = ('/dev/ttyUSB0', '/dev/ttyACM0', '/dev/ttyUSB1', '/dev/ttyACM1')
|
||||
serport = ''
|
||||
# Find first serial port that exists
|
||||
for port in portList:
|
||||
if port.device in candidates:
|
||||
serport = port.device
|
||||
break
|
||||
else:
|
||||
print(portList)
|
||||
sys.exit("No serial port found")
|
||||
|
||||
aser: aioserial.AioSerial = aioserial.AioSerial(
|
||||
port=serport,
|
||||
|
@ -32,10 +40,8 @@ aser: aioserial.AioSerial = aioserial.AioSerial(
|
|||
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,
|
||||
|
@ -63,10 +69,10 @@ reverse_lookup = {
|
|||
1: ("power", "master", ""),
|
||||
2: ("power", "audio", ""),
|
||||
3: ("power", "projectors", ""),
|
||||
|
||||
|
||||
5: ("platno", "main", "MOVING_DOWN"),
|
||||
6: ("platno", "main", "MOVING_UP"),
|
||||
|
||||
|
||||
7: ("platno", "side", "MOVING_DOWN"),
|
||||
8: ("platno", "side", "MOVING_UP"),
|
||||
|
||||
|
@ -113,7 +119,7 @@ async def executeAndPublish(mqttClient, pubTopic, pubPayload, relStat):
|
|||
await mqttClient.publish(pubTopic, payload=pubPayload)
|
||||
await asyncio.sleep(0.1) #TODO probably remove
|
||||
|
||||
|
||||
|
||||
async def handleTsePower(client, sysSelect, cmd):
|
||||
rel = RelayState(mapping_toggles[sysSelect], cmd == '1')
|
||||
await executeAndPublish(client, f'{room}/power/{sysSelect}/status', cmd, rel)
|
||||
|
@ -129,13 +135,8 @@ async def handleTseSencilo(client, cmd):
|
|||
await executeAndPublish(client, topicPub, "MOVING_DOWN", rel)
|
||||
else:
|
||||
await executeAndPublish(client, topicPub, "STOPPED", RelayState(shades_mapping['UP'], False))
|
||||
await executeAndPublish(client, topicPub, "STOPPED", RelayState(shades_mapping['DOWN'], False))
|
||||
|
||||
await executeAndPublish(client, topicPub, "STOPPED", RelayState(shades_mapping['DOWN'], False))
|
||||
|
||||
platnoBckgdMoving = {
|
||||
'main': False,
|
||||
'side': False,
|
||||
} # mucho importante variable prav zares dedoles
|
||||
|
||||
async def platnoTimeout(mqttClient, pubTopic, pubPayload, relStat: RelayState, intent, select):
|
||||
global platnoBckgdMoving
|
||||
|
@ -144,12 +145,10 @@ async def platnoTimeout(mqttClient, pubTopic, pubPayload, relStat: RelayState, i
|
|||
await executeAndPublish(mqttClient, pubTopic, intent, relStat)
|
||||
platnoBckgdMoving[select] = False #TODO properly document why this is here and what it does
|
||||
|
||||
|
||||
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}/platno/status'
|
||||
if not (proj == "main" or proj == "side"):
|
||||
return
|
||||
|
@ -203,21 +202,13 @@ async def handleTsePlatno(client, proj, cmdType, cmd):
|
|||
print('unknown command')
|
||||
|
||||
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')
|
||||
#print(mesg, mesg.topic)
|
||||
|
||||
#mesg: aiomqtt.Message
|
||||
async for mesg in controlClient.messages:
|
||||
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/move') or mesg.topic.matches(f'{room}/projectors/+/platno/goto'):
|
||||
proj = msgTopic[-3]
|
||||
cmdType = msgTopic[-1] # move / goto
|
||||
|
@ -236,7 +227,7 @@ async def task_command2serial(controlClient: aiomqtt.Client):
|
|||
await handleTseSencilo(controlClient, cmnd)
|
||||
|
||||
else:
|
||||
continue
|
||||
continue
|
||||
# code after if block doesnt execute in this case
|
||||
#print("after")
|
||||
await asyncio.sleep(0.01) #TODO do we need this? (probably)
|
||||
|
@ -244,12 +235,17 @@ async def task_command2serial(controlClient: aiomqtt.Client):
|
|||
|
||||
async def main():
|
||||
global room
|
||||
conf = toml.load('./malinaConfig.toml')
|
||||
|
||||
config_file = os.getenv('MM_CONFIG_PATH', './malinaConfig.toml')
|
||||
conf = toml.load(config_file)
|
||||
room = conf['global']['room']
|
||||
async with aiomqtt.Client('localhost', 1883) as client: #TODO omehčaj kodiranje
|
||||
mqttHost = conf['global']['mqttHost']
|
||||
mqttPort = conf['global']['mqttPort']
|
||||
|
||||
async with aiomqtt.Client(mqttHost, mqttPort) 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())
|
||||
asyncio.run(main())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue