Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b41c566
Add Islamic Prayer Times config_flow
engrbm87 Nov 13, 2019
211b60a
Add Islamic Prayer Times config_flow
engrbm87 Nov 13, 2019
a98ee58
Merge branch 'islamic-prayer-config-flow' of https://github.com/engrb…
engrbm87 Feb 5, 2020
4fd2cea
handle options update and fix tests
engrbm87 Feb 7, 2020
87460d0
fix sensor update handling
engrbm87 Feb 7, 2020
8336df4
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
engrbm87 Feb 11, 2020
0e544e5
fix pylint
engrbm87 Feb 13, 2020
d412176
fix scheduled update and add test
engrbm87 Feb 15, 2020
8f9d01b
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
engrbm87 Mar 16, 2020
2a61a7d
update test_init
engrbm87 Mar 18, 2020
d5e6dfb
update flow options to show drop list
engrbm87 Mar 19, 2020
da7f34c
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
engrbm87 Apr 1, 2020
33e757e
Merge branch 'dev' into islamic-prayer-config-flow
engrbm87 Apr 10, 2020
25547a3
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
engrbm87 Apr 10, 2020
ee453f6
clean up code
engrbm87 Apr 13, 2020
3c75168
Merge branch 'islamic-prayer-config-flow' of https://github.com/engrb…
engrbm87 Apr 13, 2020
92ca24c
async scheduling and revert state to timestamp
engrbm87 Apr 13, 2020
d2b9782
fix update retry method
engrbm87 Apr 15, 2020
a1c57a9
Merge branch 'dev' of https://github.com/home-assistant/home-assistan…
engrbm87 Apr 20, 2020
d8d3df0
update strings
engrbm87 Apr 20, 2020
f3dc053
keep title as root key
engrbm87 Apr 20, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ homeassistant/components/ipma/* @dgomes @abmantis
homeassistant/components/ipp/* @ctalkington
homeassistant/components/iqvia/* @bachya
homeassistant/components/irish_rail_transport/* @ttroy50
homeassistant/components/islamic_prayer_times/* @engrbm87
homeassistant/components/izone/* @Swamp-Ig
homeassistant/components/jewish_calendar/* @tsvi
homeassistant/components/juicenet/* @jesserockz
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"title": "Islamic Prayer Times",
"config": {
"step": {
"user": {
"title": "Set up Islamic Prayer Times",
"description": "Do you want to set up Islamic Prayer Times?"
}
},
"abort": {
"one_instance_allowed": "Only a single instance is necessary."
}
},
"options": {
"step": {
"init": {
"data": {
"calc_method": "Prayer calculation method"
}
}
}
}
}
205 changes: 205 additions & 0 deletions homeassistant/components/islamic_prayer_times/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,206 @@
"""The islamic_prayer_times component."""
from datetime import timedelta
import logging

from prayer_times_calculator import PrayerTimesCalculator, exceptions
from requests.exceptions import ConnectionError as ConnError
import voluptuous as vol

from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.event import async_call_later, async_track_point_in_time
import homeassistant.util.dt as dt_util

from .const import (
CALC_METHODS,
CONF_CALC_METHOD,
DATA_UPDATED,
DEFAULT_CALC_METHOD,
DOMAIN,
)

_LOGGER = logging.getLogger(__name__)


CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: {
vol.Optional(CONF_CALC_METHOD, default=DEFAULT_CALC_METHOD): vol.In(
CALC_METHODS
),
}
},
extra=vol.ALLOW_EXTRA,
)


async def async_setup(hass, config):
"""Import the Islamic Prayer component from config."""
if DOMAIN in config:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}, data=config[DOMAIN]
)
)

return True


async def async_setup_entry(hass, config_entry):
"""Set up the Islamic Prayer Component."""
client = IslamicPrayerClient(hass, config_entry)

if not await client.async_setup():
return False

hass.data.setdefault(DOMAIN, client)
return True


async def async_unload_entry(hass, config_entry):
"""Unload Islamic Prayer entry from config_entry."""
if hass.data[DOMAIN].event_unsub:
hass.data[DOMAIN].event_unsub()
hass.data.pop(DOMAIN)
await hass.config_entries.async_forward_entry_unload(config_entry, "sensor")

return True


class IslamicPrayerClient:
Comment thread
bdraco marked this conversation as resolved.
"""Islamic Prayer Client Object."""

def __init__(self, hass, config_entry):
"""Initialize the Islamic Prayer client."""
self.hass = hass
self.config_entry = config_entry
self.prayer_times_info = {}
self.available = True
self.event_unsub = None

@property
def calc_method(self):
"""Return the calculation method."""
return self.config_entry.options[CONF_CALC_METHOD]

def get_new_prayer_times(self):
"""Fetch prayer times for today."""
calc = PrayerTimesCalculator(
latitude=self.hass.config.latitude,
longitude=self.hass.config.longitude,
calculation_method=self.calc_method,
date=str(dt_util.now().date()),
)
return calc.fetch_prayer_times()

async def async_schedule_future_update(self):
"""Schedule future update for sensors.

Midnight is a calculated time. The specifics of the calculation
depends on the method of the prayer time calculation. This calculated
midnight is the time at which the time to pray the Isha prayers have
expired.

Calculated Midnight: The Islamic midnight.
Traditional Midnight: 12:00AM

Update logic for prayer times:

If the Calculated Midnight is before the traditional midnight then wait
until the traditional midnight to run the update. This way the day
will have changed over and we don't need to do any fancy calculations.

If the Calculated Midnight is after the traditional midnight, then wait
until after the calculated Midnight. We don't want to update the prayer
times too early or else the timings might be incorrect.

Example:
calculated midnight = 11:23PM (before traditional midnight)
Update time: 12:00AM

calculated midnight = 1:35AM (after traditional midnight)
update time: 1:36AM.

"""
_LOGGER.debug("Scheduling next update for Islamic prayer times")

now = dt_util.as_local(dt_util.now())

midnight_dt = self.prayer_times_info["Midnight"]

if now > dt_util.as_local(midnight_dt):
next_update_at = midnight_dt + timedelta(days=1, minutes=1)
_LOGGER.debug(
"Midnight is after day the changes so schedule update for after Midnight the next day"
)
else:
_LOGGER.debug(
"Midnight is before the day changes so schedule update for the next start of day"
)
next_update_at = dt_util.start_of_local_day(now + timedelta(days=1))

_LOGGER.info("Next update scheduled for: %s", next_update_at)

self.event_unsub = async_track_point_in_time(
self.hass, self.async_update, next_update_at
)

async def async_update(self, *_):
"""Update sensors with new prayer times."""
try:
prayer_times = await self.hass.async_add_executor_job(
self.get_new_prayer_times
)
self.available = True
except (exceptions.InvalidResponseError, ConnError):
self.available = False
_LOGGER.debug("Error retrieving prayer times.")
async_call_later(self.hass, 60, self.async_update)
return

for prayer, time in prayer_times.items():
self.prayer_times_info[prayer] = dt_util.parse_datetime(
f"{dt_util.now().date()} {time}"
)
await self.async_schedule_future_update()

_LOGGER.debug("New prayer times retrieved. Updating sensors.")
async_dispatcher_send(self.hass, DATA_UPDATED)

async def async_setup(self):
"""Set up the Islamic prayer client."""
await self.async_add_options()

try:
await self.hass.async_add_executor_job(self.get_new_prayer_times)
except (exceptions.InvalidResponseError, ConnError):
raise ConfigEntryNotReady

await self.async_update()
self.config_entry.add_update_listener(self.async_options_updated)

self.hass.async_create_task(
self.hass.config_entries.async_forward_entry_setup(
self.config_entry, "sensor"
)
)

return True

async def async_add_options(self):
"""Add options for entry."""
if not self.config_entry.options:
data = dict(self.config_entry.data)
calc_method = data.pop(CONF_CALC_METHOD, DEFAULT_CALC_METHOD)

self.hass.config_entries.async_update_entry(
self.config_entry, data=data, options={CONF_CALC_METHOD: calc_method}
)

@staticmethod
async def async_options_updated(hass, entry):
"""Triggered by config entry options updates."""
if hass.data[DOMAIN].event_unsub:
hass.data[DOMAIN].event_unsub()
await hass.data[DOMAIN].async_update()
59 changes: 59 additions & 0 deletions homeassistant/components/islamic_prayer_times/config_flow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Config flow for Islamic Prayer Times integration."""
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.core import callback

# pylint: disable=unused-import
from .const import CALC_METHODS, CONF_CALC_METHOD, DEFAULT_CALC_METHOD, DOMAIN, NAME
Comment thread
springstan marked this conversation as resolved.


class IslamicPrayerFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle the Islamic Prayer config flow."""

VERSION = 1
CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL

@staticmethod
@callback
def async_get_options_flow(config_entry):
"""Get the options flow for this handler."""
return IslamicPrayerOptionsFlowHandler(config_entry)

async def async_step_user(self, user_input=None):
"""Handle a flow initialized by the user."""
if self._async_current_entries():
return self.async_abort(reason="one_instance_allowed")
Comment thread
engrbm87 marked this conversation as resolved.

if user_input is None:
return self.async_show_form(step_id="user")

return self.async_create_entry(title=NAME, data=user_input)

async def async_step_import(self, import_config):
"""Import from config."""
return await self.async_step_user(user_input=import_config)


class IslamicPrayerOptionsFlowHandler(config_entries.OptionsFlow):
"""Handle Islamic Prayer client options."""

def __init__(self, config_entry):
"""Initialize options flow."""
self.config_entry = config_entry

async def async_step_init(self, user_input=None):
"""Manage options."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)

options = {
vol.Optional(
CONF_CALC_METHOD,
default=self.config_entry.options.get(
CONF_CALC_METHOD, DEFAULT_CALC_METHOD
),
): vol.In(CALC_METHODS)
}

return self.async_show_form(step_id="init", data_schema=vol.Schema(options))
14 changes: 14 additions & 0 deletions homeassistant/components/islamic_prayer_times/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Constants for the Islamic Prayer component."""
DOMAIN = "islamic_prayer_times"
NAME = "Islamic Prayer Times"
SENSOR_SUFFIX = "Prayer"
PRAYER_TIMES_ICON = "mdi:calendar-clock"

SENSOR_TYPES = ["Fajr", "Sunrise", "Dhuhr", "Asr", "Maghrib", "Isha", "Midnight"]

CONF_CALC_METHOD = "calc_method"

CALC_METHODS = ["isna", "karachi", "mwl", "makkah"]
DEFAULT_CALC_METHOD = "isna"

DATA_UPDATED = "Islamic_prayer_data_updated"
3 changes: 2 additions & 1 deletion homeassistant/components/islamic_prayer_times/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"name": "Islamic Prayer Times",
"documentation": "https://www.home-assistant.io/integrations/islamic_prayer_times",
"requirements": ["prayer_times_calculator==0.0.3"],
"codeowners": []
"codeowners": ["@engrbm87"],
"config_flow": true
}
Loading