Skip to content

Commit 5235729

Browse files
authored
Merge pull request #73 from mawaqit/core-integration
V3.0.0 - Core Integration Version - available
2 parents 7aa3188 + cf87d01 commit 5235729

File tree

15 files changed

+1948
-992
lines changed

15 files changed

+1948
-992
lines changed

custom_components/mawaqit/__init__.py

Lines changed: 137 additions & 87 deletions
Large diffs are not rendered by default.

custom_components/mawaqit/config_flow.py

Lines changed: 234 additions & 168 deletions
Large diffs are not rendered by default.

custom_components/mawaqit/const.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Constants for the Islamic Prayer component."""
2+
23
DOMAIN = "mawaqit"
34
NAME = "Mawaqit Prayer Times"
45
PRAYER_TIMES_ICON = "mdi:calendar-clock"
@@ -14,10 +15,10 @@
1415
"Jumua": "Adhan",
1516
"Jumua 2": "Adhan",
1617
"next_mawaqit": "time",
17-
"Fajr Iqama": "",
18-
"Dhuhr Iqama": "",
19-
"Asr Iqama": "",
20-
"Maghrib Iqama": "",
18+
"Fajr Iqama": "",
19+
"Dhuhr Iqama": "",
20+
"Asr Iqama": "",
21+
"Maghrib Iqama": "",
2122
"Isha Iqama": "",
2223
"Next Salat Time": "",
2324
"Next Salat Name": "",
@@ -27,7 +28,7 @@
2728
"Mosque_url": "",
2829
"Mosque_image": "",
2930
}
30-
31+
3132
CONF_CALC_METHOD = "calculation_method"
3233

3334
CALC_METHODS = ["nearest", "farthest"]
@@ -44,4 +45,33 @@
4445
PASSWORD = "password"
4546

4647
API = "api"
47-
CONF_UUID = "uuid"
48+
CONF_UUID: str = "uuid"
49+
50+
CONF_SEARCH: str = "Keyword"
51+
52+
CONF_TYPE_SEARCH_TRANSLATION_KEY: str = "search_method_choice_translation_key"
53+
CONF_TYPE_SEARCH: str = "search_method_choice"
54+
CONF_TYPE_SEARCH_COORDINATES: str = "search_method_coordinates"
55+
CONF_TYPE_SEARCH_KEYWORD: str = "search_method_keyword"
56+
57+
CONF_CHOICE: str = "choice"
58+
CONF_CHOICE_TRANSLATION_KEY: str = "choice_translation_key"
59+
CONF_KEEP: str = "keep"
60+
CONF_RESET: str = "reset"
61+
62+
63+
MAWAQIT_STORAGE_VERSION = 1
64+
MAWAQIT_STORAGE_KEY = "mawaqit_storage"
65+
MAWAQIT_TEST_STORAGE_KEY = "mawaqit_test_storage"
66+
67+
MAWAQIT_API_KEY_TOKEN = "MAWAQIT_API_KEY"
68+
MAWAQIT_ALL_MOSQUES_NN = "all_mosques_NN"
69+
MAWAQIT_MY_MOSQUE_NN = "my_mosque_NN"
70+
MAWAQIT_PRAY_TIME = "pray_time"
71+
MAWAQIT_MOSQ_LIST_DATA = "mosq_list_data"
72+
73+
# Error messages
74+
75+
NO_MOSQUE_FOUND_KEYWORD = "no_mosque_found_keyword"
76+
CANNOT_CONNECT_TO_SERVER = "cannot_connect_to_server"
77+
WRONG_CREDENTIAL = "wrong_credential"

custom_components/mawaqit/manifest.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"iot_class": "cloud_polling",
1313
"issue_tracker": "https://github.com/mawaqit/home-assistant/issues",
1414
"requirements": [
15-
"mawaqit==0.0.1"
15+
"mawaqit==1.0.0"
1616
],
17-
"version": "2.1.0"
18-
}
17+
"version": "3.0.0"
18+
}

custom_components/mawaqit/mawaqit_wrapper.py

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
1+
"""Provides a wrapper for interacting with the MAWAQIT API.
2+
3+
It includes functions for testing credentials, retrieving API tokens,
4+
fetching prayer times, and finding mosques in the neighborhood.
5+
"""
6+
17
import logging
2-
import os
8+
39
from mawaqit import AsyncMawaqitClient
410
from mawaqit.consts import BadCredentialsException
511

612
_LOGGER = logging.getLogger(__name__)
713

814

9-
async def _test_credentials(username, password):
15+
async def test_credentials(username, password):
1016
"""Return True if the MAWAQIT credentials is valid."""
1117
try:
1218
client = AsyncMawaqitClient(username=username, password=password)
1319
await client.login()
14-
return True
1520
except BadCredentialsException:
1621
_LOGGER.error("Error : Bad Credentials")
1722
return False
18-
except Exception as e:
19-
_LOGGER.error("Error %s", e)
23+
except (ConnectionError, TimeoutError) as e:
24+
_LOGGER.error("Network-related error: %s", e)
2025
finally:
2126
await client.close()
2227

28+
return True
29+
2330

2431
async def get_mawaqit_api_token(username, password):
2532
"""Return the MAWAQIT API token."""
@@ -28,8 +35,8 @@ async def get_mawaqit_api_token(username, password):
2835
token = await client.get_api_token()
2936
except BadCredentialsException as e:
3037
_LOGGER.error("Error on retrieving API Token: %s", e)
31-
except Exception as e:
32-
_LOGGER.error("Error %s", e)
38+
except (ConnectionError, TimeoutError) as e:
39+
_LOGGER.error("Network-related error: %s", e)
3340
finally:
3441
await client.close()
3542
return token
@@ -47,14 +54,39 @@ async def all_mosques_neighborhood(
4754
nearest_mosques = await client.all_mosques_neighborhood()
4855
except BadCredentialsException as e:
4956
_LOGGER.error("Error on retrieving mosques: %s", e)
50-
except Exception as e:
51-
_LOGGER.error("Error %s", e)
57+
except (ConnectionError, TimeoutError) as e:
58+
_LOGGER.error("Network-related error: %s", e)
5259
finally:
5360
await client.close()
5461

5562
return nearest_mosques
5663

5764

65+
async def all_mosques_by_keyword(
66+
search_keyword, username=None, password=None, token=None
67+
):
68+
"""Return mosques in the neighborhood if any. Returns a list of dicts."""
69+
try:
70+
client = AsyncMawaqitClient(
71+
username=username, password=password, token=token, session=None
72+
)
73+
await client.get_api_token()
74+
75+
search_mosques = []
76+
77+
if search_keyword is not None:
78+
search_mosques = await client.fetch_mosques_by_keyword(search_keyword)
79+
80+
except BadCredentialsException as e:
81+
_LOGGER.error("Error on retrieving mosques: %s", e)
82+
except (ConnectionError, TimeoutError) as e:
83+
_LOGGER.error("Network-related error: %s", e)
84+
finally:
85+
await client.close()
86+
87+
return search_mosques
88+
89+
5890
async def fetch_prayer_times(
5991
latitude=None, longitude=None, mosque=None, username=None, password=None, token=None
6092
):
@@ -69,17 +101,9 @@ async def fetch_prayer_times(
69101

70102
except BadCredentialsException as e:
71103
_LOGGER.error("Error on retrieving prayer times: %s", e)
72-
except Exception as e:
73-
_LOGGER.error("Error %s", e)
104+
except (ConnectionError, TimeoutError) as e:
105+
_LOGGER.error("Network-related error: %s", e)
74106
finally:
75107
await client.close()
76108

77109
return dict_calendar
78-
79-
80-
def get_mawaqit_token_from_env():
81-
return os.environ.get("MAWAQIT_API_KEY", "NA")
82-
83-
84-
def set_mawaqit_token_from_env(mawaqit_token):
85-
os.environ["MAWAQIT_API_KEY"] = mawaqit_token

custom_components/mawaqit/sensor.py

Lines changed: 52 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,39 @@
11
"""Platform to retrieve Mawaqit prayer times information for Home Assistant."""
22

3-
from homeassistant.components.sensor import SensorEntity
4-
from homeassistant.components.sensor import SensorDeviceClass
3+
import logging
4+
from typing import Any
5+
6+
from homeassistant.components.sensor import SensorDeviceClass, SensorEntity
7+
from homeassistant.config_entries import ConfigEntry
8+
from homeassistant.core import HomeAssistant
59
from homeassistant.helpers.dispatcher import async_dispatcher_connect
10+
from homeassistant.helpers.entity_platform import AddEntitiesCallback
11+
from homeassistant.helpers.storage import Store
612
import homeassistant.util.dt as dt_util
713

8-
from .const import DATA_UPDATED, DOMAIN, PRAYER_TIMES_ICON, SENSOR_TYPES
9-
10-
import json
11-
import os
12-
import logging
13-
14+
from . import utils
15+
from .const import (
16+
DATA_UPDATED,
17+
DOMAIN,
18+
MAWAQIT_STORAGE_KEY,
19+
MAWAQIT_STORAGE_VERSION,
20+
PRAYER_TIMES_ICON,
21+
SENSOR_TYPES,
22+
)
1423

1524
_LOGGER = logging.getLogger(__name__)
1625

1726

18-
async def async_setup_entry(hass, config_entry, async_add_entities):
27+
async def async_setup_entry(
28+
hass: HomeAssistant,
29+
config_entry: ConfigEntry,
30+
async_add_entities: AddEntitiesCallback,
31+
) -> None:
1932
"""Set up the Mawaqit prayer times sensor platform."""
2033

2134
client = hass.data[DOMAIN]
2235
if not client:
23-
_LOGGER.error("Error retrieving client object.")
36+
_LOGGER.error("Error retrieving client object")
2437

2538
entities = []
2639
for sensor_type in SENSOR_TYPES:
@@ -32,7 +45,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
3245
"Maghrib",
3346
"Isha",
3447
"Jumua",
35-
"Jumua 2", # "Aid", "Aid 2",
48+
"Jumua 2", # "Aid" and "Aid 2",
3649
"Fajr Iqama",
3750
"Shurouq Iqama",
3851
"Dhuhr Iqama",
@@ -55,38 +68,29 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
5568
class MawaqitPrayerTimeSensor(SensorEntity):
5669
"""Representation of an Mawaqit prayer time sensor."""
5770

58-
def __init__(self, sensor_type, client):
71+
def __init__(self, sensor_type, client) -> None:
5972
"""Initialize the Mawaqit prayer time sensor."""
6073
self.sensor_type = sensor_type
6174
self.client = client
6275

6376
@property
64-
def name(self):
77+
def name(self) -> str:
6578
"""Return the name of the sensor."""
6679
return f"{self.sensor_type} {SENSOR_TYPES[self.sensor_type]}"
6780

6881
@property
69-
def unique_id(self):
82+
def unique_id(self) -> str:
7083
"""Return the unique id of the entity."""
7184
return self.sensor_type
7285

7386
@property
74-
def icon(self):
87+
def icon(self) -> str:
7588
"""Icon to display in the front end."""
7689
return PRAYER_TIMES_ICON
7790

78-
# @property
79-
# def state(self):
80-
# """Return the state of the sensor."""
81-
# return (
82-
# self.client.prayer_times_info.get(self.sensor_type)
83-
# .astimezone(dt_util.UTC)
84-
# .isoformat()
85-
# )
86-
8791
@property
8892
def native_value(self):
89-
"""Return the state of the sensor. .astimezone(dt_util.UTC)"""
93+
"""Return the state of the sensor. .astimezone(dt_util.UTC)."""
9094
if self.sensor_type in [
9195
"Fajr",
9296
"Shurouq",
@@ -106,21 +110,24 @@ def native_value(self):
106110
"Next Salat Preparation",
107111
]:
108112
time = self.client.prayer_times_info.get(self.sensor_type)
113+
_LOGGER.debug("[;] before %s Time: %s", self.sensor_type, time)
109114
if time is not None:
115+
_LOGGER.debug(
116+
"[;] %s Time: %s", self.sensor_type, time.astimezone(dt_util.UTC)
117+
)
110118
return time.astimezone(dt_util.UTC)
111-
else:
112-
return None
113119

114-
else:
115-
return self.client.prayer_times_info.get(self.sensor_type)
120+
return None
121+
122+
return self.client.prayer_times_info.get(self.sensor_type)
116123

117124
@property
118-
def should_poll(self):
125+
def should_poll(self) -> bool:
119126
"""Disable polling."""
120127
return False
121128

122129
@property
123-
def device_class(self):
130+
def device_class(self) -> SensorDeviceClass | None:
124131
"""Return the device class."""
125132
if self.sensor_type in [
126133
"Fajr",
@@ -141,10 +148,9 @@ def device_class(self):
141148
"Next Salat Preparation",
142149
]:
143150
return SensorDeviceClass.TIMESTAMP
144-
else:
145-
return None
151+
return None
146152

147-
async def async_added_to_hass(self):
153+
async def async_added_to_hass(self) -> None:
148154
"""Handle entity which will be added."""
149155
self.async_on_remove(
150156
async_dispatcher_connect(self.hass, DATA_UPDATED, self.async_write_ha_state)
@@ -154,49 +160,44 @@ async def async_added_to_hass(self):
154160
class MyMosqueSensor(SensorEntity):
155161
"""Representation of a mosque sensor."""
156162

157-
def __init__(self, name, hass):
163+
def __init__(self, name, hass: HomeAssistant) -> None:
158164
"""Initialize the mosque sensor."""
159165
self.hass = hass
160-
self._attributes = {}
166+
self._attributes: dict[str, Any] = {}
161167
self._name = name
162168
self._state = None
163169
latitude = self.hass.config.latitude
164170
longitude = self.hass.config.longitude
165171
self._latitude = latitude
166172
self._longitude = longitude
173+
self.store: Store = Store(
174+
self.hass, MAWAQIT_STORAGE_VERSION, MAWAQIT_STORAGE_KEY
175+
)
167176

168-
# @Throttle(TIME_BETWEEN_UPDATES)
169-
async def async_update(self):
177+
async def async_update(self) -> None:
170178
"""Get the latest data from the Mawaqit API."""
171-
current_dir = os.path.dirname(os.path.realpath(__file__))
172-
173-
def read():
174-
with open("{}/data/my_mosque_NN.txt".format(current_dir), "r") as f:
175-
data = json.load(f)
176-
return data
177-
178-
data = await self.hass.async_add_executor_job(read)
179+
data_my_mosque_NN = await utils.read_my_mosque_NN_file(self.store)
179180

180-
for k, v in data.items():
181+
for k, v in data_my_mosque_NN.items():
181182
if str(k) != "uuid" and str(k) != "id" and str(k) != "slug":
182183
self._attributes[k] = str(v)
183184

184185
@property
185-
def name(self):
186+
def name(self) -> str | None:
186187
"""Return the name of the sensor."""
187188
return self._name
188189

189190
@property
190-
def state(self):
191+
def native_value(self) -> str | None:
191192
"""Return the state of the sensor."""
192193
return self._attributes["name"]
193194

194195
@property
195-
def icon(self):
196+
def icon(self) -> str | None:
196197
"""Return the icon of the sensor."""
197198
return "mdi:mosque"
198199

199200
@property
200-
def extra_state_attributes(self):
201+
def extra_state_attributes(self) -> dict[str, Any] | None:
201202
"""Return attributes for the sensor."""
202203
return self._attributes

0 commit comments

Comments
 (0)