Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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 @@ -190,6 +190,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,24 @@
{
"config": {
"title": "Islamic Prayer Times",
"step": {
"user": {
"title": "Set up Islamic Prayer Times",
"description": "Are you sure you want to set up Islamic Prayer Times?"
}
},
"abort": {
"one_instance_allowed": "Only a single instance is necessary."
}
},
"options": {
"title": "Configure options for Islamic Prayer Times",
"step": {
"init": {
"data": {
"calculation_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 datetime, timedelta
import logging

from prayer_times_calculator import PrayerTimesCalculator, exceptions
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_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, {})
hass.data[DOMAIN] = client
return True


async def async_unload_entry(hass, config_entry):
"""Unload Transmission Entry from config_entry."""

await hass.config_entries.async_forward_entry_unload(config_entry, "sensor")

hass.data.pop(DOMAIN)

return True


class IslamicPrayerClient:
"""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 = None
self.available = None

async 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.config_entry.options[CONF_CALC_METHOD],
date=str(dt_util.now().date()),
)
self.prayer_times_info = calc.fetch_prayer_times()

async def 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")

midnight_time = self.prayer_times_info["Midnight"]
now = dt_util.as_local(dt_util.now())
today = now.date()

midnight_dt_str = "{}::{}".format(str(today), midnight_time)
midnight_dt = datetime.strptime(midnight_dt_str, "%Y-%m-%d::%H:%M")

if now > dt_util.as_local(midnight_dt):
_LOGGER.debug(
"Midnight is after day the changes so schedule update "
"for after Midnight the next day"
)

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

tomorrow = now + timedelta(days=1)
next_update_at = dt_util.start_of_local_day(tomorrow)

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

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:
await self.get_new_prayer_times()
await self.schedule_future_update()
self.available = True
_LOGGER.debug("New prayer times retrieved. Updating sensors.")

except exceptions.InvalidResponseError:
self.available = False

async_dispatcher_send(self.hass, DATA_UPDATED)

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

self.add_options()

try:
await self.get_new_prayer_times()
except exceptions.InvalidResponseError:
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

def add_options(self):
"""Add options for entry."""
if not self.config_entry.options:
calc_method = self.config_entry.data.get(
CONF_CALC_METHOD, DEFAULT_CALC_METHOD
)

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

@staticmethod
def get_prayer_time_as_dt(prayer_time):
"""Create a datetime object for the respective prayer time."""
today = dt_util.now().date()
date_time_str = "{} {}".format(str(today), prayer_time)
pt_dt = dt_util.parse_datetime(date_time_str)
return pt_dt

@staticmethod
async def async_options_updated(hass, entry):
"""Triggered by config entry options updates."""
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


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")

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 Transmission client 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"
PRAYER_TIMES_ICON = "mdi:calendar-clock"

SENSOR_TYPES = ["fajr", "sunrise", "dhuhr", "asr", "maghrib", "isha", "midnight"]

CONF_CALC_METHOD = "calculation_method"
CONF_SENSORS = "sensors"

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

DATA_UPDATED = "Islamic_prayer_data_updated"
7 changes: 5 additions & 2 deletions homeassistant/components/islamic_prayer_times/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
"documentation": "https://www.home-assistant.io/integrations/islamic_prayer_times",
"requirements": ["prayer_times_calculator==0.0.3"],
"dependencies": [],
"codeowners": []
}
"codeowners": [
"@engrbm87"
],
"config_flow": true
}
Loading