Skip to content

Commit

Permalink
Merge of the Chuangmi Plug V1, V2, V3 and M1 (#270)
Browse files Browse the repository at this point in the history
* Chuangmi Plug V1 and V3 merged.

* Chuangmi V1 and V3 merge completed.

* Whitespace removed

* DummyDevice used in both cases

* Chuangmi Plug M1 merged.

* Missing blank line added.
Comma removed.

* Model parameter moved to the sub class.

* Missed line removed.

* Default model added.

* Deprecation warning added

* Test the device class via the deprecation layer

* Revert "Test the device class via the deprecation layer"

This reverts commit 7eb1443.

* Provide a model per mDNS prefix

* Refactoring

* Missing parameter added

* Default model added

* More compact imports

* Deprecated decorator used

* Update chuangmi_plug.py

remvoe unused warnings.
  • Loading branch information
syssi authored and rytilahti committed Mar 26, 2018
1 parent 020b861 commit 42e6035
Show file tree
Hide file tree
Showing 10 changed files with 429 additions and 516 deletions.
4 changes: 1 addition & 3 deletions miio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
from miio.vacuumcontainers import (VacuumStatus, ConsumableStatus, DNDStatus,
CleaningDetails, CleaningSummary, Timer)
from miio.vacuum import Vacuum, VacuumException
from miio.plug import Plug
from miio.plug_v1 import PlugV1
from miio.plug_v3 import PlugV3
from miio.chuangmi_plug import (Plug, PlugV1, PlugV3, ChuangmiPlug)
from miio.airpurifier import AirPurifier
from miio.airhumidifier import AirHumidifier
from miio.waterpurifier import WaterPurifier
Expand Down
180 changes: 180 additions & 0 deletions miio/chuangmi_plug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import logging
from typing import Dict, Any, Optional
from collections import defaultdict
from .device import Device
from .utils import deprecated

_LOGGER = logging.getLogger(__name__)

MODEL_CHUANGMI_PLUG_V3 = 'chuangmi.plug.v3'
MODEL_CHUANGMI_PLUG_V1 = 'chuangmi.plug.v1'
MODEL_CHUANGMI_PLUG_M1 = 'chuangmi.plug.m1'
MODEL_CHUANGMI_PLUG_V2 = 'chuangmi.plug.v2'

AVAILABLE_PROPERTIES = {
MODEL_CHUANGMI_PLUG_V1: ['on', 'usb_on', 'temperature'],
MODEL_CHUANGMI_PLUG_V3: ['on', 'usb_on', 'temperature', 'wifi_led'],
MODEL_CHUANGMI_PLUG_M1: ['power', 'temperature'],
MODEL_CHUANGMI_PLUG_V2: ['power', 'temperature'],
}


class ChuangmiPlugStatus:
"""Container for status reports from the plug."""

def __init__(self, data: Dict[str, Any]) -> None:
"""
Response of a Chuangmi Plug V1 (chuangmi.plug.v1)
{ 'power': True, 'usb_on': True, 'temperature': 32 }
Response of a Chuangmi Plug V3 (chuangmi.plug.v3):
{ 'on': True, 'usb_on': True, 'temperature': 32, 'wifi_led': True }
"""
self.data = data

@property
def power(self) -> bool:
"""Current power state."""
if "on" in self.data:
return self.data["on"]
if "power" in self.data:
return self.data["power"] == 'on'

@property
def is_on(self) -> bool:
"""True if device is on."""
return self.power

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

@property
def usb_power(self) -> Optional[bool]:
"""True if USB is on."""
if "usb_on" in self.data and self.data["usb_on"] is not None:
return self.data["usb_on"]
return None

@property
def load_power(self) -> Optional[int]:
"""Current power load, if available."""
if "load_power" in self.data and self.data["load_power"] is not None:
return self.data["load_power"]
return None

@property
def wifi_led(self) -> Optional[bool]:
"""True if the wifi led is turned on."""
if "wifi_led" in self.data and self.data["wifi_led"] is not None:
return self.data["wifi_led"] == "on"
return None

def __repr__(self) -> str:
s = "<ChuangmiPlugStatus " \
"power=%s, " \
"usb_power=%s, " \
"temperature=%s" \
"load_power=%s, " \
"wifi_led=%s>" % \
(self.power,
self.usb_power,
self.temperature,
self.load_power,
self.wifi_led)
return s


class ChuangmiPlug(Device):
"""Main class representing the Chuangmi Plug V1 and V3."""

def __init__(self, ip: str = None, token: str = None, start_id: int = 0,
debug: int = 0, lazy_discover: bool = True,
model: str = MODEL_CHUANGMI_PLUG_M1) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover)

if model in AVAILABLE_PROPERTIES:
self.model = model
else:
self.model = MODEL_CHUANGMI_PLUG_M1

def status(self) -> ChuangmiPlugStatus:
"""Retrieve properties."""
properties = AVAILABLE_PROPERTIES[self.model]
values = self.send(
"get_prop",
properties
)

properties_count = len(properties)
values_count = len(values)
if properties_count != values_count:
_LOGGER.debug(
"Count (%s) of requested properties does not match the "
"count (%s) of received values.",
properties_count, values_count)

if self.model == MODEL_CHUANGMI_PLUG_V3:
load_power = self.send("get_power", []) # Response: [300]
if len(load_power) == 1:
properties.append('load_power')
values.append(load_power[0])

return ChuangmiPlugStatus(
defaultdict(lambda: None, zip(properties, values)))

def on(self):
"""Power on."""
if self.model == MODEL_CHUANGMI_PLUG_V1:
return self.send("set_on", [])

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

def off(self):
"""Power off."""
if self.model == MODEL_CHUANGMI_PLUG_V1:
return self.send("set_off", [])

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

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

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

def set_wifi_led(self, led: bool):
"""Set the wifi led on/off."""
if led:
return self.send("set_wifi_led", ["on"])
else:
return self.send("set_wifi_led", ["off"])


@deprecated("This device class is deprecated. Please use the ChuangmiPlug "
"class in future and select a model by parameter 'model'.")
class Plug(ChuangmiPlug):
def __init__(self, ip: str = None, token: str = None, start_id: int = 0,
debug: int = 0, lazy_discover: bool = True) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover,
model=MODEL_CHUANGMI_PLUG_M1)


@deprecated("This device class is deprecated. Please use the ChuangmiPlug "
"class in future and select a model by parameter 'model'.")
class PlugV1(ChuangmiPlug):
def __init__(self, ip: str = None, token: str = None, start_id: int = 0,
debug: int = 0, lazy_discover: bool = True) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover,
model=MODEL_CHUANGMI_PLUG_V1)


@deprecated("This device class is deprecated. Please use the ChuangmiPlug "
"class in future and select a model by parameter 'model'.")
class PlugV3(ChuangmiPlug):
def __init__(self, ip: str = None, token: str = None, start_id: int = 0,
debug: int = 0, lazy_discover: bool = True) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover,
model=MODEL_CHUANGMI_PLUG_V3)
29 changes: 18 additions & 11 deletions miio/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@
import ipaddress
import inspect
import codecs
from . import (Device, Vacuum, Plug, PlugV1, PlugV3, PowerStrip, AirPurifier, Ceil,
from . import (Device, Vacuum, ChuangmiPlug, PowerStrip, AirPurifier, Ceil,
PhilipsBulb, PhilipsEyecare, ChuangmiIr, AirHumidifier,
WaterPurifier, WifiSpeaker, Yeelight)
from .chuangmi_plug import (MODEL_CHUANGMI_PLUG_V1, MODEL_CHUANGMI_PLUG_V3,
MODEL_CHUANGMI_PLUG_M1)

from functools import partial
from typing import Union, Callable, Dict, Optional # noqa: F401


Expand All @@ -15,11 +19,11 @@
DEVICE_MAP = {
"rockrobo-vacuum-v1": Vacuum,
"roborock-vacuum-s5": Vacuum,
"chuangmi-plug-m1": Plug,
"chuangmi-plug-v2": Plug,
"chuangmi-plug-v1": PlugV1,
"chuangmi-plug_": PlugV1,
"chuangmi-plug-v3": PlugV3,
"chuangmi-plug-m1": partial(ChuangmiPlug, model=MODEL_CHUANGMI_PLUG_M1),
"chuangmi-plug-v2": partial(ChuangmiPlug, model=MODEL_CHUANGMI_PLUG_M1),
"chuangmi-plug-v1": partial(ChuangmiPlug, model=MODEL_CHUANGMI_PLUG_V1),
"chuangmi-plug_": partial(ChuangmiPlug, model=MODEL_CHUANGMI_PLUG_V1),
"chuangmi-plug-v3": partial(ChuangmiPlug, model=MODEL_CHUANGMI_PLUG_V3),
"qmi-powerstrip-v1": PowerStrip,
"zimi-powerstrip-v2": PowerStrip,
"zhimi-airpurifier-m1": AirPurifier, # mini model
Expand Down Expand Up @@ -63,13 +67,16 @@ def other_package_info(info, desc):
desc)


def create_device(addr, device_cls) -> Device:
def create_device(name: str, addr: str, device_cls: partial) -> Device:
"""Return a device object for a zeroconf entry."""
_LOGGER.debug("Found a supported '%s', using '%s' class",
name, device_cls.func.__name__)

dev = device_cls(ip=addr)
m = dev.do_discover()
dev.token = m.checksum
_LOGGER.info("Found a supported '%s' at %s - token: %s",
device_cls.__name__,
device_cls.func.__name__,
addr,
pretty_token(dev.token))
return dev
Expand All @@ -87,9 +94,9 @@ def check_and_create_device(self, info, addr) -> Optional[Device]:
for identifier, v in DEVICE_MAP.items():
if name.startswith(identifier):
if inspect.isclass(v):
_LOGGER.debug("Found a supported '%s', using '%s' class",
name, v.__name__)
return create_device(addr, v)
return create_device(name, addr, partial(v))
elif type(v) is partial and inspect.isclass(v.func):
return create_device(name, addr, v)
elif callable(v):
dev = Device(ip=addr)
_LOGGER.info("%s: token: %s",
Expand Down
65 changes: 0 additions & 65 deletions miio/plug.py

This file was deleted.

Loading

0 comments on commit 42e6035

Please sign in to comment.