ha integration

This commit is contained in:
DasMoorhuhn 2024-09-12 03:21:40 +02:00
parent 932168125c
commit ed44da8427
6 changed files with 133 additions and 28 deletions

BIN
.media/ha_integration.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -38,6 +38,7 @@ Python gateway for the [custom firmware](https://github.com/atc1441/ATC_MiThermo
**Current State** **Current State**
![](.media/demo.gif) ![](.media/demo.gif)
![](.media/ha_integration.pngatc)
## Getting started ## Getting started

View File

@ -0,0 +1,66 @@
import json
from requests import api
from dataclasses import dataclass
@dataclass
class EntityState:
timestamp: str
name:str
room:str
temperature:float
humidity:int
battery_percent:int
battery_volt:float
rssi:int
def test_api(gateway):
request = f'http://{gateway}:8000/api'
response = api.get(request)
if not response.ok: return False
response_json = json.loads(response.text)
version = response_json['version']['version']
return True
def get_state(gateway, device) -> EntityState | None:
request = f'http://{gateway}:8000/api/state/{device}'
response = api.get(request)
if not response.ok: return None
response_json = json.loads(response.text)
return EntityState(response_json['timestamp'],
response_json['name'],
response_json['room'],
response_json['temperature'],
response_json['humidity'],
response_json['battery_percent'],
response_json['battery_volt'],
response_json['rssi'])
def get_deices(gateway) -> list | None:
request = f'http://{gateway}:8000/api'
response = api.get(request)
if not response.ok: return None
response_json = json.loads(response.text)
return response_json['info']['devices']
def get_device(gateway, device) -> str | None:
devices = get_deices(gateway)
if device in devices:
index = devices.index(device)
return devices[index]
return None
def get_entity_state(entity_mac, gateway):
device = get_device(gateway, entity_mac)
if device is None: return None
entity_state = get_state(gateway, device)
return entity_state

View File

@ -3,21 +3,23 @@ from __future__ import annotations
import logging import logging
from .discover_gateways import start_discovery_client from api import *
import voluptuous as vol import voluptuous as vol
from pprint import pformat
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.components.sensor import PLATFORM_SCHEMA from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.components.sensor import SensorEntity from homeassistant.components.sensor import SensorEntity
from homeassistant.const import CONF_NAME, CONF_MAC from homeassistant.const import CONF_NAME, CONF_HOST
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.typing import DiscoveryInfoType from homeassistant.helpers.typing import DiscoveryInfoType
_LOGGER = logging.getLogger("atc_mi_thermometer_gateway") _LOGGER = logging.getLogger("atc_mi_thermometer_gateway")
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Optional(CONF_NAME): cv.string,
vol.Required(CONF_HOST): cv.string,
})
def setup_platform( def setup_platform(
@ -26,26 +28,55 @@ def setup_platform(
add_entities: AddEntitiesCallback, add_entities: AddEntitiesCallback,
discovery_info: DiscoveryInfoType | None = None): discovery_info: DiscoveryInfoType | None = None):
_LOGGER.info(pformat(config)) devices = get_deices(gateway=config[CONF_HOST])
gateway = start_discovery_client() for device in devices:
device_state = get_state(gateway=config[CONF_HOST], device=device)
add_entities([Gateway(ip=gateway[0])]) add_entities([MiThermometer(mac=device, state=device_state)])
class Gateway(SensorEntity): class MiThermometer(SensorEntity):
def __init__(self, ip:str): def __init__(self, mac:str, state:EntityState):
self._ip = ip self._mac = mac
self._online = False self._online = False
self._discovered_devices = [] self._last_update = ""
self._temperature = state.temperature
self._humidity = state.temperature
self._battery_percentage = state.battery_percent
self._rssi = state.rssi
self._name = state.name
self._room = state.room
@property @property
def ip(self): def mac(self):
return self._ip return self._mac
@property @property
def online(self): def online(self):
return self._online return self._online
@property @property
def discovered_devices(self): def get_name(self):
return self._discovered_devices return self._name
@property
def get_room(self):
return self._room
@property
def get_temperature(self):
return self._temperature
@property
def get_humidity(self):
return self._humidity
@property
def get_battery_percentage(self):
return self._battery_percentage
@property
def get_rssi(self):
return self._rssi
def update(self) -> None:
pass

View File

@ -24,11 +24,8 @@ class API:
@self.app.route('/api') @self.app.route('/api')
@cross_origin() @cross_origin()
def serve_root(): def serve_root():
workdir, filename = os.path.split(os.path.abspath(__file__))
update = check_for_update() update = check_for_update()
files = os.listdir(f'{workdir}/data')
for file in files:
if not file.endswith('.json'): files.remove(file)
root_dict = { root_dict = {
"version": { "version": {
"version": os.getenv('VERSION'), "version": os.getenv('VERSION'),
@ -40,7 +37,7 @@ class API:
"name": os.getenv('NAME'), "name": os.getenv('NAME'),
"info": { "info": {
"files_size_sum": self.get_file_size(), "files_size_sum": self.get_file_size(),
"files": files "devices": self.get_files()
} }
} }
return jsonify(root_dict) return jsonify(root_dict)
@ -49,14 +46,15 @@ class API:
@cross_origin() @cross_origin()
def serve_json(path): def serve_json(path):
workdir, filename = os.path.split(os.path.abspath(__file__)) workdir, filename = os.path.split(os.path.abspath(__file__))
path += '.json'
return send_from_directory(f'{workdir}/data', path) return send_from_directory(f'{workdir}/data', path)
@self.app.route('/api/state/<path:path>') @self.app.route('/api/state/<path:path>')
@cross_origin() @cross_origin()
def serve_entity_state(path): def serve_entity_state(path):
workdir, filename = os.path.split(os.path.abspath(__file__)) workdir, filename = os.path.split(os.path.abspath(__file__))
path += '.json'
state = json.load(open(f'{workdir}/data/{path}', mode='r')) state = json.load(open(f'{workdir}/data/{path}', mode='r'))
print(state['measurements'])
entity_state = LogEntry(data=state) entity_state = LogEntry(data=state)
return jsonify(entity_state.to_json()) return jsonify(entity_state.to_json())
@ -74,6 +72,15 @@ class API:
if file.endswith('.json'): sizes += os.path.getsize(f'{workdir}/data/{file}') if file.endswith('.json'): sizes += os.path.getsize(f'{workdir}/data/{file}')
return sizes return sizes
def get_files(self):
workdir, filename = os.path.split(os.path.abspath(__file__))
files = os.listdir(f'{workdir}/data')
files_list = []
for file in files:
if file.endswith('.json'):
files_list.append(file.replace('.json', ''))
return files_list
api = API() api = API()
api.app.run(host='0.0.0.0', port=8000) api.app.run(host='0.0.0.0', port=8000)

View File

@ -43,12 +43,12 @@ class LogEntry:
def __init__(self, data): def __init__(self, data):
self.name = data['name'] self.name = data['name']
self.room = data['room'] self.room = data['room']
self.timestamp = data['measurements'][:-1]['timestamp'] self.timestamp = data['measurements'][-1]['timestamp']
self.temperature = data['measurements'][:-1]['temperature'] self.temperature = data['measurements'][-1]['temperature']
self.humidity = data['measurements'][:-1]['humidity'] self.humidity = data['measurements'][-1]['humidity']
self.battery_percent = data['measurements'][:-1]['battery_percent'] self.battery_percent = data['measurements'][-1]['battery_percent']
self.battery_volt = data['measurements'][:-1]['battery_volt'] self.battery_volt = data['measurements'][-1]['battery_volt']
self.rssi = data['measurements'][:-1]['rssi'] self.rssi = data['measurements'][-1]['rssi']
def to_json(self): def to_json(self):
return { return {