forked from rytilahti/python-miio
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pull request rytilahti#35 merged for testing.
- Loading branch information
Showing
5 changed files
with
302 additions
and
2 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
# flake8: noqa | ||
from mirobo.protocol import Message, Utils | ||
from mirobo.vacuumcontainers import VacuumStatus, ConsumableStatus, CleaningDetails, CleaningSummary, Timer | ||
from mirobo.containers import VacuumStatus, ConsumableStatus, CleaningDetails, CleaningSummary, Timer | ||
from mirobo.vacuum import Vacuum, VacuumException | ||
from mirobo.plug import Plug | ||
from mirobo.strip import Strip | ||
from mirobo.ceil import Ceil | ||
from mirobo.device import Device, DeviceException |
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,112 @@ | ||
from .device import Device | ||
from typing import Any, Dict | ||
|
||
|
||
class Ceil(Device): | ||
"""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): | ||
"""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))) | ||
|
||
|
||
class CeilStatus: | ||
"""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 |
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,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): | ||
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): | ||
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): | ||
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): | ||
try: | ||
ipaddress.ip_address(value) | ||
return value | ||
except ValueError as ex: | ||
raise click.BadParameter("Invalid IP: %s" % ex) | ||
|
||
|
||
def validate_token(ctx, param, value): | ||
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() | ||
def discover(): | ||
"""Search for plugs in the network.""" | ||
mirobo.Ceil.discover() | ||
|
||
|
||
@cli.command() | ||
@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() | ||
@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() | ||
@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() | ||
@pass_dev | ||
def acct_off(dev: mirobo.Ceil): | ||
"""Auto CCT on.""" | ||
click.echo("Auto CCT Off: %s" % dev.ac_off()) | ||
|
||
|
||
if __name__ == "__main__": | ||
cli() |
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