From 1372cbd098039eedfeb5e1d6cc5a7d00a6599a9f Mon Sep 17 00:00:00 2001 From: Konstantin Deev Date: Thu, 24 Aug 2023 10:11:27 -0500 Subject: [PATCH 1/9] use newer version of pyonwater --- .pre-commit-config.yaml | 10 +++++++ custom_components/eyeonwater/binary_sensor.py | 28 +++++++++++++------ custom_components/eyeonwater/config_flow.py | 2 +- custom_components/eyeonwater/manifest.json | 4 +-- custom_components/eyeonwater/sensor.py | 28 +++++++++++-------- mypy.ini | 11 ++++++++ 6 files changed, 59 insertions(+), 24 deletions(-) create mode 100644 mypy.ini diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c3ad7d8..479500b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,6 +7,7 @@ repos: - id: end-of-file-fixer - id: mixed-line-ending args: ["--fix=lf"] + # - repo: https://github.com/astral-sh/ruff-pre-commit # rev: v0.0.284 # hooks: @@ -22,3 +23,12 @@ repos: rev: 5.12.0 hooks: - id: isort + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.5.1 + hooks: + - id: mypy + language_version: python + additional_dependencies: + - pydantic~=2.0 + - types-pytz diff --git a/custom_components/eyeonwater/binary_sensor.py b/custom_components/eyeonwater/binary_sensor.py index 7f365fe..0eab0f5 100644 --- a/custom_components/eyeonwater/binary_sensor.py +++ b/custom_components/eyeonwater/binary_sensor.py @@ -18,36 +18,43 @@ FLAG_SENSORS = [ BinarySensorEntityDescription( - key="Leak", + name="Leak", + key="leak`", translation_key="leak", device_class=BinarySensorDeviceClass.MOISTURE, ), BinarySensorEntityDescription( - key="EmptyPipe", + name="EmptyPipe", + key="empty_pipe", translation_key="emptypipe", device_class=BinarySensorDeviceClass.PROBLEM, ), BinarySensorEntityDescription( - key="Tamper", + name="Tamper", + key="tamper", translation_key="tamper", device_class=BinarySensorDeviceClass.TAMPER, ), BinarySensorEntityDescription( - key="CoverRemoved", + name="CoverRemoved", + key="cover_removed", translation_key="coverremoved", device_class=BinarySensorDeviceClass.TAMPER, ), BinarySensorEntityDescription( - key="ReverseFlow", + name="ReverseFlow", + key="reverse_flow", translation_key="reverseflow", device_class=BinarySensorDeviceClass.PROBLEM, ), BinarySensorEntityDescription( - key="LowBattery", + name="LowBattery", + key="low_battery", device_class=BinarySensorDeviceClass.BATTERY, ), BinarySensorEntityDescription( - key="BatteryCharging", + name="BatteryCharging", + key="battery_charging", device_class=BinarySensorDeviceClass.BATTERY_CHARGING, ), ] @@ -83,19 +90,22 @@ def __init__( self.meter = meter self._state = False self._available = False - self._attr_unique_id = f"{description.key}_{self.meter.meter_uuid}" + self._attr_unique_id = f"{description.name}_{self.meter.meter_uuid}" self._attr_is_on = self._state self._attr_device_info = DeviceInfo( identifiers={(DOMAIN, self.meter.meter_uuid)}, name=f"Water Meter {self.meter.meter_id}", ) + def get_flag(self, key: str) -> bool: + return self.meter.meter_info.reading.flags.__dict__[self.entity_description.key] + @callback def _state_update(self): """Call when the coordinator has an update.""" self._available = self.coordinator.last_update_success if self._available: - self._state = self.meter.get_flags(self.entity_description.key) + self._state = self.get_flags(self.entity_description.key) self.async_write_ha_state() async def async_added_to_hass(self): diff --git a/custom_components/eyeonwater/config_flow.py b/custom_components/eyeonwater/config_flow.py index 7b881ac..d567e00 100644 --- a/custom_components/eyeonwater/config_flow.py +++ b/custom_components/eyeonwater/config_flow.py @@ -78,7 +78,7 @@ async def validate_input(hass: core.HomeAssistant, data): return {"title": account.username} -class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): +class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): # type: ignore """Handle a config flow for EyeOnWater.""" VERSION = 1 diff --git a/custom_components/eyeonwater/manifest.json b/custom_components/eyeonwater/manifest.json index 835d2af..a642c75 100644 --- a/custom_components/eyeonwater/manifest.json +++ b/custom_components/eyeonwater/manifest.json @@ -6,6 +6,6 @@ "dependencies": ["recorder"], "documentation": "https://github.com/kdeyev/eyeonwater", "iot_class": "cloud_polling", - "requirements": ["pyonwater==0.1.2"], - "version": "2.0.2" + "requirements": ["pyonwater==0.2.0"], + "version": "2.0.4" } diff --git a/custom_components/eyeonwater/sensor.py b/custom_components/eyeonwater/sensor.py index 5503fc8..91d4cda 100644 --- a/custom_components/eyeonwater/sensor.py +++ b/custom_components/eyeonwater/sensor.py @@ -1,8 +1,9 @@ """Support for EyeOnWater sensors.""" import datetime import logging +from typing import Any -from pyonwater import Meter +from pyonwater import DataPoint, Meter import pytz from homeassistant.components.recorder import get_instance @@ -30,7 +31,7 @@ _LOGGER.addHandler(logging.StreamHandler()) -def get_statistics_id(meter) -> str: +def get_statistics_id(meter: Meter) -> str: return f"sensor.water_meter_{meter.meter_id}" @@ -80,7 +81,10 @@ class EyeOnWaterSensor(CoordinatorEntity, SensorEntity): # _attr_state_class = SensorStateClass.TOTAL_INCREASING def __init__( - self, meter: Meter, last_imported_time, coordinator: DataUpdateCoordinator + self, + meter: Meter, + last_imported_time: datetime.datetime, + coordinator: DataUpdateCoordinator, ) -> None: """Initialize the sensor.""" super().__init__(coordinator) @@ -93,7 +97,7 @@ def __init__( identifiers={(DOMAIN, self.meter.meter_uuid)}, name=f"{WATER_METER_NAME} {self.meter.meter_id}", ) - self._last_historical_data = [] + self._last_historical_data: list[DataPoint] = [] self._last_imported_time = last_imported_time @property @@ -107,9 +111,9 @@ def native_value(self): return self._state @property - def extra_state_attributes(self): + def extra_state_attributes(self) -> dict[str, Any]: """Return the device specific state attributes.""" - return self.meter.attributes["register_0"] + return self.meter.meter_info.reading.dict() @callback def _state_update(self): @@ -121,11 +125,11 @@ def _state_update(self): self._last_historical_data = self.meter.last_historical_data.copy() if self._last_imported_time and self._last_historical_data: _LOGGER.info( - f"_last_imported_time {self._last_imported_time} - self._last_historical_data {self._last_historical_data[-1]['dt']}" + f"_last_imported_time {self._last_imported_time} - self._last_historical_data {self._last_historical_data[-1].dt}" ) self._last_historical_data = list( filter( - lambda r: r["dt"] > self._last_imported_time, + lambda r: r.dt > self._last_imported_time, self._last_historical_data, ) ) @@ -136,7 +140,7 @@ def _state_update(self): if self._last_historical_data: self.import_historical_data() - self._last_imported_time = self._last_historical_data[-1]["dt"] + self._last_imported_time = self._last_historical_data[-1].dt self.async_write_ha_state() @@ -163,9 +167,9 @@ def import_historical_data(self): statistics = [ StatisticData( - start=row["dt"], - sum=row["reading"], - state=row["reading"], + start=row.dt, + sum=row.reading, + state=row.reading, ) for row in self._last_historical_data ] diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..123d708 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,11 @@ +[tool.mypy] +plugins = ["pydantic.mypy"] +strict = true +disallow_untyped_calls = true +disallow_untyped_defs = true +disallow_incomplete_defs = true +warn_redundant_casts = true +warn_unused_ignores = true +no_implicit_optional = true +show_error_codes = true +implicit_reexport = true From 4f322edf9a429062dfac215a1df6f3c58a1f0e0d Mon Sep 17 00:00:00 2001 From: Konstantin Deev Date: Thu, 24 Aug 2023 10:26:12 -0500 Subject: [PATCH 2/9] update pyonwater --- custom_components/eyeonwater/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/eyeonwater/manifest.json b/custom_components/eyeonwater/manifest.json index a642c75..c2c94b0 100644 --- a/custom_components/eyeonwater/manifest.json +++ b/custom_components/eyeonwater/manifest.json @@ -6,6 +6,6 @@ "dependencies": ["recorder"], "documentation": "https://github.com/kdeyev/eyeonwater", "iot_class": "cloud_polling", - "requirements": ["pyonwater==0.2.0"], + "requirements": ["pyonwater==0.2.1"], "version": "2.0.4" } From a581c176149fabb281790f1e2af63aa88f7a80f0 Mon Sep 17 00:00:00 2001 From: Konstantin Deev Date: Thu, 24 Aug 2023 22:07:11 -0500 Subject: [PATCH 3/9] organize entity description --- custom_components/eyeonwater/binary_sensor.py | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/custom_components/eyeonwater/binary_sensor.py b/custom_components/eyeonwater/binary_sensor.py index 0eab0f5..3a15a18 100644 --- a/custom_components/eyeonwater/binary_sensor.py +++ b/custom_components/eyeonwater/binary_sensor.py @@ -1,5 +1,6 @@ """Support for EyeOnWater binary sensors.""" from pyonwater import Meter +from dataclasses import dataclass from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -16,45 +17,55 @@ from .const import DATA_COORDINATOR, DATA_SMART_METER, DOMAIN +@dataclass +class Description: + name: str + key: str + translation_key: str + device_class: BinarySensorDeviceClass + + FLAG_SENSORS = [ - BinarySensorEntityDescription( + Description( name="Leak", key="leak`", translation_key="leak", device_class=BinarySensorDeviceClass.MOISTURE, ), - BinarySensorEntityDescription( + Description( name="EmptyPipe", key="empty_pipe", translation_key="emptypipe", device_class=BinarySensorDeviceClass.PROBLEM, ), - BinarySensorEntityDescription( + Description( name="Tamper", key="tamper", translation_key="tamper", device_class=BinarySensorDeviceClass.TAMPER, ), - BinarySensorEntityDescription( + Description( name="CoverRemoved", key="cover_removed", translation_key="coverremoved", device_class=BinarySensorDeviceClass.TAMPER, ), - BinarySensorEntityDescription( + Description( name="ReverseFlow", key="reverse_flow", translation_key="reverseflow", device_class=BinarySensorDeviceClass.PROBLEM, ), - BinarySensorEntityDescription( + Description( name="LowBattery", key="low_battery", + translation_key="lowbattery", device_class=BinarySensorDeviceClass.BATTERY, ), - BinarySensorEntityDescription( + Description( name="BatteryCharging", key="battery_charging", + translation_key="batterycharging", device_class=BinarySensorDeviceClass.BATTERY_CHARGING, ), ] @@ -82,11 +93,11 @@ def __init__( self, meter: Meter, coordinator: DataUpdateCoordinator, - description: BinarySensorEntityDescription, + description: Description, ) -> None: """Initialize the sensor.""" super().__init__(coordinator) - self.entity_description = description + self.entity_description = BinarySensorEntityDescription(key=description.key, device_class=description.device_class, translation_key=description.translation_key) self.meter = meter self._state = False self._available = False From 281a57149d3d7c08d8a36bef2ca0a1e8720b6cee Mon Sep 17 00:00:00 2001 From: Konstantin Deev Date: Thu, 24 Aug 2023 22:08:39 -0500 Subject: [PATCH 4/9] organize entity description --- custom_components/eyeonwater/binary_sensor.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/custom_components/eyeonwater/binary_sensor.py b/custom_components/eyeonwater/binary_sensor.py index 3a15a18..8006f8f 100644 --- a/custom_components/eyeonwater/binary_sensor.py +++ b/custom_components/eyeonwater/binary_sensor.py @@ -1,7 +1,8 @@ """Support for EyeOnWater binary sensors.""" -from pyonwater import Meter from dataclasses import dataclass +from pyonwater import Meter + from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, BinarySensorEntity, @@ -17,6 +18,7 @@ from .const import DATA_COORDINATOR, DATA_SMART_METER, DOMAIN + @dataclass class Description: name: str @@ -97,7 +99,11 @@ def __init__( ) -> None: """Initialize the sensor.""" super().__init__(coordinator) - self.entity_description = BinarySensorEntityDescription(key=description.key, device_class=description.device_class, translation_key=description.translation_key) + self.entity_description = BinarySensorEntityDescription( + key=description.key, + device_class=description.device_class, + translation_key=description.translation_key, + ) self.meter = meter self._state = False self._available = False From 425289237bf3893b4b89ded5878bd5f1e09bb267 Mon Sep 17 00:00:00 2001 From: Konstantin Deev Date: Fri, 25 Aug 2023 18:17:34 -0500 Subject: [PATCH 5/9] newer version of pyonwater --- custom_components/eyeonwater/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/eyeonwater/manifest.json b/custom_components/eyeonwater/manifest.json index c2c94b0..8d0b70f 100644 --- a/custom_components/eyeonwater/manifest.json +++ b/custom_components/eyeonwater/manifest.json @@ -6,6 +6,6 @@ "dependencies": ["recorder"], "documentation": "https://github.com/kdeyev/eyeonwater", "iot_class": "cloud_polling", - "requirements": ["pyonwater==0.2.1"], + "requirements": ["pyonwater==0.2.2"], "version": "2.0.4" } From bd85c4c116ac1d7376fdef28fa023d543cde7339 Mon Sep 17 00:00:00 2001 From: Konstantin Deev Date: Fri, 25 Aug 2023 18:29:07 -0500 Subject: [PATCH 6/9] newer version of pyonwater --- custom_components/eyeonwater/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/eyeonwater/manifest.json b/custom_components/eyeonwater/manifest.json index 8d0b70f..b177ee7 100644 --- a/custom_components/eyeonwater/manifest.json +++ b/custom_components/eyeonwater/manifest.json @@ -6,6 +6,6 @@ "dependencies": ["recorder"], "documentation": "https://github.com/kdeyev/eyeonwater", "iot_class": "cloud_polling", - "requirements": ["pyonwater==0.2.2"], + "requirements": ["pyonwater==0.2.3"], "version": "2.0.4" } From 988df6762e926da0c419518ff889dc1a062d3b8c Mon Sep 17 00:00:00 2001 From: Konstantin Deev Date: Fri, 25 Aug 2023 18:49:52 -0500 Subject: [PATCH 7/9] newer version of pyonwater --- custom_components/eyeonwater/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/eyeonwater/manifest.json b/custom_components/eyeonwater/manifest.json index b177ee7..1492f93 100644 --- a/custom_components/eyeonwater/manifest.json +++ b/custom_components/eyeonwater/manifest.json @@ -6,6 +6,6 @@ "dependencies": ["recorder"], "documentation": "https://github.com/kdeyev/eyeonwater", "iot_class": "cloud_polling", - "requirements": ["pyonwater==0.2.3"], + "requirements": ["pyonwater==0.2.4"], "version": "2.0.4" } From 75bd85b6bb5da60956c2f4923a38523c197ef755 Mon Sep 17 00:00:00 2001 From: Konstantin Deev Date: Fri, 25 Aug 2023 18:57:35 -0500 Subject: [PATCH 8/9] bugfix --- custom_components/eyeonwater/binary_sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/eyeonwater/binary_sensor.py b/custom_components/eyeonwater/binary_sensor.py index 8006f8f..d31944d 100644 --- a/custom_components/eyeonwater/binary_sensor.py +++ b/custom_components/eyeonwater/binary_sensor.py @@ -114,7 +114,7 @@ def __init__( name=f"Water Meter {self.meter.meter_id}", ) - def get_flag(self, key: str) -> bool: + def get_flag(self) -> bool: return self.meter.meter_info.reading.flags.__dict__[self.entity_description.key] @callback @@ -122,7 +122,7 @@ def _state_update(self): """Call when the coordinator has an update.""" self._available = self.coordinator.last_update_success if self._available: - self._state = self.get_flags(self.entity_description.key) + self._state = self.get_flag() self.async_write_ha_state() async def async_added_to_hass(self): From c026e2bb505e6df0c419041f79c5198aeabb4c02 Mon Sep 17 00:00:00 2001 From: Konstantin Deev Date: Fri, 25 Aug 2023 19:09:25 -0500 Subject: [PATCH 9/9] typo --- custom_components/eyeonwater/binary_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/eyeonwater/binary_sensor.py b/custom_components/eyeonwater/binary_sensor.py index d31944d..8b08b5b 100644 --- a/custom_components/eyeonwater/binary_sensor.py +++ b/custom_components/eyeonwater/binary_sensor.py @@ -30,7 +30,7 @@ class Description: FLAG_SENSORS = [ Description( name="Leak", - key="leak`", + key="leak", translation_key="leak", device_class=BinarySensorDeviceClass.MOISTURE, ),