2019-02-13 20:21:14 +00:00
|
|
|
"""Support for Xiaomi Smart WiFi Socket and Smart Power Strip."""
|
2017-10-09 05:11:11 +00:00
|
|
|
import asyncio
|
|
|
|
from functools import partial
|
|
|
|
import logging
|
|
|
|
|
|
|
|
import voluptuous as vol
|
|
|
|
|
2018-10-10 21:52:45 +00:00
|
|
|
from homeassistant.components.switch import (
|
|
|
|
DOMAIN, PLATFORM_SCHEMA, SwitchDevice)
|
|
|
|
from homeassistant.const import (
|
|
|
|
ATTR_ENTITY_ID, CONF_HOST, CONF_NAME, CONF_TOKEN)
|
2017-10-09 05:11:11 +00:00
|
|
|
from homeassistant.exceptions import PlatformNotReady
|
2018-10-10 21:52:45 +00:00
|
|
|
import homeassistant.helpers.config_validation as cv
|
|
|
|
|
2018-12-05 17:19:30 +00:00
|
|
|
REQUIREMENTS = ['python-miio==0.4.4', 'construct==2.9.45']
|
2017-10-09 05:11:11 +00:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
DEFAULT_NAME = 'Xiaomi Miio Switch'
|
2018-03-16 18:58:03 +00:00
|
|
|
DATA_KEY = 'switch.xiaomi_miio'
|
2017-10-26 21:37:30 +00:00
|
|
|
|
2018-03-05 07:25:12 +00:00
|
|
|
CONF_MODEL = 'model'
|
2018-03-16 18:58:03 +00:00
|
|
|
MODEL_POWER_STRIP_V2 = 'zimi.powerstrip.v2'
|
2018-03-30 19:02:02 +00:00
|
|
|
MODEL_PLUG_V3 = 'chuangmi.plug.v3'
|
2018-03-05 07:25:12 +00:00
|
|
|
|
2017-10-09 05:11:11 +00:00
|
|
|
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,
|
2018-03-05 07:25:12 +00:00
|
|
|
vol.Optional(CONF_MODEL): vol.In(
|
|
|
|
['chuangmi.plug.v1',
|
|
|
|
'qmi.powerstrip.v1',
|
|
|
|
'zimi.powerstrip.v2',
|
|
|
|
'chuangmi.plug.m1',
|
2019-01-11 18:44:55 +00:00
|
|
|
'chuangmi.plug.m3',
|
2018-03-30 19:02:02 +00:00
|
|
|
'chuangmi.plug.v2',
|
2018-11-07 08:03:35 +00:00
|
|
|
'chuangmi.plug.v3',
|
2018-12-07 22:40:48 +00:00
|
|
|
'chuangmi.plug.hmi205',
|
2018-11-07 08:03:35 +00:00
|
|
|
]),
|
2017-10-09 05:11:11 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
ATTR_POWER = 'power'
|
|
|
|
ATTR_TEMPERATURE = 'temperature'
|
|
|
|
ATTR_LOAD_POWER = 'load_power'
|
|
|
|
ATTR_MODEL = 'model'
|
2018-03-16 18:58:03 +00:00
|
|
|
ATTR_MODE = 'mode'
|
|
|
|
ATTR_POWER_MODE = 'power_mode'
|
|
|
|
ATTR_WIFI_LED = 'wifi_led'
|
|
|
|
ATTR_POWER_PRICE = 'power_price'
|
|
|
|
ATTR_PRICE = 'price'
|
|
|
|
|
2017-10-09 05:11:11 +00:00
|
|
|
SUCCESS = ['ok']
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
FEATURE_SET_POWER_MODE = 1
|
|
|
|
FEATURE_SET_WIFI_LED = 2
|
|
|
|
FEATURE_SET_POWER_PRICE = 4
|
2018-03-16 18:58:03 +00:00
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
FEATURE_FLAGS_GENERIC = 0
|
2018-03-16 18:58:03 +00:00
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
FEATURE_FLAGS_POWER_STRIP_V1 = (FEATURE_SET_POWER_MODE |
|
|
|
|
FEATURE_SET_WIFI_LED |
|
|
|
|
FEATURE_SET_POWER_PRICE)
|
2018-03-16 18:58:03 +00:00
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
FEATURE_FLAGS_POWER_STRIP_V2 = (FEATURE_SET_WIFI_LED |
|
|
|
|
FEATURE_SET_POWER_PRICE)
|
|
|
|
|
|
|
|
FEATURE_FLAGS_PLUG_V3 = (FEATURE_SET_WIFI_LED)
|
2018-03-16 18:58:03 +00:00
|
|
|
|
|
|
|
SERVICE_SET_WIFI_LED_ON = 'xiaomi_miio_set_wifi_led_on'
|
|
|
|
SERVICE_SET_WIFI_LED_OFF = 'xiaomi_miio_set_wifi_led_off'
|
|
|
|
SERVICE_SET_POWER_MODE = 'xiaomi_miio_set_power_mode'
|
|
|
|
SERVICE_SET_POWER_PRICE = 'xiaomi_miio_set_power_price'
|
|
|
|
|
|
|
|
SERVICE_SCHEMA = vol.Schema({
|
|
|
|
vol.Optional(ATTR_ENTITY_ID): cv.entity_ids,
|
|
|
|
})
|
|
|
|
|
|
|
|
SERVICE_SCHEMA_POWER_MODE = SERVICE_SCHEMA.extend({
|
|
|
|
vol.Required(ATTR_MODE): vol.All(vol.In(['green', 'normal'])),
|
|
|
|
})
|
|
|
|
|
|
|
|
SERVICE_SCHEMA_POWER_PRICE = SERVICE_SCHEMA.extend({
|
|
|
|
vol.Required(ATTR_PRICE): vol.All(vol.Coerce(float), vol.Range(min=0))
|
|
|
|
})
|
|
|
|
|
|
|
|
SERVICE_TO_METHOD = {
|
|
|
|
SERVICE_SET_WIFI_LED_ON: {'method': 'async_set_wifi_led_on'},
|
|
|
|
SERVICE_SET_WIFI_LED_OFF: {'method': 'async_set_wifi_led_off'},
|
|
|
|
SERVICE_SET_POWER_MODE: {
|
|
|
|
'method': 'async_set_power_mode',
|
|
|
|
'schema': SERVICE_SCHEMA_POWER_MODE},
|
|
|
|
SERVICE_SET_POWER_PRICE: {
|
|
|
|
'method': 'async_set_power_price',
|
|
|
|
'schema': SERVICE_SCHEMA_POWER_PRICE},
|
|
|
|
}
|
|
|
|
|
2017-10-09 05:11:11 +00:00
|
|
|
|
2018-08-24 14:37:30 +00:00
|
|
|
async def async_setup_platform(hass, config, async_add_entities,
|
2018-03-16 18:58:03 +00:00
|
|
|
discovery_info=None):
|
2017-10-09 05:11:11 +00:00
|
|
|
"""Set up the switch from config."""
|
2017-10-26 21:37:30 +00:00
|
|
|
from miio import Device, DeviceException
|
2018-03-16 18:58:03 +00:00
|
|
|
if DATA_KEY not in hass.data:
|
|
|
|
hass.data[DATA_KEY] = {}
|
2017-10-09 05:11:11 +00:00
|
|
|
|
|
|
|
host = config.get(CONF_HOST)
|
|
|
|
name = config.get(CONF_NAME)
|
|
|
|
token = config.get(CONF_TOKEN)
|
2018-03-05 07:25:12 +00:00
|
|
|
model = config.get(CONF_MODEL)
|
2017-10-09 05:11:11 +00:00
|
|
|
|
|
|
|
_LOGGER.info("Initializing with host %s (token %s...)", host, token[:5])
|
|
|
|
|
2017-10-26 21:37:30 +00:00
|
|
|
devices = []
|
2018-03-16 18:58:03 +00:00
|
|
|
unique_id = None
|
2018-03-05 07:25:12 +00:00
|
|
|
|
|
|
|
if model is None:
|
|
|
|
try:
|
|
|
|
miio_device = Device(host, token)
|
|
|
|
device_info = miio_device.info()
|
|
|
|
model = device_info.model
|
2018-03-16 18:58:03 +00:00
|
|
|
unique_id = "{}-{}".format(model, device_info.mac_address)
|
2018-03-05 07:25:12 +00:00
|
|
|
_LOGGER.info("%s %s %s detected",
|
|
|
|
model,
|
|
|
|
device_info.firmware_version,
|
|
|
|
device_info.hardware_version)
|
|
|
|
except DeviceException:
|
|
|
|
raise PlatformNotReady
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
if model in ['chuangmi.plug.v1', 'chuangmi.plug.v3']:
|
|
|
|
from miio import ChuangmiPlug
|
|
|
|
plug = ChuangmiPlug(host, token, model=model)
|
2018-03-05 07:25:12 +00:00
|
|
|
|
|
|
|
# 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]:
|
2018-03-30 19:02:02 +00:00
|
|
|
device = ChuangMiPlugSwitch(
|
2018-03-16 18:58:03 +00:00
|
|
|
name, plug, model, unique_id, channel_usb)
|
2017-10-26 21:37:30 +00:00
|
|
|
devices.append(device)
|
2018-03-16 18:58:03 +00:00
|
|
|
hass.data[DATA_KEY][host] = device
|
2018-03-05 07:25:12 +00:00
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
elif model in ['qmi.powerstrip.v1', 'zimi.powerstrip.v2']:
|
2018-03-05 07:25:12 +00:00
|
|
|
from miio import PowerStrip
|
2018-06-04 21:50:18 +00:00
|
|
|
plug = PowerStrip(host, token, model=model)
|
2018-03-16 18:58:03 +00:00
|
|
|
device = XiaomiPowerStripSwitch(name, plug, model, unique_id)
|
2018-03-05 07:25:12 +00:00
|
|
|
devices.append(device)
|
2018-03-16 18:58:03 +00:00
|
|
|
hass.data[DATA_KEY][host] = device
|
2019-01-11 18:44:55 +00:00
|
|
|
elif model in ['chuangmi.plug.m1', 'chuangmi.plug.m3',
|
|
|
|
'chuangmi.plug.v2', 'chuangmi.plug.hmi205']:
|
2018-03-30 19:02:02 +00:00
|
|
|
from miio import ChuangmiPlug
|
|
|
|
plug = ChuangmiPlug(host, token, model=model)
|
2018-03-16 18:58:03 +00:00
|
|
|
device = XiaomiPlugGenericSwitch(name, plug, model, unique_id)
|
2018-03-05 07:25:12 +00:00
|
|
|
devices.append(device)
|
2018-03-16 18:58:03 +00:00
|
|
|
hass.data[DATA_KEY][host] = device
|
2018-03-05 07:25:12 +00:00
|
|
|
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
|
2017-10-09 05:11:11 +00:00
|
|
|
|
2018-08-24 14:37:30 +00:00
|
|
|
async_add_entities(devices, update_before_add=True)
|
2017-10-09 05:11:11 +00:00
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
async def async_service_handler(service):
|
|
|
|
"""Map services to methods on XiaomiPlugGenericSwitch."""
|
|
|
|
method = SERVICE_TO_METHOD.get(service.service)
|
|
|
|
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[DATA_KEY].values() if
|
|
|
|
device.entity_id in entity_ids]
|
|
|
|
else:
|
|
|
|
devices = hass.data[DATA_KEY].values()
|
|
|
|
|
|
|
|
update_tasks = []
|
|
|
|
for device in devices:
|
|
|
|
if not hasattr(device, method['method']):
|
|
|
|
continue
|
|
|
|
await getattr(device, method['method'])(**params)
|
|
|
|
update_tasks.append(device.async_update_ha_state(True))
|
|
|
|
|
|
|
|
if update_tasks:
|
|
|
|
await asyncio.wait(update_tasks, loop=hass.loop)
|
|
|
|
|
|
|
|
for plug_service in SERVICE_TO_METHOD:
|
|
|
|
schema = SERVICE_TO_METHOD[plug_service].get('schema', SERVICE_SCHEMA)
|
|
|
|
hass.services.async_register(
|
|
|
|
DOMAIN, plug_service, async_service_handler, schema=schema)
|
|
|
|
|
2017-10-09 05:11:11 +00:00
|
|
|
|
2017-10-26 21:37:30 +00:00
|
|
|
class XiaomiPlugGenericSwitch(SwitchDevice):
|
|
|
|
"""Representation of a Xiaomi Plug Generic."""
|
2017-10-09 05:11:11 +00:00
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
def __init__(self, name, plug, model, unique_id):
|
2017-10-09 05:11:11 +00:00
|
|
|
"""Initialize the plug switch."""
|
|
|
|
self._name = name
|
2018-03-16 18:58:03 +00:00
|
|
|
self._plug = plug
|
2018-03-05 07:25:12 +00:00
|
|
|
self._model = model
|
2018-03-16 18:58:03 +00:00
|
|
|
self._unique_id = unique_id
|
2017-10-09 05:11:11 +00:00
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
self._icon = 'mdi:power-socket'
|
|
|
|
self._available = False
|
2017-10-09 05:11:11 +00:00
|
|
|
self._state = None
|
|
|
|
self._state_attrs = {
|
|
|
|
ATTR_TEMPERATURE: None,
|
2018-03-05 07:25:12 +00:00
|
|
|
ATTR_MODEL: self._model,
|
2017-10-09 05:11:11 +00:00
|
|
|
}
|
2018-03-30 19:02:02 +00:00
|
|
|
self._device_features = FEATURE_FLAGS_GENERIC
|
2017-10-09 05:11:11 +00:00
|
|
|
self._skip_update = False
|
|
|
|
|
|
|
|
@property
|
|
|
|
def should_poll(self):
|
|
|
|
"""Poll the plug."""
|
|
|
|
return True
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
@property
|
|
|
|
def unique_id(self):
|
|
|
|
"""Return an unique ID."""
|
|
|
|
return self._unique_id
|
|
|
|
|
2017-10-09 05:11:11 +00:00
|
|
|
@property
|
|
|
|
def name(self):
|
|
|
|
"""Return the name of the device if any."""
|
|
|
|
return self._name
|
|
|
|
|
|
|
|
@property
|
|
|
|
def icon(self):
|
|
|
|
"""Return the icon to use for device if any."""
|
|
|
|
return self._icon
|
|
|
|
|
|
|
|
@property
|
|
|
|
def available(self):
|
|
|
|
"""Return true when state is known."""
|
2018-03-16 18:58:03 +00:00
|
|
|
return self._available
|
2017-10-09 05:11:11 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def device_state_attributes(self):
|
|
|
|
"""Return the state attributes of the device."""
|
|
|
|
return self._state_attrs
|
|
|
|
|
|
|
|
@property
|
|
|
|
def is_on(self):
|
|
|
|
"""Return true if switch is on."""
|
|
|
|
return self._state
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
async def _try_command(self, mask_error, func, *args, **kwargs):
|
2017-10-09 05:11:11 +00:00
|
|
|
"""Call a plug command handling error messages."""
|
2017-10-26 21:37:30 +00:00
|
|
|
from miio import DeviceException
|
2017-10-09 05:11:11 +00:00
|
|
|
try:
|
2018-11-07 08:03:35 +00:00
|
|
|
result = await self.hass.async_add_executor_job(
|
2017-10-09 05:11:11 +00:00
|
|
|
partial(func, *args, **kwargs))
|
|
|
|
|
|
|
|
_LOGGER.debug("Response received from plug: %s", result)
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
# The Chuangmi Plug V3 returns 0 on success on usb_on/usb_off.
|
|
|
|
if func in ['usb_on', 'usb_off'] and result == 0:
|
|
|
|
return True
|
|
|
|
|
2017-10-09 05:11:11 +00:00
|
|
|
return result == SUCCESS
|
|
|
|
except DeviceException as exc:
|
|
|
|
_LOGGER.error(mask_error, exc)
|
2018-03-16 18:58:03 +00:00
|
|
|
self._available = False
|
2017-10-09 05:11:11 +00:00
|
|
|
return False
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
async def async_turn_on(self, **kwargs):
|
2017-10-09 05:11:11 +00:00
|
|
|
"""Turn the plug on."""
|
2018-03-16 18:58:03 +00:00
|
|
|
result = await self._try_command(
|
2017-10-09 05:11:11 +00:00
|
|
|
"Turning the plug on failed.", self._plug.on)
|
|
|
|
|
|
|
|
if result:
|
|
|
|
self._state = True
|
|
|
|
self._skip_update = True
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
async def async_turn_off(self, **kwargs):
|
2017-10-09 05:11:11 +00:00
|
|
|
"""Turn the plug off."""
|
2018-03-16 18:58:03 +00:00
|
|
|
result = await self._try_command(
|
2017-10-09 05:11:11 +00:00
|
|
|
"Turning the plug off failed.", self._plug.off)
|
|
|
|
|
|
|
|
if result:
|
|
|
|
self._state = False
|
|
|
|
self._skip_update = True
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
async def async_update(self):
|
2017-10-09 05:11:11 +00:00
|
|
|
"""Fetch state from the device."""
|
2017-10-26 21:37:30 +00:00
|
|
|
from miio import DeviceException
|
|
|
|
|
|
|
|
# On state change the device doesn't provide the new state immediately.
|
|
|
|
if self._skip_update:
|
|
|
|
self._skip_update = False
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
2018-11-07 08:03:35 +00:00
|
|
|
state = await self.hass.async_add_executor_job(self._plug.status)
|
2017-10-26 21:37:30 +00:00
|
|
|
_LOGGER.debug("Got new state: %s", state)
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
self._available = True
|
2017-10-26 21:37:30 +00:00
|
|
|
self._state = state.is_on
|
|
|
|
self._state_attrs.update({
|
|
|
|
ATTR_TEMPERATURE: state.temperature
|
|
|
|
})
|
|
|
|
|
|
|
|
except DeviceException as ex:
|
2018-03-16 18:58:03 +00:00
|
|
|
self._available = False
|
2017-10-26 21:37:30 +00:00
|
|
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
async def async_set_wifi_led_on(self):
|
|
|
|
"""Turn the wifi led on."""
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._device_features & FEATURE_SET_WIFI_LED == 0:
|
2018-03-16 18:58:03 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
await self._try_command(
|
|
|
|
"Turning the wifi led on failed.",
|
|
|
|
self._plug.set_wifi_led, True)
|
|
|
|
|
|
|
|
async def async_set_wifi_led_off(self):
|
|
|
|
"""Turn the wifi led on."""
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._device_features & FEATURE_SET_WIFI_LED == 0:
|
2018-03-16 18:58:03 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
await self._try_command(
|
|
|
|
"Turning the wifi led off failed.",
|
|
|
|
self._plug.set_wifi_led, False)
|
|
|
|
|
|
|
|
async def async_set_power_price(self, price: int):
|
|
|
|
"""Set the power price."""
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._device_features & FEATURE_SET_POWER_PRICE == 0:
|
2018-03-16 18:58:03 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
await self._try_command(
|
|
|
|
"Setting the power price of the power strip failed.",
|
|
|
|
self._plug.set_power_price, price)
|
2017-10-26 21:37:30 +00:00
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
|
|
|
|
class XiaomiPowerStripSwitch(XiaomiPlugGenericSwitch):
|
2017-10-26 21:37:30 +00:00
|
|
|
"""Representation of a Xiaomi Power Strip."""
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
def __init__(self, name, plug, model, unique_id):
|
2017-10-26 21:37:30 +00:00
|
|
|
"""Initialize the plug switch."""
|
2018-03-30 19:02:02 +00:00
|
|
|
super().__init__(name, plug, model, unique_id)
|
2017-10-26 21:37:30 +00:00
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
if self._model == MODEL_POWER_STRIP_V2:
|
2018-03-30 19:02:02 +00:00
|
|
|
self._device_features = FEATURE_FLAGS_POWER_STRIP_V2
|
2018-03-16 18:58:03 +00:00
|
|
|
else:
|
2018-03-30 19:02:02 +00:00
|
|
|
self._device_features = FEATURE_FLAGS_POWER_STRIP_V1
|
2018-03-16 18:58:03 +00:00
|
|
|
|
|
|
|
self._state_attrs.update({
|
2017-10-26 21:37:30 +00:00
|
|
|
ATTR_LOAD_POWER: None,
|
2018-03-16 18:58:03 +00:00
|
|
|
})
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._device_features & FEATURE_SET_POWER_MODE == 1:
|
2018-03-16 18:58:03 +00:00
|
|
|
self._state_attrs[ATTR_POWER_MODE] = None
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._device_features & FEATURE_SET_WIFI_LED == 1:
|
2018-03-16 18:58:03 +00:00
|
|
|
self._state_attrs[ATTR_WIFI_LED] = None
|
2017-10-26 21:37:30 +00:00
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._device_features & FEATURE_SET_POWER_PRICE == 1:
|
2018-03-16 18:58:03 +00:00
|
|
|
self._state_attrs[ATTR_POWER_PRICE] = None
|
|
|
|
|
|
|
|
async def async_update(self):
|
2017-10-26 21:37:30 +00:00
|
|
|
"""Fetch state from the device."""
|
|
|
|
from miio import DeviceException
|
2017-10-09 05:11:11 +00:00
|
|
|
|
|
|
|
# On state change the device doesn't provide the new state immediately.
|
|
|
|
if self._skip_update:
|
|
|
|
self._skip_update = False
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
2018-11-07 08:03:35 +00:00
|
|
|
state = await self.hass.async_add_executor_job(self._plug.status)
|
2017-10-09 05:11:11 +00:00
|
|
|
_LOGGER.debug("Got new state: %s", state)
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
self._available = True
|
2017-10-09 05:11:11 +00:00
|
|
|
self._state = state.is_on
|
|
|
|
self._state_attrs.update({
|
|
|
|
ATTR_TEMPERATURE: state.temperature,
|
2018-03-16 18:58:03 +00:00
|
|
|
ATTR_LOAD_POWER: state.load_power,
|
2017-10-09 05:11:11 +00:00
|
|
|
})
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._device_features & FEATURE_SET_POWER_MODE == 1 and \
|
|
|
|
state.mode:
|
2018-03-16 18:58:03 +00:00
|
|
|
self._state_attrs[ATTR_POWER_MODE] = state.mode.value
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._device_features & FEATURE_SET_WIFI_LED == 1 and \
|
|
|
|
state.wifi_led:
|
2018-03-16 18:58:03 +00:00
|
|
|
self._state_attrs[ATTR_WIFI_LED] = state.wifi_led
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._device_features & FEATURE_SET_POWER_PRICE == 1 and \
|
|
|
|
state.power_price:
|
2018-03-16 18:58:03 +00:00
|
|
|
self._state_attrs[ATTR_POWER_PRICE] = state.power_price
|
|
|
|
|
2017-10-09 05:11:11 +00:00
|
|
|
except DeviceException as ex:
|
2018-03-16 18:58:03 +00:00
|
|
|
self._available = False
|
2017-10-09 05:11:11 +00:00
|
|
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
2017-10-26 21:37:30 +00:00
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
async def async_set_power_mode(self, mode: str):
|
|
|
|
"""Set the power mode."""
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._device_features & FEATURE_SET_POWER_MODE == 0:
|
2018-03-16 18:58:03 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
from miio.powerstrip import PowerMode
|
2017-10-26 21:37:30 +00:00
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
await self._try_command(
|
|
|
|
"Setting the power mode of the power strip failed.",
|
|
|
|
self._plug.set_power_mode, PowerMode(mode))
|
|
|
|
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
class ChuangMiPlugSwitch(XiaomiPlugGenericSwitch):
|
|
|
|
"""Representation of a Chuang Mi Plug V1 and V3."""
|
2017-10-26 21:37:30 +00:00
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
def __init__(self, name, plug, model, unique_id, channel_usb):
|
2017-10-26 21:37:30 +00:00
|
|
|
"""Initialize the plug switch."""
|
2018-03-05 07:25:12 +00:00
|
|
|
name = '{} USB'.format(name) if channel_usb else name
|
2017-10-26 21:37:30 +00:00
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
if unique_id is not None and channel_usb:
|
|
|
|
unique_id = "{}-{}".format(unique_id, 'usb')
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
super().__init__(name, plug, model, unique_id)
|
2017-10-26 21:37:30 +00:00
|
|
|
self._channel_usb = channel_usb
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
if self._model == MODEL_PLUG_V3:
|
|
|
|
self._device_features = FEATURE_FLAGS_PLUG_V3
|
|
|
|
self._state_attrs.update({
|
|
|
|
ATTR_WIFI_LED: None,
|
|
|
|
})
|
2018-06-16 19:53:25 +00:00
|
|
|
if self._channel_usb is False:
|
|
|
|
self._state_attrs.update({
|
|
|
|
ATTR_LOAD_POWER: None,
|
|
|
|
})
|
2018-03-30 19:02:02 +00:00
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
async def async_turn_on(self, **kwargs):
|
2017-10-26 21:37:30 +00:00
|
|
|
"""Turn a channel on."""
|
|
|
|
if self._channel_usb:
|
2018-03-16 18:58:03 +00:00
|
|
|
result = await self._try_command(
|
2017-10-26 21:37:30 +00:00
|
|
|
"Turning the plug on failed.", self._plug.usb_on)
|
|
|
|
else:
|
2018-03-16 18:58:03 +00:00
|
|
|
result = await self._try_command(
|
2017-10-26 21:37:30 +00:00
|
|
|
"Turning the plug on failed.", self._plug.on)
|
|
|
|
|
|
|
|
if result:
|
|
|
|
self._state = True
|
|
|
|
self._skip_update = True
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
async def async_turn_off(self, **kwargs):
|
2017-10-26 21:37:30 +00:00
|
|
|
"""Turn a channel off."""
|
|
|
|
if self._channel_usb:
|
2018-03-16 18:58:03 +00:00
|
|
|
result = await self._try_command(
|
2017-10-26 21:37:30 +00:00
|
|
|
"Turning the plug on failed.", self._plug.usb_off)
|
|
|
|
else:
|
2018-03-16 18:58:03 +00:00
|
|
|
result = await self._try_command(
|
2017-10-26 21:37:30 +00:00
|
|
|
"Turning the plug on failed.", self._plug.off)
|
|
|
|
|
|
|
|
if result:
|
|
|
|
self._state = False
|
|
|
|
self._skip_update = True
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
async def async_update(self):
|
2017-10-26 21:37:30 +00:00
|
|
|
"""Fetch state from the device."""
|
|
|
|
from miio import DeviceException
|
|
|
|
|
|
|
|
# On state change the device doesn't provide the new state immediately.
|
|
|
|
if self._skip_update:
|
|
|
|
self._skip_update = False
|
|
|
|
return
|
|
|
|
|
|
|
|
try:
|
2018-11-07 08:03:35 +00:00
|
|
|
state = await self.hass.async_add_executor_job(self._plug.status)
|
2017-10-26 21:37:30 +00:00
|
|
|
_LOGGER.debug("Got new state: %s", state)
|
|
|
|
|
2018-03-16 18:58:03 +00:00
|
|
|
self._available = True
|
2017-10-26 21:37:30 +00:00
|
|
|
if self._channel_usb:
|
|
|
|
self._state = state.usb_power
|
|
|
|
else:
|
|
|
|
self._state = state.is_on
|
|
|
|
|
2017-11-02 20:38:18 +00:00
|
|
|
self._state_attrs.update({
|
|
|
|
ATTR_TEMPERATURE: state.temperature
|
|
|
|
})
|
|
|
|
|
2018-03-30 19:02:02 +00:00
|
|
|
if state.wifi_led:
|
|
|
|
self._state_attrs[ATTR_WIFI_LED] = state.wifi_led
|
|
|
|
|
2018-06-16 19:53:25 +00:00
|
|
|
if self._channel_usb is False and state.load_power:
|
2018-03-30 19:02:02 +00:00
|
|
|
self._state_attrs[ATTR_LOAD_POWER] = state.load_power
|
|
|
|
|
2017-10-26 21:37:30 +00:00
|
|
|
except DeviceException as ex:
|
2018-03-16 18:58:03 +00:00
|
|
|
self._available = False
|
2017-10-26 21:37:30 +00:00
|
|
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|