From 25fda235165bc27693b0f6ef699bab1534011f5a Mon Sep 17 00:00:00 2001 From: Vader Date: Sat, 10 Jun 2023 11:12:28 +0200 Subject: [PATCH] MQTT support --- .env_sample | 8 +++++++- README.md | 10 +++++++++ main.py | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + 4 files changed, 71 insertions(+), 1 deletion(-) diff --git a/.env_sample b/.env_sample index 3661206..dd902c1 100644 --- a/.env_sample +++ b/.env_sample @@ -2,4 +2,10 @@ SLEEP_DATA_PATH=/path/to/sleep/data DEBUG=False OWL=False VIDEO_PATH=/path/to/video -HATCH_IP=192.168.HATCH.IP \ No newline at end of file +HATCH_IP=192.168.HATCH.IP +MQTT=True +MQTT_Broker_IP=IP_OF_MQTT_BROKER +MQTT_Broker_Port=1883 +MQTT_Client_ID=babysleepcoach +MQTT_Broker_Username= +MQTT_Broker_Password= \ No newline at end of file diff --git a/README.md b/README.md index 49a38c7..16eb8b0 100644 --- a/README.md +++ b/README.md @@ -63,3 +63,13 @@ And you'll probably get a warning about the app trying to boot on port `80`. You You'll need to update some paths and IPs in the code.

## Someone send me proof you got it all running. + +

+### Home automation / MQTT +Baby Sleep Coach can also be integrated into home automations like OpenHAB, Home Assistant or ioBroker. For this purpose, there is an MQTT topic + +`homeassistant/binary_sensor/baby_sleep_coach/is_awake/state` + +which can be used to query the awake status of the baby. This can then be used, for example, to turn off the doorbell when the baby is asleep. + +MQTT auto discovery from OpenHAB and Home Assistant is also supported. diff --git a/main.py b/main.py index 2a6be97..a34b8d5 100644 --- a/main.py +++ b/main.py @@ -14,9 +14,18 @@ # from cast_service import CastSoundService from http.server import HTTPServer, SimpleHTTPRequestHandler from helpers import check_eyes_open, set_hatch, check_mouth_open, maintain_aspect_ratio_resize, gamma_correction +from paho.mqtt import client as mqtt_client +import json load_dotenv() + +broker = str(os.getenv("MQTT_Broker_IP")) +port = int(os.getenv("MQTT_Broker_Port")) +client_id = str(os.getenv("MQTT_Client_ID")) +username = str(os.getenv("MQTT_Broker_Username", default="")) +password = str(os.getenv("MQTT_Broker_Password", default="")) + # Uncomment if want phone notifications during daytime wakings. # Configuration of telegram API key in this dir also needed. # import telegram_send @@ -64,6 +73,7 @@ def __init__(self): self.multi_face_landmarks = [] self.is_awake = False + self.is_awake_before = not self.is_awake self.ser = None # serial connection to arduino for controlling demon owl # If demon owl mode, setup connection to arduino and cast service for playing audio @@ -519,6 +529,9 @@ def live(self, consumer_q): except Exception as e: print("Something went wrong: ", e) + if os.getenv("MQTT", 'False').lower() in ('true', '1'): + publish(client, self.is_awake, self.is_awake_before) + self.is_awake_before = self.is_awake #################################### # TODO: move out of this file, break it up @@ -561,6 +574,46 @@ def receive(producer_q): producer_q.append(img) +def connect_mqtt(): + def on_connect(client, userdata, flags, rc): + if rc == 0: + print("Connected to MQTT Broker!") + else: + print("Failed to connect, return code %d\n", rc) + + client = mqtt_client.Client(client_id) + if username != "" and password != "": + client.username_pw_set(username, password) + client.on_connect = on_connect + client.connect(broker, port) + return client + +def publish(client, is_awake, is_awake_before): + if is_awake_before != is_awake: + if is_awake: + msg = "ON" + else: + msg = "OFF" + stateTopic = "homeassistant/binary_sensor/baby_sleep_coach/is_awake/state" + discoveryTopic = "homeassistant/binary_sensor/baby_sleep_coach/is_awake/config" + discoveryPayload = {"unique_id": "baby_sleep_coach_awake", "name":"Baby Sleep Coach: Baby awake", "state_topic": stateTopic, "state_on": "ON", "state_off": "OFF"} + + result = client.publish(discoveryTopic, payload=json.dumps(discoveryPayload), retain=True) + + result = client.publish(stateTopic, payload=msg, retain=True) + # result: [0, 1] + status = result[0] + if status == 0: + print(f"Send `{msg}` to topic `{stateTopic}`") + else: + print(f"Failed to send message to topic {stateTopic}") + + + +if os.getenv("MQTT", 'False').lower() in ('true', '1'): + client = connect_mqtt() + client.loop_start() + # Had to split frame receive and processing into different threads due to underlying FFMPEG issue. Read more here: # https://stackoverflow.com/questions/49233433/opencv-read-errorh264-0x8f915e0-error-while-decoding-mb-53-20-bytestream # Current solution is to insert into deque on the thread receiving images, and process on the other diff --git a/requirements.txt b/requirements.txt index 1678df8..033b5ea 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,4 @@ python-dotenv==0.21.1 scikit_learn==1.2.1 statsmodels==0.13.5 tbats==1.1.2 +paho-mqtt==1.6.1