Skip to content

Commit

Permalink
Fix #28
Browse files Browse the repository at this point in the history
  • Loading branch information
Limych committed Apr 5, 2021
1 parent 42ac60d commit f7b4f6e
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 43 deletions.
5 changes: 4 additions & 1 deletion custom_components/jq300/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import logging
from datetime import timedelta

import async_timeout
import homeassistant.helpers.config_validation as cv
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
Expand All @@ -31,6 +32,7 @@
DOMAIN,
PLATFORMS,
STARTUP_MESSAGE,
UPDATE_TIMEOUT,
)
from .util import mask_email

Expand Down Expand Up @@ -95,7 +97,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)

try:
devices = await account.async_update_devices_or_timeout()
with async_timeout.timeout(UPDATE_TIMEOUT):
devices = await hass.async_add_executor_job(account.update_devices)
except TimeoutError as exc:
raise ConfigEntryNotReady from exc

Expand Down
58 changes: 24 additions & 34 deletions custom_components/jq300/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import json
import logging
from datetime import timedelta
from time import monotonic, sleep
from time import monotonic
from typing import Any, Dict, Optional
from urllib.parse import urlparse

Expand Down Expand Up @@ -184,10 +184,16 @@ def _get_url(self, query_type, function: str, extra_params=None) -> str:
url = self._add_url_params(url, extra_params)
return url

# pylint: disable=too-many-return-statements,too-many-branches
async def _async_query(
self, query_type, function: str, extra_params=None
) -> Optional[dict]:
"""Query data from cloud."""
return await self.hass.async_add_executor_job(
self._query, query_type, function, extra_params
)

# pylint: disable=too-many-return-statements,too-many-branches
def _query(self, query_type, function: str, extra_params=None) -> Optional[dict]:
"""Query data from cloud."""
url = self._get_url(query_type, function)
_LOGGER.debug("Requesting URL %s", url)
Expand Down Expand Up @@ -252,7 +258,7 @@ def is_connected(self) -> bool:
"""Return True if connected to account."""
return self.params["uid"] > 0

async def async_connect(self, force: bool = 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
Expand All @@ -261,7 +267,7 @@ async def async_connect(self, force: bool = False) -> bool:

self.params["uid"] = -1000
self.params["safeToken"] = "anonymous"
ret = await self._async_query(
ret = self._query(
QUERY_TYPE_API,
"loginByEmail",
extra_params={
Expand All @@ -272,7 +278,7 @@ async def async_connect(self, force: bool = False) -> bool:
},
)
if not ret:
return await self.async_connect(True) if not force else False
return self.connect(True) if not force else False

self.params["uid"] = ret["uid"]
self.params["safeToken"] = ret["safeToken"]
Expand Down Expand Up @@ -347,6 +353,7 @@ def _mqtt_process_message(self, message: dict):
int(dt_util.now().timestamp()),
json.loads(message["content"]),
)
self.devices[device_id]["onlinets"] = monotonic()

elif message["type"] == "C":
if self.devices.get(device_id) is None:
Expand All @@ -362,8 +369,7 @@ def _mqtt_process_message(self, message: dict):

def _get_devices_mqtt_topics(self, device_ids: list) -> list:
if not self.devices:
self.hass.add_job(self.async_update_devices())
sleep(1)
self.update_devices()

if not self.devices:
return []
Expand Down Expand Up @@ -400,11 +406,9 @@ def devices(self) -> dict:
"""Get available devices."""
return self._devices

async def async_update_devices(
self, force=False
) -> Optional[Dict[int, Dict[str, Any]]]:
"""Update available devices."""
if not await self.async_connect():
def update_devices(self, force=False) -> Optional[Dict[int, Dict[str, Any]]]:
"""Update available devices from cloud."""
if not self.connect():
_LOGGER.error("Can't connect to cloud.")
return None

Expand All @@ -413,7 +417,7 @@ async def async_update_devices(

_LOGGER.debug("Updating devices list for account %s", self.name_secure)

ret = await self._async_query(
ret = self._query(
QUERY_TYPE_API,
"deviceManager",
extra_params={
Expand All @@ -423,7 +427,7 @@ async def async_update_devices(
},
)
if not ret:
return await self.async_update_devices(True) if not force else None
return self.update_devices(True) if not force else None

tstamp = int(dt_util.now().timestamp() * 1000)
for dev in ret["deviceInfoBodyList"]:
Expand All @@ -436,24 +440,6 @@ async def async_update_devices(

return self._devices

async def async_update_devices_or_timeout(self, timeout=UPDATE_TIMEOUT):
"""Get available devices list from cloud."""
start = monotonic()
try:
with async_timeout.timeout(timeout):
return await self.async_update_devices()

except TimeoutError as exc:
_LOGGER.error("Timeout fetching %s devices list", self.name_secure)
raise exc

finally:
_LOGGER.debug(
"Finished fetching %s devices list in %.3f seconds",
self.name_secure,
monotonic() - start,
)

def device_available(self, device_id) -> bool:
"""Return True if device is available."""
dev = self.devices.get(device_id, {})
Expand Down Expand Up @@ -493,8 +479,12 @@ def _extract_sensors_data(self, device_id, ts_now: int, sensors: dict):
self._sensors[device_id][ts_now] = res
self._sensors_raw[device_id] = res

@Throttle(timedelta(minutes=10))
async def async_update_sensors(self):
"""Update current states of all active devices for account."""
return await self.hass.async_add_executor_job(self.update_sensors)

@Throttle(timedelta(minutes=10))
def update_sensors(self):
"""Update current states of all active devices for account."""
_LOGGER.debug("Updating sensors state for account %s", self.name_secure)

Expand All @@ -504,7 +494,7 @@ async def async_update_sensors(self):
if self.get_sensors_raw(device_id) is not None:
continue

ret = await self._async_query(
ret = self._query(
QUERY_TYPE_DEVICE,
"list",
extra_params={
Expand Down
12 changes: 4 additions & 8 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,14 @@ def bypass_get_data_fixture():
"test_device": {"pt_name": "test_name"},
"another_device": {"pt_name": "another_name"},
}
with patch.object(
Jq300Account, "async_update_devices_or_timeout", return_value=res
):
with patch.object(Jq300Account, "update_devices", return_value=res):
yield


# In this fixture, we are forcing calls to async_get_data to raise an Exception. This is useful
# for exception handling.
# In this fixture, we are forcing calls to async_get_data to raise an Exception.
# This is useful for exception handling.
@pytest.fixture(name="error_on_get_data")
def error_get_data_fixture():
"""Simulate error when retrieving data from API."""
with patch.object(
Jq300Account, "async_update_devices_or_timeout", side_effect=TimeoutError
):
with patch.object(Jq300Account, "update_devices", side_effect=TimeoutError):
yield

0 comments on commit f7b4f6e

Please sign in to comment.