-
-
Notifications
You must be signed in to change notification settings - Fork 540
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial support for HUIZUO PISCES For Bedroom (#868)
* Adding Huizuo basic support * Add huizuo.py to the repo * Update __init__.py to the latest version * Fix _LOGGER error * Enabling iSort in VSCode on Save * 1. Removed unnecessary click.argument calls 2. Changing "color_temp" instead of "level" during manipulation with color temperature * 1. Removed Huizuo from discovery - devices are not mdns discoverable 2. Updated based on PR#868 * Re-arranged color_temp parameter for better understanding * fixing linting issues * Added only example of JSON payload from the lamp * Updated README.rst, added separations * 1. Use MiotDevice class 2. Add tests * Fixing linting issues * Fixing linting issue - second try... * Processing comments from PR#868
- Loading branch information
Showing
4 changed files
with
242 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
""" | ||
Basic implementation for HUAYI HUIZUO PISCES For Bedroom (huayi.light.pis123) lamp | ||
This lamp is white color only and supports dimming and control of the temperature from 3000K to 6400K | ||
Specs: https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:light:0000A001:huayi-pis123:1 | ||
""" | ||
|
||
import logging | ||
from typing import Any, Dict | ||
|
||
import click | ||
|
||
from .click_common import command, format_output | ||
from .exceptions import DeviceException | ||
from .miot_device import MiotDevice | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
_MAPPING = { | ||
"power": {"siid": 2, "piid": 1}, | ||
"brightness": {"siid": 2, "piid": 2}, | ||
"color_temp": {"siid": 2, "piid": 3}, | ||
} | ||
|
||
MODEL_HUIZUO_PIS123 = "huayi.light.pis123" | ||
|
||
MODELS_SUPPORTED = [MODEL_HUIZUO_PIS123] | ||
|
||
|
||
class HuizuoException(DeviceException): | ||
pass | ||
|
||
|
||
class HuizuoStatus: | ||
def __init__(self, data: Dict[str, Any]) -> None: | ||
""" | ||
Response of a Huizuo Pisces For Bedroom (huayi.light.pis123) | ||
{'id': 1, 'result': [ | ||
{'did': '', 'siid': 2, 'piid': 1, 'code': 0, 'value': False}, | ||
{'did': '', 'siid': 2, 'piid': 2, 'code': 0, 'value': 94}, | ||
{'did': '', 'siid': 2, 'piid': 3, 'code': 0, 'value': 6400} | ||
] | ||
} | ||
Explanation (line-by-line): | ||
power = '{"siid":2,"piid":1}' values = true,false | ||
brightless(%) = '{"siid":2,"piid":2}' values = 1-100 | ||
color temperature(Kelvin) = '{"siid":2,"piid":3}' values = 3000-6400 | ||
""" | ||
|
||
self.data = data | ||
|
||
@property | ||
def is_on(self) -> bool: | ||
"""Return True if device is on.""" | ||
return self.data["power"] | ||
|
||
@property | ||
def brightness(self) -> int: | ||
"""Return current brightness.""" | ||
return self.data["brightness"] | ||
|
||
@property | ||
def color_temp(self) -> int: | ||
"""Return current color temperature.""" | ||
return self.data["color_temp"] | ||
|
||
def __repr__(self): | ||
s = "<Huizuo on=%s brightness=%s color_temp=%s>" % ( | ||
self.is_on, | ||
self.brightness, | ||
self.color_temp, | ||
) | ||
return s | ||
|
||
|
||
class Huizuo(MiotDevice): | ||
"""A support for Huizuo PIS123.""" | ||
|
||
def __init__( | ||
self, | ||
ip: str = None, | ||
token: str = None, | ||
start_id: int = 0, | ||
debug: int = 0, | ||
lazy_discover: bool = True, | ||
model: str = MODEL_HUIZUO_PIS123, | ||
) -> None: | ||
super().__init__(_MAPPING, ip, token, start_id, debug, lazy_discover) | ||
|
||
if model in MODELS_SUPPORTED: | ||
self.model = model | ||
else: | ||
self.model = MODEL_HUIZUO_PIS123 | ||
_LOGGER.error( | ||
"Device model %s unsupported. Falling back to %s.", model, self.model | ||
) | ||
|
||
@command( | ||
default_output=format_output("Powering on"), | ||
) | ||
def on(self): | ||
"""Power on.""" | ||
return self.set_property("power", True) | ||
|
||
@command( | ||
default_output=format_output("Powering off"), | ||
) | ||
def off(self): | ||
"""Power off.""" | ||
return self.set_property("power", False) | ||
|
||
@command( | ||
default_output=format_output( | ||
"\n", | ||
"Power: {result.is_on}\n" | ||
"Brightness: {result.brightness}\n" | ||
"Color Temperature: {result.color_temp}\n" | ||
"\n", | ||
) | ||
) | ||
def status(self) -> HuizuoStatus: | ||
"""Retrieve properties.""" | ||
|
||
return HuizuoStatus( | ||
{ | ||
prop["did"]: prop["value"] if prop["code"] == 0 else None | ||
for prop in self.get_properties_for_mapping() | ||
} | ||
) | ||
|
||
@command( | ||
click.argument("level", type=int), | ||
default_output=format_output("Setting brightness to {level}"), | ||
) | ||
def set_brightness(self, level): | ||
"""Set brightness.""" | ||
if level < 0 or level > 100: | ||
raise HuizuoException("Invalid brightness: %s" % level) | ||
|
||
return self.set_property("brightness", level) | ||
|
||
@command( | ||
click.argument("color_temp", type=int), | ||
default_output=format_output("Setting color temperature to {color_temp}"), | ||
) | ||
def set_color_temp(self, color_temp): | ||
"""Set color temp in kelvin.""" | ||
if color_temp < 3000 or color_temp > 6400: | ||
raise HuizuoException("Invalid color temperature: %s" % color_temp) | ||
|
||
return self.set_property("color_temp", color_temp) |
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,87 @@ | ||
from unittest import TestCase | ||
|
||
import pytest | ||
|
||
from miio import Huizuo | ||
from miio.huizuo import HuizuoException | ||
|
||
from .dummies import DummyMiotDevice | ||
|
||
_INITIAL_STATE = { | ||
"power": True, | ||
"brightness": 60, | ||
"color_temp": 4000, | ||
} | ||
|
||
|
||
class DummyHuizuo(DummyMiotDevice, Huizuo): | ||
def __init__(self, *args, **kwargs): | ||
self.state = _INITIAL_STATE | ||
self.return_values = { | ||
"get_prop": self._get_state, | ||
"set_power": lambda x: self._set_state("power", x), | ||
"set_brightness": lambda x: self._set_state("brightness", x), | ||
} | ||
super().__init__(*args, **kwargs) | ||
|
||
|
||
@pytest.fixture(scope="function") | ||
def huizuo(request): | ||
request.cls.device = DummyHuizuo() | ||
|
||
|
||
@pytest.mark.usefixtures("huizuo") | ||
class TestHuizuo(TestCase): | ||
def test_on(self): | ||
self.device.off() # ensure off | ||
assert self.device.status().is_on is False | ||
|
||
self.device.on() | ||
assert self.device.status().is_on is True | ||
|
||
def test_off(self): | ||
self.device.on() # ensure on | ||
assert self.device.status().is_on is True | ||
|
||
self.device.off() | ||
assert self.device.status().is_on is False | ||
|
||
def test_status(self): | ||
status = self.device.status() | ||
assert status.is_on is _INITIAL_STATE["power"] | ||
assert status.brightness is _INITIAL_STATE["brightness"] | ||
assert status.color_temp is _INITIAL_STATE["color_temp"] | ||
|
||
def test_brightness(self): | ||
def lamp_brightness(): | ||
return self.device.status().brightness | ||
|
||
self.device.set_brightness(1) | ||
assert lamp_brightness() == 1 | ||
self.device.set_brightness(64) | ||
assert lamp_brightness() == 64 | ||
self.device.set_brightness(100) | ||
assert lamp_brightness() == 100 | ||
|
||
with pytest.raises(HuizuoException): | ||
self.device.set_brightness(-1) | ||
|
||
with pytest.raises(HuizuoException): | ||
self.device.set_brightness(101) | ||
|
||
def test_color_temp(self): | ||
def lamp_color_temp(): | ||
return self.device.status().color_temp | ||
|
||
self.device.set_color_temp(3000) | ||
assert lamp_color_temp() == 3000 | ||
self.device.set_color_temp(4200) | ||
assert lamp_color_temp() == 4200 | ||
self.device.set_color_temp(6400) | ||
assert lamp_color_temp() == 6400 | ||
|
||
with pytest.raises(HuizuoException): | ||
self.device.set_color_temp(2999) | ||
|
||
with pytest.raises(HuizuoException): | ||
self.device.set_color_temp(6401) |