aboutsummaryrefslogtreecommitdiffstats
path: root/mqtt-client
diff options
context:
space:
mode:
Diffstat (limited to 'mqtt-client')
-rw-r--r--mqtt-client/Dockerfile6
-rw-r--r--mqtt-client/mqtt-client.py164
m---------mqtt-client/omada-api0
-rw-r--r--mqtt-client/requirements.txt2
4 files changed, 161 insertions, 11 deletions
diff --git a/mqtt-client/Dockerfile b/mqtt-client/Dockerfile
index ad59f9b..be304a1 100644
--- a/mqtt-client/Dockerfile
+++ b/mqtt-client/Dockerfile
@@ -1,10 +1,12 @@
-FROM debian:11-slim
+FROM debian:12-slim
ENV TZ=Europe/London
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update -y
RUN apt-get install -y python3-pip iputils-ping
COPY . /app
WORKDIR /app
-RUN pip3 install -r requirements.txt
+RUN pip3 install --break-system-packages -r requirements.txt
+RUN pip3 install --break-system-packages docker
+RUN pip3 install --break-system-packages -r TasmotaCLI/requirements.txt
ENTRYPOINT ["python3"]
CMD ["mqtt-client.py"]
diff --git a/mqtt-client/mqtt-client.py b/mqtt-client/mqtt-client.py
index 18dcd82..363e57e 100644
--- a/mqtt-client/mqtt-client.py
+++ b/mqtt-client/mqtt-client.py
@@ -1,7 +1,12 @@
import paho.mqtt.client as paho
from influxdb_client import InfluxDBClient, Point, WritePrecision
from influxdb_client.client.write_api import SYNCHRONOUS
+import prometheus_client
+import urllib.parse
import threading
+import datetime
+import requests
+import asyncio
import time
import json
import sys
@@ -9,6 +14,10 @@ import os
sys.path.insert(1, os.path.join(os.path.dirname(__file__), "TasmotaCLI"))
import tasmotaMQTTClient
+import tasmotaHTTPClient
+
+sys.path.insert(2, os.path.join(os.path.dirname(__file__), "omada-api"))
+from omada import Omada
class MQTTClient:
def __init__(self, mqtt_client_name = "reg.reaweb.uk/mqtt-client", loop_forever = True):
@@ -18,6 +27,44 @@ class MQTTClient:
org = os.environ["DOCKER_INFLUXDB_INIT_ORG"]
)
self.influxc.ping()
+ self.tasmota_power_prom = prometheus_client.Gauge(
+ "tasmota_power",
+ "Power metrics as reported by Tasmota-flashed plugs",
+ labelnames = ["plug", "field"]
+ )
+ self.humidity_prom = prometheus_client.Gauge(
+ "humidity",
+ "Humidity as reported by a zigbee device over MQTT",
+ labelnames = ["location"]
+ )
+ self.temperature_prom = prometheus_client.Gauge(
+ "temperature",
+ "Temperature as reported by a zigbee device over MQTT",
+ labelnames = ["location"]
+ )
+ self.doorsensor_prom = prometheus_client.Enum(
+ "door_sensor",
+ "Door sensor state change as reported by zigbee door sensor over MQTT",
+ states = ["opened", "closed"],
+ labelnames = ["location"]
+ )
+ self.door_opened_counter = prometheus_client.Counter(
+ "door_opened",
+ "Door sensor opened as reported by zigbee door sensor over MQTT",
+ labelnames = ["location"]
+ )
+
+ self.zigbee_plugs = {"MikroTikZigbeePlug"}
+ self.zigbee_to_tasmota_transformations = {
+ "RMSVoltage": ("Voltage", int),
+ "ActivePower": ("Power", int),
+ "RMSCurrent": ("Current", float)
+ }
+ self.sonoff_scene_buttons = {
+ "BedroomLightsButtons": "192.168.5.17"
+ }
+
+ # print(self.send_raw_tasmota_http("192.168.5.6", os.environ["MQTT_PASSWD"], "Power"))
self.mqttc = paho.Client(mqtt_client_name, clean_session = True)
if loop_forever:
@@ -49,38 +96,109 @@ class MQTTClient:
elif type_ == "TasmotaZigbee":
self.handle_zigbee(msg_j)
+ def handle_zigbee_plug(self, friendlyname, fields):
+ fields2 = {}
+ for k, v in fields.items():
+ if k in self.zigbee_to_tasmota_transformations.keys():
+ fields2[self.zigbee_to_tasmota_transformations[k][0]] = self.zigbee_to_tasmota_transformations[k][1](v)
+
+ print("Logged the following information for the zigbee plug '%s' as a tasmota plug: %s" % (friendlyname, str(fields2)))
+ self.append_influxdb(fields2, "tasmota_power", {"plug": friendlyname})
+
+ for k, v in fields2.items():
+ self.tasmota_power_prom.labels(plug = friendlyname, field = k).set(v)
+
def handle_plug(self, msg_j, location):
print("'%s' is using %.1fw @ %s. %.1fkWh so far today, %.1fkWh yesterday" % (location, msg_j["ENERGY"]["Power"], msg_j["Time"], msg_j["ENERGY"]["Today"], msg_j["ENERGY"]["Yesterday"]))
fields = {k: v for k, v in msg_j["ENERGY"].items() if k not in {"TotalStartTime"}}
self.append_influxdb(fields, "tasmota_power", {"plug": location})
+ for k, v in fields.items():
+ self.tasmota_power_prom.labels(plug = location, field = k).set(v)
+
def handle_zigbee(self, msg_j):
- def toggle_geoffery():
+ def toggle_firestick(status_before):
print("Starting thread...")
- tasmotaMQTTClient.MQTTClient(MQTT_HOST, "TasmotaGeoffery", os.environ["MQTT_USER"], os.environ["MQTT_PASSWD"], "OFF")
- print("Waiting...")
- time.sleep(8)
- tasmotaMQTTClient.MQTTClient(MQTT_HOST, "TasmotaGeoffery", os.environ["MQTT_USER"], os.environ["MQTT_PASSWD"], "ON")
+
+ if status_before == "OFF":
+ print("TV was formerly off, so its being turned on, so we're going to turn the firestick on.")
+ set_omada_poe("Firestick", 0)
+ print("Waiting...")
+ time.sleep(6)
+ set_omada_poe("Firestick", 1)
+ print("Turned firestick on.")
+ else:
+ print("TV was formerly on, so its being turned off, so we're going to turn the firestick off.")
+ set_omada_poe("Firestick", 1)
+ print("Waiting...")
+ time.sleep(6)
+ set_omada_poe("Firestick", 0)
+ print("Turned firestick off.")
zigbee_id = list(msg_j["ZbReceived"].keys())[0]
fields = msg_j["ZbReceived"][zigbee_id]
friendlyname = fields.pop("Name")
del fields["Device"]
print("Zigbee device '%s' reported: %s" % (friendlyname, str(fields)))
- if "Read" not in fields.keys():
- self.append_influxdb(fields, "zigbee", {"friendlyname": friendlyname, "id": zigbee_id})
- if zigbee_id == "0x7327" and friendlyname == "TVButton" and "Power" in fields.keys():
+ if friendlyname in self.zigbee_plugs:
+ self.handle_zigbee_plug(friendlyname, fields)
+
+ if friendlyname in self.sonoff_scene_buttons.keys() and "FC12/0000" in fields.keys():
+ self.handle_sonoff_scene_button(friendlyname = friendlyname, timespressed = fields["FC12/0000"], button = fields["Endpoint"])
+
+ if friendlyname == "DoorSensor":
+ with open(os.path.join(os.path.dirname(__file__), "door_log.csv"), "a") as f:
+ f.write("%s,%s,%s\n" % (datetime.datetime.now().astimezone().isoformat(), friendlyname, ",".join(sorted(["%s=%s" % (k, v) for k, v in fields.items()]))))
+
+ if zigbee_id == "0x0A05" and friendlyname == "TVButton2" and "Power" in fields.keys():
if fields["Power"] == 2:
print("TV Zigbee button pressed, toggling TasmotaTV Tasmota Plug")
+ status_before = self.get_http_power_status("192.168.5.6", os.environ["MQTT_PASSWD"])
self.toggle_plug("TasmotaTV")
- threading.Thread(target = toggle_geoffery, args = ()).start()
+ threading.Thread(target = toggle_firestick, args = (status_before, )).start()
+ # toggle_firestick(status_before)
if zigbee_id == "0x74B3" and friendlyname == "HarveyButton" and "Power" in fields.keys():
if fields["Power"] == 2:
print("Harvey's button pressed, toggling TasmotaHarveyPC Plug")
self.toggle_plug("TasmotaHarveyPC")
+ if "Humidity" in fields.keys():
+ fields["Humidity"] = float(fields["Humidity"])
+ self.humidity_prom.labels(location = friendlyname).set(fields["Humidity"])
+ elif "Temperature" in fields.keys():
+ fields["Temperature"] = float(fields["Temperature"])
+ self.temperature_prom.labels(location = friendlyname).set(fields["Temperature"])
+ elif "ZoneStatus" in fields.keys() and "Contact" in fields.keys():
+ if fields["ZoneStatusChange"] == 1 and fields["ZoneStatusChangeZone"] == 1:
+ self.doorsensor_prom.labels(location = friendlyname).state("opened")
+ self.door_opened_counter.labels(location = friendlyname).inc()
+ elif fields["ZoneStatusChange"] == 0 and fields["ZoneStatusChangeZone"] == 1:
+ self.doorsensor_prom.labels(location = friendlyname).state("closed")
+
+ if "Read" not in fields.keys():
+ self.append_influxdb(fields, "zigbee", {"friendlyname": friendlyname, "id": zigbee_id})
+
+ def handle_sonoff_scene_button(self, friendlyname, timespressed, button):
+ url_base = "http://%s/cm?cmnd=" % self.sonoff_scene_buttons[friendlyname]
+
+ if timespressed == 1:
+ if button == 1:
+ req = requests.get(url_base + urllib.parse.quote_plus("Color2 -"))
+ elif button == 2:
+ req = requests.get(url_base + urllib.parse.quote_plus("Color2 +"))
+ elif button == 3:
+ req = requests.get(url_base + urllib.parse.quote_plus("Dimmer2 -"))
+ elif button == 4:
+ req = requests.get(url_base + urllib.parse.quote_plus("Dimmer2 +"))
+
+ response = req.json()
+ print("%s responded with %s" % (self.sonoff_scene_buttons[friendlyname], json.dumps(response)))
+ if "Dimmer" in response.keys():
+ if response["Dimmer"] == 1:
+ req = requests.get(url_base + urllib.parse.quote_plus("Power OFF"))
+
def set_plug(self, friendlyname, payload):
t = "cmnd/TasmotaPlug/%s/Power" % friendlyname
self.mqttc.publish(t, payload = payload)
@@ -89,6 +207,17 @@ class MQTTClient:
def toggle_plug(self, friendlyname):
self.set_plug(friendlyname, "TOGGLE")
+ def send_raw_tasmota_http(self, host, password, command):
+ req = requests.get("http://%s/cm" % host, params = {
+ "cmnd": str(command),
+ "user": "admin",
+ "password": password
+ })
+ return req.json()
+
+ def get_http_power_status(self, host, password):
+ return self.send_raw_tasmota_http(host, password, "Power")["POWER"]
+
def append_influxdb(self, fields, measurement_name, tags):
points = [{"measurement": measurement_name, "tags": tags, "fields": fields}]
write_api = self.influxc.write_api(write_options = SYNCHRONOUS)
@@ -99,6 +228,20 @@ class MQTTClient:
write_precision = WritePrecision.S
)
+def set_omada_poe(profile, status):
+ o = Omada(os.path.join(os.path.dirname(__file__), "omada.cfg"))
+ o.login()
+
+ profileId = o.getProfileId(profile)
+ settings = o.getProfileSettings(profileId)
+ settings['poe'] = status
+ o.setProfileSettings(profileId, settings)
+
+ settings = o.getProfileSettings(profileId)
+
+ print("Sent POE setting '%d' to profile '%s'" % (settings["poe"], settings["name"]))
+ o.logout()
+
if __name__ == "__main__":
env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "config.env")
if os.path.exists(env_path):
@@ -106,8 +249,11 @@ if __name__ == "__main__":
dotenv.load_dotenv(dotenv_path = env_path)
INFLUXDB_HOST = "dns.athome"
MQTT_HOST = "dns.athome"
+ PROM_HOST = "dns.athome"
else:
INFLUXDB_HOST = "influxdb"
MQTT_HOST = "mqtt"
+ PROM_HOST = "prometheus"
+ prometheus_client.start_http_server(8000)
mqtt_client = MQTTClient()
diff --git a/mqtt-client/omada-api b/mqtt-client/omada-api
new file mode 160000
+Subproject b169929137ec8647bf520067aa2e75e61839a9a
diff --git a/mqtt-client/requirements.txt b/mqtt-client/requirements.txt
index ac151c7..6566b93 100644
--- a/mqtt-client/requirements.txt
+++ b/mqtt-client/requirements.txt
@@ -1,3 +1,5 @@
paho-mqtt==1.6.1
python-dotenv
influxdb-client
+prometheus-client
+requests