Skip to content

Commit

Permalink
Unified and scalable command line interface (#191)
Browse files Browse the repository at this point in the history
  • Loading branch information
yawor authored and syssi committed Mar 31, 2018
1 parent 2d44ede commit 5973100
Show file tree
Hide file tree
Showing 9 changed files with 593 additions and 14 deletions.
102 changes: 102 additions & 0 deletions miio/airpurifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import re
from typing import Any, Dict, Optional
from collections import defaultdict
import click
from .device import Device, DeviceException
from .click_common import command, format_output, EnumType

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -352,10 +354,45 @@ def __repr__(self) -> str:
self.button_pressed)
return s

def __json__(self):
return self.data


class AirPurifier(Device):
"""Main class representing the air purifier."""

@command(
default_output=format_output(
"",
"Power: {result.power}\n"
"AQI: {result.aqi} μg/m³\n"
"Average AQI: {result.average_aqi} μg/m³\n"
"Temperature: {result.temperature} °C\n"
"Humidity: {result.humidity} %\n"
"Mode: {result.mode.value}\n"
"LED: {result.led}\n"
"LED brightness: {result.led_brightness}\n"
"Illuminance: {result.illuminance} lx\n"
"Buzzer: {result.buzzer}\n"
"Child lock: {result.child_lock}\n"
"Favorite level: {result.favorite_level}\n"
"Filter life remaining: {result.filter_life_remaining} %\n"
"Filter hours used: {result.filter_hours_used}\n"
"Use time: {result.use_time} s\n"
"Purify volume: {result.purify_volume} m³\n"
"Motor speed: {result.motor_speed} rpm\n"
"Motor 2 speed: {result.motor2_speed} rpm\n"
"Sound volume: {result.volume} %\n"
"Filter RFID product id: {result.filter_rfid_product_id}\n"
"Filter RFID tag: {result.filter_rfid_tag}\n"
"Filter type: {result.filter_type.value}\n"
"Learn mode: {result.learn_mode}\n"
"Sleep mode: {result.sleep_mode.value}\n"
"Sleep time: {result.sleep_time}\n"
"Sleep mode learn count: {result.sleep_mode_learn_count}\n"
"AQI sensor enabled on power off: {result.auto_detect}\n"
)
)
def status(self) -> AirPurifierStatus:
"""Retrieve properties."""

Expand Down Expand Up @@ -388,18 +425,32 @@ def status(self) -> AirPurifierStatus:
return AirPurifierStatus(
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("mode", type=EnumType(OperationMode, False)),
default_output=format_output("Setting mode to '{mode.value}'")
)
def set_mode(self, mode: OperationMode):
"""Set mode."""
return self.send("set_mode", [mode.value])

@command(
click.argument("level", type=int),
default_output=format_output("Setting favorite level to {level}")
)
def set_favorite_level(self, level: int):
"""Set favorite level."""
if level < 0 or level > 16:
Expand All @@ -411,52 +462,100 @@ def set_favorite_level(self, level: int):
# should be between 0 and 16.
return self.send("set_level_favorite", [level]) # 0 ... 16

@command(
click.argument("brightness", type=EnumType(LedBrightness, False)),
default_output=format_output(
"Setting LED brightness to {brightness}")
)
def set_led_brightness(self, brightness: LedBrightness):
"""Set led brightness."""
return self.send("set_led_b", [brightness.value])

@command(
click.argument("led", type=bool),
default_output=format_output(
lambda led: "Turning on LED"
if led else "Turning off LED"
)
)
def set_led(self, led: bool):
"""Turn led on/off."""
if led:
return self.send("set_led", ['on'])
else:
return self.send("set_led", ['off'])

@command(
click.argument("buzzer", type=bool),
default_output=format_output(
lambda buzzer: "Turning on buzzer"
if buzzer else "Turning off buzzer"
)
)
def set_buzzer(self, buzzer: bool):
"""Set buzzer on/off."""
if buzzer:
return self.send("set_buzzer", ["on"])
else:
return self.send("set_buzzer", ["off"])

@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."""
if lock:
return self.send("set_child_lock", ["on"])
else:
return self.send("set_child_lock", ["off"])

@command(
click.argument("volume", type=int),
default_output=format_output("Setting favorite level to {volume}")
)
def set_volume(self, volume: int):
"""Set volume of sound notifications [0-100]."""
if volume < 0 or volume > 100:
raise AirPurifierException("Invalid volume: %s" % volume)

return self.send("set_volume", [volume])

@command(
click.argument("learn_mode", type=bool),
default_output=format_output(
lambda learn_mode: "Turning on learn mode"
if learn_mode else "Turning off learn mode"
)
)
def set_learn_mode(self, learn_mode: bool):
"""Set the Learn Mode on/off."""
if learn_mode:
return self.send("set_act_sleep", ["single"])
else:
return self.send("set_act_sleep", ["close"])

@command(
click.argument("auto_detect", type=bool),
default_output=format_output(
lambda auto_detect: "Turning on auto detect"
if auto_detect else "Turning off auto detect"
)
)
def set_auto_detect(self, auto_detect: bool):
"""Set auto detect on/off. It's a feature of the AirPurifier V1 & V3"""
if auto_detect:
return self.send("set_act_det", ["on"])
else:
return self.send("set_act_det", ["off"])

@command(
click.argument("value", type=int),
default_output=format_output("Setting extra to {value}")
)
def set_extra_features(self, value: int):
"""Storage register to enable extra features at the app.
Expand All @@ -467,6 +566,9 @@ def set_extra_features(self, value: int):

return self.send("set_app_extra", [value])

@command(
default_output=format_output("Resetting filter")
)
def reset_filter(self):
"""Resets filter hours used and remaining life."""
return self.send('reset_filter1')
33 changes: 33 additions & 0 deletions miio/chuangmi_plug.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import logging
import click
from typing import Dict, Any, Optional
from collections import defaultdict
from .device import Device
from .utils import deprecated
from .click_common import command, format_output

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -84,6 +86,9 @@ def __repr__(self) -> str:
self.wifi_led)
return s

def __json__(self):
return self.data


class ChuangmiPlug(Device):
"""Main class representing the Chuangmi Plug V1 and V3."""
Expand All @@ -98,6 +103,15 @@ def __init__(self, ip: str = None, token: str = None, start_id: int = 0,
else:
self.model = MODEL_CHUANGMI_PLUG_M1

@command(
default_output=format_output(
"",
"Power: {result.power}\n"
"USB Power: {result.usb_power}\n"
"Temperature: {result.temperature} °C\n"
"Load power: {result.load_power}\n"
"WiFi LED: {result.wifi_led}\n")
)
def status(self) -> ChuangmiPlugStatus:
"""Retrieve properties."""
properties = AVAILABLE_PROPERTIES[self.model]
Expand All @@ -123,28 +137,47 @@ def status(self) -> ChuangmiPlugStatus:
return ChuangmiPlugStatus(
defaultdict(lambda: None, zip(properties, values)))

@command(
default_output = format_output("Powering on"),
)
def on(self):
"""Power on."""
if self.model == MODEL_CHUANGMI_PLUG_V1:
return self.send("set_on", [])

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

@command(
default_output = format_output("Powering off"),
)
def off(self):
"""Power off."""
if self.model == MODEL_CHUANGMI_PLUG_V1:
return self.send("set_off", [])

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

@command(
default_output = format_output("Powering USB on"),
)
def usb_on(self):
"""Power on."""
return self.send("set_usb_on", [])

@command(
default_output = format_output("Powering USB off"),
)
def usb_off(self):
"""Power off."""
return self.send("set_usb_off", [])

@command(
click.argument("wifi_led", type=bool),
default_output=format_output(
lambda wifi_led: "Turning on WiFi LED"
if wifi_led else "Turning off WiFi LED"
)
)
def set_wifi_led(self, led: bool):
"""Set the wifi led on/off."""
if led:
Expand Down
45 changes: 45 additions & 0 deletions miio/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- coding: UTF-8 -*-
import logging
import click
from miio.click_common import (
ExceptionHandlerGroup, DeviceGroupMeta, GlobalContextObject,
json_output,
)

_LOGGER = logging.getLogger(__name__)


@click.group(cls=ExceptionHandlerGroup)
@click.option('-d', '--debug', default=False, count=True)
@click.option('-o', '--output', type=click.Choice([
'default', 'json', 'json_pretty',
]), default='default')
@click.pass_context
def cli(ctx, debug: int, output: str):
if debug:
logging.basicConfig(level=logging.DEBUG)
_LOGGER.info("Debug mode active")
else:
logging.basicConfig(level=logging.INFO)

if output in ('json', 'json_pretty'):
output_func = json_output(pretty=output == 'json_pretty')
else:
output_func = None

ctx.obj = GlobalContextObject(
debug=debug,
output=output_func,
)


for device_class in DeviceGroupMeta.device_classes:
cli.add_command(device_class.get_device_group())


def create_cli():
return cli(auto_envvar_prefix="MIIO")


if __name__ == '__main__':
create_cli()
Loading

0 comments on commit 5973100

Please sign in to comment.