Add config flow to Jewish Calendar (#84464)
* Initial commit * add basic tests (will probably fail) * Set basic UID for now * Various improvements * use new naming convention? * bit by bit, still not working tho * Add tz selection * Remove failing tests * update unique_id * add the tests again * revert to previous binary_sensor test * remove translations * apply suggestions * remove const.py * Address review * revert changes * Initial fixes for tests * Initial commit * add basic tests (will probably fail) * Set basic UID for now * Various improvements * use new naming convention? * bit by bit, still not working tho * Add tz selection * Remove failing tests * update unique_id * add the tests again * revert to previous binary_sensor test * remove translations * apply suggestions * remove const.py * Address review * revert changes * Fix bad merges in rebase * Get tests to run again * Fixes due to fails in ruff/pylint * Fix binary sensor tests * Fix config flow tests * Fix sensor tests * Apply review * Adjust candle lights * Apply suggestion * revert unrelated change * Address some of the comments * We should only allow a single jewish calendar config entry * Make data schema easier to read * Add test and confirm only single entry is allowed * Move OPTIONS_SCHEMA to top of file * Add options test * Simplify import tests * Test import end2end * Use a single async_forward_entry_setups statement * Revert schema updates for YAML schema * Remove unneeded brackets * Remove CONF_NAME from config_flow * Assign hass.data[DOMAIN][config_entry.entry_id] to a local variable before creating the sensors * Data doesn't have a name remove slugifying of it * Test that the entry has been created correctly * Simplify setup_entry * Use suggested values helper and flatten location dictionary * Remove the string for name exists as this error doesn't exist * Remove name from config entry * Remove _attr_has_entity_name - will be added in a subsequent PR * Don't override entity id's - we'll fixup the naming later * Make location optional - will by default revert to the user's home location * Update homeassistant/components/jewish_calendar/strings.json Co-authored-by: Erik Montnemery <erik@montnemery.com> * No need for local lat/long variable * Return name attribute, will deal with it in another PR * Revert unique_id changes, will deal with this in a subsequent PR * Add time zone data description * Don't break the YAML config until the user has removed it. * Cleanup initial config flow test --------- Co-authored-by: Tsvi Mostovicz <ttmost@gmail.com> Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
parent
23597a8cdf
commit
2c09f72c33
13 changed files with 544 additions and 182 deletions
|
@ -5,41 +5,54 @@ from __future__ import annotations
|
||||||
from hdate import Location
|
from hdate import Location
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME, Platform
|
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.const import (
|
||||||
|
CONF_ELEVATION,
|
||||||
|
CONF_LANGUAGE,
|
||||||
|
CONF_LATITUDE,
|
||||||
|
CONF_LONGITUDE,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_TIME_ZONE,
|
||||||
|
Platform,
|
||||||
|
)
|
||||||
|
from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN, HomeAssistant
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.helpers.discovery import async_load_platform
|
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
DOMAIN = "jewish_calendar"
|
DOMAIN = "jewish_calendar"
|
||||||
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
|
||||||
|
|
||||||
CONF_DIASPORA = "diaspora"
|
CONF_DIASPORA = "diaspora"
|
||||||
CONF_LANGUAGE = "language"
|
|
||||||
CONF_CANDLE_LIGHT_MINUTES = "candle_lighting_minutes_before_sunset"
|
CONF_CANDLE_LIGHT_MINUTES = "candle_lighting_minutes_before_sunset"
|
||||||
CONF_HAVDALAH_OFFSET_MINUTES = "havdalah_minutes_after_sunset"
|
CONF_HAVDALAH_OFFSET_MINUTES = "havdalah_minutes_after_sunset"
|
||||||
|
|
||||||
CANDLE_LIGHT_DEFAULT = 18
|
|
||||||
|
|
||||||
DEFAULT_NAME = "Jewish Calendar"
|
DEFAULT_NAME = "Jewish Calendar"
|
||||||
|
DEFAULT_CANDLE_LIGHT = 18
|
||||||
|
DEFAULT_DIASPORA = False
|
||||||
|
DEFAULT_HAVDALAH_OFFSET_MINUTES = 0
|
||||||
|
DEFAULT_LANGUAGE = "english"
|
||||||
|
|
||||||
|
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.SENSOR]
|
||||||
|
|
||||||
CONFIG_SCHEMA = vol.Schema(
|
CONFIG_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
DOMAIN: vol.Schema(
|
DOMAIN: vol.All(
|
||||||
|
cv.deprecated(DOMAIN),
|
||||||
{
|
{
|
||||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||||
vol.Optional(CONF_DIASPORA, default=False): cv.boolean,
|
vol.Optional(CONF_DIASPORA, default=DEFAULT_DIASPORA): cv.boolean,
|
||||||
vol.Inclusive(CONF_LATITUDE, "coordinates"): cv.latitude,
|
vol.Inclusive(CONF_LATITUDE, "coordinates"): cv.latitude,
|
||||||
vol.Inclusive(CONF_LONGITUDE, "coordinates"): cv.longitude,
|
vol.Inclusive(CONF_LONGITUDE, "coordinates"): cv.longitude,
|
||||||
vol.Optional(CONF_LANGUAGE, default="english"): vol.In(
|
vol.Optional(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): vol.In(
|
||||||
["hebrew", "english"]
|
["hebrew", "english"]
|
||||||
),
|
),
|
||||||
vol.Optional(
|
vol.Optional(
|
||||||
CONF_CANDLE_LIGHT_MINUTES, default=CANDLE_LIGHT_DEFAULT
|
CONF_CANDLE_LIGHT_MINUTES, default=DEFAULT_CANDLE_LIGHT
|
||||||
): int,
|
): int,
|
||||||
# Default of 0 means use 8.5 degrees / 'three_stars' time.
|
# Default of 0 means use 8.5 degrees / 'three_stars' time.
|
||||||
vol.Optional(CONF_HAVDALAH_OFFSET_MINUTES, default=0): int,
|
vol.Optional(
|
||||||
}
|
CONF_HAVDALAH_OFFSET_MINUTES,
|
||||||
|
default=DEFAULT_HAVDALAH_OFFSET_MINUTES,
|
||||||
|
): int,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
extra=vol.ALLOW_EXTRA,
|
extra=vol.ALLOW_EXTRA,
|
||||||
|
@ -72,37 +85,72 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
if DOMAIN not in config:
|
if DOMAIN not in config:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
name = config[DOMAIN][CONF_NAME]
|
async_create_issue(
|
||||||
language = config[DOMAIN][CONF_LANGUAGE]
|
hass,
|
||||||
|
HOMEASSISTANT_DOMAIN,
|
||||||
|
f"deprecated_yaml_{DOMAIN}",
|
||||||
|
is_fixable=False,
|
||||||
|
breaks_in_ha_version="2024.10.0",
|
||||||
|
severity=IssueSeverity.WARNING,
|
||||||
|
translation_key="deprecated_yaml",
|
||||||
|
translation_placeholders={
|
||||||
|
"domain": DOMAIN,
|
||||||
|
"integration_title": DEFAULT_NAME,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
latitude = config[DOMAIN].get(CONF_LATITUDE, hass.config.latitude)
|
hass.async_create_task(
|
||||||
longitude = config[DOMAIN].get(CONF_LONGITUDE, hass.config.longitude)
|
hass.config_entries.flow.async_init(
|
||||||
diaspora = config[DOMAIN][CONF_DIASPORA]
|
DOMAIN, context={"source": SOURCE_IMPORT}, data=config[DOMAIN]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
candle_lighting_offset = config[DOMAIN][CONF_CANDLE_LIGHT_MINUTES]
|
return True
|
||||||
havdalah_offset = config[DOMAIN][CONF_HAVDALAH_OFFSET_MINUTES]
|
|
||||||
|
|
||||||
|
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||||
|
"""Set up a configuration entry for Jewish calendar."""
|
||||||
|
language = config_entry.data.get(CONF_LANGUAGE, DEFAULT_LANGUAGE)
|
||||||
|
diaspora = config_entry.data.get(CONF_DIASPORA, DEFAULT_DIASPORA)
|
||||||
|
candle_lighting_offset = config_entry.data.get(
|
||||||
|
CONF_CANDLE_LIGHT_MINUTES, DEFAULT_CANDLE_LIGHT
|
||||||
|
)
|
||||||
|
havdalah_offset = config_entry.data.get(
|
||||||
|
CONF_HAVDALAH_OFFSET_MINUTES, DEFAULT_HAVDALAH_OFFSET_MINUTES
|
||||||
|
)
|
||||||
|
|
||||||
location = Location(
|
location = Location(
|
||||||
latitude=latitude,
|
name=hass.config.location_name,
|
||||||
longitude=longitude,
|
|
||||||
timezone=hass.config.time_zone,
|
|
||||||
diaspora=diaspora,
|
diaspora=diaspora,
|
||||||
|
latitude=config_entry.data.get(CONF_LATITUDE, hass.config.latitude),
|
||||||
|
longitude=config_entry.data.get(CONF_LONGITUDE, hass.config.longitude),
|
||||||
|
altitude=config_entry.data.get(CONF_ELEVATION, hass.config.elevation),
|
||||||
|
timezone=config_entry.data.get(CONF_TIME_ZONE, hass.config.time_zone),
|
||||||
)
|
)
|
||||||
|
|
||||||
prefix = get_unique_prefix(
|
prefix = get_unique_prefix(
|
||||||
location, language, candle_lighting_offset, havdalah_offset
|
location, language, candle_lighting_offset, havdalah_offset
|
||||||
)
|
)
|
||||||
hass.data[DOMAIN] = {
|
hass.data.setdefault(DOMAIN, {})[config_entry.entry_id] = {
|
||||||
"location": location,
|
|
||||||
"name": name,
|
|
||||||
"language": language,
|
"language": language,
|
||||||
|
"diaspora": diaspora,
|
||||||
|
"location": location,
|
||||||
"candle_lighting_offset": candle_lighting_offset,
|
"candle_lighting_offset": candle_lighting_offset,
|
||||||
"havdalah_offset": havdalah_offset,
|
"havdalah_offset": havdalah_offset,
|
||||||
"diaspora": diaspora,
|
|
||||||
"prefix": prefix,
|
"prefix": prefix,
|
||||||
}
|
}
|
||||||
|
|
||||||
for platform in PLATFORMS:
|
await hass.config_entries.async_forward_entry_setups(config_entry, PLATFORMS)
|
||||||
hass.async_create_task(async_load_platform(hass, platform, DOMAIN, {}, config))
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||||
|
"""Unload a config entry."""
|
||||||
|
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||||
|
config_entry, PLATFORMS
|
||||||
|
)
|
||||||
|
|
||||||
|
if unload_ok:
|
||||||
|
hass.data[DOMAIN].pop(config_entry.entry_id)
|
||||||
|
|
||||||
|
return unload_ok
|
||||||
|
|
|
@ -6,6 +6,7 @@ from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import datetime as dt
|
import datetime as dt
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import hdate
|
import hdate
|
||||||
from hdate.zmanim import Zmanim
|
from hdate.zmanim import Zmanim
|
||||||
|
@ -14,13 +15,14 @@ from homeassistant.components.binary_sensor import (
|
||||||
BinarySensorEntity,
|
BinarySensorEntity,
|
||||||
BinarySensorEntityDescription,
|
BinarySensorEntityDescription,
|
||||||
)
|
)
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
from homeassistant.helpers import event
|
from homeassistant.helpers import event
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from . import DOMAIN
|
from . import DEFAULT_NAME, DOMAIN
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
|
@ -63,15 +65,25 @@ async def async_setup_platform(
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
discovery_info: DiscoveryInfoType | None = None,
|
discovery_info: DiscoveryInfoType | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Jewish Calendar binary sensor devices."""
|
"""Set up the Jewish calendar binary sensors from YAML.
|
||||||
if discovery_info is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
|
The YAML platform config is automatically
|
||||||
|
imported to a config entry, this method can be removed
|
||||||
|
when YAML support is removed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up the Jewish Calendar binary sensors."""
|
||||||
async_add_entities(
|
async_add_entities(
|
||||||
[
|
JewishCalendarBinarySensor(
|
||||||
JewishCalendarBinarySensor(hass.data[DOMAIN], description)
|
hass.data[DOMAIN][config_entry.entry_id], description
|
||||||
for description in BINARY_SENSORS
|
)
|
||||||
]
|
for description in BINARY_SENSORS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,13 +95,13 @@ class JewishCalendarBinarySensor(BinarySensorEntity):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
data: dict[str, str | bool | int | float],
|
data: dict[str, Any],
|
||||||
description: JewishCalendarBinarySensorEntityDescription,
|
description: JewishCalendarBinarySensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the binary sensor."""
|
"""Initialize the binary sensor."""
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_name = f"{data['name']} {description.name}"
|
self._attr_name = f"{DEFAULT_NAME} {description.name}"
|
||||||
self._attr_unique_id = f"{data['prefix']}_{description.key}"
|
self._attr_unique_id = f'{data["prefix"]}_{description.key}'
|
||||||
self._location = data["location"]
|
self._location = data["location"]
|
||||||
self._hebrew = data["language"] == "hebrew"
|
self._hebrew = data["language"] == "hebrew"
|
||||||
self._candle_lighting_offset = data["candle_lighting_offset"]
|
self._candle_lighting_offset = data["candle_lighting_offset"]
|
||||||
|
|
135
homeassistant/components/jewish_calendar/config_flow.py
Normal file
135
homeassistant/components/jewish_calendar/config_flow.py
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
"""Config flow for Jewish calendar integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
import zoneinfo
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.config_entries import (
|
||||||
|
ConfigEntry,
|
||||||
|
ConfigFlow,
|
||||||
|
ConfigFlowResult,
|
||||||
|
OptionsFlowWithConfigEntry,
|
||||||
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_ELEVATION,
|
||||||
|
CONF_LANGUAGE,
|
||||||
|
CONF_LATITUDE,
|
||||||
|
CONF_LOCATION,
|
||||||
|
CONF_LONGITUDE,
|
||||||
|
CONF_TIME_ZONE,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
|
from homeassistant.helpers.selector import (
|
||||||
|
BooleanSelector,
|
||||||
|
LocationSelector,
|
||||||
|
SelectOptionDict,
|
||||||
|
SelectSelector,
|
||||||
|
SelectSelectorConfig,
|
||||||
|
)
|
||||||
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
|
DOMAIN = "jewish_calendar"
|
||||||
|
CONF_DIASPORA = "diaspora"
|
||||||
|
CONF_CANDLE_LIGHT_MINUTES = "candle_lighting_minutes_before_sunset"
|
||||||
|
CONF_HAVDALAH_OFFSET_MINUTES = "havdalah_minutes_after_sunset"
|
||||||
|
DEFAULT_NAME = "Jewish Calendar"
|
||||||
|
DEFAULT_CANDLE_LIGHT = 18
|
||||||
|
DEFAULT_DIASPORA = False
|
||||||
|
DEFAULT_HAVDALAH_OFFSET_MINUTES = 0
|
||||||
|
DEFAULT_LANGUAGE = "english"
|
||||||
|
|
||||||
|
LANGUAGE = [
|
||||||
|
SelectOptionDict(value="hebrew", label="Hebrew"),
|
||||||
|
SelectOptionDict(value="english", label="English"),
|
||||||
|
]
|
||||||
|
|
||||||
|
OPTIONS_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(CONF_CANDLE_LIGHT_MINUTES, default=DEFAULT_CANDLE_LIGHT): int,
|
||||||
|
vol.Optional(
|
||||||
|
CONF_HAVDALAH_OFFSET_MINUTES, default=DEFAULT_HAVDALAH_OFFSET_MINUTES
|
||||||
|
): int,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_data_schema(hass: HomeAssistant) -> vol.Schema:
|
||||||
|
default_location = {
|
||||||
|
CONF_LATITUDE: hass.config.latitude,
|
||||||
|
CONF_LONGITUDE: hass.config.longitude,
|
||||||
|
}
|
||||||
|
return vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_DIASPORA, default=DEFAULT_DIASPORA): BooleanSelector(),
|
||||||
|
vol.Required(CONF_LANGUAGE, default=DEFAULT_LANGUAGE): SelectSelector(
|
||||||
|
SelectSelectorConfig(options=LANGUAGE)
|
||||||
|
),
|
||||||
|
vol.Optional(CONF_LOCATION, default=default_location): LocationSelector(),
|
||||||
|
vol.Optional(CONF_ELEVATION, default=hass.config.elevation): int,
|
||||||
|
vol.Optional(CONF_TIME_ZONE, default=hass.config.time_zone): SelectSelector(
|
||||||
|
SelectSelectorConfig(
|
||||||
|
options=sorted(zoneinfo.available_timezones()),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class JewishCalendarConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||||
|
"""Handle a config flow for Jewish calendar."""
|
||||||
|
|
||||||
|
VERSION = 1
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@callback
|
||||||
|
def async_get_options_flow(config_entry: ConfigEntry) -> OptionsFlowWithConfigEntry:
|
||||||
|
"""Get the options flow for this handler."""
|
||||||
|
return JewishCalendarOptionsFlowHandler(config_entry)
|
||||||
|
|
||||||
|
async def async_step_user(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Handle the initial step."""
|
||||||
|
if user_input is not None:
|
||||||
|
if CONF_LOCATION in user_input:
|
||||||
|
user_input[CONF_LATITUDE] = user_input[CONF_LOCATION][CONF_LATITUDE]
|
||||||
|
user_input[CONF_LONGITUDE] = user_input[CONF_LOCATION][CONF_LONGITUDE]
|
||||||
|
return self.async_create_entry(title=DEFAULT_NAME, data=user_input)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user",
|
||||||
|
data_schema=self.add_suggested_values_to_schema(
|
||||||
|
_get_data_schema(self.hass), user_input
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def async_step_import(
|
||||||
|
self, import_config: ConfigType | None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Import a config entry from configuration.yaml."""
|
||||||
|
return await self.async_step_user(import_config)
|
||||||
|
|
||||||
|
|
||||||
|
class JewishCalendarOptionsFlowHandler(OptionsFlowWithConfigEntry):
|
||||||
|
"""Handle Jewish Calendar options."""
|
||||||
|
|
||||||
|
async def async_step_init(
|
||||||
|
self, user_input: dict[str, str] | None = None
|
||||||
|
) -> ConfigFlowResult:
|
||||||
|
"""Manage the Jewish Calendar options."""
|
||||||
|
if user_input is not None:
|
||||||
|
return self.async_create_entry(data=user_input)
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="init",
|
||||||
|
data_schema=self.add_suggested_values_to_schema(
|
||||||
|
OPTIONS_SCHEMA, self.config_entry.options
|
||||||
|
),
|
||||||
|
)
|
|
@ -2,8 +2,10 @@
|
||||||
"domain": "jewish_calendar",
|
"domain": "jewish_calendar",
|
||||||
"name": "Jewish Calendar",
|
"name": "Jewish Calendar",
|
||||||
"codeowners": ["@tsvi"],
|
"codeowners": ["@tsvi"],
|
||||||
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/jewish_calendar",
|
"documentation": "https://www.home-assistant.io/integrations/jewish_calendar",
|
||||||
"iot_class": "calculated",
|
"iot_class": "calculated",
|
||||||
"loggers": ["hdate"],
|
"loggers": ["hdate"],
|
||||||
"requirements": ["hdate==0.10.8"]
|
"requirements": ["hdate==0.10.8"],
|
||||||
|
"single_config_entry": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
"""Platform to retrieve Jewish calendar information for Home Assistant."""
|
"""Support for Jewish calendar sensors."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ from homeassistant.components.sensor import (
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
SensorEntityDescription,
|
SensorEntityDescription,
|
||||||
)
|
)
|
||||||
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import SUN_EVENT_SUNSET
|
from homeassistant.const import SUN_EVENT_SUNSET
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
@ -21,11 +22,11 @@ from homeassistant.helpers.sun import get_astral_event_date
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from . import DOMAIN
|
from . import DEFAULT_NAME, DOMAIN
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
INFO_SENSORS = (
|
INFO_SENSORS: tuple[SensorEntityDescription, ...] = (
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="date",
|
key="date",
|
||||||
name="Date",
|
name="Date",
|
||||||
|
@ -53,7 +54,7 @@ INFO_SENSORS = (
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
TIME_SENSORS = (
|
TIME_SENSORS: tuple[SensorEntityDescription, ...] = (
|
||||||
SensorEntityDescription(
|
SensorEntityDescription(
|
||||||
key="first_light",
|
key="first_light",
|
||||||
name="Alot Hashachar", # codespell:ignore alot
|
name="Alot Hashachar", # codespell:ignore alot
|
||||||
|
@ -148,17 +149,24 @@ async def async_setup_platform(
|
||||||
async_add_entities: AddEntitiesCallback,
|
async_add_entities: AddEntitiesCallback,
|
||||||
discovery_info: DiscoveryInfoType | None = None,
|
discovery_info: DiscoveryInfoType | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the Jewish calendar sensor platform."""
|
"""Set up the Jewish calendar sensors from YAML.
|
||||||
if discovery_info is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
sensors = [
|
The YAML platform config is automatically
|
||||||
JewishCalendarSensor(hass.data[DOMAIN], description)
|
imported to a config entry, this method can be removed
|
||||||
for description in INFO_SENSORS
|
when YAML support is removed.
|
||||||
]
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
|
"""Set up the Jewish calendar sensors ."""
|
||||||
|
entry = hass.data[DOMAIN][config_entry.entry_id]
|
||||||
|
sensors = [JewishCalendarSensor(entry, description) for description in INFO_SENSORS]
|
||||||
sensors.extend(
|
sensors.extend(
|
||||||
JewishCalendarTimeSensor(hass.data[DOMAIN], description)
|
JewishCalendarTimeSensor(entry, description) for description in TIME_SENSORS
|
||||||
for description in TIME_SENSORS
|
|
||||||
)
|
)
|
||||||
|
|
||||||
async_add_entities(sensors)
|
async_add_entities(sensors)
|
||||||
|
@ -169,13 +177,13 @@ class JewishCalendarSensor(SensorEntity):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
data: dict[str, str | bool | int | float],
|
data: dict[str, Any],
|
||||||
description: SensorEntityDescription,
|
description: SensorEntityDescription,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Jewish calendar sensor."""
|
"""Initialize the Jewish calendar sensor."""
|
||||||
self.entity_description = description
|
self.entity_description = description
|
||||||
self._attr_name = f"{data['name']} {description.name}"
|
self._attr_name = f"{DEFAULT_NAME} {description.name}"
|
||||||
self._attr_unique_id = f"{data['prefix']}_{description.key}"
|
self._attr_unique_id = f'{data["prefix"]}_{description.key}'
|
||||||
self._location = data["location"]
|
self._location = data["location"]
|
||||||
self._hebrew = data["language"] == "hebrew"
|
self._hebrew = data["language"] == "hebrew"
|
||||||
self._candle_lighting_offset = data["candle_lighting_offset"]
|
self._candle_lighting_offset = data["candle_lighting_offset"]
|
||||||
|
|
37
homeassistant/components/jewish_calendar/strings.json
Normal file
37
homeassistant/components/jewish_calendar/strings.json
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"step": {
|
||||||
|
"user": {
|
||||||
|
"data": {
|
||||||
|
"name": "[%key:common::config_flow::data::name%]",
|
||||||
|
"diaspora": "Outside of Israel?",
|
||||||
|
"language": "Language for Holidays and Dates",
|
||||||
|
"location": "[%key:common::config_flow::data::location%]",
|
||||||
|
"elevation": "[%key:common::config_flow::data::elevation%]",
|
||||||
|
"time_zone": "Time Zone"
|
||||||
|
},
|
||||||
|
"data_description": {
|
||||||
|
"time_zone": "If you specify a location, make sure to specify the time zone for correct calendar times calculations"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"title": "Configure options for Jewish Calendar",
|
||||||
|
"data": {
|
||||||
|
"candle_lighting_minutes_before_sunset": "Minutes before sunset for candle lighthing",
|
||||||
|
"havdalah_minutes_after_sunset": "Minutes after sunset for Havdalah"
|
||||||
|
},
|
||||||
|
"data_description": {
|
||||||
|
"candle_lighting_minutes_before_sunset": "Defaults to 18 minutes. In Israel you probably want to use 20/30/40 depending on your location. Outside of Israel you probably want to use 18/24.",
|
||||||
|
"havdalah_minutes_after_sunset": "Setting this to 0 means 36 minutes as fixed degrees (8.5°) will be used instead"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -269,6 +269,7 @@ FLOWS = {
|
||||||
"isy994",
|
"isy994",
|
||||||
"izone",
|
"izone",
|
||||||
"jellyfin",
|
"jellyfin",
|
||||||
|
"jewish_calendar",
|
||||||
"juicenet",
|
"juicenet",
|
||||||
"justnimbus",
|
"justnimbus",
|
||||||
"jvc_projector",
|
"jvc_projector",
|
||||||
|
|
|
@ -2950,8 +2950,9 @@
|
||||||
"jewish_calendar": {
|
"jewish_calendar": {
|
||||||
"name": "Jewish Calendar",
|
"name": "Jewish Calendar",
|
||||||
"integration_type": "hub",
|
"integration_type": "hub",
|
||||||
"config_flow": false,
|
"config_flow": true,
|
||||||
"iot_class": "calculated"
|
"iot_class": "calculated",
|
||||||
|
"single_config_entry": true
|
||||||
},
|
},
|
||||||
"joaoapps_join": {
|
"joaoapps_join": {
|
||||||
"name": "Joaoapps Join",
|
"name": "Joaoapps Join",
|
||||||
|
|
|
@ -27,7 +27,7 @@ def make_nyc_test_params(dtime, results, havdalah_offset=0):
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
dtime,
|
dtime,
|
||||||
jewish_calendar.CANDLE_LIGHT_DEFAULT,
|
jewish_calendar.DEFAULT_CANDLE_LIGHT,
|
||||||
havdalah_offset,
|
havdalah_offset,
|
||||||
True,
|
True,
|
||||||
"America/New_York",
|
"America/New_York",
|
||||||
|
@ -49,7 +49,7 @@ def make_jerusalem_test_params(dtime, results, havdalah_offset=0):
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
dtime,
|
dtime,
|
||||||
jewish_calendar.CANDLE_LIGHT_DEFAULT,
|
jewish_calendar.DEFAULT_CANDLE_LIGHT,
|
||||||
havdalah_offset,
|
havdalah_offset,
|
||||||
False,
|
False,
|
||||||
"Asia/Jerusalem",
|
"Asia/Jerusalem",
|
||||||
|
|
28
tests/components/jewish_calendar/conftest.py
Normal file
28
tests/components/jewish_calendar/conftest.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
"""Common fixtures for the jewish_calendar tests."""
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components.jewish_calendar import config_flow
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_config_entry() -> MockConfigEntry:
|
||||||
|
"""Return the default mocked config entry."""
|
||||||
|
return MockConfigEntry(
|
||||||
|
title=config_flow.DEFAULT_NAME,
|
||||||
|
domain=config_flow.DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||||
|
"""Override async_setup_entry."""
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.jewish_calendar.async_setup_entry", return_value=True
|
||||||
|
) as mock_setup_entry:
|
||||||
|
yield mock_setup_entry
|
|
@ -1,6 +1,7 @@
|
||||||
"""The tests for the Jewish calendar binary sensors."""
|
"""The tests for the Jewish calendar binary sensors."""
|
||||||
|
|
||||||
from datetime import datetime as dt, timedelta
|
from datetime import datetime as dt, timedelta
|
||||||
|
import logging
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -8,18 +9,15 @@ from homeassistant.components import jewish_calendar
|
||||||
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
||||||
from homeassistant.const import STATE_OFF, STATE_ON
|
from homeassistant.const import STATE_OFF, STATE_ON
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from . import (
|
from . import alter_time, make_jerusalem_test_params, make_nyc_test_params
|
||||||
HDATE_DEFAULT_ALTITUDE,
|
|
||||||
alter_time,
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
make_jerusalem_test_params,
|
|
||||||
make_nyc_test_params,
|
_LOGGER = logging.getLogger(__name__)
|
||||||
)
|
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
|
||||||
|
|
||||||
MELACHA_PARAMS = [
|
MELACHA_PARAMS = [
|
||||||
make_nyc_test_params(
|
make_nyc_test_params(
|
||||||
|
@ -170,7 +168,6 @@ MELACHA_TEST_IDS = [
|
||||||
)
|
)
|
||||||
async def test_issur_melacha_sensor(
|
async def test_issur_melacha_sensor(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry: er.EntityRegistry,
|
|
||||||
now,
|
now,
|
||||||
candle_lighting,
|
candle_lighting,
|
||||||
havdalah,
|
havdalah,
|
||||||
|
@ -189,49 +186,33 @@ async def test_issur_melacha_sensor(
|
||||||
hass.config.longitude = longitude
|
hass.config.longitude = longitude
|
||||||
|
|
||||||
with alter_time(test_time):
|
with alter_time(test_time):
|
||||||
assert await async_setup_component(
|
entry = MockConfigEntry(
|
||||||
hass,
|
domain=jewish_calendar.DOMAIN,
|
||||||
jewish_calendar.DOMAIN,
|
data={
|
||||||
{
|
"language": "english",
|
||||||
"jewish_calendar": {
|
"diaspora": diaspora,
|
||||||
"name": "test",
|
"candle_lighting_minutes_before_sunset": candle_lighting,
|
||||||
"language": "english",
|
"havdalah_minutes_after_sunset": havdalah,
|
||||||
"diaspora": diaspora,
|
|
||||||
"candle_lighting_minutes_before_sunset": candle_lighting,
|
|
||||||
"havdalah_minutes_after_sunset": havdalah,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
hass.states.get("binary_sensor.test_issur_melacha_in_effect").state
|
hass.states.get(
|
||||||
|
"binary_sensor.jewish_calendar_issur_melacha_in_effect"
|
||||||
|
).state
|
||||||
== result["state"]
|
== result["state"]
|
||||||
)
|
)
|
||||||
entity = entity_registry.async_get("binary_sensor.test_issur_melacha_in_effect")
|
|
||||||
target_uid = "_".join(
|
|
||||||
map(
|
|
||||||
str,
|
|
||||||
[
|
|
||||||
latitude,
|
|
||||||
longitude,
|
|
||||||
tzname,
|
|
||||||
HDATE_DEFAULT_ALTITUDE,
|
|
||||||
diaspora,
|
|
||||||
"english",
|
|
||||||
candle_lighting,
|
|
||||||
havdalah,
|
|
||||||
"issur_melacha_in_effect",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assert entity.unique_id == target_uid
|
|
||||||
|
|
||||||
with alter_time(result["update"]):
|
with alter_time(result["update"]):
|
||||||
async_fire_time_changed(hass, result["update"])
|
async_fire_time_changed(hass, result["update"])
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert (
|
assert (
|
||||||
hass.states.get("binary_sensor.test_issur_melacha_in_effect").state
|
hass.states.get(
|
||||||
|
"binary_sensor.jewish_calendar_issur_melacha_in_effect"
|
||||||
|
).state
|
||||||
== result["new_state"]
|
== result["new_state"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -277,22 +258,22 @@ async def test_issur_melacha_sensor_update(
|
||||||
hass.config.longitude = longitude
|
hass.config.longitude = longitude
|
||||||
|
|
||||||
with alter_time(test_time):
|
with alter_time(test_time):
|
||||||
assert await async_setup_component(
|
entry = MockConfigEntry(
|
||||||
hass,
|
domain=jewish_calendar.DOMAIN,
|
||||||
jewish_calendar.DOMAIN,
|
data={
|
||||||
{
|
"language": "english",
|
||||||
"jewish_calendar": {
|
"diaspora": diaspora,
|
||||||
"name": "test",
|
"candle_lighting_minutes_before_sunset": candle_lighting,
|
||||||
"language": "english",
|
"havdalah_minutes_after_sunset": havdalah,
|
||||||
"diaspora": diaspora,
|
|
||||||
"candle_lighting_minutes_before_sunset": candle_lighting,
|
|
||||||
"havdalah_minutes_after_sunset": havdalah,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert (
|
assert (
|
||||||
hass.states.get("binary_sensor.test_issur_melacha_in_effect").state
|
hass.states.get(
|
||||||
|
"binary_sensor.jewish_calendar_issur_melacha_in_effect"
|
||||||
|
).state
|
||||||
== result[0]
|
== result[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -301,7 +282,9 @@ async def test_issur_melacha_sensor_update(
|
||||||
async_fire_time_changed(hass, test_time)
|
async_fire_time_changed(hass, test_time)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert (
|
assert (
|
||||||
hass.states.get("binary_sensor.test_issur_melacha_in_effect").state
|
hass.states.get(
|
||||||
|
"binary_sensor.jewish_calendar_issur_melacha_in_effect"
|
||||||
|
).state
|
||||||
== result[1]
|
== result[1]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
138
tests/components/jewish_calendar/test_config_flow.py
Normal file
138
tests/components/jewish_calendar/test_config_flow.py
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
"""Test the Jewish calendar config flow."""
|
||||||
|
|
||||||
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant import config_entries, setup
|
||||||
|
from homeassistant.components.jewish_calendar import (
|
||||||
|
CONF_CANDLE_LIGHT_MINUTES,
|
||||||
|
CONF_DIASPORA,
|
||||||
|
CONF_HAVDALAH_OFFSET_MINUTES,
|
||||||
|
CONF_LANGUAGE,
|
||||||
|
DEFAULT_CANDLE_LIGHT,
|
||||||
|
DEFAULT_DIASPORA,
|
||||||
|
DEFAULT_HAVDALAH_OFFSET_MINUTES,
|
||||||
|
DEFAULT_LANGUAGE,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
from homeassistant.config_entries import SOURCE_USER
|
||||||
|
from homeassistant.const import (
|
||||||
|
CONF_ELEVATION,
|
||||||
|
CONF_LATITUDE,
|
||||||
|
CONF_LONGITUDE,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_TIME_ZONE,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_step_user(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
|
||||||
|
"""Test user config."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
{CONF_DIASPORA: DEFAULT_DIASPORA, CONF_LANGUAGE: DEFAULT_LANGUAGE},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result2["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(mock_setup_entry.mock_calls) == 1
|
||||||
|
|
||||||
|
entries = hass.config_entries.async_entries(DOMAIN)
|
||||||
|
assert len(entries) == 1
|
||||||
|
assert entries[0].data[CONF_DIASPORA] == DEFAULT_DIASPORA
|
||||||
|
assert entries[0].data[CONF_LANGUAGE] == DEFAULT_LANGUAGE
|
||||||
|
assert entries[0].data[CONF_LATITUDE] == hass.config.latitude
|
||||||
|
assert entries[0].data[CONF_LONGITUDE] == hass.config.longitude
|
||||||
|
assert entries[0].data[CONF_ELEVATION] == hass.config.elevation
|
||||||
|
assert entries[0].data[CONF_TIME_ZONE] == hass.config.time_zone
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("diaspora", [True, False])
|
||||||
|
@pytest.mark.parametrize("language", ["hebrew", "english"])
|
||||||
|
async def test_import_no_options(hass: HomeAssistant, language, diaspora) -> None:
|
||||||
|
"""Test that the import step works."""
|
||||||
|
conf = {
|
||||||
|
DOMAIN: {CONF_NAME: "test", CONF_LANGUAGE: language, CONF_DIASPORA: diaspora}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, DOMAIN, conf.copy())
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entries = hass.config_entries.async_entries(DOMAIN)
|
||||||
|
assert len(entries) == 1
|
||||||
|
assert entries[0].data == conf[DOMAIN] | {
|
||||||
|
CONF_CANDLE_LIGHT_MINUTES: DEFAULT_CANDLE_LIGHT,
|
||||||
|
CONF_HAVDALAH_OFFSET_MINUTES: DEFAULT_HAVDALAH_OFFSET_MINUTES,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_import_with_options(hass: HomeAssistant) -> None:
|
||||||
|
"""Test that the import step works."""
|
||||||
|
conf = {
|
||||||
|
DOMAIN: {
|
||||||
|
CONF_NAME: "test",
|
||||||
|
CONF_DIASPORA: DEFAULT_DIASPORA,
|
||||||
|
CONF_LANGUAGE: DEFAULT_LANGUAGE,
|
||||||
|
CONF_CANDLE_LIGHT_MINUTES: 20,
|
||||||
|
CONF_HAVDALAH_OFFSET_MINUTES: 50,
|
||||||
|
CONF_LATITUDE: 31.76,
|
||||||
|
CONF_LONGITUDE: 35.235,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, DOMAIN, conf.copy())
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entries = hass.config_entries.async_entries(DOMAIN)
|
||||||
|
assert len(entries) == 1
|
||||||
|
assert entries[0].data == conf[DOMAIN]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_single_instance_allowed(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
) -> None:
|
||||||
|
"""Test we abort if already setup."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.get("type") is FlowResultType.ABORT
|
||||||
|
assert result.get("reason") == "single_instance_allowed"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_options(hass: HomeAssistant, mock_config_entry: MockConfigEntry) -> None:
|
||||||
|
"""Test updating options."""
|
||||||
|
mock_config_entry.add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.FORM
|
||||||
|
assert result["step_id"] == "init"
|
||||||
|
|
||||||
|
result = await hass.config_entries.options.async_configure(
|
||||||
|
result["flow_id"],
|
||||||
|
user_input={
|
||||||
|
CONF_CANDLE_LIGHT_MINUTES: 25,
|
||||||
|
CONF_HAVDALAH_OFFSET_MINUTES: 34,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
|
assert result["data"][CONF_CANDLE_LIGHT_MINUTES] == 25
|
||||||
|
assert result["data"][CONF_HAVDALAH_OFFSET_MINUTES] == 34
|
|
@ -7,34 +7,28 @@ import pytest
|
||||||
from homeassistant.components import jewish_calendar
|
from homeassistant.components import jewish_calendar
|
||||||
from homeassistant.components.binary_sensor import DOMAIN as SENSOR_DOMAIN
|
from homeassistant.components.binary_sensor import DOMAIN as SENSOR_DOMAIN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers import entity_registry as er
|
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
from . import (
|
from . import alter_time, make_jerusalem_test_params, make_nyc_test_params
|
||||||
HDATE_DEFAULT_ALTITUDE,
|
|
||||||
alter_time,
|
|
||||||
make_jerusalem_test_params,
|
|
||||||
make_nyc_test_params,
|
|
||||||
)
|
|
||||||
|
|
||||||
from tests.common import async_fire_time_changed
|
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
async def test_jewish_calendar_min_config(hass: HomeAssistant) -> None:
|
async def test_jewish_calendar_min_config(hass: HomeAssistant) -> None:
|
||||||
"""Test minimum jewish calendar configuration."""
|
"""Test minimum jewish calendar configuration."""
|
||||||
assert await async_setup_component(
|
entry = MockConfigEntry(domain=jewish_calendar.DOMAIN, data={})
|
||||||
hass, jewish_calendar.DOMAIN, {"jewish_calendar": {}}
|
entry.add_to_hass(hass)
|
||||||
)
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert hass.states.get("sensor.jewish_calendar_date") is not None
|
assert hass.states.get("sensor.jewish_calendar_date") is not None
|
||||||
|
|
||||||
|
|
||||||
async def test_jewish_calendar_hebrew(hass: HomeAssistant) -> None:
|
async def test_jewish_calendar_hebrew(hass: HomeAssistant) -> None:
|
||||||
"""Test jewish calendar sensor with language set to hebrew."""
|
"""Test jewish calendar sensor with language set to hebrew."""
|
||||||
assert await async_setup_component(
|
entry = MockConfigEntry(domain=jewish_calendar.DOMAIN, data={"language": "hebrew"})
|
||||||
hass, jewish_calendar.DOMAIN, {"jewish_calendar": {"language": "hebrew"}}
|
entry.add_to_hass(hass)
|
||||||
)
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert hass.states.get("sensor.jewish_calendar_date") is not None
|
assert hass.states.get("sensor.jewish_calendar_date") is not None
|
||||||
|
|
||||||
|
@ -172,17 +166,15 @@ async def test_jewish_calendar_sensor(
|
||||||
hass.config.longitude = longitude
|
hass.config.longitude = longitude
|
||||||
|
|
||||||
with alter_time(test_time):
|
with alter_time(test_time):
|
||||||
assert await async_setup_component(
|
entry = MockConfigEntry(
|
||||||
hass,
|
domain=jewish_calendar.DOMAIN,
|
||||||
jewish_calendar.DOMAIN,
|
data={
|
||||||
{
|
"language": language,
|
||||||
"jewish_calendar": {
|
"diaspora": diaspora,
|
||||||
"name": "test",
|
|
||||||
"language": language,
|
|
||||||
"diaspora": diaspora,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
future = dt_util.utcnow() + timedelta(seconds=30)
|
future = dt_util.utcnow() + timedelta(seconds=30)
|
||||||
|
@ -195,7 +187,7 @@ async def test_jewish_calendar_sensor(
|
||||||
else result
|
else result
|
||||||
)
|
)
|
||||||
|
|
||||||
sensor_object = hass.states.get(f"sensor.test_{sensor}")
|
sensor_object = hass.states.get(f"sensor.jewish_calendar_{sensor}")
|
||||||
assert sensor_object.state == result
|
assert sensor_object.state == result
|
||||||
|
|
||||||
if sensor == "holiday":
|
if sensor == "holiday":
|
||||||
|
@ -497,7 +489,6 @@ SHABBAT_TEST_IDS = [
|
||||||
)
|
)
|
||||||
async def test_shabbat_times_sensor(
|
async def test_shabbat_times_sensor(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entity_registry: er.EntityRegistry,
|
|
||||||
language,
|
language,
|
||||||
now,
|
now,
|
||||||
candle_lighting,
|
candle_lighting,
|
||||||
|
@ -517,19 +508,17 @@ async def test_shabbat_times_sensor(
|
||||||
hass.config.longitude = longitude
|
hass.config.longitude = longitude
|
||||||
|
|
||||||
with alter_time(test_time):
|
with alter_time(test_time):
|
||||||
assert await async_setup_component(
|
entry = MockConfigEntry(
|
||||||
hass,
|
domain=jewish_calendar.DOMAIN,
|
||||||
jewish_calendar.DOMAIN,
|
data={
|
||||||
{
|
"language": language,
|
||||||
"jewish_calendar": {
|
"diaspora": diaspora,
|
||||||
"name": "test",
|
"candle_lighting_minutes_before_sunset": candle_lighting,
|
||||||
"language": language,
|
"havdalah_minutes_after_sunset": havdalah,
|
||||||
"diaspora": diaspora,
|
|
||||||
"candle_lighting_minutes_before_sunset": candle_lighting,
|
|
||||||
"havdalah_minutes_after_sunset": havdalah,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
entry.add_to_hass(hass)
|
||||||
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
future = dt_util.utcnow() + timedelta(seconds=30)
|
future = dt_util.utcnow() + timedelta(seconds=30)
|
||||||
|
@ -548,30 +537,10 @@ async def test_shabbat_times_sensor(
|
||||||
else result_value
|
else result_value
|
||||||
)
|
)
|
||||||
|
|
||||||
assert hass.states.get(f"sensor.test_{sensor_type}").state == str(
|
assert hass.states.get(f"sensor.jewish_calendar_{sensor_type}").state == str(
|
||||||
result_value
|
result_value
|
||||||
), f"Value for {sensor_type}"
|
), f"Value for {sensor_type}"
|
||||||
|
|
||||||
entity = entity_registry.async_get(f"sensor.test_{sensor_type}")
|
|
||||||
target_sensor_type = sensor_type.replace("parshat_hashavua", "weekly_portion")
|
|
||||||
target_uid = "_".join(
|
|
||||||
map(
|
|
||||||
str,
|
|
||||||
[
|
|
||||||
latitude,
|
|
||||||
longitude,
|
|
||||||
tzname,
|
|
||||||
HDATE_DEFAULT_ALTITUDE,
|
|
||||||
diaspora,
|
|
||||||
language,
|
|
||||||
candle_lighting,
|
|
||||||
havdalah,
|
|
||||||
target_sensor_type,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assert entity.unique_id == target_uid
|
|
||||||
|
|
||||||
|
|
||||||
OMER_PARAMS = [
|
OMER_PARAMS = [
|
||||||
(dt(2019, 4, 21, 0), "1"),
|
(dt(2019, 4, 21, 0), "1"),
|
||||||
|
@ -597,16 +566,16 @@ async def test_omer_sensor(hass: HomeAssistant, test_time, result) -> None:
|
||||||
test_time = test_time.replace(tzinfo=dt_util.get_time_zone(hass.config.time_zone))
|
test_time = test_time.replace(tzinfo=dt_util.get_time_zone(hass.config.time_zone))
|
||||||
|
|
||||||
with alter_time(test_time):
|
with alter_time(test_time):
|
||||||
assert await async_setup_component(
|
entry = MockConfigEntry(domain=jewish_calendar.DOMAIN)
|
||||||
hass, jewish_calendar.DOMAIN, {"jewish_calendar": {"name": "test"}}
|
entry.add_to_hass(hass)
|
||||||
)
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
future = dt_util.utcnow() + timedelta(seconds=30)
|
future = dt_util.utcnow() + timedelta(seconds=30)
|
||||||
async_fire_time_changed(hass, future)
|
async_fire_time_changed(hass, future)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get("sensor.test_day_of_the_omer").state == result
|
assert hass.states.get("sensor.jewish_calendar_day_of_the_omer").state == result
|
||||||
|
|
||||||
|
|
||||||
DAFYOMI_PARAMS = [
|
DAFYOMI_PARAMS = [
|
||||||
|
@ -631,16 +600,16 @@ async def test_dafyomi_sensor(hass: HomeAssistant, test_time, result) -> None:
|
||||||
test_time = test_time.replace(tzinfo=dt_util.get_time_zone(hass.config.time_zone))
|
test_time = test_time.replace(tzinfo=dt_util.get_time_zone(hass.config.time_zone))
|
||||||
|
|
||||||
with alter_time(test_time):
|
with alter_time(test_time):
|
||||||
assert await async_setup_component(
|
entry = MockConfigEntry(domain=jewish_calendar.DOMAIN)
|
||||||
hass, jewish_calendar.DOMAIN, {"jewish_calendar": {"name": "test"}}
|
entry.add_to_hass(hass)
|
||||||
)
|
await hass.config_entries.async_setup(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
future = dt_util.utcnow() + timedelta(seconds=30)
|
future = dt_util.utcnow() + timedelta(seconds=30)
|
||||||
async_fire_time_changed(hass, future)
|
async_fire_time_changed(hass, future)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert hass.states.get("sensor.test_daf_yomi").state == result
|
assert hass.states.get("sensor.jewish_calendar_daf_yomi").state == result
|
||||||
|
|
||||||
|
|
||||||
async def test_no_discovery_info(
|
async def test_no_discovery_info(
|
||||||
|
|
Loading…
Add table
Reference in a new issue