From 4aab073bd207e02ecbd89f5a1c3c4e9a724ae3c5 Mon Sep 17 00:00:00 2001 From: Collin Fair Date: Mon, 15 Apr 2024 15:42:33 -0500 Subject: [PATCH] Remove cloud dependency from `islamic-prayer-times` (#115146) Co-authored-by: J. Nick Koston --- CODEOWNERS | 4 +- .../islamic_prayer_times/config_flow.py | 42 ++-------- .../islamic_prayer_times/coordinator.py | 15 +--- .../islamic_prayer_times/manifest.json | 6 +- homeassistant/generated/integrations.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../islamic_prayer_times/__init__.py | 10 --- .../islamic_prayer_times/test_config_flow.py | 46 +---------- .../islamic_prayer_times/test_init.py | 81 ++----------------- .../islamic_prayer_times/test_sensor.py | 2 +- 11 files changed, 33 insertions(+), 179 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 919777391ab..39fa804314d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -683,8 +683,8 @@ build.json @home-assistant/supervisor /homeassistant/components/iqvia/ @bachya /tests/components/iqvia/ @bachya /homeassistant/components/irish_rail_transport/ @ttroy50 -/homeassistant/components/islamic_prayer_times/ @engrbm87 -/tests/components/islamic_prayer_times/ @engrbm87 +/homeassistant/components/islamic_prayer_times/ @engrbm87 @cpfair +/tests/components/islamic_prayer_times/ @engrbm87 @cpfair /homeassistant/components/iss/ @DurgNomis-drol /tests/components/iss/ @DurgNomis-drol /homeassistant/components/isy994/ @bdraco @shbatm diff --git a/homeassistant/components/islamic_prayer_times/config_flow.py b/homeassistant/components/islamic_prayer_times/config_flow.py index 12730c9be08..2db89183499 100644 --- a/homeassistant/components/islamic_prayer_times/config_flow.py +++ b/homeassistant/components/islamic_prayer_times/config_flow.py @@ -4,8 +4,6 @@ from __future__ import annotations from typing import Any -from prayer_times_calculator import InvalidResponseError, PrayerTimesCalculator -from requests.exceptions import ConnectionError as ConnError import voluptuous as vol from homeassistant.config_entries import ( @@ -15,7 +13,7 @@ from homeassistant.config_entries import ( OptionsFlow, ) from homeassistant.const import CONF_LATITUDE, CONF_LOCATION, CONF_LONGITUDE, CONF_NAME -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import callback from homeassistant.helpers.selector import ( LocationSelector, SelectSelector, @@ -23,7 +21,6 @@ from homeassistant.helpers.selector import ( SelectSelectorMode, TextSelector, ) -import homeassistant.util.dt as dt_util from .const import ( CALC_METHODS, @@ -43,26 +40,6 @@ from .const import ( ) -async def async_validate_location( - hass: HomeAssistant, lat: float, lon: float -) -> dict[str, str]: - """Check if the selected location is valid.""" - errors = {} - calc = PrayerTimesCalculator( - latitude=lat, - longitude=lon, - calculation_method=DEFAULT_CALC_METHOD, - date=str(dt_util.now().date()), - ) - try: - await hass.async_add_executor_job(calc.fetch_prayer_times) - except InvalidResponseError: - errors["base"] = "invalid_location" - except ConnError: - errors["base"] = "conn_error" - return errors - - class IslamicPrayerFlowHandler(ConfigFlow, domain=DOMAIN): """Handle the Islamic Prayer config flow.""" @@ -81,7 +58,6 @@ class IslamicPrayerFlowHandler(ConfigFlow, domain=DOMAIN): self, user_input: dict[str, Any] | None = None ) -> ConfigFlowResult: """Handle a flow initialized by the user.""" - errors = {} if user_input is not None: lat: float = user_input[CONF_LOCATION][CONF_LATITUDE] @@ -89,14 +65,13 @@ class IslamicPrayerFlowHandler(ConfigFlow, domain=DOMAIN): await self.async_set_unique_id(f"{lat}-{lon}") self._abort_if_unique_id_configured() - if not (errors := await async_validate_location(self.hass, lat, lon)): - return self.async_create_entry( - title=user_input[CONF_NAME], - data={ - CONF_LATITUDE: lat, - CONF_LONGITUDE: lon, - }, - ) + return self.async_create_entry( + title=user_input[CONF_NAME], + data={ + CONF_LATITUDE: lat, + CONF_LONGITUDE: lon, + }, + ) home_location = { CONF_LATITUDE: self.hass.config.latitude, @@ -112,7 +87,6 @@ class IslamicPrayerFlowHandler(ConfigFlow, domain=DOMAIN): ): LocationSelector(), } ), - errors=errors, ) diff --git a/homeassistant/components/islamic_prayer_times/coordinator.py b/homeassistant/components/islamic_prayer_times/coordinator.py index d70d0e2f4fe..2785f69534c 100644 --- a/homeassistant/components/islamic_prayer_times/coordinator.py +++ b/homeassistant/components/islamic_prayer_times/coordinator.py @@ -6,14 +6,13 @@ from datetime import datetime, timedelta import logging from typing import Any, cast -from prayer_times_calculator import PrayerTimesCalculator, exceptions -from requests.exceptions import ConnectionError as ConnError +from prayer_times_calculator_offline import PrayerTimesCalculator from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback -from homeassistant.helpers.event import async_call_later, async_track_point_in_time -from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed +from homeassistant.helpers.event import async_track_point_in_time +from homeassistant.helpers.update_coordinator import DataUpdateCoordinator import homeassistant.util.dt as dt_util from .const import ( @@ -142,13 +141,7 @@ class IslamicPrayerDataUpdateCoordinator(DataUpdateCoordinator[dict[str, datetim async def _async_update_data(self) -> dict[str, datetime]: """Update sensors with new prayer times.""" - try: - prayer_times = await self.hass.async_add_executor_job( - self.get_new_prayer_times - ) - except (exceptions.InvalidResponseError, ConnError) as err: - async_call_later(self.hass, 60, self.async_request_update) - raise UpdateFailed from err + prayer_times = self.get_new_prayer_times() # introduced in prayer-times-calculator 0.0.8 prayer_times.pop("date", None) diff --git a/homeassistant/components/islamic_prayer_times/manifest.json b/homeassistant/components/islamic_prayer_times/manifest.json index 5f7e52dd3db..cae3d31feb2 100644 --- a/homeassistant/components/islamic_prayer_times/manifest.json +++ b/homeassistant/components/islamic_prayer_times/manifest.json @@ -1,10 +1,10 @@ { "domain": "islamic_prayer_times", "name": "Islamic Prayer Times", - "codeowners": ["@engrbm87"], + "codeowners": ["@engrbm87", "@cpfair"], "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/islamic_prayer_times", - "iot_class": "cloud_polling", + "iot_class": "calculated", "loggers": ["prayer_times_calculator"], - "requirements": ["prayer-times-calculator==0.0.12"] + "requirements": ["prayer-times-calculator-offline==1.0.3"] } diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 20fbc883207..340be50978d 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -2879,7 +2879,7 @@ "islamic_prayer_times": { "integration_type": "hub", "config_flow": true, - "iot_class": "cloud_polling" + "iot_class": "calculated" }, "ismartwindow": { "name": "iSmartWindow", diff --git a/requirements_all.txt b/requirements_all.txt index 8090db881bc..82d1e50a479 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1556,7 +1556,7 @@ poolsense==0.0.8 praw==7.5.0 # homeassistant.components.islamic_prayer_times -prayer-times-calculator==0.0.12 +prayer-times-calculator-offline==1.0.3 # homeassistant.components.proliphix proliphix==0.4.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2782ae4e9bd..a85c110477b 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -1227,7 +1227,7 @@ poolsense==0.0.8 praw==7.5.0 # homeassistant.components.islamic_prayer_times -prayer-times-calculator==0.0.12 +prayer-times-calculator-offline==1.0.3 # homeassistant.components.prometheus prometheus-client==0.17.1 diff --git a/tests/components/islamic_prayer_times/__init__.py b/tests/components/islamic_prayer_times/__init__.py index 4df733a93fc..1e6d6815921 100644 --- a/tests/components/islamic_prayer_times/__init__.py +++ b/tests/components/islamic_prayer_times/__init__.py @@ -22,14 +22,4 @@ PRAYER_TIMES = { "Midnight": "2020-01-01T00:45:00+00:00", } -NEW_PRAYER_TIMES = { - "Fajr": "2020-01-02T06:00:00+00:00", - "Sunrise": "2020-01-02T07:25:00+00:00", - "Dhuhr": "2020-01-02T12:30:00+00:00", - "Asr": "2020-01-02T15:32:00+00:00", - "Maghrib": "2020-01-02T17:45:00+00:00", - "Isha": "2020-01-02T18:53:00+00:00", - "Midnight": "2020-01-02T00:43:00+00:00", -} - NOW = datetime(2020, 1, 1, 00, 00, 0, tzinfo=dt_util.UTC) diff --git a/tests/components/islamic_prayer_times/test_config_flow.py b/tests/components/islamic_prayer_times/test_config_flow.py index be8eca210d3..cb37a6b147d 100644 --- a/tests/components/islamic_prayer_times/test_config_flow.py +++ b/tests/components/islamic_prayer_times/test_config_flow.py @@ -1,10 +1,6 @@ """Tests for Islamic Prayer Times config flow.""" -from unittest.mock import patch - -from prayer_times_calculator import InvalidResponseError import pytest -from requests.exceptions import ConnectionError as ConnError from homeassistant import config_entries from homeassistant.components import islamic_prayer_times @@ -33,49 +29,15 @@ async def test_flow_works(hass: HomeAssistant) -> None: assert result["type"] is FlowResultType.FORM assert result["step_id"] == "user" - with patch( - "homeassistant.components.islamic_prayer_times.config_flow.async_validate_location", - return_value={}, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=MOCK_USER_INPUT - ) - await hass.async_block_till_done() + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input=MOCK_USER_INPUT + ) + await hass.async_block_till_done() assert result["type"] is FlowResultType.CREATE_ENTRY assert result["title"] == "Home" -@pytest.mark.parametrize( - ("exception", "error"), - [ - (InvalidResponseError, "invalid_location"), - (ConnError, "conn_error"), - ], -) -async def test_flow_error( - hass: HomeAssistant, exception: Exception, error: str -) -> None: - """Test flow errors.""" - result = await hass.config_entries.flow.async_init( - islamic_prayer_times.DOMAIN, context={"source": config_entries.SOURCE_USER} - ) - assert result["type"] is FlowResultType.FORM - assert result["step_id"] == "user" - - with patch( - "homeassistant.components.islamic_prayer_times.config_flow.PrayerTimesCalculator.fetch_prayer_times", - side_effect=exception, - ): - result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=MOCK_USER_INPUT - ) - await hass.async_block_till_done() - - assert result["type"] is FlowResultType.FORM - assert result["errors"]["base"] == error - - async def test_options(hass: HomeAssistant) -> None: """Test updating options.""" entry = MockConfigEntry( diff --git a/tests/components/islamic_prayer_times/test_init.py b/tests/components/islamic_prayer_times/test_init.py index aa865ee05a4..c5d4933e24a 100644 --- a/tests/components/islamic_prayer_times/test_init.py +++ b/tests/components/islamic_prayer_times/test_init.py @@ -1,24 +1,21 @@ """Tests for Islamic Prayer Times init.""" -from datetime import timedelta from unittest.mock import patch from freezegun import freeze_time -from prayer_times_calculator.exceptions import InvalidResponseError import pytest from homeassistant.components import islamic_prayer_times from homeassistant.components.islamic_prayer_times.const import CONF_CALC_METHOD from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN from homeassistant.config_entries import ConfigEntryState -from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, STATE_UNAVAILABLE +from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er -import homeassistant.util.dt as dt_util -from . import NEW_PRAYER_TIMES, NOW, PRAYER_TIMES +from . import NOW, PRAYER_TIMES -from tests.common import MockConfigEntry, async_fire_time_changed +from tests.common import MockConfigEntry @pytest.fixture(autouse=True) @@ -37,7 +34,7 @@ async def test_successful_config_entry(hass: HomeAssistant) -> None: entry.add_to_hass(hass) with patch( - "prayer_times_calculator.PrayerTimesCalculator.fetch_prayer_times", + "prayer_times_calculator_offline.PrayerTimesCalculator.fetch_prayer_times", return_value=PRAYER_TIMES, ): await hass.config_entries.async_setup(entry.entry_id) @@ -46,25 +43,6 @@ async def test_successful_config_entry(hass: HomeAssistant) -> None: assert entry.state is ConfigEntryState.LOADED -async def test_setup_failed(hass: HomeAssistant) -> None: - """Test Islamic Prayer Times failed due to an error.""" - - entry = MockConfigEntry( - domain=islamic_prayer_times.DOMAIN, - data={}, - ) - entry.add_to_hass(hass) - - # test request error raising ConfigEntryNotReady - with patch( - "prayer_times_calculator.PrayerTimesCalculator.fetch_prayer_times", - side_effect=InvalidResponseError(), - ): - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - assert entry.state is ConfigEntryState.SETUP_RETRY - - async def test_unload_entry(hass: HomeAssistant) -> None: """Test removing Islamic Prayer Times.""" entry = MockConfigEntry( @@ -74,7 +52,7 @@ async def test_unload_entry(hass: HomeAssistant) -> None: entry.add_to_hass(hass) with patch( - "prayer_times_calculator.PrayerTimesCalculator.fetch_prayer_times", + "prayer_times_calculator_offline.PrayerTimesCalculator.fetch_prayer_times", return_value=PRAYER_TIMES, ): await hass.config_entries.async_setup(entry.entry_id) @@ -91,7 +69,7 @@ async def test_options_listener(hass: HomeAssistant) -> None: with ( patch( - "prayer_times_calculator.PrayerTimesCalculator.fetch_prayer_times", + "prayer_times_calculator_offline.PrayerTimesCalculator.fetch_prayer_times", return_value=PRAYER_TIMES, ) as mock_fetch_prayer_times, freeze_time(NOW), @@ -107,49 +85,6 @@ async def test_options_listener(hass: HomeAssistant) -> None: assert mock_fetch_prayer_times.call_count == 2 -async def test_update_failed(hass: HomeAssistant) -> None: - """Test integrations tries to update after 1 min if update fails.""" - entry = MockConfigEntry(domain=islamic_prayer_times.DOMAIN, data={}) - entry.add_to_hass(hass) - - with ( - patch( - "prayer_times_calculator.PrayerTimesCalculator.fetch_prayer_times", - return_value=PRAYER_TIMES, - ), - freeze_time(NOW), - ): - await hass.config_entries.async_setup(entry.entry_id) - await hass.async_block_till_done() - - assert entry.state is ConfigEntryState.LOADED - - with patch( - "prayer_times_calculator.PrayerTimesCalculator.fetch_prayer_times" - ) as FetchPrayerTimes: - FetchPrayerTimes.side_effect = [ - InvalidResponseError, - NEW_PRAYER_TIMES, - ] - midnight_time = dt_util.parse_datetime(PRAYER_TIMES["Midnight"]) - assert midnight_time - future = midnight_time + timedelta(days=1, minutes=1) - with freeze_time(future): - async_fire_time_changed(hass, future) - await hass.async_block_till_done() - - state = hass.states.get("sensor.islamic_prayer_times_fajr_prayer") - assert state.state == STATE_UNAVAILABLE - - # coordinator tries to update after 1 minute - future = future + timedelta(minutes=1) - with freeze_time(future): - async_fire_time_changed(hass, future) - await hass.async_block_till_done() - state = hass.states.get("sensor.islamic_prayer_times_fajr_prayer") - assert state.state == "2020-01-02T06:00:00+00:00" - - @pytest.mark.parametrize( ("object_id", "old_unique_id"), [ @@ -184,7 +119,7 @@ async def test_migrate_unique_id( with ( patch( - "prayer_times_calculator.PrayerTimesCalculator.fetch_prayer_times", + "prayer_times_calculator_offline.PrayerTimesCalculator.fetch_prayer_times", return_value=PRAYER_TIMES, ), freeze_time(NOW), @@ -207,7 +142,7 @@ async def test_migration_from_1_1_to_1_2(hass: HomeAssistant) -> None: with ( patch( - "prayer_times_calculator.PrayerTimesCalculator.fetch_prayer_times", + "prayer_times_calculator_offline.PrayerTimesCalculator.fetch_prayer_times", return_value=PRAYER_TIMES, ), freeze_time(NOW), diff --git a/tests/components/islamic_prayer_times/test_sensor.py b/tests/components/islamic_prayer_times/test_sensor.py index 22629819e05..1f8d28dfb6f 100644 --- a/tests/components/islamic_prayer_times/test_sensor.py +++ b/tests/components/islamic_prayer_times/test_sensor.py @@ -40,7 +40,7 @@ async def test_islamic_prayer_times_sensors( with ( patch( - "prayer_times_calculator.PrayerTimesCalculator.fetch_prayer_times", + "prayer_times_calculator_offline.PrayerTimesCalculator.fetch_prayer_times", return_value=PRAYER_TIMES, ), freeze_time(NOW),