diff --git a/homeassistant/components/binary_sensor/xiaomi_aqara.py b/homeassistant/components/binary_sensor/xiaomi_aqara.py index 49f716b9eb7b9..eee1c6a79bad0 100644 --- a/homeassistant/components/binary_sensor/xiaomi_aqara.py +++ b/homeassistant/components/binary_sensor/xiaomi_aqara.py @@ -2,8 +2,8 @@ import logging from homeassistant.components.binary_sensor import BinarySensorDevice -from homeassistant.components.xiaomi_aqara import (PY_XIAOMI_GATEWAY, - XiaomiDevice) +from homeassistant.components.xiaomi_aqara import ( + PY_XIAOMI_GATEWAY, XiaomiDevice, DOMAIN_CONFIG, CONF_LOCKUIDS) _LOGGER = logging.getLogger(__name__) @@ -15,18 +15,29 @@ ATTR_LAST_ACTION = 'last_action' ATTR_NO_MOTION_SINCE = 'No motion since' +VERIFIED_WRONG = 'verified_wrong' +FING_VERIFIED = 'fing_verified' +CARD_VERIFIED = 'card_verified' +PSW_VERIFIED = 'psw_verified' + DENSITY = 'density' ATTR_DENSITY = 'Density' def setup_platform(hass, config, add_devices, discovery_info=None): """Perform the setup for Xiaomi devices.""" + if PY_XIAOMI_GATEWAY not in hass.data: + return devices = [] for (_, gateway) in hass.data[PY_XIAOMI_GATEWAY].gateways.items(): for device in gateway.devices['binary_sensor']: model = device['model'] if model in ['motion', 'sensor_motion', 'sensor_motion.aq2']: devices.append(XiaomiMotionSensor(device, hass, gateway)) + elif model in ['lock.aq1']: + devices.append(XiaomiUnlockSensor(hass.data[DOMAIN_CONFIG], + devices, device, + hass, gateway)) elif model in ['magnet', 'sensor_magnet', 'sensor_magnet.aq2']: devices.append(XiaomiDoorSensor(device, gateway)) elif model == 'sensor_wleak.aq1': @@ -64,6 +75,8 @@ class XiaomiBinarySensor(XiaomiDevice, BinarySensorDevice): def __init__(self, device, name, xiaomi_hub, data_key, device_class): """Initialize the XiaomiSmokeSensor.""" self._data_key = data_key + self._real_sid = \ + None if 'real_sid' not in device else device['real_sid'] self._device_class = device_class self._should_poll = False self._density = 0 @@ -86,6 +99,8 @@ def device_class(self): def update(self): """Update the sensor state.""" + if self._real_sid is not None: + return _LOGGER.debug('Updating xiaomi sensor by polling') self._get_from_hub(self._sid) @@ -187,6 +202,102 @@ def parse_data(self, data, raw_data): return True +class XiaomiUnlockSensor(XiaomiBinarySensor): + """Representation of a XiaomiUnlockSensor.""" + + def __init__(self, config, devices, device, hass, xiaomi_hub): + """Initialize the XiaomiUnlockSensor.""" + self._hass = hass + self._no_motion_since = 0 + self.sub_devices = {} + users = config[CONF_LOCKUIDS] if CONF_LOCKUIDS in config else [] + users.append({ + 'uid': 'unsecu' + }) + for user in users: + self.sub_devices[str(user['uid'])] = XiaomiUnlockSubSensor({ + 'sid': str(user['uid']), + 'real_sid': device['sid'], + 'model': 'motion', + 'data': { + }, + 'raw_data': { + 'cmd': 'report' + } + }, hass, xiaomi_hub) + devices.append(self.sub_devices[str(user['uid'])]) + XiaomiBinarySensor.__init__(self, device, 'Unlock Sensor', xiaomi_hub, + 'status', 'motion') + + def push_sub_devices(self, uid=None): + """Parse data sent by gateway.""" + for key in self.sub_devices: + self.sub_devices[key].push_data({ + 'status': + MOTION if uid is not None and uid == key else NO_MOTION + }, { + 'sid': key, + 'cmd': 'report', + 'status': + MOTION if uid is not None and uid == key else NO_MOTION + }) + + def parse_data(self, data, raw_data): + """Parse data sent by gateway.""" + if raw_data['cmd'] in ['heartbeat', 'read_ack', 'read_rsp']: + return + + self._should_poll = False + if VERIFIED_WRONG in data: # handle push from the hub + self.push_sub_devices('unsecu') + self._state = False + return True + + value = data.get(FING_VERIFIED) + if value is None: + value = data.get(CARD_VERIFIED) + if value is None: + value = data.get(PSW_VERIFIED) + if value is None: + self.push_sub_devices() + self._state = False + return False + + self.push_sub_devices(str(value)) + + self._should_poll = True + if self.entity_id is not None: + self._hass.bus.fire('motion', { + 'entity_id': self.entity_id + }) + self._state = True + return True + + +class XiaomiUnlockSubSensor(XiaomiBinarySensor): + """Representation of a XiaomiUnlockSubSensor.""" + + def __init__(self, device, hass, xiaomi_hub): + """Initialize the XiaomiUnlockSubSensor.""" + self._hass = hass + self._no_motion_since = 0 + if 'proto' not in device or int(device['proto'][0:1]) == 1: + data_key = 'status' + else: + data_key = 'motion_status' + XiaomiBinarySensor.__init__(self, device, 'UnlockSub Sensor', + xiaomi_hub, data_key, 'motion') + + def parse_data(self, data, raw_data): + """Parse data sent by gateway.""" + if raw_data['cmd'] == 'heartbeat': + return + + value = data.get(self._data_key) + self._state = value is not None and value == MOTION + return True + + class XiaomiDoorSensor(XiaomiBinarySensor): """Representation of a XiaomiDoorSensor.""" diff --git a/homeassistant/components/sensor/xiaomi_aqara.py b/homeassistant/components/sensor/xiaomi_aqara.py index 497a3915154d2..10de19257a1e8 100644 --- a/homeassistant/components/sensor/xiaomi_aqara.py +++ b/homeassistant/components/sensor/xiaomi_aqara.py @@ -80,7 +80,10 @@ def parse_data(self, data, raw_data): if self._data_key in ['temperature', 'humidity', 'pressure']: value /= 100 elif self._data_key in ['illumination']: - value = max(value - 300, 0) + if self._model in 'acpartner.v3': + value = max(value, 0) + else: + value = max(value - 300, 0) if self._data_key == 'temperature' and (value < -50 or value > 60): return False elif self._data_key == 'humidity' and (value <= 0 or value > 100): diff --git a/homeassistant/components/xiaomi_aqara.py b/homeassistant/components/xiaomi_aqara.py index 48c54cdecff90..e8bcf27a6195f 100644 --- a/homeassistant/components/xiaomi_aqara.py +++ b/homeassistant/components/xiaomi_aqara.py @@ -34,10 +34,12 @@ CONF_DISCOVERY_RETRY = 'discovery_retry' CONF_GATEWAYS = 'gateways' +CONF_LOCKUIDS = 'lockuids' CONF_INTERFACE = 'interface' CONF_KEY = 'key' DOMAIN = 'xiaomi_aqara' +DOMAIN_CONFIG = 'xiaomi_aqara_config' PY_XIAOMI_GATEWAY = "xiaomi_gw" @@ -95,6 +97,7 @@ def _fix_conf_defaults(config): DOMAIN: vol.Schema({ vol.Optional(CONF_GATEWAYS, default={}): vol.All(cv.ensure_list, [GATEWAY_CONFIG], [_fix_conf_defaults]), + vol.Optional(CONF_LOCKUIDS, default={}): vol.All(cv.ensure_list), vol.Optional(CONF_INTERFACE, default='any'): cv.string, vol.Optional(CONF_DISCOVERY_RETRY, default=3): cv.positive_int }) @@ -110,6 +113,7 @@ def setup(hass, config): gateways = config[DOMAIN][CONF_GATEWAYS] interface = config[DOMAIN][CONF_INTERFACE] discovery_retry = config[DOMAIN][CONF_DISCOVERY_RETRY] + hass.data[DOMAIN_CONFIG] = config[DOMAIN] @asyncio.coroutine def xiaomi_gw_discovered(service, discovery_info): @@ -209,6 +213,7 @@ def __init__(self, device, device_type, xiaomi_hub): self._state = None self._is_available = True self._sid = device['sid'] + self._model = device['model'] self._name = '{}_{}'.format(device_type, self._sid) self._type = device_type self._write_to_hub = xiaomi_hub.write_to_hub