1 """Preference management for cloud."""
3 from __future__
import annotations
5 from collections.abc
import Callable, Coroutine
9 from hass_nabucasa.voice
import MAP_VOICE, Gender
15 async_get_users
as async_get_google_assistant_users,
23 DEFAULT_ALEXA_REPORT_STATE,
24 DEFAULT_EXPOSED_DOMAINS,
25 DEFAULT_GOOGLE_REPORT_STATE,
26 DEFAULT_TTS_DEFAULT_VOICE,
28 PREF_ALEXA_DEFAULT_EXPOSE,
29 PREF_ALEXA_ENTITY_CONFIGS,
30 PREF_ALEXA_REPORT_STATE,
31 PREF_ALEXA_SETTINGS_VERSION,
35 PREF_ENABLE_CLOUD_ICE_SERVERS,
38 PREF_GOOGLE_CONNECTED,
39 PREF_GOOGLE_DEFAULT_EXPOSE,
40 PREF_GOOGLE_ENTITY_CONFIGS,
41 PREF_GOOGLE_LOCAL_WEBHOOK_ID,
42 PREF_GOOGLE_REPORT_STATE,
43 PREF_GOOGLE_SECURE_DEVICES_PIN,
44 PREF_GOOGLE_SETTINGS_VERSION,
46 PREF_REMOTE_ALLOW_REMOTE_ENABLE,
48 PREF_TTS_DEFAULT_VOICE,
54 STORAGE_VERSION_MINOR = 4
56 ALEXA_SETTINGS_VERSION = 3
57 GOOGLE_SETTINGS_VERSION = 3
61 """Store cloud preferences."""
64 self, old_major_version: int, old_minor_version: int, old_data: dict[str, Any]
66 """Migrate to the new version."""
68 async
def google_connected() -> bool:
69 """Return True if our user is preset in the google_assistant store."""
71 if not (cur_username := old_data.get(PREF_USERNAME)):
75 return cur_username
in await async_get_google_assistant_users(self.hass)
77 if old_major_version == 1:
78 if old_minor_version < 2:
79 old_data.setdefault(PREF_ALEXA_SETTINGS_VERSION, 1)
80 old_data.setdefault(PREF_GOOGLE_SETTINGS_VERSION, 1)
81 if old_minor_version < 3:
87 old_data.setdefault(PREF_GOOGLE_CONNECTED, await google_connected())
88 if old_minor_version < 4:
93 default_tts_voice = old_data.get(PREF_TTS_DEFAULT_VOICE)
94 if default_tts_voice
and (voice_item_two := default_tts_voice[1])
in (
98 language: str = default_tts_voice[0]
99 if voice := MAP_VOICE.get((language, voice_item_two)):
100 old_data[PREF_TTS_DEFAULT_VOICE] = (
105 old_data[PREF_TTS_DEFAULT_VOICE] = DEFAULT_TTS_DEFAULT_VOICE
111 """Handle cloud preferences."""
113 _prefs: dict[str, Any]
116 """Initialize cloud prefs."""
119 hass, STORAGE_VERSION, STORAGE_KEY, minor_version=STORAGE_VERSION_MINOR
121 self._listeners: list[
122 Callable[[CloudPreferences], Coroutine[Any, Any,
None]]
127 """Finish initializing the preferences."""
133 if PREF_GOOGLE_LOCAL_WEBHOOK_ID
not in self.
_prefs_prefs:
137 PREF_GOOGLE_LOCAL_WEBHOOK_ID: webhook.async_generate_id(),
140 if PREF_INSTANCE_ID
not in self.
_prefs_prefs:
144 PREF_INSTANCE_ID: uuid.uuid4().hex,
150 self, listener: Callable[[CloudPreferences], Coroutine[Any, Any,
None]]
151 ) -> Callable[[],
None]:
152 """Listen for updates to the preferences."""
155 def unsubscribe() -> None:
156 """Remove the listener."""
157 self._listeners.
remove(listener)
159 self._listeners.append(listener)
166 alexa_enabled: bool | UndefinedType = UNDEFINED,
167 alexa_report_state: bool | UndefinedType = UNDEFINED,
168 alexa_settings_version: int | UndefinedType = UNDEFINED,
169 cloud_ice_servers_enabled: bool | UndefinedType = UNDEFINED,
170 cloud_user: str | UndefinedType = UNDEFINED,
171 cloudhooks: dict[str, dict[str, str | bool]] | UndefinedType = UNDEFINED,
172 google_connected: bool | UndefinedType = UNDEFINED,
173 google_enabled: bool | UndefinedType = UNDEFINED,
174 google_report_state: bool | UndefinedType = UNDEFINED,
175 google_secure_devices_pin: str |
None | UndefinedType = UNDEFINED,
176 google_settings_version: int | UndefinedType = UNDEFINED,
177 remote_allow_remote_enable: bool | UndefinedType = UNDEFINED,
178 remote_domain: str |
None | UndefinedType = UNDEFINED,
179 remote_enabled: bool | UndefinedType = UNDEFINED,
180 tts_default_voice: tuple[str, str] | UndefinedType = UNDEFINED,
182 """Update user preferences."""
183 prefs = {**self.
_prefs_prefs}
189 (PREF_ALEXA_REPORT_STATE, alexa_report_state),
190 (PREF_ALEXA_SETTINGS_VERSION, alexa_settings_version),
191 (PREF_CLOUD_USER, cloud_user),
192 (PREF_CLOUDHOOKS, cloudhooks),
193 (PREF_ENABLE_ALEXA, alexa_enabled),
194 (PREF_ENABLE_CLOUD_ICE_SERVERS, cloud_ice_servers_enabled),
195 (PREF_ENABLE_GOOGLE, google_enabled),
196 (PREF_ENABLE_REMOTE, remote_enabled),
197 (PREF_GOOGLE_CONNECTED, google_connected),
198 (PREF_GOOGLE_REPORT_STATE, google_report_state),
199 (PREF_GOOGLE_SECURE_DEVICES_PIN, google_secure_devices_pin),
200 (PREF_GOOGLE_SETTINGS_VERSION, google_settings_version),
201 (PREF_REMOTE_ALLOW_REMOTE_ENABLE, remote_allow_remote_enable),
202 (PREF_REMOTE_DOMAIN, remote_domain),
203 (PREF_TTS_DEFAULT_VOICE, tts_default_voice),
205 if value
is not UNDEFINED
212 """Set the username that is logged in."""
218 await self.
_hass_hass.auth.async_remove_user(user)
222 cur_username = self.
_prefs_prefs.
get(PREF_USERNAME)
224 if cur_username == username:
227 if cur_username
is None:
228 await self.
_save_prefs_save_prefs({**self.
_prefs_prefs, PREF_USERNAME: username})
235 """Erase the configuration."""
239 """Return dictionary version."""
257 """Return if it's allowed to remotely activate remote."""
258 allowed: bool = self.
_prefs_prefs.
get(PREF_REMOTE_ALLOW_REMOTE_ENABLE,
True)
263 """Return if remote is enabled on start."""
264 if not self.
_prefs_prefs.
get(PREF_ENABLE_REMOTE,
False):
271 """Return remote domain."""
272 return self.
_prefs_prefs.
get(PREF_REMOTE_DOMAIN)
276 """Return if Alexa is enabled."""
277 alexa_enabled: bool = self.
_prefs_prefs[PREF_ENABLE_ALEXA]
282 """Return if Alexa report state is enabled."""
283 return self.
_prefs_prefs.
get(PREF_ALEXA_REPORT_STATE, DEFAULT_ALEXA_REPORT_STATE)
287 """Return array of entity domains that are exposed by default to Alexa.
289 Can return None, in which case for backwards should be interpreted as allow all domains.
291 return self.
_prefs_prefs.
get(PREF_ALEXA_DEFAULT_EXPOSE)
295 """Return Alexa Entity configurations."""
296 return self.
_prefs_prefs.
get(PREF_ALEXA_ENTITY_CONFIGS, {})
300 """Return version of Alexa settings."""
301 alexa_settings_version: int = self.
_prefs_prefs[PREF_ALEXA_SETTINGS_VERSION]
302 return alexa_settings_version
306 """Return if Google is enabled."""
307 google_enabled: bool = self.
_prefs_prefs[PREF_ENABLE_GOOGLE]
308 return google_enabled
312 """Return if Google is connected."""
313 google_connected: bool = self.
_prefs_prefs[PREF_GOOGLE_CONNECTED]
314 return google_connected
318 """Return if Google report state is enabled."""
319 return self.
_prefs_prefs.
get(PREF_GOOGLE_REPORT_STATE, DEFAULT_GOOGLE_REPORT_STATE)
323 """Return if Google is allowed to unlock locks."""
324 return self.
_prefs_prefs.
get(PREF_GOOGLE_SECURE_DEVICES_PIN)
328 """Return Google Entity configurations."""
329 return self.
_prefs_prefs.
get(PREF_GOOGLE_ENTITY_CONFIGS, {})
333 """Return version of Google settings."""
334 google_settings_version: int = self.
_prefs_prefs[PREF_GOOGLE_SETTINGS_VERSION]
335 return google_settings_version
339 """Return Google webhook ID to receive local messages."""
340 google_local_webhook_id: str = self.
_prefs_prefs[PREF_GOOGLE_LOCAL_WEBHOOK_ID]
341 return google_local_webhook_id
345 """Return array of entity domains that are exposed by default to Google.
347 Can return None, in which case for backwards should be interpreted as allow all domains.
349 return self.
_prefs_prefs.
get(PREF_GOOGLE_DEFAULT_EXPOSE)
353 """Return the published cloud webhooks."""
354 return self.
_prefs_prefs.
get(PREF_CLOUDHOOKS, {})
358 """Return the instance ID."""
359 return self.
_prefs_prefs.
get(PREF_INSTANCE_ID)
363 """Return the default TTS voice.
365 The return value is a tuple of language and voice.
367 return self.
_prefs_prefs.
get(PREF_TTS_DEFAULT_VOICE, DEFAULT_TTS_DEFAULT_VOICE)
371 """Return if cloud ICE servers are enabled."""
372 cloud_ice_servers_enabled: bool = self.
_prefs_prefs.
get(
373 PREF_ENABLE_CLOUD_ICE_SERVERS,
True
375 return cloud_ice_servers_enabled
378 """Return ID of Home Assistant Cloud system user."""
384 user = await self.
_hass_hass.auth.async_create_system_user(
385 "Home Assistant Cloud", group_ids=[GROUP_ID_ADMIN], local_only=
True
387 assert user
is not None
392 """Load cloud user if available."""
393 if (user_id := self.
_prefs_prefs.
get(PREF_CLOUD_USER))
is None:
398 return await self.
_hass_hass.auth.async_get_user(user_id)
401 """Save preferences to disk."""
403 key
for key, value
in prefs.items()
if value != self.
_prefs_prefs.
get(key)
408 for listener
in self._listeners:
409 self.
_hass_hass.async_create_task(async_create_catching_coro(listener(self)))
414 """Return an empty config."""
416 PREF_ALEXA_DEFAULT_EXPOSE: DEFAULT_EXPOSED_DOMAINS,
417 PREF_ALEXA_ENTITY_CONFIGS: {},
418 PREF_ALEXA_SETTINGS_VERSION: ALEXA_SETTINGS_VERSION,
419 PREF_CLOUD_USER:
None,
421 PREF_ENABLE_ALEXA:
True,
422 PREF_ENABLE_GOOGLE:
True,
423 PREF_ENABLE_REMOTE:
False,
424 PREF_ENABLE_CLOUD_ICE_SERVERS:
True,
425 PREF_GOOGLE_CONNECTED:
False,
426 PREF_GOOGLE_DEFAULT_EXPOSE: DEFAULT_EXPOSED_DOMAINS,
427 PREF_GOOGLE_ENTITY_CONFIGS: {},
428 PREF_GOOGLE_SETTINGS_VERSION: GOOGLE_SETTINGS_VERSION,
429 PREF_GOOGLE_LOCAL_WEBHOOK_ID: webhook.async_generate_id(),
430 PREF_INSTANCE_ID: uuid.uuid4().hex,
431 PREF_GOOGLE_SECURE_DEVICES_PIN:
None,
432 PREF_REMOTE_DOMAIN:
None,
433 PREF_REMOTE_ALLOW_REMOTE_ENABLE:
True,
434 PREF_USERNAME: username,
dict[str, Any] _async_migrate_func(self, int old_major_version, int old_minor_version, dict[str, Any] old_data)
None async_erase_config(self)
dict[str, Any] as_dict(self)
list[str]|None google_default_expose(self)
bool remote_allow_remote_enable(self)
bool google_enabled(self)
User|None _load_cloud_user(self)
dict[str, Any] _empty_config(str username)
tuple[str, str] tts_default_voice(self)
None _save_prefs(self, dict[str, Any] prefs)
bool async_set_username(self, str|None username)
str|None instance_id(self)
None async_initialize(self)
dict[str, Any] alexa_entity_configs(self)
dict[str, dict[str, Any]] google_entity_configs(self)
None __init__(self, HomeAssistant hass)
int alexa_settings_version(self)
str google_local_webhook_id(self)
str|None remote_domain(self)
int google_settings_version(self)
dict[str, Any] cloudhooks(self)
bool google_connected(self)
bool alexa_report_state(self)
Callable[[], None] async_listen_updates(self, Callable[[CloudPreferences], Coroutine[Any, Any, None]] listener)
None async_update(self, *bool|UndefinedType alexa_enabled=UNDEFINED, bool|UndefinedType alexa_report_state=UNDEFINED, int|UndefinedType alexa_settings_version=UNDEFINED, bool|UndefinedType cloud_ice_servers_enabled=UNDEFINED, str|UndefinedType cloud_user=UNDEFINED, dict[str, dict[str, str|bool]]|UndefinedType cloudhooks=UNDEFINED, bool|UndefinedType google_connected=UNDEFINED, bool|UndefinedType google_enabled=UNDEFINED, bool|UndefinedType google_report_state=UNDEFINED, str|None|UndefinedType google_secure_devices_pin=UNDEFINED, int|UndefinedType google_settings_version=UNDEFINED, bool|UndefinedType remote_allow_remote_enable=UNDEFINED, str|None|UndefinedType remote_domain=UNDEFINED, bool|UndefinedType remote_enabled=UNDEFINED, tuple[str, str]|UndefinedType tts_default_voice=UNDEFINED)
list[str]|None alexa_default_expose(self)
bool remote_enabled(self)
bool cloud_ice_servers_enabled(self)
str|None google_secure_devices_pin(self)
bool google_report_state(self)
bool remove(self, _T matcher)
web.Response get(self, web.Request request, str config_key)
None async_load(HomeAssistant hass)
None async_save(self, _T data)