From a8b9b901cd556df8676e6eb0294b0797bff729ef Mon Sep 17 00:00:00 2001 From: Chris Abberley <26394346+cabberley@users.noreply.github.com> Date: Fri, 10 May 2024 01:32:42 +1000 Subject: [PATCH] Converted market times sensor to ISO Date Time format & added AUD Currency (#130) * Update const.py Added - 'aud' to CURRENCY_CODES Added New Constants - ATTR_REGULAR_MARKET_TIME - ATTR_POST_MARKET_TIME - DATA_REGULAR_MARKET_TIME - DATA_POST_MARKET_TIME * epoch to datetime conversion for market times Added Code to convert Regular & Post market times epoch values to ISO time formats * Add Extra Numeric Value tests Validate new datetime conversions * Update const.py Added new Constant * refactor Epoch timestamp conversions Refactored and renamed Epoch timestamp conversion * Update sensor.py - fixed return value Updated convert_timestamp_to_datetime to return date_timestamp value to support both None and 0 options. IF 0 was allowed to continue on to Epoch conversion then it would return datetime for Epoch 0 which is 1970 * Update test_sensor.py - rewrote test for timestamp conversion rewrote test to use the convert_timestamp_to_datetime which replaced the dividend_date function. Added additional tests to valid the functionality and confirm it correctly returns 0 or None based on the input value. --- custom_components/yahoofinance/const.py | 15 +++++++-- custom_components/yahoofinance/sensor.py | 42 +++++++++++++++++------- tests/test_sensor.py | 31 ++++++++++------- 3 files changed, 63 insertions(+), 25 deletions(-) diff --git a/custom_components/yahoofinance/const.py b/custom_components/yahoofinance/const.py index 7287939..72b174e 100755 --- a/custom_components/yahoofinance/const.py +++ b/custom_components/yahoofinance/const.py @@ -13,6 +13,9 @@ ATTR_TRENDING: Final = "trending" ATTR_MARKET_STATE: Final = "marketState" ATTR_DIVIDEND_DATE: Final = "dividendDate" +ATTR_REGULAR_MARKET_TIME: Final = "regularMarketTime" +ATTR_PRE_MARKET_TIME: Final = "preMarketTime" +ATTR_POST_MARKET_TIME: Final = "postMarketTime" # Hass data HASS_DATA_CONFIG: Final = "config" @@ -26,6 +29,11 @@ DATA_SHORT_NAME: Final = "shortName" DATA_MARKET_STATE: Final = "marketState" DATA_DIVIDEND_DATE: Final = "dividendDate" +DATA_REGULAR_MARKET_TIME: Final = "regularMarketTime" +DATA_PRE_MARKET_TIME: Final = 'preMarketTime' +DATA_POST_MARKET_TIME: Final = "postMarketTime" + + DATA_REGULAR_MARKET_PREVIOUS_CLOSE: Final = "regularMarketPreviousClose" DATA_REGULAR_MARKET_PRICE: Final = "regularMarketPrice" @@ -66,7 +74,7 @@ (DATA_REGULAR_MARKET_PREVIOUS_CLOSE, True), (DATA_REGULAR_MARKET_PRICE, True), ("regularMarketVolume", False), - ("regularMarketTime", False), + (DATA_REGULAR_MARKET_TIME, False), (DATA_DIVIDEND_DATE, False), ], CONF_INCLUDE_FIFTY_DAY_VALUES: [ @@ -77,14 +85,14 @@ CONF_INCLUDE_PRE_VALUES: [ ("preMarketChange", True), ("preMarketChangePercent", False), - ("preMarketTime", False), + (DATA_PRE_MARKET_TIME, False), ("preMarketPrice", True), ], CONF_INCLUDE_POST_VALUES: [ ("postMarketChange", True), ("postMarketChangePercent", False), ("postMarketPrice", True), - ("postMarketTime", False), + (DATA_POST_MARKET_TIME, False), ], CONF_INCLUDE_TWO_HUNDRED_DAY_VALUES: [ ("twoHundredDayAverage", True), @@ -160,6 +168,7 @@ """Duration for crumb re-try when receiving 429 code.""" CURRENCY_CODES: Final = { + "aud": "$", "bdt": "৳", "brl": "R$", "btc": "₿", diff --git a/custom_components/yahoofinance/sensor.py b/custom_components/yahoofinance/sensor.py index d31f4f8..79b70f8 100755 --- a/custom_components/yahoofinance/sensor.py +++ b/custom_components/yahoofinance/sensor.py @@ -5,7 +5,7 @@ from __future__ import annotations -from datetime import date, datetime, timedelta +from datetime import date, datetime, timedelta, time import logging from homeassistant.components.sensor import ( @@ -26,8 +26,11 @@ ATTR_CURRENCY_SYMBOL, ATTR_DIVIDEND_DATE, ATTR_MARKET_STATE, + ATTR_PRE_MARKET_TIME, + ATTR_POST_MARKET_TIME, ATTR_QUOTE_SOURCE_NAME, ATTR_QUOTE_TYPE, + ATTR_REGULAR_MARKET_TIME, ATTR_SYMBOL, ATTR_TRENDING, ATTRIBUTION, @@ -39,10 +42,13 @@ DATA_DIVIDEND_DATE, DATA_FINANCIAL_CURRENCY, DATA_MARKET_STATE, + DATA_PRE_MARKET_TIME, + DATA_POST_MARKET_TIME, DATA_QUOTE_SOURCE_NAME, DATA_QUOTE_TYPE, DATA_REGULAR_MARKET_PREVIOUS_CLOSE, DATA_REGULAR_MARKET_PRICE, + DATA_REGULAR_MARKET_TIME, DATA_SHORT_NAME, DEFAULT_CURRENCY, DEFAULT_NUMERIC_DATA_GROUP, @@ -166,18 +172,20 @@ def safe_convert(value: float | None, conversion: float | None) -> float | None: if conversion is None: return value return value * conversion - + @staticmethod - def parse_dividend_date(dividend_date_timestamp) -> str | None: - """Parse dividendDate JSON element.""" + def convert_timestamp_to_datetime(date_timestamp, return_format) -> str | None: + """Convert Epoch JSON element to datetime.""" - dividend_date_timestamp = convert_to_float(dividend_date_timestamp) - if dividend_date_timestamp is None: - return None + date_timestamp = convert_to_float(date_timestamp) + if date_timestamp is None or date_timestamp == 0: + return date_timestamp - dividend_date = datetime.fromtimestamp(dividend_date_timestamp,tz=dt_util.DEFAULT_TIME_ZONE) - dividend_date_date = dividend_date.date() - return dividend_date_date.isoformat() + converted_date = datetime.fromtimestamp(date_timestamp,tz=dt_util.DEFAULT_TIME_ZONE) + if return_format == "date": + converted_date = converted_date.date() + + return converted_date.isoformat() @property def unique_id(self) -> str: @@ -371,7 +379,19 @@ def update_properties(self) -> None: self._attr_extra_state_attributes[ ATTR_DIVIDEND_DATE - ] = self.parse_dividend_date(symbol_data.get(DATA_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_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') # Use target_currency if we have conversion data. Otherwise keep using the # currency from data. diff --git a/tests/test_sensor.py b/tests/test_sensor.py index 7948d53..9305cfa 100755 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -23,6 +23,9 @@ DATA_DIVIDEND_DATE, DATA_REGULAR_MARKET_PREVIOUS_CLOSE, DATA_REGULAR_MARKET_PRICE, + DATA_PRE_MARKET_TIME, + DATA_POST_MARKET_TIME, + DATA_REGULAR_MARKET_TIME, DATA_SHORT_NAME, DEFAULT_CONF_DECIMAL_PLACES, DEFAULT_CONF_INCLUDE_FIFTY_DAY_VALUES, @@ -165,9 +168,9 @@ def test_sensor_creation( for data_group in NUMERIC_DATA_GROUPS.values(): for value in data_group: key = value[0] - if (key != DATA_REGULAR_MARKET_PRICE) and (key != DATA_DIVIDEND_DATE): # noqa: PLR1714 + if (key != DATA_REGULAR_MARKET_PRICE) 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 assert sensor.unit_of_measurement == DEFAULT_CURRENCY assert attributes[ATTR_CURRENCY_SYMBOL] == DEFAULT_CURRENCY_SYMBOL @@ -455,15 +458,21 @@ def test_conversion_not_attempted_if_target_currency_same(hass): @pytest.mark.parametrize( - "dividend_date,expected_date", + "epoch_date,return_format,expected_datetime", [ - (None, None), - (1642118400, "2022-01-14"), - (1646870400, "2022-03-10"), - ("1646870400", "2022-03-10"), - ("164687040 0", None), + (None, "date", None), + (1642118400, "date","2022-01-14"), + (1646870400, "date","2022-03-10"), + ("1646870400", "date","2022-03-10"), + ("164687040 0", "date",None), + (1642118453, "datetime","2022-01-14T00:00:53+00:00"), + (1646878750, "datetime","2022-03-10T02:19:10+00:00"), + ("1646878750", "datetime","2022-03-10T02:19:10+00:00"), + ("164687040 0", "datetime",None), + (0, "date",0), + (0, "datetime",0), ], ) -def test_parse_dividend_date(dividend_date, expected_date): - """Test dividend date parsing.""" - assert YahooFinanceSensor.parse_dividend_date(dividend_date) == expected_date +def test_convert_timestamp_to_datetime(epoch_date,return_format,expected_datetime): + """Test converting Epoch times to datetime and return date or datetime based on return format.""" + assert YahooFinanceSensor.convert_timestamp_to_datetime(epoch_date,return_format) == expected_datetime