add MQTT support plus homeassistant MQTT discovery

This commit is contained in:
DasMoorhuhn 2024-11-10 02:24:25 +01:00
parent 6d3755e465
commit 84cae7fc26
6 changed files with 143 additions and 40 deletions

View File

@ -22,16 +22,15 @@ Python gateway for the [custom firmware](https://github.com/atc1441/ATC_MiThermo
- Make in runnable in a docker container (because only cool people are using docker) - Make in runnable in a docker container (because only cool people are using docker)
- Make docker image smaller. I mean shiiit 1GB D: should be possible to be under 500MB (It's now around 100MB) - Make docker image smaller. I mean shiiit 1GB D: should be possible to be under 500MB (It's now around 100MB)
- Implement a loop for fetching the data every X seconds - Implement a loop for fetching the data every X seconds
- Storing temperature, humidity and battery state as json in a text file
- Can run on Raspberry Pi (3, 4, zero w) or any other Linux driven hardware which has BLE and WiFi support
**TODOs:** **TODOs:**
- [WIP] Can run on Raspberry Pi (3, 4, zero w) or any other Linux driven hardware which has BLE and WiFi support - [WIP] MQTT publishing with discovery for homeassistant
- [WIP] Storing temperature, humidity and battery state as json in a text file
- [TODO] Make a microPython version for using the raspberry pico w or any other microcontroller with BLE and WiFi support - [TODO] Make a microPython version for using the raspberry pico w or any other microcontroller with BLE and WiFi support
- [TODO] Collect data from multiple devices/gateways - [TODO] Collect data from multiple devices/gateways
- [TODO] Command line tool for managing the devices - [TODO] Command line tool for managing the devices
- [TODO] Analyzing tool for making statistics - [TODO] Analyzing tool for making statistics
- [TODO] HomeAssistant integration
- [TODO] MQTT publishing
- [TODO] Maybe... a webinterface. But I suck at web stuff, so I don't know. - [TODO] Maybe... a webinterface. But I suck at web stuff, so I don't know.
- [TODO] Implement other BLE Sensors - [TODO] Implement other BLE Sensors

View File

@ -8,3 +8,5 @@ DEBUG=DEBUG
MODE=1 MODE=1
LOOP=20 LOOP=20
TIMEOUT=20 TIMEOUT=20
MQTT_IP=
MQTT_PORT=1883

View File

@ -7,6 +7,18 @@ from logger import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
def generate_json(device:Device):
dev, data_obj, from_config = device
return {
"timestamp": data_obj.timestamp,
"temperature": data_obj.temperature,
"humidity": data_obj.humidity,
"battery_percent": data_obj.battery_percent,
"battery_volt": data_obj.battery_volt,
"rssi": dev.rssi
}
def log_to_json(devices): def log_to_json(devices):
workdir, filename = os.path.split(os.path.abspath(__file__)) workdir, filename = os.path.split(os.path.abspath(__file__))
@ -29,15 +41,7 @@ def log_to_json(devices):
file.write(json.dumps(new_file)) file.write(json.dumps(new_file))
data = new_file data = new_file
measurements = { data['measurements'].append(generate_json(device))
"timestamp": data_obj.timestamp,
"temperature": data_obj.temperature,
"humidity": data_obj.humidity,
"battery_percent": data_obj.battery_percent,
"battery_volt": data_obj.battery_volt,
"rssi": dev.rssi
}
data['measurements'].append(measurements)
# logger.debug(measurements) # logger.debug(measurements)
@ -48,6 +52,5 @@ def log_to_mongodb(data):
pass pass
def log_to_mqtt(data):
pass

View File

@ -1,13 +1,25 @@
from time import sleep from time import sleep
from log_data import log_to_json from log_data import log_to_json
from mqtt import publish_home_assistant_device_config
from mqtt import publish_device_state
from devices import Device
from data_class import Data
from ble_discovery import start_discovery from ble_discovery import start_discovery
from logger import get_logger from logger import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
def publish_data(devices):
log_to_json(devices)
for device in devices:
publish_home_assistant_device_config(device)
publish_device_state(device)
def start_loop(interval=40, timeout=20): def start_loop(interval=40, timeout=20):
logger.info(f"Starting loop with interval {interval}s") logger.info(f"Starting loop with interval {interval}s")
while True: while True:
devices = start_discovery(timeout=timeout) devices = start_discovery(timeout=timeout)
log_to_json(devices) publish_data(devices)
sleep(interval) sleep(interval)

View File

@ -26,20 +26,17 @@ logger.debug(f"VERSION: {os.getenv('VERSION')}")
update_state = check_for_update() update_state = check_for_update()
print_state(update_state) print_state(update_state)
try: if DOCKER:
if DOCKER: logger.info('Running in Docker')
logger.info('Running in Docker')
try:INTERVAL = int(interval) try:INTERVAL = int(interval)
except:pass except:pass
try:TIMEOUT = int(timeout) try:TIMEOUT = int(timeout)
except:pass except:pass
if interval is None: log_to_json(start_discovery(timeout=TIMEOUT)) if interval is None: log_to_json(start_discovery(timeout=TIMEOUT))
else:start_loop(INTERVAL, TIMEOUT) else:start_loop(INTERVAL, TIMEOUT)
else: else:
start_loop(interval=40) start_loop(interval=40)
except Exception as err:
logger.error(err)

View File

@ -1,15 +1,105 @@
import paho.mqtt.client as MQTT import os
import json
import paho.mqtt.client as mqtt
topic_root = "/atc_mithermometer_gateway" from devices import Device
from data_class import Data
from logger import get_logger
mqtt = MQTT.Client(mqtt.CallbackAPIVersion.VERSION2) topic_root = "homeassistant"
mqtt.connect("192.168.178.140", 1883, 60) logger = get_logger(__name__)
client = mqtt.Client(client_id='atc_mithermometer_gateway')
IP = os.getenv('MQTT_IP')
PORT = os.getenv('MQTT_PORT')
if IP and PORT:
client.connect(IP, int(PORT), 60)
def publish_measurement(mac): def generate_json(device:Device):
topic = f"{topic_root}/measurements/{mac}" dev, data_obj, from_config = device
topic_temp = f"{topic}/temperature" return {
topic_humid = f"{topic}/humidity" "timestamp": data_obj.timestamp,
topic_battery = f"{topic}/battery" "temperature": data_obj.temperature,
"humidity": data_obj.humidity,
"battery_percent": data_obj.battery_percent,
"battery_volt": data_obj.battery_volt,
"rssi": dev.rssi
}
mqtt.publish(topic_temp, )
def generate_config_payloads(device:Device):
mac = '-'.join(device.mac.split(':')[3:])
state_topic = f"atc/device/{mac}/state"
device_json = {
"name": f"ATC {device.name}",
"identifiers":[f"atc-{mac}"],
}
config_temperature_payload = {
"name": f"{device.name} temperature",
"unique_id": f"atc-{mac}-temperature",
"state_topic": state_topic,
"unit_of_measurement": "°C",
"value_template": "{{ value_json.temperature}}",
"device": device_json
}
config_humidity_payload = {
"name": f"{device.name} humidity",
"unique_id": f"atc-{mac}-humidity",
"state_topic": state_topic,
"unit_of_measurement": "%",
"value_template": "{{ value_json.humidity}}",
"device": device_json
}
config_battery_percent_payload = {
"name": f"{device.name} battery",
"unique_id": f"atc-{mac}-battery-percent",
"state_topic": state_topic,
"unit_of_measurement": "%",
"value_template": "{{ value_json.battery_percent}}",
"device": device_json
}
config_rssi_payload = {
"name": f"{device.name} rssi",
"unique_id": f"atc-{mac}-rssi",
"state_topic": state_topic,
"value_template": "{{ value_json.rssi}}",
"device": device_json
}
return [config_temperature_payload, config_humidity_payload, config_battery_percent_payload, config_rssi_payload]
def publish_device_state(device:Device):
dev, data_obj, from_config = device
data_obj: Data
from_config: Device
mac = '-'.join(from_config.mac.split(':')[3:])
state_topic = f"atc/device/{mac}/state"
payload = generate_json(device)
logger.info(f"Publishing {payload}")
client.publish(state_topic, json.dumps(payload))
def publish_home_assistant_device_config(device:Device):
dev, data_obj, from_config = device
data_obj: Data
from_config: Device
mac = '-'.join(from_config.mac.split(':')[3:])
topic = f"{topic_root}/sensor/atc-{mac}/config"
temperature_payload, humidity_payload, battery_percent, rssi_payload = generate_config_payloads(from_config)
client.publish(f'{topic}-temperature/config', json.dumps(temperature_payload))
client.publish(f'{topic}-humidity/config', json.dumps(humidity_payload))
client.publish(f'{topic}-battery/config', json.dumps(battery_percent))
client.publish(f'{topic}-rssi/config', json.dumps(rssi_payload))
# client.publish(f'{topic}-temperature/config', json.dumps({}))
# client.publish(f'{topic}-humidity/config', json.dumps({}))
# client.publish(f'{topic}-battery/config', json.dumps({}))
# client.publish(f'{topic}-rssi/config', json.dumps({}))