diff --git a/custom_components/jq300/__init__.py b/custom_components/jq300/__init__.py index bb0f9ca..cc8e364 100644 --- a/custom_components/jq300/__init__.py +++ b/custom_components/jq300/__init__.py @@ -33,7 +33,6 @@ CONCENTRATION_PARTS_PER_MILLION, ) from homeassistant.helpers import discovery -from homeassistant.helpers.dispatcher import dispatcher_send from homeassistant.util import Throttle from requests import PreparedRequest @@ -61,9 +60,9 @@ PLATFORMS, UPDATE_TIMEOUT, ACCOUNT_CONTROLLER, - SIGNAL_UPDATE_JQ300, CONF_ACCOUNT_ID, MQTT_URL, + AVAILABLE_TIMEOUT, ) from .util import mask_email @@ -311,11 +310,11 @@ def _query(self, query_type, function: str, extra_params=None) -> Optional[dict] return response @property - def is_connected(self): + def is_connected(self) -> bool: """Return True if connected to account.""" return self.params["uid"] > 0 - def connect(self, force=False) -> bool: + def connect(self, force: bool = False) -> bool: """(Re)Connect to account and return connection status.""" if not force and self.params["uid"] > 0: return True @@ -412,17 +411,16 @@ def _mqtt_process_message(self, message: dict): ) elif message["type"] == "C": - _LOGGER.debug("Update online status for device %d", device_id) if self.devices.get(device_id) is None: return - self.devices[device_id]["onlinestat"] = message["content"] + online = int(message["content"]) + _LOGGER.debug("Update online status for device %d: %d", device_id, online) + if online != self.devices[device_id].get("onlinestat"): + self.devices[device_id]["onlinets"] = monotonic() + self.devices[device_id]["onlinestat"] = online else: _LOGGER.warning("Unknown message type: %s", message) - return - - if self.hass: - dispatcher_send(self.hass, SIGNAL_UPDATE_JQ300) def _get_devices_mqtt_topics(self, device_ids: list) -> list: devs = self.update_devices() @@ -487,6 +485,7 @@ def update_devices(self, force=False) -> Optional[Dict[int, Dict[str, Any]]]: tstamp = int(dt_util.now().timestamp() * 1000) for dev in ret["deviceInfoBodyList"]: dev[""] = tstamp + dev["onlinets"] = monotonic() self._devices[dev["deviceid"]] = dev for device_id in self._devices: @@ -524,11 +523,23 @@ async def async_update_devices_or_timeout(self, timeout=UPDATE_TIMEOUT): def device_available(self, device_id) -> bool: """Return True if device is available.""" - return self.available and ( - self.devices.get(device_id, {}).get("onlinestat") == "1" - or monotonic() - self._sensors_last_update < 60 + dev = self.devices.get(device_id, {}) + device_available = dev.get("onlinestat") == 1 + device_timeout = (monotonic() - dev.get("onlinets", 0)) <= AVAILABLE_TIMEOUT + online = self.available and (device_available or device_timeout) + + # pylint: disable=logging-too-many-args + _LOGGER.debug( + "Availability: %s (account) AND (%s (device %s) OR %s (timeout)) = $s", + self.available, + device_available, + device_id, + device_timeout, + online, ) + return online + def _extract_sensors_data(self, device_id, ts_now: int, sensors: dict): res = {} for sensor in sensors: @@ -577,9 +588,6 @@ def update_sensors(self, _=None): self._extract_sensors_data(device_id, ts_now, ret["deviceValueVos"]) - if self.hass: - dispatcher_send(self.hass, SIGNAL_UPDATE_JQ300) - @Throttle(timedelta(minutes=10)) async def async_update_sensors_or_timeout(self, timeout=UPDATE_TIMEOUT): """Update current states of all active devices for account.""" diff --git a/custom_components/jq300/binary_sensor.py b/custom_components/jq300/binary_sensor.py index 0be45ee..72d6189 100644 --- a/custom_components/jq300/binary_sensor.py +++ b/custom_components/jq300/binary_sensor.py @@ -15,8 +15,6 @@ from homeassistant import exceptions from homeassistant.components.binary_sensor import ENTITY_ID_FORMAT from homeassistant.const import CONF_DEVICES -from homeassistant.core import callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import async_generate_entity_id # Code snippet to keep compatibility with Home Assistant 0.109 @@ -35,7 +33,6 @@ ATTR_DEVICE_MODEL, ATTR_DEVICE_ID, ACCOUNT_CONTROLLER, - SIGNAL_UPDATE_JQ300, CONF_ACCOUNT_ID, ) @@ -114,19 +111,6 @@ def __init__( self._icon = BINARY_SENSORS[sensor_id][2] self._device_class = BINARY_SENSORS[sensor_id][3] - async def async_added_to_hass(self): - """Register callbacks.""" - self.async_on_remove( - async_dispatcher_connect( - self.hass, SIGNAL_UPDATE_JQ300, self._update_callback - ) - ) - - @callback - def _update_callback(self): - """Call update method.""" - self.async_schedule_update_ha_state(True) - @property def name(self): """Return the name of the binary sensor.""" @@ -169,11 +153,16 @@ def icon(self): @property def should_poll(self): """Return the polling state.""" - return False + return True def update(self): """Update the sensor state if it needed.""" ret = self._account.get_sensors_raw(self._device_id) - if ret: - self._state = ret[self._sensor_id] - _LOGGER.debug("Update state: %s = %s", self.entity_id, self._state) + if not ret: + return + + if self._state == ret[self._sensor_id]: + return + + self._state = ret[self._sensor_id] + _LOGGER.debug("Update state: %s = %s", self.entity_id, self._state) diff --git a/custom_components/jq300/const.py b/custom_components/jq300/const.py index f86cef0..7ba50b2 100644 --- a/custom_components/jq300/const.py +++ b/custom_components/jq300/const.py @@ -106,10 +106,9 @@ SENSORS_FILTER_FRAME = timedelta(minutes=5) -SIGNAL_UPDATE_JQ300 = "jq300_update" - QUERY_TIMEOUT = 7 # seconds UPDATE_TIMEOUT = 12 # seconds +AVAILABLE_TIMEOUT = 30 # seconds MWEIGTH_TVOC = 56.1060 # g/mol MWEIGTH_HCHO = 30.0260 # g/mol diff --git a/custom_components/jq300/sensor.py b/custom_components/jq300/sensor.py index c225347..7501abb 100644 --- a/custom_components/jq300/sensor.py +++ b/custom_components/jq300/sensor.py @@ -15,8 +15,6 @@ from homeassistant import exceptions from homeassistant.components.sensor import ENTITY_ID_FORMAT from homeassistant.const import CONF_DEVICES -from homeassistant.core import callback -from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity, async_generate_entity_id from . import JqAccount, CannotConnect @@ -29,7 +27,6 @@ ATTR_DEVICE_ID, ACCOUNT_CONTROLLER, CONF_ACCOUNT_ID, - SIGNAL_UPDATE_JQ300, ) _LOGGER = logging.getLogger(__name__) @@ -109,19 +106,6 @@ def __init__( self._icon = SENSORS[sensor_id][2] self._device_class = SENSORS[sensor_id][3] - async def async_added_to_hass(self): - """Register callbacks.""" - self.async_on_remove( - async_dispatcher_connect( - self.hass, SIGNAL_UPDATE_JQ300, self._update_callback - ) - ) - - @callback - def _update_callback(self): - """Call update method.""" - self.async_schedule_update_ha_state(True) - @property def name(self): """Return the name of the sensor.""" @@ -170,19 +154,21 @@ def unit_of_measurement(self): @property def should_poll(self): """Return the polling state.""" - return False + return True def update(self): """Update the sensor state if it needed.""" ret = self._account.get_sensors(self._device_id) - if ret: - self._state = ret[self._sensor_id] - self._state_raw = self._account.get_sensors_raw(self._device_id)[ - self._sensor_id - ] - _LOGGER.debug( - "Update state: %s = %s (%s)", - self.entity_id, - self._state, - self._state_raw, - ) + if not ret: + return + + state = ret[self._sensor_id] + state_raw = self._account.get_sensors_raw(self._device_id)[self._sensor_id] + if self._state == state and self._state_raw == state_raw: + return + + self._state = state + self._state_raw = state_raw + _LOGGER.debug( + "Update state: %s = %s (%s)", self.entity_id, self._state, self._state_raw, + )