From 01a9fba1670c17f67afc38a44d07d5d65af7cb92 Mon Sep 17 00:00:00 2001 From: Patrick Vorgers Date: Sun, 25 Aug 2024 17:59:48 +0000 Subject: [PATCH 1/3] Logfile flooding with warnings: "Could not find hp2 of hp.temperatureWaterOut" #86 Ignore hp2 warnings for single quatt installations --- custom_components/quatt/coordinator.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/custom_components/quatt/coordinator.py b/custom_components/quatt/coordinator.py index 23201b5..25a0a73 100644 --- a/custom_components/quatt/coordinator.py +++ b/custom_components/quatt/coordinator.py @@ -1,4 +1,5 @@ """DataUpdateCoordinator for quatt.""" + from __future__ import annotations from datetime import timedelta @@ -248,8 +249,10 @@ def getValue(self, value_path: str, default: float = None): method = getattr(self, key) return method(parent_key) elif key not in value: - LOGGER.warning("Could not find %s of %s", key, value_path) - LOGGER.debug("in %s", value) + """Ignore any warnings about hp2 - for single quatt installations it is valid that hp2 does not exist.""" + if key != "hp2": + LOGGER.warning("Could not find %s of %s", key, value_path) + LOGGER.debug("in %s", value) return default value = value[key] parent_key = key From 8185de2374e22cf9d4a70e5cb4c1de70cefd8171 Mon Sep 17 00:00:00 2001 From: Patrick Vorgers Date: Wed, 28 Aug 2024 20:28:50 +0000 Subject: [PATCH 2/3] Make the update_interval configurable #54 --- custom_components/quatt/__init__.py | 44 +++++++++--- custom_components/quatt/config_flow.py | 70 +++++++++++++++++--- custom_components/quatt/const.py | 14 ++-- custom_components/quatt/coordinator.py | 37 ++++++----- custom_components/quatt/manifest.json | 2 + custom_components/quatt/strings.json | 24 ++++++- custom_components/quatt/translations/en.json | 26 ++++++-- custom_components/quatt/translations/nl.json | 26 ++++++-- custom_components/quatt/translations/pt.json | 26 ++++++-- 9 files changed, 217 insertions(+), 52 deletions(-) diff --git a/custom_components/quatt/__init__.py b/custom_components/quatt/__init__.py index d76dc97..3c1c90a 100644 --- a/custom_components/quatt/__init__.py +++ b/custom_components/quatt/__init__.py @@ -6,17 +6,17 @@ from __future__ import annotations from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_IP_ADDRESS, Platform +from homeassistant.const import CONF_IP_ADDRESS, CONF_SCAN_INTERVAL, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession from .api import QuattApiClient -from .const import DOMAIN +from .const import CONF_POWER_SENSOR, DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER from .coordinator import QuattDataUpdateCoordinator PLATFORMS: list[Platform] = [ - Platform.SENSOR, Platform.BINARY_SENSOR, + Platform.SENSOR, ] @@ -26,6 +26,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = coordinator = QuattDataUpdateCoordinator( hass=hass, + update_interval=entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL), client=QuattApiClient( ip_address=entry.data[CONF_IP_ADDRESS], session=async_get_clientsession(hass), @@ -35,7 +36,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await coordinator.async_config_entry_first_refresh() await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - entry.async_on_unload(entry.add_update_listener(async_reload_entry)) + # On update of the options reload the entry which reloads the coordinator + entry.async_on_unload(entry.add_update_listener(update_listener)) return True @@ -47,7 +49,33 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unloaded -async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Reload config entry.""" - await async_unload_entry(hass, entry) - await async_setup_entry(hass, entry) +async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Update listener.""" + await hass.config_entries.async_reload(entry.entry_id) + + +async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Migrate old entry.""" + + if config_entry.version == 1: + # Migrate CONF_POWER_SENSOR from data to options + # Set the unique_id of the cic + LOGGER.debug("Migrating config entry from version '%s'", config_entry.version) + + new_data = {**config_entry.data} + new_options = {**config_entry.options} + + if CONF_POWER_SENSOR in new_data: + new_options[CONF_POWER_SENSOR] = new_data.pop(CONF_POWER_SENSOR) + + # Update the version to 2 + config_entry.version = 2 + + hass.config_entries.async_update_entry( + config_entry, + data=new_data, # Updated data without CONF_POWER_SENSOR + options=new_options, # Updated options with CONF_POWER_SENSOR + version=config_entry.version + ) + + return True diff --git a/custom_components/quatt/config_flow.py b/custom_components/quatt/config_flow.py index 4531cb5..b9b9d68 100644 --- a/custom_components/quatt/config_flow.py +++ b/custom_components/quatt/config_flow.py @@ -1,10 +1,14 @@ """Adds config flow for Quatt.""" + from __future__ import annotations import voluptuous as vol + from homeassistant import config_entries from homeassistant.components.sensor import SensorDeviceClass -from homeassistant.const import CONF_IP_ADDRESS +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_IP_ADDRESS, CONF_SCAN_INTERVAL +from homeassistant.core import callback from homeassistant.helpers import selector from homeassistant.helpers.aiohttp_client import async_create_clientsession @@ -15,16 +19,19 @@ QuattApiClientError, ) from .const import ( + CONF_POWER_SENSOR, + DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER, - CONF_POWER_SENSOR, + MAX_SCAN_INTERVAL, + MIN_SCAN_INTERVAL, ) class QuattFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for Quatt.""" - VERSION = 1 + VERSION = 2 async def async_step_user( self, @@ -47,6 +54,11 @@ async def async_step_user( LOGGER.exception(exception) _errors["base"] = "unknown" else: + # Check if this cic has already been configured + # Pre-version 2 config flows are not detected! + await self.async_set_unique_id(cic_hostname) + self._abort_if_unique_id_configured() + return self.async_create_entry( title=cic_hostname, data=user_input, @@ -64,11 +76,6 @@ async def async_step_user( type=selector.TextSelectorType.TEXT ), ), - vol.Optional(CONF_POWER_SENSOR): selector.EntitySelector( - selector.EntityFilterSelectorConfig( - device_class=SensorDeviceClass.POWER - ) - ), } ), errors=_errors, @@ -82,3 +89,50 @@ async def _test_credentials(self, ip_address: str) -> str: ) data = await client.async_get_data() return data["system"]["hostName"] + + @staticmethod + @callback + def async_get_options_flow(config_entry): + """Return the options flow handler for this config entry.""" + return QuattOptionsFlowHandler(config_entry) + + +class QuattOptionsFlowHandler(config_entries.OptionsFlow): + """Options flow for Quatt.""" + + def __init__(self, config_entry: ConfigEntry) -> None: + """Initialize options flow.""" + self.config_entry = config_entry + + async def async_step_init(self, user_input=None): + """Manage the options.""" + _errors = {} + + # Retrieve the current value of CONF_POWER_SENSOR from options + current_power_sensor = self.config_entry.options.get(CONF_POWER_SENSOR, "") if self.config_entry.options is not None else "" + + if user_input is not None: + return self.async_create_entry(title="", data=user_input) + + return self.async_show_form( + step_id="init", + data_schema=vol.Schema( + { + vol.Required( + CONF_SCAN_INTERVAL, + default=self.config_entry.options.get( + CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL + ), + ): vol.All(vol.Coerce(int), vol.Range(min=MIN_SCAN_INTERVAL, max=MAX_SCAN_INTERVAL)), + vol.Optional( + CONF_POWER_SENSOR, + description={"suggested_value": current_power_sensor if self.hass.states.get(current_power_sensor) else ""}, + ): selector.EntitySelector( + selector.EntityFilterSelectorConfig( + device_class=SensorDeviceClass.POWER + ) + ) + } + ), + errors=_errors, + ) diff --git a/custom_components/quatt/const.py b/custom_components/quatt/const.py index ab57648..788393e 100644 --- a/custom_components/quatt/const.py +++ b/custom_components/quatt/const.py @@ -1,11 +1,10 @@ """Constants for quatt.""" + from logging import Logger, getLogger +from typing import Final -from homeassistant.components.sensor import SensorEntityDescription, SensorDeviceClass -from homeassistant.const import ( - EntityCategory, - UnitOfTemperature, -) +from homeassistant.components.sensor import SensorDeviceClass, SensorEntityDescription +from homeassistant.const import EntityCategory, UnitOfTemperature LOGGER: Logger = getLogger(__package__) @@ -16,6 +15,11 @@ CONF_POWER_SENSOR = "power_sensor" +# Defaults +DEFAULT_SCAN_INTERVAL: Final = 10 +MIN_SCAN_INTERVAL: Final = 5 +MAX_SCAN_INTERVAL: Final = 600 + BINARY_SENSORS = [ # Heatpump 1 SensorEntityDescription( diff --git a/custom_components/quatt/coordinator.py b/custom_components/quatt/coordinator.py index 25a0a73..b018857 100644 --- a/custom_components/quatt/coordinator.py +++ b/custom_components/quatt/coordinator.py @@ -7,17 +7,10 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN from homeassistant.core import HomeAssistant -from homeassistant.helpers.update_coordinator import ( - DataUpdateCoordinator, - UpdateFailed, -) from homeassistant.exceptions import ConfigEntryAuthFailed +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .api import ( - QuattApiClient, - QuattApiClientAuthenticationError, - QuattApiClientError, -) +from .api import QuattApiClient, QuattApiClientAuthenticationError, QuattApiClientError from .const import CONF_POWER_SENSOR, DOMAIN, LOGGER @@ -30,6 +23,7 @@ class QuattDataUpdateCoordinator(DataUpdateCoordinator): def __init__( self, hass: HomeAssistant, + update_interval: int, client: QuattApiClient, ) -> None: """Initialize.""" @@ -38,12 +32,12 @@ def __init__( hass=hass, logger=LOGGER, name=DOMAIN, - update_interval=timedelta(seconds=10), + update_interval=timedelta(seconds=update_interval), ) self._power_sensor_id: str = ( - self.config_entry.data.get(CONF_POWER_SENSOR) - if len(self.config_entry.data.get(CONF_POWER_SENSOR, "")) > 6 + self.config_entry.options.get(CONF_POWER_SENSOR, "") + if (self.config_entry is not None) and (len(self.config_entry.options.get(CONF_POWER_SENSOR, "")) > 6) else None ) @@ -83,6 +77,7 @@ def electicalPower(self): STATE_UNKNOWN, ]: return self.hass.states.get(self._power_sensor_id).state + return None def computedWaterDelta(self, parent_key: str = None): """Compute waterDelta.""" @@ -94,8 +89,16 @@ def computedWaterDelta(self, parent_key: str = None): temperatureWaterOut = self.getValue(parent_key + ".temperatureWaterOut") temperatureWaterIn = self.getValue(parent_key + ".temperatureWaterIn") - LOGGER.debug("%s.computedWaterDelta.temperatureWaterOut %s", parent_key, temperatureWaterOut) - LOGGER.debug("%s.computedWaterDelta.temperatureWaterIn %s", parent_key, temperatureWaterIn) + LOGGER.debug( + "%s.computedWaterDelta.temperatureWaterOut %s", + parent_key, + temperatureWaterOut, + ) + LOGGER.debug( + "%s.computedWaterDelta.temperatureWaterIn %s", + parent_key, + temperatureWaterIn, + ) if temperatureWaterOut is None or temperatureWaterIn is None: return None @@ -157,7 +160,9 @@ def computedQuattCop(self, parent_key: str = None): """Compute Quatt COP.""" if parent_key is None: parent_key = "" - powerInput = self.getValue("hp1.powerInput", 0) + self.getValue("hp2.powerInput", 0) + powerInput = self.getValue("hp1.powerInput", 0) + self.getValue( + "hp2.powerInput", 0 + ) powerOutput = self.getValue("hp1.power", 0) + self.getValue("hp2.power", 0) else: powerInput = self.getValue(parent_key + ".powerInput") @@ -249,7 +254,7 @@ def getValue(self, value_path: str, default: float = None): method = getattr(self, key) return method(parent_key) elif key not in value: - """Ignore any warnings about hp2 - for single quatt installations it is valid that hp2 does not exist.""" + # Ignore any warnings about hp2 - for single quatt installations it is valid that hp2 does not exist. if key != "hp2": LOGGER.warning("Could not find %s of %s", key, value_path) LOGGER.debug("in %s", value) diff --git a/custom_components/quatt/manifest.json b/custom_components/quatt/manifest.json index 87fac92..1d2deec 100644 --- a/custom_components/quatt/manifest.json +++ b/custom_components/quatt/manifest.json @@ -8,7 +8,9 @@ "@marcoboers" ], "config_flow": true, + "single_config_entry": false, "documentation": "https://github.com/marcoboers/home-assistant-quatt", + "integration_type": "hub", "iot_class": "local_polling", "issue_tracker": "https://github.com/marcoboers/home-assistant-quatt/issues", "requirements": [], diff --git a/custom_components/quatt/strings.json b/custom_components/quatt/strings.json index b7e17be..9c12916 100644 --- a/custom_components/quatt/strings.json +++ b/custom_components/quatt/strings.json @@ -2,17 +2,35 @@ "config": { "step": { "user": { - "description": "Set up your Quatt to allow monitoring.", + "description": "Set up your Quatt heatpump for monitoring.", "data": { "ip_address": "[%key:common::config_flow::data::ip%]" }, "data_description": { - "ip_address": "Without http:// and port" + "ip_address": "Without http:// and port." } } }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" + }, + "abort": { + "already_configured": "[%key:common::config_flow::abort::already_configured%]" + } + }, + "options": { + "step": { + "init": { + "description": "Quatt options", + "data": { + "scan_interval": "Update interval (Seconds)", + "power_sensor": "Power sensor (Optional)" + }, + "data_description": { + "scan_interval": "Number of seconds between requesting information from the Quatt.", + "power_sensor": "External energy sensor that measures the current energy consumption of the Quatt." + } + } } } -} \ No newline at end of file +} diff --git a/custom_components/quatt/translations/en.json b/custom_components/quatt/translations/en.json index 1ed5fe2..c80b2d6 100644 --- a/custom_components/quatt/translations/en.json +++ b/custom_components/quatt/translations/en.json @@ -4,15 +4,33 @@ "user": { "description": "Set up your Quatt heatpump for monitoring.", "data": { - "ip_address": "IP address" + "ip_address": "IP address / hostname" }, "data_description": { - "ip_address": "Without http:// and port" + "ip_address": "Without http:// and port." } } }, "error": { - "cannot_connect": "Failed to connect" + "cannot_connect": "Failed to connect." + }, + "abort": { + "already_configured": "Device is already configured." + } + }, + "options": { + "step": { + "init": { + "description": "Quatt options", + "data": { + "scan_interval": "Update interval (Seconds)", + "power_sensor": "Power sensor (Optional)" + }, + "data_description": { + "scan_interval": "Number of seconds between requesting information from the Quatt.", + "power_sensor": "External energy sensor that measures the current energy consumption of the Quatt." + } + } } } -} \ No newline at end of file +} diff --git a/custom_components/quatt/translations/nl.json b/custom_components/quatt/translations/nl.json index 0c8f1d8..3c8eac3 100644 --- a/custom_components/quatt/translations/nl.json +++ b/custom_components/quatt/translations/nl.json @@ -4,15 +4,33 @@ "user": { "description": "Configureer Quatt warmtepomp voor monitoring.", "data": { - "ip_address": "IP adres" + "ip_address": "IP-adres / hostnaam" }, "data_description": { - "ip_address": "Zonder http:// en poort" + "ip_address": "Zonder http:// en poort." } } }, "error": { - "cannot_connect": "Fout bij het verbinden" + "cannot_connect": "Fout bij het verbinden." + }, + "abort": { + "already_configured": "Apparaat is al geconfigureerd." + } + }, + "options": { + "step": { + "init": { + "description": "Quatt opties", + "data": { + "scan_interval": "Update-interval (seconden)", + "power_sensor": "Vermogenssensor (Optioneel)" + }, + "data_description": { + "scan_interval": "Aantal seconden tussen het opvragen van informatie van de Quatt.", + "power_sensor": "Externe energiesensor die het huidige energieverbruik van de Quatt meet." + } + } } } -} \ No newline at end of file +} diff --git a/custom_components/quatt/translations/pt.json b/custom_components/quatt/translations/pt.json index 8d42936..75784b7 100644 --- a/custom_components/quatt/translations/pt.json +++ b/custom_components/quatt/translations/pt.json @@ -2,17 +2,35 @@ "config": { "step": { "user": { - "description": "Configurar a tua monitorização da bomba Quatt .", + "description": "Configurar a tua monitorização da bomba Quatt.", "data": { - "ip_address": "Endereço IP" + "ip_address": "Endereço IP / nome do host" }, "data_description": { - "ip_address": "sem http:// e porta" + "ip_address": "Sem http:// e porta." } } }, "error": { - "cannot_connect": "Falha na ligação" + "cannot_connect": "Falha na ligação." + }, + "abort": { + "already_configured": "O dispositivo já está configurado." + } + }, + "options": { + "step": { + "init": { + "description": "Opções do Quatt", + "data": { + "scan_interval": "Intervalo de atualização (Segundos)", + "power_sensor": "Sensor de potência (Opcional)" + }, + "data_description": { + "scan_interval": "Número de segundos entre a solicitação de informações do Quatt.", + "power_sensor": "Sensor de energia externo que mede o consumo de energia atual do Quatt." + } + } } } } From b571fe03faa7c24257f74297e81d5fa4b8c20871 Mon Sep 17 00:00:00 2001 From: Patrick Vorgers Date: Wed, 28 Aug 2024 22:51:05 +0200 Subject: [PATCH 3/3] Revert "Make the update_interval configurable #54" --- custom_components/quatt/__init__.py | 44 +++--------- custom_components/quatt/config_flow.py | 70 +++----------------- custom_components/quatt/const.py | 14 ++-- custom_components/quatt/coordinator.py | 37 +++++------ custom_components/quatt/manifest.json | 2 - custom_components/quatt/strings.json | 24 +------ custom_components/quatt/translations/en.json | 26 ++------ custom_components/quatt/translations/nl.json | 26 ++------ custom_components/quatt/translations/pt.json | 26 ++------ 9 files changed, 52 insertions(+), 217 deletions(-) diff --git a/custom_components/quatt/__init__.py b/custom_components/quatt/__init__.py index 3c1c90a..d76dc97 100644 --- a/custom_components/quatt/__init__.py +++ b/custom_components/quatt/__init__.py @@ -6,17 +6,17 @@ from __future__ import annotations from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_IP_ADDRESS, CONF_SCAN_INTERVAL, Platform +from homeassistant.const import CONF_IP_ADDRESS, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession from .api import QuattApiClient -from .const import CONF_POWER_SENSOR, DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER +from .const import DOMAIN from .coordinator import QuattDataUpdateCoordinator PLATFORMS: list[Platform] = [ - Platform.BINARY_SENSOR, Platform.SENSOR, + Platform.BINARY_SENSOR, ] @@ -26,7 +26,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: hass.data.setdefault(DOMAIN, {}) hass.data[DOMAIN][entry.entry_id] = coordinator = QuattDataUpdateCoordinator( hass=hass, - update_interval=entry.options.get(CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL), client=QuattApiClient( ip_address=entry.data[CONF_IP_ADDRESS], session=async_get_clientsession(hass), @@ -36,8 +35,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: await coordinator.async_config_entry_first_refresh() await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) - # On update of the options reload the entry which reloads the coordinator - entry.async_on_unload(entry.add_update_listener(update_listener)) + entry.async_on_unload(entry.add_update_listener(async_reload_entry)) return True @@ -49,33 +47,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: return unloaded -async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Update listener.""" - await hass.config_entries.async_reload(entry.entry_id) - - -async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: - """Migrate old entry.""" - - if config_entry.version == 1: - # Migrate CONF_POWER_SENSOR from data to options - # Set the unique_id of the cic - LOGGER.debug("Migrating config entry from version '%s'", config_entry.version) - - new_data = {**config_entry.data} - new_options = {**config_entry.options} - - if CONF_POWER_SENSOR in new_data: - new_options[CONF_POWER_SENSOR] = new_data.pop(CONF_POWER_SENSOR) - - # Update the version to 2 - config_entry.version = 2 - - hass.config_entries.async_update_entry( - config_entry, - data=new_data, # Updated data without CONF_POWER_SENSOR - options=new_options, # Updated options with CONF_POWER_SENSOR - version=config_entry.version - ) - - return True +async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Reload config entry.""" + await async_unload_entry(hass, entry) + await async_setup_entry(hass, entry) diff --git a/custom_components/quatt/config_flow.py b/custom_components/quatt/config_flow.py index b9b9d68..4531cb5 100644 --- a/custom_components/quatt/config_flow.py +++ b/custom_components/quatt/config_flow.py @@ -1,14 +1,10 @@ """Adds config flow for Quatt.""" - from __future__ import annotations import voluptuous as vol - from homeassistant import config_entries from homeassistant.components.sensor import SensorDeviceClass -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import CONF_IP_ADDRESS, CONF_SCAN_INTERVAL -from homeassistant.core import callback +from homeassistant.const import CONF_IP_ADDRESS from homeassistant.helpers import selector from homeassistant.helpers.aiohttp_client import async_create_clientsession @@ -19,19 +15,16 @@ QuattApiClientError, ) from .const import ( - CONF_POWER_SENSOR, - DEFAULT_SCAN_INTERVAL, DOMAIN, LOGGER, - MAX_SCAN_INTERVAL, - MIN_SCAN_INTERVAL, + CONF_POWER_SENSOR, ) class QuattFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for Quatt.""" - VERSION = 2 + VERSION = 1 async def async_step_user( self, @@ -54,11 +47,6 @@ async def async_step_user( LOGGER.exception(exception) _errors["base"] = "unknown" else: - # Check if this cic has already been configured - # Pre-version 2 config flows are not detected! - await self.async_set_unique_id(cic_hostname) - self._abort_if_unique_id_configured() - return self.async_create_entry( title=cic_hostname, data=user_input, @@ -76,6 +64,11 @@ async def async_step_user( type=selector.TextSelectorType.TEXT ), ), + vol.Optional(CONF_POWER_SENSOR): selector.EntitySelector( + selector.EntityFilterSelectorConfig( + device_class=SensorDeviceClass.POWER + ) + ), } ), errors=_errors, @@ -89,50 +82,3 @@ async def _test_credentials(self, ip_address: str) -> str: ) data = await client.async_get_data() return data["system"]["hostName"] - - @staticmethod - @callback - def async_get_options_flow(config_entry): - """Return the options flow handler for this config entry.""" - return QuattOptionsFlowHandler(config_entry) - - -class QuattOptionsFlowHandler(config_entries.OptionsFlow): - """Options flow for Quatt.""" - - def __init__(self, config_entry: ConfigEntry) -> None: - """Initialize options flow.""" - self.config_entry = config_entry - - async def async_step_init(self, user_input=None): - """Manage the options.""" - _errors = {} - - # Retrieve the current value of CONF_POWER_SENSOR from options - current_power_sensor = self.config_entry.options.get(CONF_POWER_SENSOR, "") if self.config_entry.options is not None else "" - - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - return self.async_show_form( - step_id="init", - data_schema=vol.Schema( - { - vol.Required( - CONF_SCAN_INTERVAL, - default=self.config_entry.options.get( - CONF_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL - ), - ): vol.All(vol.Coerce(int), vol.Range(min=MIN_SCAN_INTERVAL, max=MAX_SCAN_INTERVAL)), - vol.Optional( - CONF_POWER_SENSOR, - description={"suggested_value": current_power_sensor if self.hass.states.get(current_power_sensor) else ""}, - ): selector.EntitySelector( - selector.EntityFilterSelectorConfig( - device_class=SensorDeviceClass.POWER - ) - ) - } - ), - errors=_errors, - ) diff --git a/custom_components/quatt/const.py b/custom_components/quatt/const.py index 788393e..ab57648 100644 --- a/custom_components/quatt/const.py +++ b/custom_components/quatt/const.py @@ -1,10 +1,11 @@ """Constants for quatt.""" - from logging import Logger, getLogger -from typing import Final -from homeassistant.components.sensor import SensorDeviceClass, SensorEntityDescription -from homeassistant.const import EntityCategory, UnitOfTemperature +from homeassistant.components.sensor import SensorEntityDescription, SensorDeviceClass +from homeassistant.const import ( + EntityCategory, + UnitOfTemperature, +) LOGGER: Logger = getLogger(__package__) @@ -15,11 +16,6 @@ CONF_POWER_SENSOR = "power_sensor" -# Defaults -DEFAULT_SCAN_INTERVAL: Final = 10 -MIN_SCAN_INTERVAL: Final = 5 -MAX_SCAN_INTERVAL: Final = 600 - BINARY_SENSORS = [ # Heatpump 1 SensorEntityDescription( diff --git a/custom_components/quatt/coordinator.py b/custom_components/quatt/coordinator.py index b018857..25a0a73 100644 --- a/custom_components/quatt/coordinator.py +++ b/custom_components/quatt/coordinator.py @@ -7,10 +7,17 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN from homeassistant.core import HomeAssistant +from homeassistant.helpers.update_coordinator import ( + DataUpdateCoordinator, + UpdateFailed, +) from homeassistant.exceptions import ConfigEntryAuthFailed -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed -from .api import QuattApiClient, QuattApiClientAuthenticationError, QuattApiClientError +from .api import ( + QuattApiClient, + QuattApiClientAuthenticationError, + QuattApiClientError, +) from .const import CONF_POWER_SENSOR, DOMAIN, LOGGER @@ -23,7 +30,6 @@ class QuattDataUpdateCoordinator(DataUpdateCoordinator): def __init__( self, hass: HomeAssistant, - update_interval: int, client: QuattApiClient, ) -> None: """Initialize.""" @@ -32,12 +38,12 @@ def __init__( hass=hass, logger=LOGGER, name=DOMAIN, - update_interval=timedelta(seconds=update_interval), + update_interval=timedelta(seconds=10), ) self._power_sensor_id: str = ( - self.config_entry.options.get(CONF_POWER_SENSOR, "") - if (self.config_entry is not None) and (len(self.config_entry.options.get(CONF_POWER_SENSOR, "")) > 6) + self.config_entry.data.get(CONF_POWER_SENSOR) + if len(self.config_entry.data.get(CONF_POWER_SENSOR, "")) > 6 else None ) @@ -77,7 +83,6 @@ def electicalPower(self): STATE_UNKNOWN, ]: return self.hass.states.get(self._power_sensor_id).state - return None def computedWaterDelta(self, parent_key: str = None): """Compute waterDelta.""" @@ -89,16 +94,8 @@ def computedWaterDelta(self, parent_key: str = None): temperatureWaterOut = self.getValue(parent_key + ".temperatureWaterOut") temperatureWaterIn = self.getValue(parent_key + ".temperatureWaterIn") - LOGGER.debug( - "%s.computedWaterDelta.temperatureWaterOut %s", - parent_key, - temperatureWaterOut, - ) - LOGGER.debug( - "%s.computedWaterDelta.temperatureWaterIn %s", - parent_key, - temperatureWaterIn, - ) + LOGGER.debug("%s.computedWaterDelta.temperatureWaterOut %s", parent_key, temperatureWaterOut) + LOGGER.debug("%s.computedWaterDelta.temperatureWaterIn %s", parent_key, temperatureWaterIn) if temperatureWaterOut is None or temperatureWaterIn is None: return None @@ -160,9 +157,7 @@ def computedQuattCop(self, parent_key: str = None): """Compute Quatt COP.""" if parent_key is None: parent_key = "" - powerInput = self.getValue("hp1.powerInput", 0) + self.getValue( - "hp2.powerInput", 0 - ) + powerInput = self.getValue("hp1.powerInput", 0) + self.getValue("hp2.powerInput", 0) powerOutput = self.getValue("hp1.power", 0) + self.getValue("hp2.power", 0) else: powerInput = self.getValue(parent_key + ".powerInput") @@ -254,7 +249,7 @@ def getValue(self, value_path: str, default: float = None): method = getattr(self, key) return method(parent_key) elif key not in value: - # Ignore any warnings about hp2 - for single quatt installations it is valid that hp2 does not exist. + """Ignore any warnings about hp2 - for single quatt installations it is valid that hp2 does not exist.""" if key != "hp2": LOGGER.warning("Could not find %s of %s", key, value_path) LOGGER.debug("in %s", value) diff --git a/custom_components/quatt/manifest.json b/custom_components/quatt/manifest.json index 1d2deec..87fac92 100644 --- a/custom_components/quatt/manifest.json +++ b/custom_components/quatt/manifest.json @@ -8,9 +8,7 @@ "@marcoboers" ], "config_flow": true, - "single_config_entry": false, "documentation": "https://github.com/marcoboers/home-assistant-quatt", - "integration_type": "hub", "iot_class": "local_polling", "issue_tracker": "https://github.com/marcoboers/home-assistant-quatt/issues", "requirements": [], diff --git a/custom_components/quatt/strings.json b/custom_components/quatt/strings.json index 9c12916..b7e17be 100644 --- a/custom_components/quatt/strings.json +++ b/custom_components/quatt/strings.json @@ -2,35 +2,17 @@ "config": { "step": { "user": { - "description": "Set up your Quatt heatpump for monitoring.", + "description": "Set up your Quatt to allow monitoring.", "data": { "ip_address": "[%key:common::config_flow::data::ip%]" }, "data_description": { - "ip_address": "Without http:// and port." + "ip_address": "Without http:// and port" } } }, "error": { "cannot_connect": "[%key:common::config_flow::error::cannot_connect%]" - }, - "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured%]" - } - }, - "options": { - "step": { - "init": { - "description": "Quatt options", - "data": { - "scan_interval": "Update interval (Seconds)", - "power_sensor": "Power sensor (Optional)" - }, - "data_description": { - "scan_interval": "Number of seconds between requesting information from the Quatt.", - "power_sensor": "External energy sensor that measures the current energy consumption of the Quatt." - } - } } } -} +} \ No newline at end of file diff --git a/custom_components/quatt/translations/en.json b/custom_components/quatt/translations/en.json index c80b2d6..1ed5fe2 100644 --- a/custom_components/quatt/translations/en.json +++ b/custom_components/quatt/translations/en.json @@ -4,33 +4,15 @@ "user": { "description": "Set up your Quatt heatpump for monitoring.", "data": { - "ip_address": "IP address / hostname" + "ip_address": "IP address" }, "data_description": { - "ip_address": "Without http:// and port." + "ip_address": "Without http:// and port" } } }, "error": { - "cannot_connect": "Failed to connect." - }, - "abort": { - "already_configured": "Device is already configured." - } - }, - "options": { - "step": { - "init": { - "description": "Quatt options", - "data": { - "scan_interval": "Update interval (Seconds)", - "power_sensor": "Power sensor (Optional)" - }, - "data_description": { - "scan_interval": "Number of seconds between requesting information from the Quatt.", - "power_sensor": "External energy sensor that measures the current energy consumption of the Quatt." - } - } + "cannot_connect": "Failed to connect" } } -} +} \ No newline at end of file diff --git a/custom_components/quatt/translations/nl.json b/custom_components/quatt/translations/nl.json index 3c8eac3..0c8f1d8 100644 --- a/custom_components/quatt/translations/nl.json +++ b/custom_components/quatt/translations/nl.json @@ -4,33 +4,15 @@ "user": { "description": "Configureer Quatt warmtepomp voor monitoring.", "data": { - "ip_address": "IP-adres / hostnaam" + "ip_address": "IP adres" }, "data_description": { - "ip_address": "Zonder http:// en poort." + "ip_address": "Zonder http:// en poort" } } }, "error": { - "cannot_connect": "Fout bij het verbinden." - }, - "abort": { - "already_configured": "Apparaat is al geconfigureerd." - } - }, - "options": { - "step": { - "init": { - "description": "Quatt opties", - "data": { - "scan_interval": "Update-interval (seconden)", - "power_sensor": "Vermogenssensor (Optioneel)" - }, - "data_description": { - "scan_interval": "Aantal seconden tussen het opvragen van informatie van de Quatt.", - "power_sensor": "Externe energiesensor die het huidige energieverbruik van de Quatt meet." - } - } + "cannot_connect": "Fout bij het verbinden" } } -} +} \ No newline at end of file diff --git a/custom_components/quatt/translations/pt.json b/custom_components/quatt/translations/pt.json index 75784b7..8d42936 100644 --- a/custom_components/quatt/translations/pt.json +++ b/custom_components/quatt/translations/pt.json @@ -2,35 +2,17 @@ "config": { "step": { "user": { - "description": "Configurar a tua monitorização da bomba Quatt.", + "description": "Configurar a tua monitorização da bomba Quatt .", "data": { - "ip_address": "Endereço IP / nome do host" + "ip_address": "Endereço IP" }, "data_description": { - "ip_address": "Sem http:// e porta." + "ip_address": "sem http:// e porta" } } }, "error": { - "cannot_connect": "Falha na ligação." - }, - "abort": { - "already_configured": "O dispositivo já está configurado." - } - }, - "options": { - "step": { - "init": { - "description": "Opções do Quatt", - "data": { - "scan_interval": "Intervalo de atualização (Segundos)", - "power_sensor": "Sensor de potência (Opcional)" - }, - "data_description": { - "scan_interval": "Número de segundos entre a solicitação de informações do Quatt.", - "power_sensor": "Sensor de energia externo que mede o consumo de energia atual do Quatt." - } - } + "cannot_connect": "Falha na ligação" } } }