From dd52ec78c7162c4994f8ed4a9cf91285881b3155 Mon Sep 17 00:00:00 2001 From: Kevin Stillhammer Date: Thu, 30 Sep 2021 13:23:46 +0200 Subject: [PATCH] Add Kraken delay after first update to avoid limit (#55736) * Add delay after first update to avoid limit * Apply suggestions --- homeassistant/components/kraken/__init__.py | 4 +- homeassistant/components/kraken/sensor.py | 6 +- tests/components/kraken/const.py | 70 ++++++++++++++++++++ tests/components/kraken/test_sensor.py | 73 ++++++++++++--------- 4 files changed, 120 insertions(+), 33 deletions(-) diff --git a/homeassistant/components/kraken/__init__.py b/homeassistant/components/kraken/__init__.py index 76a4976f163..5b1fd2626e3 100644 --- a/homeassistant/components/kraken/__init__.py +++ b/homeassistant/components/kraken/__init__.py @@ -127,7 +127,7 @@ class KrakenData: self._config_entry, options=options ) await self._async_refresh_tradable_asset_pairs() - # Wait 1 second to avoid triggering the CallRateLimiter + # Wait 1 second to avoid triggering the KrakenAPI CallRateLimiter await asyncio.sleep(CALL_RATE_LIMIT_SLEEP) self.coordinator = DataUpdateCoordinator( self._hass, @@ -139,6 +139,8 @@ class KrakenData: ), ) await self.coordinator.async_config_entry_first_refresh() + # Wait 1 second to avoid triggering the KrakenAPI CallRateLimiter + await asyncio.sleep(CALL_RATE_LIMIT_SLEEP) def _get_websocket_name_asset_pairs(self) -> str: return ",".join(wsname for wsname in self.tradable_asset_pairs.values()) diff --git a/homeassistant/components/kraken/sensor.py b/homeassistant/components/kraken/sensor.py index 9c2030766f7..b7d38d4796b 100644 --- a/homeassistant/components/kraken/sensor.py +++ b/homeassistant/components/kraken/sensor.py @@ -129,12 +129,14 @@ class KrakenSensor(CoordinatorEntity[Optional[KrakenResponse]], SensorEntity): super()._handle_coordinator_update() def _update_internal_state(self) -> None: + if not self.coordinator.data: + return try: self._attr_native_value = self.entity_description.value_fn( self.coordinator, self.tracked_asset_pair_wsname # type: ignore[arg-type] ) - self._received_data_at_least_once = True # Received data at least one time. - except TypeError: + self._received_data_at_least_once = True + except KeyError: if self._received_data_at_least_once: if self._available: _LOGGER.warning( diff --git a/tests/components/kraken/const.py b/tests/components/kraken/const.py index 6e3174a9ae7..78658ffd660 100644 --- a/tests/components/kraken/const.py +++ b/tests/components/kraken/const.py @@ -7,6 +7,12 @@ TRADEABLE_ASSET_PAIR_RESPONSE = pandas.DataFrame( index=["ADAXBT", "ADAETH", "XBTEUR", "XXBTZGBP", "XXBTZUSD", "XXBTZJPY"], ) +MISSING_PAIR_TRADEABLE_ASSET_PAIR_RESPONSE = pandas.DataFrame( + {"wsname": ["ADA/XBT", "ADA/ETH", "XBT/EUR", "XBT/GBP", "XBT/JPY"]}, + columns=["wsname"], + index=["ADAXBT", "ADAETH", "XBTEUR", "XXBTZGBP", "XXBTZJPY"], +) + TICKER_INFORMATION_RESPONSE = pandas.DataFrame( { "a": [ @@ -78,3 +84,67 @@ TICKER_INFORMATION_RESPONSE = pandas.DataFrame( columns=["a", "b", "c", "h", "l", "o", "p", "t", "v"], index=["ADAXBT", "ADAETH", "XBTEUR", "XXBTZGBP", "XXBTZUSD", "XXBTZJPY"], ) + +MISSING_PAIR_TICKER_INFORMATION_RESPONSE = pandas.DataFrame( + { + "a": [ + [0.000349400, 15949, 15949.000], + [0.000349400, 15949, 15949.000], + [0.000349400, 15949, 15949.000], + [0.000349400, 15949, 15949.000], + [0.000349400, 15949, 15949.000], + ], + "b": [ + [0.000348400, 20792, 20792.000], + [0.000348400, 20792, 20792.000], + [0.000348400, 20792, 20792.000], + [0.000348400, 20792, 20792.000], + [0.000348400, 20792, 20792.000], + ], + "c": [ + [0.000347800, 2809.36384377], + [0.000347800, 2809.36384377], + [0.000347800, 2809.36384377], + [0.000347800, 2809.36384377], + [0.000347800, 2809.36384377], + ], + "h": [ + [0.000351600, 0.000352100], + [0.000351600, 0.000352100], + [0.000351600, 0.000352100], + [0.000351600, 0.000352100], + [0.000351600, 0.000352100], + ], + "l": [ + [0.000344600, 0.000344600], + [0.000344600, 0.000344600], + [0.000344600, 0.000344600], + [0.000344600, 0.000344600], + [0.000344600, 0.000344600], + ], + "o": [ + 0.000351300, + 0.000351300, + 0.000351300, + 0.000351300, + 0.000351300, + ], + "p": [ + [0.000348573, 0.000344881], + [0.000348573, 0.000344881], + [0.000348573, 0.000344881], + [0.000348573, 0.000344881], + [0.000348573, 0.000344881], + ], + "t": [[82, 128], [82, 128], [82, 128], [82, 128], [82, 128]], + "v": [ + [146300.24906838, 253478.04715403], + [146300.24906838, 253478.04715403], + [146300.24906838, 253478.04715403], + [146300.24906838, 253478.04715403], + [146300.24906838, 253478.04715403], + ], + }, + columns=["a", "b", "c", "h", "l", "o", "p", "t", "v"], + index=["ADAXBT", "ADAETH", "XBTEUR", "XXBTZGBP", "XXBTZJPY"], +) diff --git a/tests/components/kraken/test_sensor.py b/tests/components/kraken/test_sensor.py index 98760a3002d..110a944a4d5 100644 --- a/tests/components/kraken/test_sensor.py +++ b/tests/components/kraken/test_sensor.py @@ -13,7 +13,12 @@ from homeassistant.components.kraken.const import ( from homeassistant.const import CONF_SCAN_INTERVAL, EVENT_HOMEASSISTANT_START import homeassistant.util.dt as dt_util -from .const import TICKER_INFORMATION_RESPONSE, TRADEABLE_ASSET_PAIR_RESPONSE +from .const import ( + MISSING_PAIR_TICKER_INFORMATION_RESPONSE, + MISSING_PAIR_TRADEABLE_ASSET_PAIR_RESPONSE, + TICKER_INFORMATION_RESPONSE, + TRADEABLE_ASSET_PAIR_RESPONSE, +) from tests.common import MockConfigEntry, async_fire_time_changed @@ -230,38 +235,46 @@ async def test_missing_pair_marks_sensor_unavailable(hass): with patch("homeassistant.util.dt.utcnow", return_value=utcnow), patch( "pykrakenapi.KrakenAPI.get_tradable_asset_pairs", return_value=TRADEABLE_ASSET_PAIR_RESPONSE, - ): - with patch( - "pykrakenapi.KrakenAPI.get_ticker_information", - return_value=TICKER_INFORMATION_RESPONSE, - ): - entry = MockConfigEntry( - domain=DOMAIN, - options={ - CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL, - CONF_TRACKED_ASSET_PAIRS: [DEFAULT_TRACKED_ASSET_PAIR], - }, - ) - entry.add_to_hass(hass) + ) as tradeable_asset_pairs_mock, patch( + "pykrakenapi.KrakenAPI.get_ticker_information", + return_value=TICKER_INFORMATION_RESPONSE, + ) as ticket_information_mock: + entry = MockConfigEntry( + domain=DOMAIN, + options={ + CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL, + CONF_TRACKED_ASSET_PAIRS: [DEFAULT_TRACKED_ASSET_PAIR], + }, + ) + entry.add_to_hass(hass) - await hass.config_entries.async_setup(entry.entry_id) + await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() + await hass.async_block_till_done() - hass.bus.async_fire(EVENT_HOMEASSISTANT_START) - await hass.async_block_till_done() + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + await hass.async_block_till_done() - sensor = hass.states.get("sensor.xbt_usd_ask") - assert sensor.state == "0.0003494" + sensor = hass.states.get("sensor.xbt_usd_ask") + assert sensor.state == "0.0003494" - with patch( - "pykrakenapi.KrakenAPI.get_ticker_information", - side_effect=KrakenAPIError("EQuery:Unknown asset pair"), - ): - async_fire_time_changed( - hass, utcnow + timedelta(seconds=DEFAULT_SCAN_INTERVAL * 2) - ) - await hass.async_block_till_done() + tradeable_asset_pairs_mock.return_value = ( + MISSING_PAIR_TRADEABLE_ASSET_PAIR_RESPONSE + ) + ticket_information_mock.side_effect = KrakenAPIError( + "EQuery:Unknown asset pair" + ) + async_fire_time_changed( + hass, utcnow + timedelta(seconds=DEFAULT_SCAN_INTERVAL * 2) + ) + await hass.async_block_till_done() - sensor = hass.states.get("sensor.xbt_usd_ask") - assert sensor.state == "unavailable" + ticket_information_mock.side_effect = None + ticket_information_mock.return_value = MISSING_PAIR_TICKER_INFORMATION_RESPONSE + async_fire_time_changed( + hass, utcnow + timedelta(seconds=DEFAULT_SCAN_INTERVAL * 2) + ) + await hass.async_block_till_done() + + sensor = hass.states.get("sensor.xbt_usd_ask") + assert sensor.state == "unavailable"