-
-
Notifications
You must be signed in to change notification settings - Fork 563
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add philips.light.rwread support (#589)
* First draft * Add tests * Fix docstring
- Loading branch information
Showing
5 changed files
with
401 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
import enum | ||
import logging | ||
from collections import defaultdict | ||
from typing import Any, Dict | ||
|
||
import click | ||
|
||
from .click_common import EnumType, command, format_output | ||
from .device import Device, DeviceException | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
MODEL_PHILIPS_LIGHT_RWREAD = "philips.light.rwread" | ||
|
||
AVAILABLE_PROPERTIES = { | ||
MODEL_PHILIPS_LIGHT_RWREAD: ["power", "bright", "dv", "snm", "flm", "chl", "flmv"], | ||
} | ||
|
||
|
||
class PhilipsRwreadException(DeviceException): | ||
pass | ||
|
||
|
||
class MotionDetectionSensitivity(enum.Enum): | ||
Low = 1 | ||
Medium = 2 | ||
High = 3 | ||
|
||
|
||
class PhilipsRwreadStatus: | ||
"""Container for status reports from Xiaomi Philips RW Read""" | ||
|
||
def __init__(self, data: Dict[str, Any]) -> None: | ||
""" | ||
Response of a RW Read (philips.light.rwread): | ||
{'power': 'on', 'bright': 53, 'dv': 0, 'snm': 1, | ||
'flm': 0, 'chl': 0, 'flmv': 0} | ||
""" | ||
self.data = data | ||
|
||
@property | ||
def power(self) -> str: | ||
"""Power state.""" | ||
return self.data["power"] | ||
|
||
@property | ||
def is_on(self) -> bool: | ||
"""True if the device is turned on.""" | ||
return self.power == "on" | ||
|
||
@property | ||
def brightness(self) -> int: | ||
"""Current brightness.""" | ||
return self.data["bright"] | ||
|
||
@property | ||
def delay_off_countdown(self) -> int: | ||
"""Countdown until turning off in seconds.""" | ||
return self.data["dv"] | ||
|
||
@property | ||
def scene(self) -> int: | ||
"""Current fixed scene.""" | ||
return self.data["snm"] | ||
|
||
@property | ||
def motion_detection(self) -> bool: | ||
"""True if motion detection is enabled.""" | ||
return self.data["flm"] == 1 | ||
|
||
@property | ||
def motion_detection_sensitivity(self) -> MotionDetectionSensitivity: | ||
"""The sensitivity of the motion detection.""" | ||
return MotionDetectionSensitivity(self.data["flmv"]) | ||
|
||
@property | ||
def child_lock(self) -> bool: | ||
"""True if child lock is enabled.""" | ||
return self.data["chl"] == 1 | ||
|
||
def __repr__(self) -> str: | ||
s = ( | ||
"<PhilipsRwreadStatus power=%s, " | ||
"brightness=%s, " | ||
"delay_off_countdown=%s, " | ||
"scene=%s, " | ||
"motion_detection=%s, " | ||
"motion_detection_sensitivity=%s, " | ||
"child_lock=%s>" | ||
% ( | ||
self.power, | ||
self.brightness, | ||
self.delay_off_countdown, | ||
self.scene, | ||
self.motion_detection, | ||
self.motion_detection_sensitivity, | ||
self.child_lock, | ||
) | ||
) | ||
return s | ||
|
||
def __json__(self): | ||
return self.data | ||
|
||
|
||
class PhilipsRwread(Device): | ||
"""Main class representing Xiaomi Philips RW Read.""" | ||
|
||
def __init__( | ||
self, | ||
ip: str = None, | ||
token: str = None, | ||
start_id: int = 0, | ||
debug: int = 0, | ||
lazy_discover: bool = True, | ||
model: str = MODEL_PHILIPS_LIGHT_RWREAD, | ||
) -> None: | ||
super().__init__(ip, token, start_id, debug, lazy_discover) | ||
|
||
if model in AVAILABLE_PROPERTIES: | ||
self.model = model | ||
else: | ||
self.model = MODEL_PHILIPS_LIGHT_RWREAD | ||
|
||
@command( | ||
default_output=format_output( | ||
"", | ||
"Power: {result.power}\n" | ||
"Brightness: {result.brightness}\n" | ||
"Delayed turn off: {result.delay_off_countdown}\n" | ||
"Scene: {result.scene}\n" | ||
"Motion detection: {result.motion_detection}\n" | ||
"Motion detection sensitivity: {result.motion_detection_sensitivity}\n" | ||
"Child lock: {result.child_lock}\n", | ||
) | ||
) | ||
def status(self) -> PhilipsRwreadStatus: | ||
"""Retrieve properties.""" | ||
properties = AVAILABLE_PROPERTIES[self.model] | ||
values = self.send("get_prop", properties) | ||
properties_count = len(properties) | ||
values_count = len(values) | ||
if properties_count != values_count: | ||
_LOGGER.debug( | ||
"Count (%s) of requested properties does not match the " | ||
"count (%s) of received values.", | ||
properties_count, | ||
values_count, | ||
) | ||
|
||
return PhilipsRwreadStatus(defaultdict(lambda: None, zip(properties, values))) | ||
|
||
@command(default_output=format_output("Powering on")) | ||
def on(self): | ||
"""Power on.""" | ||
return self.send("set_power", ["on"]) | ||
|
||
@command(default_output=format_output("Powering off")) | ||
def off(self): | ||
"""Power off.""" | ||
return self.send("set_power", ["off"]) | ||
|
||
@command( | ||
click.argument("level", type=int), | ||
default_output=format_output("Setting brightness to {level}"), | ||
) | ||
def set_brightness(self, level: int): | ||
"""Set brightness level of the primary light.""" | ||
if level < 1 or level > 100: | ||
raise PhilipsRwreadException("Invalid brightness: %s" % level) | ||
|
||
return self.send("set_bright", [level]) | ||
|
||
@command( | ||
click.argument("number", type=int), | ||
default_output=format_output("Setting fixed scene to {number}"), | ||
) | ||
def set_scene(self, number: int): | ||
"""Set one of the fixed eyecare user scenes.""" | ||
if number < 1 or number > 4: | ||
raise PhilipsRwreadException("Invalid fixed scene number: %s" % number) | ||
|
||
return self.send("apply_fixed_scene", [number]) | ||
|
||
@command( | ||
click.argument("seconds", type=int), | ||
default_output=format_output("Setting delayed turn off to {seconds} seconds"), | ||
) | ||
def delay_off(self, seconds: int): | ||
"""Set delay off in seconds.""" | ||
|
||
if seconds < 0: | ||
raise PhilipsRwreadException( | ||
"Invalid value for a delayed turn off: %s" % seconds | ||
) | ||
|
||
return self.send("delay_off", [seconds]) | ||
|
||
@command( | ||
click.argument("motion_detection", type=bool), | ||
default_output=format_output( | ||
lambda motion_detection: "Turning on motion detection" | ||
if motion_detection | ||
else "Turning off motion detection" | ||
), | ||
) | ||
def set_motion_detection(self, motion_detection: bool): | ||
"""Set motion detection on/off.""" | ||
return self.send("enable_flm", [int(motion_detection)]) | ||
|
||
@command( | ||
click.argument("sensitivity", type=EnumType(MotionDetectionSensitivity, False)), | ||
default_output=format_output( | ||
"Setting motion detection sensitivity to {sensitivity}" | ||
), | ||
) | ||
def set_motion_detection_sensitivity(self, sensitivity: MotionDetectionSensitivity): | ||
"""Set motion detection sensitivity.""" | ||
return self.send("set_flmvalue", [sensitivity.value]) | ||
|
||
@command( | ||
click.argument("lock", type=bool), | ||
default_output=format_output( | ||
lambda lock: "Turning on child lock" if lock else "Turning off child lock" | ||
), | ||
) | ||
def set_child_lock(self, lock: bool): | ||
"""Set child lock on/off.""" | ||
return self.send("enable_chl", [int(lock)]) |
Oops, something went wrong.