Skip to content

Commit

Permalink
Merge pull request #625 from petretiandrea/release/2.12.0
Browse files Browse the repository at this point in the history
Release 2.12.0
  • Loading branch information
petretiandrea authored Nov 27, 2023
2 parents a8aee7f + e56c7c7 commit 96fcefa
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 71 deletions.
47 changes: 24 additions & 23 deletions .github/workflows/validation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
- dev

env:
DEFAULT_PYTHON: 3.9
DEFAULT_PYTHON: 3.11

jobs:
pre-commit:
Expand Down Expand Up @@ -52,25 +52,26 @@ jobs:
with:
category: "integration"

# tests:
# runs-on: "ubuntu-latest"
# name: Run tests
# steps:
# - name: Check out code from GitHub
# uses: "actions/[email protected]"
# - name: Setup Python ${{ env.DEFAULT_PYTHON }}
# uses: "actions/[email protected]"
# with:
# python-version: ${{ env.DEFAULT_PYTHON }}
# - name: Install requirements
# run: |
# pip install --constraint=.github/workflows/constraints.txt pip
# pip install -r requirements_test.txt
# - name: Tests suite
# run: |
# pytest \
# --timeout=9 \
# --durations=10 \
# -n auto \
# -p no:sugar \
# tests
tests:
runs-on: "ubuntu-latest"
name: Run tests
steps:
- name: Check out code from GitHub
uses: actions/[email protected]
- name: Setup Python ${{ env.DEFAULT_PYTHON }}
uses: actions/[email protected]
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Install requirements
run: |
pip install --constraint=.github/workflows/constraints.txt pip
pip install -r requirements_test.txt
- name: Unit tests suite
run: |
pytest \
--timeout=9 \
--durations=10 \
--cov-fail-under=40 \
-n auto \
-p no:sugar \
tests/unit
4 changes: 2 additions & 2 deletions custom_components/tapo/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@
"p115",
"p125",
"p125m",
"s500",
"p110m",
"tp15",
"p100m",
]
SUPPORTED_DEVICE_AS_SWITCH_POWER_MONITOR = ["p110", "p115", "p110m"]
SUPPORTED_DEVICE_AS_SWITCH_POWER_MONITOR = ["p110", "p115", "p110m", "p125m"]
SUPPORTED_DEVICE_AS_LIGHT = {
"l920": [ColorMode.ONOFF, ColorMode.BRIGHTNESS, ColorMode.HS],
"l930": [ColorMode.ONOFF, ColorMode.BRIGHTNESS, ColorMode.COLOR_TEMP, ColorMode.HS],
Expand All @@ -35,6 +34,7 @@
"tl33": [ColorMode.ONOFF, ColorMode.BRIGHTNESS, ColorMode.COLOR_TEMP, ColorMode.HS],
"tl31": [ColorMode.ONOFF, ColorMode.BRIGHTNESS, ColorMode.COLOR_TEMP],
"s500d": [ColorMode.ONOFF, ColorMode.BRIGHTNESS],
"s505d": [ColorMode.ONOFF, ColorMode.BRIGHTNESS],
"s500": [ColorMode.ONOFF, ColorMode.BRIGHTNESS],
"s505": [ColorMode.ONOFF],
"ts15": [ColorMode.ONOFF],
Expand Down
60 changes: 34 additions & 26 deletions custom_components/tapo/hub/tapo_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
from plugp100.api.hub.hub_device import HubDevice
from plugp100.api.hub.hub_device_tracker import DeviceAdded
from plugp100.api.hub.hub_device_tracker import HubDeviceEvent
from plugp100.api.hub.ke100_device import KE100Device
from plugp100.api.hub.s200b_device import S200ButtonDevice
from plugp100.api.hub.switch_child_device import SwitchChildDevice
from plugp100.api.hub.t100_device import T100MotionSensor
from plugp100.api.hub.t110_device import T110SmartDoor
from plugp100.api.hub.t31x_device import T31Device
from plugp100.api.hub.ke100_device import KE100Device
from plugp100.responses.device_state import DeviceInfo
from plugp100.responses.hub_childs.hub_child_base_info import HubChildBaseInfo

Expand Down Expand Up @@ -60,9 +60,13 @@ async def initialize_hub(self, hass: HomeAssistant):
device_list = (
(await self.hub.get_children()).get_or_else([]).get_children_base_info()
)
await self.setup_child_devices(hass, registry, device_list)
child_coordinators = await self.setup_child_coordinators(
hass, device_list, polling_rate
_LOGGER.info(
"Found %d children associated to hub %s",
len(device_list),
device_info.device_id,
)
child_coordinators = await self.setup_children(
hass, registry, device_list, polling_rate
)
hass.data[DOMAIN][self.entry.entry_id] = HassTapoDeviceData(
coordinator=hub_coordinator,
Expand All @@ -89,42 +93,46 @@ async def _handle_child_device_event(event: HubDeviceEvent):
await hass.config_entries.async_forward_entry_setups(self.entry, HUB_PLATFORMS)
return True

async def setup_child_devices(
async def setup_children(
self,
hass: HomeAssistant,
registry: DeviceRegistry,
device_list: list[HubChildBaseInfo],
):
knwon_children = [
self.add_child_device(registry, device_state).id
for device_state in device_list
polling_rate: timedelta,
) -> List[TapoHubChildCoordinator]:
setup_results = [
await self._add_hass_tapo_child_device(
hass, registry, child_device, polling_rate
)
for child_device in device_list
]
device_entries, child_coordinators = zip(*setup_results)

# delete device which is no longer available to hub
for device in dr.async_entries_for_config_entry(registry, self.entry.entry_id):
# avoid delete hub device which has a connection
if device.id not in knwon_children and len(device.connections) == 0:
if (
device.id not in map(lambda x: x.id, device_entries)
and len(device.connections) == 0
):
registry.async_remove_device(device.id)

async def setup_child_coordinators(
return child_coordinators

async def _add_hass_tapo_child_device(
self,
hass: HomeAssistant,
device_list: list[HubChildBaseInfo],
registry: DeviceRegistry,
device_state: HubChildBaseInfo,
polling_rate: timedelta,
) -> List[TapoHubChildCoordinator]:
child_coordinators = []
for child in device_list:
child_device = _create_child_device(child, self.hub)
if child_device is not None:
child_coordinator = TapoHubChildCoordinator(
hass, child_device, polling_rate
)
await child_coordinator.async_config_entry_first_refresh()
child_coordinators.append(child_coordinator)

return child_coordinators

def add_child_device(
) -> (DeviceEntry, TapoHubChildCoordinator):
entry = self._hass_add_child_device(registry, device_state)
child_device = _create_child_device(device_state, self.hub)
coordinator = TapoHubChildCoordinator(hass, child_device, polling_rate)
await coordinator.async_config_entry_first_refresh()
return (entry, coordinator)

def _hass_add_child_device(
self, registry: DeviceRegistry, device_state: HubChildBaseInfo
) -> DeviceEntry:
return registry.async_get_or_create(
Expand Down
80 changes: 60 additions & 20 deletions tests/unit/hub/test_tapo_hub.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
import pytest
from datetime import timedelta

from unittest.mock import Mock
from unittest.mock import patch

import pytest
from custom_components.tapo.hub.tapo_hub import TapoHub
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant

from homeassistant.helpers.device_registry import DeviceRegistry
from plugp100.api.hub.hub_device import HubDevice
from plugp100.responses.hub_childs.hub_child_base_info import HubChildBaseInfo
from plugp100.api.hub.ke100_device import KE100Device
from plugp100.api.hub.s200b_device import S200ButtonDevice
from plugp100.api.hub.switch_child_device import SwitchChildDevice
from plugp100.api.hub.t100_device import T100MotionSensor
from plugp100.api.hub.t110_device import T110SmartDoor
from plugp100.api.hub.t31x_device import T31Device

from custom_components.tapo.hub.tapo_hub import TapoHub
from plugp100.responses.hub_childs.hub_child_base_info import HubChildBaseInfo


class TestTapoHub:
config = Mock(ConfigEntry)
hub_device = Mock(HubDevice)
hass = Mock(HomeAssistant)
registry = Mock(DeviceRegistry)
polling_rate = Mock(timedelta)

@pytest.mark.asyncio
Expand All @@ -44,17 +43,58 @@ async def test_setup_child_coordinators_should_create_correct_types(
with patch(
"homeassistant.helpers.update_coordinator.DataUpdateCoordinator.async_config_entry_first_refresh"
):
base_child_info = Mock(HubChildBaseInfo)
base_child_info.model = model
base_child_info.device_id = "123ABC"

hub = TapoHub(entry=self.config, hub=self.hub_device)
result = await hub.setup_child_coordinators(
hass=self.hass,
device_list=[base_child_info],
polling_rate=self.polling_rate,
)

assert len(result) == 1
assert type(result[0].device) == expected_type
print(result[0].device)
with patch(
"homeassistant.helpers.device_registry.DeviceRegistry.async_get_or_create"
):
with patch(
"homeassistant.helpers.device_registry.async_entries_for_config_entry"
):
base_child_info = Mock(HubChildBaseInfo)
base_child_info.model = model
base_child_info.device_id = "123ABC"
base_child_info.nickname = "123ABC"
base_child_info.firmware_version = "1.2.3"
base_child_info.hardware_version = "hw1.0"

hub = TapoHub(entry=self.config, hub=self.hub_device)
result = await hub.setup_children(
hass=self.hass,
registry=self.registry,
device_list=[base_child_info],
polling_rate=self.polling_rate,
)

assert len(result) == 1
assert type(result[0].device) is expected_type

@pytest.mark.asyncio
@pytest.mark.parametrize("children_count", [10, 50, 55, 101])
async def test_setup_all_children(self, children_count: int):
with patch(
"homeassistant.helpers.update_coordinator.DataUpdateCoordinator.async_config_entry_first_refresh"
):
with patch(
"homeassistant.helpers.device_registry.DeviceRegistry.async_get_or_create"
):
with patch(
"homeassistant.helpers.device_registry.async_entries_for_config_entry"
):
children = []
for i in range(0, children_count):
mock = Mock(HubChildBaseInfo)
mock.model = "T110"
mock.device_id = f"123ABC{i}"
mock.nickname = f"123ABC{i}"
mock.firmware_version = f"1.2.{i}"
mock.hardware_version = f"hw{i}.0"
children.append(mock)

hub = TapoHub(entry=self.config, hub=self.hub_device)
result = await hub.setup_children(
hass=self.hass,
registry=self.registry,
device_list=children,
polling_rate=self.polling_rate,
)

assert len(result) == children_count

0 comments on commit 96fcefa

Please sign in to comment.