add MQTT support plus homeassistant MQTT discovery
This commit is contained in:
parent
6d3755e465
commit
84cae7fc26
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -8,3 +8,5 @@ DEBUG=DEBUG
|
|||||||
MODE=1
|
MODE=1
|
||||||
LOOP=20
|
LOOP=20
|
||||||
TIMEOUT=20
|
TIMEOUT=20
|
||||||
|
MQTT_IP=
|
||||||
|
MQTT_PORT=1883
|
||||||
@ -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
|
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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)
|
|
||||||
|
|||||||
@ -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({}))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user