From 703728341365674416525e1185752246cb74ab5d Mon Sep 17 00:00:00 2001 From: juanillo62gm Date: Thu, 14 Nov 2024 22:37:32 +0000 Subject: [PATCH 1/5] feature(versions): bump and improve build --- .devcontainer.json | 2 +- hacs.json | 2 +- requirements.txt | 4 ++-- scripts/develop.sh | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.devcontainer.json b/.devcontainer.json index b645228..54ac2b6 100755 --- a/.devcontainer.json +++ b/.devcontainer.json @@ -1,7 +1,7 @@ { "name": "juanillo62gm/panda_pwr", "image": "mcr.microsoft.com/devcontainers/python:3.12", - "postCreateCommand": "scripts/setup", + "postCreateCommand": "scripts/setup.sh", "forwardPorts": [8123], "portsAttributes": { "8123": { diff --git a/hacs.json b/hacs.json index ede4792..b451cf5 100755 --- a/hacs.json +++ b/hacs.json @@ -1,6 +1,6 @@ { "name": "Panda PWR", "hide_default_branch": true, - "homeassistant": "2024.6.0", + "homeassistant": "2024.11.1", "render_readme": true } diff --git a/requirements.txt b/requirements.txt index 734e202..43b20bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ colorlog==6.9.0 -homeassistant==2024.6.0 -pip>=21.3.1 +homeassistant==2024.11.1 +pip>=24.3.1 ruff==0.7.3 diff --git a/scripts/develop.sh b/scripts/develop.sh index 5e7295f..5bda856 100755 --- a/scripts/develop.sh +++ b/scripts/develop.sh @@ -5,7 +5,7 @@ set -e cd "$(dirname "$0")/.." # Create config dir if not present -if [[ ! -d "${PWD}/config" ]]; then +if [ ! -d "${PWD}/config" ]; then mkdir -p "${PWD}/config" hass --config "${PWD}/config" --script ensure_config fi From a6c3afde6f700ba5ba3a357d1f369b7bd407d1fb Mon Sep 17 00:00:00 2001 From: juanillo62gm Date: Thu, 14 Nov 2024 22:53:45 +0000 Subject: [PATCH 2/5] refactor(lint): work in progress --- custom_components/pandapwr/__init__.py | 2 ++ custom_components/pandapwr/api.py | 2 ++ custom_components/pandapwr/binary_sensor.py | 2 ++ custom_components/pandapwr/config_flow.py | 5 +++++ custom_components/pandapwr/sensor.py | 2 ++ custom_components/pandapwr/switch.py | 2 ++ 6 files changed, 15 insertions(+) diff --git a/custom_components/pandapwr/__init__.py b/custom_components/pandapwr/__init__.py index 03098c9..f8478b4 100644 --- a/custom_components/pandapwr/__init__.py +++ b/custom_components/pandapwr/__init__.py @@ -1,3 +1,5 @@ +"""PandaPWR integration for Home Assistant.""" + from homeassistant.config_entries import ConfigEntry from homeassistant.const import Platform from homeassistant.core import HomeAssistant diff --git a/custom_components/pandapwr/api.py b/custom_components/pandapwr/api.py index 1030850..f40551e 100644 --- a/custom_components/pandapwr/api.py +++ b/custom_components/pandapwr/api.py @@ -1,3 +1,5 @@ +"""API client for interacting with PandaPWR devices.""" + import aiohttp import async_timeout diff --git a/custom_components/pandapwr/binary_sensor.py b/custom_components/pandapwr/binary_sensor.py index 15dda7a..bf9982c 100644 --- a/custom_components/pandapwr/binary_sensor.py +++ b/custom_components/pandapwr/binary_sensor.py @@ -1,3 +1,5 @@ +"""Binary sensor platform for PandaPWR integration in Home Assistant.""" + from homeassistant.components.binary_sensor import BinarySensorEntity from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import EntityCategory diff --git a/custom_components/pandapwr/config_flow.py b/custom_components/pandapwr/config_flow.py index 81d8546..8683ee1 100644 --- a/custom_components/pandapwr/config_flow.py +++ b/custom_components/pandapwr/config_flow.py @@ -1,3 +1,5 @@ +"""Config flow for PandaPWR integration in Home Assistant.""" + import voluptuous as vol from homeassistant import config_entries @@ -6,9 +8,12 @@ class PandaPWRConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a config flow for PandaPWR.""" + VERSION = 1 async def async_step_user(self, user_input=None): + """Handle the initial step for setting up the integration.""" errors = {} if user_input: ip_address = user_input["ip_address"] diff --git a/custom_components/pandapwr/sensor.py b/custom_components/pandapwr/sensor.py index d0de72c..bb34316 100644 --- a/custom_components/pandapwr/sensor.py +++ b/custom_components/pandapwr/sensor.py @@ -1,3 +1,5 @@ +"""Sensor platform for PandaPWR integration in Home Assistant.""" + from homeassistant.components.sensor import SensorEntity from homeassistant.const import ( UnitOfElectricCurrent, diff --git a/custom_components/pandapwr/switch.py b/custom_components/pandapwr/switch.py index b35b1e2..ecf10f1 100644 --- a/custom_components/pandapwr/switch.py +++ b/custom_components/pandapwr/switch.py @@ -1,3 +1,5 @@ +"""Switch platform for PandaPWR integration in Home Assistant.""" + from homeassistant.components.switch import SwitchEntity from homeassistant.helpers.entity import EntityCategory From 1e8dccf794e9b04366d46899d963ee73669430e8 Mon Sep 17 00:00:00 2001 From: juanillo62gm Date: Thu, 14 Nov 2024 23:00:56 +0000 Subject: [PATCH 3/5] refactor(lint): all classes with docstrings --- custom_components/pandapwr/api.py | 2 ++ custom_components/pandapwr/binary_sensor.py | 12 +++++++++-- custom_components/pandapwr/sensor.py | 23 +++++++++++++++++++++ custom_components/pandapwr/switch.py | 12 +++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/custom_components/pandapwr/api.py b/custom_components/pandapwr/api.py index f40551e..ef35e2f 100644 --- a/custom_components/pandapwr/api.py +++ b/custom_components/pandapwr/api.py @@ -5,6 +5,8 @@ class PandaPWRApi: + """API client for PandaPWR devices.""" + def __init__(self, ip_address: str): """Initialize the API client.""" self._base_url = f"http://{ip_address}" diff --git a/custom_components/pandapwr/binary_sensor.py b/custom_components/pandapwr/binary_sensor.py index bf9982c..b6e88d0 100644 --- a/custom_components/pandapwr/binary_sensor.py +++ b/custom_components/pandapwr/binary_sensor.py @@ -16,6 +16,8 @@ async def async_setup_entry(hass, entry, async_add_entities): class PandaPWRBinarySensor(BinarySensorEntity): + """Base class for a PandaPWR binary sensor.""" + def __init__(self, api, entry): """Initialize the binary sensor.""" self._api = api @@ -58,6 +60,8 @@ def process_data(self, data): class PowerStateBinarySensor(PandaPWRBinarySensor): + """Binary sensor for monitoring the power state of a PandaPWR device.""" + def __init__(self, api, entry): super().__init__(api, entry) self._attr_name = "Power State" @@ -66,18 +70,22 @@ def __init__(self, api, entry): self._attr_unique_id = f"{self._device_id}_power_state" def process_data(self, data): + """Process power state data from the API.""" self._attr_is_on = data.get("power_state") == 1 class UsbStateBinarySensor(PandaPWRBinarySensor): + """Binary sensor for monitoring the USB state of a PandaPWR device.""" + def __init__(self, api, entry): super().__init__(api, entry) self._attr_name = "USB State" - self._attr_device_class = "power" # Cambiado a "power" para mostrar "on/off" + self._attr_device_class = "power" # Set to "power" for on/off display self._attr_entity_category = EntityCategory.DIAGNOSTIC self._attr_unique_id = f"{self._device_id}_usb_state" def process_data(self, data): + """Process USB state data from the API.""" self._attr_is_on = ( data.get("usb_state") == 1 - ) # Mostrar como "on" cuando es 1, "off" cuando es 0 + ) # Display as "on" when 1, "off" when 0 diff --git a/custom_components/pandapwr/sensor.py b/custom_components/pandapwr/sensor.py index bb34316..8df57f9 100644 --- a/custom_components/pandapwr/sensor.py +++ b/custom_components/pandapwr/sensor.py @@ -30,6 +30,8 @@ async def async_setup_entry(hass, entry, async_add_entities): class PandaPWRSensor(SensorEntity): + """Base class for PandaPWR sensors.""" + def __init__( self, api, @@ -72,30 +74,41 @@ def process_data(self, data): class CountdownStateSensor(PandaPWRSensor): + """Sensor for monitoring countdown state on a PandaPWR device.""" + def __init__(self, api, entry, device_id): super().__init__(api, entry, device_id, "Countdown State", "countdown_state") def process_data(self, data): + """Process countdown state data from the API.""" self._attr_native_value = data.get("countdown_state") class AutoPoweroffSensor(PandaPWRSensor): + """Sensor for monitoring auto power-off state on a PandaPWR device.""" + def __init__(self, api, entry, device_id): super().__init__(api, entry, device_id, "Auto Poweroff", "auto_poweroff") def process_data(self, data): + """Process auto power-off data from the API.""" self._attr_native_value = data.get("auto_poweroff") class CountdownSensor(PandaPWRSensor): + """Sensor for monitoring countdown timer on a PandaPWR device.""" + def __init__(self, api, entry, device_id): super().__init__(api, entry, device_id, "Countdown", "countdown", "s") def process_data(self, data): + """Process countdown timer data from the API.""" self._attr_native_value = data.get("countdown") class VoltageSensor(PandaPWRSensor): + """Sensor for monitoring voltage on a PandaPWR device.""" + def __init__(self, api, entry, device_id): super().__init__( api, @@ -108,10 +121,13 @@ def __init__(self, api, entry, device_id): ) def process_data(self, data): + """Process voltage data from the API.""" self._attr_native_value = data.get("voltage") or 0.0 class CurrentSensor(PandaPWRSensor): + """Sensor for monitoring current on a PandaPWR device.""" + def __init__(self, api, entry, device_id): super().__init__( api, @@ -124,20 +140,26 @@ def __init__(self, api, entry, device_id): ) def process_data(self, data): + """Process current data from the API.""" self._attr_native_value = data.get("current") or 0.0 class PowerSensor(PandaPWRSensor): + """Sensor for monitoring power on a PandaPWR device.""" + def __init__(self, api, entry, device_id): super().__init__( api, entry, device_id, "Power", "power", UnitOfPower.WATT, "power" ) def process_data(self, data): + """Process power data from the API.""" self._attr_native_value = data.get("power") or 0.0 class EnergyUsageSensor(PandaPWRSensor): + """Sensor for monitoring energy usage on a PandaPWR device.""" + def __init__(self, api, entry, device_id): super().__init__( api, @@ -150,4 +172,5 @@ def __init__(self, api, entry, device_id): ) def process_data(self, data): + """Process energy usage data from the API.""" self._attr_native_value = data.get("ele") or 0.0 diff --git a/custom_components/pandapwr/switch.py b/custom_components/pandapwr/switch.py index ecf10f1..83f2ec8 100644 --- a/custom_components/pandapwr/switch.py +++ b/custom_components/pandapwr/switch.py @@ -15,6 +15,8 @@ async def async_setup_entry(hass, entry, async_add_entities): class PandaPWRSwitch(SwitchEntity): + """Base class for a PandaPWR switch.""" + def __init__(self, api, entry, hass): """Initialize the switch.""" self._api = api @@ -45,6 +47,8 @@ def process_data(self, data): class PowerSwitch(PandaPWRSwitch): + """Switch entity for controlling the power state of a PandaPWR device.""" + def __init__(self, api, entry, hass): super().__init__(api, entry, hass) self._attr_name = "Power Switch" @@ -52,20 +56,25 @@ def __init__(self, api, entry, hass): self._attr_unique_id = f"{self._device_id}_power_switch" async def async_turn_on(self): + """Turn on the power switch.""" await self._api.set_power_state(1) self._attr_is_on = True self.async_write_ha_state() async def async_turn_off(self): + """Turn off the power switch.""" await self._api.set_power_state(0) self._attr_is_on = False self.async_write_ha_state() def process_data(self, data): + """Process power state data from the API.""" self._attr_is_on = data.get("power_state") == 1 class UsbSwitch(PandaPWRSwitch): + """Switch entity for controlling the USB state of a PandaPWR device.""" + def __init__(self, api, entry, hass): super().__init__(api, entry, hass) self._attr_name = "USB Switch" @@ -73,14 +82,17 @@ def __init__(self, api, entry, hass): self._attr_unique_id = f"{self._device_id}_usb_switch" async def async_turn_on(self): + """Turn on the USB switch.""" await self._api.set_usb_state(1) self._attr_is_on = True self.async_write_ha_state() async def async_turn_off(self): + """Turn off the USB switch.""" await self._api.set_usb_state(0) self._attr_is_on = False self.async_write_ha_state() def process_data(self, data): + """Process USB state data from the API.""" self._attr_is_on = data.get("usb_state") == 1 From 9f0feddd80882fe83e48f592b9a176d57a6daaab Mon Sep 17 00:00:00 2001 From: juanillo62gm Date: Thu, 14 Nov 2024 23:23:24 +0000 Subject: [PATCH 4/5] refactor(lint): keep working --- custom_components/pandapwr/api.py | 55 +++++++++++++-------- custom_components/pandapwr/binary_sensor.py | 30 ++++++++--- custom_components/pandapwr/switch.py | 2 +- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/custom_components/pandapwr/api.py b/custom_components/pandapwr/api.py index ef35e2f..9d912c9 100644 --- a/custom_components/pandapwr/api.py +++ b/custom_components/pandapwr/api.py @@ -3,11 +3,13 @@ import aiohttp import async_timeout +HTTP_OK = 200 + class PandaPWRApi: """API client for PandaPWR devices.""" - def __init__(self, ip_address: str): + def __init__(self, ip_address: str) -> None: """Initialize the API client.""" self._base_url = f"http://{ip_address}" self._session = aiohttp.ClientSession() @@ -15,34 +17,45 @@ def __init__(self, ip_address: str): async def test_connection(self) -> bool: """Test if the connection to the device can be established.""" try: - async with async_timeout.timeout(10): - async with self._session.get( - f"{self._base_url}/update_ele_data" - ) as response: - return response.status == 200 - except Exception: + async with ( + async_timeout.timeout(10), + self._session.get(f"{self._base_url}/update_ele_data") as response, + ): + return response.status == HTTP_OK + except aiohttp.ClientError: return False async def get_data(self) -> dict: """Fetch data from the device.""" - async with async_timeout.timeout(10): - async with self._session.get( - f"{self._base_url}/update_ele_data" - ) as response: + try: + async with ( + async_timeout.timeout(10), + self._session.get(f"{self._base_url}/update_ele_data") as response, + ): return await response.json() + except aiohttp.ClientError: + return {} - async def set_power_state(self, state: int): + async def set_power_state(self, state: int) -> bool: """Set power state (0 for off, 1 for on) using RAW payload.""" payload = f"power={state}" - async with self._session.post( - f"{self._base_url}/set", data=payload - ) as response: - return response.status == 200 + try: + async with ( + async_timeout.timeout(10), + self._session.post(f"{self._base_url}/set", data=payload) as response, + ): + return response.status == HTTP_OK + except aiohttp.ClientError: + return False - async def set_usb_state(self, state: int): + async def set_usb_state(self, state: int) -> bool: """Set USB state (0 for off, 1 for on) using RAW payload.""" payload = f"usb={state}" - async with self._session.post( - f"{self._base_url}/set", data=payload - ) as response: - return response.status == 200 + try: + async with ( + async_timeout.timeout(10), + self._session.post(f"{self._base_url}/set", data=payload) as response, + ): + return response.status == HTTP_OK + except aiohttp.ClientError: + return False diff --git a/custom_components/pandapwr/binary_sensor.py b/custom_components/pandapwr/binary_sensor.py index b6e88d0..83d8618 100644 --- a/custom_components/pandapwr/binary_sensor.py +++ b/custom_components/pandapwr/binary_sensor.py @@ -1,17 +1,29 @@ """Binary sensor platform for PandaPWR integration in Home Assistant.""" -from homeassistant.components.binary_sensor import BinarySensorEntity +import aiohttp +from homeassistant.components.binary_sensor import ( + BinarySensorDeviceClass, + BinarySensorEntity, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import EntityCategory +from homeassistant.core import HomeAssistant from homeassistant.helpers.dispatcher import async_dispatcher_connect -from homeassistant.helpers.entity import EntityCategory +from homeassistant.helpers.entity_platform import ( + AddEntitiesCallback, +) from .const import DOMAIN -async def async_setup_entry(hass, entry, async_add_entities): +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: """Set up the binary sensor platform from a config entry.""" api = hass.data[DOMAIN][entry.entry_id] async_add_entities( - [PowerStateBinarySensor(api, entry), UsbStateBinarySensor(api, entry)], True + [PowerStateBinarySensor(api, entry), UsbStateBinarySensor(api, entry)], + update_before_add=True, ) @@ -32,7 +44,7 @@ async def async_update(self): data = await self._api.get_data() self._attr_available = True self.process_data(data) - except Exception: + except aiohttp.ClientError: self._attr_available = False async def async_added_to_hass(self): @@ -65,7 +77,9 @@ class PowerStateBinarySensor(PandaPWRBinarySensor): def __init__(self, api, entry): super().__init__(api, entry) self._attr_name = "Power State" - self._attr_device_class = "power" + self._attr_device_class = ( + BinarySensorDeviceClass.POWER + ) # Corrected device class self._attr_entity_category = EntityCategory.DIAGNOSTIC self._attr_unique_id = f"{self._device_id}_power_state" @@ -80,7 +94,9 @@ class UsbStateBinarySensor(PandaPWRBinarySensor): def __init__(self, api, entry): super().__init__(api, entry) self._attr_name = "USB State" - self._attr_device_class = "power" # Set to "power" for on/off display + self._attr_device_class = ( + BinarySensorDeviceClass.POWER + ) # Corrected device class self._attr_entity_category = EntityCategory.DIAGNOSTIC self._attr_unique_id = f"{self._device_id}_usb_state" diff --git a/custom_components/pandapwr/switch.py b/custom_components/pandapwr/switch.py index 83f2ec8..1a5e5a4 100644 --- a/custom_components/pandapwr/switch.py +++ b/custom_components/pandapwr/switch.py @@ -1,7 +1,7 @@ """Switch platform for PandaPWR integration in Home Assistant.""" from homeassistant.components.switch import SwitchEntity -from homeassistant.helpers.entity import EntityCategory +from homeassistant.const import EntityCategory from .const import DOMAIN From 6e94fee15358f7f61564d32e9f9b3baa8f35914c Mon Sep 17 00:00:00 2001 From: juanillo62gm Date: Fri, 15 Nov 2024 00:57:13 +0000 Subject: [PATCH 5/5] refactor(lint): ready --- custom_components/pandapwr/binary_sensor.py | 34 ++--- custom_components/pandapwr/config_flow.py | 4 +- custom_components/pandapwr/sensor.py | 153 ++++++++++++-------- custom_components/pandapwr/switch.py | 38 +++-- 4 files changed, 132 insertions(+), 97 deletions(-) diff --git a/custom_components/pandapwr/binary_sensor.py b/custom_components/pandapwr/binary_sensor.py index 83d8618..c37cf69 100644 --- a/custom_components/pandapwr/binary_sensor.py +++ b/custom_components/pandapwr/binary_sensor.py @@ -1,5 +1,7 @@ """Binary sensor platform for PandaPWR integration in Home Assistant.""" +from typing import Any + import aiohttp from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, @@ -30,7 +32,7 @@ async def async_setup_entry( class PandaPWRBinarySensor(BinarySensorEntity): """Base class for a PandaPWR binary sensor.""" - def __init__(self, api, entry): + def __init__(self, api: Any, entry: ConfigEntry) -> None: """Initialize the binary sensor.""" self._api = api self._entry = entry @@ -38,7 +40,7 @@ def __init__(self, api, entry): self._attr_available = False self._device_id = f"pandapwr_{self._entry.data['ip_address']}" - async def async_update(self): + async def async_update(self) -> None: """Fetch new state data for the sensor.""" try: data = await self._api.get_data() @@ -47,7 +49,7 @@ async def async_update(self): except aiohttp.ClientError: self._attr_available = False - async def async_added_to_hass(self): + async def async_added_to_hass(self) -> None: """Subscribe to update signal.""" self.async_on_remove( async_dispatcher_connect( @@ -56,7 +58,7 @@ async def async_added_to_hass(self): ) @property - def device_info(self): + def device_info(self) -> dict: """Return device information for grouping in the UI.""" return { "identifiers": {(DOMAIN, self._device_id)}, @@ -66,7 +68,7 @@ def device_info(self): "sw_version": "1.0", } - def process_data(self, data): + def process_data(self, data: Any) -> None: """Process data received from the API.""" raise NotImplementedError @@ -74,16 +76,15 @@ def process_data(self, data): class PowerStateBinarySensor(PandaPWRBinarySensor): """Binary sensor for monitoring the power state of a PandaPWR device.""" - def __init__(self, api, entry): + def __init__(self, api: Any, entry: ConfigEntry) -> None: + """Initialize the PowerStateBinarySensor.""" super().__init__(api, entry) self._attr_name = "Power State" - self._attr_device_class = ( - BinarySensorDeviceClass.POWER - ) # Corrected device class + self._attr_device_class = BinarySensorDeviceClass.POWER self._attr_entity_category = EntityCategory.DIAGNOSTIC self._attr_unique_id = f"{self._device_id}_power_state" - def process_data(self, data): + def process_data(self, data: Any) -> None: """Process power state data from the API.""" self._attr_is_on = data.get("power_state") == 1 @@ -91,17 +92,14 @@ def process_data(self, data): class UsbStateBinarySensor(PandaPWRBinarySensor): """Binary sensor for monitoring the USB state of a PandaPWR device.""" - def __init__(self, api, entry): + def __init__(self, api: Any, entry: ConfigEntry) -> None: + """Initialize the UsbStateBinarySensor.""" super().__init__(api, entry) self._attr_name = "USB State" - self._attr_device_class = ( - BinarySensorDeviceClass.POWER - ) # Corrected device class + self._attr_device_class = BinarySensorDeviceClass.POWER self._attr_entity_category = EntityCategory.DIAGNOSTIC self._attr_unique_id = f"{self._device_id}_usb_state" - def process_data(self, data): + def process_data(self, data: Any) -> None: """Process USB state data from the API.""" - self._attr_is_on = ( - data.get("usb_state") == 1 - ) # Display as "on" when 1, "off" when 0 + self._attr_is_on = data.get("usb_state") == 1 diff --git a/custom_components/pandapwr/config_flow.py b/custom_components/pandapwr/config_flow.py index 8683ee1..226ef67 100644 --- a/custom_components/pandapwr/config_flow.py +++ b/custom_components/pandapwr/config_flow.py @@ -12,7 +12,9 @@ class PandaPWRConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): VERSION = 1 - async def async_step_user(self, user_input=None): + async def async_step_user( + self, user_input: dict | None = None + ) -> config_entries.ConfigFlowResult: """Handle the initial step for setting up the integration.""" errors = {} if user_input: diff --git a/custom_components/pandapwr/sensor.py b/custom_components/pandapwr/sensor.py index 8df57f9..a50508a 100644 --- a/custom_components/pandapwr/sensor.py +++ b/custom_components/pandapwr/sensor.py @@ -1,17 +1,25 @@ """Sensor platform for PandaPWR integration in Home Assistant.""" +from typing import Any + from homeassistant.components.sensor import SensorEntity +from homeassistant.components.sensor.const import SensorDeviceClass +from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( UnitOfElectricCurrent, UnitOfElectricPotential, UnitOfEnergy, UnitOfPower, ) +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -async def async_setup_entry(hass, entry, async_add_entities): +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: """Set up the sensor platform for PandaPWR.""" api = hass.data[DOMAIN][entry.entry_id] device_id = f"pandapwr_{entry.data['ip_address']}" @@ -25,7 +33,7 @@ async def async_setup_entry(hass, entry, async_add_entities): PowerSensor(api, entry, device_id), EnergyUsageSensor(api, entry, device_id), ], - True, + update_before_add=True, ) @@ -34,31 +42,30 @@ class PandaPWRSensor(SensorEntity): def __init__( self, - api, - entry, - device_id, - name, - unique_id_suffix, - native_unit_of_measurement=None, - device_class=None, - ): + api: Any, + entry: ConfigEntry, + device_id: str, + sensor_info: dict, + ) -> None: """Initialize the sensor with common attributes.""" self._api = api self._entry = entry - self._attr_name = name - self._attr_unique_id = f"{device_id}_{unique_id_suffix}" + self._attr_name = sensor_info["name"] + self._attr_unique_id = f"{device_id}_{sensor_info['unique_id_suffix']}" self._attr_native_value = None - self._attr_native_unit_of_measurement = native_unit_of_measurement - self._attr_device_class = device_class + self._attr_native_unit_of_measurement = sensor_info.get( + "native_unit_of_measurement" + ) + self._attr_device_class = sensor_info.get("device_class") self._device_id = device_id - async def async_update(self): + async def async_update(self) -> None: """Fetch latest state data from the device.""" data = await self._api.get_data() self.process_data(data) @property - def device_info(self): + def device_info(self) -> dict: """Return device information to group in the UI.""" return { "identifiers": {(DOMAIN, self._device_id)}, @@ -68,7 +75,7 @@ def device_info(self): "sw_version": "1.0", } - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process data received from the API.""" raise NotImplementedError @@ -76,10 +83,15 @@ def process_data(self, data): class CountdownStateSensor(PandaPWRSensor): """Sensor for monitoring countdown state on a PandaPWR device.""" - def __init__(self, api, entry, device_id): - super().__init__(api, entry, device_id, "Countdown State", "countdown_state") + def __init__(self, api: Any, entry: ConfigEntry, device_id: str) -> None: + """Initialize the sensor.""" + sensor_info = { + "name": "Countdown State", + "unique_id_suffix": "countdown_state", + } + super().__init__(api, entry, device_id, sensor_info) - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process countdown state data from the API.""" self._attr_native_value = data.get("countdown_state") @@ -87,10 +99,15 @@ def process_data(self, data): class AutoPoweroffSensor(PandaPWRSensor): """Sensor for monitoring auto power-off state on a PandaPWR device.""" - def __init__(self, api, entry, device_id): - super().__init__(api, entry, device_id, "Auto Poweroff", "auto_poweroff") + def __init__(self, api: Any, entry: ConfigEntry, device_id: str) -> None: + """Initialize the sensor.""" + sensor_info = { + "name": "Auto Poweroff", + "unique_id_suffix": "auto_poweroff", + } + super().__init__(api, entry, device_id, sensor_info) - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process auto power-off data from the API.""" self._attr_native_value = data.get("auto_poweroff") @@ -98,10 +115,16 @@ def process_data(self, data): class CountdownSensor(PandaPWRSensor): """Sensor for monitoring countdown timer on a PandaPWR device.""" - def __init__(self, api, entry, device_id): - super().__init__(api, entry, device_id, "Countdown", "countdown", "s") + def __init__(self, api: Any, entry: ConfigEntry, device_id: str) -> None: + """Initialize the sensor.""" + sensor_info = { + "name": "Countdown", + "unique_id_suffix": "countdown", + "native_unit_of_measurement": "s", + } + super().__init__(api, entry, device_id, sensor_info) - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process countdown timer data from the API.""" self._attr_native_value = data.get("countdown") @@ -109,18 +132,17 @@ def process_data(self, data): class VoltageSensor(PandaPWRSensor): """Sensor for monitoring voltage on a PandaPWR device.""" - def __init__(self, api, entry, device_id): - super().__init__( - api, - entry, - device_id, - "Voltage", - "voltage", - UnitOfElectricPotential.VOLT, - "voltage", - ) + def __init__(self, api: Any, entry: ConfigEntry, device_id: str) -> None: + """Initialize the sensor.""" + sensor_info = { + "name": "Voltage", + "unique_id_suffix": "voltage", + "native_unit_of_measurement": UnitOfElectricPotential.VOLT, + "device_class": SensorDeviceClass.VOLTAGE, + } + super().__init__(api, entry, device_id, sensor_info) - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process voltage data from the API.""" self._attr_native_value = data.get("voltage") or 0.0 @@ -128,18 +150,17 @@ def process_data(self, data): class CurrentSensor(PandaPWRSensor): """Sensor for monitoring current on a PandaPWR device.""" - def __init__(self, api, entry, device_id): - super().__init__( - api, - entry, - device_id, - "Current", - "current", - UnitOfElectricCurrent.AMPERE, - "current", - ) + def __init__(self, api: Any, entry: ConfigEntry, device_id: str) -> None: + """Initialize the sensor.""" + sensor_info = { + "name": "Current", + "unique_id_suffix": "current", + "native_unit_of_measurement": UnitOfElectricCurrent.AMPERE, + "device_class": SensorDeviceClass.CURRENT, + } + super().__init__(api, entry, device_id, sensor_info) - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process current data from the API.""" self._attr_native_value = data.get("current") or 0.0 @@ -147,12 +168,17 @@ def process_data(self, data): class PowerSensor(PandaPWRSensor): """Sensor for monitoring power on a PandaPWR device.""" - def __init__(self, api, entry, device_id): - super().__init__( - api, entry, device_id, "Power", "power", UnitOfPower.WATT, "power" - ) + def __init__(self, api: Any, entry: ConfigEntry, device_id: str) -> None: + """Initialize the sensor.""" + sensor_info = { + "name": "Power", + "unique_id_suffix": "power", + "native_unit_of_measurement": UnitOfPower.WATT, + "device_class": SensorDeviceClass.POWER, + } + super().__init__(api, entry, device_id, sensor_info) - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process power data from the API.""" self._attr_native_value = data.get("power") or 0.0 @@ -160,17 +186,16 @@ def process_data(self, data): class EnergyUsageSensor(PandaPWRSensor): """Sensor for monitoring energy usage on a PandaPWR device.""" - def __init__(self, api, entry, device_id): - super().__init__( - api, - entry, - device_id, - "Energy Usage", - "energy_usage", - UnitOfEnergy.KILO_WATT_HOUR, - "energy", - ) + def __init__(self, api: Any, entry: ConfigEntry, device_id: str) -> None: + """Initialize the sensor.""" + sensor_info = { + "name": "Energy Usage", + "unique_id_suffix": "energy_usage", + "native_unit_of_measurement": UnitOfEnergy.KILO_WATT_HOUR, + "device_class": SensorDeviceClass.ENERGY, + } + super().__init__(api, entry, device_id, sensor_info) - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process energy usage data from the API.""" self._attr_native_value = data.get("ele") or 0.0 diff --git a/custom_components/pandapwr/switch.py b/custom_components/pandapwr/switch.py index 1a5e5a4..82fc345 100644 --- a/custom_components/pandapwr/switch.py +++ b/custom_components/pandapwr/switch.py @@ -1,23 +1,31 @@ """Switch platform for PandaPWR integration in Home Assistant.""" +from typing import Any + from homeassistant.components.switch import SwitchEntity +from homeassistant.config_entries import ConfigEntry from homeassistant.const import EntityCategory +from homeassistant.core import HomeAssistant +from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DOMAIN -async def async_setup_entry(hass, entry, async_add_entities): +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: """Set up switch platform for PandaPWR.""" api = hass.data[DOMAIN][entry.entry_id] async_add_entities( - [PowerSwitch(api, entry, hass), UsbSwitch(api, entry, hass)], True + [PowerSwitch(api, entry, hass), UsbSwitch(api, entry, hass)], + update_before_add=True, ) class PandaPWRSwitch(SwitchEntity): """Base class for a PandaPWR switch.""" - def __init__(self, api, entry, hass): + def __init__(self, api: Any, entry: ConfigEntry, hass: HomeAssistant) -> None: """Initialize the switch.""" self._api = api self._entry = entry @@ -25,13 +33,13 @@ def __init__(self, api, entry, hass): self._attr_is_on = None self._device_id = f"pandapwr_{self._entry.data['ip_address']}" - async def async_update(self): + async def async_update(self) -> None: """Fetch latest state data from the device.""" data = await self._api.get_data() self.process_data(data) @property - def device_info(self): + def device_info(self) -> dict: """Return device information for grouping in the UI.""" return { "identifiers": {(DOMAIN, self._device_id)}, @@ -41,7 +49,7 @@ def device_info(self): "sw_version": "1.0", } - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process API data.""" raise NotImplementedError @@ -49,25 +57,26 @@ def process_data(self, data): class PowerSwitch(PandaPWRSwitch): """Switch entity for controlling the power state of a PandaPWR device.""" - def __init__(self, api, entry, hass): + def __init__(self, api: Any, entry: ConfigEntry, hass: HomeAssistant) -> None: + """Initialize the PowerSwitch.""" super().__init__(api, entry, hass) self._attr_name = "Power Switch" self._attr_entity_category = EntityCategory.CONFIG self._attr_unique_id = f"{self._device_id}_power_switch" - async def async_turn_on(self): + async def async_turn_on(self) -> None: """Turn on the power switch.""" await self._api.set_power_state(1) self._attr_is_on = True self.async_write_ha_state() - async def async_turn_off(self): + async def async_turn_off(self) -> None: """Turn off the power switch.""" await self._api.set_power_state(0) self._attr_is_on = False self.async_write_ha_state() - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process power state data from the API.""" self._attr_is_on = data.get("power_state") == 1 @@ -75,24 +84,25 @@ def process_data(self, data): class UsbSwitch(PandaPWRSwitch): """Switch entity for controlling the USB state of a PandaPWR device.""" - def __init__(self, api, entry, hass): + def __init__(self, api: Any, entry: ConfigEntry, hass: HomeAssistant) -> None: + """Initialize the UsbSwitch.""" super().__init__(api, entry, hass) self._attr_name = "USB Switch" self._attr_entity_category = EntityCategory.CONFIG self._attr_unique_id = f"{self._device_id}_usb_switch" - async def async_turn_on(self): + async def async_turn_on(self) -> None: """Turn on the USB switch.""" await self._api.set_usb_state(1) self._attr_is_on = True self.async_write_ha_state() - async def async_turn_off(self): + async def async_turn_off(self) -> None: """Turn off the USB switch.""" await self._api.set_usb_state(0) self._attr_is_on = False self.async_write_ha_state() - def process_data(self, data): + def process_data(self, data: dict) -> None: """Process USB state data from the API.""" self._attr_is_on = data.get("usb_state") == 1