From 5e4f375f71d5b9d05b045204dc87be93eae66b98 Mon Sep 17 00:00:00 2001 From: James Andariese Date: Mon, 24 Jul 2023 15:33:13 -0500 Subject: [PATCH] initial import --- .gitignore | 5 +++ Dockerfile | 14 ++++++ build.sh | 4 ++ grab.py | 110 +++++++++++++++++++++++++++++++++++++++++++++++ health.sh | 12 ++++++ requirements.txt | 14 ++++++ test.env.sample | 9 ++++ test.sh | 4 ++ 8 files changed, 172 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100755 build.sh create mode 100644 grab.py create mode 100755 health.sh create mode 100644 requirements.txt create mode 100644 test.env.sample create mode 100755 test.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..83a4713 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +test.env +*~ +\#*# +.* +!.gitignore diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ceba7dc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +FROM alpine:latest + +RUN apk add python3 py3-pip curl mosquitto-clients + +COPY requirements.txt /requirements.txt +RUN pip install -r /requirements.txt +COPY grab.py /grab.py + +WORKDIR / + +CMD ["python3", "/grab.py"] + +COPY health.sh /health.sh +HEALTHCHECK CMD /health.sh diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..bdec22d --- /dev/null +++ b/build.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +docker build -t jamesandariese/vncmqtt:latest . +docker push jamesandariese/vncmqtt:latest diff --git a/grab.py b/grab.py new file mode 100644 index 0000000..bb54768 --- /dev/null +++ b/grab.py @@ -0,0 +1,110 @@ +import base64 +import hashlib +import logging +import json +import os +import sys +import time + +from vncdotool import api +from twisted.internet import reactor +import paho.mqtt.client as paho + +# LOGGING + +# setup logging to console +class SkipLevelsFilter(logging.Filter): + def __init__(self, skip_levels=[]): + self.skip_levels = skip_levels + + def filter(self, rec): + return rec.levelno not in self.skip_levels + + +logger = logging.getLogger('keywords_pipeline') +formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + +# stderr for >=ERROR +stderr_handler = logging.StreamHandler(sys.stderr) +stderr_handler.setLevel(logging.ERROR) +stderr_handler.setFormatter(formatter) + +# stdout for everything but ERROR. +stdout_handler = logging.StreamHandler(sys.stdout) +stdout_handler.addFilter(SkipLevelsFilter([logging.ERROR])) +stdout_handler.setLevel(logging.DEBUG) +stdout_handler.setFormatter(formatter) + +# attach the handlers and set the default level to DEBUG for the remainder of the logger setup +logger.addHandler(stdout_handler) +logger.addHandler(stderr_handler) +logger.setLevel(logging.DEBUG) + +# now extract the desired loglevel or use INFO if not specified, DEBUG is still the _active_ level though +# so if anything fails here, it will be logged even if it's a debug message +LOGLEVEL = os.getenv('LOGLEVEL') or "INFO" +numeric_level = getattr(logging, LOGLEVEL.upper(), None) +if not isinstance(numeric_level, int): + logger.critical('Invalid log level: %s' % LOGLEVEL) + os.exit(78) +# here's the debug message which will be logged before changing the level. +logger.debug(f'setting loglevel to {LOGLEVEL}') +logger.setLevel(numeric_level) + +# END LOGGING + +MQTT_USER=os.environ['MQTT_USER'] +MQTT_PASSWORD=os.environ['MQTT_PASSWORD'] +MQTT_HOST=os.environ['MQTT_HOST'] +VNC_HOST=os.environ['VNC_HOST'] +VNC_PASSWORD=os.environ['VNC_PASSWORD'] +MQTT_TOPIC=os.environ['MQTT_TOPIC'] +DEVICE_NAME=os.environ.get('DEVICE_NAME', None) +DEVICE_ID=os.environ.get('DEVICE_ID', None) +INTERVAL=float(os.environ.get('INTERVAL', '5')) + +SANITIZED_HOST=VNC_HOST.replace('.','_') +NAME=DEVICE_NAME or SANITIZED_HOST +DEVICE_ID = DEVICE_ID or SANITIZED_HOST +OBJECT_ID = base64.b32encode(hashlib.sha1(NAME.encode('utf-8')).digest()).decode('utf-8') + +mqtt = paho.Client() +mqtt.username_pw_set(MQTT_USER, MQTT_PASSWORD) +mqtt.connect(MQTT_HOST) +mqtt.loop_start() + +mqtt.publish(f"homeassistant/camera/{DEVICE_ID}/config", json.dumps({ + "name": NAME, + "topic": MQTT_TOPIC + "/image", + "unique_id": DEVICE_ID+"-T", + "device_class": "camera", + "device": { + "identifiers": DEVICE_ID, + "manufacturer": "VNC-MQTT", + "model": "v0.1", + }, +}), retain=True) + +while True: + t = time.time() + with api.connect(VNC_HOST, VNC_PASSWORD) as vnc: + logger.debug("refreshing for capture") + vnc.refreshScreen() + logger.debug("capturing") + vnc.captureScreen('capture.png') + logger.debug("publishing to mqtt") + with open('capture.png', 'rb') as f: + capture = f.read() + with open('capture.time', 'wb') as f: + pass # touch :D + + p = mqtt.publish(topic=MQTT_TOPIC + "/image", payload=capture) + time_left = INTERVAL - (time.time() - t) + if time_left > 0: + logger.debug(f"sleeping for {time_left}s") + time.sleep(time_left) + else: + logger.warning(f"interval missed ({time_left}s overdue)") + +mqtt.loop_end() +reactor.stop() diff --git a/health.sh b/health.sh new file mode 100755 index 0000000..c2aadfb --- /dev/null +++ b/health.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +DT=$(date +%s) +CT=$(stat -c %Z capture.time) + +if [ $(( DT - CT )) -gt $INTERVAL ];then + echo bad + false +else + echo good + true +fi diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..17d7728 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,14 @@ +attrs==23.1.0 +Automat==22.10.0 +constantly==15.1.0 +hyperlink==21.0.0 +idna==3.4 +incremental==22.10.0 +paho-mqtt==1.6.1 +Pillow==10.0.0 +pycryptodomex==3.18.0 +six==1.16.0 +Twisted==22.10.0 +typing_extensions==4.7.1 +vncdotool==1.2.0 +zope.interface==6.0 diff --git a/test.env.sample b/test.env.sample new file mode 100644 index 0000000..1ebf6d3 --- /dev/null +++ b/test.env.sample @@ -0,0 +1,9 @@ +DEVICE_NAME="Testly Desktop" +LOGLEVEL=debug +MQTT_TOPIC=cameras/testly-desktop +MQTT_HOST=192.168.1.3 +MQTT_USER=write +MQTT_PASSWORD=mqttpassword +VNC_HOST=192.168.1.185 +VNC_PASSWORD=vncpassword +INTERVAL=5 diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..2267dda --- /dev/null +++ b/test.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +bash build.sh +docker run --rm -ti --name vncmqtt-test --env-file test.env jamesandariese/vncmqtt:latest