Skip to content

Commit

Permalink
Merge branch 'release/1.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
syssi committed Feb 24, 2018
2 parents 3d0d2ad + a2eee3e commit 2e0970d
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 52 deletions.
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Credits: Thanks to [Rytilahti](https://github.com/rytilahti/python-miio) for all

## Features
* On, Off
* USB on, off (Chuangmi Plug V1)
* USB on, off (Chuangmi Plug V1 only)
* Current state
* Attributes
- Temperature
Expand All @@ -22,9 +22,27 @@ switch:
name: Original Xiaomi Mi Smart WiFi Socket
host: 192.168.130.59
token: b7c4a758c251955d2c24b1d9e41ce47d
model: chuangmi.plug.m1
- platform: xiaomi_miio
name: Xiaomi Mi Smart WiFi Socket 2
name: Xiaomi Mi Smart Power Strip
host: 192.168.130.60
token: 0ed0fdccb2d0cd718108f18a447726a6
model: zimi.powerstrip.v2
```

Configuration variables:
- **host** (*Required*): The IP of your light.
- **token** (*Required*): The API token of your light.
- **name** (*Optional*): The name of your light.
- **model** (*Optional*): The model of your device. Valid values are `chuangmi.plug.v1`, `qmi.powerstrip.v1`, `zimi.powerstrip.v2`, `chuangmi.plug.m1` and `chuangmi.plug.v2`. This setting can be used to bypass the device model detection and is recommended if your device isn't always available.

## Platform services

#### Service switch/xiaomi_miio_set_power_mode (Power Strip only)

Set the power mode.

| Service data attribute | Optional | Description |
|---------------------------|----------|---------------------------------------------------------------|
| `entity_id` | yes | Only act on a specific xiaomi miio entity. Else targets all. |
| `mode` | no | Power mode, valid values are 'normal' and 'green' |
9 changes: 9 additions & 0 deletions custom_components/switch/services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
xiaomi_miio_set_power_mode:
description: Set the power mode.
fields:
entity_id:
description: Name of the xiaomi miio entity.
example: 'fan.xiaomi_miio_device'
mode:
description: Power mode, valid values are 'normal' and 'green'.
example: 'green'
183 changes: 133 additions & 50 deletions custom_components/switch/xiaomi_miio.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,30 @@
import voluptuous as vol

import homeassistant.helpers.config_validation as cv
from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA, )
from homeassistant.const import (CONF_NAME, CONF_HOST, CONF_TOKEN, )
from homeassistant.components.switch import (SwitchDevice, PLATFORM_SCHEMA,
DOMAIN, )
from homeassistant.const import (CONF_NAME, CONF_HOST, CONF_TOKEN,
ATTR_ENTITY_ID)
from homeassistant.exceptions import PlatformNotReady

_LOGGER = logging.getLogger(__name__)

DEFAULT_NAME = 'Xiaomi Miio Switch'
PLATFORM = 'xiaomi_miio'

CONF_MODEL = 'model'

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_HOST): cv.string,
vol.Required(CONF_TOKEN): vol.All(cv.string, vol.Length(min=32, max=32)),
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_MODEL, default=None): vol.In(
['chuangmi.plug.v1',
'qmi.powerstrip.v1',
'zimi.powerstrip.v2',
'chuangmi.plug.m1',
'chuangmi.plug.v2', None]),

})

REQUIREMENTS = ['python-miio>=0.3.5']
Expand All @@ -31,81 +43,129 @@
ATTR_TEMPERATURE = 'temperature'
ATTR_LOAD_POWER = 'load_power'
ATTR_MODEL = 'model'
ATTR_MODE = 'mode'
SUCCESS = ['ok']

SUPPORT_SET_POWER_MODE = 1

SERVICE_SET_POWER_MODE = 'xiaomi_miio_set_power_mode'

SERVICE_SCHEMA_POWER_MODE = vol.Schema({
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
vol.Required(ATTR_MODE): vol.All(vol.In(['green', 'normal'])),
})

# pylint: disable=unused-argument
@asyncio.coroutine
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
"""Set up the switch from config."""
from miio import Device, DeviceException
if PLATFORM not in hass.data:
hass.data[PLATFORM] = {}

host = config.get(CONF_HOST)
name = config.get(CONF_NAME)
token = config.get(CONF_TOKEN)
model = config.get(CONF_MODEL)

_LOGGER.info("Initializing with host %s (token %s...)", host, token[:5])

devices = []
try:
plug = Device(host, token)
device_info = plug.info()
_LOGGER.info("%s %s %s initialized",
device_info.model,
device_info.firmware_version,
device_info.hardware_version)

if device_info.model in ['chuangmi.plug.v1']:
from miio import PlugV1
plug = PlugV1(host, token)

# The device has two switchable channels (mains and a USB port).
# A switch device per channel will be created.
for channel_usb in [True, False]:
device = ChuangMiPlugV1Switch(
name, plug, device_info, channel_usb)
devices.append(device)

elif device_info.model in ['qmi.powerstrip.v1',
'zimi.powerstrip.v2']:
from miio import PowerStrip
plug = PowerStrip(host, token)
device = XiaomiPowerStripSwitch(name, plug, device_info)
devices.append(device)
elif device_info.model in ['chuangmi.plug.m1',
'chuangmi.plug.v2']:
from miio import Plug
plug = Plug(host, token)
device = XiaomiPlugGenericSwitch(name, plug, device_info)

if model is None:
try:
miio_device = Device(host, token)
device_info = miio_device.info()
model = device_info.model
_LOGGER.info("%s %s %s detected",
model,
device_info.firmware_version,
device_info.hardware_version)
except DeviceException:
raise PlatformNotReady

if model in ['chuangmi.plug.v1']:
from miio import PlugV1
plug = PlugV1(host, token)

# The device has two switchable channels (mains and a USB port).
# A switch device per channel will be created.
for channel_usb in [True, False]:
device = ChuangMiPlugV1Switch(
name, plug, model, channel_usb)
devices.append(device)
else:
_LOGGER.error(
'Unsupported device found! Please create an issue at '
'https://github.com/rytilahti/python-miio/issues '
'and provide the following data: %s', device_info.model)
except DeviceException:
raise PlatformNotReady
hass.data[PLATFORM][host] = device

elif model in ['qmi.powerstrip.v1',
'zimi.powerstrip.v2']:
from miio import PowerStrip
plug = PowerStrip(host, token)
device = XiaomiPowerStripSwitch(name, plug, model)
devices.append(device)
hass.data[PLATFORM][host] = device
elif model in ['chuangmi.plug.m1',
'chuangmi.plug.v2']:
from miio import Plug
plug = Plug(host, token)
device = XiaomiPlugGenericSwitch(name, plug, model)
devices.append(device)
hass.data[PLATFORM][host] = device
else:
_LOGGER.error(
'Unsupported device found! Please create an issue at '
'https://github.com/rytilahti/python-miio/issues '
'and provide the following data: %s', model)
return False

async_add_devices(devices, update_before_add=True)

@asyncio.coroutine
def async_service_handler(service):
"""Map services to methods on XiaomiPlugGenericSwitch."""
params = {key: value for key, value in service.data.items()
if key != ATTR_ENTITY_ID}
entity_ids = service.data.get(ATTR_ENTITY_ID)
if entity_ids:
devices = [device for device in hass.data[PLATFORM].values() if
device.entity_id in entity_ids]
else:
devices = hass.data[PLATFORM].values()

update_tasks = []
for device in devices:
yield from getattr(device, 'async_set_power_mode')(**params)
update_tasks.append(device.async_update_ha_state(True))

if update_tasks:
yield from asyncio.wait(update_tasks, loop=hass.loop)

hass.services.async_register(
DOMAIN, SERVICE_SET_POWER_MODE, async_service_handler,
schema=SERVICE_SCHEMA_POWER_MODE)


class XiaomiPlugGenericSwitch(SwitchDevice):
"""Representation of a Xiaomi Plug Generic."""

def __init__(self, name, plug, device_info):
def __init__(self, name, plug, model):
"""Initialize the plug switch."""
self._name = name
self._icon = 'mdi:power-socket'
self._device_info = device_info
self._model = model

self._plug = plug
self._state = None
self._state_attrs = {
ATTR_TEMPERATURE: None,
ATTR_MODEL: self._device_info.model,
ATTR_MODEL: self._model,
}
self._skip_update = False

@property
def supported_features(self):
"""Flag supported features."""
return 0

@property
def should_poll(self):
"""Poll the plug."""
Expand Down Expand Up @@ -191,21 +251,30 @@ def async_update(self):
})

except DeviceException as ex:
self._state = None
_LOGGER.error("Got exception while fetching the state: %s", ex)

@asyncio.coroutine
def async_set_power_mode(self, mode: str):
"""Set the power mode."""
return


class XiaomiPowerStripSwitch(XiaomiPlugGenericSwitch, SwitchDevice):
"""Representation of a Xiaomi Power Strip."""

def __init__(self, name, plug, device_info):
def __init__(self, name, plug, model):
"""Initialize the plug switch."""
XiaomiPlugGenericSwitch.__init__(self, name, plug, device_info)
XiaomiPlugGenericSwitch.__init__(self, name, plug, model)

self._state_attrs = {
ATTR_TEMPERATURE: None,
self._state_attrs.update({
ATTR_LOAD_POWER: None,
ATTR_MODEL: self._device_info.model,
}
})

@property
def supported_features(self):
"""Flag supported features."""
return SUPPORT_SET_POWER_MODE

@asyncio.coroutine
def async_update(self):
Expand All @@ -228,17 +297,30 @@ def async_update(self):
})

except DeviceException as ex:
self._state = None
_LOGGER.error("Got exception while fetching the state: %s", ex)

@asyncio.coroutine
def async_set_power_mode(self, mode: str):
"""Set the power mode."""
if self.supported_features & SUPPORT_SET_POWER_MODE == 0:
return

from miio.powerstrip import PowerMode

yield from self._try_command(
"Setting the power mode of the power strip failed.",
self._plug.set_power_mode, PowerMode(mode))


class ChuangMiPlugV1Switch(XiaomiPlugGenericSwitch, SwitchDevice):
"""Representation of a Chuang Mi Plug V1."""

def __init__(self, name, plug, device_info, channel_usb):
def __init__(self, name, plug, model, channel_usb):
"""Initialize the plug switch."""
name = name + ' USB' if channel_usb else name

XiaomiPlugGenericSwitch.__init__(self, name, plug, device_info)
XiaomiPlugGenericSwitch.__init__(self, name, plug, model)
self._channel_usb = channel_usb

@asyncio.coroutine
Expand Down Expand Up @@ -293,4 +375,5 @@ def async_update(self):
})

except DeviceException as ex:
self._state = None
_LOGGER.error("Got exception while fetching the state: %s", ex)

0 comments on commit 2e0970d

Please sign in to comment.