mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
Add device_id property, rename context to child_id (#15)
For regular devices, device_id is the mac address and for child devices it is a combination of the mac address and the child_id.
This commit is contained in:
parent
524d28abbc
commit
0f0df481a0
@ -76,7 +76,7 @@ class SmartBulb(SmartDevice):
|
||||
self,
|
||||
host: str,
|
||||
protocol: TPLinkSmartHomeProtocol = None,
|
||||
context: str = None,
|
||||
child_id: str = None,
|
||||
cache_ttl: int = 3,
|
||||
*,
|
||||
ioloop=None,
|
||||
@ -85,7 +85,7 @@ class SmartBulb(SmartDevice):
|
||||
self,
|
||||
host=host,
|
||||
protocol=protocol,
|
||||
context=context,
|
||||
child_id=child_id,
|
||||
cache_ttl=cache_ttl,
|
||||
ioloop=ioloop,
|
||||
)
|
||||
|
@ -111,7 +111,7 @@ class SmartDevice:
|
||||
self,
|
||||
host: str,
|
||||
protocol: Optional[TPLinkSmartHomeProtocol] = None,
|
||||
context: str = None,
|
||||
child_id: str = None,
|
||||
cache_ttl: int = 3,
|
||||
*,
|
||||
ioloop=None,
|
||||
@ -119,20 +119,19 @@ class SmartDevice:
|
||||
"""Create a new SmartDevice instance.
|
||||
|
||||
:param str host: host name or ip address on which the device listens
|
||||
:param context: optional child ID for context in a parent device
|
||||
:param child_id: optional child ID for context in a parent device
|
||||
"""
|
||||
self.host = host
|
||||
if protocol is None: # pragma: no cover
|
||||
protocol = TPLinkSmartHomeProtocol()
|
||||
self.protocol = protocol
|
||||
self.emeter_type = "emeter"
|
||||
self.context = context
|
||||
self.num_children = 0
|
||||
self.child_id = child_id
|
||||
self.cache_ttl = timedelta(seconds=cache_ttl)
|
||||
_LOGGER.debug(
|
||||
"Initializing %s using context %s and cache ttl %s",
|
||||
"Initializing %s using child_id %s and cache ttl %s",
|
||||
self.host,
|
||||
self.context,
|
||||
self.child_id,
|
||||
self.cache_ttl,
|
||||
)
|
||||
self.cache = defaultdict(lambda: defaultdict(lambda: None)) # type: ignore
|
||||
@ -189,8 +188,8 @@ class SmartDevice:
|
||||
:raises SmartDeviceException: if command was not executed correctly
|
||||
"""
|
||||
request: Dict[str, Any] = {target: {cmd: arg}}
|
||||
if self.context is not None:
|
||||
request = {"context": {"child_ids": [self.context]}, target: {cmd: arg}}
|
||||
if self.child_id is not None:
|
||||
request = {"context": {"child_ids": [self.child_id]}, target: {cmd: arg}}
|
||||
|
||||
try:
|
||||
response = self._result_from_cache(target, cmd)
|
||||
@ -219,6 +218,16 @@ class SmartDevice:
|
||||
|
||||
return result
|
||||
|
||||
def _get_child_info(self) -> Dict:
|
||||
"""Return the child information dict, if available.
|
||||
|
||||
:raises SmartDeviceException: if there is no child or it cannot be found.
|
||||
"""
|
||||
for plug in self.sys_info["children"]:
|
||||
if plug["id"] == self.child_id:
|
||||
return plug
|
||||
raise SmartDeviceException("Unable to find children %s")
|
||||
|
||||
def has_emeter(self) -> bool:
|
||||
"""Return if device has an energy meter.
|
||||
|
||||
@ -614,6 +623,18 @@ class SmartDevice:
|
||||
"""
|
||||
raise NotImplementedError("Device subclass needs to implement this.")
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def device_id(self) -> str:
|
||||
"""Return unique ID for the device.
|
||||
|
||||
For regular devices this is the MAC address of the device,
|
||||
for child devices a combination of MAC and child's ID.
|
||||
"""
|
||||
if self.is_child_device:
|
||||
return f"{self.mac}_{self.child_id}"
|
||||
return self.mac
|
||||
|
||||
@property
|
||||
def device_type(self) -> DeviceType:
|
||||
"""Return the device type."""
|
||||
@ -635,7 +656,7 @@ class SmartDevice:
|
||||
return self._device_type == DeviceType.Strip
|
||||
|
||||
@property
|
||||
def is_dimmable(self):
|
||||
def is_dimmable(self) -> bool:
|
||||
"""Return True if the device is dimmable."""
|
||||
return False
|
||||
|
||||
@ -644,6 +665,11 @@ class SmartDevice:
|
||||
"""Return True if the device supports color temperature."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_child_device(self) -> bool:
|
||||
"""Return True if the device is a child device of another device."""
|
||||
return self.child_id is not None
|
||||
|
||||
def __repr__(self):
|
||||
return "<{} model {} at {} ({}), is_on: {} - dev specific: {}>".format(
|
||||
self.__class__.__name__,
|
||||
@ -653,31 +679,3 @@ class SmartDevice:
|
||||
self.is_on,
|
||||
self.state_information,
|
||||
)
|
||||
|
||||
|
||||
class SyncSmartDevice:
|
||||
"""A synchronous SmartDevice speaker class.
|
||||
|
||||
This has the same methods as `SyncSmartDevice`, however, it wraps all async
|
||||
methods and call them in a blocking way.
|
||||
|
||||
Taken from https://github.com/basnijholt/media_player.kef/
|
||||
"""
|
||||
|
||||
def __init__(self, async_device, ioloop):
|
||||
self.async_device = async_device
|
||||
self.ioloop = ioloop
|
||||
|
||||
def __getattr__(self, attr):
|
||||
method = getattr(self.async_device, attr)
|
||||
if method is None:
|
||||
raise AttributeError(f"'SyncSmartDevice' object has no attribute '{attr}.'")
|
||||
if inspect.iscoroutinefunction(method):
|
||||
|
||||
@functools.wraps(method)
|
||||
def wrapped(*args, **kwargs):
|
||||
return self.ioloop.run_until_complete(method(*args, **kwargs))
|
||||
|
||||
return wrapped
|
||||
else:
|
||||
return method
|
||||
|
@ -40,12 +40,12 @@ class SmartPlug(SmartDevice):
|
||||
self,
|
||||
host: str,
|
||||
protocol: "TPLinkSmartHomeProtocol" = None,
|
||||
context: str = None,
|
||||
child_id: str = None,
|
||||
cache_ttl: int = 3,
|
||||
*,
|
||||
ioloop=None,
|
||||
) -> None:
|
||||
SmartDevice.__init__(self, host, protocol, context, cache_ttl, ioloop=ioloop)
|
||||
SmartDevice.__init__(self, host, protocol, child_id, cache_ttl, ioloop=ioloop)
|
||||
self.emeter_type = "emeter"
|
||||
self._device_type = DeviceType.Plug
|
||||
|
||||
@ -90,12 +90,6 @@ class SmartPlug(SmartDevice):
|
||||
else:
|
||||
raise ValueError("Brightness value %s is not valid." % value)
|
||||
|
||||
def _get_child_info(self):
|
||||
for plug in self.sys_info["children"]:
|
||||
if plug["id"] == self.context:
|
||||
return plug
|
||||
raise SmartDeviceException("Unable to find children %s")
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def alias(self) -> str:
|
||||
@ -104,7 +98,7 @@ class SmartPlug(SmartDevice):
|
||||
:return: Device name aka alias.
|
||||
:rtype: str
|
||||
"""
|
||||
if self.context:
|
||||
if self.is_child_device:
|
||||
info = self._get_child_info()
|
||||
return info["alias"]
|
||||
else:
|
||||
@ -140,7 +134,7 @@ class SmartPlug(SmartDevice):
|
||||
|
||||
:return: True if device is on, False otherwise
|
||||
"""
|
||||
if self.context:
|
||||
if self.is_child_device:
|
||||
info = self._get_child_info()
|
||||
return info["state"]
|
||||
|
||||
@ -192,7 +186,7 @@ class SmartPlug(SmartDevice):
|
||||
:rtype: datetime
|
||||
"""
|
||||
sys_info = self.sys_info
|
||||
if self.context:
|
||||
if self.is_child_device:
|
||||
info = self._get_child_info()
|
||||
on_time = info["on_time"]
|
||||
else:
|
||||
|
@ -79,7 +79,7 @@ class SmartStrip(SmartPlug):
|
||||
SmartPlug(
|
||||
self.host,
|
||||
self.protocol,
|
||||
context=child["id"],
|
||||
child_id=child["id"],
|
||||
cache_ttl=self.cache_ttl.total_seconds(),
|
||||
ioloop=self.ioloop,
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user