#!/usr/bin/env python3 import asyncio import os import sys from typing import Dict from fritzbox import FritzBox, Device from homeassistant import HomeAssistantAPI import logging import json sensor_mappings = {} thermostate_mappings = {} async def handle_event(idx: int): logging.debug(f"Wait for events for {idx}") while event := await ha.events[idx].get(): entity_id = event["data"]["entity_id"] if entity_id in sensor_mappings.keys() or entity_id in thermostate_mappings.keys(): state = await ha.get_device_state(entity_id) new_state = event["data"]["new_state"] logging.info(f"received changed state from {entity_id}") if entity_id in thermostate_mappings.keys() and state["state"] != "unavailable": therm_temp = new_state["attributes"]["current_temperature"] therm_name = new_state["attributes"]["friendly_name"] sensor = thermostate_mappings[entity_id] sensor_state = await ha.get_device_state(sensor) sensor_temp = round(float(sensor_state["attributes"]["temperature"]) * 2) / 2 if therm_temp != sensor_temp: logging.info(f"{therm_name}: {therm_temp}") logging.info(f"{sensor}: {sensor_state['attributes']['temperature']} ({sensor_temp})") fb.correct_offset(therm_name, sensor_temp) elif entity_id in sensor_mappings.keys(): sensor_temp = round(float(new_state["attributes"]["temperature"]) * 2) / 2 """ fb.login() logged = False """ for thermostate in sensor_mappings[entity_id]: therm_state = await ha.get_device_state(thermostate) if therm_state["state"] == "unavailable": continue therm_temp = float(therm_state["attributes"]["current_temperature"]) therm_name = therm_state["attributes"]["friendly_name"] if therm_temp != sensor_temp: logging.info(f"{therm_name}: {therm_temp}") logging.info(f"{entity_id}: {new_state['attributes']['temperature']} ({sensor_temp})") fb.correct_offset(therm_name, sensor_temp) """ current_temp, current_offset, id, name = fb.get_device_data(name=thermostate) if not logged: logging.info( f"Current measurement from {entity_id}: {new_state['attributes']['temperature']} ({rounded})") logged = True logging.info(f"Current measurement from {thermostate}: {current_temp}") new_offset = current_offset + rounded - current_temp if new_offset != current_offset: old_offset = current_offset logging.debug(f"Set offset for {thermostate} from {current_offset} to {new_offset}") fb.set_offset(current_temp, new_offset, id, name) current_temp, current_offset, id, name = fb.get_device_data(name=thermostate) logging.debug(f"Target: {new_offset} ; Set: {current_offset}") if new_offset == current_offset: logging.info(f"Adjustet offset from {old_offset} to {new_offset}") else: logging.warning(f"Failed to adjust offset from {old_offset} to {new_offset}") fb.logout() """ async def init(ha: HomeAssistantAPI, fb: FritzBox): await ha.connect() logging.debug("Subscribe") state_changed_id = await ha.subscribe_event("state_changed") logging.debug(state_changed_id) asyncio.create_task(handle_event(state_changed_id)) fb.login() await ha.wait_for_close() logging.info("Websocket closed, shutting down..") asyncio.get_running_loop().stop() logging.basicConfig(level=logging.INFO, format="[%(asctime)s] [%(levelname)s] %(message)s") config_path = sys.argv[1] config = json.load(open(config_path)) logging.debug(config) loop = asyncio.get_event_loop() fb = FritzBox(url=config["fritzbox"]["url"], user=config["fritzbox"]["username"], password=config["fritzbox"]["password"], update_timeout=config["update_timeout"], dry_run=False) supervisor_url = "ws://supervisor/core/websocket" ha = HomeAssistantAPI(os.environ["SUPERVISOR_TOKEN"], supervisor_url) for mapping in config["mappings"]: if mapping["sensor"] not in sensor_mappings.keys(): sensor_mappings[mapping["sensor"]] = [] sensor_mappings[mapping["sensor"]].append(mapping["thermostate"]) thermostate_mappings[mapping["thermostate"]] = mapping["sensor"] loop.create_task(init(ha, fb)) try: loop.run_forever() except KeyboardInterrupt: pass