Skip to content

Commit

Permalink
Added support for Xiaomi Philips Eyecare Smart Lamp 2 (#34)
Browse files Browse the repository at this point in the history
* Added support for Xiaomi Philips Eyecare Smart Lamp 2

* [FIX] Adding blank lines for houndci-bot complains

* [FIX] houndci-bot errors second time
  • Loading branch information
kuduka authored and rytilahti committed Aug 14, 2017
1 parent 2e3039b commit 3b886ad
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 1 deletion.
1 change: 1 addition & 0 deletions mirobo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
from mirobo.plug_v1 import PlugV1
from mirobo.airpurifier import AirPurifier
from mirobo.strip import Strip
from mirobo.philips_eyecare import PhilipsEyecare
from mirobo.device import Device, DeviceException
134 changes: 134 additions & 0 deletions mirobo/philips_eyecare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from .device import Device
from typing import Any, Dict


class PhilipsEyecare(Device):
"""Main class representing Xiaomi Philips Eyecare Smart Lamp 2."""

def on(self):
"""Power on."""
return self.send("set_power", ["on"])

def off(self):
"""Power off."""
return self.send("set_power", ["off"])

def eyecare_on(self):
"""Eyecare on."""
return self.send("set_eyecare", ["on"])

def eyecare_off(self):
"""Eyecare off."""
return self.send("set_eyecare", ["off"])

def set_bright(self, level: int):
"""Set brightness level."""
return self.send("set_bright", [level])

def set_user_scene(self, num: int):
"""Set eyecare user scene."""
return self.send("set_user_scene", [num])

def delay_off(self, minutes: int):
"""Set delay off minutes."""
return self.send("delay_off", [minutes])

def bl_on(self):
"""Night Light On."""
return self.send("enable_bl", ["on"])

def bl_off(self):
"""Night Light Off."""
return self.send("enable_bl", ["off"])

def notify_user_on(self):
"""Notify User On."""
return self.send("set_notifyuser", ["on"])

def notify_user_off(self):
"""Notify USer Off."""
return self.send("set_notifyuser", ["off"])

def amb_on(self):
"""Amblient Light On."""
return self.send("enable_amb", ["on"])

def amb_off(self):
"""Ambient Light Off."""
return self.send("enable_amb", ["off"])

def set_amb_bright(self, level: int):
"""Set Ambient Light brightness level."""
return self.send("set_amb_bright", [level])

def status(self):
"""Retrieve properties."""
properties = ['power', 'bright', 'notifystatus', 'ambstatus',
'ambvalue', 'eyecare', 'scene_num', 'bls',
'dvalue', ]
values = self.send(
"get_prop",
properties
)
return PhilipsEyecareStatus(dict(zip(properties, values)))


class PhilipsEyecareStatus:
"""Container for status reports from Xiaomi Philips Eyecare Smart Lamp 2"""

def __init__(self, data: Dict[str, Any]) -> None:
# ["power","bright","notifystatus","ambstatus","ambvalue","eyecare",
# "scene_num","bls","dvalue"]}
# ["off",5,"off","off",41,"on",3,"on",0]
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 notifystatus(self) -> str:
return self.data["notifystatus"]

@property
def ambstatus(self) -> str:
return self.data["ambstatus"]

@property
def ambvalue(self) -> int:
return self.data["ambvalue"]

@property
def eyecare(self) -> str:
return self.data["eyecare"]

@property
def scene_num(self) -> str:
return self.data["scene_num"]

@property
def bls(self) -> str:
return self.data["bls"]

@property
def dvalue(self) -> int:
return self.data["dvalue"]

def __str__(self) -> str:
s = "<PhilipsEyecareStatus power=%s, bright=%s, " \
"notifystatus=%s, ambstatus=%s, ambvalue=%s, " \
"eyecare=%s, scene_num=%s, bls=%s, " \
"dvalue=%s >" % \
(self.power, self.bright,
self.notifystatus, self.ambstatus, self.ambvalue,
self.eyecare, self.scene_num,
self.bls)
return s
199 changes: 199 additions & 0 deletions mirobo/philips_eyecare_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# -*- 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.PhilipsEyecare)


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_minutes(ctx, param, value):
value = int(value)
if value < 0 or value > 60:
raise click.BadParameter('Should be a positive int between 1-60.')
return value


def validate_scene(ctx, param, value):
value = int(value)
if value < 1 or value > 3:
raise click.BadParameter('Should be a positive int between 1-3.')
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 Eyecare Smart Lamp 2."""

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.PhilipsEyecare(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.PhilipsEyecare.discover()


@cli.command()
@pass_dev
def status(dev: mirobo.PhilipsEyecare):
"""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("Eye Fatigue Reminder: %s" % res.notifystatus)
click.echo("Ambient Light: %s" % res.ambstatus)
click.echo("Ambient Light Brightness: %s" % res.ambvalue)
click.echo("Eyecare Mode: %s" % res.eyecare)
click.echo("Eyecare Scene: %s" % res.scene_num)
click.echo("Night Light: %s " % res.bls)
click.echo("Delay Off: %s minutes" % res.dvalue)


@cli.command()
@pass_dev
def on(dev: mirobo.PhilipsEyecare):
"""Power on."""
click.echo("Power on: %s" % dev.on())


@cli.command()
@pass_dev
def off(dev: mirobo.PhilipsEyecare):
"""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.PhilipsEyecare, level):
"""Set brightness level."""
click.echo("Brightness: %s" % dev.set_bright(level))


@cli.command()
@click.argument('scene', callback=validate_scene, required=True,)
@pass_dev
def set_scene(dev: mirobo.PhilipsEyecare, scene):
"""Set eyecare scene number."""
click.echo("Eyecare Scene: %s" % dev.set_user_scene(scene))


@cli.command()
@click.argument('minutes', callback=validate_minutes, required=True,)
@pass_dev
def delay_off(dev: mirobo.PhilipsEyecare, minutes):
"""Set delay off in minutes."""
click.echo("Delay off: %s" % dev.delay_off(minutes))


@cli.command()
@pass_dev
def bl_on(dev: mirobo.PhilipsEyecare):
"""Night Light on."""
click.echo("Night Light On: %s" % dev.bl_on())


@cli.command()
@pass_dev
def bl_off(dev: mirobo.PhilipsEyecare):
"""Night Light off."""
click.echo("Night Light off: %s" % dev.bl_off())


@cli.command()
@pass_dev
def notify_on(dev: mirobo.PhilipsEyecare):
"""Eye Fatigue Reminder On."""
click.echo("Eye Fatigue Reminder On: %s" % dev.notify_user_on())


@cli.command()
@pass_dev
def notify_off(dev: mirobo.PhilipsEyecare):
"""Eye Fatigue Reminder off."""
click.echo("Eye Fatigue Reminder Off: %s" % dev.notify_user_off())


@cli.command()
@pass_dev
def ambient_on(dev: mirobo.PhilipsEyecare):
"""Ambient Light on."""
click.echo("Ambient Light On: %s" % dev.amb_on())


@cli.command()
@pass_dev
def ambient_off(dev: mirobo.PhilipsEyecare):
"""Ambient Light off."""
click.echo("Ambient Light Off: %s" % dev.amb_off())


@cli.command()
@click.argument('level', callback=validate_bright, required=True,)
@pass_dev
def set_amb_bright(dev: mirobo.PhilipsEyecare, level):
"""Set Ambient Light brightness level."""
click.echo("Ambient Light Brightness: %s" % dev.set_amb_bright(level))


if __name__ == "__main__":
cli()
3 changes: 2 additions & 1 deletion mirobo/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
0x02f2: "Xiaomi Mi Robot Vacuum",
0x00c4: "Xiaomi Smart Mi Air Purifier",
0x031a: "Xiaomi Smart home gateway",
0x0330: "Yeelight color bulb"
0x0330: "Yeelight color bulb",
0x02f9: "Xiaomi Philips Eyecare Smart Lamp 2",
}
xiaomi_devices = {y: x for x, y in xiaomi_devices_reverse.items()}

Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
'console_scripts': [
'mirobo=mirobo.vacuum_cli:cli',
'miplug=mirobo.plug_cli:cli',
'mieye=mirobo.philips_eyecare_cli:cli',
],
},
)

0 comments on commit 3b886ad

Please sign in to comment.