homeassistant 2025.9.0b0__py3-none-any.whl → 2025.9.0b1__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 (214) hide show
  1. homeassistant/components/ai_task/translations/cs.json +25 -3
  2. homeassistant/components/ai_task/translations/fr.json +22 -0
  3. homeassistant/components/ai_task/translations/lt.json +22 -0
  4. homeassistant/components/ai_task/translations/pt.json +22 -0
  5. homeassistant/components/ai_task/translations/sk.json +1 -1
  6. homeassistant/components/airgradient/translations/pt.json +1 -0
  7. homeassistant/components/airos/manifest.json +1 -1
  8. homeassistant/components/airos/translations/lt.json +18 -1
  9. homeassistant/components/aladdin_connect/translations/cs.json +30 -1
  10. homeassistant/components/aladdin_connect/translations/et.json +30 -1
  11. homeassistant/components/aladdin_connect/translations/fr.json +10 -0
  12. homeassistant/components/aladdin_connect/translations/ga.json +30 -1
  13. homeassistant/components/aladdin_connect/translations/lt.json +30 -1
  14. homeassistant/components/aladdin_connect/translations/nl.json +28 -0
  15. homeassistant/components/aladdin_connect/translations/pt.json +30 -1
  16. homeassistant/components/aladdin_connect/translations/sk.json +30 -1
  17. homeassistant/components/aladdin_connect/translations/zh-Hans.json +30 -1
  18. homeassistant/components/aladdin_connect/translations/zh-Hant.json +30 -1
  19. homeassistant/components/alarm_control_panel/translations/cs.json +3 -3
  20. homeassistant/components/alexa_devices/__init__.py +1 -1
  21. homeassistant/components/alexa_devices/const.py +9 -9
  22. homeassistant/components/alexa_devices/sensor.py +3 -0
  23. homeassistant/components/alexa_devices/translations/lt.json +7 -0
  24. homeassistant/components/alexa_devices/translations/pt.json +11 -0
  25. homeassistant/components/alexa_devices/translations/sk.json +20 -20
  26. homeassistant/components/asuswrt/manifest.json +1 -1
  27. homeassistant/components/august/translations/lt.json +23 -1
  28. homeassistant/components/august/translations/pt.json +26 -1
  29. homeassistant/components/backup/translations/de.json +1 -1
  30. homeassistant/components/bayesian/strings.json +1 -1
  31. homeassistant/components/bayesian/translations/cs.json +1 -1
  32. homeassistant/components/bayesian/translations/de.json +17 -17
  33. homeassistant/components/bayesian/translations/en-GB.json +1 -1
  34. homeassistant/components/bayesian/translations/en.json +3 -3
  35. homeassistant/components/bayesian/translations/et.json +1 -1
  36. homeassistant/components/bayesian/translations/fr.json +41 -0
  37. homeassistant/components/bayesian/translations/ga.json +8 -3
  38. homeassistant/components/bayesian/translations/lt.json +200 -0
  39. homeassistant/components/bayesian/translations/pl.json +7 -0
  40. homeassistant/components/bayesian/translations/pt.json +257 -0
  41. homeassistant/components/bayesian/translations/sk.json +5 -5
  42. homeassistant/components/bayesian/translations/zh-Hans.json +1 -1
  43. homeassistant/components/bayesian/translations/zh-Hant.json +1 -1
  44. homeassistant/components/bluetooth/manifest.json +2 -2
  45. homeassistant/components/bring/translations/sk.json +1 -1
  46. homeassistant/components/button/translations/ru.json +1 -1
  47. homeassistant/components/cover/translations/mk.json +13 -5
  48. homeassistant/components/cover/translations/pl.json +1 -1
  49. homeassistant/components/deconz/translations/ga.json +14 -0
  50. homeassistant/components/downloader/translations/sk.json +1 -1
  51. homeassistant/components/dsmr/translations/ga.json +8 -0
  52. homeassistant/components/dwd_weather_warnings/translations/sk.json +1 -1
  53. homeassistant/components/ecovacs/manifest.json +1 -1
  54. homeassistant/components/enphase_envoy/translations/lt.json +6 -0
  55. homeassistant/components/eq3btsmart/manifest.json +1 -1
  56. homeassistant/components/esphome/manifest.json +2 -2
  57. homeassistant/components/esphome/translations/sk.json +1 -1
  58. homeassistant/components/flux_led/translations/sk.json +1 -1
  59. homeassistant/components/frontend/manifest.json +1 -1
  60. homeassistant/components/fully_kiosk/translations/fr.json +5 -0
  61. homeassistant/components/fully_kiosk/translations/lt.json +3 -0
  62. homeassistant/components/fully_kiosk/translations/pt.json +3 -0
  63. homeassistant/components/fyta/translations/fr.json +3 -0
  64. homeassistant/components/fyta/translations/lt.json +64 -2
  65. homeassistant/components/fyta/translations/pt.json +64 -2
  66. homeassistant/components/google_assistant_sdk/translations/sk.json +1 -1
  67. homeassistant/components/google_generative_ai_conversation/translations/cs.json +3 -3
  68. homeassistant/components/google_generative_ai_conversation/translations/sk.json +1 -1
  69. homeassistant/components/google_mail/translations/sk.json +1 -1
  70. homeassistant/components/google_photos/translations/sk.json +1 -1
  71. homeassistant/components/google_sheets/translations/sk.json +1 -1
  72. homeassistant/components/google_tasks/translations/sk.json +1 -1
  73. homeassistant/components/govee_light_local/__init__.py +16 -3
  74. homeassistant/components/govee_light_local/config_flow.py +28 -10
  75. homeassistant/components/govee_light_local/coordinator.py +39 -24
  76. homeassistant/components/habitica/translations/cs.json +3 -3
  77. homeassistant/components/hassio/translations/sk.json +1 -1
  78. homeassistant/components/history_stats/translations/sk.json +3 -3
  79. homeassistant/components/home_connect/translations/sk.json +1 -1
  80. homeassistant/components/husqvarna_automower_ble/translations/cs.json +35 -1
  81. homeassistant/components/husqvarna_automower_ble/translations/lt.json +35 -1
  82. homeassistant/components/husqvarna_automower_ble/translations/pt.json +35 -1
  83. homeassistant/components/hyperion/translations/sk.json +1 -1
  84. homeassistant/components/iaqualink/__init__.py +10 -0
  85. homeassistant/components/iaqualink/entity.py +1 -0
  86. homeassistant/components/imeon_inverter/translations/lt.json +54 -0
  87. homeassistant/components/imeon_inverter/translations/pt.json +79 -0
  88. homeassistant/components/knx/translations/lt.json +245 -0
  89. homeassistant/components/knx/translations/pt.json +270 -0
  90. homeassistant/components/lg_thinq/translations/lt.json +3 -0
  91. homeassistant/components/lg_thinq/translations/pt.json +4 -0
  92. homeassistant/components/lock/translations/pl.json +1 -0
  93. homeassistant/components/logbook/translations/ko.json +2 -1
  94. homeassistant/components/lyric/translations/sk.json +1 -1
  95. homeassistant/components/mastodon/__init__.py +13 -2
  96. homeassistant/components/mastodon/config_flow.py +7 -2
  97. homeassistant/components/mastodon/diagnostics.py +8 -3
  98. homeassistant/components/matter/translations/lt.json +36 -0
  99. homeassistant/components/matter/translations/pt.json +27 -0
  100. homeassistant/components/media_player/translations/cs.json +5 -0
  101. homeassistant/components/media_player/translations/fr.json +3 -0
  102. homeassistant/components/media_player/translations/lt.json +4 -0
  103. homeassistant/components/media_player/translations/pt.json +5 -0
  104. homeassistant/components/media_player/translations/sk.json +1 -0
  105. homeassistant/components/media_player/translations/zh-Hans.json +1 -0
  106. homeassistant/components/media_player/translations/zh-Hant.json +1 -0
  107. homeassistant/components/meteo_france/const.py +1 -1
  108. homeassistant/components/metoffice/translations/sk.json +1 -1
  109. homeassistant/components/miele/translations/lt.json +12 -0
  110. homeassistant/components/miele/translations/pt.json +12 -0
  111. homeassistant/components/miele/translations/sk.json +8 -8
  112. homeassistant/components/modbus/translations/sk.json +1 -1
  113. homeassistant/components/mqtt/translations/cs.json +52 -0
  114. homeassistant/components/mqtt/translations/fr.json +11 -0
  115. homeassistant/components/mqtt/translations/lt.json +47 -0
  116. homeassistant/components/mqtt/translations/nl.json +68 -0
  117. homeassistant/components/mqtt/translations/pt.json +81 -0
  118. homeassistant/components/mqtt/translations/sk.json +8 -8
  119. homeassistant/components/mqtt/translations/zh-Hans.json +1 -0
  120. homeassistant/components/nest/translations/sk.json +2 -2
  121. homeassistant/components/nexia/manifest.json +1 -1
  122. homeassistant/components/nextdns/translations/lt.json +3 -0
  123. homeassistant/components/niko_home_control/translations/lt.json +10 -1
  124. homeassistant/components/niko_home_control/translations/pt.json +10 -1
  125. homeassistant/components/ollama/translations/cs.json +3 -3
  126. homeassistant/components/onewire/translations/lt.json +3 -0
  127. homeassistant/components/onvif/binary_sensor.py +1 -1
  128. homeassistant/components/onvif/sensor.py +1 -1
  129. homeassistant/components/open_router/translations/cs.json +2 -2
  130. homeassistant/components/openai_conversation/translations/cs.json +3 -3
  131. homeassistant/components/opower/translations/sk.json +1 -1
  132. homeassistant/components/playstation_network/__init__.py +4 -1
  133. homeassistant/components/playstation_network/config_flow.py +4 -13
  134. homeassistant/components/playstation_network/coordinator.py +55 -8
  135. homeassistant/components/playstation_network/helpers.py +4 -1
  136. homeassistant/components/playstation_network/notify.py +52 -27
  137. homeassistant/components/playstation_network/strings.json +11 -5
  138. homeassistant/components/playstation_network/translations/bg.json +0 -5
  139. homeassistant/components/playstation_network/translations/cs.json +5 -5
  140. homeassistant/components/playstation_network/translations/de.json +5 -5
  141. homeassistant/components/playstation_network/translations/en-GB.json +5 -5
  142. homeassistant/components/playstation_network/translations/en.json +5 -5
  143. homeassistant/components/playstation_network/translations/es.json +1 -13
  144. homeassistant/components/playstation_network/translations/et.json +0 -17
  145. homeassistant/components/playstation_network/translations/fr.json +1 -1
  146. homeassistant/components/playstation_network/translations/ga.json +0 -17
  147. homeassistant/components/playstation_network/translations/it.json +1 -1
  148. homeassistant/components/playstation_network/translations/lt.json +0 -17
  149. homeassistant/components/playstation_network/translations/pt.json +5 -5
  150. homeassistant/components/playstation_network/translations/sk.json +0 -17
  151. homeassistant/components/playstation_network/translations/zh-Hans.json +0 -17
  152. homeassistant/components/playstation_network/translations/zh-Hant.json +5 -5
  153. homeassistant/components/pooldose/translations/lt.json +102 -0
  154. homeassistant/components/pooldose/translations/nl.json +7 -2
  155. homeassistant/components/pooldose/translations/pt.json +102 -0
  156. homeassistant/components/profiler/translations/cs.json +2 -2
  157. homeassistant/components/qbus/translations/lt.json +8 -0
  158. homeassistant/components/recorder/translations/cs.json +2 -2
  159. homeassistant/components/remember_the_milk/translations/cs.json +7 -7
  160. homeassistant/components/reolink/manifest.json +1 -1
  161. homeassistant/components/reolink/number.py +3 -4
  162. homeassistant/components/reolink/select.py +0 -1
  163. homeassistant/components/reolink/translations/fr.json +3 -0
  164. homeassistant/components/reolink/translations/pt.json +9 -0
  165. homeassistant/components/roborock/translations/sk.json +9 -9
  166. homeassistant/components/sensor/translations/sk.json +1 -1
  167. homeassistant/components/sleep_as_android/translations/de.json +1 -1
  168. homeassistant/components/snapcast/translations/sk.json +2 -2
  169. homeassistant/components/sonos/translations/lt.json +12 -0
  170. homeassistant/components/sonos/translations/pt.json +15 -0
  171. homeassistant/components/sun/translations/sk.json +1 -1
  172. homeassistant/components/switchbot_cloud/translations/lt.json +16 -0
  173. homeassistant/components/switchbot_cloud/translations/pt.json +16 -0
  174. homeassistant/components/tautulli/translations/sk.json +1 -1
  175. homeassistant/components/template/translations/lt.json +102 -0
  176. homeassistant/components/template/translations/pl.json +140 -7
  177. homeassistant/components/template/translations/pt.json +149 -0
  178. homeassistant/components/template/translations/sk.json +6 -6
  179. homeassistant/components/togrill/translations/fr.json +4 -0
  180. homeassistant/components/togrill/translations/lt.json +30 -0
  181. homeassistant/components/togrill/translations/pt.json +49 -0
  182. homeassistant/components/tts/translations/cs.json +1 -1
  183. homeassistant/components/tuya/translations/pl.json +1 -1
  184. homeassistant/components/velux/translations/cs.json +7 -0
  185. homeassistant/components/velux/translations/lt.json +7 -0
  186. homeassistant/components/velux/translations/nl.json +7 -0
  187. homeassistant/components/velux/translations/pt.json +7 -0
  188. homeassistant/components/volvo/translations/lt.json +128 -0
  189. homeassistant/components/volvo/translations/pt.json +137 -0
  190. homeassistant/components/volvo/translations/sk.json +1 -1
  191. homeassistant/components/wallbox/translations/sk.json +1 -1
  192. homeassistant/components/withings/translations/sk.json +1 -1
  193. homeassistant/components/yolink/translations/pt.json +13 -0
  194. homeassistant/components/zha/translations/fr.json +48 -0
  195. homeassistant/components/zha/translations/lt.json +63 -0
  196. homeassistant/components/zha/translations/pl.json +3 -0
  197. homeassistant/components/zha/translations/pt.json +69 -0
  198. homeassistant/components/zha/translations/sk.json +3 -3
  199. homeassistant/components/zwave_js/binary_sensor.py +14 -6
  200. homeassistant/components/zwave_js/translations/lt.json +10 -0
  201. homeassistant/components/zwave_js/translations/pt.json +10 -0
  202. homeassistant/components/zwave_js/translations/sk.json +2 -2
  203. homeassistant/components/zwave_me/translations/sk.json +1 -1
  204. homeassistant/const.py +1 -1
  205. homeassistant/helpers/device_registry.py +37 -14
  206. homeassistant/helpers/entity_registry.py +68 -29
  207. homeassistant/package_constraints.txt +7 -3
  208. {homeassistant-2025.9.0b0.dist-info → homeassistant-2025.9.0b1.dist-info}/METADATA +1 -1
  209. {homeassistant-2025.9.0b0.dist-info → homeassistant-2025.9.0b1.dist-info}/RECORD +214 -210
  210. {homeassistant-2025.9.0b0.dist-info → homeassistant-2025.9.0b1.dist-info}/WHEEL +0 -0
  211. {homeassistant-2025.9.0b0.dist-info → homeassistant-2025.9.0b1.dist-info}/entry_points.txt +0 -0
  212. {homeassistant-2025.9.0b0.dist-info → homeassistant-2025.9.0b1.dist-info}/licenses/LICENSE.md +0 -0
  213. {homeassistant-2025.9.0b0.dist-info → homeassistant-2025.9.0b1.dist-info}/licenses/homeassistant/backports/LICENSE.Python +0 -0
  214. {homeassistant-2025.9.0b0.dist-info → homeassistant-2025.9.0b1.dist-info}/top_level.txt +0 -0
@@ -10,7 +10,6 @@ from psnawp_api.core.psnawp_exceptions import (
10
10
  PSNAWPInvalidTokenError,
11
11
  PSNAWPNotFoundError,
12
12
  )
13
- from psnawp_api.models import User
14
13
  from psnawp_api.utils.misc import parse_npsso_token
15
14
  import voluptuous as vol
16
15
 
@@ -169,13 +168,12 @@ class PlaystationNetworkConfigFlow(ConfigFlow, domain=DOMAIN):
169
168
  class FriendSubentryFlowHandler(ConfigSubentryFlow):
170
169
  """Handle subentry flow for adding a friend."""
171
170
 
172
- friends_list: dict[str, User]
173
-
174
171
  async def async_step_user(
175
172
  self, user_input: dict[str, Any] | None = None
176
173
  ) -> SubentryFlowResult:
177
174
  """Subentry user flow."""
178
175
  config_entry: PlaystationNetworkConfigEntry = self._get_entry()
176
+ friends_list = config_entry.runtime_data.user_data.psn.friends_list
179
177
 
180
178
  if user_input is not None:
181
179
  config_entries = self.hass.config_entries.async_entries(DOMAIN)
@@ -190,19 +188,12 @@ class FriendSubentryFlowHandler(ConfigSubentryFlow):
190
188
  return self.async_abort(reason="already_configured")
191
189
 
192
190
  return self.async_create_entry(
193
- title=self.friends_list[user_input[CONF_ACCOUNT_ID]].online_id,
191
+ title=friends_list[user_input[CONF_ACCOUNT_ID]].online_id,
194
192
  data={},
195
193
  unique_id=user_input[CONF_ACCOUNT_ID],
196
194
  )
197
195
 
198
- self.friends_list = await self.hass.async_add_executor_job(
199
- lambda: {
200
- friend.account_id: friend
201
- for friend in config_entry.runtime_data.user_data.psn.user.friends_list()
202
- }
203
- )
204
-
205
- if not self.friends_list:
196
+ if not friends_list:
206
197
  return self.async_abort(reason="no_friends")
207
198
 
208
199
  options = [
@@ -210,7 +201,7 @@ class FriendSubentryFlowHandler(ConfigSubentryFlow):
210
201
  value=friend.account_id,
211
202
  label=friend.online_id,
212
203
  )
213
- for friend in self.friends_list.values()
204
+ for friend in friends_list.values()
214
205
  ]
215
206
 
216
207
  return self.async_show_form(
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from abc import abstractmethod
6
6
  from dataclasses import dataclass
7
7
  from datetime import timedelta
8
+ import json
8
9
  import logging
9
10
  from typing import TYPE_CHECKING, Any
10
11
 
@@ -21,12 +22,14 @@ from psnawp_api.models.group.group_datatypes import GroupDetails
21
22
  from psnawp_api.models.trophies import TrophyTitle
22
23
 
23
24
  from homeassistant.config_entries import ConfigEntry, ConfigSubentry
25
+ from homeassistant.const import CONF_NAME
24
26
  from homeassistant.core import HomeAssistant
25
27
  from homeassistant.exceptions import (
26
28
  ConfigEntryAuthFailed,
27
29
  ConfigEntryError,
28
30
  ConfigEntryNotReady,
29
31
  )
32
+ from homeassistant.helpers import issue_registry as ir
30
33
  from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
31
34
 
32
35
  from .const import DOMAIN
@@ -45,6 +48,7 @@ class PlaystationNetworkRuntimeData:
45
48
  trophy_titles: PlaystationNetworkTrophyTitlesCoordinator
46
49
  groups: PlaystationNetworkGroupsUpdateCoordinator
47
50
  friends: dict[str, PlaystationNetworkFriendDataCoordinator]
51
+ friends_list: PlaystationNetworkFriendlistCoordinator
48
52
 
49
53
 
50
54
  class PlayStationNetworkBaseCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
@@ -134,6 +138,25 @@ class PlaystationNetworkTrophyTitlesCoordinator(
134
138
  return self.psn.trophy_titles
135
139
 
136
140
 
141
+ class PlaystationNetworkFriendlistCoordinator(
142
+ PlayStationNetworkBaseCoordinator[dict[str, User]]
143
+ ):
144
+ """Friend list data update coordinator for PSN."""
145
+
146
+ _update_interval = timedelta(hours=3)
147
+
148
+ async def update_data(self) -> dict[str, User]:
149
+ """Update trophy titles data."""
150
+
151
+ self.psn.friends_list = await self.hass.async_add_executor_job(
152
+ lambda: {
153
+ friend.account_id: friend for friend in self.psn.user.friends_list()
154
+ }
155
+ )
156
+ await self.config_entry.runtime_data.user_data.async_request_refresh()
157
+ return self.psn.friends_list
158
+
159
+
137
160
  class PlaystationNetworkGroupsUpdateCoordinator(
138
161
  PlayStationNetworkBaseCoordinator[dict[str, GroupDetails]]
139
162
  ):
@@ -143,13 +166,34 @@ class PlaystationNetworkGroupsUpdateCoordinator(
143
166
 
144
167
  async def update_data(self) -> dict[str, GroupDetails]:
145
168
  """Update groups data."""
146
- return await self.hass.async_add_executor_job(
147
- lambda: {
148
- group_info.group_id: group_info.get_group_information()
149
- for group_info in self.psn.client.get_groups()
150
- if not group_info.group_id.startswith("~")
151
- }
152
- )
169
+ try:
170
+ return await self.hass.async_add_executor_job(
171
+ lambda: {
172
+ group_info.group_id: group_info.get_group_information()
173
+ for group_info in self.psn.client.get_groups()
174
+ if not group_info.group_id.startswith("~")
175
+ }
176
+ )
177
+ except PSNAWPForbiddenError as e:
178
+ try:
179
+ error = json.loads(e.args[0])
180
+ except json.JSONDecodeError as err:
181
+ raise PSNAWPServerError from err
182
+ ir.async_create_issue(
183
+ self.hass,
184
+ DOMAIN,
185
+ f"group_chat_forbidden_{self.config_entry.entry_id}",
186
+ is_fixable=False,
187
+ issue_domain=DOMAIN,
188
+ severity=ir.IssueSeverity.ERROR,
189
+ translation_key="group_chat_forbidden",
190
+ translation_placeholders={
191
+ CONF_NAME: self.config_entry.title,
192
+ "error_message": error["error"]["message"],
193
+ },
194
+ )
195
+ await self.async_shutdown()
196
+ return {}
153
197
 
154
198
 
155
199
  class PlaystationNetworkFriendDataCoordinator(
@@ -178,7 +222,10 @@ class PlaystationNetworkFriendDataCoordinator(
178
222
  """Set up the coordinator."""
179
223
  if TYPE_CHECKING:
180
224
  assert self.subentry.unique_id
181
- self.user = self.psn.psn.user(account_id=self.subentry.unique_id)
225
+ self.user = self.psn.friends_list.get(
226
+ self.subentry.unique_id
227
+ ) or self.psn.psn.user(account_id=self.subentry.unique_id)
228
+
182
229
  self.profile = self.user.profile()
183
230
 
184
231
  async def _async_setup(self) -> None:
@@ -60,7 +60,7 @@ class PlaystationNetwork:
60
60
  self.legacy_profile: dict[str, Any] | None = None
61
61
  self.trophy_titles: list[TrophyTitle] = []
62
62
  self._title_icon_urls: dict[str, str] = {}
63
- self.friends_list: dict[str, User] | None = None
63
+ self.friends_list: dict[str, User] = {}
64
64
 
65
65
  def _setup(self) -> None:
66
66
  """Setup PSN."""
@@ -68,6 +68,9 @@ class PlaystationNetwork:
68
68
  self.client = self.psn.me()
69
69
  self.shareable_profile_link = self.client.get_shareable_profile_link()
70
70
  self.trophy_titles = list(self.user.trophy_titles(page_size=500))
71
+ self.friends_list = {
72
+ friend.account_id: friend for friend in self.user.friends_list()
73
+ }
71
74
 
72
75
  async def async_setup(self) -> None:
73
76
  """Setup PSN."""
@@ -18,7 +18,7 @@ from homeassistant.components.notify import (
18
18
  NotifyEntity,
19
19
  NotifyEntityDescription,
20
20
  )
21
- from homeassistant.config_entries import ConfigSubentry
21
+ from homeassistant.const import CONF_NAME
22
22
  from homeassistant.core import HomeAssistant, callback
23
23
  from homeassistant.exceptions import HomeAssistantError
24
24
  from homeassistant.helpers import entity_registry as er
@@ -27,7 +27,7 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
27
27
  from .const import DOMAIN
28
28
  from .coordinator import (
29
29
  PlaystationNetworkConfigEntry,
30
- PlaystationNetworkFriendDataCoordinator,
30
+ PlaystationNetworkFriendlistCoordinator,
31
31
  PlaystationNetworkGroupsUpdateCoordinator,
32
32
  )
33
33
  from .entity import PlaystationNetworkServiceEntity
@@ -50,8 +50,10 @@ async def async_setup_entry(
50
50
  """Set up the notify entity platform."""
51
51
 
52
52
  coordinator = config_entry.runtime_data.groups
53
+ friends_list = config_entry.runtime_data.friends_list
53
54
 
54
55
  groups_added: set[str] = set()
56
+ friends_added: set[str] = set()
55
57
  entity_registry = er.async_get(hass)
56
58
 
57
59
  @callback
@@ -78,16 +80,32 @@ async def async_setup_entry(
78
80
  coordinator.async_add_listener(add_entities)
79
81
  add_entities()
80
82
 
81
- for subentry_id, friend_coordinator in config_entry.runtime_data.friends.items():
82
- async_add_entities(
83
- [
84
- PlaystationNetworkDirectMessageNotifyEntity(
85
- friend_coordinator,
86
- config_entry.subentries[subentry_id],
87
- )
88
- ],
89
- config_subentry_id=subentry_id,
90
- )
83
+ @callback
84
+ def add_dm_entities() -> None:
85
+ nonlocal friends_added
86
+
87
+ new_friends = set(friends_list.psn.friends_list.keys()) - friends_added
88
+ if new_friends:
89
+ async_add_entities(
90
+ [
91
+ PlaystationNetworkDirectMessageNotifyEntity(
92
+ friends_list, account_id
93
+ )
94
+ for account_id in new_friends
95
+ ],
96
+ )
97
+ friends_added |= new_friends
98
+ deleted_friends = friends_added - set(coordinator.psn.friends_list.keys())
99
+ for account_id in deleted_friends:
100
+ if entity_id := entity_registry.async_get_entity_id(
101
+ NOTIFY_DOMAIN,
102
+ DOMAIN,
103
+ f"{coordinator.config_entry.unique_id}_{account_id}_{PlaystationNetworkNotify.DIRECT_MESSAGE}",
104
+ ):
105
+ entity_registry.async_remove(entity_id)
106
+
107
+ friends_list.async_add_listener(add_dm_entities)
108
+ add_dm_entities()
91
109
 
92
110
 
93
111
  class PlaystationNetworkNotifyBaseEntity(PlaystationNetworkServiceEntity, NotifyEntity):
@@ -95,12 +113,17 @@ class PlaystationNetworkNotifyBaseEntity(PlaystationNetworkServiceEntity, Notify
95
113
 
96
114
  group: Group | None = None
97
115
 
98
- def send_message(self, message: str, title: str | None = None) -> None:
99
- """Send a message."""
116
+ def _send_message(self, message: str) -> None:
117
+ """Send message."""
100
118
  if TYPE_CHECKING:
101
119
  assert self.group
120
+ self.group.send_message(message)
121
+
122
+ def send_message(self, message: str, title: str | None = None) -> None:
123
+ """Send a message."""
124
+
102
125
  try:
103
- self.group.send_message(message)
126
+ self._send_message(message)
104
127
  except PSNAWPNotFoundError as e:
105
128
  raise HomeAssistantError(
106
129
  translation_domain=DOMAIN,
@@ -138,7 +161,7 @@ class PlaystationNetworkNotifyEntity(PlaystationNetworkNotifyBaseEntity):
138
161
  key=group_id,
139
162
  translation_key=PlaystationNetworkNotify.GROUP_MESSAGE,
140
163
  translation_placeholders={
141
- "group_name": group_details["groupName"]["value"]
164
+ CONF_NAME: group_details["groupName"]["value"]
142
165
  or ", ".join(
143
166
  member["onlineId"]
144
167
  for member in group_details["members"]
@@ -153,27 +176,29 @@ class PlaystationNetworkNotifyEntity(PlaystationNetworkNotifyBaseEntity):
153
176
  class PlaystationNetworkDirectMessageNotifyEntity(PlaystationNetworkNotifyBaseEntity):
154
177
  """Representation of a PlayStation Network notify entity for sending direct messages."""
155
178
 
156
- coordinator: PlaystationNetworkFriendDataCoordinator
179
+ coordinator: PlaystationNetworkFriendlistCoordinator
157
180
 
158
181
  def __init__(
159
182
  self,
160
- coordinator: PlaystationNetworkFriendDataCoordinator,
161
- subentry: ConfigSubentry,
183
+ coordinator: PlaystationNetworkFriendlistCoordinator,
184
+ account_id: str,
162
185
  ) -> None:
163
186
  """Initialize a notification entity."""
164
-
187
+ self.account_id = account_id
165
188
  self.entity_description = NotifyEntityDescription(
166
- key=PlaystationNetworkNotify.DIRECT_MESSAGE,
189
+ key=f"{account_id}_{PlaystationNetworkNotify.DIRECT_MESSAGE}",
167
190
  translation_key=PlaystationNetworkNotify.DIRECT_MESSAGE,
191
+ translation_placeholders={
192
+ CONF_NAME: coordinator.psn.friends_list[account_id].online_id
193
+ },
194
+ entity_registry_enabled_default=False,
168
195
  )
169
196
 
170
- super().__init__(coordinator, self.entity_description, subentry)
171
-
172
- def send_message(self, message: str, title: str | None = None) -> None:
173
- """Send a message."""
197
+ super().__init__(coordinator, self.entity_description)
174
198
 
199
+ def _send_message(self, message: str) -> None:
175
200
  if not self.group:
176
201
  self.group = self.coordinator.psn.psn.group(
177
- users_list=[self.coordinator.user]
202
+ users_list=[self.coordinator.psn.friends_list[self.account_id]]
178
203
  )
179
- super().send_message(message, title)
204
+ super()._send_message(message)
@@ -82,13 +82,13 @@
82
82
  "message": "Data retrieval failed when trying to access the PlayStation Network."
83
83
  },
84
84
  "group_invalid": {
85
- "message": "Failed to send message to group {group_name}. The group is invalid or does not exist."
85
+ "message": "Failed to send message to group {name}. The group is invalid or does not exist."
86
86
  },
87
87
  "send_message_forbidden": {
88
- "message": "Failed to send message to group {group_name}. You are not allowed to send messages to this group."
88
+ "message": "Failed to send message to {name}. You are not allowed to send messages to this group or friend."
89
89
  },
90
90
  "send_message_failed": {
91
- "message": "Failed to send message to group {group_name}. Try again later."
91
+ "message": "Failed to send message to {name}. Try again later."
92
92
  },
93
93
  "user_profile_private": {
94
94
  "message": "Unable to retrieve data for {user}. Privacy settings restrict access to activity."
@@ -158,11 +158,17 @@
158
158
  },
159
159
  "notify": {
160
160
  "group_message": {
161
- "name": "Group: {group_name}"
161
+ "name": "Group: {name}"
162
162
  },
163
163
  "direct_message": {
164
- "name": "Direct message"
164
+ "name": "Direct message: {name}"
165
165
  }
166
166
  }
167
+ },
168
+ "issues": {
169
+ "group_chat_forbidden": {
170
+ "title": "Failed to retrieve group chats for {name}",
171
+ "description": "The PlayStation Network integration was unable to retrieve group chats for **{name}**.\n\nThis is likely due to insufficient permissions (Error: `{error_message}`).\n\nTo resolve this issue, please ensure the account's chat and messaging feature is not restricted by parental controls or other privacy settings.\n\nIf the restriction is intentional, you can safely ignore this message."
172
+ }
167
173
  }
168
174
  }
@@ -29,11 +29,6 @@
29
29
  "name": "\u0410\u0432\u0430\u0442\u0430\u0440"
30
30
  }
31
31
  },
32
- "notify": {
33
- "group_message": {
34
- "name": "\u0413\u0440\u0443\u043f\u0430: {group_name}"
35
- }
36
- },
37
32
  "sensor": {
38
33
  "earned_trophies_bronze": {
39
34
  "name": "\u0411\u0440\u043e\u043d\u0437\u043e\u0432\u0438 \u0442\u0440\u043e\u0444\u0435\u0438",
@@ -93,10 +93,10 @@
93
93
  },
94
94
  "notify": {
95
95
  "direct_message": {
96
- "name": "P\u0159\u00edm\u00e1 zpr\u00e1va"
96
+ "name": "P\u0159\u00edm\u00e1 zpr\u00e1va: {name}"
97
97
  },
98
98
  "group_message": {
99
- "name": "Skupina: {group_name}"
99
+ "name": "Skupina: {name}"
100
100
  }
101
101
  },
102
102
  "sensor": {
@@ -144,16 +144,16 @@
144
144
  },
145
145
  "exceptions": {
146
146
  "group_invalid": {
147
- "message": "Nepoda\u0159ilo se odeslat zpr\u00e1vu skupin\u011b {group_name}. Skupina je neplatn\u00e1 nebo neexistuje."
147
+ "message": "Nepoda\u0159ilo se odeslat zpr\u00e1vu skupin\u011b {name}. Skupina je neplatn\u00e1 nebo neexistuje."
148
148
  },
149
149
  "not_ready": {
150
150
  "message": "Ov\u011b\u0159en\u00ed k PlayStation Network selhalo."
151
151
  },
152
152
  "send_message_failed": {
153
- "message": "Nepoda\u0159ilo se odeslat zpr\u00e1vu skupin\u011b {group_name}. Zkuste to znovu pozd\u011bji."
153
+ "message": "Nepoda\u0159ilo se odeslat zpr\u00e1vu skupin\u011b {name}. Zkuste to znovu pozd\u011bji."
154
154
  },
155
155
  "send_message_forbidden": {
156
- "message": "Nepoda\u0159ilo se odeslat zpr\u00e1vu skupin\u011b {group_name}. Do t\u00e9to skupiny nem\u00e1te opr\u00e1vn\u011bn\u00ed odes\u00edlat zpr\u00e1vy."
156
+ "message": "Nepoda\u0159ilo se odeslat zpr\u00e1vu skupin\u011b {name}. Do t\u00e9to skupiny nem\u00e1te opr\u00e1vn\u011bn\u00ed odes\u00edlat zpr\u00e1vy."
157
157
  },
158
158
  "update_failed": {
159
159
  "message": "P\u0159i pokusu o p\u0159\u00edstup k PlayStation Network se nepoda\u0159ilo na\u010d\u00edst data."
@@ -93,10 +93,10 @@
93
93
  },
94
94
  "notify": {
95
95
  "direct_message": {
96
- "name": "Direktnachricht"
96
+ "name": "Direktnachricht: {name}"
97
97
  },
98
98
  "group_message": {
99
- "name": "Gruppe: {group_name}"
99
+ "name": "Gruppe: {name}"
100
100
  }
101
101
  },
102
102
  "sensor": {
@@ -144,16 +144,16 @@
144
144
  },
145
145
  "exceptions": {
146
146
  "group_invalid": {
147
- "message": "Das Senden der Nachricht an die Gruppe {group_name} ist fehlgeschlagen. Die Gruppe ist ung\u00fcltig oder nicht vorhanden."
147
+ "message": "Das Senden der Nachricht an die Gruppe {name} ist fehlgeschlagen. Die Gruppe ist ung\u00fcltig oder nicht vorhanden."
148
148
  },
149
149
  "not_ready": {
150
150
  "message": "Die Authentifizierung beim PlayStation Network ist fehlgeschlagen."
151
151
  },
152
152
  "send_message_failed": {
153
- "message": "Das Senden der Nachricht an die Gruppe {group_name} ist fehlgeschlagen. Versuche es sp\u00e4ter erneut."
153
+ "message": "Das Senden der Nachricht an {name} ist fehlgeschlagen. Versuche es sp\u00e4ter erneut."
154
154
  },
155
155
  "send_message_forbidden": {
156
- "message": "Das Senden der Nachricht an die Gruppe {group_name} ist fehlgeschlagen. Du bist nicht berechtigt, Nachrichten an diese Gruppe zu senden."
156
+ "message": "Das Senden der Nachricht an {name} ist fehlgeschlagen. Du bist nicht berechtigt, Nachrichten an diese Gruppe oder diesen Freund zu senden."
157
157
  },
158
158
  "update_failed": {
159
159
  "message": "Beim Versuch, auf das PlayStation Network zuzugreifen, ist der Datenabruf fehlgeschlagen."
@@ -93,10 +93,10 @@
93
93
  },
94
94
  "notify": {
95
95
  "direct_message": {
96
- "name": "Direct message"
96
+ "name": "Direct message: {name}"
97
97
  },
98
98
  "group_message": {
99
- "name": "Group: {group_name}"
99
+ "name": "Group: {name}"
100
100
  }
101
101
  },
102
102
  "sensor": {
@@ -144,16 +144,16 @@
144
144
  },
145
145
  "exceptions": {
146
146
  "group_invalid": {
147
- "message": "Failed to send message to group {group_name}. The group is invalid or does not exist."
147
+ "message": "Failed to send message to group {name}. The group is invalid or does not exist."
148
148
  },
149
149
  "not_ready": {
150
150
  "message": "Authentication to the PlayStation Network failed."
151
151
  },
152
152
  "send_message_failed": {
153
- "message": "Failed to send message to group {group_name}. Try again later."
153
+ "message": "Failed to send message to {name}. Try again later."
154
154
  },
155
155
  "send_message_forbidden": {
156
- "message": "Failed to send message to group {group_name}. You are not allowed to send messages to this group."
156
+ "message": "Failed to send message to {name}. You are not allowed to send messages to this group or friend."
157
157
  },
158
158
  "update_failed": {
159
159
  "message": "Data retrieval failed when trying to access the PlayStation Network."
@@ -93,10 +93,10 @@
93
93
  },
94
94
  "notify": {
95
95
  "direct_message": {
96
- "name": "Direct message"
96
+ "name": "Direct message: {name}"
97
97
  },
98
98
  "group_message": {
99
- "name": "Group: {group_name}"
99
+ "name": "Group: {name}"
100
100
  }
101
101
  },
102
102
  "sensor": {
@@ -144,16 +144,16 @@
144
144
  },
145
145
  "exceptions": {
146
146
  "group_invalid": {
147
- "message": "Failed to send message to group {group_name}. The group is invalid or does not exist."
147
+ "message": "Failed to send message to group {name}. The group is invalid or does not exist."
148
148
  },
149
149
  "not_ready": {
150
150
  "message": "Authentication to the PlayStation Network failed."
151
151
  },
152
152
  "send_message_failed": {
153
- "message": "Failed to send message to group {group_name}. Try again later."
153
+ "message": "Failed to send message to {name}. Try again later."
154
154
  },
155
155
  "send_message_forbidden": {
156
- "message": "Failed to send message to group {group_name}. You are not allowed to send messages to this group."
156
+ "message": "Failed to send message to {name}. You are not allowed to send messages to this group or friend."
157
157
  },
158
158
  "update_failed": {
159
159
  "message": "Data retrieval failed when trying to access the PlayStation Network."
@@ -92,10 +92,7 @@
92
92
  },
93
93
  "notify": {
94
94
  "direct_message": {
95
- "name": "Mensaje directo"
96
- },
97
- "group_message": {
98
- "name": "Grupo: {group_name}"
95
+ "name": "Mensaje directo: {name}"
99
96
  }
100
97
  },
101
98
  "sensor": {
@@ -142,18 +139,9 @@
142
139
  }
143
140
  },
144
141
  "exceptions": {
145
- "group_invalid": {
146
- "message": "No se pudo enviar el mensaje al grupo {group_name}. El grupo no es v\u00e1lido o no existe."
147
- },
148
142
  "not_ready": {
149
143
  "message": "Error en la autenticaci\u00f3n a PlayStation Network."
150
144
  },
151
- "send_message_failed": {
152
- "message": "No se pudo enviar el mensaje al grupo {group_name}. Int\u00e9ntalo de nuevo m\u00e1s tarde."
153
- },
154
- "send_message_forbidden": {
155
- "message": "No se pudo enviar el mensaje al grupo {group_name}. No tienes permiso para enviar mensajes a este grupo."
156
- },
157
145
  "update_failed": {
158
146
  "message": "La recuperaci\u00f3n de datos fall\u00f3 al intentar acceder a PlayStation Network."
159
147
  },
@@ -91,14 +91,6 @@
91
91
  "name": "Jaga profiili"
92
92
  }
93
93
  },
94
- "notify": {
95
- "direct_message": {
96
- "name": "Otses\u00f5num"
97
- },
98
- "group_message": {
99
- "name": "Grupp: {group_name}"
100
- }
101
- },
102
94
  "sensor": {
103
95
  "earned_trophies_bronze": {
104
96
  "name": "Pronksist karikad",
@@ -143,18 +135,9 @@
143
135
  }
144
136
  },
145
137
  "exceptions": {
146
- "group_invalid": {
147
- "message": "S\u00f5numi saatmine grupile {group_name} nurjus. Grupp on sobimatu v\u00f5i seda pole olemas."
148
- },
149
138
  "not_ready": {
150
139
  "message": "PlayStation Networki autentimine eba\u00f5nnestus."
151
140
  },
152
- "send_message_failed": {
153
- "message": "Grupile {group_name} s\u00f5numi saatmine nurjus. Proovi hiljem uuesti."
154
- },
155
- "send_message_forbidden": {
156
- "message": "Grupile {group_name} s\u00f5numi saatmine nurjus. Sa ei tohi sellele grupile s\u00f5numeid saata."
157
- },
158
141
  "update_failed": {
159
142
  "message": "PlayStation Networkile juurdep\u00e4\u00e4su proovimisel andmete otsimine eba\u00f5nnestus."
160
143
  },
@@ -57,7 +57,7 @@
57
57
  },
58
58
  "notify": {
59
59
  "direct_message": {
60
- "name": "Message direct"
60
+ "name": "Message direct: {name}"
61
61
  }
62
62
  },
63
63
  "sensor": {
@@ -91,14 +91,6 @@
91
91
  "name": "Comhroinn pr\u00f3if\u00edl"
92
92
  }
93
93
  },
94
- "notify": {
95
- "direct_message": {
96
- "name": "Teachtaireacht dh\u00edreach"
97
- },
98
- "group_message": {
99
- "name": "Gr\u00fapa: {group_name}"
100
- }
101
- },
102
94
  "sensor": {
103
95
  "earned_trophies_bronze": {
104
96
  "name": "Tr\u00f3faithe cr\u00e9-umha",
@@ -140,18 +132,9 @@
140
132
  }
141
133
  },
142
134
  "exceptions": {
143
- "group_invalid": {
144
- "message": "Theip ar theachtaireacht a sheoladh chuig an ngr\u00fapa {group_name}. T\u00e1 an gr\u00fapa neamhbhail\u00ed n\u00f3 n\u00edl s\u00e9 ann."
145
- },
146
135
  "not_ready": {
147
136
  "message": "Theip ar fh\u00edordheimhni\u00fa leis an PlayStation Network."
148
137
  },
149
- "send_message_failed": {
150
- "message": "Theip ar theachtaireacht a sheoladh chuig an ngr\u00fapa {group_name}. D\u00e9an iarracht ar\u00eds ar ball."
151
- },
152
- "send_message_forbidden": {
153
- "message": "Theip ar theachtaireacht a sheoladh chuig an ngr\u00fapa {group_name}. N\u00edl cead agat teachtaireachta\u00ed a sheoladh chuig an ngr\u00fapa seo."
154
- },
155
138
  "update_failed": {
156
139
  "message": "Theip ar aisghabh\u00e1il sonra\u00ed agus iarracht \u00e1 d\u00e9anamh rochtain a fh\u00e1il ar L\u00edonra PlayStation."
157
140
  },
@@ -59,7 +59,7 @@
59
59
  },
60
60
  "notify": {
61
61
  "direct_message": {
62
- "name": "Messaggio diretto"
62
+ "name": "Messaggio diretto: {name}"
63
63
  }
64
64
  },
65
65
  "sensor": {