Use 'python-homewizard-energy' dependency for HomeWizard (#71781)

* Update requirement

* Remove aiohwenergy and use python-homewizard-energy

* Update test to work with python-homewizard-energy

* Bumb python-homewizard-energy to 1.0.3
This commit is contained in:
Duco Sebel 2022-05-25 09:05:11 +02:00 committed by GitHub
parent ce477e65ce
commit 88c49f034a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 218 additions and 336 deletions

View File

@ -1,14 +1,11 @@
"""The Homewizard integration."""
import logging
from aiohwenergy import DisabledError
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_IP_ADDRESS
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.update_coordinator import UpdateFailed
from .const import DOMAIN, PLATFORMS
from .coordinator import HWEnergyDeviceUpdateCoordinator as Coordinator
@ -69,16 +66,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Create coordinator
coordinator = Coordinator(hass, entry.data[CONF_IP_ADDRESS])
try:
await coordinator.initialize_api()
except DisabledError:
_LOGGER.error("API is disabled, enable API in HomeWizard Energy app")
return False
except UpdateFailed as ex:
raise ConfigEntryNotReady from ex
await coordinator.async_config_entry_first_refresh()
await coordinator.async_config_entry_first_refresh()
except ConfigEntryNotReady:
await coordinator.api.close()
raise
# Finalize
hass.data.setdefault(DOMAIN, {})

View File

@ -4,9 +4,8 @@ from __future__ import annotations
import logging
from typing import Any
import aiohwenergy
from aiohwenergy.hwenergy import SUPPORTED_DEVICES
import async_timeout
from homewizard_energy import HomeWizardEnergy
from homewizard_energy.errors import DisabledError, UnsupportedError
from voluptuous import Required, Schema
from homeassistant import config_entries
@ -175,16 +174,19 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
# Make connection with device
# This is to test the connection and to get info for unique_id
energy_api = aiohwenergy.HomeWizardEnergy(ip_address)
energy_api = HomeWizardEnergy(ip_address)
try:
with async_timeout.timeout(10):
await energy_api.initialize()
device = await energy_api.device()
except aiohwenergy.DisabledError as ex:
except DisabledError as ex:
_LOGGER.error("API disabled, API must be enabled in the app")
raise AbortFlow("api_not_enabled") from ex
except UnsupportedError as ex:
_LOGGER.error("API version unsuppored")
raise AbortFlow("unsupported_api_version") from ex
except Exception as ex:
_LOGGER.exception(
"Error connecting with Energy Device at %s",
@ -195,25 +197,10 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
finally:
await energy_api.close()
if energy_api.device is None:
_LOGGER.error("Initialization failed")
raise AbortFlow("unknown_error")
# Validate metadata
if energy_api.device.api_version != "v1":
raise AbortFlow("unsupported_api_version")
if energy_api.device.product_type not in SUPPORTED_DEVICES:
_LOGGER.error(
"Device (%s) not supported by integration",
energy_api.device.product_type,
)
raise AbortFlow("device_not_supported")
return {
CONF_PRODUCT_NAME: energy_api.device.product_name,
CONF_PRODUCT_TYPE: energy_api.device.product_type,
CONF_SERIAL: energy_api.device.serial,
CONF_PRODUCT_NAME: device.product_name,
CONF_PRODUCT_TYPE: device.product_type,
CONF_SERIAL: device.serial,
}
async def _async_set_and_check_unique_id(self, entry_info: dict[str, Any]) -> None:

View File

@ -5,10 +5,9 @@ from datetime import timedelta
from typing import TypedDict
# Set up.
from aiohwenergy.device import Device
from homewizard_energy.models import Data, Device, State
from homeassistant.const import Platform
from homeassistant.helpers.typing import StateType
DOMAIN = "homewizard"
PLATFORMS = [Platform.SENSOR, Platform.SWITCH]
@ -29,4 +28,5 @@ class DeviceResponseEntry(TypedDict):
"""Dict describing a single response entry."""
device: Device
data: dict[str, StateType]
data: Data
state: State

View File

@ -1,11 +1,10 @@
"""Update coordinator for HomeWizard."""
from __future__ import annotations
import asyncio
import logging
import aiohwenergy
import async_timeout
from homewizard_energy import HomeWizardEnergy
from homewizard_energy.errors import DisabledError
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
@ -19,7 +18,7 @@ _LOGGER = logging.getLogger(__name__)
class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry]):
"""Gather data for the energy device."""
api: aiohwenergy.HomeWizardEnergy
api: HomeWizardEnergy
def __init__(
self,
@ -29,56 +28,20 @@ class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry]
"""Initialize Update Coordinator."""
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=UPDATE_INTERVAL)
session = async_get_clientsession(hass)
self.api = aiohwenergy.HomeWizardEnergy(host, clientsession=session)
self.api = HomeWizardEnergy(host, clientsession=async_get_clientsession(hass))
async def _async_update_data(self) -> DeviceResponseEntry:
"""Fetch all device and sensor data from api."""
async with async_timeout.timeout(10):
if self.api.device is None:
await self.initialize_api()
# Update all properties
try:
if not await self.api.update():
raise UpdateFailed("Failed to communicate with device")
except aiohwenergy.DisabledError as ex:
raise UpdateFailed(
"API disabled, API must be enabled in the app"
) from ex
# Update all properties
try:
data: DeviceResponseEntry = {
"device": self.api.device,
"data": {},
"device": await self.api.device(),
"data": await self.api.data(),
"state": await self.api.state(),
}
for datapoint in self.api.data.available_datapoints:
data["data"][datapoint] = getattr(self.api.data, datapoint)
except DisabledError as ex:
raise UpdateFailed("API disabled, API must be enabled in the app") from ex
return data
async def initialize_api(self) -> aiohwenergy:
"""Initialize API and validate connection."""
try:
await self.api.initialize()
except (asyncio.TimeoutError, aiohwenergy.RequestError) as ex:
raise UpdateFailed(
f"Error connecting to the Energy device at {self.api.host}"
) from ex
except aiohwenergy.DisabledError as ex:
raise ex
except aiohwenergy.AiohwenergyException as ex:
raise UpdateFailed("Unknown Energy API error occurred") from ex
except Exception as ex:
raise UpdateFailed(
f"Unknown error connecting with Energy Device at {self.api.host}"
) from ex

View File

@ -4,9 +4,9 @@
"documentation": "https://www.home-assistant.io/integrations/homewizard",
"codeowners": ["@DCSBL"],
"dependencies": [],
"requirements": ["aiohwenergy==0.8.0"],
"requirements": ["python-homewizard-energy==1.0.3"],
"zeroconf": ["_hwenergy._tcp.local."],
"config_flow": true,
"iot_class": "local_polling",
"loggers": ["aiohwenergy"]
"loggers": ["homewizard_energy"]
}

View File

@ -2,7 +2,7 @@
from __future__ import annotations
import logging
from typing import Final
from typing import Final, cast
from homeassistant.components.sensor import (
SensorDeviceClass,
@ -129,12 +129,9 @@ async def async_setup_entry(
coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
entities = []
if coordinator.api.data is not None:
if coordinator.data["data"] is not None:
for description in SENSORS:
if (
description.key in coordinator.api.data.available_datapoints
and getattr(coordinator.api.data, description.key) is not None
):
if getattr(coordinator.data["data"], description.key) is not None:
entities.append(HWEnergySensor(coordinator, entry, description))
async_add_entities(entities)
@ -165,7 +162,7 @@ class HWEnergySensor(CoordinatorEntity[HWEnergyDeviceUpdateCoordinator], SensorE
"total_power_export_t1_kwh",
"total_power_export_t2_kwh",
]:
if self.data["data"][self.data_type] == 0:
if self.native_value == 0:
self._attr_entity_registry_enabled_default = False
@property
@ -187,9 +184,9 @@ class HWEnergySensor(CoordinatorEntity[HWEnergyDeviceUpdateCoordinator], SensorE
@property
def native_value(self) -> StateType:
"""Return state of meter."""
return self.data["data"][self.data_type]
return cast(StateType, getattr(self.data["data"], self.data_type))
@property
def available(self) -> bool:
"""Return availability of meter."""
return super().available and self.data_type in self.data["data"]
return super().available and self.native_value is not None

View File

@ -22,7 +22,7 @@ async def async_setup_entry(
"""Set up switches."""
coordinator: HWEnergyDeviceUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
if coordinator.api.state:
if coordinator.data["state"]:
async_add_entities(
[
HWEnergyMainSwitchEntity(coordinator, entry),
@ -70,12 +70,12 @@ class HWEnergyMainSwitchEntity(HWEnergySwitchEntity):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on."""
await self.coordinator.api.state.set(power_on=True)
await self.coordinator.api.state_set(power_on=True)
await self.coordinator.async_refresh()
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off."""
await self.coordinator.api.state.set(power_on=False)
await self.coordinator.api.state_set(power_on=False)
await self.coordinator.async_refresh()
@property
@ -85,12 +85,12 @@ class HWEnergyMainSwitchEntity(HWEnergySwitchEntity):
This switch becomes unavailable when switch_lock is enabled.
"""
return super().available and not self.coordinator.api.state.switch_lock
return super().available and not self.coordinator.data["state"].switch_lock
@property
def is_on(self) -> bool:
"""Return true if switch is on."""
return bool(self.coordinator.api.state.power_on)
return bool(self.coordinator.data["state"].power_on)
class HWEnergySwitchLockEntity(HWEnergySwitchEntity):
@ -115,15 +115,15 @@ class HWEnergySwitchLockEntity(HWEnergySwitchEntity):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn switch-lock on."""
await self.coordinator.api.state.set(switch_lock=True)
await self.coordinator.api.state_set(switch_lock=True)
await self.coordinator.async_refresh()
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn switch-lock off."""
await self.coordinator.api.state.set(switch_lock=False)
await self.coordinator.api.state_set(switch_lock=False)
await self.coordinator.async_refresh()
@property
def is_on(self) -> bool:
"""Return true if switch is on."""
return bool(self.coordinator.api.state.switch_lock)
return bool(self.coordinator.data["state"].switch_lock)

View File

@ -171,9 +171,6 @@ aiohttp_cors==0.7.0
# homeassistant.components.hue
aiohue==4.4.1
# homeassistant.components.homewizard
aiohwenergy==0.8.0
# homeassistant.components.imap
aioimaplib==0.9.0
@ -1903,6 +1900,9 @@ python-gc100==1.0.3a0
# homeassistant.components.gitlab_ci
python-gitlab==1.6.0
# homeassistant.components.homewizard
python-homewizard-energy==1.0.3
# homeassistant.components.hp_ilo
python-hpilo==4.3

View File

@ -155,9 +155,6 @@ aiohttp_cors==0.7.0
# homeassistant.components.hue
aiohue==4.4.1
# homeassistant.components.homewizard
aiohwenergy==0.8.0
# homeassistant.components.apache_kafka
aiokafka==0.6.0
@ -1265,6 +1262,9 @@ python-ecobee-api==0.2.14
# homeassistant.components.darksky
python-forecastio==1.4.0
# homeassistant.components.homewizard
python-homewizard-energy==1.0.3
# homeassistant.components.izone
python-izone==1.2.3

View File

@ -2,6 +2,8 @@
from unittest.mock import AsyncMock
from homewizard_energy.models import Device
def get_mock_device(
serial="aabbccddeeff",
@ -13,15 +15,18 @@ def get_mock_device(
mock_device = AsyncMock()
mock_device.host = host
mock_device.device.product_name = product_name
mock_device.device.product_type = product_type
mock_device.device.serial = serial
mock_device.device.api_version = "v1"
mock_device.device.firmware_version = "1.00"
mock_device.device = AsyncMock(
return_value=Device(
product_name=product_name,
product_type=product_type,
serial=serial,
api_version="V1",
firmware_version="1.00",
)
)
mock_device.data = AsyncMock(return_value=None)
mock_device.state = AsyncMock(return_value=None)
mock_device.state = None
mock_device.initialize = AsyncMock()
mock_device.close = AsyncMock()
return mock_device

View File

@ -2,7 +2,7 @@
import logging
from unittest.mock import patch
from aiohwenergy import DisabledError
from homewizard_energy.errors import DisabledError, UnsupportedError
from homeassistant import config_entries
from homeassistant.components import zeroconf
@ -33,7 +33,10 @@ async def test_manual_flow_works(hass, aioclient_mock):
assert result["type"] == "form"
assert result["step_id"] == "user"
with patch("aiohwenergy.HomeWizardEnergy", return_value=device,), patch(
with patch(
"homeassistant.components.homewizard.config_flow.HomeWizardEnergy",
return_value=device,
), patch(
"homeassistant.components.homewizard.async_setup_entry",
return_value=True,
) as mock_setup_entry:
@ -42,12 +45,12 @@ async def test_manual_flow_works(hass, aioclient_mock):
)
assert result["type"] == "create_entry"
assert result["title"] == f"{device.device.product_name} (aabbccddeeff)"
assert result["title"] == "P1 meter (aabbccddeeff)"
assert result["data"][CONF_IP_ADDRESS] == "2.2.2.2"
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert len(device.initialize.mock_calls) == 1
assert len(device.device.mock_calls) == 1
assert len(device.close.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
@ -72,7 +75,10 @@ async def test_discovery_flow_works(hass, aioclient_mock):
},
)
with patch("aiohwenergy.HomeWizardEnergy", return_value=get_mock_device()):
with patch(
"homeassistant.components.homewizard.config_flow.HomeWizardEnergy",
return_value=get_mock_device(),
):
flow = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_ZEROCONF},
@ -82,7 +88,10 @@ async def test_discovery_flow_works(hass, aioclient_mock):
with patch(
"homeassistant.components.homewizard.async_setup_entry",
return_value=True,
), patch("aiohwenergy.HomeWizardEnergy", return_value=get_mock_device()):
), patch(
"homeassistant.components.homewizard.config_flow.HomeWizardEnergy",
return_value=get_mock_device(),
):
result = await hass.config_entries.flow.async_configure(
flow["flow_id"], user_input=None
)
@ -92,7 +101,10 @@ async def test_discovery_flow_works(hass, aioclient_mock):
with patch(
"homeassistant.components.homewizard.async_setup_entry",
return_value=True,
), patch("aiohwenergy.HomeWizardEnergy", return_value=get_mock_device()):
), patch(
"homeassistant.components.homewizard.config_flow.HomeWizardEnergy",
return_value=get_mock_device(),
):
result = await hass.config_entries.flow.async_configure(
flow["flow_id"], user_input={"ip_address": "192.168.43.183"}
)
@ -113,7 +125,10 @@ async def test_config_flow_imports_entry(aioclient_mock, hass):
mock_entry = MockConfigEntry(domain="homewizard_energy", data={"host": "1.2.3.4"})
mock_entry.add_to_hass(hass)
with patch("aiohwenergy.HomeWizardEnergy", return_value=device,), patch(
with patch(
"homeassistant.components.homewizard.config_flow.HomeWizardEnergy",
return_value=device,
), patch(
"homeassistant.components.homewizard.async_setup_entry",
return_value=True,
) as mock_setup_entry:
@ -127,11 +142,11 @@ async def test_config_flow_imports_entry(aioclient_mock, hass):
)
assert result["type"] == "create_entry"
assert result["title"] == f"{device.device.product_name} (aabbccddeeff)"
assert result["title"] == "P1 meter (aabbccddeeff)"
assert result["data"][CONF_IP_ADDRESS] == "1.2.3.4"
assert len(hass.config_entries.async_entries(DOMAIN)) == 1
assert len(device.initialize.mock_calls) == 1
assert len(device.device.mock_calls) == 1
assert len(device.close.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
@ -166,7 +181,10 @@ async def test_discovery_disabled_api(hass, aioclient_mock):
with patch(
"homeassistant.components.homewizard.async_setup_entry",
return_value=True,
), patch("aiohwenergy.HomeWizardEnergy", return_value=get_mock_device()):
), patch(
"homeassistant.components.homewizard.config_flow.HomeWizardEnergy",
return_value=get_mock_device(),
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={"ip_address": "192.168.43.183"}
)
@ -240,7 +258,7 @@ async def test_check_disabled_api(hass, aioclient_mock):
raise DisabledError
device = get_mock_device()
device.initialize.side_effect = mock_initialize
device.device.side_effect = mock_initialize
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -250,7 +268,7 @@ async def test_check_disabled_api(hass, aioclient_mock):
assert result["step_id"] == "user"
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.config_flow.HomeWizardEnergy",
return_value=device,
):
result = await hass.config_entries.flow.async_configure(
@ -268,7 +286,7 @@ async def test_check_error_handling_api(hass, aioclient_mock):
raise Exception()
device = get_mock_device()
device.initialize.side_effect = mock_initialize
device.device.side_effect = mock_initialize
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -278,32 +296,7 @@ async def test_check_error_handling_api(hass, aioclient_mock):
assert result["step_id"] == "user"
with patch(
"aiohwenergy.HomeWizardEnergy",
return_value=device,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"}
)
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "unknown_error"
async def test_check_detects_unexpected_api_response(hass, aioclient_mock):
"""Test check detecting device endpoint failed fetching data."""
device = get_mock_device()
device.device = None
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.config_flow.HomeWizardEnergy",
return_value=device,
):
result = await hass.config_entries.flow.async_configure(
@ -317,8 +310,11 @@ async def test_check_detects_unexpected_api_response(hass, aioclient_mock):
async def test_check_detects_invalid_api(hass, aioclient_mock):
"""Test check detecting device endpoint failed fetching data."""
def mock_initialize():
raise UnsupportedError
device = get_mock_device()
device.device.api_version = "not_v1"
device.device.side_effect = mock_initialize
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
@ -328,7 +324,7 @@ async def test_check_detects_invalid_api(hass, aioclient_mock):
assert result["step_id"] == "user"
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.config_flow.HomeWizardEnergy",
return_value=device,
):
result = await hass.config_entries.flow.async_configure(
@ -337,27 +333,3 @@ async def test_check_detects_invalid_api(hass, aioclient_mock):
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "unsupported_api_version"
async def test_check_detects_unsuported_device(hass, aioclient_mock):
"""Test check detecting device endpoint failed fetching data."""
device = get_mock_device(product_type="not_an_energy_device")
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
assert result["type"] == "form"
assert result["step_id"] == "user"
with patch(
"aiohwenergy.HomeWizardEnergy",
return_value=device,
):
result = await hass.config_entries.flow.async_configure(
result["flow_id"], {CONF_IP_ADDRESS: "2.2.2.2"}
)
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "device_not_supported"

View File

@ -2,7 +2,7 @@
from asyncio import TimeoutError
from unittest.mock import patch
from aiohwenergy import AiohwenergyException, DisabledError
from homewizard_energy.errors import DisabledError, HomeWizardEnergyException
from homeassistant import config_entries
from homeassistant.components.homewizard.const import DOMAIN
@ -28,7 +28,7 @@ async def test_load_unload(aioclient_mock, hass):
entry.add_to_hass(hass)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=device,
):
await hass.config_entries.async_setup(entry.entry_id)
@ -50,7 +50,7 @@ async def test_load_failed_host_unavailable(aioclient_mock, hass):
raise TimeoutError()
device = get_mock_device()
device.initialize.side_effect = MockInitialize
device.device.side_effect = MockInitialize
entry = MockConfigEntry(
domain=DOMAIN,
@ -60,7 +60,7 @@ async def test_load_failed_host_unavailable(aioclient_mock, hass):
entry.add_to_hass(hass)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=device,
):
await hass.config_entries.async_setup(entry.entry_id)
@ -127,7 +127,7 @@ async def test_init_accepts_and_migrates_old_entry(aioclient_mock, hass):
# Add the entry_id to trigger migration
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=device,
):
await hass.config_entries.async_setup(imported_entry.entry_id)
@ -168,7 +168,7 @@ async def test_load_detect_api_disabled(aioclient_mock, hass):
raise DisabledError()
device = get_mock_device()
device.initialize.side_effect = MockInitialize
device.device.side_effect = MockInitialize
entry = MockConfigEntry(
domain=DOMAIN,
@ -178,24 +178,24 @@ async def test_load_detect_api_disabled(aioclient_mock, hass):
entry.add_to_hass(hass)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=device,
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert entry.state is ConfigEntryState.SETUP_ERROR
assert entry.state is ConfigEntryState.SETUP_RETRY
async def test_load_handles_aiohwenergy_exception(aioclient_mock, hass):
async def test_load_handles_homewizardenergy_exception(aioclient_mock, hass):
"""Test setup handles exception from API."""
def MockInitialize():
raise AiohwenergyException()
raise HomeWizardEnergyException()
device = get_mock_device()
device.initialize.side_effect = MockInitialize
device.device.side_effect = MockInitialize
entry = MockConfigEntry(
domain=DOMAIN,
@ -205,7 +205,7 @@ async def test_load_handles_aiohwenergy_exception(aioclient_mock, hass):
entry.add_to_hass(hass)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=device,
):
await hass.config_entries.async_setup(entry.entry_id)
@ -222,7 +222,7 @@ async def test_load_handles_generic_exception(aioclient_mock, hass):
raise Exception()
device = get_mock_device()
device.initialize.side_effect = MockInitialize
device.device.side_effect = MockInitialize
entry = MockConfigEntry(
domain=DOMAIN,
@ -232,7 +232,7 @@ async def test_load_handles_generic_exception(aioclient_mock, hass):
entry.add_to_hass(hass)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=device,
):
await hass.config_entries.async_setup(entry.entry_id)
@ -256,7 +256,7 @@ async def test_load_handles_initialization_error(aioclient_mock, hass):
entry.add_to_hass(hass)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=device,
):
await hass.config_entries.async_setup(entry.entry_id)

View File

@ -3,7 +3,8 @@
from datetime import timedelta
from unittest.mock import AsyncMock, patch
from aiohwenergy.errors import DisabledError
from homewizard_energy.errors import DisabledError, RequestError
from homewizard_energy.models import Data
from homeassistant.components.sensor import (
ATTR_STATE_CLASS,
@ -36,13 +37,10 @@ async def test_sensor_entity_smr_version(
"""Test entity loads smr version."""
api = get_mock_device()
api.data.available_datapoints = [
"smr_version",
]
api.data.smr_version = 50
api.data = AsyncMock(return_value=Data.from_dict({"smr_version": 50}))
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -77,13 +75,10 @@ async def test_sensor_entity_meter_model(
"""Test entity loads meter model."""
api = get_mock_device()
api.data.available_datapoints = [
"meter_model",
]
api.data.meter_model = "Model X"
api.data = AsyncMock(return_value=Data.from_dict({"meter_model": "Model X"}))
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -118,13 +113,10 @@ async def test_sensor_entity_wifi_ssid(hass, mock_config_entry_data, mock_config
"""Test entity loads wifi ssid."""
api = get_mock_device()
api.data.available_datapoints = [
"wifi_ssid",
]
api.data.wifi_ssid = "My Wifi"
api.data = AsyncMock(return_value=Data.from_dict({"wifi_ssid": "My Wifi"}))
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -159,13 +151,10 @@ async def test_sensor_entity_wifi_strength(
"""Test entity loads wifi strength."""
api = get_mock_device()
api.data.available_datapoints = [
"wifi_strength",
]
api.data.wifi_strength = 42
api.data = AsyncMock(return_value=Data.from_dict({"wifi_strength": 42}))
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -189,13 +178,12 @@ async def test_sensor_entity_total_power_import_t1_kwh(
"""Test entity loads total power import t1."""
api = get_mock_device()
api.data.available_datapoints = [
"total_power_import_t1_kwh",
]
api.data.total_power_import_t1_kwh = 1234.123
api.data = AsyncMock(
return_value=Data.from_dict({"total_power_import_t1_kwh": 1234.123})
)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -232,13 +220,12 @@ async def test_sensor_entity_total_power_import_t2_kwh(
"""Test entity loads total power import t2."""
api = get_mock_device()
api.data.available_datapoints = [
"total_power_import_t2_kwh",
]
api.data.total_power_import_t2_kwh = 1234.123
api.data = AsyncMock(
return_value=Data.from_dict({"total_power_import_t2_kwh": 1234.123})
)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -275,13 +262,12 @@ async def test_sensor_entity_total_power_export_t1_kwh(
"""Test entity loads total power export t1."""
api = get_mock_device()
api.data.available_datapoints = [
"total_power_export_t1_kwh",
]
api.data.total_power_export_t1_kwh = 1234.123
api.data = AsyncMock(
return_value=Data.from_dict({"total_power_export_t1_kwh": 1234.123})
)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -318,13 +304,12 @@ async def test_sensor_entity_total_power_export_t2_kwh(
"""Test entity loads total power export t2."""
api = get_mock_device()
api.data.available_datapoints = [
"total_power_export_t2_kwh",
]
api.data.total_power_export_t2_kwh = 1234.123
api.data = AsyncMock(
return_value=Data.from_dict({"total_power_export_t2_kwh": 1234.123})
)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -361,13 +346,10 @@ async def test_sensor_entity_active_power(
"""Test entity loads active power."""
api = get_mock_device()
api.data.available_datapoints = [
"active_power_w",
]
api.data.active_power_w = 123.123
api.data = AsyncMock(return_value=Data.from_dict({"active_power_w": 123.123}))
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -402,13 +384,10 @@ async def test_sensor_entity_active_power_l1(
"""Test entity loads active power l1."""
api = get_mock_device()
api.data.available_datapoints = [
"active_power_l1_w",
]
api.data.active_power_l1_w = 123.123
api.data = AsyncMock(return_value=Data.from_dict({"active_power_l1_w": 123.123}))
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -445,13 +424,10 @@ async def test_sensor_entity_active_power_l2(
"""Test entity loads active power l2."""
api = get_mock_device()
api.data.available_datapoints = [
"active_power_l2_w",
]
api.data.active_power_l2_w = 456.456
api.data = AsyncMock(return_value=Data.from_dict({"active_power_l2_w": 456.456}))
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -488,13 +464,10 @@ async def test_sensor_entity_active_power_l3(
"""Test entity loads active power l3."""
api = get_mock_device()
api.data.available_datapoints = [
"active_power_l3_w",
]
api.data.active_power_l3_w = 789.789
api.data = AsyncMock(return_value=Data.from_dict({"active_power_l3_w": 789.789}))
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -529,13 +502,10 @@ async def test_sensor_entity_total_gas(hass, mock_config_entry_data, mock_config
"""Test entity loads total gas."""
api = get_mock_device()
api.data.available_datapoints = [
"total_gas_m3",
]
api.data.total_gas_m3 = 50
api.data = AsyncMock(return_value=Data.from_dict({"total_gas_m3": 50}))
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -570,17 +540,14 @@ async def test_sensor_entity_disabled_when_null(
"""Test sensor disables data with null by default."""
api = get_mock_device()
api.data.available_datapoints = [
"active_power_l2_w",
"active_power_l3_w",
"total_gas_m3",
]
api.data.active_power_l2_w = None
api.data.active_power_l3_w = None
api.data.total_gas_m3 = None
api.data = AsyncMock(
return_value=Data.from_dict(
{"active_power_l2_w": None, "active_power_l3_w": None, "total_gas_m3": None}
)
)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -612,15 +579,14 @@ async def test_sensor_entity_export_disabled_when_unused(
"""Test sensor disables export if value is 0."""
api = get_mock_device()
api.data.available_datapoints = [
"total_power_export_t1_kwh",
"total_power_export_t2_kwh",
]
api.data.total_power_export_t1_kwh = 0
api.data.total_power_export_t2_kwh = 0
api.data = AsyncMock(
return_value=Data.from_dict(
{"total_power_export_t1_kwh": 0, "total_power_export_t2_kwh": 0}
)
)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -649,17 +615,14 @@ async def test_sensors_unreachable(hass, mock_config_entry_data, mock_config_ent
"""Test sensor handles api unreachable."""
api = get_mock_device()
api.data.available_datapoints = [
"total_power_import_t1_kwh",
]
api.data.total_power_import_t1_kwh = 1234.123
api.data = AsyncMock(
return_value=Data.from_dict({"total_power_import_t1_kwh": 1234.123})
)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
api.update = AsyncMock(return_value=True)
entry = mock_config_entry
entry.data = mock_config_entry_data
entry.add_to_hass(hass)
@ -675,7 +638,7 @@ async def test_sensors_unreachable(hass, mock_config_entry_data, mock_config_ent
== "1234.123"
)
api.update = AsyncMock(return_value=False)
api.data.side_effect = RequestError
async_fire_time_changed(hass, utcnow + timedelta(seconds=5))
await hass.async_block_till_done()
assert (
@ -685,7 +648,7 @@ async def test_sensors_unreachable(hass, mock_config_entry_data, mock_config_ent
== "unavailable"
)
api.update = AsyncMock(return_value=True)
api.data.side_effect = None
async_fire_time_changed(hass, utcnow + timedelta(seconds=10))
await hass.async_block_till_done()
assert (
@ -700,17 +663,14 @@ async def test_api_disabled(hass, mock_config_entry_data, mock_config_entry):
"""Test sensor handles api unreachable."""
api = get_mock_device()
api.data.available_datapoints = [
"total_power_import_t1_kwh",
]
api.data.total_power_import_t1_kwh = 1234.123
api.data = AsyncMock(
return_value=Data.from_dict({"total_power_import_t1_kwh": 1234.123})
)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
api.update = AsyncMock(return_value=True)
entry = mock_config_entry
entry.data = mock_config_entry_data
entry.add_to_hass(hass)
@ -726,7 +686,7 @@ async def test_api_disabled(hass, mock_config_entry_data, mock_config_entry):
== "1234.123"
)
api.update = AsyncMock(side_effect=DisabledError)
api.data.side_effect = DisabledError
async_fire_time_changed(hass, utcnow + timedelta(seconds=5))
await hass.async_block_till_done()
assert (
@ -736,7 +696,7 @@ async def test_api_disabled(hass, mock_config_entry_data, mock_config_entry):
== "unavailable"
)
api.update = AsyncMock(return_value=True)
api.data.side_effect = None
async_fire_time_changed(hass, utcnow + timedelta(seconds=10))
await hass.async_block_till_done()
assert (

View File

@ -2,6 +2,8 @@
from unittest.mock import AsyncMock, patch
from homewizard_energy.models import State
from homeassistant.components import switch
from homeassistant.components.switch import DEVICE_CLASS_OUTLET, DEVICE_CLASS_SWITCH
from homeassistant.const import (
@ -27,7 +29,7 @@ async def test_switch_entity_not_loaded_when_not_available(
api = get_mock_device()
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -48,13 +50,12 @@ async def test_switch_loads_entities(hass, mock_config_entry_data, mock_config_e
"""Test entity loads smr version."""
api = get_mock_device()
api.state = AsyncMock()
api.state.power_on = False
api.state.switch_lock = False
api.state = AsyncMock(
return_value=State.from_dict({"power_on": False, "switch_lock": False})
)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -104,17 +105,19 @@ async def test_switch_power_on_off(hass, mock_config_entry_data, mock_config_ent
"""Test entity turns switch on and off."""
api = get_mock_device()
api.state = AsyncMock()
api.state.power_on = False
api.state.switch_lock = False
api.state = AsyncMock(
return_value=State.from_dict({"power_on": False, "switch_lock": False})
)
def set_power_on(power_on):
api.state.power_on = power_on
def state_set(power_on):
api.state = AsyncMock(
return_value=State.from_dict({"power_on": power_on, "switch_lock": False})
)
api.state.set = AsyncMock(side_effect=set_power_on)
api.state_set = AsyncMock(side_effect=state_set)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -138,7 +141,7 @@ async def test_switch_power_on_off(hass, mock_config_entry_data, mock_config_ent
)
await hass.async_block_till_done()
assert len(api.state.set.mock_calls) == 1
assert len(api.state_set.mock_calls) == 1
assert (
hass.states.get("switch.product_name_aabbccddeeff_switch").state == STATE_ON
)
@ -156,7 +159,7 @@ async def test_switch_power_on_off(hass, mock_config_entry_data, mock_config_ent
hass.states.get("switch.product_name_aabbccddeeff_switch").state
== STATE_OFF
)
assert len(api.state.set.mock_calls) == 2
assert len(api.state_set.mock_calls) == 2
async def test_switch_lock_power_on_off(
@ -165,17 +168,19 @@ async def test_switch_lock_power_on_off(
"""Test entity turns switch on and off."""
api = get_mock_device()
api.state = AsyncMock()
api.state.power_on = False
api.state.switch_lock = False
api.state = AsyncMock(
return_value=State.from_dict({"power_on": False, "switch_lock": False})
)
def set_switch_lock(switch_lock):
api.state.switch_lock = switch_lock
def state_set(switch_lock):
api.state = AsyncMock(
return_value=State.from_dict({"power_on": True, "switch_lock": switch_lock})
)
api.state.set = AsyncMock(side_effect=set_switch_lock)
api.state_set = AsyncMock(side_effect=state_set)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -199,7 +204,7 @@ async def test_switch_lock_power_on_off(
)
await hass.async_block_till_done()
assert len(api.state.set.mock_calls) == 1
assert len(api.state_set.mock_calls) == 1
assert (
hass.states.get("switch.product_name_aabbccddeeff_switch_lock").state
== STATE_ON
@ -218,7 +223,7 @@ async def test_switch_lock_power_on_off(
hass.states.get("switch.product_name_aabbccddeeff_switch_lock").state
== STATE_OFF
)
assert len(api.state.set.mock_calls) == 2
assert len(api.state_set.mock_calls) == 2
async def test_switch_lock_sets_power_on_unavailable(
@ -227,17 +232,19 @@ async def test_switch_lock_sets_power_on_unavailable(
"""Test entity turns switch on and off."""
api = get_mock_device()
api.state = AsyncMock()
api.state.power_on = True
api.state.switch_lock = False
api.state = AsyncMock(
return_value=State.from_dict({"power_on": True, "switch_lock": False})
)
def set_switch_lock(switch_lock):
api.state.switch_lock = switch_lock
def state_set(switch_lock):
api.state = AsyncMock(
return_value=State.from_dict({"power_on": True, "switch_lock": switch_lock})
)
api.state.set = AsyncMock(side_effect=set_switch_lock)
api.state_set = AsyncMock(side_effect=state_set)
with patch(
"aiohwenergy.HomeWizardEnergy",
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
return_value=api,
):
entry = mock_config_entry
@ -264,7 +271,7 @@ async def test_switch_lock_sets_power_on_unavailable(
)
await hass.async_block_till_done()
assert len(api.state.set.mock_calls) == 1
assert len(api.state_set.mock_calls) == 1
assert (
hass.states.get("switch.product_name_aabbccddeeff_switch").state
== STATE_UNAVAILABLE
@ -290,4 +297,4 @@ async def test_switch_lock_sets_power_on_unavailable(
hass.states.get("switch.product_name_aabbccddeeff_switch_lock").state
== STATE_OFF
)
assert len(api.state.set.mock_calls) == 2
assert len(api.state_set.mock_calls) == 2