Add support for HomeWizard enable/disable cloud feature (#82573)
This commit is contained in:
parent
093bd00807
commit
27bd1520e8
12 changed files with 148 additions and 11 deletions
|
@ -5,7 +5,7 @@ from datetime import timedelta
|
|||
from typing import TypedDict
|
||||
|
||||
# Set up.
|
||||
from homewizard_energy.models import Data, Device, State
|
||||
from homewizard_energy.models import Data, Device, State, System
|
||||
|
||||
from homeassistant.const import Platform
|
||||
|
||||
|
@ -30,3 +30,4 @@ class DeviceResponseEntry(TypedDict):
|
|||
device: Device
|
||||
data: Data
|
||||
state: State
|
||||
system: System
|
||||
|
|
|
@ -39,8 +39,13 @@ class HWEnergyDeviceUpdateCoordinator(DataUpdateCoordinator[DeviceResponseEntry]
|
|||
"device": await self.api.device(),
|
||||
"data": await self.api.data(),
|
||||
"state": await self.api.state(),
|
||||
"system": None,
|
||||
}
|
||||
|
||||
features = await self.api.features()
|
||||
if features.has_system:
|
||||
data["system"] = await self.api.system()
|
||||
|
||||
except RequestError as ex:
|
||||
raise UpdateFailed("Device did not respond as expected") from ex
|
||||
|
||||
|
|
|
@ -27,6 +27,9 @@ async def async_get_config_entry_diagnostics(
|
|||
"state": asdict(coordinator.data["state"])
|
||||
if coordinator.data["state"] is not None
|
||||
else None,
|
||||
"system": asdict(coordinator.data["system"])
|
||||
if coordinator.data["system"] is not None
|
||||
else None,
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"documentation": "https://www.home-assistant.io/integrations/homewizard",
|
||||
"codeowners": ["@DCSBL"],
|
||||
"dependencies": [],
|
||||
"requirements": ["python-homewizard-energy==1.1.0"],
|
||||
"requirements": ["python-homewizard-energy==1.3.1"],
|
||||
"zeroconf": ["_hwenergy._tcp.local."],
|
||||
"config_flow": true,
|
||||
"iot_class": "local_polling",
|
||||
|
|
|
@ -30,6 +30,13 @@ async def async_setup_entry(
|
|||
]
|
||||
)
|
||||
|
||||
if coordinator.data["system"]:
|
||||
async_add_entities(
|
||||
[
|
||||
HWEnergyEnableCloudEntity(hass, coordinator, entry),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
class HWEnergySwitchEntity(
|
||||
CoordinatorEntity[HWEnergyDeviceUpdateCoordinator], SwitchEntity
|
||||
|
@ -124,3 +131,47 @@ class HWEnergySwitchLockEntity(HWEnergySwitchEntity):
|
|||
def is_on(self) -> bool:
|
||||
"""Return true if switch is on."""
|
||||
return bool(self.coordinator.data["state"].switch_lock)
|
||||
|
||||
|
||||
class HWEnergyEnableCloudEntity(HWEnergySwitchEntity):
|
||||
"""
|
||||
Representation of the enable cloud configuration.
|
||||
|
||||
Turning off 'cloud connection' turns off all communication to HomeWizard Cloud.
|
||||
At this point, the device is fully local.
|
||||
"""
|
||||
|
||||
_attr_name = "Cloud connection"
|
||||
_attr_device_class = SwitchDeviceClass.SWITCH
|
||||
_attr_entity_category = EntityCategory.CONFIG
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
coordinator: HWEnergyDeviceUpdateCoordinator,
|
||||
entry: ConfigEntry,
|
||||
) -> None:
|
||||
"""Initialize the switch."""
|
||||
super().__init__(coordinator, entry, "cloud_connection")
|
||||
self.hass = hass
|
||||
self.entry = entry
|
||||
|
||||
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||
"""Turn cloud connection on."""
|
||||
await self.coordinator.api.system_set(cloud_enabled=True)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||
"""Turn cloud connection off."""
|
||||
await self.coordinator.api.system_set(cloud_enabled=False)
|
||||
await self.coordinator.async_refresh()
|
||||
|
||||
@property
|
||||
def icon(self) -> str | None:
|
||||
"""Return the icon."""
|
||||
return "mdi:cloud" if self.is_on else "mdi:cloud-off-outline"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return true if cloud connection is active."""
|
||||
return bool(self.coordinator.data["system"].cloud_enabled)
|
||||
|
|
|
@ -2003,7 +2003,7 @@ python-gc100==1.0.3a0
|
|||
python-gitlab==1.6.0
|
||||
|
||||
# homeassistant.components.homewizard
|
||||
python-homewizard-energy==1.1.0
|
||||
python-homewizard-energy==1.3.1
|
||||
|
||||
# homeassistant.components.hp_ilo
|
||||
python-hpilo==4.3
|
||||
|
|
|
@ -1399,7 +1399,7 @@ python-forecastio==1.4.0
|
|||
python-fullykiosk==0.0.11
|
||||
|
||||
# homeassistant.components.homewizard
|
||||
python-homewizard-energy==1.1.0
|
||||
python-homewizard-energy==1.3.1
|
||||
|
||||
# homeassistant.components.izone
|
||||
python-izone==1.2.9
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
import json
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from homewizard_energy.models import Data, Device, State
|
||||
from homewizard_energy.features import Features
|
||||
from homewizard_energy.models import Data, Device, State, System
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.homewizard.const import DOMAIN
|
||||
|
@ -37,26 +38,32 @@ def mock_config_entry() -> MockConfigEntry:
|
|||
|
||||
@pytest.fixture
|
||||
def mock_homewizardenergy():
|
||||
"""Return a mocked P1 meter."""
|
||||
"""Return a mocked all-feature device."""
|
||||
with patch(
|
||||
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
|
||||
) as device:
|
||||
client = device.return_value
|
||||
client.features = AsyncMock(return_value=Features("HWE-SKT", "3.01"))
|
||||
client.device = AsyncMock(
|
||||
return_value=Device.from_dict(
|
||||
side_effect=lambda: Device.from_dict(
|
||||
json.loads(load_fixture("homewizard/device.json"))
|
||||
)
|
||||
)
|
||||
client.data = AsyncMock(
|
||||
return_value=Data.from_dict(
|
||||
side_effect=lambda: Data.from_dict(
|
||||
json.loads(load_fixture("homewizard/data.json"))
|
||||
)
|
||||
)
|
||||
client.state = AsyncMock(
|
||||
return_value=State.from_dict(
|
||||
side_effect=lambda: State.from_dict(
|
||||
json.loads(load_fixture("homewizard/state.json"))
|
||||
)
|
||||
)
|
||||
client.system = AsyncMock(
|
||||
side_effect=lambda: System.from_dict(
|
||||
json.loads(load_fixture("homewizard/system.json"))
|
||||
)
|
||||
)
|
||||
yield device
|
||||
|
||||
|
||||
|
|
3
tests/components/homewizard/fixtures/system.json
Normal file
3
tests/components/homewizard/fixtures/system.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"cloud_enabled": true
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from homewizard_energy.features import Features
|
||||
from homewizard_energy.models import Device
|
||||
|
||||
|
||||
|
@ -10,6 +11,7 @@ def get_mock_device(
|
|||
host="1.2.3.4",
|
||||
product_name="P1 meter",
|
||||
product_type="HWE-P1",
|
||||
firmware_version="1.00",
|
||||
):
|
||||
"""Return a mock bridge."""
|
||||
mock_device = AsyncMock()
|
||||
|
@ -21,11 +23,15 @@ def get_mock_device(
|
|||
product_type=product_type,
|
||||
serial=serial,
|
||||
api_version="V1",
|
||||
firmware_version="1.00",
|
||||
firmware_version=firmware_version,
|
||||
)
|
||||
)
|
||||
mock_device.data = AsyncMock(return_value=None)
|
||||
mock_device.state = AsyncMock(return_value=None)
|
||||
mock_device.system = AsyncMock(return_value=None)
|
||||
mock_device.features = AsyncMock(
|
||||
return_value=Features(product_type, firmware_version)
|
||||
)
|
||||
|
||||
mock_device.close = AsyncMock()
|
||||
|
||||
|
|
|
@ -45,5 +45,6 @@ async def test_diagnostics(
|
|||
"total_liter_m3": 1234.567,
|
||||
},
|
||||
"state": {"power_on": True, "switch_lock": False, "brightness": 255},
|
||||
"system": {"cloud_enabled": True},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from homewizard_energy.models import State
|
||||
from homewizard_energy.models import State, System
|
||||
|
||||
from homeassistant.components import switch
|
||||
from homeassistant.components.switch import SwitchDeviceClass
|
||||
|
@ -286,3 +286,63 @@ async def test_switch_lock_sets_power_on_unavailable(
|
|||
== STATE_OFF
|
||||
)
|
||||
assert len(api.state_set.mock_calls) == 2
|
||||
|
||||
|
||||
async def test_cloud_connection_on_off(hass, mock_config_entry_data, mock_config_entry):
|
||||
"""Test entity turns switch on and off."""
|
||||
|
||||
api = get_mock_device(product_type="HWE-SKT", firmware_version="3.02")
|
||||
api.system = AsyncMock(return_value=System.from_dict({"cloud_enabled": False}))
|
||||
|
||||
def system_set(cloud_enabled):
|
||||
api.system = AsyncMock(
|
||||
return_value=System.from_dict({"cloud_enabled": cloud_enabled})
|
||||
)
|
||||
|
||||
api.system_set = AsyncMock(side_effect=system_set)
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.homewizard.coordinator.HomeWizardEnergy",
|
||||
return_value=api,
|
||||
):
|
||||
entry = mock_config_entry
|
||||
entry.data = mock_config_entry_data
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
hass.states.get("switch.product_name_aabbccddeeff_cloud_connection").state
|
||||
== STATE_OFF
|
||||
)
|
||||
|
||||
# Enable cloud
|
||||
await hass.services.async_call(
|
||||
switch.DOMAIN,
|
||||
SERVICE_TURN_ON,
|
||||
{"entity_id": "switch.product_name_aabbccddeeff_cloud_connection"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert len(api.system_set.mock_calls) == 1
|
||||
assert (
|
||||
hass.states.get("switch.product_name_aabbccddeeff_cloud_connection").state
|
||||
== STATE_ON
|
||||
)
|
||||
|
||||
# Disable cloud
|
||||
await hass.services.async_call(
|
||||
switch.DOMAIN,
|
||||
SERVICE_TURN_OFF,
|
||||
{"entity_id": "switch.product_name_aabbccddeeff_cloud_connection"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
assert (
|
||||
hass.states.get("switch.product_name_aabbccddeeff_cloud_connection").state
|
||||
== STATE_OFF
|
||||
)
|
||||
assert len(api.system_set.mock_calls) == 2
|
||||
|
|
Loading…
Add table
Reference in a new issue