mirror of
https://github.com/home-assistant/core.git
synced 2025-04-19 14:57:52 +00:00
Add Israel rail integration (#121418)
* Add Israel Rail integration * israel_rail tests * israel_rail tests * 1. use entry.runtime 2. DataConnection - data class 3. remove unique id from coordinator 4. use EntityDescription * add a list of stations in user form * 1. extend ConfigEntry 2. remove unused pop 3. use IsraelRailSensorEntityDescription to have only one kind of Sensor 4. add test for already configured 5. use snapshot in test * change user step description * 1. ConfigEntry[IsraelRailDataUpdateCoordinator] 2. remove redundant attributes 3. use snapshot_platform helper * remove attr * remove attr * move test to test_init.py * Fix * Fix * Fix * Fix * fix timezone * fix * fix --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
ea5eb0f8f2
commit
56b6747bc0
@ -707,6 +707,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/isal/ @bdraco
|
||||
/homeassistant/components/islamic_prayer_times/ @engrbm87 @cpfair
|
||||
/tests/components/islamic_prayer_times/ @engrbm87 @cpfair
|
||||
/homeassistant/components/israel_rail/ @shaiu
|
||||
/tests/components/israel_rail/ @shaiu
|
||||
/homeassistant/components/iss/ @DurgNomis-drol
|
||||
/tests/components/iss/ @DurgNomis-drol
|
||||
/homeassistant/components/ista_ecotrend/ @tr4nt0r
|
||||
|
58
homeassistant/components/israel_rail/__init__.py
Normal file
58
homeassistant/components/israel_rail/__init__.py
Normal file
@ -0,0 +1,58 @@
|
||||
"""The Israel Rail component."""
|
||||
|
||||
import logging
|
||||
|
||||
from israelrailapi import TrainSchedule
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
|
||||
from .const import CONF_DESTINATION, CONF_START, DOMAIN
|
||||
from .coordinator import IsraelRailDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.SENSOR]
|
||||
|
||||
|
||||
type IsraelRailConfigEntry = ConfigEntry[IsraelRailDataUpdateCoordinator]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: IsraelRailConfigEntry) -> bool:
|
||||
"""Set up Israel rail from a config entry."""
|
||||
config = entry.data
|
||||
|
||||
start = config[CONF_START]
|
||||
destination = config[CONF_DESTINATION]
|
||||
|
||||
train_schedule = TrainSchedule()
|
||||
|
||||
try:
|
||||
await hass.async_add_executor_job(train_schedule.query, start, destination)
|
||||
except Exception as e:
|
||||
raise ConfigEntryNotReady(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="request_timeout",
|
||||
translation_placeholders={
|
||||
"config_title": entry.title,
|
||||
"error": str(e),
|
||||
},
|
||||
) from e
|
||||
|
||||
israel_rail_coordinator = IsraelRailDataUpdateCoordinator(
|
||||
hass, train_schedule, start, destination
|
||||
)
|
||||
await israel_rail_coordinator.async_config_entry_first_refresh()
|
||||
entry.runtime_data = israel_rail_coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: IsraelRailConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
61
homeassistant/components/israel_rail/config_flow.py
Normal file
61
homeassistant/components/israel_rail/config_flow.py
Normal file
@ -0,0 +1,61 @@
|
||||
"""Config flow for israel rail."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from israelrailapi import TrainSchedule
|
||||
from israelrailapi.stations import STATIONS
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
|
||||
from .const import CONF_DESTINATION, CONF_START, DOMAIN
|
||||
|
||||
STATIONS_NAMES = [station["Heb"] for station in STATIONS.values()]
|
||||
|
||||
DATA_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_START): vol.In(STATIONS_NAMES),
|
||||
vol.Required(CONF_DESTINATION): vol.In(STATIONS_NAMES),
|
||||
}
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IsraelRailConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Israel rail config flow."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Async user step to set up the connection."""
|
||||
errors = {}
|
||||
if user_input:
|
||||
train_schedule = TrainSchedule()
|
||||
try:
|
||||
await self.hass.async_add_executor_job(
|
||||
train_schedule.query,
|
||||
user_input[CONF_START],
|
||||
user_input[CONF_DESTINATION],
|
||||
)
|
||||
except Exception:
|
||||
_LOGGER.exception("Unknown error")
|
||||
errors["base"] = "unknown"
|
||||
if not errors:
|
||||
unique_id = f"{user_input[CONF_START]} {user_input[CONF_DESTINATION]}"
|
||||
await self.async_set_unique_id(unique_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(
|
||||
title=unique_id,
|
||||
data=user_input,
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=DATA_SCHEMA,
|
||||
errors=errors,
|
||||
)
|
17
homeassistant/components/israel_rail/const.py
Normal file
17
homeassistant/components/israel_rail/const.py
Normal file
@ -0,0 +1,17 @@
|
||||
"""Constants for the israel rail integration."""
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import Final
|
||||
|
||||
DOMAIN = "israel_rail"
|
||||
|
||||
CONF_START: Final = "from"
|
||||
CONF_DESTINATION: Final = "to"
|
||||
|
||||
DEFAULT_NAME = "Next Destination"
|
||||
|
||||
DEPARTURES_COUNT = 3
|
||||
|
||||
DEFAULT_SCAN_INTERVAL = timedelta(seconds=90)
|
||||
|
||||
ATTRIBUTION = "Data provided by Israel rail."
|
113
homeassistant/components/israel_rail/coordinator.py
Normal file
113
homeassistant/components/israel_rail/coordinator.py
Normal file
@ -0,0 +1,113 @@
|
||||
"""DataUpdateCoordinator for the israel rail integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
from israelrailapi import TrainSchedule
|
||||
from israelrailapi.api import TrainRoute
|
||||
from israelrailapi.train_station import station_name_to_id
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .const import DEFAULT_SCAN_INTERVAL, DEPARTURES_COUNT, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass
|
||||
class DataConnection:
|
||||
"""A connection data class."""
|
||||
|
||||
departure: datetime | None
|
||||
duration: int | None
|
||||
platform: str
|
||||
remaining_time: str
|
||||
start: str
|
||||
destination: str
|
||||
train_number: str
|
||||
transfers: int
|
||||
|
||||
|
||||
def calculate_duration_in_seconds(start_time: str, end_time: str) -> int | None:
|
||||
"""Transform and calculate the duration from start and end time into seconds."""
|
||||
end_time_date = dt_util.parse_datetime(end_time)
|
||||
start_time_date = dt_util.parse_datetime(start_time)
|
||||
if not end_time_date or not start_time_date:
|
||||
return None
|
||||
return (end_time_date - start_time_date).seconds
|
||||
|
||||
|
||||
def departure_time(train_route: TrainRoute) -> datetime | None:
|
||||
"""Get departure time."""
|
||||
start_datetime = dt_util.parse_datetime(train_route.start_time)
|
||||
return start_datetime.astimezone() if start_datetime else None
|
||||
|
||||
|
||||
def remaining_time(departure) -> timedelta | None:
|
||||
"""Calculate the remaining time for the departure."""
|
||||
departure_datetime = dt_util.parse_datetime(departure)
|
||||
|
||||
if departure_datetime:
|
||||
return dt_util.as_local(departure_datetime) - dt_util.as_local(dt_util.utcnow())
|
||||
return None
|
||||
|
||||
|
||||
class IsraelRailDataUpdateCoordinator(DataUpdateCoordinator[list[DataConnection]]):
|
||||
"""A IsraelRail Data Update Coordinator."""
|
||||
|
||||
config_entry: ConfigEntry
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
train_schedule: TrainSchedule,
|
||||
start: str,
|
||||
destination: str,
|
||||
) -> None:
|
||||
"""Initialize the IsraelRail data coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=DOMAIN,
|
||||
update_interval=DEFAULT_SCAN_INTERVAL,
|
||||
)
|
||||
self._train_schedule = train_schedule
|
||||
self._start = start
|
||||
self._destination = destination
|
||||
|
||||
async def _async_update_data(self) -> list[DataConnection]:
|
||||
try:
|
||||
train_routes = await self.hass.async_add_executor_job(
|
||||
self._train_schedule.query,
|
||||
self._start,
|
||||
self._destination,
|
||||
datetime.now().strftime("%Y-%m-%d"),
|
||||
datetime.now().strftime("%H:%M"),
|
||||
)
|
||||
except Exception as e:
|
||||
raise UpdateFailed(
|
||||
"Unable to connect and retrieve data from israelrail api",
|
||||
) from e
|
||||
|
||||
return [
|
||||
DataConnection(
|
||||
departure=departure_time(train_routes[i]),
|
||||
train_number=train_routes[i].trains[0].data["trainNumber"],
|
||||
platform=train_routes[i].trains[0].platform,
|
||||
transfers=len(train_routes[i].trains) - 1,
|
||||
duration=calculate_duration_in_seconds(
|
||||
train_routes[i].start_time, train_routes[i].end_time
|
||||
),
|
||||
start=station_name_to_id(train_routes[i].trains[0].src),
|
||||
destination=station_name_to_id(train_routes[i].trains[-1].dst),
|
||||
remaining_time=str(remaining_time(train_routes[i].trains[0].departure)),
|
||||
)
|
||||
for i in range(DEPARTURES_COUNT)
|
||||
if len(train_routes) > i and train_routes[i] is not None
|
||||
]
|
27
homeassistant/components/israel_rail/icons.json
Normal file
27
homeassistant/components/israel_rail/icons.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"departure0": {
|
||||
"default": "mdi:bus-clock"
|
||||
},
|
||||
"departure1": {
|
||||
"default": "mdi:bus-clock"
|
||||
},
|
||||
"departure2": {
|
||||
"default": "mdi:bus-clock"
|
||||
},
|
||||
"duration": {
|
||||
"default": "mdi:timeline-clock"
|
||||
},
|
||||
"transfers": {
|
||||
"default": "mdi:transit-transfer"
|
||||
},
|
||||
"platform": {
|
||||
"default": "mdi:bus-stop-uncovered"
|
||||
},
|
||||
"train_number": {
|
||||
"default": "mdi:train"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
homeassistant/components/israel_rail/manifest.json
Normal file
10
homeassistant/components/israel_rail/manifest.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"domain": "israel_rail",
|
||||
"name": "Israel Railways",
|
||||
"codeowners": ["@shaiu"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/israel_rail",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["israelrailapi"],
|
||||
"requirements": ["israel-rail-api==0.1.2"]
|
||||
}
|
125
homeassistant/components/israel_rail/sensor.py
Normal file
125
homeassistant/components/israel_rail/sensor.py
Normal file
@ -0,0 +1,125 @@
|
||||
"""Support for israel rail."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.const import UnitOfTime
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from . import IsraelRailConfigEntry
|
||||
from .const import ATTRIBUTION, DEPARTURES_COUNT, DOMAIN
|
||||
from .coordinator import DataConnection, IsraelRailDataUpdateCoordinator
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@dataclass(kw_only=True, frozen=True)
|
||||
class IsraelRailSensorEntityDescription(SensorEntityDescription):
|
||||
"""Describes israel rail sensor entity."""
|
||||
|
||||
value_fn: Callable[[DataConnection], StateType | datetime]
|
||||
|
||||
index: int = 0
|
||||
|
||||
|
||||
DEPARTURE_SENSORS: tuple[IsraelRailSensorEntityDescription, ...] = (
|
||||
*[
|
||||
IsraelRailSensorEntityDescription(
|
||||
key=f"departure{i or ''}",
|
||||
translation_key=f"departure{i}",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
value_fn=lambda data_connection: data_connection.departure,
|
||||
index=i,
|
||||
)
|
||||
for i in range(DEPARTURES_COUNT)
|
||||
],
|
||||
)
|
||||
|
||||
SENSORS: tuple[IsraelRailSensorEntityDescription, ...] = (
|
||||
IsraelRailSensorEntityDescription(
|
||||
key="duration",
|
||||
device_class=SensorDeviceClass.DURATION,
|
||||
native_unit_of_measurement=UnitOfTime.SECONDS,
|
||||
value_fn=lambda data_connection: data_connection.duration,
|
||||
),
|
||||
IsraelRailSensorEntityDescription(
|
||||
key="platform",
|
||||
translation_key="platform",
|
||||
value_fn=lambda data_connection: data_connection.platform,
|
||||
),
|
||||
IsraelRailSensorEntityDescription(
|
||||
key="transfers",
|
||||
translation_key="transfers",
|
||||
value_fn=lambda data_connection: data_connection.transfers,
|
||||
),
|
||||
IsraelRailSensorEntityDescription(
|
||||
key="train_number",
|
||||
translation_key="train_number",
|
||||
value_fn=lambda data_connection: data_connection.train_number,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: IsraelRailConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up the sensor from a config entry created in the integrations UI."""
|
||||
coordinator = config_entry.runtime_data
|
||||
|
||||
unique_id = config_entry.unique_id
|
||||
|
||||
if TYPE_CHECKING:
|
||||
assert unique_id
|
||||
|
||||
async_add_entities(
|
||||
IsraelRailEntitySensor(coordinator, description, unique_id)
|
||||
for description in (*DEPARTURE_SENSORS, *SENSORS)
|
||||
)
|
||||
|
||||
|
||||
class IsraelRailEntitySensor(
|
||||
CoordinatorEntity[IsraelRailDataUpdateCoordinator], SensorEntity
|
||||
):
|
||||
"""Define a Israel Rail sensor."""
|
||||
|
||||
entity_description: IsraelRailSensorEntityDescription
|
||||
_attr_attribution = ATTRIBUTION
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: IsraelRailDataUpdateCoordinator,
|
||||
entity_description: IsraelRailSensorEntityDescription,
|
||||
unique_id: str,
|
||||
) -> None:
|
||||
"""Initialize the sensor."""
|
||||
super().__init__(coordinator)
|
||||
self.entity_description = entity_description
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, unique_id)},
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
)
|
||||
self._attr_unique_id = f"{unique_id}_{entity_description.key}"
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType | datetime:
|
||||
"""Return the state of the sensor."""
|
||||
return self.entity_description.value_fn(
|
||||
self.coordinator.data[self.entity_description.index]
|
||||
)
|
42
homeassistant/components/israel_rail/strings.json
Normal file
42
homeassistant/components/israel_rail/strings.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"from": "Start station",
|
||||
"to": "End station"
|
||||
},
|
||||
"description": "Provide start and end station for your connection from the provided list",
|
||||
"title": "Israel Rail"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"departure0": {
|
||||
"name": "Departure"
|
||||
},
|
||||
"departure1": {
|
||||
"name": "Departure +1"
|
||||
},
|
||||
"departure2": {
|
||||
"name": "Departure +2"
|
||||
},
|
||||
"transfers": {
|
||||
"name": "Transfers"
|
||||
},
|
||||
"platform": {
|
||||
"name": "Platform"
|
||||
},
|
||||
"train_number": {
|
||||
"name": "Train number"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -273,6 +273,7 @@ FLOWS = {
|
||||
"ipp",
|
||||
"iqvia",
|
||||
"islamic_prayer_times",
|
||||
"israel_rail",
|
||||
"iss",
|
||||
"ista_ecotrend",
|
||||
"isy994",
|
||||
|
@ -2909,6 +2909,12 @@
|
||||
"integration_type": "virtual",
|
||||
"supported_by": "motion_blinds"
|
||||
},
|
||||
"israel_rail": {
|
||||
"name": "Israel Railways",
|
||||
"integration_type": "hub",
|
||||
"config_flow": true,
|
||||
"iot_class": "cloud_polling"
|
||||
},
|
||||
"iss": {
|
||||
"name": "International Space Station (ISS)",
|
||||
"integration_type": "service",
|
||||
|
@ -1184,6 +1184,9 @@ isal==1.6.1
|
||||
# homeassistant.components.gogogate2
|
||||
ismartgate==5.0.1
|
||||
|
||||
# homeassistant.components.israel_rail
|
||||
israel-rail-api==0.1.2
|
||||
|
||||
# homeassistant.components.abode
|
||||
jaraco.abode==5.2.1
|
||||
|
||||
|
@ -971,6 +971,9 @@ isal==1.6.1
|
||||
# homeassistant.components.gogogate2
|
||||
ismartgate==5.0.1
|
||||
|
||||
# homeassistant.components.israel_rail
|
||||
israel-rail-api==0.1.2
|
||||
|
||||
# homeassistant.components.abode
|
||||
jaraco.abode==5.2.1
|
||||
|
||||
|
28
tests/components/israel_rail/__init__.py
Normal file
28
tests/components/israel_rail/__init__.py
Normal file
@ -0,0 +1,28 @@
|
||||
"""Tests for the israel_rail component."""
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
|
||||
from homeassistant.components.israel_rail.const import DEFAULT_SCAN_INTERVAL
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
async def init_integration(
|
||||
hass: HomeAssistant,
|
||||
config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Set up the israel rail integration in Home Assistant."""
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def goto_future(hass: HomeAssistant, freezer: FrozenDateTimeFactory):
|
||||
"""Move to future."""
|
||||
freezer.tick(DEFAULT_SCAN_INTERVAL + timedelta(minutes=1))
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
185
tests/components/israel_rail/conftest.py
Normal file
185
tests/components/israel_rail/conftest.py
Normal file
@ -0,0 +1,185 @@
|
||||
"""Configuration for Israel rail tests."""
|
||||
|
||||
from datetime import datetime
|
||||
from unittest.mock import AsyncMock, patch
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from israelrailapi.api import TrainRoute
|
||||
import pytest
|
||||
from typing_extensions import Generator
|
||||
|
||||
from homeassistant.components.israel_rail import CONF_DESTINATION, CONF_START, DOMAIN
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
VALID_CONFIG = {
|
||||
CONF_START: "באר יעקב",
|
||||
CONF_DESTINATION: "אשקלון",
|
||||
}
|
||||
|
||||
SOURCE_DEST = "באר יעקב אשקלון"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
"""Override async_setup_entry."""
|
||||
with patch(
|
||||
"homeassistant.components.israel_rail.async_setup_entry", return_value=True
|
||||
) as mock_setup_entry:
|
||||
yield mock_setup_entry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config_entry() -> MockConfigEntry:
|
||||
"""Return the default mocked config entry."""
|
||||
return MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data=VALID_CONFIG,
|
||||
unique_id=SOURCE_DEST,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_israelrail() -> AsyncMock:
|
||||
"""Build a fixture for the Israel rail API."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.israel_rail.TrainSchedule",
|
||||
autospec=True,
|
||||
) as mock_client,
|
||||
patch(
|
||||
"homeassistant.components.israel_rail.config_flow.TrainSchedule",
|
||||
new=mock_client,
|
||||
),
|
||||
):
|
||||
client = mock_client.return_value
|
||||
client.query.return_value = TRAINS
|
||||
|
||||
yield client
|
||||
|
||||
|
||||
def get_time(hour: int, minute: int) -> str:
|
||||
"""Return a time in isoformat."""
|
||||
return datetime(2021, 10, 10, hour, minute, 10, tzinfo=ZoneInfo("UTC")).isoformat()
|
||||
|
||||
|
||||
def get_train_route(
|
||||
train_number: str = "1234",
|
||||
departure_time: str = "2021-10-10T10:10:10",
|
||||
arrival_time: str = "2021-10-10T10:10:10",
|
||||
origin_platform: str = "1",
|
||||
dest_platform: str = "2",
|
||||
origin_station: str = "3500",
|
||||
destination_station: str = "3700",
|
||||
) -> TrainRoute:
|
||||
"""Build a TrainRoute of the israelrail API."""
|
||||
return TrainRoute(
|
||||
[
|
||||
{
|
||||
"orignStation": origin_station,
|
||||
"destinationStation": destination_station,
|
||||
"departureTime": departure_time,
|
||||
"arrivalTime": arrival_time,
|
||||
"originPlatform": origin_platform,
|
||||
"destPlatform": dest_platform,
|
||||
"trainNumber": train_number,
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
TRAINS = [
|
||||
get_train_route(
|
||||
train_number="1234",
|
||||
departure_time=get_time(10, 10),
|
||||
arrival_time=get_time(10, 30),
|
||||
origin_platform="1",
|
||||
dest_platform="2",
|
||||
origin_station="3500",
|
||||
destination_station="3700",
|
||||
),
|
||||
get_train_route(
|
||||
train_number="1235",
|
||||
departure_time=get_time(10, 20),
|
||||
arrival_time=get_time(10, 40),
|
||||
origin_platform="1",
|
||||
dest_platform="2",
|
||||
origin_station="3500",
|
||||
destination_station="3700",
|
||||
),
|
||||
get_train_route(
|
||||
train_number="1236",
|
||||
departure_time=get_time(10, 30),
|
||||
arrival_time=get_time(10, 50),
|
||||
origin_platform="1",
|
||||
dest_platform="2",
|
||||
origin_station="3500",
|
||||
destination_station="3700",
|
||||
),
|
||||
get_train_route(
|
||||
train_number="1237",
|
||||
departure_time=get_time(10, 40),
|
||||
arrival_time=get_time(11, 00),
|
||||
origin_platform="1",
|
||||
dest_platform="2",
|
||||
origin_station="3500",
|
||||
destination_station="3700",
|
||||
),
|
||||
get_train_route(
|
||||
train_number="1238",
|
||||
departure_time=get_time(10, 50),
|
||||
arrival_time=get_time(11, 10),
|
||||
origin_platform="1",
|
||||
dest_platform="2",
|
||||
origin_station="3500",
|
||||
destination_station="3700",
|
||||
),
|
||||
]
|
||||
|
||||
TRAINS_WRONG_FORMAT = [
|
||||
get_train_route(
|
||||
train_number="1234",
|
||||
departure_time="2021-10-1010:10:10",
|
||||
arrival_time="2021-10-10T10:30:10",
|
||||
origin_platform="1",
|
||||
dest_platform="2",
|
||||
origin_station="3500",
|
||||
destination_station="3700",
|
||||
),
|
||||
get_train_route(
|
||||
train_number="1235",
|
||||
departure_time="2021-10-1010:20:10",
|
||||
arrival_time="2021-10-10T10:40:10",
|
||||
origin_platform="1",
|
||||
dest_platform="2",
|
||||
origin_station="3500",
|
||||
destination_station="3700",
|
||||
),
|
||||
get_train_route(
|
||||
train_number="1236",
|
||||
departure_time="2021-10-1010:30:10",
|
||||
arrival_time="2021-10-10T10:50:10",
|
||||
origin_platform="1",
|
||||
dest_platform="2",
|
||||
origin_station="3500",
|
||||
destination_station="3700",
|
||||
),
|
||||
get_train_route(
|
||||
train_number="1237",
|
||||
departure_time="2021-10-1010:40:10",
|
||||
arrival_time="2021-10-10T11:00:10",
|
||||
origin_platform="1",
|
||||
dest_platform="2",
|
||||
origin_station="3500",
|
||||
destination_station="3700",
|
||||
),
|
||||
get_train_route(
|
||||
train_number="1238",
|
||||
departure_time="2021-10-1010:50:10",
|
||||
arrival_time="2021-10-10T11:10:10",
|
||||
origin_platform="1",
|
||||
dest_platform="2",
|
||||
origin_station="3500",
|
||||
destination_station="3700",
|
||||
),
|
||||
]
|
335
tests/components/israel_rail/snapshots/test_sensor.ambr
Normal file
335
tests/components/israel_rail/snapshots/test_sensor.ambr
Normal file
@ -0,0 +1,335 @@
|
||||
# serializer version: 1
|
||||
# name: test_valid_config[sensor.mock_title_departure-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_departure',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Departure',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'departure0',
|
||||
'unique_id': 'באר יעקב אשקלון_departure',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_departure-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Mock Title Departure',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_departure',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '2021-10-10T10:10:10+00:00',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_departure_1-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_departure_1',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Departure +1',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'departure1',
|
||||
'unique_id': 'באר יעקב אשקלון_departure1',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_departure_1-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Mock Title Departure +1',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_departure_1',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '2021-10-10T10:20:10+00:00',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_departure_2-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_departure_2',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.TIMESTAMP: 'timestamp'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Departure +2',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'departure2',
|
||||
'unique_id': 'באר יעקב אשקלון_departure2',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_departure_2-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'device_class': 'timestamp',
|
||||
'friendly_name': 'Mock Title Departure +2',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_departure_2',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '2021-10-10T10:30:10+00:00',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_duration-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_duration',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': <SensorDeviceClass.DURATION: 'duration'>,
|
||||
'original_icon': None,
|
||||
'original_name': 'Duration',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': None,
|
||||
'unique_id': 'באר יעקב אשקלון_duration',
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_duration-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'device_class': 'duration',
|
||||
'friendly_name': 'Mock Title Duration',
|
||||
'unit_of_measurement': <UnitOfTime.SECONDS: 's'>,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_duration',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1200',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_platform-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_platform',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Platform',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'platform',
|
||||
'unique_id': 'באר יעקב אשקלון_platform',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_platform-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'friendly_name': 'Mock Title Platform',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_platform',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_train_number-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_train_number',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Train number',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'train_number',
|
||||
'unique_id': 'באר יעקב אשקלון_train_number',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_train_number-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'friendly_name': 'Mock Title Train number',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_train_number',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '1234',
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_transfers-entry]
|
||||
EntityRegistryEntrySnapshot({
|
||||
'aliases': set({
|
||||
}),
|
||||
'area_id': None,
|
||||
'capabilities': None,
|
||||
'config_entry_id': <ANY>,
|
||||
'device_class': None,
|
||||
'device_id': <ANY>,
|
||||
'disabled_by': None,
|
||||
'domain': 'sensor',
|
||||
'entity_category': None,
|
||||
'entity_id': 'sensor.mock_title_transfers',
|
||||
'has_entity_name': True,
|
||||
'hidden_by': None,
|
||||
'icon': None,
|
||||
'id': <ANY>,
|
||||
'labels': set({
|
||||
}),
|
||||
'name': None,
|
||||
'options': dict({
|
||||
}),
|
||||
'original_device_class': None,
|
||||
'original_icon': None,
|
||||
'original_name': 'Transfers',
|
||||
'platform': 'israel_rail',
|
||||
'previous_unique_id': None,
|
||||
'supported_features': 0,
|
||||
'translation_key': 'transfers',
|
||||
'unique_id': 'באר יעקב אשקלון_transfers',
|
||||
'unit_of_measurement': None,
|
||||
})
|
||||
# ---
|
||||
# name: test_valid_config[sensor.mock_title_transfers-state]
|
||||
StateSnapshot({
|
||||
'attributes': ReadOnlyDict({
|
||||
'attribution': 'Data provided by Israel rail.',
|
||||
'friendly_name': 'Mock Title Transfers',
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'sensor.mock_title_transfers',
|
||||
'last_changed': <ANY>,
|
||||
'last_reported': <ANY>,
|
||||
'last_updated': <ANY>,
|
||||
'state': '0',
|
||||
})
|
||||
# ---
|
87
tests/components/israel_rail/test_config_flow.py
Normal file
87
tests/components/israel_rail/test_config_flow.py
Normal file
@ -0,0 +1,87 @@
|
||||
"""Define tests for the israel rail config flow."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from homeassistant.components.israel_rail import CONF_DESTINATION, CONF_START, DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from .conftest import VALID_CONFIG
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_create_entry(
|
||||
hass: HomeAssistant, mock_setup_entry: AsyncMock, mock_israelrail: AsyncMock
|
||||
) -> None:
|
||||
"""Test that the user step works."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
assert result["type"] is FlowResultType.FORM
|
||||
assert result["errors"] == {}
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
VALID_CONFIG,
|
||||
)
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "באר יעקב אשקלון"
|
||||
assert result["data"] == {
|
||||
CONF_START: "באר יעקב",
|
||||
CONF_DESTINATION: "אשקלון",
|
||||
}
|
||||
|
||||
|
||||
async def test_flow_fails(
|
||||
hass: HomeAssistant,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test that the user step fails."""
|
||||
mock_israelrail.query.side_effect = Exception("error")
|
||||
failed_result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_USER},
|
||||
data=VALID_CONFIG,
|
||||
)
|
||||
|
||||
assert failed_result["errors"] == {"base": "unknown"}
|
||||
assert failed_result["type"] is FlowResultType.FORM
|
||||
|
||||
mock_israelrail.query.side_effect = None
|
||||
|
||||
result = await hass.config_entries.flow.async_configure(
|
||||
failed_result["flow_id"],
|
||||
VALID_CONFIG,
|
||||
)
|
||||
|
||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "באר יעקב אשקלון"
|
||||
assert result["data"] == {
|
||||
CONF_START: "באר יעקב",
|
||||
CONF_DESTINATION: "אשקלון",
|
||||
}
|
||||
|
||||
|
||||
async def test_flow_already_configured(
|
||||
hass: HomeAssistant,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_setup_entry: AsyncMock,
|
||||
) -> None:
|
||||
"""Test that the user step fails when the entry is already configured."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_USER}
|
||||
)
|
||||
|
||||
result_aborted = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
VALID_CONFIG,
|
||||
)
|
||||
|
||||
assert result_aborted["type"] is FlowResultType.ABORT
|
||||
assert result_aborted["reason"] == "already_configured"
|
22
tests/components/israel_rail/test_init.py
Normal file
22
tests/components/israel_rail/test_init.py
Normal file
@ -0,0 +1,22 @@
|
||||
"""Test init of israel_rail integration."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_invalid_config(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_israelrail: AsyncMock,
|
||||
) -> None:
|
||||
"""Ensure nothing is created when config is wrong."""
|
||||
mock_israelrail.query.side_effect = Exception("error")
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert not hass.states.async_entity_ids("sensor")
|
||||
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
|
84
tests/components/israel_rail/test_sensor.py
Normal file
84
tests/components/israel_rail/test_sensor.py
Normal file
@ -0,0 +1,84 @@
|
||||
"""Tests for the israel_rail sensor."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.const import STATE_UNAVAILABLE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import goto_future, init_integration
|
||||
from .conftest import TRAINS, TRAINS_WRONG_FORMAT, get_time
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
|
||||
async def test_valid_config(
|
||||
hass: HomeAssistant,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Ensure everything starts correctly."""
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_update_train(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure the train data is updated."""
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
departure_sensor = hass.states.get("sensor.mock_title_departure")
|
||||
expected_time = get_time(10, 10)
|
||||
assert departure_sensor.state == expected_time
|
||||
|
||||
mock_israelrail.query.return_value = TRAINS[1:]
|
||||
|
||||
await goto_future(hass, freezer)
|
||||
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
departure_sensor = hass.states.get("sensor.mock_title_departure")
|
||||
expected_time = get_time(10, 20)
|
||||
assert departure_sensor.state == expected_time
|
||||
|
||||
|
||||
async def test_no_duration_wrong_date_format(
|
||||
hass: HomeAssistant,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure the duration is not set when there is no departure time."""
|
||||
mock_israelrail.query.return_value = TRAINS_WRONG_FORMAT
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
departure_sensor = hass.states.get("sensor.mock_title_train_number")
|
||||
assert departure_sensor.state == "1234"
|
||||
duration_sensor = hass.states.get("sensor.mock_title_duration")
|
||||
assert duration_sensor.state == "unknown"
|
||||
|
||||
|
||||
async def test_fail_query(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_israelrail: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Ensure the integration handles query failures."""
|
||||
await init_integration(hass, mock_config_entry)
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
mock_israelrail.query.side_effect = Exception("error")
|
||||
await goto_future(hass, freezer)
|
||||
assert len(hass.states.async_entity_ids()) == 7
|
||||
departure_sensor = hass.states.get("sensor.mock_title_departure")
|
||||
assert departure_sensor.state == STATE_UNAVAILABLE
|
Loading…
x
Reference in New Issue
Block a user