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