From c1cf1684e5a9b7a0dfadc2859e9d79d66747a921 Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Wed, 4 Aug 2021 22:55:55 +0200 Subject: [PATCH 01/22] Move updatecoordinator to __init__ so it can be reused --- .../homewizard_energy/__init__.py | 67 ++++++++++++++++++ custom_components/homewizard_energy/const.py | 4 ++ .../homewizard_energy/manifest.json | 2 +- custom_components/homewizard_energy/sensor.py | 69 ++++++------------- 4 files changed, 92 insertions(+), 50 deletions(-) diff --git a/custom_components/homewizard_energy/__init__.py b/custom_components/homewizard_energy/__init__.py index 43800a7..494034f 100644 --- a/custom_components/homewizard_energy/__init__.py +++ b/custom_components/homewizard_energy/__init__.py @@ -2,21 +2,29 @@ import asyncio import logging import re +from datetime import timedelta from enum import unique import aiohwenergy import async_timeout import voluptuous as vol from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_API_VERSION, CONF_ID, CONF_STATE from homeassistant.core import Config, HomeAssistant from homeassistant.helpers import entity_registry +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import slugify from .const import ( CONF_API, + CONF_DATA, + CONF_MODEL, + CONF_NAME, CONF_OVERRIDE_POLL_INTERVAL, CONF_POLL_INTERVAL_SECONDS, + CONF_SW_VERSION, CONF_UNLOAD_CB, + COORDINATOR, DEFAULT_OVERRIDE_POLL_INTERVAL, DEFAULT_POLL_INTERVAL_SECONDS, DOMAIN, @@ -122,7 +130,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): CONF_UNLOAD_CB ] = entry.add_update_listener(async_entry_updated) + # Get api and do a initialization energy_api = aiohwenergy.HomeWizardEnergy(entry.data.get("host")) + await energy_api.initialize() + + # Create coordinator + coordinator = hass.data[DOMAIN][entry.data["unique_id"]][ + COORDINATOR + ] = HWEnergyDeviceUpdateCoordinator(hass, energy_api) + await coordinator.async_config_entry_first_refresh() + hass.data[DOMAIN][entry.data["unique_id"]][CONF_API] = energy_api for component in PLATFORMS: hass.async_create_task( @@ -167,3 +184,53 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: Logger.debug("__init async_remove_entry") pass + + +class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator): + """Gather data for the energy device""" + + def __init__( + self, + hass: HomeAssistant, + api: aiohwenergy.HomeWizardEnergy, + ) -> None: + self.api = api + + update_interval = timedelta(seconds=10) + super().__init__(hass, Logger, name="", update_interval=update_interval) + + async def _async_update_data(self) -> dict: + """Fetch all device and sensor data from api.""" + try: + async with async_timeout.timeout(10): + # Update all properties + status = await self.api.update() + + if not status: + raise Exception("Failed to fetch data") + + data = { + CONF_NAME: self.api.device.product_name, + CONF_MODEL: self.api.device.product_type, + CONF_ID: self.api.device.serial, + CONF_SW_VERSION: self.api.device.firmware_version, + CONF_API_VERSION: self.api.device.api_version, + CONF_DATA: {}, + CONF_STATE: None, + } + + for datapoint in self.api.data.available_datapoints: + data[CONF_DATA][datapoint] = getattr(self.api.data, datapoint) + + if self.api.state is not None: + data[CONF_STATE] = { + "power_on": self.api.state.power_on, + "switch_lock": self.api.state.switch_lock, + "brightness": self.api.state.brightness, + } + + except Exception as ex: + raise UpdateFailed(ex) from ex + + self.name = data[CONF_NAME] + return data diff --git a/custom_components/homewizard_energy/const.py b/custom_components/homewizard_energy/const.py index f360a5e..1dad5ac 100644 --- a/custom_components/homewizard_energy/const.py +++ b/custom_components/homewizard_energy/const.py @@ -4,6 +4,7 @@ # Set up. DOMAIN = "homewizard_energy" +COORDINATOR = "coordinator" MANUFACTURER_NAME = "HomeWizard" PLATFORMS = ["sensor"] @@ -14,6 +15,9 @@ CONF_API = "api" CONF_UNLOAD_CB = "unload_unsub" +CONF_MODEL = "model" +CONF_SW_VERSION = "sw_ver" +CONF_DATA = "data" # Services. SERVICE_UPDATE = "update" diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index e2ac3da..a823ec7 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -11,7 +11,7 @@ "zeroconf" ], "requirements": [ - "aiohwenergy==0.1.0" + "aiohwenergy==0.2.1" ], "zeroconf": [ "_hwenergy._tcp.local." diff --git a/custom_components/homewizard_energy/sensor.py b/custom_components/homewizard_energy/sensor.py index 5378fa4..896808b 100644 --- a/custom_components/homewizard_energy/sensor.py +++ b/custom_components/homewizard_energy/sensor.py @@ -1,10 +1,8 @@ """Creates Homewizard Energy sensor entities.""" import asyncio import logging -import sys from datetime import timedelta -from enum import unique -from typing import Final +from typing import Any, Final import aiohwenergy import async_timeout @@ -15,16 +13,18 @@ SensorEntityDescription, ) from homeassistant.const import ( + CONF_STATE, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_TIMESTAMP, ENERGY_KILO_WATT_HOUR, PERCENTAGE, POWER_WATT, VOLUME_CUBIC_METERS, - DEVICE_CLASS_TIMESTAMP ) from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, @@ -47,8 +47,12 @@ ATTR_WIFI_SSID, ATTR_WIFI_STRENGTH, CONF_API, + CONF_DATA, + CONF_MODEL, CONF_OVERRIDE_POLL_INTERVAL, CONF_POLL_INTERVAL_SECONDS, + CONF_SW_VERSION, + COORDINATOR, DEFAULT_OVERRIDE_POLL_INTERVAL, DEFAULT_POLL_INTERVAL_SECONDS, DOMAIN, @@ -56,8 +60,6 @@ Logger = logging.getLogger(__name__) -_PLATFORM = "sensor" - SENSORS: Final[list[SensorEntityDescription]] = [ SensorEntityDescription( key=ATTR_SMR_VERSION, @@ -158,7 +160,7 @@ key=ATTR_GAS_TIMESTAMP, name="Gas timestamp", icon="mdi:timeline-clock", - device_class=DEVICE_CLASS_TIMESTAMP + device_class=DEVICE_CLASS_TIMESTAMP, ), ] @@ -226,40 +228,7 @@ async def async_setup_entry(hass, entry, async_add_entities): if not initialized: await energy_api.close() - # Callback for updating data - async def async_update_data(): - """Fetch data from API endpoint.""" - data = {} - async with async_timeout.timeout(10): - try: - status = await energy_api.data.update() - - if status: - for datapoint in energy_api.data.available_datapoints: - data[datapoint] = getattr(energy_api.data, datapoint) - - return data - except AttributeError: - Logger.error("Datapoint missing") - return - except aiohwenergy.errors.InvalidState: - Logger.error("Failed tot fetch new data") - finally: - return data - - # Determine update interval - update_interval = get_update_interval(entry, energy_api) - - # Create coordiantor for fetching the complete device - coordinator = DataUpdateCoordinator( - hass, - Logger, - # Name of the data. For logging purposes. - name=entry.data["name"], - update_method=async_update_data, - # Polling interval. Will only be polled if there are subscribers. - update_interval=timedelta(seconds=update_interval), - ) + coordinator = hass.data[DOMAIN][entry.data["unique_id"]][COORDINATOR] # Fetch initial data so we have data when entities subscribe await coordinator.async_refresh() @@ -301,12 +270,19 @@ def __init__(self, coordinator, entry_data, description): self._attr_last_reset = utc_from_timestamp(0) @property - def device_info(self): + def device_info(self) -> DeviceInfo: return { "name": self.name, "manufacturer": "HomeWizard", + "sw_version": self.data[CONF_SW_VERSION], + "model": self.data[CONF_MODEL], } + @property + def data(self) -> dict[str:Any]: + """Return data from DataUpdateCoordinator""" + return self.coordinator.data + @property def icon(self): """Return the icon.""" @@ -315,17 +291,12 @@ def icon(self): @property def state(self): """Returns state of meter.""" - return self.coordinator.data[self.data_type] + return self.data[CONF_DATA][self.data_type] @property def available(self): """Returns state of meter.""" - return self.data_type in self.coordinator.data - - @property - def unit_of_measurement(self): - """Return the unit of measurement.""" - return self.entity_description.unit_of_measurement + return self.data_type in self.data[CONF_DATA] async def async_get_aiohwenergy_from_entry_data(entry_data): From 7c9bdc5fa804fe12108f15b47957ea7a9b766547 Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Wed, 4 Aug 2021 23:26:53 +0200 Subject: [PATCH 02/22] Add identifiers to device_info so the integrations creates devices --- custom_components/homewizard_energy/sensor.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/custom_components/homewizard_energy/sensor.py b/custom_components/homewizard_energy/sensor.py index ad7205a..19a7eec 100644 --- a/custom_components/homewizard_energy/sensor.py +++ b/custom_components/homewizard_energy/sensor.py @@ -13,6 +13,7 @@ SensorEntityDescription, ) from homeassistant.const import ( + CONF_ID, CONF_STATE, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, @@ -25,6 +26,7 @@ ) from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.entity import DeviceInfo +import homeassistant.helpers.device_registry as dr from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, @@ -272,10 +274,11 @@ def __init__(self, coordinator, entry_data, description): @property def device_info(self) -> DeviceInfo: return { - "name": self.name, + "name": self.entry_data["custom_name"], "manufacturer": "HomeWizard", "sw_version": self.data[CONF_SW_VERSION], "model": self.data[CONF_MODEL], + "identifiers": {(DOMAIN, self.data[CONF_ID])}, } @property From f000353a320a53a4f6356284a11f42704091ad0f Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Thu, 5 Aug 2021 09:07:07 +0200 Subject: [PATCH 03/22] Bumb aiohwenergy requirement --- custom_components/homewizard_energy/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index a823ec7..5dc779b 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -11,7 +11,7 @@ "zeroconf" ], "requirements": [ - "aiohwenergy==0.2.1" + "aiohwenergy==0.2.2" ], "zeroconf": [ "_hwenergy._tcp.local." From f4158dc0ad65b9a04329a02c8ae2c2a18ec878e8 Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Thu, 5 Aug 2021 09:07:53 +0200 Subject: [PATCH 04/22] Make optional entities disabled by default when not given --- custom_components/homewizard_energy/sensor.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/custom_components/homewizard_energy/sensor.py b/custom_components/homewizard_energy/sensor.py index b9608ef..bf932b7 100644 --- a/custom_components/homewizard_energy/sensor.py +++ b/custom_components/homewizard_energy/sensor.py @@ -270,6 +270,19 @@ def __init__(self, coordinator, entry_data, description): self.unique_id = "%s_%s" % (entry_data["unique_id"], description.key) self._attr_last_reset = utc_from_timestamp(0) + # Some values are given, but set to NULL (eg. gas_timestamp when no gas meter is connected) + if self.data[CONF_DATA][self.data_type] is None: + self.entity_description.entity_registry_enabled_default = False + + # Special case for export, not everyone has solarpanels + # The change that 'export' is non-zero when you have solar panels is nil + if self.data_type in [ + ATTR_TOTAL_POWER_EXPORT_T1_KWH, + ATTR_TOTAL_POWER_EXPORT_T2_KWH, + ]: + if self.data[CONF_DATA][self.data_type] == 0: + self.entity_description.entity_registry_enabled_default = False + @property def device_info(self) -> DeviceInfo: return { From 0c1daa9aee26a4135d0557a0e3c9510f4be4153d Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Wed, 4 Aug 2021 22:55:55 +0200 Subject: [PATCH 05/22] Move updatecoordinator to __init__ so it can be reused --- .../homewizard_energy/__init__.py | 67 ++++++++++++++++++ custom_components/homewizard_energy/const.py | 4 ++ .../homewizard_energy/manifest.json | 2 +- custom_components/homewizard_energy/sensor.py | 69 ++++++------------- 4 files changed, 92 insertions(+), 50 deletions(-) diff --git a/custom_components/homewizard_energy/__init__.py b/custom_components/homewizard_energy/__init__.py index 43800a7..494034f 100644 --- a/custom_components/homewizard_energy/__init__.py +++ b/custom_components/homewizard_energy/__init__.py @@ -2,21 +2,29 @@ import asyncio import logging import re +from datetime import timedelta from enum import unique import aiohwenergy import async_timeout import voluptuous as vol from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_API_VERSION, CONF_ID, CONF_STATE from homeassistant.core import Config, HomeAssistant from homeassistant.helpers import entity_registry +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import slugify from .const import ( CONF_API, + CONF_DATA, + CONF_MODEL, + CONF_NAME, CONF_OVERRIDE_POLL_INTERVAL, CONF_POLL_INTERVAL_SECONDS, + CONF_SW_VERSION, CONF_UNLOAD_CB, + COORDINATOR, DEFAULT_OVERRIDE_POLL_INTERVAL, DEFAULT_POLL_INTERVAL_SECONDS, DOMAIN, @@ -122,7 +130,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): CONF_UNLOAD_CB ] = entry.add_update_listener(async_entry_updated) + # Get api and do a initialization energy_api = aiohwenergy.HomeWizardEnergy(entry.data.get("host")) + await energy_api.initialize() + + # Create coordinator + coordinator = hass.data[DOMAIN][entry.data["unique_id"]][ + COORDINATOR + ] = HWEnergyDeviceUpdateCoordinator(hass, energy_api) + await coordinator.async_config_entry_first_refresh() + hass.data[DOMAIN][entry.data["unique_id"]][CONF_API] = energy_api for component in PLATFORMS: hass.async_create_task( @@ -167,3 +184,53 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry): async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: Logger.debug("__init async_remove_entry") pass + + +class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator): + """Gather data for the energy device""" + + def __init__( + self, + hass: HomeAssistant, + api: aiohwenergy.HomeWizardEnergy, + ) -> None: + self.api = api + + update_interval = timedelta(seconds=10) + super().__init__(hass, Logger, name="", update_interval=update_interval) + + async def _async_update_data(self) -> dict: + """Fetch all device and sensor data from api.""" + try: + async with async_timeout.timeout(10): + # Update all properties + status = await self.api.update() + + if not status: + raise Exception("Failed to fetch data") + + data = { + CONF_NAME: self.api.device.product_name, + CONF_MODEL: self.api.device.product_type, + CONF_ID: self.api.device.serial, + CONF_SW_VERSION: self.api.device.firmware_version, + CONF_API_VERSION: self.api.device.api_version, + CONF_DATA: {}, + CONF_STATE: None, + } + + for datapoint in self.api.data.available_datapoints: + data[CONF_DATA][datapoint] = getattr(self.api.data, datapoint) + + if self.api.state is not None: + data[CONF_STATE] = { + "power_on": self.api.state.power_on, + "switch_lock": self.api.state.switch_lock, + "brightness": self.api.state.brightness, + } + + except Exception as ex: + raise UpdateFailed(ex) from ex + + self.name = data[CONF_NAME] + return data diff --git a/custom_components/homewizard_energy/const.py b/custom_components/homewizard_energy/const.py index f360a5e..1dad5ac 100644 --- a/custom_components/homewizard_energy/const.py +++ b/custom_components/homewizard_energy/const.py @@ -4,6 +4,7 @@ # Set up. DOMAIN = "homewizard_energy" +COORDINATOR = "coordinator" MANUFACTURER_NAME = "HomeWizard" PLATFORMS = ["sensor"] @@ -14,6 +15,9 @@ CONF_API = "api" CONF_UNLOAD_CB = "unload_unsub" +CONF_MODEL = "model" +CONF_SW_VERSION = "sw_ver" +CONF_DATA = "data" # Services. SERVICE_UPDATE = "update" diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index e2ac3da..a823ec7 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -11,7 +11,7 @@ "zeroconf" ], "requirements": [ - "aiohwenergy==0.1.0" + "aiohwenergy==0.2.1" ], "zeroconf": [ "_hwenergy._tcp.local." diff --git a/custom_components/homewizard_energy/sensor.py b/custom_components/homewizard_energy/sensor.py index 5378fa4..896808b 100644 --- a/custom_components/homewizard_energy/sensor.py +++ b/custom_components/homewizard_energy/sensor.py @@ -1,10 +1,8 @@ """Creates Homewizard Energy sensor entities.""" import asyncio import logging -import sys from datetime import timedelta -from enum import unique -from typing import Final +from typing import Any, Final import aiohwenergy import async_timeout @@ -15,16 +13,18 @@ SensorEntityDescription, ) from homeassistant.const import ( + CONF_STATE, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, DEVICE_CLASS_SIGNAL_STRENGTH, + DEVICE_CLASS_TIMESTAMP, ENERGY_KILO_WATT_HOUR, PERCENTAGE, POWER_WATT, VOLUME_CUBIC_METERS, - DEVICE_CLASS_TIMESTAMP ) from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, @@ -47,8 +47,12 @@ ATTR_WIFI_SSID, ATTR_WIFI_STRENGTH, CONF_API, + CONF_DATA, + CONF_MODEL, CONF_OVERRIDE_POLL_INTERVAL, CONF_POLL_INTERVAL_SECONDS, + CONF_SW_VERSION, + COORDINATOR, DEFAULT_OVERRIDE_POLL_INTERVAL, DEFAULT_POLL_INTERVAL_SECONDS, DOMAIN, @@ -56,8 +60,6 @@ Logger = logging.getLogger(__name__) -_PLATFORM = "sensor" - SENSORS: Final[list[SensorEntityDescription]] = [ SensorEntityDescription( key=ATTR_SMR_VERSION, @@ -158,7 +160,7 @@ key=ATTR_GAS_TIMESTAMP, name="Gas timestamp", icon="mdi:timeline-clock", - device_class=DEVICE_CLASS_TIMESTAMP + device_class=DEVICE_CLASS_TIMESTAMP, ), ] @@ -226,40 +228,7 @@ async def async_setup_entry(hass, entry, async_add_entities): if not initialized: await energy_api.close() - # Callback for updating data - async def async_update_data(): - """Fetch data from API endpoint.""" - data = {} - async with async_timeout.timeout(10): - try: - status = await energy_api.data.update() - - if status: - for datapoint in energy_api.data.available_datapoints: - data[datapoint] = getattr(energy_api.data, datapoint) - - return data - except AttributeError: - Logger.error("Datapoint missing") - return - except aiohwenergy.errors.InvalidState: - Logger.error("Failed tot fetch new data") - finally: - return data - - # Determine update interval - update_interval = get_update_interval(entry, energy_api) - - # Create coordiantor for fetching the complete device - coordinator = DataUpdateCoordinator( - hass, - Logger, - # Name of the data. For logging purposes. - name=entry.data["name"], - update_method=async_update_data, - # Polling interval. Will only be polled if there are subscribers. - update_interval=timedelta(seconds=update_interval), - ) + coordinator = hass.data[DOMAIN][entry.data["unique_id"]][COORDINATOR] # Fetch initial data so we have data when entities subscribe await coordinator.async_refresh() @@ -301,12 +270,19 @@ def __init__(self, coordinator, entry_data, description): self._attr_last_reset = utc_from_timestamp(0) @property - def device_info(self): + def device_info(self) -> DeviceInfo: return { "name": self.name, "manufacturer": "HomeWizard", + "sw_version": self.data[CONF_SW_VERSION], + "model": self.data[CONF_MODEL], } + @property + def data(self) -> dict[str:Any]: + """Return data from DataUpdateCoordinator""" + return self.coordinator.data + @property def icon(self): """Return the icon.""" @@ -315,17 +291,12 @@ def icon(self): @property def state(self): """Returns state of meter.""" - return self.coordinator.data[self.data_type] + return self.data[CONF_DATA][self.data_type] @property def available(self): """Returns state of meter.""" - return self.data_type in self.coordinator.data - - @property - def unit_of_measurement(self): - """Return the unit of measurement.""" - return self.entity_description.unit_of_measurement + return self.data_type in self.data[CONF_DATA] async def async_get_aiohwenergy_from_entry_data(entry_data): From 4234f77bc716ef73fec8b5f20772781ff2714ab4 Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Thu, 5 Aug 2021 09:24:44 +0200 Subject: [PATCH 06/22] Remove poll_interval setting --- .../homewizard_energy/__init__.py | 4 -- .../homewizard_energy/config_flow.py | 47 +------------------ custom_components/homewizard_energy/const.py | 10 +--- custom_components/homewizard_energy/sensor.py | 9 ---- 4 files changed, 2 insertions(+), 68 deletions(-) diff --git a/custom_components/homewizard_energy/__init__.py b/custom_components/homewizard_energy/__init__.py index 494034f..eaa7c68 100644 --- a/custom_components/homewizard_energy/__init__.py +++ b/custom_components/homewizard_energy/__init__.py @@ -20,13 +20,9 @@ CONF_DATA, CONF_MODEL, CONF_NAME, - CONF_OVERRIDE_POLL_INTERVAL, - CONF_POLL_INTERVAL_SECONDS, CONF_SW_VERSION, CONF_UNLOAD_CB, COORDINATOR, - DEFAULT_OVERRIDE_POLL_INTERVAL, - DEFAULT_POLL_INTERVAL_SECONDS, DOMAIN, ) diff --git a/custom_components/homewizard_energy/config_flow.py b/custom_components/homewizard_energy/config_flow.py index 9e9676f..e2c3db1 100644 --- a/custom_components/homewizard_energy/config_flow.py +++ b/custom_components/homewizard_energy/config_flow.py @@ -17,10 +17,6 @@ from .const import ( CONF_IP_ADDRESS, - CONF_OVERRIDE_POLL_INTERVAL, - CONF_POLL_INTERVAL_SECONDS, - DEFAULT_OVERRIDE_POLL_INTERVAL, - DEFAULT_POLL_INTERVAL_SECONDS, DOMAIN, ) @@ -33,12 +29,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL - @staticmethod - @callback - def async_get_options_flow(config_entry): - """Get the options flow for this handler.""" - return HWEnergyConfigFlowHandler(config_entry) - def __init__(self): """Set up the instance.""" Logger.debug("config_flow __init__") @@ -155,7 +145,7 @@ async def async_step_check(self, entry_info): # return self.async_abort(reason="device_not_supported") if entry_info["api_enabled"] != "1": - # Logger.warning("API not enabled, please enable API in app") + Logger.warning("API not enabled, please enable API in app") return self.async_abort(reason="api_not_enabled") Logger.debug(f"entry_info: {entry_info}") @@ -245,38 +235,3 @@ async def async_step_discovery_confirm(self, user_input=None): title=title, data=self.context, ) - - -class HWEnergyConfigFlowHandler(config_entries.OptionsFlow): - """Handle options.""" - - def __init__(self, config_entry): - """Initialize Hue options flow.""" - self.config_entry = config_entry - - async def async_step_init( - self, user_input: Optional[Dict[str, Any]] = None - ) -> Dict[str, Any]: - """Manage Energy options.""" - 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_OVERRIDE_POLL_INTERVAL, - default=self.config_entry.options.get( - CONF_OVERRIDE_POLL_INTERVAL, DEFAULT_OVERRIDE_POLL_INTERVAL - ), - ): bool, - vol.Required( - CONF_POLL_INTERVAL_SECONDS, - default=self.config_entry.options.get( - CONF_POLL_INTERVAL_SECONDS, DEFAULT_POLL_INTERVAL_SECONDS - ), - ): vol.All(cv.positive_int, vol.Range(min=1)), - } - ), - ) diff --git a/custom_components/homewizard_energy/const.py b/custom_components/homewizard_energy/const.py index 1dad5ac..772fa55 100644 --- a/custom_components/homewizard_energy/const.py +++ b/custom_components/homewizard_energy/const.py @@ -40,12 +40,4 @@ # Default values. DEFAULT_STR_VALUE = "undefined" -DEVICE_DEFAULT_NAME = "P1 Meter" - - -# Config -CONF_OVERRIDE_POLL_INTERVAL = "override_poll_interval" -DEFAULT_OVERRIDE_POLL_INTERVAL = False - -CONF_POLL_INTERVAL_SECONDS = "poll_interval_seconds" -DEFAULT_POLL_INTERVAL_SECONDS = 10 +DEVICE_DEFAULT_NAME = "P1 Meter" \ No newline at end of file diff --git a/custom_components/homewizard_energy/sensor.py b/custom_components/homewizard_energy/sensor.py index d4c3848..955fc80 100644 --- a/custom_components/homewizard_energy/sensor.py +++ b/custom_components/homewizard_energy/sensor.py @@ -50,12 +50,8 @@ CONF_API, CONF_DATA, CONF_MODEL, - CONF_OVERRIDE_POLL_INTERVAL, - CONF_POLL_INTERVAL_SECONDS, CONF_SW_VERSION, COORDINATOR, - DEFAULT_OVERRIDE_POLL_INTERVAL, - DEFAULT_POLL_INTERVAL_SECONDS, DOMAIN, ) @@ -168,11 +164,6 @@ def get_update_interval(entry, energy_api): - if entry.options.get(CONF_OVERRIDE_POLL_INTERVAL, DEFAULT_OVERRIDE_POLL_INTERVAL): - return entry.options.get( - CONF_POLL_INTERVAL_SECONDS, DEFAULT_POLL_INTERVAL_SECONDS - ) - try: product_type = energy_api.device.product_type except AttributeError: From e141f1ab7e01eee4cbbc5d0b8428b06702f65caa Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Thu, 5 Aug 2021 09:46:11 +0200 Subject: [PATCH 07/22] Bring back update-interval based on device type --- .../homewizard_energy/__init__.py | 28 ++++++++++++++++++- custom_components/homewizard_energy/const.py | 8 +++++- custom_components/homewizard_energy/sensor.py | 23 --------------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/custom_components/homewizard_energy/__init__.py b/custom_components/homewizard_energy/__init__.py index eaa7c68..4bcd765 100644 --- a/custom_components/homewizard_energy/__init__.py +++ b/custom_components/homewizard_energy/__init__.py @@ -24,6 +24,10 @@ CONF_UNLOAD_CB, COORDINATOR, DOMAIN, + MODEL_KWH_1, + MODEL_KWH_3, + MODEL_P1, + MODEL_SOCKET, ) Logger = logging.getLogger(__name__) @@ -192,9 +196,31 @@ def __init__( ) -> None: self.api = api - update_interval = timedelta(seconds=10) + update_interval = self.get_update_interval() super().__init__(hass, Logger, name="", update_interval=update_interval) + def get_update_interval(self) -> timedelta: + + try: + product_type = self.api.device.product_type + except AttributeError: + product_type = "Unknown" + + if product_type == MODEL_P1: + try: + smr_version = self.api.data.smr_version + if smr_version == 50 : + return timedelta(seconds=1) + else: + return timedelta(seconds=5) + except AttributeError: + pass + + elif product_type in [MODEL_KWH_1, MODEL_KWH_3, MODEL_SOCKET]: + return timedelta(seconds=5) + + return timedelta(seconds=10) + async def _async_update_data(self) -> dict: """Fetch all device and sensor data from api.""" try: diff --git a/custom_components/homewizard_energy/const.py b/custom_components/homewizard_energy/const.py index 772fa55..1f22d58 100644 --- a/custom_components/homewizard_energy/const.py +++ b/custom_components/homewizard_energy/const.py @@ -40,4 +40,10 @@ # Default values. DEFAULT_STR_VALUE = "undefined" -DEVICE_DEFAULT_NAME = "P1 Meter" \ No newline at end of file +DEVICE_DEFAULT_NAME = "P1 Meter" + +# Device models +MODEL_P1 = "HWE-P1" +MODEL_KWH_1 = "SDM230-wifi" +MODEL_KWH_3 = "SDM630-wifi" +MODEL_SOCKET = "HWE-SKT" diff --git a/custom_components/homewizard_energy/sensor.py b/custom_components/homewizard_energy/sensor.py index 955fc80..9b1141c 100644 --- a/custom_components/homewizard_energy/sensor.py +++ b/custom_components/homewizard_energy/sensor.py @@ -162,29 +162,6 @@ ] -def get_update_interval(entry, energy_api): - - try: - product_type = energy_api.device.product_type - except AttributeError: - product_type = "Unknown" - - if product_type == "HWE-P1": - try: - smr_version = energy_api.data.smr_version - if smr_version == 50: - return 1 - else: - return 5 - except AttributeError: - pass - - elif product_type == "SDM230-wifi" or product_type == "SDM630-wifi": - return 1 - - return 10 - - async def async_setup_entry(hass, entry, async_add_entities): """Config entry example.""" Logger.info("Setting up sensor for HomeWizard Energy.") From 282f314973c946b6128cadae2889004f61fb723c Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Thu, 5 Aug 2021 22:09:55 +0200 Subject: [PATCH 08/22] Add support for socket --- .../homewizard_energy/__init__.py | 45 +++++- custom_components/homewizard_energy/const.py | 7 +- .../homewizard_energy/manifest.json | 2 +- custom_components/homewizard_energy/sensor.py | 42 +----- custom_components/homewizard_energy/switch.py | 130 ++++++++++++++++++ hacs.json | 2 +- 6 files changed, 178 insertions(+), 50 deletions(-) create mode 100644 custom_components/homewizard_energy/switch.py diff --git a/custom_components/homewizard_energy/__init__.py b/custom_components/homewizard_energy/__init__.py index 4bcd765..800ebfd 100644 --- a/custom_components/homewizard_energy/__init__.py +++ b/custom_components/homewizard_energy/__init__.py @@ -11,11 +11,15 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_VERSION, CONF_ID, CONF_STATE from homeassistant.core import Config, HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import entity_registry from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import slugify from .const import ( + ATTR_BRIGHTNESS, + ATTR_POWER_ON, + ATTR_SWITCHLOCK, CONF_API, CONF_DATA, CONF_MODEL, @@ -28,14 +32,13 @@ MODEL_KWH_3, MODEL_P1, MODEL_SOCKET, + PLATFORMS, ) Logger = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA) -PLATFORMS = ["sensor"] - async def async_setup(hass: HomeAssistant, config: dict): """Set up the Homewizard Energy component.""" @@ -132,7 +135,35 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): # Get api and do a initialization energy_api = aiohwenergy.HomeWizardEnergy(entry.data.get("host")) - await energy_api.initialize() + + # Validate connection + initialized = False + try: + with async_timeout.timeout(10): + await energy_api.initialize() + initialized = True + + except (asyncio.TimeoutError, aiohwenergy.RequestError): + Logger.error( + "Error connecting to the Energy device at %s", + energy_api._host, + ) + raise ConfigEntryNotReady + + except aiohwenergy.AioHwEnergyException: + Logger.exception("Unknown Energy API error occurred") + raise ConfigEntryNotReady + + except Exception: # pylint: disable=broad-except + Logger.exception( + "Unknown error connecting with Energy Device at %s", + energy_api._host["host"], + ) + return False + + finally: + if not initialized: + await energy_api.close() # Create coordinator coordinator = hass.data[DOMAIN][entry.data["unique_id"]][ @@ -209,7 +240,7 @@ def get_update_interval(self) -> timedelta: if product_type == MODEL_P1: try: smr_version = self.api.data.smr_version - if smr_version == 50 : + if smr_version == 50: return timedelta(seconds=1) else: return timedelta(seconds=5) @@ -246,9 +277,9 @@ async def _async_update_data(self) -> dict: if self.api.state is not None: data[CONF_STATE] = { - "power_on": self.api.state.power_on, - "switch_lock": self.api.state.switch_lock, - "brightness": self.api.state.brightness, + ATTR_POWER_ON: self.api.state.power_on, + ATTR_SWITCHLOCK: self.api.state.switch_lock, + ATTR_BRIGHTNESS: self.api.state.brightness, } except Exception as ex: diff --git a/custom_components/homewizard_energy/const.py b/custom_components/homewizard_energy/const.py index 1f22d58..a5a942a 100644 --- a/custom_components/homewizard_energy/const.py +++ b/custom_components/homewizard_energy/const.py @@ -6,7 +6,7 @@ DOMAIN = "homewizard_energy" COORDINATOR = "coordinator" MANUFACTURER_NAME = "HomeWizard" -PLATFORMS = ["sensor"] +PLATFORMS = ["sensor", "switch"] # Platform config. CONF_ENTITY_ID = const.CONF_ENTITY_ID @@ -38,6 +38,11 @@ ATTR_TOTAL_GAS_M3 = "total_gas_m3" ATTR_GAS_TIMESTAMP = "gas_timestamp" +# State attributes +ATTR_POWER_ON = "power_on" +ATTR_SWITCHLOCK = "switch_lock" +ATTR_BRIGHTNESS = "brightness" + # Default values. DEFAULT_STR_VALUE = "undefined" DEVICE_DEFAULT_NAME = "P1 Meter" diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index 5dc779b..f0d196f 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -11,7 +11,7 @@ "zeroconf" ], "requirements": [ - "aiohwenergy==0.2.2" + "aiohwenergy==0.2.3" ], "zeroconf": [ "_hwenergy._tcp.local." diff --git a/custom_components/homewizard_energy/sensor.py b/custom_components/homewizard_energy/sensor.py index 9b1141c..9510169 100644 --- a/custom_components/homewizard_energy/sensor.py +++ b/custom_components/homewizard_energy/sensor.py @@ -4,17 +4,13 @@ from typing import Any, Final import aiohwenergy -import async_timeout -import homeassistant.helpers.device_registry as dr from homeassistant.components.sensor import ( - ATTR_LAST_RESET, STATE_CLASS_MEASUREMENT, SensorEntity, SensorEntityDescription, ) from homeassistant.const import ( CONF_ID, - CONF_STATE, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, DEVICE_CLASS_SIGNAL_STRENGTH, @@ -24,11 +20,10 @@ POWER_WATT, VOLUME_CUBIC_METERS, ) -from homeassistant.exceptions import ConfigEntryNotReady + from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, - DataUpdateCoordinator, ) from homeassistant.util.dt import utc_from_timestamp @@ -167,36 +162,6 @@ async def async_setup_entry(hass, entry, async_add_entities): Logger.info("Setting up sensor for HomeWizard Energy.") energy_api = hass.data[DOMAIN][entry.data["unique_id"]][CONF_API] - - # Validate connection - initialized = False - try: - with async_timeout.timeout(10): - await energy_api.initialize() - initialized = True - - except (asyncio.TimeoutError, aiohwenergy.RequestError): - Logger.error( - "Error connecting to the Energy device at %s", - energy_api._host, - ) - raise ConfigEntryNotReady - - except aiohwenergy.AioHwEnergyException: - Logger.exception("Unknown Energy API error occurred") - raise ConfigEntryNotReady - - except Exception: # pylint: disable=broad-except - Logger.exception( - "Unknown error connecting with Energy Device at %s", - energy_api._host["host"], - ) - return False - - finally: - if not initialized: - await energy_api.close() - coordinator = hass.data[DOMAIN][entry.data["unique_id"]][COORDINATOR] # Fetch initial data so we have data when entities subscribe @@ -211,14 +176,12 @@ async def async_setup_entry(hass, entry, async_add_entities): return True else: - await energy_api.close() return False class HWEnergySensor(CoordinatorEntity, SensorEntity): - """Representation of a HomeWizard Energy""" + """Representation of a HomeWizard Energy Sensor""" - host = None name = None entry_data = None unique_id = None @@ -233,7 +196,6 @@ def __init__(self, coordinator, entry_data, description): # Config attributes. self.name = "%s %s" % (entry_data["custom_name"], description.name) - self.host = entry_data["host"] self.data_type = description.key self.unique_id = "%s_%s" % (entry_data["unique_id"], description.key) self._attr_last_reset = utc_from_timestamp(0) diff --git a/custom_components/homewizard_energy/switch.py b/custom_components/homewizard_energy/switch.py new file mode 100644 index 0000000..a439b52 --- /dev/null +++ b/custom_components/homewizard_energy/switch.py @@ -0,0 +1,130 @@ +"""Creates Homewizard Energy switch entities.""" + +from config.custom_components.homewizard_energy import Logger +from homeassistant.const import CONF_STATE +from typing import Any, Final + +import aiohwenergy + +from homeassistant.components.switch import ( + DEVICE_CLASS_OUTLET, + DEVICE_CLASS_SWITCH, + SwitchEntity, + SwitchEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import DOMAIN, HomeAssistant +import homeassistant.helpers.device_registry as dr +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, +) + +from homeassistant.const import CONF_ID + +from .const import ( + ATTR_POWER_ON, + ATTR_SWITCHLOCK, + CONF_MODEL, + CONF_SW_VERSION, + COORDINATOR, + DOMAIN, + CONF_API, +) + +SWITCHES: Final[list[SwitchEntityDescription]] = [ + SwitchEntityDescription( + key=ATTR_POWER_ON, name="On", device_class=DEVICE_CLASS_OUTLET + ), + SwitchEntityDescription( + key=ATTR_SWITCHLOCK, + name="Switch lock", + device_class=DEVICE_CLASS_SWITCH, + icon="mdi:lock", + ), +] + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up switches.""" + + energy_api = hass.data[DOMAIN][entry.data["unique_id"]][CONF_API] + coordinator = hass.data[DOMAIN][entry.data["unique_id"]][COORDINATOR] + + if energy_api.state != None: + entities = [] + for description in SWITCHES: + entities.append( + HWEnergySwitch(coordinator, entry.data, description, energy_api) + ) + + async_add_entities(entities) + + +class HWEnergySwitch(CoordinatorEntity, SwitchEntity): + """Representation of a HomeWizard Energy Switch""" + + unique_id = None + name = None + + def __init__(self, coordinator, entry_data, description, api) -> None: + """Initialize the switch.""" + super().__init__(coordinator) + self.entity_description = description + self.coordinator = coordinator + self.entry_data = entry_data + self.api = api + + # Config attributes + self.name = "%s %s" % (entry_data["custom_name"], description.name) + self.unique_id = "%s_%s" % (entry_data["unique_id"], description.key) + self.data_type = description.key + + @property + def data(self) -> dict[str, Any]: + """Return data from DataUpdateCoordinator.""" + return self.coordinator.data + + @property + def device_info(self) -> DeviceInfo: + """Return information about the device.""" + return { + "name": self.entry_data["custom_name"], + "manufacturer": "HomeWizard", + "sw_version": self.data[CONF_SW_VERSION], + "model": self.data[CONF_MODEL], + "identifiers": {(DOMAIN, self.data[CONF_ID])}, + } + + @property + def is_on(self): + """Return true if switch is on.""" + return self.data[CONF_STATE][self.data_type] + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the switch on.""" + if self.data_type == ATTR_POWER_ON: + await self.api.state.set(power_on=True) + elif self.data_type == ATTR_SWITCHLOCK: + await self.api.state.set(switch_lock=True) + else: + Logger.error("Internal error, unknown action") + + await self.coordinator.async_refresh() + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the switch off.""" + if self.data_type == ATTR_POWER_ON: + await self.api.state.set(power_on=False) + elif self.data_type == ATTR_SWITCHLOCK: + await self.api.state.set(switch_lock=False) + else: + Logger.error("Internal error, unknown action") + + await self.coordinator.async_refresh() diff --git a/hacs.json b/hacs.json index bfbe9e3..14768ad 100644 --- a/hacs.json +++ b/hacs.json @@ -1,7 +1,7 @@ { "name": "HomeWizard Energy", "render_readme": true, - "domains": ["sensor"], + "domains": ["sensor", "switch"], "homeassistant": "2021.8.0", "iot_class": "local_polling", "zip_release": true, From c9a28c02ed39bf274d9fa21f55d9ab985edfe3e6 Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Thu, 5 Aug 2021 22:09:55 +0200 Subject: [PATCH 09/22] Add support for socket --- .../homewizard_energy/__init__.py | 45 ++++++- .../homewizard_energy/config_flow.py | 5 +- custom_components/homewizard_energy/const.py | 7 +- .../homewizard_energy/manifest.json | 2 +- custom_components/homewizard_energy/sensor.py | 45 +------ custom_components/homewizard_energy/switch.py | 127 ++++++++++++++++++ hacs.json | 2 +- 7 files changed, 176 insertions(+), 57 deletions(-) create mode 100644 custom_components/homewizard_energy/switch.py diff --git a/custom_components/homewizard_energy/__init__.py b/custom_components/homewizard_energy/__init__.py index 4bcd765..800ebfd 100644 --- a/custom_components/homewizard_energy/__init__.py +++ b/custom_components/homewizard_energy/__init__.py @@ -11,11 +11,15 @@ from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_VERSION, CONF_ID, CONF_STATE from homeassistant.core import Config, HomeAssistant +from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers import entity_registry from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed from homeassistant.util import slugify from .const import ( + ATTR_BRIGHTNESS, + ATTR_POWER_ON, + ATTR_SWITCHLOCK, CONF_API, CONF_DATA, CONF_MODEL, @@ -28,14 +32,13 @@ MODEL_KWH_3, MODEL_P1, MODEL_SOCKET, + PLATFORMS, ) Logger = logging.getLogger(__name__) CONFIG_SCHEMA = vol.Schema({DOMAIN: vol.Schema({})}, extra=vol.ALLOW_EXTRA) -PLATFORMS = ["sensor"] - async def async_setup(hass: HomeAssistant, config: dict): """Set up the Homewizard Energy component.""" @@ -132,7 +135,35 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): # Get api and do a initialization energy_api = aiohwenergy.HomeWizardEnergy(entry.data.get("host")) - await energy_api.initialize() + + # Validate connection + initialized = False + try: + with async_timeout.timeout(10): + await energy_api.initialize() + initialized = True + + except (asyncio.TimeoutError, aiohwenergy.RequestError): + Logger.error( + "Error connecting to the Energy device at %s", + energy_api._host, + ) + raise ConfigEntryNotReady + + except aiohwenergy.AioHwEnergyException: + Logger.exception("Unknown Energy API error occurred") + raise ConfigEntryNotReady + + except Exception: # pylint: disable=broad-except + Logger.exception( + "Unknown error connecting with Energy Device at %s", + energy_api._host["host"], + ) + return False + + finally: + if not initialized: + await energy_api.close() # Create coordinator coordinator = hass.data[DOMAIN][entry.data["unique_id"]][ @@ -209,7 +240,7 @@ def get_update_interval(self) -> timedelta: if product_type == MODEL_P1: try: smr_version = self.api.data.smr_version - if smr_version == 50 : + if smr_version == 50: return timedelta(seconds=1) else: return timedelta(seconds=5) @@ -246,9 +277,9 @@ async def _async_update_data(self) -> dict: if self.api.state is not None: data[CONF_STATE] = { - "power_on": self.api.state.power_on, - "switch_lock": self.api.state.switch_lock, - "brightness": self.api.state.brightness, + ATTR_POWER_ON: self.api.state.power_on, + ATTR_SWITCHLOCK: self.api.state.switch_lock, + ATTR_BRIGHTNESS: self.api.state.brightness, } except Exception as ex: diff --git a/custom_components/homewizard_energy/config_flow.py b/custom_components/homewizard_energy/config_flow.py index e2c3db1..5119f3a 100644 --- a/custom_components/homewizard_energy/config_flow.py +++ b/custom_components/homewizard_energy/config_flow.py @@ -15,10 +15,7 @@ from voluptuous import All, Length, Required, Schema from voluptuous.util import Lower -from .const import ( - CONF_IP_ADDRESS, - DOMAIN, -) +from .const import CONF_IP_ADDRESS, DOMAIN Logger = logging.getLogger(__name__) diff --git a/custom_components/homewizard_energy/const.py b/custom_components/homewizard_energy/const.py index 1f22d58..a5a942a 100644 --- a/custom_components/homewizard_energy/const.py +++ b/custom_components/homewizard_energy/const.py @@ -6,7 +6,7 @@ DOMAIN = "homewizard_energy" COORDINATOR = "coordinator" MANUFACTURER_NAME = "HomeWizard" -PLATFORMS = ["sensor"] +PLATFORMS = ["sensor", "switch"] # Platform config. CONF_ENTITY_ID = const.CONF_ENTITY_ID @@ -38,6 +38,11 @@ ATTR_TOTAL_GAS_M3 = "total_gas_m3" ATTR_GAS_TIMESTAMP = "gas_timestamp" +# State attributes +ATTR_POWER_ON = "power_on" +ATTR_SWITCHLOCK = "switch_lock" +ATTR_BRIGHTNESS = "brightness" + # Default values. DEFAULT_STR_VALUE = "undefined" DEVICE_DEFAULT_NAME = "P1 Meter" diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index 5dc779b..f0d196f 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -11,7 +11,7 @@ "zeroconf" ], "requirements": [ - "aiohwenergy==0.2.2" + "aiohwenergy==0.2.3" ], "zeroconf": [ "_hwenergy._tcp.local." diff --git a/custom_components/homewizard_energy/sensor.py b/custom_components/homewizard_energy/sensor.py index 9b1141c..8cec355 100644 --- a/custom_components/homewizard_energy/sensor.py +++ b/custom_components/homewizard_energy/sensor.py @@ -4,17 +4,13 @@ from typing import Any, Final import aiohwenergy -import async_timeout -import homeassistant.helpers.device_registry as dr from homeassistant.components.sensor import ( - ATTR_LAST_RESET, STATE_CLASS_MEASUREMENT, SensorEntity, SensorEntityDescription, ) from homeassistant.const import ( CONF_ID, - CONF_STATE, DEVICE_CLASS_ENERGY, DEVICE_CLASS_POWER, DEVICE_CLASS_SIGNAL_STRENGTH, @@ -24,12 +20,8 @@ POWER_WATT, VOLUME_CUBIC_METERS, ) -from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.helpers.entity import DeviceInfo -from homeassistant.helpers.update_coordinator import ( - CoordinatorEntity, - DataUpdateCoordinator, -) +from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.util.dt import utc_from_timestamp from .const import ( @@ -167,36 +159,6 @@ async def async_setup_entry(hass, entry, async_add_entities): Logger.info("Setting up sensor for HomeWizard Energy.") energy_api = hass.data[DOMAIN][entry.data["unique_id"]][CONF_API] - - # Validate connection - initialized = False - try: - with async_timeout.timeout(10): - await energy_api.initialize() - initialized = True - - except (asyncio.TimeoutError, aiohwenergy.RequestError): - Logger.error( - "Error connecting to the Energy device at %s", - energy_api._host, - ) - raise ConfigEntryNotReady - - except aiohwenergy.AioHwEnergyException: - Logger.exception("Unknown Energy API error occurred") - raise ConfigEntryNotReady - - except Exception: # pylint: disable=broad-except - Logger.exception( - "Unknown error connecting with Energy Device at %s", - energy_api._host["host"], - ) - return False - - finally: - if not initialized: - await energy_api.close() - coordinator = hass.data[DOMAIN][entry.data["unique_id"]][COORDINATOR] # Fetch initial data so we have data when entities subscribe @@ -211,14 +173,12 @@ async def async_setup_entry(hass, entry, async_add_entities): return True else: - await energy_api.close() return False class HWEnergySensor(CoordinatorEntity, SensorEntity): - """Representation of a HomeWizard Energy""" + """Representation of a HomeWizard Energy Sensor""" - host = None name = None entry_data = None unique_id = None @@ -233,7 +193,6 @@ def __init__(self, coordinator, entry_data, description): # Config attributes. self.name = "%s %s" % (entry_data["custom_name"], description.name) - self.host = entry_data["host"] self.data_type = description.key self.unique_id = "%s_%s" % (entry_data["unique_id"], description.key) self._attr_last_reset = utc_from_timestamp(0) diff --git a/custom_components/homewizard_energy/switch.py b/custom_components/homewizard_energy/switch.py new file mode 100644 index 0000000..28d6295 --- /dev/null +++ b/custom_components/homewizard_energy/switch.py @@ -0,0 +1,127 @@ +"""Creates Homewizard Energy switch entities.""" + +from typing import Any, Final + +import aiohwenergy +import homeassistant.helpers.device_registry as dr +from config.custom_components.homewizard_energy import Logger +from homeassistant.components.switch import ( + DEVICE_CLASS_OUTLET, + DEVICE_CLASS_SWITCH, + SwitchEntity, + SwitchEntityDescription, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import CONF_ID, CONF_STATE +from homeassistant.core import DOMAIN, HomeAssistant +from homeassistant.helpers.entity import DeviceInfo +from homeassistant.helpers.entity_platform import AddEntitiesCallback +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity, + DataUpdateCoordinator, +) + +from .const import ( + ATTR_POWER_ON, + ATTR_SWITCHLOCK, + CONF_API, + CONF_MODEL, + CONF_SW_VERSION, + COORDINATOR, + DOMAIN, +) + +SWITCHES: Final[list[SwitchEntityDescription]] = [ + SwitchEntityDescription( + key=ATTR_POWER_ON, name="On", device_class=DEVICE_CLASS_OUTLET + ), + SwitchEntityDescription( + key=ATTR_SWITCHLOCK, + name="Switch lock", + device_class=DEVICE_CLASS_SWITCH, + icon="mdi:lock", + ), +] + + +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up switches.""" + + energy_api = hass.data[DOMAIN][entry.data["unique_id"]][CONF_API] + coordinator = hass.data[DOMAIN][entry.data["unique_id"]][COORDINATOR] + + if energy_api.state != None: + entities = [] + for description in SWITCHES: + entities.append( + HWEnergySwitch(coordinator, entry.data, description, energy_api) + ) + + async_add_entities(entities) + + +class HWEnergySwitch(CoordinatorEntity, SwitchEntity): + """Representation of a HomeWizard Energy Switch""" + + unique_id = None + name = None + + def __init__(self, coordinator, entry_data, description, api) -> None: + """Initialize the switch.""" + super().__init__(coordinator) + self.entity_description = description + self.coordinator = coordinator + self.entry_data = entry_data + self.api = api + + # Config attributes + self.name = "%s %s" % (entry_data["custom_name"], description.name) + self.unique_id = "%s_%s" % (entry_data["unique_id"], description.key) + self.data_type = description.key + + @property + def data(self) -> dict[str, Any]: + """Return data from DataUpdateCoordinator.""" + return self.coordinator.data + + @property + def device_info(self) -> DeviceInfo: + """Return information about the device.""" + return { + "name": self.entry_data["custom_name"], + "manufacturer": "HomeWizard", + "sw_version": self.data[CONF_SW_VERSION], + "model": self.data[CONF_MODEL], + "identifiers": {(DOMAIN, self.data[CONF_ID])}, + } + + @property + def is_on(self): + """Return true if switch is on.""" + return self.data[CONF_STATE][self.data_type] + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the switch on.""" + if self.data_type == ATTR_POWER_ON: + await self.api.state.set(power_on=True) + elif self.data_type == ATTR_SWITCHLOCK: + await self.api.state.set(switch_lock=True) + else: + Logger.error("Internal error, unknown action") + + await self.coordinator.async_refresh() + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the switch off.""" + if self.data_type == ATTR_POWER_ON: + await self.api.state.set(power_on=False) + elif self.data_type == ATTR_SWITCHLOCK: + await self.api.state.set(switch_lock=False) + else: + Logger.error("Internal error, unknown action") + + await self.coordinator.async_refresh() diff --git a/hacs.json b/hacs.json index bfbe9e3..14768ad 100644 --- a/hacs.json +++ b/hacs.json @@ -1,7 +1,7 @@ { "name": "HomeWizard Energy", "render_readme": true, - "domains": ["sensor"], + "domains": ["sensor", "switch"], "homeassistant": "2021.8.0", "iot_class": "local_polling", "zip_release": true, From d5835a85989e82cb17b21770d9c18286276868cc Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Thu, 5 Aug 2021 22:30:57 +0200 Subject: [PATCH 10/22] bumb version --- custom_components/homewizard_energy/manifest.json | 2 +- readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index f0d196f..712f5fd 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -1,7 +1,7 @@ { "domain": "homewizard_energy", "name": "HomeWizard Energy", - "version": "0.8.1", + "version": "0.9.2-beta-2", "documentation": "https://github.com/DCSBL/ha-homewizard-energy", "issue_tracker": "https://github.com/DCSBL/ha-homewizard-energy/issues", "codeowners": [ diff --git a/readme.md b/readme.md index 1fdfbd9..51c5ef3 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://github.com/custom-components/hacs) -[![Version](https://img.shields.io/badge/Version-0.8.1-blue.svg)](https://github.com/DCSBL/ha-homewizard-energy) +[![Version](https://img.shields.io/badge/Version-0.9.2-beta-2-blue.svg)](https://github.com/DCSBL/ha-homewizard-energy) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/Create%20release/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/Validation%20And%20Formatting/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/CodeQL/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) From 9d58e20057f88c6bd85bf6de4b1c6906c0a33536 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Thu, 5 Aug 2021 22:32:28 +0200 Subject: [PATCH 11/22] Update combined.yml --- .github/workflows/combined.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/combined.yml b/.github/workflows/combined.yml index dd7cc81..4a6125e 100644 --- a/.github/workflows/combined.yml +++ b/.github/workflows/combined.yml @@ -1,9 +1,7 @@ name: "Validation And Formatting" on: - push: pull_request: - schedule: - - cron: '0 0 * * *' + jobs: ci: From 8292484859a65b2e4989322df6ceb26865dc418a Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Thu, 5 Aug 2021 22:43:32 +0200 Subject: [PATCH 12/22] Fix: Wrong import --- custom_components/homewizard_energy/manifest.json | 2 +- custom_components/homewizard_energy/switch.py | 3 ++- readme.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index 712f5fd..bf82174 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -1,7 +1,7 @@ { "domain": "homewizard_energy", "name": "HomeWizard Energy", - "version": "0.9.2-beta-2", + "version": "0.9.0-beta-3", "documentation": "https://github.com/DCSBL/ha-homewizard-energy", "issue_tracker": "https://github.com/DCSBL/ha-homewizard-energy/issues", "codeowners": [ diff --git a/custom_components/homewizard_energy/switch.py b/custom_components/homewizard_energy/switch.py index 28d6295..7f3b710 100644 --- a/custom_components/homewizard_energy/switch.py +++ b/custom_components/homewizard_energy/switch.py @@ -4,7 +4,6 @@ import aiohwenergy import homeassistant.helpers.device_registry as dr -from config.custom_components.homewizard_energy import Logger from homeassistant.components.switch import ( DEVICE_CLASS_OUTLET, DEVICE_CLASS_SWITCH, @@ -31,6 +30,8 @@ DOMAIN, ) +Logger = logging.getLogger(__name__) + SWITCHES: Final[list[SwitchEntityDescription]] = [ SwitchEntityDescription( key=ATTR_POWER_ON, name="On", device_class=DEVICE_CLASS_OUTLET diff --git a/readme.md b/readme.md index 51c5ef3..3ce73ec 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://github.com/custom-components/hacs) -[![Version](https://img.shields.io/badge/Version-0.9.2-beta-2-blue.svg)](https://github.com/DCSBL/ha-homewizard-energy) +[![Version](https://img.shields.io/badge/Version-0.9.0-beta-3-blue.svg)](https://github.com/DCSBL/ha-homewizard-energy) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/Create%20release/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/Validation%20And%20Formatting/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/CodeQL/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) From 3e321fe4f0970fd6ba89890964fd5f473efe8f7e Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Thu, 5 Aug 2021 22:49:21 +0200 Subject: [PATCH 13/22] AImport logging --- custom_components/homewizard_energy/switch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/custom_components/homewizard_energy/switch.py b/custom_components/homewizard_energy/switch.py index 7f3b710..17d8c9d 100644 --- a/custom_components/homewizard_energy/switch.py +++ b/custom_components/homewizard_energy/switch.py @@ -3,6 +3,7 @@ from typing import Any, Final import aiohwenergy +import logging import homeassistant.helpers.device_registry as dr from homeassistant.components.switch import ( DEVICE_CLASS_OUTLET, From cd31c1dab9a40654977b919566e9c0dd3592e2cb Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Thu, 5 Aug 2021 22:49:55 +0200 Subject: [PATCH 14/22] Bumb version --- custom_components/homewizard_energy/manifest.json | 2 +- readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index bf82174..55d592e 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -1,7 +1,7 @@ { "domain": "homewizard_energy", "name": "HomeWizard Energy", - "version": "0.9.0-beta-3", + "version": "0.9.0-beta-4", "documentation": "https://github.com/DCSBL/ha-homewizard-energy", "issue_tracker": "https://github.com/DCSBL/ha-homewizard-energy/issues", "codeowners": [ diff --git a/readme.md b/readme.md index 3ce73ec..47d87e0 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://github.com/custom-components/hacs) -[![Version](https://img.shields.io/badge/Version-0.9.0-beta-3-blue.svg)](https://github.com/DCSBL/ha-homewizard-energy) +[![Version](https://img.shields.io/badge/Version-0.9.0-beta-4-blue.svg)](https://github.com/DCSBL/ha-homewizard-energy) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/Create%20release/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/Validation%20And%20Formatting/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/CodeQL/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) From e01a8906d2a14fb283c85055bab8c2b2574b06f4 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Mon, 9 Aug 2021 16:35:25 +0200 Subject: [PATCH 15/22] Fix: Type not subscriptable --- custom_components/homewizard_energy/sensor.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/custom_components/homewizard_energy/sensor.py b/custom_components/homewizard_energy/sensor.py index 9b1141c..6914811 100644 --- a/custom_components/homewizard_energy/sensor.py +++ b/custom_components/homewizard_energy/sensor.py @@ -1,4 +1,6 @@ """Creates Homewizard Energy sensor entities.""" +from __future__ import annotations + import asyncio import logging from typing import Any, Final @@ -57,7 +59,7 @@ Logger = logging.getLogger(__name__) -SENSORS: Final[list[SensorEntityDescription]] = [ +SENSORS: Final[tuple[SensorEntityDescription, ...]] = ( SensorEntityDescription( key=ATTR_SMR_VERSION, name="SMR version", @@ -159,7 +161,7 @@ icon="mdi:timeline-clock", device_class=DEVICE_CLASS_TIMESTAMP, ), -] +) async def async_setup_entry(hass, entry, async_add_entities): From f177f7e93ffd44a2415e1c4d30ff549c93d5f8db Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Mon, 9 Aug 2021 16:43:15 +0200 Subject: [PATCH 16/22] Bumb version --- custom_components/homewizard_energy/manifest.json | 2 +- readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index 55d592e..492306b 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -1,7 +1,7 @@ { "domain": "homewizard_energy", "name": "HomeWizard Energy", - "version": "0.9.0-beta-4", + "version": "0.9.0-beta-5", "documentation": "https://github.com/DCSBL/ha-homewizard-energy", "issue_tracker": "https://github.com/DCSBL/ha-homewizard-energy/issues", "codeowners": [ diff --git a/readme.md b/readme.md index 47d87e0..aa0e170 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg)](https://github.com/custom-components/hacs) -[![Version](https://img.shields.io/badge/Version-0.9.0-beta-4-blue.svg)](https://github.com/DCSBL/ha-homewizard-energy) +[![Version](https://img.shields.io/badge/Version-0.9.0-beta-5-blue.svg)](https://github.com/DCSBL/ha-homewizard-energy) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/Create%20release/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/Validation%20And%20Formatting/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) [![Actions Status](https://github.com/DCSBL/ha-homewizard-energy/workflows/CodeQL/badge.svg)](https://github.com/DCSBL/ha-homewizard-energy/actions) From 612dfa8c3031073d24938bc3bdbd00eac0f1fd00 Mon Sep 17 00:00:00 2001 From: Duco Sebel Date: Mon, 9 Aug 2021 22:12:50 +0200 Subject: [PATCH 17/22] Set version to 0.0.0 as it is autofilled by generator --- custom_components/homewizard_energy/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index 701dea5..15e8f2f 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -1,7 +1,7 @@ { "domain": "homewizard_energy", "name": "HomeWizard Energy", - "version": "0.8.2", + "version": "0.0.0", "documentation": "https://github.com/DCSBL/ha-homewizard-energy", "issue_tracker": "https://github.com/DCSBL/ha-homewizard-energy/issues", "codeowners": [ From ea24afd92c496cad11632e7ad885870fe9830fb0 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Tue, 10 Aug 2021 22:04:28 +0200 Subject: [PATCH 18/22] Rollback requirement --- custom_components/homewizard_energy/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/homewizard_energy/manifest.json b/custom_components/homewizard_energy/manifest.json index 15e8f2f..2f12aef 100644 --- a/custom_components/homewizard_energy/manifest.json +++ b/custom_components/homewizard_energy/manifest.json @@ -11,7 +11,7 @@ "zeroconf" ], "requirements": [ - "aiohwenergy==0.1.1" + "aiohwenergy==0.2.3" ], "zeroconf": [ "_hwenergy._tcp.local." From d40641441c680870c1c8c121bbe27b563df969c6 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Wed, 11 Aug 2021 21:48:05 +0200 Subject: [PATCH 19/22] Update en.json --- .../homewizard_energy/translations/en.json | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/custom_components/homewizard_energy/translations/en.json b/custom_components/homewizard_energy/translations/en.json index dc2d49e..1cd1990 100644 --- a/custom_components/homewizard_energy/translations/en.json +++ b/custom_components/homewizard_energy/translations/en.json @@ -17,22 +17,12 @@ "data": { "ip_address": "IP Address" }, - "description": "Set up your HomeWizard Energy P1 meter to integrate with Home Assistant.", + "description": "Enter the IP address of your HomeWizard Energy device to integrate with Home Assistant.", "title": "Configure device" }, "discovery_confirm": { "description": "Please enter a name for your {name}:" } } - }, - "options": { - "step": { - "init": { - "data": { - "override_poll_interval": "Override default poll interval", - "poll_interval_seconds": "Poll interval (seconds)" - } - } - } } -} \ No newline at end of file +} From c07370a5c3f50f2974bb95d46039912b701017f6 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Wed, 11 Aug 2021 21:50:17 +0200 Subject: [PATCH 20/22] Update nl.json --- .../homewizard_energy/translations/nl.json | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/custom_components/homewizard_energy/translations/nl.json b/custom_components/homewizard_energy/translations/nl.json index 78e620e..e10581f 100644 --- a/custom_components/homewizard_energy/translations/nl.json +++ b/custom_components/homewizard_energy/translations/nl.json @@ -11,20 +11,19 @@ "manual_config_unsupported_api_version": "API versie niet ondersteund", "invalid_discovery_parameters": "Fout: invalid_discovery_parameters" }, + "step": { + "user": + { + "data": { + "ip_address": "IP Address" + }, + "description": "Vul het IP adres in van je HomeWizard Energy apparaat", + "title": "Apparaat toevoegen" + }, "discovery_confirm": { "description": "Geef een naam aan je apparaat:" } } - }, - "options": { - "step": { - "init": { - "data": { - "override_poll_interval": "Gebruik eigen update frequentie", - "poll_interval_seconds": "Update frequentie (in seconden)" - } - } - } } -} \ No newline at end of file +} From 4a7a37c52b2623e2a88132ae73c3907338b3a03b Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Thu, 12 Aug 2021 21:45:04 +0200 Subject: [PATCH 21/22] Rename default name of 'switch' --- custom_components/homewizard_energy/switch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/homewizard_energy/switch.py b/custom_components/homewizard_energy/switch.py index 17d8c9d..012664e 100644 --- a/custom_components/homewizard_energy/switch.py +++ b/custom_components/homewizard_energy/switch.py @@ -35,7 +35,7 @@ SWITCHES: Final[list[SwitchEntityDescription]] = [ SwitchEntityDescription( - key=ATTR_POWER_ON, name="On", device_class=DEVICE_CLASS_OUTLET + key=ATTR_POWER_ON, name="Switch", device_class=DEVICE_CLASS_OUTLET ), SwitchEntityDescription( key=ATTR_SWITCHLOCK, From f49611d274076a8e7908ebead84b26089d2f0737 Mon Sep 17 00:00:00 2001 From: Duco Sebel <74970928+DCSBL@users.noreply.github.com> Date: Thu, 12 Aug 2021 22:53:03 +0200 Subject: [PATCH 22/22] Create update_manifest.py --- .github/helpers/update_manifest.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/helpers/update_manifest.py diff --git a/.github/helpers/update_manifest.py b/.github/helpers/update_manifest.py new file mode 100644 index 0000000..abbe2f5 --- /dev/null +++ b/.github/helpers/update_manifest.py @@ -0,0 +1,27 @@ +"""Update the manifest file.""" +"""Idea from https://github.com/hacs/integration/blob/main/manage/update_manifest.py""" + +import sys +import json +import os + + +def update_manifest(): + """Update the manifest file.""" + version = "0.0.0" + for index, value in enumerate(sys.argv): + if value in ["--version", "-V"]: + version = sys.argv[index + 1] + + with open(f"{os.getcwd()}/custom_components/homewizard_energy/manifest.json") as manifestfile: + manifest = json.load(manifestfile) + + manifest["version"] = version + + with open( + f"{os.getcwd()}/custom_components/homewizard_energy/manifest.json", "w" + ) as manifestfile: + manifestfile.write(json.dumps(manifest, indent=4, sort_keys=True)) + + +update_manifest()