Xiaomi Gateway subdevice support & AqaraHT + SensorHT devices (#36539)

* Gateway subdevice support & AqaraHT + SensorHT devices

* Gateway subdevice support & AqaraHT + SensorHT devices

* Add starkillerOG to codeowners

as proposed by @rytilahti in this issue: https://github.com/home-assistant/core/issues/36516

* add starkillerOG to xiaomi_miio

* fix config flow tests

* Update CODEOWNERS

* Update manifest.json

* prosess revieuw comments

* fix missing import

* use proper pressure unit hPa

* subdevice --> sub_device

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* subdevice --> sub_device

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* use key acces instead of get

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* subdevice --> sub_device

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* subdevice --> sub_device

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* subdevice --> sub_device

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* subdevice --> sub_device

* use dataclass instead of namedtuple

* update to newest python-miio functions (not yet released)

* Move device info to entitie

* remove unused variable

* improve default names

* SensorHT does not support pressure

* bump python-miio to 0.5.2

* bump python-miio to 0.5.2

* bump python-miio to 0.5.2

* Fix missing brackets

Co-authored-by: Teemu R. <tpr@iki.fi>

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
Co-authored-by: Teemu R. <tpr@iki.fi>
pull/37472/head
starkillerOG 2020-07-04 14:56:16 +02:00 committed by GitHub
parent 0b11fda017
commit ffcdd85117
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 158 additions and 2 deletions

View File

@ -11,7 +11,7 @@ from .gateway import ConnectXiaomiGateway
_LOGGER = logging.getLogger(__name__)
GATEWAY_PLATFORMS = ["alarm_control_panel"]
GATEWAY_PLATFORMS = ["alarm_control_panel", "sensor"]
async def async_setup(hass: core.HomeAssistant, config: dict):

View File

@ -3,6 +3,10 @@ import logging
from miio import DeviceException, gateway
from homeassistant.helpers.entity import Entity
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
@ -30,9 +34,14 @@ class ConnectXiaomiGateway:
_LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])
try:
self._gateway_device = gateway.Gateway(host, token)
# get the gateway info
self._gateway_info = await self._hass.async_add_executor_job(
self._gateway_device.info
)
# get the connected sub devices
await self._hass.async_add_executor_job(
self._gateway_device.discover_devices
)
except DeviceException:
_LOGGER.error(
"DeviceException during setup of xiaomi gateway with host %s", host
@ -45,3 +54,51 @@ class ConnectXiaomiGateway:
self._gateway_info.hardware_version,
)
return True
class XiaomiGatewayDevice(Entity):
"""Representation of a base Xiaomi Gateway Device."""
def __init__(self, sub_device, entry):
"""Initialize the Xiaomi Gateway Device."""
self._sub_device = sub_device
self._entry = entry
self._unique_id = sub_device.sid
self._name = f"{sub_device.name} ({sub_device.sid})"
self._available = None
@property
def unique_id(self):
"""Return an unique ID."""
return self._unique_id
@property
def name(self):
"""Return the name of this entity, if any."""
return self._name
@property
def device_info(self):
"""Return the device info of the gateway."""
return {
"identifiers": {(DOMAIN, self._sub_device.sid)},
"via_device": (DOMAIN, self._entry.unique_id),
"manufacturer": "Xiaomi",
"name": self._sub_device.name,
"model": self._sub_device.model,
"sw_version": self._sub_device.firmware_version,
}
@property
def available(self):
"""Return true when state is known."""
return self._available
async def async_update(self):
"""Fetch state from the sub device."""
try:
await self.hass.async_add_executor_job(self._sub_device.update)
self._available = True
except gateway.GatewayException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)

View File

@ -1,15 +1,31 @@
"""Support for Xiaomi Mi Air Quality Monitor (PM2.5)."""
from dataclasses import dataclass
import logging
from miio import AirQualityMonitor, DeviceException # pylint: disable=import-error
from miio.gateway import DeviceType
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_TOKEN
from homeassistant.const import (
CONF_HOST,
CONF_NAME,
CONF_TOKEN,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
PRESSURE_HPA,
TEMP_CELSIUS,
UNIT_PERCENTAGE,
)
from homeassistant.exceptions import PlatformNotReady
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import Entity
from .config_flow import CONF_FLOW_TYPE, CONF_GATEWAY
from .const import DOMAIN
from .gateway import XiaomiGatewayDevice
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = "Xiaomi Miio Sensor"
@ -36,6 +52,51 @@ ATTR_MODEL = "model"
SUCCESS = ["ok"]
@dataclass
class SensorType:
"""Class that holds device specific info for a xiaomi aqara sensor."""
unit: str = None
icon: str = None
device_class: str = None
GATEWAY_SENSOR_TYPES = {
"temperature": SensorType(
unit=TEMP_CELSIUS, icon=None, device_class=DEVICE_CLASS_TEMPERATURE
),
"humidity": SensorType(
unit=UNIT_PERCENTAGE, icon=None, device_class=DEVICE_CLASS_HUMIDITY
),
"pressure": SensorType(
unit=PRESSURE_HPA, icon=None, device_class=DEVICE_CLASS_PRESSURE
),
}
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]
sub_devices = gateway.devices
for sub_device in sub_devices.values():
if sub_device.type == DeviceType.SensorHT:
sensor_variables = ["temperature", "humidity"]
if sub_device.type == DeviceType.AqaraHT:
sensor_variables = ["temperature", "humidity", "pressure"]
entities.extend(
[
XiaomiGatewaySensor(sub_device, config_entry, variable)
for variable in sensor_variables
]
)
async_add_entities(entities, update_before_add=True)
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up the sensor from config."""
if DATA_KEY not in hass.data:
@ -156,3 +217,34 @@ class XiaomiAirQualityMonitor(Entity):
except DeviceException as ex:
self._available = False
_LOGGER.error("Got exception while fetching the state: %s", ex)
class XiaomiGatewaySensor(XiaomiGatewayDevice):
"""Representation of a XiaomiGatewaySensor."""
def __init__(self, sub_device, entry, data_key):
"""Initialize the XiaomiSensor."""
super().__init__(sub_device, entry)
self._data_key = data_key
self._unique_id = f"{sub_device.sid}-{data_key}"
self._name = f"{data_key} ({sub_device.sid})".capitalize()
@property
def icon(self):
"""Return the icon to use in the frontend."""
return GATEWAY_SENSOR_TYPES[self._data_key].icon
@property
def unit_of_measurement(self):
"""Return the unit of measurement of this entity, if any."""
return GATEWAY_SENSOR_TYPES[self._data_key].unit
@property
def device_class(self):
"""Return the device class of this entity."""
return GATEWAY_SENSOR_TYPES[self._data_key].device_class
@property
def state(self):
"""Return the state of the sensor."""
return self._sub_device.status[self._data_key]

View File

@ -21,6 +21,7 @@ TEST_GATEWAY_ID = TEST_MAC
TEST_HARDWARE_VERSION = "AB123"
TEST_FIRMWARE_VERSION = "1.2.3_456"
TEST_ZEROCONF_NAME = "lumi-gateway-v3_miio12345678._miio._udp.local."
TEST_SUB_DEVICE_LIST = []
def get_mock_info(
@ -111,6 +112,9 @@ async def test_config_flow_gateway_success(hass):
with patch(
"homeassistant.components.xiaomi_miio.gateway.gateway.Gateway.info",
return_value=mock_info,
), patch(
"homeassistant.components.xiaomi_miio.gateway.gateway.Gateway.discover_devices",
return_value=TEST_SUB_DEVICE_LIST,
), patch(
"homeassistant.components.xiaomi_miio.async_setup_entry", return_value=True
):
@ -151,6 +155,9 @@ async def test_zeroconf_gateway_success(hass):
with patch(
"homeassistant.components.xiaomi_miio.gateway.gateway.Gateway.info",
return_value=mock_info,
), patch(
"homeassistant.components.xiaomi_miio.gateway.gateway.Gateway.discover_devices",
return_value=TEST_SUB_DEVICE_LIST,
), patch(
"homeassistant.components.xiaomi_miio.async_setup_entry", return_value=True
):