diff --git a/kasa/device.py b/kasa/device.py index 9bf0903e..ac23fdb2 100644 --- a/kasa/device.py +++ b/kasa/device.py @@ -328,6 +328,11 @@ class Device(ABC): """Send a raw query to the device.""" return await self.protocol.query(request=request) + @property + def parent(self) -> Device | None: + """Return the parent on child devices.""" + return self._parent + @property def children(self) -> Sequence[Device]: """Returns the child devices.""" diff --git a/kasa/iot/iotstrip.py b/kasa/iot/iotstrip.py index e64ace05..3a1406aa 100755 --- a/kasa/iot/iotstrip.py +++ b/kasa/iot/iotstrip.py @@ -307,10 +307,12 @@ class IotStripPlug(IotPlug): The plug inherits (most of) the system information from the parent. """ + _parent: IotStrip + def __init__(self, host: str, parent: IotStrip, child_id: str) -> None: super().__init__(host) - self.parent = parent + self._parent = parent self.child_id = child_id self._last_update = parent._last_update self._set_sys_info(parent.sys_info) @@ -380,7 +382,7 @@ class IotStripPlug(IotPlug): self, target: str, cmd: str, arg: dict | None = None, child_ids=None ) -> Any: """Override query helper to include the child_ids.""" - return await self.parent._query_helper( + return await self._parent._query_helper( target, cmd, arg, child_ids=[self.child_id] ) @@ -441,13 +443,15 @@ class IotStripPlug(IotPlug): @requires_update def model(self) -> str: """Return device model for a child socket.""" - sys_info = self.parent.sys_info + sys_info = self._parent.sys_info return f"Socket for {sys_info['model']}" def _get_child_info(self) -> dict: """Return the subdevice information for this device.""" - for plug in self.parent.sys_info["children"]: + for plug in self._parent.sys_info["children"]: if plug["id"] == self.child_id: return plug - raise KasaException(f"Unable to find children {self.child_id}") + raise KasaException( + f"Unable to find children {self.child_id}" + ) # pragma: no cover diff --git a/kasa/tests/test_childdevice.py b/kasa/tests/test_childdevice.py index 26568c24..251af878 100644 --- a/kasa/tests/test_childdevice.py +++ b/kasa/tests/test_childdevice.py @@ -3,12 +3,19 @@ import sys import pytest +from kasa import Device from kasa.device_type import DeviceType from kasa.smart.smartchilddevice import SmartChildDevice from kasa.smart.smartdevice import NON_HUB_PARENT_ONLY_MODULES from kasa.smartprotocol import _ChildProtocolWrapper -from .conftest import parametrize, parametrize_subtract, strip_smart +from .conftest import ( + parametrize, + parametrize_combine, + parametrize_subtract, + strip_iot, + strip_smart, +) has_children_smart = parametrize( "has children", component_filter="control_child", protocol_filter={"SMART"} @@ -18,6 +25,8 @@ hub_smart = parametrize( ) non_hub_parent_smart = parametrize_subtract(has_children_smart, hub_smart) +has_children = parametrize_combine([has_children_smart, strip_iot]) + @strip_smart def test_childdevice_init(dev, dummy_protocol, mocker): @@ -100,3 +109,14 @@ async def test_parent_only_modules(dev, dummy_protocol, mocker): for child in dev.children: for module in NON_HUB_PARENT_ONLY_MODULES: assert module not in [type(module) for module in child.modules.values()] + + +@has_children +async def test_parent_property(dev: Device): + """Test a child device exposes it's parent.""" + if not dev.children: + pytest.skip(f"Device {dev} fixture does not have any children") + + assert dev.parent is None + for child in dev.children: + assert child.parent == dev