diff --git a/.devcontainer/configuration.yaml b/.devcontainer/configuration.yaml index 875b0fd..ef92cda 100755 --- a/.devcontainer/configuration.yaml +++ b/.devcontainer/configuration.yaml @@ -12,6 +12,7 @@ logger: yahoofinance: show_trending_icon: true + show_currency_symbol_as_unit: true #Interval too small - will throw an error #scan_interval: 30 diff --git a/README.md b/README.md index f958f89..f5abd94 100755 --- a/README.md +++ b/README.md @@ -106,6 +106,10 @@ friendly_name: Ivy Science & Technology Fund C include_two_hundred_day_values: false ``` +- The currency symbol e.g. $ can be show as the unit instead of USD by setting `show_currency_symbol_as_unit: true`. + - **Note:** Using this setting will generate a warning like `The unit of this entity changed to '$' which can't be converted ...` You will have to manually resolve it by picking the first option to update the unit of the historicalvalues without convertion. This can be done from `Developer tools > STATISTICS`. + + ### Symbol - An alternate target currency can be specified for a symbol using the extended declaration format. Here, the symbol EMIM.L is reported in USD but will be presented in EUR. The conversion would be based on the value of the symbol USDEUR=X. diff --git a/custom_components/yahoofinance/__init__.py b/custom_components/yahoofinance/__init__.py index 6db8b13..8fa8035 100755 --- a/custom_components/yahoofinance/__init__.py +++ b/custom_components/yahoofinance/__init__.py @@ -25,6 +25,7 @@ CONF_INCLUDE_PRE_VALUES, CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES, CONF_NO_UNIT, + CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, CONF_SHOW_TRENDING_ICON, CONF_SYMBOLS, CONF_TARGET_CURRENCY, @@ -35,6 +36,7 @@ DEFAULT_CONF_INCLUDE_PRE_VALUES, DEFAULT_CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES, DEFAULT_CONF_NO_UNIT, + DEFAULT_CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, DEFAULT_CONF_SHOW_TRENDING_ICON, DEFAULT_SCAN_INTERVAL, DOMAIN, @@ -88,6 +90,10 @@ def minimum_scan_interval(value: timedelta) -> timedelta: vol.Optional( CONF_SHOW_TRENDING_ICON, default=DEFAULT_CONF_SHOW_TRENDING_ICON ): cv.boolean, + vol.Optional( + CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, + default=DEFAULT_CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, + ): cv.boolean, vol.Optional( CONF_DECIMAL_PLACES, default=DEFAULT_CONF_DECIMAL_PLACES ): vol.Coerce(int), @@ -134,12 +140,12 @@ def __init__(self, symbol: str, **kwargs: any) -> None: """ self.symbol = symbol - if "target_currency" in kwargs: - self.target_currency = kwargs["target_currency"] - if "scan_interval" in kwargs: - self.scan_interval = kwargs["scan_interval"] - if "no_unit" in kwargs: - self.no_unit = kwargs["no_unit"] + if CONF_TARGET_CURRENCY in kwargs: + self.target_currency = kwargs[CONF_TARGET_CURRENCY] + if CONF_SCAN_INTERVAL in kwargs: + self.scan_interval = kwargs[CONF_SCAN_INTERVAL] + if CONF_NO_UNIT in kwargs: + self.no_unit = kwargs[CONF_NO_UNIT] def __repr__(self) -> str: """Return the representation.""" diff --git a/custom_components/yahoofinance/const.py b/custom_components/yahoofinance/const.py index ed102ca..21e1a75 100755 --- a/custom_components/yahoofinance/const.py +++ b/custom_components/yahoofinance/const.py @@ -47,6 +47,7 @@ CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES: Final = "include_two_hundred_day_values" CONF_INCLUDE_FIFTY_TWO_WEEK_VALUES: Final = "include_fifty_two_week_values" CONF_SHOW_TRENDING_ICON: Final = "show_trending_icon" +CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT = "show_currency_symbol_as_unit" CONF_TARGET_CURRENCY: Final = "target_currency" CONF_NO_UNIT: Final = "no_unit" @@ -57,6 +58,7 @@ DEFAULT_CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES: Final = True DEFAULT_CONF_INCLUDE_FIFTY_TWO_WEEK_VALUES: Final = True DEFAULT_CONF_SHOW_TRENDING_ICON: Final = False +DEFAULT_CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT: Final = False DEFAULT_CONF_NO_UNIT: Final = False DEFAULT_NUMERIC_DATA_GROUP: Final = "default" @@ -176,22 +178,32 @@ "bdt": "৳", "brl": "R$", "btc": "₿", + "cad": "CA$", "chf": "₣", "cny": "¥", "eth": "Ξ", "eur": "€", "gbp": "£", + "hkd": "HK$", "ils": "₪", "inr": "₹", "jpy": "¥", "krw": "₩", "kzt": "лв", + "mxn": "MX$", "ngn": "₦", + "nzd": "NZ$", "php": "₱", "rial": "﷼", "rub": "₽", "sign": "", "try": "₺", - "twd": "$", + "twd": "NT$", "usd": "$", + "vnd": "₫", + "xaf": "FCFA", + "xcd": "EC$", + "xof": "F\u202fCFA", + "xpf": "CFPF", + "xxx": "¤", } diff --git a/custom_components/yahoofinance/sensor.py b/custom_components/yahoofinance/sensor.py index 9ff2d8e..53e6b2f 100755 --- a/custom_components/yahoofinance/sensor.py +++ b/custom_components/yahoofinance/sensor.py @@ -35,6 +35,7 @@ ATTR_TRENDING, ATTRIBUTION, CONF_DECIMAL_PLACES, + CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, CONF_SHOW_TRENDING_ICON, CONF_SYMBOLS, CURRENCY_CODES, @@ -124,6 +125,9 @@ def __init__( symbol = symbol_definition.symbol self._symbol = symbol self._show_trending_icon = domain_config[CONF_SHOW_TRENDING_ICON] + self._show_currency_symbol_as_unit = domain_config[ + CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT + ] self._decimal_places = domain_config[CONF_DECIMAL_PLACES] self._previous_close = None self._target_currency = symbol_definition.target_currency @@ -181,7 +185,9 @@ def convert_timestamp_to_datetime(date_timestamp, return_format) -> str | None: if date_timestamp is None or date_timestamp == 0: return date_timestamp - converted_date = datetime.fromtimestamp(date_timestamp,tz=dt_util.DEFAULT_TIME_ZONE) + converted_date = datetime.fromtimestamp( + date_timestamp, tz=dt_util.DEFAULT_TIME_ZONE + ) if return_format == "date": converted_date = converted_date.date() @@ -211,9 +217,12 @@ def native_unit_of_measurement(self) -> str | None: if self._no_unit: return None - if self._target_currency: - return self._target_currency - return self._currency + currency = self._target_currency if self._target_currency else self._currency + + if self._show_currency_symbol_as_unit: + return CURRENCY_CODES.get(currency.lower(), currency) + + return currency @property def icon(self) -> str: @@ -377,21 +386,29 @@ def update_properties(self) -> None: DATA_MARKET_STATE ] - self._attr_extra_state_attributes[ - ATTR_DIVIDEND_DATE - ] = self.convert_timestamp_to_datetime(symbol_data.get(DATA_DIVIDEND_DATE),'date') + self._attr_extra_state_attributes[ATTR_DIVIDEND_DATE] = ( + self.convert_timestamp_to_datetime( + symbol_data.get(DATA_DIVIDEND_DATE), "date" + ) + ) - self._attr_extra_state_attributes[ - ATTR_REGULAR_MARKET_TIME - ] = self.convert_timestamp_to_datetime(symbol_data.get(DATA_REGULAR_MARKET_TIME),'dateTime') + self._attr_extra_state_attributes[ATTR_REGULAR_MARKET_TIME] = ( + self.convert_timestamp_to_datetime( + symbol_data.get(DATA_REGULAR_MARKET_TIME), "dateTime" + ) + ) - self._attr_extra_state_attributes[ - ATTR_POST_MARKET_TIME - ] = self.convert_timestamp_to_datetime(symbol_data.get(DATA_POST_MARKET_TIME),'dateTime') + self._attr_extra_state_attributes[ATTR_POST_MARKET_TIME] = ( + self.convert_timestamp_to_datetime( + symbol_data.get(DATA_POST_MARKET_TIME), "dateTime" + ) + ) - self._attr_extra_state_attributes[ - ATTR_PRE_MARKET_TIME - ] = self.convert_timestamp_to_datetime(symbol_data.get(DATA_PRE_MARKET_TIME),'dateTime') + self._attr_extra_state_attributes[ATTR_PRE_MARKET_TIME] = ( + self.convert_timestamp_to_datetime( + symbol_data.get(DATA_PRE_MARKET_TIME), "dateTime" + ) + ) # Use target_currency if we have conversion data. Otherwise keep using the # currency from data. diff --git a/tests/test_init.py b/tests/test_init.py index ebfdc24..29b40c9 100755 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -13,6 +13,7 @@ CONF_INCLUDE_POST_VALUES, CONF_INCLUDE_PRE_VALUES, CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES, + CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, CONF_SHOW_TRENDING_ICON, CONF_SYMBOLS, DEFAULT_CONF_DECIMAL_PLACES, @@ -21,6 +22,7 @@ DEFAULT_CONF_INCLUDE_POST_VALUES, DEFAULT_CONF_INCLUDE_PRE_VALUES, DEFAULT_CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES, + DEFAULT_CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, DEFAULT_CONF_SHOW_TRENDING_ICON, DEFAULT_SCAN_INTERVAL, DOMAIN, @@ -31,8 +33,10 @@ SERVICE_REFRESH, ) from homeassistant.const import CONF_SCAN_INTERVAL +from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -from tests import TEST_CRUMB, TEST_SYMBOL + +from . import TEST_CRUMB, TEST_SYMBOL SAMPLE_CONFIG = {DOMAIN: {CONF_SYMBOLS: [TEST_SYMBOL]}} YSUC = "custom_components.yahoofinance.YahooSymbolUpdateCoordinator" @@ -45,33 +49,27 @@ CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES: DEFAULT_CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES, CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL, CONF_SHOW_TRENDING_ICON: DEFAULT_CONF_SHOW_TRENDING_ICON, + CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT: DEFAULT_CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, CONF_INCLUDE_FIFTY_TWO_WEEK_VALUES: DEFAULT_CONF_INCLUDE_FIFTY_TWO_WEEK_VALUES, } + @pytest.mark.parametrize( "domain_config", [ # a is invalid interval - { - CONF_SYMBOLS: ["xyz"], - CONF_SCAN_INTERVAL: "a" - }, + {CONF_SYMBOLS: ["xyz"], CONF_SCAN_INTERVAL: "a"}, # less than MINIMUM_SCAN_INTERVAL { CONF_SYMBOLS: ["xyz"], - CONF_SCAN_INTERVAL: MINIMUM_SCAN_INTERVAL + timedelta(seconds = -1) + CONF_SCAN_INTERVAL: MINIMUM_SCAN_INTERVAL + timedelta(seconds=-1), }, - { - CONF_SYMBOLS: ["xyz"], - CONF_SCAN_INTERVAL: None - } - ] + {CONF_SYMBOLS: ["xyz"], CONF_SCAN_INTERVAL: None}, + ], ) async def test_invalid_global_scan_interval( - hass, - domain_config, - enable_custom_integrations -): + hass: HomeAssistant, domain_config, enable_custom_integrations: None +) -> None: """Check for invalid scan_interval.""" assert await async_setup_component(hass, DOMAIN, {DOMAIN: domain_config}) is False @@ -82,12 +80,20 @@ async def test_invalid_global_scan_interval( ( # Simple format - no scan_interval defaults to DEFAULT_SCAN_INTERVAL {CONF_SYMBOLS: ["xyz"]}, - {CONF_SYMBOLS: [SymbolDefinition("XYZ", scan_interval=DEFAULT_SCAN_INTERVAL)]}, + { + CONF_SYMBOLS: [ + SymbolDefinition("XYZ", scan_interval=DEFAULT_SCAN_INTERVAL) + ] + }, ), ( # Expanded format - no scan_interval defaults to DEFAULT_SCAN_INTERVAL {CONF_SYMBOLS: [{"symbol": "xyz"}]}, - {CONF_SYMBOLS: [SymbolDefinition("XYZ", scan_interval=DEFAULT_SCAN_INTERVAL)]}, + { + CONF_SYMBOLS: [ + SymbolDefinition("XYZ", scan_interval=DEFAULT_SCAN_INTERVAL) + ] + }, ), ( # Mix of Simple and expanded formats - no scan_interval defaults to DEFAULT_SCAN_INTERVAL @@ -102,12 +108,20 @@ async def test_invalid_global_scan_interval( ( # Simple format - Duplicate removed, scan_interval defaults to DEFAULT_SCAN_INTERVAL {CONF_SYMBOLS: ["xyz", "xyz"]}, - {CONF_SYMBOLS: [SymbolDefinition("XYZ", scan_interval=DEFAULT_SCAN_INTERVAL)]}, + { + CONF_SYMBOLS: [ + SymbolDefinition("XYZ", scan_interval=DEFAULT_SCAN_INTERVAL) + ] + }, ), ( # Mixed formats - duplicate removed, scan_interval defaults to DEFAULT_SCAN_INTERVAL {CONF_SYMBOLS: [{"symbol": "xyz"}, "xyz"]}, - {CONF_SYMBOLS: [SymbolDefinition("XYZ", scan_interval=DEFAULT_SCAN_INTERVAL)]}, + { + CONF_SYMBOLS: [ + SymbolDefinition("XYZ", scan_interval=DEFAULT_SCAN_INTERVAL) + ] + }, ), ( # Simple format - custom global scan interval @@ -146,7 +160,9 @@ async def test_invalid_global_scan_interval( CONF_SCAN_INTERVAL: MANUAL_SCAN_INTERVAL, }, { - CONF_SYMBOLS: [SymbolDefinition("XYZ", scan_interval=MANUAL_SCAN_INTERVAL)], + CONF_SYMBOLS: [ + SymbolDefinition("XYZ", scan_interval=MANUAL_SCAN_INTERVAL) + ], CONF_SCAN_INTERVAL: MANUAL_SCAN_INTERVAL, }, ), @@ -157,29 +173,35 @@ async def test_invalid_global_scan_interval( CONF_SCAN_INTERVAL: MANUAL_SCAN_INTERVAL, }, { - CONF_SYMBOLS: [SymbolDefinition("XYZ", scan_interval=MANUAL_SCAN_INTERVAL)], + CONF_SYMBOLS: [ + SymbolDefinition("XYZ", scan_interval=MANUAL_SCAN_INTERVAL) + ], CONF_SCAN_INTERVAL: MANUAL_SCAN_INTERVAL, }, ), ( # Expanded format - override None scan interval { - CONF_SYMBOLS: [{"symbol": "xyz", "scan_interval": MANUAL_SCAN_INTERVAL}], - CONF_SCAN_INTERVAL: 3600 + CONF_SYMBOLS: [ + {"symbol": "xyz", "scan_interval": MANUAL_SCAN_INTERVAL} + ], + CONF_SCAN_INTERVAL: 3600, }, { - CONF_SYMBOLS: [SymbolDefinition("XYZ", scan_interval=MANUAL_SCAN_INTERVAL)], + CONF_SYMBOLS: [ + SymbolDefinition("XYZ", scan_interval=MANUAL_SCAN_INTERVAL) + ], CONF_SCAN_INTERVAL: timedelta(hours=1), }, ), ], ) async def test_scan_interval( - hass, + hass: HomeAssistant, domain_config, expected_partial_config, - enable_custom_integrations -): + enable_custom_integrations: None, +) -> None: """Component setup refreshed data coordinator and loads the platform.""" mock_instance = MagicMock() @@ -188,7 +210,9 @@ async def test_scan_interval( type(mock_instance).data = PropertyMock(return_value=None) type(mock_instance).last_update_success = PropertyMock(return_value=False) - with patch(YSUC, return_value=mock_instance), patch(f"{YCC}.try_get_crumb_cookies", AsyncMock(return_value=TEST_CRUMB)): + with patch(YSUC, return_value=mock_instance), patch( + f"{YCC}.try_get_crumb_cookies", AsyncMock(return_value=TEST_CRUMB) + ): config = {DOMAIN: domain_config} assert await async_setup_component(hass, DOMAIN, config) is True @@ -203,8 +227,8 @@ async def test_scan_interval( async def test_setup_optionally_requests_coordinator_refresh( - hass, enable_custom_integrations -): + hass: HomeAssistant, enable_custom_integrations: None +) -> None: """Component setup requests data coordinator refresh if it failed to load data.""" mock_instance = MagicMock() @@ -213,7 +237,9 @@ async def test_setup_optionally_requests_coordinator_refresh( type(mock_instance).data = PropertyMock(return_value=None) type(mock_instance).last_update_success = PropertyMock(return_value=False) - with patch(YSUC, return_value=mock_instance), patch(f"{YCC}.try_get_crumb_cookies", AsyncMock(return_value=TEST_CRUMB)): + with patch(YSUC, return_value=mock_instance), patch( + f"{YCC}.try_get_crumb_cookies", AsyncMock(return_value=TEST_CRUMB) + ): assert await async_setup_component(hass, DOMAIN, SAMPLE_CONFIG) is True await hass.async_block_till_done() @@ -222,12 +248,14 @@ async def test_setup_optionally_requests_coordinator_refresh( async def test_refresh_symbols_service( - hass, - enable_custom_integrations, -): + hass: HomeAssistant, + enable_custom_integrations: None, +) -> None: """Test refresh_symbols service callback.""" - with patch(f"{YCC}.try_get_crumb_cookies", AsyncMock(return_value=TEST_CRUMB)), patch( + with patch( + f"{YCC}.try_get_crumb_cookies", AsyncMock(return_value=TEST_CRUMB) + ), patch( f"{YSUC}._async_update", AsyncMock(return_value=None) ) as mock_async_request_refresh: assert await async_setup_component(hass, DOMAIN, SAMPLE_CONFIG) is True @@ -281,7 +309,7 @@ async def test_refresh_symbols_service( ) def test_symbol_definition_comparison( sym1: SymbolDefinition, sym2: SymbolDefinition, expected: bool -): +) -> None: """Test SymbolDefinition instance comparison.""" if expected: @@ -303,6 +331,6 @@ def test_symbol_definition_comparison( ("164687040 0", None), ], ) -def test_convert_to_float(value, expected): +def test_convert_to_float(value, expected) -> None: """Tests float conversion.""" assert convert_to_float(value) == expected diff --git a/tests/test_sensor.py b/tests/test_sensor.py index ca4138e..e23560f 100755 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -18,6 +18,7 @@ CONF_INCLUDE_POST_VALUES, CONF_INCLUDE_PRE_VALUES, CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES, + CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, CONF_SHOW_TRENDING_ICON, CONF_SYMBOLS, DATA_CURRENCY_SYMBOL, @@ -33,6 +34,7 @@ DEFAULT_CONF_INCLUDE_POST_VALUES, DEFAULT_CONF_INCLUDE_PRE_VALUES, DEFAULT_CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES, + DEFAULT_CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, DEFAULT_CONF_SHOW_TRENDING_ICON, DEFAULT_CURRENCY, DEFAULT_CURRENCY_SYMBOL, @@ -46,7 +48,9 @@ async_setup_platform, ) from homeassistant.const import CONF_SCAN_INTERVAL -from tests import TEST_SYMBOL +from homeassistant.core import HomeAssistant + +from . import TEST_SYMBOL DEFAULT_OPTIONAL_CONFIG = { CONF_DECIMAL_PLACES: DEFAULT_CONF_DECIMAL_PLACES, @@ -56,6 +60,7 @@ CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES: DEFAULT_CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES, CONF_SCAN_INTERVAL: DEFAULT_SCAN_INTERVAL, CONF_SHOW_TRENDING_ICON: DEFAULT_CONF_SHOW_TRENDING_ICON, + CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT: DEFAULT_CONF_SHOW_CURRENCY_SYMBOL_AS_UNIT, "numeric_values_to_include": ["default"], } @@ -76,7 +81,9 @@ def build_mock_symbol_data( return YahooSymbolUpdateCoordinator.parse_symbol_data(source_data) -def build_mock_coordinator(hass, last_update_success, symbol, market_price): +def build_mock_coordinator( + hass: HomeAssistant, last_update_success, symbol, market_price +): """Build a mock data coordinator.""" return Mock( data={symbol: build_mock_symbol_data(symbol, market_price)}, @@ -86,7 +93,12 @@ def build_mock_coordinator(hass, last_update_success, symbol, market_price): def build_mock_coordinator_for_conversion( - hass, symbol, market_price, currency, target_currency, target_market_price + hass: HomeAssistant, + symbol, + market_price, + currency, + target_currency, + target_market_price, ): """Build a mock data coordinator with conversion data.""" @@ -103,12 +115,12 @@ def build_mock_coordinator_for_conversion( ) -def install_coordinator(hass, coordinator) -> None: +def install_coordinator(hass: HomeAssistant, coordinator) -> None: """Install the coordinator into HASS_DATA_COORDINATORS store.""" hass.data[DOMAIN] = {HASS_DATA_COORDINATORS: {DEFAULT_SCAN_INTERVAL: coordinator}} -async def test_setup_platform(hass) -> None: +async def test_setup_platform(hass: HomeAssistant) -> None: """Test platform setup.""" async_add_entities = MagicMock() @@ -136,7 +148,11 @@ async def test_setup_platform(hass) -> None: [(True, "XYZ", 12, 12), (False, "^ABC", 0.1221, 0.12), (True, "BOB", 6.156, 6.16)], ) def test_sensor_creation( - hass, last_update_success, symbol, market_price, expected_market_price + hass: HomeAssistant, + last_update_success, + symbol, + market_price, + expected_market_price, ) -> None: """Test sensor status based on the expected_market_price.""" @@ -166,12 +182,12 @@ def test_sensor_creation( for value in data_group: key = value[0] if ( - (key != DATA_REGULAR_MARKET_PRICE) + (key != DATA_REGULAR_MARKET_PRICE) # noqa: PLR1714 and (key != DATA_DIVIDEND_DATE) and (key != DATA_REGULAR_MARKET_TIME) and (key != DATA_PRE_MARKET_TIME) and (key != DATA_POST_MARKET_TIME) - ): # noqa: PLR1714 + ): assert attributes[key] == 0 # Since we did not provide any data so currency should be the default value @@ -191,7 +207,7 @@ def test_sensor_creation( ], ) def test_sensor_decimal_placs( - hass, market_price, decimal_places, expected_market_price + hass: HomeAssistant, market_price, decimal_places, expected_market_price ) -> None: """Tests numeric value rounding.""" @@ -217,7 +233,7 @@ def test_sensor_decimal_placs( ("last_update_success", "symbol", "market_price"), [(True, "XYZ", 12)] ) def test_sensor_data_when_coordinator_is_missing_symbol_data( - hass, last_update_success, symbol, market_price + hass: HomeAssistant, last_update_success, symbol, market_price ) -> None: """Test sensor status when data coordinator does not have data for that symbol.""" @@ -243,7 +259,7 @@ def test_sensor_data_when_coordinator_is_missing_symbol_data( assert sensor.name == symbol_to_test -def test_sensor_data_when_coordinator_returns_none(hass) -> None: +def test_sensor_data_when_coordinator_returns_none(hass: HomeAssistant) -> None: """Test sensor status when data coordinator does not have any data.""" symbol = "XYZ" @@ -266,7 +282,7 @@ def test_sensor_data_when_coordinator_returns_none(hass) -> None: assert sensor.name == symbol -async def test_sensor_update_calls_coordinator(hass) -> None: +async def test_sensor_update_calls_coordinator(hass: HomeAssistant) -> None: """Test sensor data update.""" symbol = "XYZ" @@ -292,7 +308,7 @@ async def test_sensor_update_calls_coordinator(hass) -> None: ], ) def test_sensor_trend( - hass, market_price, previous_close, show_trending, expected_trend + hass: HomeAssistant, market_price, previous_close, show_trending, expected_trend ) -> None: """Test sensor trending status.""" @@ -323,7 +339,7 @@ def test_sensor_trend( def test_sensor_trending_state_is_not_populate_if_previous_closing_missing( - hass, + hass: HomeAssistant, ) -> None: """The trending state is None if _previous_close is None for some reason.""" @@ -353,7 +369,9 @@ def test_sensor_trending_state_is_not_populate_if_previous_closing_missing( assert sensor.icon == f"mdi:currency-{lower_currency}" -async def test_data_from_json(hass, mock_json, mocked_crumb_coordinator) -> None: +async def test_data_from_json( + hass: HomeAssistant, mock_json, mocked_crumb_coordinator +) -> None: """Tests data update all the way from from json.""" symbol = TEST_SYMBOL coordinator = YahooSymbolUpdateCoordinator( @@ -388,7 +406,7 @@ def test_safe_convert(value, conversion, expected) -> None: assert YahooFinanceSensor.safe_convert(value, conversion) == expected -def test_conversion(hass) -> None: +def test_conversion(hass: HomeAssistant) -> None: """Numeric values get multiplied based on conversion currency.""" symbol = "XYZ" @@ -413,7 +431,9 @@ def test_conversion(hass) -> None: assert sensor.state == (12 * 1.5) -def test_conversion_requests_additional_data_from_coordinator(hass) -> None: +def test_conversion_requests_additional_data_from_coordinator( + hass: HomeAssistant, +) -> None: """Numeric values get multiplied based on conversion currency.""" symbol = "XYZ" @@ -439,7 +459,7 @@ def test_conversion_requests_additional_data_from_coordinator(hass) -> None: assert mock_add_symbol.call_count == 1 -def test_conversion_not_attempted_if_target_currency_same(hass) -> None: +def test_conversion_not_attempted_if_target_currency_same(hass: HomeAssistant) -> None: """No conversion is attempted if target curency is the same as symbol currency.""" symbol = "XYZ"