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:
parent
719f2e650c
commit
fa0d653216
13 changed files with 86 additions and 79 deletions
|
@ -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
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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,11 +168,14 @@ 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 (
|
||||||
|
self.data_type
|
||||||
|
in [
|
||||||
"total_power_export_t1_kwh",
|
"total_power_export_t1_kwh",
|
||||||
"total_power_export_t2_kwh",
|
"total_power_export_t2_kwh",
|
||||||
]:
|
]
|
||||||
if self.native_value == 0:
|
and self.native_value == 0
|
||||||
|
):
|
||||||
self._attr_entity_registry_enabled_default = False
|
self._attr_entity_registry_enabled_default = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue