homeassistant 2025.9.0b2__py3-none-any.whl → 2025.9.0b3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. homeassistant/components/airgradient/translations/lt.json +2 -1
  2. homeassistant/components/alexa_devices/translations/cs.json +50 -0
  3. homeassistant/components/alexa_devices/translations/lt.json +47 -0
  4. homeassistant/components/alexa_devices/translations/nl.json +17 -0
  5. homeassistant/components/alexa_devices/translations/pt.json +50 -0
  6. homeassistant/components/alexa_devices/translations/sk.json +50 -0
  7. homeassistant/components/bang_olufsen/translations/lt.json +4 -0
  8. homeassistant/components/bayesian/strings.json +1 -1
  9. homeassistant/components/bayesian/translations/lt.json +23 -4
  10. homeassistant/components/bosch_alarm/translations/lt.json +3 -0
  11. homeassistant/components/bsblan/climate.py +4 -0
  12. homeassistant/components/bsblan/config_flow.py +1 -1
  13. homeassistant/components/bsblan/sensor.py +23 -3
  14. homeassistant/components/bsblan/water_heater.py +22 -2
  15. homeassistant/components/button/translations/lt.json +1 -0
  16. homeassistant/components/devolo_home_network/translations/lt.json +4 -0
  17. homeassistant/components/dominos/translations/lt.json +2 -0
  18. homeassistant/components/dsmr/translations/lt.json +2 -1
  19. homeassistant/components/dsmr_reader/translations/lt.json +1 -0
  20. homeassistant/components/fritz/translations/lt.json +3 -0
  21. homeassistant/components/fritzbox/translations/lt.json +3 -1
  22. homeassistant/components/fritzbox_callmonitor/translations/lt.json +2 -1
  23. homeassistant/components/frontend/manifest.json +1 -1
  24. homeassistant/components/google_travel_time/translations/lt.json +3 -2
  25. homeassistant/components/govee_light_local/__init__.py +6 -1
  26. homeassistant/components/govee_light_local/config_flow.py +7 -12
  27. homeassistant/components/govee_light_local/coordinator.py +2 -3
  28. homeassistant/components/habitica/translations/lt.json +2 -1
  29. homeassistant/components/hassio/translations/lt.json +25 -0
  30. homeassistant/components/homekit/__init__.py +2 -3
  31. homeassistant/components/homekit/services.yaml +7 -3
  32. homeassistant/components/homekit/strings.json +7 -1
  33. homeassistant/components/homekit/translations/cs.json +7 -1
  34. homeassistant/components/homekit/translations/de.json +6 -0
  35. homeassistant/components/homekit/translations/el.json +0 -1
  36. homeassistant/components/homekit/translations/en-GB.json +7 -1
  37. homeassistant/components/homekit/translations/en.json +7 -1
  38. homeassistant/components/homekit/translations/es.json +0 -1
  39. homeassistant/components/homekit/translations/et.json +7 -1
  40. homeassistant/components/homekit/translations/ga.json +0 -1
  41. homeassistant/components/homekit/translations/hu.json +0 -1
  42. homeassistant/components/homekit/translations/it.json +6 -0
  43. homeassistant/components/homekit/translations/ja.json +0 -1
  44. homeassistant/components/homekit/translations/lt.json +7 -1
  45. homeassistant/components/homekit/translations/nl.json +5 -0
  46. homeassistant/components/homekit/translations/pt.json +6 -0
  47. homeassistant/components/homekit/translations/ru.json +0 -1
  48. homeassistant/components/homekit/translations/sk.json +6 -0
  49. homeassistant/components/homekit/translations/sv.json +0 -1
  50. homeassistant/components/homekit/translations/tr.json +0 -1
  51. homeassistant/components/homekit/translations/vi.json +0 -1
  52. homeassistant/components/homekit/translations/zh-Hans.json +6 -1
  53. homeassistant/components/homekit/translations/zh-Hant.json +6 -0
  54. homeassistant/components/hue/translations/ar.json +0 -5
  55. homeassistant/components/hue/translations/bg.json +0 -1
  56. homeassistant/components/hue/translations/ca.json +0 -1
  57. homeassistant/components/hue/translations/cs.json +1 -1
  58. homeassistant/components/hue/translations/cy.json +0 -1
  59. homeassistant/components/hue/translations/da.json +0 -1
  60. homeassistant/components/hue/translations/de.json +1 -1
  61. homeassistant/components/hue/translations/el.json +1 -1
  62. homeassistant/components/hue/translations/en-GB.json +1 -1
  63. homeassistant/components/hue/translations/en.json +1 -1
  64. homeassistant/components/hue/translations/es-419.json +0 -1
  65. homeassistant/components/hue/translations/es.json +0 -1
  66. homeassistant/components/hue/translations/et.json +1 -1
  67. homeassistant/components/hue/translations/fi.json +0 -1
  68. homeassistant/components/hue/translations/fr.json +0 -1
  69. homeassistant/components/hue/translations/ga.json +0 -1
  70. homeassistant/components/hue/translations/he.json +0 -1
  71. homeassistant/components/hue/translations/hu.json +0 -1
  72. homeassistant/components/hue/translations/id.json +0 -1
  73. homeassistant/components/hue/translations/it.json +0 -1
  74. homeassistant/components/hue/translations/ja.json +0 -1
  75. homeassistant/components/hue/translations/ko.json +0 -1
  76. homeassistant/components/hue/translations/lb.json +0 -1
  77. homeassistant/components/hue/translations/lt.json +1 -1
  78. homeassistant/components/hue/translations/nb.json +0 -1
  79. homeassistant/components/hue/translations/nl.json +0 -1
  80. homeassistant/components/hue/translations/nn.json +0 -1
  81. homeassistant/components/hue/translations/pl.json +0 -1
  82. homeassistant/components/hue/translations/pt-BR.json +0 -1
  83. homeassistant/components/hue/translations/pt.json +1 -1
  84. homeassistant/components/hue/translations/ro.json +0 -1
  85. homeassistant/components/hue/translations/ru.json +0 -1
  86. homeassistant/components/hue/translations/sk.json +1 -1
  87. homeassistant/components/hue/translations/sl.json +0 -1
  88. homeassistant/components/hue/translations/sv.json +0 -1
  89. homeassistant/components/hue/translations/tr.json +0 -1
  90. homeassistant/components/hue/translations/uk.json +0 -1
  91. homeassistant/components/hue/translations/vi.json +0 -1
  92. homeassistant/components/hue/translations/zh-Hans.json +0 -1
  93. homeassistant/components/hue/translations/zh-Hant.json +1 -1
  94. homeassistant/components/hue/v2/group.py +67 -12
  95. homeassistant/components/hydrawise/translations/lt.json +2 -1
  96. homeassistant/components/imeon_inverter/const.py +1 -1
  97. homeassistant/components/imeon_inverter/translations/cs.json +1 -0
  98. homeassistant/components/imeon_inverter/translations/de.json +1 -0
  99. homeassistant/components/imeon_inverter/translations/el.json +5 -0
  100. homeassistant/components/imeon_inverter/translations/en-GB.json +1 -0
  101. homeassistant/components/imeon_inverter/translations/et.json +1 -0
  102. homeassistant/components/imeon_inverter/translations/it.json +5 -0
  103. homeassistant/components/imeon_inverter/translations/lt.json +13 -0
  104. homeassistant/components/imeon_inverter/translations/pt.json +1 -0
  105. homeassistant/components/imeon_inverter/translations/sk.json +1 -0
  106. homeassistant/components/imeon_inverter/translations/zh-Hant.json +1 -0
  107. homeassistant/components/insteon/translations/lt.json +6 -2
  108. homeassistant/components/iskra/manifest.json +1 -1
  109. homeassistant/components/lcn/translations/lt.json +2 -0
  110. homeassistant/components/leaone/translations/lt.json +2 -1
  111. homeassistant/components/lg_thinq/translations/lt.json +4 -0
  112. homeassistant/components/luftdaten/translations/lt.json +7 -0
  113. homeassistant/components/matter/translations/lt.json +3 -0
  114. homeassistant/components/mealie/manifest.json +1 -1
  115. homeassistant/components/miele/climate.py +20 -10
  116. homeassistant/components/miele/translations/lt.json +3 -0
  117. homeassistant/components/mqtt/translations/lt.json +8 -2
  118. homeassistant/components/music_assistant/media_browser.py +8 -8
  119. homeassistant/components/music_assistant/translations/lt.json +2 -1
  120. homeassistant/components/onkyo/config_flow.py +1 -1
  121. homeassistant/components/openweathermap/translations/lt.json +2 -1
  122. homeassistant/components/opower/translations/lt.json +1 -0
  123. homeassistant/components/pooldose/entity.py +1 -4
  124. homeassistant/components/pooldose/manifest.json +1 -1
  125. homeassistant/components/pooldose/quality_scale.yaml +13 -27
  126. homeassistant/components/qnap/translations/lt.json +1 -0
  127. homeassistant/components/random/translations/lt.json +1 -1
  128. homeassistant/components/risco/translations/lt.json +8 -0
  129. homeassistant/components/scrape/translations/lt.json +2 -1
  130. homeassistant/components/sensibo/translations/lt.json +5 -0
  131. homeassistant/components/sensor/translations/lt.json +5 -0
  132. homeassistant/components/somfy_mylink/translations/lt.json +4 -2
  133. homeassistant/components/sql/translations/lt.json +2 -1
  134. homeassistant/components/switchbot/translations/lt.json +3 -1
  135. homeassistant/components/tellduslive/translations/lt.json +4 -0
  136. homeassistant/components/template/translations/lt.json +40 -10
  137. homeassistant/components/teslemetry/translations/lt.json +1 -0
  138. homeassistant/components/tuya/translations/lt.json +5 -2
  139. homeassistant/components/unifiprotect/translations/lt.json +3 -0
  140. homeassistant/components/waze_travel_time/translations/lt.json +8 -0
  141. homeassistant/components/waze_travel_time/translations/nl.json +3 -0
  142. homeassistant/components/whois/translations/lt.json +1 -0
  143. homeassistant/components/xiaomi_ble/translations/lt.json +2 -1
  144. homeassistant/components/yalexs_ble/translations/cs.json +16 -1
  145. homeassistant/components/yalexs_ble/translations/el.json +11 -0
  146. homeassistant/components/yalexs_ble/translations/et.json +16 -1
  147. homeassistant/components/yalexs_ble/translations/lt.json +16 -1
  148. homeassistant/components/yalexs_ble/translations/pt.json +16 -1
  149. homeassistant/components/yalexs_ble/translations/sk.json +16 -1
  150. homeassistant/components/zwave_js/translations/lt.json +2 -0
  151. homeassistant/const.py +1 -1
  152. homeassistant/generated/config_flows.py +0 -1
  153. homeassistant/generated/integrations.json +0 -6
  154. homeassistant/package_constraints.txt +1 -1
  155. {homeassistant-2025.9.0b2.dist-info → homeassistant-2025.9.0b3.dist-info}/METADATA +1 -1
  156. {homeassistant-2025.9.0b2.dist-info → homeassistant-2025.9.0b3.dist-info}/RECORD +161 -205
  157. homeassistant/components/vulcan/__init__.py +0 -48
  158. homeassistant/components/vulcan/calendar.py +0 -176
  159. homeassistant/components/vulcan/config_flow.py +0 -327
  160. homeassistant/components/vulcan/const.py +0 -3
  161. homeassistant/components/vulcan/fetch_data.py +0 -98
  162. homeassistant/components/vulcan/manifest.json +0 -9
  163. homeassistant/components/vulcan/register.py +0 -12
  164. homeassistant/components/vulcan/strings.json +0 -62
  165. homeassistant/components/vulcan/translations/bg.json +0 -37
  166. homeassistant/components/vulcan/translations/ca.json +0 -59
  167. homeassistant/components/vulcan/translations/cs.json +0 -62
  168. homeassistant/components/vulcan/translations/de.json +0 -62
  169. homeassistant/components/vulcan/translations/el.json +0 -61
  170. homeassistant/components/vulcan/translations/en-GB.json +0 -62
  171. homeassistant/components/vulcan/translations/en.json +0 -62
  172. homeassistant/components/vulcan/translations/es.json +0 -62
  173. homeassistant/components/vulcan/translations/et.json +0 -62
  174. homeassistant/components/vulcan/translations/fi.json +0 -59
  175. homeassistant/components/vulcan/translations/fr.json +0 -61
  176. homeassistant/components/vulcan/translations/ga.json +0 -9
  177. homeassistant/components/vulcan/translations/gl.json +0 -7
  178. homeassistant/components/vulcan/translations/he.json +0 -29
  179. homeassistant/components/vulcan/translations/hu.json +0 -62
  180. homeassistant/components/vulcan/translations/id.json +0 -59
  181. homeassistant/components/vulcan/translations/it.json +0 -60
  182. homeassistant/components/vulcan/translations/ja.json +0 -62
  183. homeassistant/components/vulcan/translations/ko.json +0 -59
  184. homeassistant/components/vulcan/translations/lt.json +0 -62
  185. homeassistant/components/vulcan/translations/mk.json +0 -11
  186. homeassistant/components/vulcan/translations/nb.json +0 -59
  187. homeassistant/components/vulcan/translations/nl.json +0 -60
  188. homeassistant/components/vulcan/translations/pl.json +0 -57
  189. homeassistant/components/vulcan/translations/pt-BR.json +0 -58
  190. homeassistant/components/vulcan/translations/pt.json +0 -62
  191. homeassistant/components/vulcan/translations/ro.json +0 -45
  192. homeassistant/components/vulcan/translations/ru.json +0 -62
  193. homeassistant/components/vulcan/translations/sk.json +0 -62
  194. homeassistant/components/vulcan/translations/sl.json +0 -21
  195. homeassistant/components/vulcan/translations/sv.json +0 -62
  196. homeassistant/components/vulcan/translations/tr.json +0 -62
  197. homeassistant/components/vulcan/translations/uk.json +0 -52
  198. homeassistant/components/vulcan/translations/vi.json +0 -15
  199. homeassistant/components/vulcan/translations/zh-Hans.json +0 -62
  200. homeassistant/components/vulcan/translations/zh-Hant.json +0 -62
  201. {homeassistant-2025.9.0b2.dist-info → homeassistant-2025.9.0b3.dist-info}/WHEEL +0 -0
  202. {homeassistant-2025.9.0b2.dist-info → homeassistant-2025.9.0b3.dist-info}/entry_points.txt +0 -0
  203. {homeassistant-2025.9.0b2.dist-info → homeassistant-2025.9.0b3.dist-info}/licenses/LICENSE.md +0 -0
  204. {homeassistant-2025.9.0b2.dist-info → homeassistant-2025.9.0b3.dist-info}/licenses/homeassistant/backports/LICENSE.Python +0 -0
  205. {homeassistant-2025.9.0b2.dist-info → homeassistant-2025.9.0b3.dist-info}/top_level.txt +0 -0
@@ -1,48 +0,0 @@
1
- """The Vulcan component."""
2
-
3
- from aiohttp import ClientConnectorError
4
- from vulcan import Account, Keystore, UnauthorizedCertificateException, Vulcan
5
-
6
- from homeassistant.config_entries import ConfigEntry
7
- from homeassistant.const import Platform
8
- from homeassistant.core import HomeAssistant
9
- from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
10
- from homeassistant.helpers.aiohttp_client import async_get_clientsession
11
-
12
- from .const import DOMAIN
13
-
14
- PLATFORMS = [Platform.CALENDAR]
15
-
16
-
17
- async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
18
- """Set up Uonet+ Vulcan integration."""
19
- hass.data.setdefault(DOMAIN, {})
20
- try:
21
- keystore = Keystore.load(entry.data["keystore"])
22
- account = Account.load(entry.data["account"])
23
- client = Vulcan(keystore, account, async_get_clientsession(hass))
24
- await client.select_student()
25
- students = await client.get_students()
26
- for student in students:
27
- if str(student.pupil.id) == str(entry.data["student_id"]):
28
- client.student = student
29
- break
30
- except UnauthorizedCertificateException as err:
31
- raise ConfigEntryAuthFailed("The certificate is not authorized.") from err
32
- except ClientConnectorError as err:
33
- raise ConfigEntryNotReady(
34
- f"Connection error - please check your internet connection: {err}"
35
- ) from err
36
- hass.data[DOMAIN][entry.entry_id] = client
37
-
38
- await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
39
-
40
- return True
41
-
42
-
43
- async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
44
- """Unload a config entry."""
45
- if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
46
- hass.data[DOMAIN].pop(entry.entry_id)
47
-
48
- return unload_ok
@@ -1,176 +0,0 @@
1
- """Support for Vulcan Calendar platform."""
2
-
3
- from __future__ import annotations
4
-
5
- from datetime import date, datetime, timedelta
6
- import logging
7
- from typing import cast
8
- from zoneinfo import ZoneInfo
9
-
10
- from aiohttp import ClientConnectorError
11
- from vulcan import UnauthorizedCertificateException
12
-
13
- from homeassistant.components.calendar import (
14
- ENTITY_ID_FORMAT,
15
- CalendarEntity,
16
- CalendarEvent,
17
- )
18
- from homeassistant.config_entries import ConfigEntry
19
- from homeassistant.core import HomeAssistant
20
- from homeassistant.exceptions import ConfigEntryAuthFailed
21
- from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
22
- from homeassistant.helpers.entity import generate_entity_id
23
- from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
24
-
25
- from . import DOMAIN
26
- from .fetch_data import get_lessons, get_student_info
27
-
28
- _LOGGER = logging.getLogger(__name__)
29
-
30
-
31
- async def async_setup_entry(
32
- hass: HomeAssistant,
33
- config_entry: ConfigEntry,
34
- async_add_entities: AddConfigEntryEntitiesCallback,
35
- ) -> None:
36
- """Set up the calendar platform for entity."""
37
- client = hass.data[DOMAIN][config_entry.entry_id]
38
- data = {
39
- "student_info": await get_student_info(
40
- client, config_entry.data.get("student_id")
41
- ),
42
- }
43
- async_add_entities(
44
- [
45
- VulcanCalendarEntity(
46
- client,
47
- data,
48
- generate_entity_id(
49
- ENTITY_ID_FORMAT,
50
- f"vulcan_calendar_{data['student_info']['full_name']}",
51
- hass=hass,
52
- ),
53
- )
54
- ],
55
- )
56
-
57
-
58
- class VulcanCalendarEntity(CalendarEntity):
59
- """A calendar entity."""
60
-
61
- _attr_has_entity_name = True
62
- _attr_translation_key = "calendar"
63
-
64
- def __init__(self, client, data, entity_id) -> None:
65
- """Create the Calendar entity."""
66
- self._event: CalendarEvent | None = None
67
- self.client = client
68
- self.entity_id = entity_id
69
- student_info = data["student_info"]
70
- self._attr_unique_id = f"vulcan_calendar_{student_info['id']}"
71
- self._attr_device_info = DeviceInfo(
72
- identifiers={(DOMAIN, f"calendar_{student_info['id']}")},
73
- entry_type=DeviceEntryType.SERVICE,
74
- name=cast(str, student_info["full_name"]),
75
- model=(
76
- f"{student_info['full_name']} -"
77
- f" {student_info['class']} {student_info['school']}"
78
- ),
79
- manufacturer="Uonet +",
80
- configuration_url=(
81
- f"https://uonetplus.vulcan.net.pl/{student_info['symbol']}"
82
- ),
83
- )
84
-
85
- @property
86
- def event(self) -> CalendarEvent | None:
87
- """Return the next upcoming event."""
88
- return self._event
89
-
90
- async def async_get_events(
91
- self, hass: HomeAssistant, start_date: datetime, end_date: datetime
92
- ) -> list[CalendarEvent]:
93
- """Get all events in a specific time frame."""
94
- try:
95
- events = await get_lessons(
96
- self.client,
97
- date_from=start_date,
98
- date_to=end_date,
99
- )
100
- except UnauthorizedCertificateException as err:
101
- raise ConfigEntryAuthFailed(
102
- "The certificate is not authorized, please authorize integration again"
103
- ) from err
104
- except ClientConnectorError as err:
105
- if self.available:
106
- _LOGGER.warning(
107
- "Connection error - please check your internet connection: %s", err
108
- )
109
- events = []
110
-
111
- event_list = []
112
- for item in events:
113
- event = CalendarEvent(
114
- start=datetime.combine(
115
- item["date"], item["time"].from_, ZoneInfo("Europe/Warsaw")
116
- ),
117
- end=datetime.combine(
118
- item["date"], item["time"].to, ZoneInfo("Europe/Warsaw")
119
- ),
120
- summary=item["lesson"],
121
- location=item["room"],
122
- description=item["teacher"],
123
- )
124
-
125
- event_list.append(event)
126
-
127
- return event_list
128
-
129
- async def async_update(self) -> None:
130
- """Get the latest data."""
131
-
132
- try:
133
- events = await get_lessons(self.client)
134
-
135
- if not self.available:
136
- _LOGGER.warning("Restored connection with API")
137
- self._attr_available = True
138
-
139
- if events == []:
140
- events = await get_lessons(
141
- self.client,
142
- date_to=date.today() + timedelta(days=7),
143
- )
144
- if events == []:
145
- self._event = None
146
- return
147
- except UnauthorizedCertificateException as err:
148
- raise ConfigEntryAuthFailed(
149
- "The certificate is not authorized, please authorize integration again"
150
- ) from err
151
- except ClientConnectorError as err:
152
- if self.available:
153
- _LOGGER.warning(
154
- "Connection error - please check your internet connection: %s", err
155
- )
156
- self._attr_available = False
157
- return
158
-
159
- new_event = min(
160
- events,
161
- key=lambda d: (
162
- datetime.combine(d["date"], d["time"].to) < datetime.now(),
163
- abs(datetime.combine(d["date"], d["time"].to) - datetime.now()),
164
- ),
165
- )
166
- self._event = CalendarEvent(
167
- start=datetime.combine(
168
- new_event["date"], new_event["time"].from_, ZoneInfo("Europe/Warsaw")
169
- ),
170
- end=datetime.combine(
171
- new_event["date"], new_event["time"].to, ZoneInfo("Europe/Warsaw")
172
- ),
173
- summary=new_event["lesson"],
174
- location=new_event["room"],
175
- description=new_event["teacher"],
176
- )
@@ -1,327 +0,0 @@
1
- """Adds config flow for Vulcan."""
2
-
3
- from collections.abc import Mapping
4
- import logging
5
- from typing import TYPE_CHECKING, Any
6
-
7
- from aiohttp import ClientConnectionError
8
- import voluptuous as vol
9
- from vulcan import (
10
- Account,
11
- ExpiredTokenException,
12
- InvalidPINException,
13
- InvalidSymbolException,
14
- InvalidTokenException,
15
- Keystore,
16
- UnauthorizedCertificateException,
17
- Vulcan,
18
- )
19
- from vulcan.model import Student
20
-
21
- from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
22
- from homeassistant.const import CONF_PIN, CONF_REGION, CONF_TOKEN
23
- from homeassistant.helpers.aiohttp_client import async_get_clientsession
24
-
25
- from . import DOMAIN
26
- from .register import register
27
-
28
- _LOGGER = logging.getLogger(__name__)
29
-
30
- LOGIN_SCHEMA = {
31
- vol.Required(CONF_TOKEN): str,
32
- vol.Required(CONF_REGION): str,
33
- vol.Required(CONF_PIN): str,
34
- }
35
-
36
-
37
- class VulcanFlowHandler(ConfigFlow, domain=DOMAIN):
38
- """Handle a Uonet+ Vulcan config flow."""
39
-
40
- VERSION = 1
41
-
42
- account: Account
43
- keystore: Keystore
44
-
45
- def __init__(self) -> None:
46
- """Initialize config flow."""
47
- self.students: list[Student] | None = None
48
-
49
- async def async_step_user(
50
- self, user_input: dict[str, Any] | None = None
51
- ) -> ConfigFlowResult:
52
- """Handle config flow."""
53
- if self._async_current_entries():
54
- return await self.async_step_add_next_config_entry()
55
-
56
- return await self.async_step_auth()
57
-
58
- async def async_step_auth(
59
- self,
60
- user_input: dict[str, str] | None = None,
61
- errors: dict[str, str] | None = None,
62
- ) -> ConfigFlowResult:
63
- """Authorize integration."""
64
-
65
- if user_input is not None:
66
- try:
67
- credentials = await register(
68
- user_input[CONF_TOKEN],
69
- user_input[CONF_REGION],
70
- user_input[CONF_PIN],
71
- )
72
- except InvalidSymbolException:
73
- errors = {"base": "invalid_symbol"}
74
- except InvalidTokenException:
75
- errors = {"base": "invalid_token"}
76
- except InvalidPINException:
77
- errors = {"base": "invalid_pin"}
78
- except ExpiredTokenException:
79
- errors = {"base": "expired_token"}
80
- except ClientConnectionError as err:
81
- errors = {"base": "cannot_connect"}
82
- _LOGGER.error("Connection error: %s", err)
83
- except Exception:
84
- _LOGGER.exception("Unexpected exception")
85
- errors = {"base": "unknown"}
86
- if not errors:
87
- account = credentials["account"]
88
- keystore = credentials["keystore"]
89
- client = Vulcan(keystore, account, async_get_clientsession(self.hass))
90
- students = await client.get_students()
91
-
92
- if len(students) > 1:
93
- self.account = account
94
- self.keystore = keystore
95
- self.students = students
96
- return await self.async_step_select_student()
97
- student = students[0]
98
- await self.async_set_unique_id(str(student.pupil.id))
99
- self._abort_if_unique_id_configured()
100
- return self.async_create_entry(
101
- title=f"{student.pupil.first_name} {student.pupil.last_name}",
102
- data={
103
- "student_id": str(student.pupil.id),
104
- "keystore": keystore.as_dict,
105
- "account": account.as_dict,
106
- },
107
- )
108
-
109
- return self.async_show_form(
110
- step_id="auth",
111
- data_schema=vol.Schema(LOGIN_SCHEMA),
112
- errors=errors,
113
- )
114
-
115
- async def async_step_select_student(
116
- self, user_input: dict[str, str] | None = None
117
- ) -> ConfigFlowResult:
118
- """Allow user to select student."""
119
- errors: dict[str, str] = {}
120
- students: dict[str, str] = {}
121
- if self.students is not None:
122
- for student in self.students:
123
- students[str(student.pupil.id)] = (
124
- f"{student.pupil.first_name} {student.pupil.last_name}"
125
- )
126
- if user_input is not None:
127
- if TYPE_CHECKING:
128
- assert self.keystore is not None
129
- student_id = user_input["student"]
130
- await self.async_set_unique_id(str(student_id))
131
- self._abort_if_unique_id_configured()
132
- return self.async_create_entry(
133
- title=students[student_id],
134
- data={
135
- "student_id": str(student_id),
136
- "keystore": self.keystore.as_dict,
137
- "account": self.account.as_dict,
138
- },
139
- )
140
-
141
- return self.async_show_form(
142
- step_id="select_student",
143
- data_schema=vol.Schema({vol.Required("student"): vol.In(students)}),
144
- errors=errors,
145
- )
146
-
147
- async def async_step_select_saved_credentials(
148
- self,
149
- user_input: dict[str, str] | None = None,
150
- errors: dict[str, str] | None = None,
151
- ) -> ConfigFlowResult:
152
- """Allow user to select saved credentials."""
153
-
154
- credentials: dict[str, Any] = {}
155
- for entry in self.hass.config_entries.async_entries(DOMAIN):
156
- credentials[entry.entry_id] = entry.data["account"]["UserName"]
157
-
158
- if user_input is not None:
159
- existing_entry = self.hass.config_entries.async_get_entry(
160
- user_input["credentials"]
161
- )
162
- if TYPE_CHECKING:
163
- assert existing_entry is not None
164
- keystore = Keystore.load(existing_entry.data["keystore"])
165
- account = Account.load(existing_entry.data["account"])
166
- client = Vulcan(keystore, account, async_get_clientsession(self.hass))
167
- try:
168
- students = await client.get_students()
169
- except UnauthorizedCertificateException:
170
- return await self.async_step_auth(
171
- errors={"base": "expired_credentials"}
172
- )
173
- except ClientConnectionError as err:
174
- _LOGGER.error("Connection error: %s", err)
175
- return await self.async_step_select_saved_credentials(
176
- errors={"base": "cannot_connect"}
177
- )
178
- except Exception:
179
- _LOGGER.exception("Unexpected exception")
180
- return await self.async_step_auth(errors={"base": "unknown"})
181
- if len(students) == 1:
182
- student = students[0]
183
- await self.async_set_unique_id(str(student.pupil.id))
184
- self._abort_if_unique_id_configured()
185
- return self.async_create_entry(
186
- title=f"{student.pupil.first_name} {student.pupil.last_name}",
187
- data={
188
- "student_id": str(student.pupil.id),
189
- "keystore": keystore.as_dict,
190
- "account": account.as_dict,
191
- },
192
- )
193
- self.account = account
194
- self.keystore = keystore
195
- self.students = students
196
- return await self.async_step_select_student()
197
-
198
- data_schema = {
199
- vol.Required(
200
- "credentials",
201
- ): vol.In(credentials),
202
- }
203
- return self.async_show_form(
204
- step_id="select_saved_credentials",
205
- data_schema=vol.Schema(data_schema),
206
- errors=errors,
207
- )
208
-
209
- async def async_step_add_next_config_entry(
210
- self, user_input: dict[str, bool] | None = None
211
- ) -> ConfigFlowResult:
212
- """Flow initialized when user is adding next entry of that integration."""
213
-
214
- existing_entries = self.hass.config_entries.async_entries(DOMAIN)
215
-
216
- errors: dict[str, str] = {}
217
-
218
- if user_input is not None:
219
- if not user_input["use_saved_credentials"]:
220
- return await self.async_step_auth()
221
- if len(existing_entries) > 1:
222
- return await self.async_step_select_saved_credentials()
223
- keystore = Keystore.load(existing_entries[0].data["keystore"])
224
- account = Account.load(existing_entries[0].data["account"])
225
- client = Vulcan(keystore, account, async_get_clientsession(self.hass))
226
- students = await client.get_students()
227
- existing_entry_ids = [
228
- entry.data["student_id"] for entry in existing_entries
229
- ]
230
- new_students = [
231
- student
232
- for student in students
233
- if str(student.pupil.id) not in existing_entry_ids
234
- ]
235
- if not new_students:
236
- return self.async_abort(reason="all_student_already_configured")
237
- if len(new_students) == 1:
238
- await self.async_set_unique_id(str(new_students[0].pupil.id))
239
- self._abort_if_unique_id_configured()
240
- return self.async_create_entry(
241
- title=(
242
- f"{new_students[0].pupil.first_name} {new_students[0].pupil.last_name}"
243
- ),
244
- data={
245
- "student_id": str(new_students[0].pupil.id),
246
- "keystore": keystore.as_dict,
247
- "account": account.as_dict,
248
- },
249
- )
250
- self.account = account
251
- self.keystore = keystore
252
- self.students = new_students
253
- return await self.async_step_select_student()
254
-
255
- data_schema = {
256
- vol.Required("use_saved_credentials", default=True): bool,
257
- }
258
- return self.async_show_form(
259
- step_id="add_next_config_entry",
260
- data_schema=vol.Schema(data_schema),
261
- errors=errors,
262
- )
263
-
264
- async def async_step_reauth(
265
- self, entry_data: Mapping[str, Any]
266
- ) -> ConfigFlowResult:
267
- """Perform reauth upon an API authentication error."""
268
- return await self.async_step_reauth_confirm()
269
-
270
- async def async_step_reauth_confirm(
271
- self, user_input: dict[str, str] | None = None
272
- ) -> ConfigFlowResult:
273
- """Reauthorize integration."""
274
- errors = {}
275
- if user_input is not None:
276
- try:
277
- credentials = await register(
278
- user_input[CONF_TOKEN],
279
- user_input[CONF_REGION],
280
- user_input[CONF_PIN],
281
- )
282
- except InvalidSymbolException:
283
- errors = {"base": "invalid_symbol"}
284
- except InvalidTokenException:
285
- errors = {"base": "invalid_token"}
286
- except InvalidPINException:
287
- errors = {"base": "invalid_pin"}
288
- except ExpiredTokenException:
289
- errors = {"base": "expired_token"}
290
- except ClientConnectionError as err:
291
- errors["base"] = "cannot_connect"
292
- _LOGGER.error("Connection error: %s", err)
293
- except Exception:
294
- _LOGGER.exception("Unexpected exception")
295
- errors["base"] = "unknown"
296
- if not errors:
297
- account = credentials["account"]
298
- keystore = credentials["keystore"]
299
- client = Vulcan(keystore, account, async_get_clientsession(self.hass))
300
- students = await client.get_students()
301
- existing_entries = self.hass.config_entries.async_entries(DOMAIN)
302
- matching_entries = False
303
- for student in students:
304
- for entry in existing_entries:
305
- if str(student.pupil.id) == str(entry.data["student_id"]):
306
- self.hass.config_entries.async_update_entry(
307
- entry,
308
- title=(
309
- f"{student.pupil.first_name} {student.pupil.last_name}"
310
- ),
311
- data={
312
- "student_id": str(student.pupil.id),
313
- "keystore": keystore.as_dict,
314
- "account": account.as_dict,
315
- },
316
- )
317
- await self.hass.config_entries.async_reload(entry.entry_id)
318
- matching_entries = True
319
- if not matching_entries:
320
- return self.async_abort(reason="no_matching_entries")
321
- return self.async_abort(reason="reauth_successful")
322
-
323
- return self.async_show_form(
324
- step_id="reauth_confirm",
325
- data_schema=vol.Schema(LOGIN_SCHEMA),
326
- errors=errors,
327
- )
@@ -1,3 +0,0 @@
1
- """Constants for the Vulcan integration."""
2
-
3
- DOMAIN = "vulcan"
@@ -1,98 +0,0 @@
1
- """Support for fetching Vulcan data."""
2
-
3
-
4
- async def get_lessons(client, date_from=None, date_to=None):
5
- """Support for fetching Vulcan lessons."""
6
- changes = {}
7
- list_ans = []
8
- async for lesson in await client.data.get_changed_lessons(
9
- date_from=date_from, date_to=date_to
10
- ):
11
- temp_dict = {}
12
- _id = str(lesson.id)
13
- temp_dict["id"] = lesson.id
14
- temp_dict["number"] = lesson.time.position if lesson.time is not None else None
15
- temp_dict["lesson"] = (
16
- lesson.subject.name if lesson.subject is not None else None
17
- )
18
- temp_dict["room"] = lesson.room.code if lesson.room is not None else None
19
- temp_dict["changes"] = lesson.changes
20
- temp_dict["note"] = lesson.note
21
- temp_dict["reason"] = lesson.reason
22
- temp_dict["event"] = lesson.event
23
- temp_dict["group"] = lesson.group
24
- temp_dict["teacher"] = (
25
- lesson.teacher.display_name if lesson.teacher is not None else None
26
- )
27
- temp_dict["from_to"] = (
28
- lesson.time.displayed_time if lesson.time is not None else None
29
- )
30
-
31
- changes[str(_id)] = temp_dict
32
-
33
- async for lesson in await client.data.get_lessons(
34
- date_from=date_from, date_to=date_to
35
- ):
36
- temp_dict = {}
37
- temp_dict["id"] = lesson.id
38
- temp_dict["number"] = lesson.time.position
39
- temp_dict["time"] = lesson.time
40
- temp_dict["date"] = lesson.date.date
41
- temp_dict["lesson"] = (
42
- lesson.subject.name if lesson.subject is not None else None
43
- )
44
- if lesson.room is not None:
45
- temp_dict["room"] = lesson.room.code
46
- else:
47
- temp_dict["room"] = "-"
48
- temp_dict["visible"] = lesson.visible
49
- temp_dict["changes"] = lesson.changes
50
- temp_dict["group"] = lesson.group
51
- temp_dict["reason"] = None
52
- temp_dict["teacher"] = (
53
- lesson.teacher.display_name if lesson.teacher is not None else None
54
- )
55
- temp_dict["from_to"] = (
56
- lesson.time.displayed_time if lesson.time is not None else None
57
- )
58
- if temp_dict["changes"] is None:
59
- temp_dict["changes"] = ""
60
- elif temp_dict["changes"].type == 1:
61
- temp_dict["lesson"] = f"Lekcja odwołana ({temp_dict['lesson']})"
62
- temp_dict["changes_info"] = f"Lekcja odwołana ({temp_dict['lesson']})"
63
- if str(temp_dict["changes"].id) in changes:
64
- temp_dict["reason"] = changes[str(temp_dict["changes"].id)]["reason"]
65
- elif temp_dict["changes"].type == 2:
66
- temp_dict["lesson"] = f"{temp_dict['lesson']} (Zastępstwo)"
67
- temp_dict["teacher"] = changes[str(temp_dict["changes"].id)]["teacher"]
68
- if str(temp_dict["changes"].id) in changes:
69
- temp_dict["teacher"] = changes[str(temp_dict["changes"].id)]["teacher"]
70
- temp_dict["reason"] = changes[str(temp_dict["changes"].id)]["reason"]
71
- elif temp_dict["changes"].type == 4:
72
- temp_dict["lesson"] = f"Lekcja odwołana ({temp_dict['lesson']})"
73
- if str(temp_dict["changes"].id) in changes:
74
- temp_dict["reason"] = changes[str(temp_dict["changes"].id)]["reason"]
75
- if temp_dict["visible"]:
76
- list_ans.append(temp_dict)
77
-
78
- return list_ans
79
-
80
-
81
- async def get_student_info(client, student_id):
82
- """Support for fetching Student info by student id."""
83
- student_info = {}
84
- for student in await client.get_students():
85
- if str(student.pupil.id) == str(student_id):
86
- student_info["first_name"] = student.pupil.first_name
87
- if student.pupil.second_name:
88
- student_info["second_name"] = student.pupil.second_name
89
- student_info["last_name"] = student.pupil.last_name
90
- student_info["full_name"] = (
91
- f"{student.pupil.first_name} {student.pupil.last_name}"
92
- )
93
- student_info["id"] = student.pupil.id
94
- student_info["class"] = student.class_
95
- student_info["school"] = student.school.name
96
- student_info["symbol"] = student.symbol
97
- break
98
- return student_info
@@ -1,9 +0,0 @@
1
- {
2
- "domain": "vulcan",
3
- "name": "Uonet+ Vulcan",
4
- "codeowners": ["@Antoni-Czaplicki"],
5
- "config_flow": true,
6
- "documentation": "https://www.home-assistant.io/integrations/vulcan",
7
- "iot_class": "cloud_polling",
8
- "requirements": ["vulcan-api==2.4.2"]
9
- }
@@ -1,12 +0,0 @@
1
- """Support for register Vulcan account."""
2
-
3
- from typing import Any
4
-
5
- from vulcan import Account, Keystore
6
-
7
-
8
- async def register(token: str, symbol: str, pin: str) -> dict[str, Any]:
9
- """Register integration and save credentials."""
10
- keystore = await Keystore.create(device_model="Home Assistant")
11
- account = await Account.register(keystore, token, symbol, pin)
12
- return {"account": account, "keystore": keystore}