Add Xiaomi Miio gateway illuminance sensor and gateway light (#37959)
Co-authored-by: Xiaonan Shen <s@sxn.dev> Co-authored-by: Teemu R. <tpr@iki.fi>
This commit is contained in:
parent
84205a9a57
commit
f187091594
5 changed files with 217 additions and 7 deletions
|
@ -11,7 +11,7 @@ from .gateway import ConnectXiaomiGateway
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
GATEWAY_PLATFORMS = ["alarm_control_panel", "sensor"]
|
||||
GATEWAY_PLATFORMS = ["alarm_control_panel", "sensor", "light"]
|
||||
|
||||
|
||||
async def async_setup(hass: core.HomeAssistant, config: dict):
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
from functools import partial
|
||||
import logging
|
||||
|
||||
from miio import DeviceException
|
||||
from miio.gateway import GatewayException
|
||||
|
||||
from homeassistant.components.alarm_control_panel import (
|
||||
SUPPORT_ALARM_ARM_AWAY,
|
||||
|
@ -103,7 +103,7 @@ class XiaomiGatewayAlarm(AlarmControlPanelEntity):
|
|||
partial(func, *args, **kwargs)
|
||||
)
|
||||
_LOGGER.debug("Response received from miio device: %s", result)
|
||||
except DeviceException as exc:
|
||||
except GatewayException as exc:
|
||||
_LOGGER.error(mask_error, exc)
|
||||
|
||||
async def async_alarm_arm_away(self, code=None):
|
||||
|
@ -122,7 +122,7 @@ class XiaomiGatewayAlarm(AlarmControlPanelEntity):
|
|||
"""Fetch state from the device."""
|
||||
try:
|
||||
state = await self.hass.async_add_executor_job(self._gateway.alarm.status)
|
||||
except DeviceException as ex:
|
||||
except GatewayException as ex:
|
||||
self._available = False
|
||||
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||
return
|
||||
|
|
|
@ -17,6 +17,7 @@ CONF_FLOW_TYPE = "config_flow_device"
|
|||
CONF_GATEWAY = "gateway"
|
||||
DEFAULT_GATEWAY_NAME = "Xiaomi Gateway"
|
||||
ZEROCONF_GATEWAY = "lumi-gateway"
|
||||
ZEROCONF_ACPARTNER = "lumi-acpartner"
|
||||
|
||||
GATEWAY_SETTINGS = {
|
||||
vol.Required(CONF_TOKEN): vol.All(str, vol.Length(min=32, max=32)),
|
||||
|
@ -61,7 +62,7 @@ class XiaomiMiioFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
return self.async_abort(reason="not_xiaomi_miio")
|
||||
|
||||
# Check which device is discovered.
|
||||
if name.startswith(ZEROCONF_GATEWAY):
|
||||
if name.startswith(ZEROCONF_GATEWAY) or name.startswith(ZEROCONF_ACPARTNER):
|
||||
unique_id = format_mac(mac_address)
|
||||
await self.async_set_unique_id(unique_id)
|
||||
self._abort_if_unique_id_configured({CONF_HOST: self.host})
|
||||
|
|
|
@ -14,6 +14,12 @@ from miio import ( # pylint: disable=import-error
|
|||
PhilipsEyecare,
|
||||
PhilipsMoonlight,
|
||||
)
|
||||
from miio.gateway import (
|
||||
GATEWAY_MODEL_AC_V1,
|
||||
GATEWAY_MODEL_AC_V2,
|
||||
GATEWAY_MODEL_AC_V3,
|
||||
GatewayException,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.light import (
|
||||
|
@ -31,6 +37,7 @@ from homeassistant.exceptions import PlatformNotReady
|
|||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.util import color, dt
|
||||
|
||||
from .config_flow import CONF_FLOW_TYPE, CONF_GATEWAY
|
||||
from .const import (
|
||||
DOMAIN,
|
||||
SERVICE_EYECARE_MODE_OFF,
|
||||
|
@ -123,6 +130,25 @@ SERVICE_TO_METHOD = {
|
|||
}
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up the Xiaomi light from a config entry."""
|
||||
entities = []
|
||||
|
||||
if config_entry.data[CONF_FLOW_TYPE] == CONF_GATEWAY:
|
||||
gateway = hass.data[DOMAIN][config_entry.entry_id]
|
||||
# Gateway light
|
||||
if gateway.model not in [
|
||||
GATEWAY_MODEL_AC_V1,
|
||||
GATEWAY_MODEL_AC_V2,
|
||||
GATEWAY_MODEL_AC_V3,
|
||||
]:
|
||||
entities.append(
|
||||
XiaomiGatewayLight(gateway, config_entry.title, config_entry.unique_id)
|
||||
)
|
||||
|
||||
async_add_entities(entities, update_before_add=True)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the light from config."""
|
||||
if DATA_KEY not in hass.data:
|
||||
|
@ -938,3 +964,104 @@ class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
|||
async def async_set_delayed_turn_off(self, time_period: timedelta):
|
||||
"""Set delayed turn off. Unsupported."""
|
||||
return
|
||||
|
||||
|
||||
class XiaomiGatewayLight(LightEntity):
|
||||
"""Representation of a gateway device's light."""
|
||||
|
||||
def __init__(self, gateway_device, gateway_name, gateway_device_id):
|
||||
"""Initialize the XiaomiGatewayLight."""
|
||||
self._gateway = gateway_device
|
||||
self._name = f"{gateway_name} Light"
|
||||
self._gateway_device_id = gateway_device_id
|
||||
self._unique_id = gateway_device_id
|
||||
self._available = False
|
||||
self._is_on = None
|
||||
self._brightness_pct = 100
|
||||
self._rgb = (255, 255, 255)
|
||||
self._hs = (0, 0)
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return an unique ID."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device info of the gateway."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self._gateway_device_id)},
|
||||
}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of this entity, if any."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return true when state is known."""
|
||||
return self._available
|
||||
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return true if it is on."""
|
||||
return self._is_on
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
"""Return the brightness of this light between 0..255."""
|
||||
return int(255 * self._brightness_pct / 100)
|
||||
|
||||
@property
|
||||
def hs_color(self):
|
||||
"""Return the hs color value."""
|
||||
return self._hs
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Return the supported features."""
|
||||
return SUPPORT_BRIGHTNESS | SUPPORT_COLOR
|
||||
|
||||
def turn_on(self, **kwargs):
|
||||
"""Turn the light on."""
|
||||
if ATTR_HS_COLOR in kwargs:
|
||||
rgb = color.color_hs_to_RGB(*kwargs[ATTR_HS_COLOR])
|
||||
else:
|
||||
rgb = self._rgb
|
||||
|
||||
if ATTR_BRIGHTNESS in kwargs:
|
||||
brightness_pct = int(100 * kwargs[ATTR_BRIGHTNESS] / 255)
|
||||
else:
|
||||
brightness_pct = self._brightness_pct
|
||||
|
||||
self._gateway.light.set_rgb(brightness_pct, rgb)
|
||||
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def turn_off(self, **kwargs):
|
||||
"""Turn the light off."""
|
||||
self._gateway.light.set_rgb(0, self._rgb)
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
async def async_update(self):
|
||||
"""Fetch state from the device."""
|
||||
try:
|
||||
state_dict = await self.hass.async_add_executor_job(
|
||||
self._gateway.light.rgb_status
|
||||
)
|
||||
except GatewayException as ex:
|
||||
if self._available:
|
||||
self._available = False
|
||||
_LOGGER.error(
|
||||
"Got exception while fetching the gateway light state: %s", ex
|
||||
)
|
||||
return
|
||||
|
||||
self._available = True
|
||||
self._is_on = state_dict["is_on"]
|
||||
|
||||
if self._is_on:
|
||||
self._brightness_pct = state_dict["brightness"]
|
||||
self._rgb = state_dict["rgb"]
|
||||
self._hs = color.color_RGB_to_hs(*self._rgb)
|
||||
|
|
|
@ -3,7 +3,13 @@ from dataclasses import dataclass
|
|||
import logging
|
||||
|
||||
from miio import AirQualityMonitor, DeviceException # pylint: disable=import-error
|
||||
from miio.gateway import DeviceType
|
||||
from miio.gateway import (
|
||||
GATEWAY_MODEL_AC_V1,
|
||||
GATEWAY_MODEL_AC_V2,
|
||||
GATEWAY_MODEL_AC_V3,
|
||||
DeviceType,
|
||||
GatewayException,
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
|
@ -12,6 +18,7 @@ from homeassistant.const import (
|
|||
CONF_NAME,
|
||||
CONF_TOKEN,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
PRESSURE_HPA,
|
||||
|
@ -78,9 +85,20 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
"""Set up the Xiaomi sensor from a config entry."""
|
||||
entities = []
|
||||
|
||||
# Gateway sub devices
|
||||
if config_entry.data[CONF_FLOW_TYPE] == CONF_GATEWAY:
|
||||
gateway = hass.data[DOMAIN][config_entry.entry_id]
|
||||
# Gateway illuminance sensor
|
||||
if gateway.model not in [
|
||||
GATEWAY_MODEL_AC_V1,
|
||||
GATEWAY_MODEL_AC_V2,
|
||||
GATEWAY_MODEL_AC_V3,
|
||||
]:
|
||||
entities.append(
|
||||
XiaomiGatewayIlluminanceSensor(
|
||||
gateway, config_entry.title, config_entry.unique_id
|
||||
)
|
||||
)
|
||||
# Gateway sub devices
|
||||
sub_devices = gateway.devices
|
||||
for sub_device in sub_devices.values():
|
||||
sensor_variables = None
|
||||
|
@ -250,3 +268,67 @@ class XiaomiGatewaySensor(XiaomiGatewayDevice):
|
|||
def state(self):
|
||||
"""Return the state of the sensor."""
|
||||
return self._sub_device.status[self._data_key]
|
||||
|
||||
|
||||
class XiaomiGatewayIlluminanceSensor(Entity):
|
||||
"""Representation of the gateway device's illuminance sensor."""
|
||||
|
||||
def __init__(self, gateway_device, gateway_name, gateway_device_id):
|
||||
"""Initialize the entity."""
|
||||
self._gateway = gateway_device
|
||||
self._name = f"{gateway_name} Illuminance"
|
||||
self._gateway_device_id = gateway_device_id
|
||||
self._unique_id = f"{gateway_device_id}-illuminance"
|
||||
self._available = False
|
||||
self._state = None
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return an unique ID."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device info of the gateway."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self._gateway_device_id)},
|
||||
}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of this entity, if any."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return true when state is known."""
|
||||
return self._available
|
||||
|
||||
@property
|
||||
def unit_of_measurement(self):
|
||||
"""Return the unit of measurement of this entity."""
|
||||
return "lux"
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the device class of this entity."""
|
||||
return DEVICE_CLASS_ILLUMINANCE
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the device."""
|
||||
return self._state
|
||||
|
||||
async def async_update(self):
|
||||
"""Fetch state from the device."""
|
||||
try:
|
||||
self._state = await self.hass.async_add_executor_job(
|
||||
self._gateway.get_illumination
|
||||
)
|
||||
self._available = True
|
||||
except GatewayException as ex:
|
||||
if self._available:
|
||||
self._available = False
|
||||
_LOGGER.error(
|
||||
"Got exception while fetching the gateway illuminance state: %s", ex
|
||||
)
|
||||
|
|
Loading…
Add table
Reference in a new issue