Update python-homewizard-energy to 1.5.0 (#85966)

* Update python-homewizard-energy to 1.5.0

* Remove strict typing for now

* Revert "Remove strict typing for now"

This reverts commit ebcd327fdf.

* Adjust typing to resolve upstream changes
This commit is contained in:
Franck Nijhof 2023-01-16 09:23:03 +01:00 committed by GitHub
parent 719f2e650c
commit fa0d653216
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 86 additions and 79 deletions

View file

@ -1,6 +1,4 @@
"""The Homewizard integration.""" """The Homewizard integration."""
import logging
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS from homeassistant.const import CONF_IP_ADDRESS
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -9,8 +7,6 @@ from homeassistant.exceptions import ConfigEntryNotReady
from .const import DOMAIN, PLATFORMS from .const import DOMAIN, PLATFORMS
from .coordinator import HWEnergyDeviceUpdateCoordinator as Coordinator from .coordinator import HWEnergyDeviceUpdateCoordinator as Coordinator
_LOGGER = logging.getLogger(__name__)
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Homewizard from a config entry.""" """Set up Homewizard from a config entry."""
@ -19,22 +15,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
except ConfigEntryNotReady: except ConfigEntryNotReady:
await coordinator.api.close() await coordinator.api.close() # type: ignore[no-untyped-call]
if coordinator.api_disabled: if coordinator.api_disabled:
entry.async_start_reauth(hass) entry.async_start_reauth(hass)
raise raise
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
# Abort reauth config flow if active # Abort reauth config flow if active
for progress_flow in hass.config_entries.flow.async_progress_by_handler(DOMAIN): for progress_flow in hass.config_entries.flow.async_progress_by_handler(DOMAIN):
if progress_flow["context"].get("source") == SOURCE_REAUTH: if (
"context" in progress_flow
and progress_flow["context"].get("source") == SOURCE_REAUTH
):
hass.config_entries.flow.async_abort(progress_flow["flow_id"]) hass.config_entries.flow.async_abort(progress_flow["flow_id"])
# Setup entry
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = coordinator
# Finalize # Finalize
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
@ -43,12 +40,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry.""" """Unload a config entry."""
_LOGGER.debug("__init__ async_unload_entry") if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
coordinator = hass.data[DOMAIN].pop(entry.entry_id) coordinator = hass.data[DOMAIN].pop(entry.entry_id)
await coordinator.api.close() await coordinator.api.close()
return unload_ok return unload_ok

View file

@ -251,7 +251,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
raise AbortFlow("unknown_error") from ex raise AbortFlow("unknown_error") from ex
finally: finally:
await energy_api.close() await energy_api.close() # type: ignore[no-untyped-call]
async def _async_set_and_check_unique_id(self, entry_info: dict[str, Any]) -> None: async def _async_set_and_check_unique_id(self, entry_info: dict[str, Any]) -> None:
"""Validate if entry exists.""" """Validate if entry exists."""

View file

@ -1,8 +1,8 @@
"""Constants for the Homewizard integration.""" """Constants for the Homewizard integration."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta from datetime import timedelta
from typing import TypedDict
# Set up. # Set up.
from homewizard_energy.models import Data, Device, State, System from homewizard_energy.models import Data, Device, State, System
@ -24,10 +24,11 @@ CONF_SERIAL = "serial"
UPDATE_INTERVAL = timedelta(seconds=5) UPDATE_INTERVAL = timedelta(seconds=5)
class DeviceResponseEntry(TypedDict): @dataclass
class DeviceResponseEntry:
"""Dict describing a single response entry.""" """Dict describing a single response entry."""
device: Device device: Device
data: Data data: Data
state: State state: State | None
system: System system: System | None = None

View file

@ -39,16 +39,15 @@ class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry]
# Update all properties # Update all properties
try: try:
data: DeviceResponseEntry = { data = DeviceResponseEntry(
"device": await self.api.device(), device=await self.api.device(),
"data": await self.api.data(), data=await self.api.data(),
"state": await self.api.state(), state=await self.api.state(),
"system": None, )
}
features = await self.api.features() features = await self.api.features()
if features.has_system: if features.has_system:
data["system"] = await self.api.system() data.system = await self.api.system()
except RequestError as ex: except RequestError as ex:
raise UpdateFailed(ex) from ex raise UpdateFailed(ex) from ex

View file

@ -22,13 +22,13 @@ async def async_get_config_entry_diagnostics(
coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
meter_data = { meter_data = {
"device": asdict(coordinator.data["device"]), "device": asdict(coordinator.data.device),
"data": asdict(coordinator.data["data"]), "data": asdict(coordinator.data.data),
"state": asdict(coordinator.data["state"]) "state": asdict(coordinator.data.state)
if coordinator.data["state"] is not None if coordinator.data.state is not None
else None, else None,
"system": asdict(coordinator.data["system"]) "system": asdict(coordinator.data.system)
if coordinator.data["system"] is not None if coordinator.data.system is not None
else None, else None,
} }

View file

@ -1,6 +1,7 @@
"""Base entity for the HomeWizard integration.""" """Base entity for the HomeWizard integration."""
from __future__ import annotations from __future__ import annotations
from homeassistant.const import ATTR_IDENTIFIERS
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -19,7 +20,11 @@ class HomeWizardEntity(CoordinatorEntity[HWEnergyDeviceUpdateCoordinator]):
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
name=coordinator.entry.title, name=coordinator.entry.title,
manufacturer="HomeWizard", manufacturer="HomeWizard",
sw_version=coordinator.data["device"].firmware_version, sw_version=coordinator.data.device.firmware_version,
model=coordinator.data["device"].product_type, model=coordinator.data.device.product_type,
identifiers={(DOMAIN, coordinator.data["device"].serial)},
) )
if coordinator.data.device.serial is not None:
self._attr_device_info[ATTR_IDENTIFIERS] = {
(DOMAIN, coordinator.data.device.serial)
}

View file

@ -21,7 +21,8 @@ def homewizard_exception_handler(
"""Decorate HomeWizard Energy calls to handle HomeWizardEnergy exceptions. """Decorate HomeWizard Energy calls to handle HomeWizardEnergy exceptions.
A decorator that wraps the passed in function, catches HomeWizardEnergy errors, A decorator that wraps the passed in function, catches HomeWizardEnergy errors,
and reloads the integration when the API was disabled so the reauth flow is triggered. and reloads the integration when the API was disabled so the reauth flow is
triggered.
""" """
async def handler( async def handler(
@ -29,7 +30,6 @@ def homewizard_exception_handler(
) -> None: ) -> None:
try: try:
await func(self, *args, **kwargs) await func(self, *args, **kwargs)
except RequestError as ex: except RequestError as ex:
raise HomeAssistantError from ex raise HomeAssistantError from ex
except DisabledError as ex: except DisabledError as ex:

View file

@ -4,7 +4,7 @@
"documentation": "https://www.home-assistant.io/integrations/homewizard", "documentation": "https://www.home-assistant.io/integrations/homewizard",
"codeowners": ["@DCSBL"], "codeowners": ["@DCSBL"],
"dependencies": [], "dependencies": [],
"requirements": ["python-homewizard-energy==1.4.0"], "requirements": ["python-homewizard-energy==1.5.0"],
"zeroconf": ["_hwenergy._tcp.local."], "zeroconf": ["_hwenergy._tcp.local."],
"config_flow": true, "config_flow": true,
"quality_scale": "platinum", "quality_scale": "platinum",

View file

@ -1,8 +1,6 @@
"""Creates HomeWizard Number entities.""" """Creates HomeWizard Number entities."""
from __future__ import annotations from __future__ import annotations
from typing import Optional, cast
from homeassistant.components.number import NumberEntity from homeassistant.components.number import NumberEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE from homeassistant.const import PERCENTAGE
@ -23,19 +21,17 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up numbers for device.""" """Set up numbers for device."""
coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
if coordinator.data.state:
if coordinator.data["state"]: async_add_entities([HWEnergyNumberEntity(coordinator, entry)])
async_add_entities(
[
HWEnergyNumberEntity(coordinator, entry),
]
)
class HWEnergyNumberEntity(HomeWizardEntity, NumberEntity): class HWEnergyNumberEntity(HomeWizardEntity, NumberEntity):
"""Representation of status light number.""" """Representation of status light number."""
_attr_entity_category = EntityCategory.CONFIG _attr_entity_category = EntityCategory.CONFIG
_attr_icon = "mdi:lightbulb-on"
_attr_name = "Status light brightness"
_attr_native_unit_of_measurement = PERCENTAGE
def __init__( def __init__(
self, self,
@ -45,20 +41,19 @@ class HWEnergyNumberEntity(HomeWizardEntity, NumberEntity):
"""Initialize the control number.""" """Initialize the control number."""
super().__init__(coordinator) super().__init__(coordinator)
self._attr_unique_id = f"{entry.unique_id}_status_light_brightness" self._attr_unique_id = f"{entry.unique_id}_status_light_brightness"
self._attr_name = "Status light brightness"
self._attr_native_unit_of_measurement = PERCENTAGE
self._attr_icon = "mdi:lightbulb-on"
@homewizard_exception_handler @homewizard_exception_handler
async def async_set_native_value(self, value: float) -> None: async def async_set_native_value(self, value: float) -> None:
"""Set a new value.""" """Set a new value."""
await self.coordinator.api.state_set(brightness=value * (255 / 100)) await self.coordinator.api.state_set(brightness=int(value * (255 / 100)))
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
@property @property
def native_value(self) -> float | None: def native_value(self) -> float | None:
"""Return the current value.""" """Return the current value."""
brightness = cast(Optional[float], self.coordinator.data["state"].brightness) if (
if brightness is None: self.coordinator.data.state is None
or self.coordinator.data.state.brightness is None
):
return None return None
return round(brightness * (100 / 255)) return round(self.coordinator.data.state.brightness * (100 / 255))

View file

@ -137,11 +137,13 @@ async def async_setup_entry(
"""Initialize sensors.""" """Initialize sensors."""
coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
entities = [] entities: list[HWEnergySensor] = []
if coordinator.data["data"] is not None: if coordinator.data.data is not None:
for description in SENSORS: entities.extend(
if getattr(coordinator.data["data"], description.key) is not None: HWEnergySensor(coordinator, entry, description)
entities.append(HWEnergySensor(coordinator, entry, description)) for description in SENSORS
if getattr(coordinator.data.data, description.key) is not None
)
async_add_entities(entities) async_add_entities(entities)
@ -166,12 +168,15 @@ class HWEnergySensor(HomeWizardEntity, SensorEntity):
# Special case for export, not everyone has solarpanels # Special case for export, not everyone has solarpanels
# The chance that 'export' is non-zero when you have solar panels is nil # The chance that 'export' is non-zero when you have solar panels is nil
if self.data_type in [ if (
"total_power_export_t1_kwh", self.data_type
"total_power_export_t2_kwh", in [
]: "total_power_export_t1_kwh",
if self.native_value == 0: "total_power_export_t2_kwh",
self._attr_entity_registry_enabled_default = False ]
and self.native_value == 0
):
self._attr_entity_registry_enabled_default = False
@property @property
def data(self) -> DeviceResponseEntry: def data(self) -> DeviceResponseEntry:
@ -181,7 +186,7 @@ class HWEnergySensor(HomeWizardEntity, SensorEntity):
@property @property
def native_value(self) -> StateType: def native_value(self) -> StateType:
"""Return state of meter.""" """Return state of meter."""
return cast(StateType, getattr(self.data["data"], self.data_type)) return cast(StateType, getattr(self.data.data, self.data_type))
@property @property
def available(self) -> bool: def available(self) -> bool:

View file

@ -25,11 +25,11 @@ async def async_setup_entry(
entities: list[SwitchEntity] = [] entities: list[SwitchEntity] = []
if coordinator.data["state"]: if coordinator.data.state:
entities.append(HWEnergyMainSwitchEntity(coordinator, entry)) entities.append(HWEnergyMainSwitchEntity(coordinator, entry))
entities.append(HWEnergySwitchLockEntity(coordinator, entry)) entities.append(HWEnergySwitchLockEntity(coordinator, entry))
if coordinator.data["system"]: if coordinator.data.system:
entities.append(HWEnergyEnableCloudEntity(hass, coordinator, entry)) entities.append(HWEnergyEnableCloudEntity(hass, coordinator, entry))
async_add_entities(entities) async_add_entities(entities)
@ -79,12 +79,18 @@ class HWEnergyMainSwitchEntity(HWEnergySwitchEntity):
This switch becomes unavailable when switch_lock is enabled. This switch becomes unavailable when switch_lock is enabled.
""" """
return super().available and not self.coordinator.data["state"].switch_lock return (
super().available
and self.coordinator.data.state is not None
and not self.coordinator.data.state.switch_lock
)
@property @property
def is_on(self) -> bool: def is_on(self) -> bool | None:
"""Return true if switch is on.""" """Return true if switch is on."""
return bool(self.coordinator.data["state"].power_on) if self.coordinator.data.state is None:
return None
return self.coordinator.data.state.power_on
class HWEnergySwitchLockEntity(HWEnergySwitchEntity): class HWEnergySwitchLockEntity(HWEnergySwitchEntity):
@ -118,9 +124,11 @@ class HWEnergySwitchLockEntity(HWEnergySwitchEntity):
await self.coordinator.async_refresh() await self.coordinator.async_refresh()
@property @property
def is_on(self) -> bool: def is_on(self) -> bool | None:
"""Return true if switch is on.""" """Return true if switch is on."""
return bool(self.coordinator.data["state"].switch_lock) if self.coordinator.data.state is None:
return None
return self.coordinator.data.state.switch_lock
class HWEnergyEnableCloudEntity(HWEnergySwitchEntity): class HWEnergyEnableCloudEntity(HWEnergySwitchEntity):
@ -164,6 +172,8 @@ class HWEnergyEnableCloudEntity(HWEnergySwitchEntity):
return "mdi:cloud" if self.is_on else "mdi:cloud-off-outline" return "mdi:cloud" if self.is_on else "mdi:cloud-off-outline"
@property @property
def is_on(self) -> bool: def is_on(self) -> bool | None:
"""Return true if cloud connection is active.""" """Return true if cloud connection is active."""
return bool(self.coordinator.data["system"].cloud_enabled) if self.coordinator.data.system is None:
return None
return self.coordinator.data.system.cloud_enabled

View file

@ -2039,7 +2039,7 @@ python-gc100==1.0.3a0
python-gitlab==1.6.0 python-gitlab==1.6.0
# homeassistant.components.homewizard # homeassistant.components.homewizard
python-homewizard-energy==1.4.0 python-homewizard-energy==1.5.0
# homeassistant.components.hp_ilo # homeassistant.components.hp_ilo
python-hpilo==4.3 python-hpilo==4.3

View file

@ -1444,7 +1444,7 @@ python-forecastio==1.4.0
python-fullykiosk==0.0.12 python-fullykiosk==0.0.12
# homeassistant.components.homewizard # homeassistant.components.homewizard
python-homewizard-energy==1.4.0 python-homewizard-energy==1.5.0
# homeassistant.components.izone # homeassistant.components.izone
python-izone==1.2.9 python-izone==1.2.9