Checking Xiaomi Aqara devices unavailability states (#11631)
* added unavailability tracker, updated sensor component * change hass argument position according to position in binary_sensor * added hass argument to binary_sensor, updated is_on(), it can be UNAVAILABLE now * updated switch component to support unavailability feature * updated light component to support unavailability feature * updated cover component to support unavailability feature * set _hass property * added unavailability tracker, updated sensor component * change hass argument position according to position in binary_sensor * added hass argument to binary_sensor, updated is_on(), it can be UNAVAILABLE now * updated switch component to support unavailability feature * updated light component to support unavailability feature * updated cover component to support unavailability feature * set _hass property * fixed error with wrong arguments number during callback call * reset unavailability state on new message received from device * use locks to fix race condition during managing _state property * overriden state() method for some components to check for STATE_UNAVAILABLE and return it instead e.g. STATE_OFF * fixed linter * removed blank line * use available() method instead of changing _state * filter motion sensors 'heartbeat', was removed from PyXiaomiGateway * remove self._hass, use self.hass set by HA on attach * self.push_data now running in the event loop, use async_schedule_update_ha_state() * merge fix * removed accidentally added home-assistant-polymer * bump PyXiaomiGateway version to 0.8.0 * bump PyXiaomiGateway to 0.8.0 * updated methods names and annotations
This commit is contained in:
parent
95592d9283
commit
3417c6ad8d
7 changed files with 72 additions and 21 deletions
|
@ -101,7 +101,7 @@ class XiaomiNatgasSensor(XiaomiBinarySensor):
|
|||
attrs.update(super().device_state_attributes)
|
||||
return attrs
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
if DENSITY in data:
|
||||
self._density = int(data.get(DENSITY))
|
||||
|
@ -139,8 +139,16 @@ class XiaomiMotionSensor(XiaomiBinarySensor):
|
|||
attrs.update(super().device_state_attributes)
|
||||
return attrs
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
if raw_data['cmd'] == 'heartbeat':
|
||||
_LOGGER.debug(
|
||||
'Skipping heartbeat of the motion sensor. '
|
||||
'It can introduce an incorrect state because of a firmware '
|
||||
'bug (https://github.com/home-assistant/home-assistant/pull/'
|
||||
'11631#issuecomment-357507744).')
|
||||
return
|
||||
|
||||
self._should_poll = False
|
||||
if NO_MOTION in data: # handle push from the hub
|
||||
self._no_motion_since = data[NO_MOTION]
|
||||
|
@ -186,7 +194,7 @@ class XiaomiDoorSensor(XiaomiBinarySensor):
|
|||
attrs.update(super().device_state_attributes)
|
||||
return attrs
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
self._should_poll = False
|
||||
if NO_CLOSE in data: # handle push from the hub
|
||||
|
@ -219,7 +227,7 @@ class XiaomiWaterLeakSensor(XiaomiBinarySensor):
|
|||
XiaomiBinarySensor.__init__(self, device, 'Water Leak Sensor',
|
||||
xiaomi_hub, 'status', 'moisture')
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
self._should_poll = False
|
||||
|
||||
|
@ -256,7 +264,7 @@ class XiaomiSmokeSensor(XiaomiBinarySensor):
|
|||
attrs.update(super().device_state_attributes)
|
||||
return attrs
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
if DENSITY in data:
|
||||
self._density = int(data.get(DENSITY))
|
||||
|
@ -293,7 +301,7 @@ class XiaomiButton(XiaomiBinarySensor):
|
|||
attrs.update(super().device_state_attributes)
|
||||
return attrs
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
value = data.get(self._data_key)
|
||||
if value is None:
|
||||
|
@ -343,7 +351,7 @@ class XiaomiCube(XiaomiBinarySensor):
|
|||
attrs.update(super().device_state_attributes)
|
||||
return attrs
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
if 'status' in data:
|
||||
self._hass.bus.fire('cube_action', {
|
||||
|
|
|
@ -59,7 +59,7 @@ class XiaomiGenericCover(XiaomiDevice, CoverDevice):
|
|||
"""Move the cover to a specific position."""
|
||||
self._write_to_hub(self._sid, **{self._data_key['pos']: str(position)})
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
if ATTR_CURTAIN_LEVEL in data:
|
||||
self._pos = int(data[ATTR_CURTAIN_LEVEL])
|
||||
|
|
|
@ -39,7 +39,7 @@ class XiaomiGatewayLight(XiaomiDevice, Light):
|
|||
"""Return true if it is on."""
|
||||
return self._state
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
value = data.get(self._data_key)
|
||||
if value is None:
|
||||
|
|
|
@ -61,7 +61,7 @@ class XiaomiSensor(XiaomiDevice):
|
|||
"""Return the state of the sensor."""
|
||||
return self._state
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
value = data.get(self._data_key)
|
||||
if value is None:
|
||||
|
|
|
@ -107,7 +107,7 @@ class XiaomiGenericSwitch(XiaomiDevice, SwitchDevice):
|
|||
self._state = False
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
if IN_USE in data:
|
||||
self._in_use = int(data[IN_USE])
|
||||
|
|
|
@ -7,17 +7,22 @@ https://home-assistant.io/components/xiaomi_aqara/
|
|||
import asyncio
|
||||
import logging
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.discovery import SERVICE_XIAOMI_GW
|
||||
from homeassistant.const import (
|
||||
ATTR_BATTERY_LEVEL, CONF_HOST, CONF_MAC, CONF_PORT,
|
||||
EVENT_HOMEASSISTANT_STOP)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import discovery
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
REQUIREMENTS = ['PyXiaomiGateway==0.7.1']
|
||||
REQUIREMENTS = ['PyXiaomiGateway==0.8.0']
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -33,7 +38,9 @@ CONF_KEY = 'key'
|
|||
|
||||
DOMAIN = 'xiaomi_aqara'
|
||||
|
||||
PY_XIAOMI_GATEWAY = 'xiaomi_gw'
|
||||
PY_XIAOMI_GATEWAY = "xiaomi_gw"
|
||||
|
||||
TIME_TILL_UNAVAILABLE = timedelta(minutes=150)
|
||||
|
||||
SERVICE_PLAY_RINGTONE = 'play_ringtone'
|
||||
SERVICE_STOP_RINGTONE = 'stop_ringtone'
|
||||
|
@ -201,20 +208,35 @@ class XiaomiDevice(Entity):
|
|||
def __init__(self, device, name, xiaomi_hub):
|
||||
"""Initialize the Xiaomi device."""
|
||||
self._state = None
|
||||
self._is_available = True
|
||||
self._sid = device['sid']
|
||||
self._name = '{}_{}'.format(name, self._sid)
|
||||
self._write_to_hub = xiaomi_hub.write_to_hub
|
||||
self._get_from_hub = xiaomi_hub.get_from_hub
|
||||
self._device_state_attributes = {}
|
||||
xiaomi_hub.callbacks[self._sid].append(self.push_data)
|
||||
self.parse_data(device['data'])
|
||||
self._remove_unavailability_tracker = None
|
||||
xiaomi_hub.callbacks[self._sid].append(self._add_push_data_job)
|
||||
self.parse_data(device['data'], device['raw_data'])
|
||||
self.parse_voltage(device['data'])
|
||||
|
||||
def _add_push_data_job(self, *args):
|
||||
self.hass.async_add_job(self.push_data, *args)
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_added_to_hass(self):
|
||||
"""Start unavailability tracking."""
|
||||
self._async_track_unavailable()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if entity is available."""
|
||||
return self._is_available
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the polling state. No polling needed."""
|
||||
|
@ -225,13 +247,34 @@ class XiaomiDevice(Entity):
|
|||
"""Return the state attributes."""
|
||||
return self._device_state_attributes
|
||||
|
||||
def push_data(self, data):
|
||||
@callback
|
||||
def _async_set_unavailable(self, now):
|
||||
"""Set state to UNAVAILABLE."""
|
||||
self._remove_unavailability_tracker = None
|
||||
self._is_available = False
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
@callback
|
||||
def _async_track_unavailable(self):
|
||||
if self._remove_unavailability_tracker:
|
||||
self._remove_unavailability_tracker()
|
||||
self._remove_unavailability_tracker = async_track_point_in_utc_time(
|
||||
self.hass, self._async_set_unavailable,
|
||||
utcnow() + TIME_TILL_UNAVAILABLE)
|
||||
if not self._is_available:
|
||||
self._is_available = True
|
||||
return True
|
||||
return False
|
||||
|
||||
@callback
|
||||
def push_data(self, data, raw_data):
|
||||
"""Push from Hub."""
|
||||
_LOGGER.debug("PUSH >> %s: %s", self, data)
|
||||
is_data = self.parse_data(data)
|
||||
was_unavailable = self._async_track_unavailable()
|
||||
is_data = self.parse_data(data, raw_data)
|
||||
is_voltage = self.parse_voltage(data)
|
||||
if is_data or is_voltage:
|
||||
self.schedule_update_ha_state()
|
||||
if is_data or is_voltage or was_unavailable:
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
def parse_voltage(self, data):
|
||||
"""Parse battery level data sent by gateway."""
|
||||
|
@ -246,7 +289,7 @@ class XiaomiDevice(Entity):
|
|||
self._device_state_attributes[ATTR_BATTERY_LEVEL] = round(percent, 1)
|
||||
return True
|
||||
|
||||
def parse_data(self, data):
|
||||
def parse_data(self, data, raw_data):
|
||||
"""Parse data sent by gateway."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ PyMVGLive==1.1.4
|
|||
PyMata==2.14
|
||||
|
||||
# homeassistant.components.xiaomi_aqara
|
||||
PyXiaomiGateway==0.7.1
|
||||
PyXiaomiGateway==0.8.0
|
||||
|
||||
# homeassistant.components.rpi_gpio
|
||||
# RPi.GPIO==0.6.1
|
||||
|
|
Loading…
Add table
Reference in a new issue