-
-
Notifications
You must be signed in to change notification settings - Fork 569
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added support for Xiaomi Philips LED Ceiling Lamp #35
Changes from 3 commits
e1ad4e8
0c8ba3b
81638cf
a398528
08ff67e
2a86ecf
e6bbc81
eb2a6cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
from .device import Device | ||
from typing import Any, Dict | ||
|
||
|
||
class Ceil(Device): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
"""Main class representing Xiaomi Philips LED Ceiling Lamp.""" | ||
|
||
# TODO: - Auto On/Off Not Supported | ||
# - Adjust Scens with Wall Switch Not Supported | ||
|
||
def on(self): | ||
"""Power on.""" | ||
return self.send("set_power", ["on"]) | ||
|
||
def off(self): | ||
"""Power off.""" | ||
return self.send("set_power", ["off"]) | ||
|
||
def set_bright(self, level: int): | ||
"""Set brightness level.""" | ||
return self.send("set_bright", [level]) | ||
|
||
def set_cct(self, level: int): | ||
"""Set Correlated Color Temperature.""" | ||
return self.send("set_cct", [level]) | ||
|
||
def delay_off(self, seconds: int): | ||
"""Set delay off seconds.""" | ||
return self.send("delay_off", [seconds]) | ||
|
||
def set_scene(self, num: int): | ||
"""Set scene number.""" | ||
return self.send("apply_fixed_scene", [num]) | ||
|
||
def bl_on(self): | ||
"""Smart Midnight Light On.""" | ||
return self.send("enable_bl", [1]) | ||
|
||
def bl_off(self): | ||
"""Smart Midnight Light off.""" | ||
return self.send("enable_bl", [0]) | ||
|
||
def ac_on(self): | ||
"""Auto CCT On.""" | ||
return self.send("enable_ac", [1]) | ||
|
||
def ac_off(self): | ||
"""Auto CCT Off.""" | ||
return self.send("enable_ac", [0]) | ||
|
||
def status(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be set with typing to return There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kuduka Could to change this line to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've fixed but I think that I did a merge that wasn't needed on this PR, bare with me as I'm not a developer. If I did something wrong, please let me know so I could improve it :) |
||
"""Retrieve properties.""" | ||
properties = ['power', 'bright', 'snm', 'dv', 'cct' | ||
'sw', 'bl', 'mb', 'ac', 'ms', ] | ||
values = self.send( | ||
"get_prop", | ||
properties | ||
) | ||
return CeilStatus(dict(zip(properties, values))) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. blank line at end of file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. blank line at end of file |
||
|
||
class CeilStatus: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
"""Container for status reports from Xiaomi Philips LED Ceiling Lamp""" | ||
|
||
def __init__(self, data: Dict[str, Any]) -> None: | ||
# ['power', 'bright', 'snm', 'dv', 'cctsw', 'bl', 'mb', 'ac', 'ms'] | ||
# ['off', 0, 4, 0, [[0, 3], [0, 2], [0, 1]], 1, 1, 1] | ||
# NOTE: ms doesn't return any value | ||
self.data = data | ||
|
||
@property | ||
def power(self) -> str: | ||
return self.data["power"] | ||
|
||
@property | ||
def is_on(self) -> bool: | ||
return self.power == "on" | ||
|
||
@property | ||
def bright(self) -> int: | ||
return self.data["bright"] | ||
|
||
@property | ||
def snm(self) -> int: | ||
return self.data["snm"] | ||
|
||
@property | ||
def dv(self) -> int: | ||
return self.data["dv"] | ||
|
||
@property | ||
def cctsw(self) -> tuple: | ||
return self.data["cctsw"] | ||
|
||
@property | ||
def bl(self) -> int: | ||
return self.data["bl"] | ||
|
||
@property | ||
def mb(self) -> int: | ||
return self.data["mb"] | ||
|
||
@property | ||
def ac(self) -> int: | ||
return self.data["ac"] | ||
|
||
def __str__(self) -> str: | ||
s = "<CeilStatus power=%s, bright=%s, snm=%s, dv=%s, cctsw=%s " \ | ||
"bl=%s, mb=%s, ac=%s, >" % \ | ||
(self.power, self.bright, self.snm, self.dv, self.cctsw, | ||
self.bl, self.mb, self.ac) | ||
return s |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
# -*- coding: UTF-8 -*- | ||
import logging | ||
import click | ||
import sys | ||
import ipaddress | ||
|
||
if sys.version_info < (3, 4): | ||
print("To use this script you need python 3.4 or newer, got %s" % | ||
sys.version_info) | ||
sys.exit(1) | ||
|
||
import mirobo # noqa: E402 | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
pass_dev = click.make_pass_decorator(mirobo.Ceil) | ||
|
||
|
||
def validate_bright(ctx, param, value): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
value = int(value) | ||
if value < 1 or value > 100: | ||
raise click.BadParameter('Should be a positive int between 1-100.') | ||
return value | ||
|
||
|
||
def validate_seconds(ctx, param, value): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
value = int(value) | ||
if value < 0 or value > 21600: | ||
raise click.BadParameter('Should be a positive int between 1-21600.') | ||
return value | ||
|
||
|
||
def validate_scene(ctx, param, value): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
value = int(value) | ||
if value < 1 or value > 4: | ||
raise click.BadParameter('Should be a positive int between 1-4.') | ||
return value | ||
|
||
|
||
def validate_ip(ctx, param, value): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
try: | ||
ipaddress.ip_address(value) | ||
return value | ||
except ValueError as ex: | ||
raise click.BadParameter("Invalid IP: %s" % ex) | ||
|
||
|
||
def validate_token(ctx, param, value): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
token_len = len(value) | ||
if token_len != 32: | ||
raise click.BadParameter("Token length != 32 chars: %s" % token_len) | ||
return value | ||
|
||
|
||
@click.group(invoke_without_command=True) | ||
@click.option('--ip', envvar="DEVICE_IP", callback=validate_ip) | ||
@click.option('--token', envvar="DEVICE_TOKEN", callback=validate_token) | ||
@click.option('-d', '--debug', default=False, count=True) | ||
@click.pass_context | ||
def cli(ctx, ip: str, token: str, debug: int): | ||
"""A tool to command Xiaomi Philips LED Ceiling Lamp.""" | ||
|
||
if debug: | ||
logging.basicConfig(level=logging.DEBUG) | ||
_LOGGER.info("Debug mode active") | ||
else: | ||
logging.basicConfig(level=logging.INFO) | ||
|
||
# if we are scanning, we do not try to connect. | ||
if ctx.invoked_subcommand == "discover": | ||
return | ||
|
||
if ip is None or token is None: | ||
click.echo("You have to give ip and token!") | ||
sys.exit(-1) | ||
|
||
dev = mirobo.Ceil(ip, token, debug) | ||
_LOGGER.debug("Connecting to %s with token %s", ip, token) | ||
|
||
ctx.obj = dev | ||
|
||
if ctx.invoked_subcommand is None: | ||
ctx.invoke(status) | ||
|
||
|
||
@cli.command() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
def discover(): | ||
"""Search for plugs in the network.""" | ||
mirobo.Ceil.discover() | ||
|
||
|
||
@cli.command() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
@pass_dev | ||
def status(dev: mirobo.Ceil): | ||
"""Returns the state information.""" | ||
res = dev.status() | ||
if not res: | ||
return # bail out | ||
|
||
click.echo(click.style("Power: %s" % res.power, bold=True)) | ||
click.echo("Brightness: %s" % res.bright) | ||
click.echo("Scene Number: %s" % res.snm) | ||
click.echo("dv: %s" % res.dv) | ||
click.echo("Scenes with Wall Switch: %s" % res.cctsw) | ||
click.echo("Smart Midnight Light: %s" % res.bl) | ||
click.echo("Auto On/Off When Mi Band is nearby: %s" % res.mb) | ||
click.echo("Auto CCT: %s" % res.ac) | ||
|
||
|
||
@cli.command() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
@pass_dev | ||
def on(dev: mirobo.Ceil): | ||
"""Power on.""" | ||
click.echo("Power on: %s" % dev.on()) | ||
|
||
|
||
@cli.command() | ||
@pass_dev | ||
def off(dev: mirobo.Ceil): | ||
"""Power off.""" | ||
click.echo("Power off: %s" % dev.off()) | ||
|
||
|
||
@cli.command() | ||
@click.argument('level', callback=validate_bright, required=True,) | ||
@pass_dev | ||
def set_bright(dev: mirobo.Ceil, level): | ||
"""Set brightness level.""" | ||
click.echo("Brightness: %s" % dev.set_bright(level)) | ||
|
||
|
||
@cli.command() | ||
@click.argument('level', callback=validate_bright, required=True,) | ||
@pass_dev | ||
def set_cct(dev: mirobo.Ceil, level): | ||
"""Set CCT level.""" | ||
click.echo("CCT level: %s" % dev.set_cct(level)) | ||
|
||
|
||
@cli.command() | ||
@click.argument('seconds', callback=validate_seconds, required=True,) | ||
@pass_dev | ||
def delay_off(dev: mirobo.Ceil, seconds): | ||
"""Set delay off in seconds.""" | ||
click.echo("Delay off: %s" % dev.delay_off(seconds)) | ||
|
||
|
||
@cli.command() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
@click.argument('scene', callback=validate_scene, required=True,) | ||
@pass_dev | ||
def set_scene(dev: mirobo.Ceil, scene): | ||
"""Set scene number.""" | ||
click.echo("Eyecare Scene: %s" % dev.set_scene(scene)) | ||
|
||
|
||
@cli.command() | ||
@pass_dev | ||
def sml_on(dev: mirobo.Ceil): | ||
"""Smart Midnight Light on.""" | ||
click.echo("Smart Midnight Light On: %s" % dev.bl_on()) | ||
|
||
|
||
@cli.command() | ||
@pass_dev | ||
def sml_off(dev: mirobo.Ceil): | ||
"""Smart Midnight Light off.""" | ||
click.echo("Smart Midnight Light Off: %s" % dev.bl_off()) | ||
|
||
|
||
@cli.command() | ||
@pass_dev | ||
def acct_on(dev: mirobo.Ceil): | ||
"""Auto CCT on.""" | ||
click.echo("Auto CCT On: %s" % dev.ac_on()) | ||
|
||
|
||
@cli.command() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expected 2 blank lines, found 1 |
||
@pass_dev | ||
def acct_off(dev: mirobo.Ceil): | ||
"""Auto CCT on.""" | ||
click.echo("Auto CCT Off: %s" % dev.ac_off()) | ||
|
||
|
||
if __name__ == "__main__": | ||
cli() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expected 2 blank lines, found 1