Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The swiss_public_transport component."""
2 
3 import logging
4 
5 from opendata_transport import OpendataTransport
6 from opendata_transport.exceptions import (
7  OpendataTransportConnectionError,
8  OpendataTransportError,
9 )
10 
11 from homeassistant.const import Platform
12 from homeassistant.core import HomeAssistant
13 from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
14 from homeassistant.helpers import (
15  config_validation as cv,
16  device_registry as dr,
17  entity_registry as er,
18 )
19 from homeassistant.helpers.aiohttp_client import async_get_clientsession
20 from homeassistant.helpers.typing import ConfigType
21 
22 from .const import (
23  CONF_DESTINATION,
24  CONF_START,
25  CONF_TIME_FIXED,
26  CONF_TIME_OFFSET,
27  CONF_TIME_STATION,
28  CONF_VIA,
29  DEFAULT_TIME_STATION,
30  DOMAIN,
31  PLACEHOLDERS,
32 )
33 from .coordinator import (
34  SwissPublicTransportConfigEntry,
35  SwissPublicTransportDataUpdateCoordinator,
36 )
37 from .helper import offset_opendata, unique_id_from_config
38 from .services import setup_services
39 
40 _LOGGER = logging.getLogger(__name__)
41 
42 
43 PLATFORMS: list[Platform] = [Platform.SENSOR]
44 
45 CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
46 
47 
48 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
49  """Set up the Swiss public transport component."""
50  setup_services(hass)
51  return True
52 
53 
55  hass: HomeAssistant, entry: SwissPublicTransportConfigEntry
56 ) -> bool:
57  """Set up Swiss public transport from a config entry."""
58  config = entry.data
59 
60  start = config[CONF_START]
61  destination = config[CONF_DESTINATION]
62 
63  time_offset: dict[str, int] | None = config.get(CONF_TIME_OFFSET)
64 
65  session = async_get_clientsession(hass)
66  opendata = OpendataTransport(
67  start,
68  destination,
69  session,
70  via=config.get(CONF_VIA),
71  time=config.get(CONF_TIME_FIXED),
72  isArrivalTime=config.get(CONF_TIME_STATION, DEFAULT_TIME_STATION) == "arrival",
73  )
74  if time_offset:
75  offset_opendata(opendata, time_offset)
76 
77  try:
78  await opendata.async_get_data()
79  except OpendataTransportConnectionError as e:
80  raise ConfigEntryNotReady(
81  translation_domain=DOMAIN,
82  translation_key="request_timeout",
83  translation_placeholders={
84  "config_title": entry.title,
85  "error": str(e),
86  },
87  ) from e
88  except OpendataTransportError as e:
89  raise ConfigEntryError(
90  translation_domain=DOMAIN,
91  translation_key="invalid_data",
92  translation_placeholders={
93  **PLACEHOLDERS,
94  "config_title": entry.title,
95  "error": str(e),
96  },
97  ) from e
98 
99  coordinator = SwissPublicTransportDataUpdateCoordinator(hass, opendata, time_offset)
100  await coordinator.async_config_entry_first_refresh()
101  entry.runtime_data = coordinator
102 
103  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
104  return True
105 
106 
108  hass: HomeAssistant, entry: SwissPublicTransportConfigEntry
109 ) -> bool:
110  """Unload a config entry."""
111  return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
112 
113 
115  hass: HomeAssistant, config_entry: SwissPublicTransportConfigEntry
116 ) -> bool:
117  """Migrate config entry."""
118  _LOGGER.debug("Migrating from version %s", config_entry.version)
119 
120  if config_entry.version > 3:
121  # This means the user has downgraded from a future version
122  return False
123 
124  if config_entry.version == 1 and config_entry.minor_version == 1:
125  # Remove wrongly registered devices and entries
126  new_unique_id = unique_id_from_config(config_entry.data)
127  entity_registry = er.async_get(hass)
128  device_registry = dr.async_get(hass)
129  device_entries = dr.async_entries_for_config_entry(
130  device_registry, config_entry_id=config_entry.entry_id
131  )
132  for dev in device_entries:
133  device_registry.async_update_device(
134  dev.id, remove_config_entry_id=config_entry.entry_id
135  )
136 
137  entity_id = entity_registry.async_get_entity_id(
138  Platform.SENSOR, DOMAIN, "None_departure"
139  )
140  if entity_id:
141  entity_registry.async_update_entity(
142  entity_id=entity_id,
143  new_unique_id=f"{new_unique_id}_departure",
144  )
145  _LOGGER.debug(
146  "Faulty entity with unique_id 'None_departure' migrated to new unique_id '%s'",
147  f"{new_unique_id}_departure",
148  )
149 
150  # Set a valid unique id for config entries
151  hass.config_entries.async_update_entry(
152  config_entry, unique_id=new_unique_id, minor_version=2
153  )
154 
155  if config_entry.version < 3:
156  # Via stations and time/offset settings now available, which are not backwards compatible if used, changes unique id
157  hass.config_entries.async_update_entry(config_entry, version=3, minor_version=1)
158 
159  _LOGGER.debug(
160  "Migration to version %s.%s successful",
161  config_entry.version,
162  config_entry.minor_version,
163  )
164 
165  return True
None offset_opendata(OpendataTransport opendata, dict[str, int] offset)
Definition: helper.py:22
str unique_id_from_config(MappingProxyType[str, Any]|dict[str, Any] config)
Definition: helper.py:39
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:48
bool async_migrate_entry(HomeAssistant hass, SwissPublicTransportConfigEntry config_entry)
Definition: __init__.py:116
bool async_setup_entry(HomeAssistant hass, SwissPublicTransportConfigEntry entry)
Definition: __init__.py:56
bool async_unload_entry(HomeAssistant hass, SwissPublicTransportConfigEntry entry)
Definition: __init__.py:109
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)