voc.infobeamer-cms: add infobeamer-monitor
This commit is contained in:
parent
2670d60906
commit
b5475df467
5 changed files with 217 additions and 0 deletions
4
bundles/infobeamer-monitor/files/config.toml
Normal file
4
bundles/infobeamer-monitor/files/config.toml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<%
|
||||||
|
from tomlkit import dumps as toml_dumps
|
||||||
|
from bundlewrap.utils.text import toml_clean
|
||||||
|
%>${toml_clean(toml_dumps(repo.libs.faults.resolve_faults(config), sort_keys=True))}
|
15
bundles/infobeamer-monitor/files/infobeamer-monitor.service
Normal file
15
bundles/infobeamer-monitor/files/infobeamer-monitor.service
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[Unit]
|
||||||
|
Description=infobeamer-monitor
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=exec
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5s
|
||||||
|
ExecStart=/opt/infobeamer-cms/venv/bin/python monitor.py
|
||||||
|
User=infobeamer-cms
|
||||||
|
Group=infobeamer-cms
|
||||||
|
WorkingDirectory=/opt/infobeamer-monitor/
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
155
bundles/infobeamer-monitor/files/monitor.py
Normal file
155
bundles/infobeamer-monitor/files/monitor.py
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from json import dumps
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
from requests import RequestException, get
|
||||||
|
|
||||||
|
try:
|
||||||
|
# python 3.11
|
||||||
|
from tomllib import loads as toml_load
|
||||||
|
except ImportError:
|
||||||
|
from rtoml import load as toml_load
|
||||||
|
|
||||||
|
with open("config.toml") as f:
|
||||||
|
CONFIG = toml_load(f.read())
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
format="[%(levelname)s %(name)s] %(message)s",
|
||||||
|
level=logging.INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
LOG = logging.getLogger("main")
|
||||||
|
MLOG = logging.getLogger("mqtt")
|
||||||
|
|
||||||
|
state = None
|
||||||
|
|
||||||
|
client = mqtt.Client()
|
||||||
|
client.username_pw_set(CONFIG["mqtt"]["user"], CONFIG["mqtt"]["password"])
|
||||||
|
client.connect(CONFIG["mqtt"]["host"], 1883, 60)
|
||||||
|
client.loop_start()
|
||||||
|
|
||||||
|
|
||||||
|
def mqtt_out(message, level="INFO", device=None):
|
||||||
|
key = "infobeamer"
|
||||||
|
if device:
|
||||||
|
key += f"/{device['id']}"
|
||||||
|
message = f"[{device['description']}] {message}"
|
||||||
|
|
||||||
|
client.publish(
|
||||||
|
CONFIG["mqtt"]["topic"],
|
||||||
|
dumps(
|
||||||
|
{
|
||||||
|
"level": level,
|
||||||
|
"component": key,
|
||||||
|
"msg": message,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def mqtt_dump_state(device):
|
||||||
|
mqtt_out(
|
||||||
|
"Sync status: {} - Location: {} - Running Setup: {} ({}) - Resolution: {}".format(
|
||||||
|
"yes" if device["is_synced"] else "unknown",
|
||||||
|
device["location"],
|
||||||
|
device["setup"]["name"],
|
||||||
|
device["setup"]["id"],
|
||||||
|
device["run"].get("resolution", "unknown"),
|
||||||
|
),
|
||||||
|
device=device,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
mqtt_out("Monitor starting up")
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
r = get("https://info-beamer.com/api/v1/device/list", auth=("", CONFIG["api_key"]))
|
||||||
|
r.raise_for_status()
|
||||||
|
ib_state = r.json()["devices"]
|
||||||
|
except RequestException as e:
|
||||||
|
LOG.exception("Could not get data from info-beamer")
|
||||||
|
mqtt_out(
|
||||||
|
f"Could not get data from info-beamer: {e!r}",
|
||||||
|
level="WARN",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
new_state = {}
|
||||||
|
for device in ib_state:
|
||||||
|
did = str(device["id"])
|
||||||
|
|
||||||
|
if did in new_state:
|
||||||
|
mqtt_out("DUPLICATE DETECTED!", level="ERROR", device=device)
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_state[did] = device
|
||||||
|
must_dump_state = False
|
||||||
|
|
||||||
|
if state is not None:
|
||||||
|
if did not in state:
|
||||||
|
LOG.info(
|
||||||
|
"new device found: {} [{}]".format(
|
||||||
|
did,
|
||||||
|
device["description"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
mqtt_out(
|
||||||
|
'new device found with name "{}"!'.format(
|
||||||
|
device["description"]
|
||||||
|
),
|
||||||
|
device=device,
|
||||||
|
)
|
||||||
|
if device["is_online"]:
|
||||||
|
must_dump_state = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
if device["is_online"] != state[did]["is_online"]:
|
||||||
|
online_status = (
|
||||||
|
"online from {}".format(device["run"]["public_addr"])
|
||||||
|
if device["is_online"]
|
||||||
|
else "offline"
|
||||||
|
)
|
||||||
|
|
||||||
|
LOG.info("device {} is now {}".format(did, online_status))
|
||||||
|
mqtt_out(
|
||||||
|
f"online status changed to {online_status}",
|
||||||
|
device=device,
|
||||||
|
)
|
||||||
|
if device["is_online"]:
|
||||||
|
must_dump_state = True
|
||||||
|
|
||||||
|
if device["is_online"]:
|
||||||
|
if device["maintenance"]:
|
||||||
|
mqtt_out(
|
||||||
|
"maintenance required: {}".join(
|
||||||
|
sorted(device["maintenance"])
|
||||||
|
),
|
||||||
|
level="WARN",
|
||||||
|
device=device,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
device["is_synced"] != state[did]["is_synced"]
|
||||||
|
or device["location"] != state[did]["location"]
|
||||||
|
or device["setup"]["name"] != state[did]["setup"]["name"]
|
||||||
|
or device["run"].get("resolution")
|
||||||
|
!= state[did]["run"].get("resolution")
|
||||||
|
):
|
||||||
|
must_dump_state = True
|
||||||
|
|
||||||
|
if must_dump_state:
|
||||||
|
mqtt_dump_state(device)
|
||||||
|
|
||||||
|
else:
|
||||||
|
LOG.info("adding device {} to empty state".format(device["id"]))
|
||||||
|
|
||||||
|
state = new_state
|
||||||
|
sleep(30)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
break
|
||||||
|
|
||||||
|
mqtt_out("Monitor exiting")
|
33
bundles/infobeamer-monitor/items.py
Normal file
33
bundles/infobeamer-monitor/items.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
assert node.has_bundle('infobeamer-cms') # uses same venv
|
||||||
|
|
||||||
|
files['/opt/infobeamer-monitor/config.toml'] = {
|
||||||
|
'content_type': 'mako',
|
||||||
|
'context': {
|
||||||
|
'config': node.metadata.get('infobeamer-monitor'),
|
||||||
|
},
|
||||||
|
'triggers': {
|
||||||
|
'svc_systemd:infobeamer-monitor:restart',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
files['/opt/infobeamer-monitor/monitor.py'] = {
|
||||||
|
'mode': '0755',
|
||||||
|
'triggers': {
|
||||||
|
'svc_systemd:infobeamer-monitor:restart',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
files['/usr/local/lib/systemd/system/infobeamer-monitor.service'] = {
|
||||||
|
'triggers': {
|
||||||
|
'action:systemd-reload',
|
||||||
|
'svc_systemd:infobeamer-monitor:restart',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
svc_systemd['infobeamer-monitor'] = {
|
||||||
|
'needs': {
|
||||||
|
'file:/opt/infobeamer-monitor/config.toml',
|
||||||
|
'file:/opt/infobeamer-monitor/monitor.py',
|
||||||
|
'file:/usr/local/lib/systemd/system/infobeamer-monitor.service',
|
||||||
|
},
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ nodes['voc.infobeamer-cms'] = {
|
||||||
'hostname': 'infobeamer-cms.c3voc.de',
|
'hostname': 'infobeamer-cms.c3voc.de',
|
||||||
'bundles': {
|
'bundles': {
|
||||||
'infobeamer-cms',
|
'infobeamer-cms',
|
||||||
|
'infobeamer-monitor',
|
||||||
'redis',
|
'redis',
|
||||||
},
|
},
|
||||||
'groups': {
|
'groups': {
|
||||||
|
@ -68,6 +69,15 @@ nodes['voc.infobeamer-cms'] = {
|
||||||
'Translations': 'translations',
|
'Translations': 'translations',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
'infobeamer-monitor': {
|
||||||
|
'api_key': vault.decrypt('encrypt$gAAAAABlitmDR1duKo_4KuMJBF_HbPO2GFo_gdoT1rvUKQ2kkugPbe2RljM4bxW5bmwhs5avjxiaSAvjnOBte9ioyPEr7cIh79WFEfMnsHeexlCHwMt6NV_t-8EAhuuEQEf3Py93g8zQ'),
|
||||||
|
'mqtt': {
|
||||||
|
'password': vault.decrypt('encrypt$gAAAAABhxakfhhwWn0vxhoO1FiMEpdCkomWvo0dHIuBrqDKav8WDpI6dXpb0hoXiWRsPV6p5m-8RlbfFbjPhz47AY-nFOOAAW6Yis3-IVD-U-InKJo9dvms='),
|
||||||
|
'host': 'mqtt.c3voc.de',
|
||||||
|
'topic': '/voc/alert',
|
||||||
|
'user': vault.decrypt('encrypt$gAAAAABhxakKHC_kHmHP2mFHorb4niuNTH4F24w1D6m5JUxl117N7znlZA6fpMmY3_NcmBr2Ihw4hL3FjZr9Fm_1oUZ1ZQdADA=='),
|
||||||
|
},
|
||||||
|
},
|
||||||
'nginx': {
|
'nginx': {
|
||||||
'vhosts': {
|
'vhosts': {
|
||||||
'redirect': {
|
'redirect': {
|
||||||
|
|
Loading…
Reference in a new issue