Add state_translated function to jinja templates (#96906)
* Add state_translated jinja function * Add tests for load_state_translations_to_cache and get_cached_translations * Cleanup state_translated template * Add tests for state_translated jinja function * Apply black formatting * Improve code quality * Apply suggestions from code review Co-authored-by: Erik Montnemery <erik@montnemery.com> * Apply suggestions from code review * Prevent invalid components from loading translations * Refactor loading translations to cache * Adjust code issues * Update homeassistant/helpers/translation.py Co-authored-by: Erik Montnemery <erik@montnemery.com> * Refactor listeners that trigger translation loading * Apply suggestions from code review Co-authored-by: Erik Montnemery <erik@montnemery.com> * Apply suggestions from code review * Adjust invalid function calls, fix code styling * Adjust code quality * Extract async_translate_state function * Apply suggestions from code review Co-authored-by: Erik Montnemery <erik@montnemery.com> * Apply suggestions from code review * Fix tests * Fix tests --------- Co-authored-by: Piotr Machowski <PiotrMachowski@users.noreply.github.com> Co-authored-by: Erik Montnemery <erik@montnemery.com>pull/110179/head
parent
d1f098c11f
commit
a2f4e99994
|
@ -35,6 +35,7 @@ from .helpers import (
|
|||
recorder,
|
||||
restore_state,
|
||||
template,
|
||||
translation,
|
||||
)
|
||||
from .helpers.dispatcher import async_dispatcher_send
|
||||
from .helpers.typing import ConfigType
|
||||
|
@ -291,6 +292,7 @@ async def async_load_base_functionality(hass: core.HomeAssistant) -> None:
|
|||
platform.uname().processor # pylint: disable=expression-not-assigned
|
||||
|
||||
# Load the registries and cache the result of platform.uname().processor
|
||||
translation.async_setup(hass)
|
||||
entity.async_setup(hass)
|
||||
template.async_setup(hass)
|
||||
await asyncio.gather(
|
||||
|
|
|
@ -80,6 +80,7 @@ from homeassistant.util.thread import ThreadWithException
|
|||
|
||||
from . import area_registry, device_registry, entity_registry, location as loc_helper
|
||||
from .singleton import singleton
|
||||
from .translation import async_translate_state
|
||||
from .typing import TemplateVarsType
|
||||
|
||||
# mypy: allow-untyped-defs, no-check-untyped-defs
|
||||
|
@ -894,6 +895,36 @@ class AllStates:
|
|||
return "<template AllStates>"
|
||||
|
||||
|
||||
class StateTranslated:
|
||||
"""Class to represent a translated state in a template."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
"""Initialize all states."""
|
||||
self._hass = hass
|
||||
|
||||
def __call__(self, entity_id: str) -> str | None:
|
||||
"""Retrieve translated state if available."""
|
||||
state = _get_state_if_valid(self._hass, entity_id)
|
||||
|
||||
if state is None:
|
||||
return STATE_UNKNOWN
|
||||
|
||||
state_value = state.state
|
||||
domain = state.domain
|
||||
device_class = state.attributes.get("device_class")
|
||||
entry = entity_registry.async_get(self._hass).async_get(entity_id)
|
||||
platform = None if entry is None else entry.platform
|
||||
translation_key = None if entry is None else entry.translation_key
|
||||
|
||||
return async_translate_state(
|
||||
self._hass, state_value, domain, platform, translation_key, device_class
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Representation of Translated state."""
|
||||
return "<template StateTranslated>"
|
||||
|
||||
|
||||
class DomainStates:
|
||||
"""Class to expose a specific HA domain as attributes."""
|
||||
|
||||
|
@ -2626,6 +2657,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||
"is_state_attr",
|
||||
"state_attr",
|
||||
"states",
|
||||
"state_translated",
|
||||
"has_value",
|
||||
"utcnow",
|
||||
"now",
|
||||
|
@ -2676,6 +2708,8 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||
self.filters["state_attr"] = self.globals["state_attr"]
|
||||
self.globals["states"] = AllStates(hass)
|
||||
self.filters["states"] = self.globals["states"]
|
||||
self.globals["state_translated"] = StateTranslated(hass)
|
||||
self.filters["state_translated"] = self.globals["state_translated"]
|
||||
self.globals["has_value"] = hassfunction(has_value)
|
||||
self.filters["has_value"] = self.globals["has_value"]
|
||||
self.tests["has_value"] = hassfunction(has_value, pass_eval_context)
|
||||
|
@ -2688,7 +2722,9 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
|||
|
||||
def is_safe_callable(self, obj):
|
||||
"""Test if callback is safe."""
|
||||
return isinstance(obj, AllStates) or super().is_safe_callable(obj)
|
||||
return isinstance(
|
||||
obj, (AllStates, StateTranslated)
|
||||
) or super().is_safe_callable(obj)
|
||||
|
||||
def is_safe_attribute(self, obj, attr, value):
|
||||
"""Test if attribute is safe."""
|
||||
|
|
|
@ -7,7 +7,13 @@ import logging
|
|||
import string
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.const import (
|
||||
EVENT_COMPONENT_LOADED,
|
||||
EVENT_CORE_CONFIG_UPDATE,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.loader import (
|
||||
Integration,
|
||||
async_get_config_flows,
|
||||
|
@ -199,12 +205,11 @@ class _TranslationCache:
|
|||
self.cache: dict[str, dict[str, dict[str, dict[str, str]]]] = {}
|
||||
self.lock = asyncio.Lock()
|
||||
|
||||
async def async_fetch(
|
||||
async def async_load(
|
||||
self,
|
||||
language: str,
|
||||
category: str,
|
||||
components: set[str],
|
||||
) -> dict[str, str]:
|
||||
) -> None:
|
||||
"""Load resources into the cache."""
|
||||
loaded = self.loaded.setdefault(language, set())
|
||||
if components_to_load := components - loaded:
|
||||
|
@ -218,6 +223,24 @@ class _TranslationCache:
|
|||
if components_to_load := components - loaded:
|
||||
await self._async_load(language, components_to_load)
|
||||
|
||||
async def async_fetch(
|
||||
self,
|
||||
language: str,
|
||||
category: str,
|
||||
components: set[str],
|
||||
) -> dict[str, str]:
|
||||
"""Load resources into the cache and return them."""
|
||||
await self.async_load(language, components)
|
||||
|
||||
return self.get_cached(language, category, components)
|
||||
|
||||
def get_cached(
|
||||
self,
|
||||
language: str,
|
||||
category: str,
|
||||
components: set[str],
|
||||
) -> dict[str, str]:
|
||||
"""Read resources from the cache."""
|
||||
category_cache = self.cache.get(language, {}).get(category, {})
|
||||
# If only one component was requested, return it directly
|
||||
# to avoid merging the dictionaries and keeping additional
|
||||
|
@ -354,14 +377,67 @@ async def async_get_translations(
|
|||
) -> dict[str, str]:
|
||||
"""Return all backend translations.
|
||||
|
||||
If integration specified, load it for that one.
|
||||
Otherwise default to loaded integrations combined with config flow
|
||||
If integration is specified, load it for that one.
|
||||
Otherwise, default to loaded integrations combined with config flow
|
||||
integrations if config_flow is true.
|
||||
"""
|
||||
if integrations is None and config_flow:
|
||||
components = (await async_get_config_flows(hass)) - hass.config.components
|
||||
else:
|
||||
components = _async_get_components(hass, category, integrations)
|
||||
|
||||
cache: _TranslationCache = hass.data[TRANSLATION_FLATTEN_CACHE]
|
||||
|
||||
return await cache.async_fetch(language, category, components)
|
||||
|
||||
|
||||
async def _async_load_translations(
|
||||
hass: HomeAssistant,
|
||||
language: str,
|
||||
category: str,
|
||||
integration: str | None,
|
||||
) -> None:
|
||||
"""Prime backend translation cache.
|
||||
|
||||
If integration is not specified, translation cache is primed for all loaded integrations.
|
||||
"""
|
||||
components = _async_get_components(
|
||||
hass, category, [integration] if integration is not None else None
|
||||
)
|
||||
|
||||
cache = hass.data[TRANSLATION_FLATTEN_CACHE]
|
||||
await cache.async_load(language, components)
|
||||
|
||||
|
||||
@callback
|
||||
def async_get_cached_translations(
|
||||
hass: HomeAssistant,
|
||||
language: str,
|
||||
category: str,
|
||||
integration: str | None = None,
|
||||
) -> dict[str, str]:
|
||||
"""Return all cached backend translations.
|
||||
|
||||
If integration is specified, return translations for it.
|
||||
Otherwise, default to all loaded integrations.
|
||||
"""
|
||||
components = _async_get_components(
|
||||
hass, category, [integration] if integration is not None else None
|
||||
)
|
||||
|
||||
cache: _TranslationCache = hass.data[TRANSLATION_FLATTEN_CACHE]
|
||||
return cache.get_cached(language, category, components)
|
||||
|
||||
|
||||
@callback
|
||||
def _async_get_components(
|
||||
hass: HomeAssistant,
|
||||
category: str,
|
||||
integrations: Iterable[str] | None = None,
|
||||
) -> set[str]:
|
||||
"""Return a set of components for which translations should be loaded."""
|
||||
if integrations is not None:
|
||||
components = set(integrations)
|
||||
elif config_flow:
|
||||
components = (await async_get_config_flows(hass)) - hass.config.components
|
||||
elif category in ("state", "entity_component", "services"):
|
||||
components = hass.config.components
|
||||
else:
|
||||
|
@ -369,10 +445,91 @@ async def async_get_translations(
|
|||
components = {
|
||||
component for component in hass.config.components if "." not in component
|
||||
}
|
||||
return components
|
||||
|
||||
if TRANSLATION_FLATTEN_CACHE in hass.data:
|
||||
cache: _TranslationCache = hass.data[TRANSLATION_FLATTEN_CACHE]
|
||||
else:
|
||||
cache = hass.data[TRANSLATION_FLATTEN_CACHE] = _TranslationCache(hass)
|
||||
|
||||
return await cache.async_fetch(language, category, components)
|
||||
async def _async_load_state_translations_to_cache(
|
||||
hass: HomeAssistant,
|
||||
language: str,
|
||||
integration: str | None,
|
||||
) -> None:
|
||||
"""Load state translations to cache."""
|
||||
await _async_load_translations(hass, language, "entity", integration)
|
||||
await _async_load_translations(hass, language, "state", integration)
|
||||
await _async_load_translations(hass, language, "entity_component", integration)
|
||||
|
||||
|
||||
@callback
|
||||
def async_setup(hass: HomeAssistant) -> None:
|
||||
"""Create translation cache and register listeners for translation loaders.
|
||||
|
||||
Listeners load translations for every loaded component and after config change.
|
||||
"""
|
||||
|
||||
hass.data[TRANSLATION_FLATTEN_CACHE] = _TranslationCache(hass)
|
||||
|
||||
async def load_translations(event: Event) -> None:
|
||||
if "language" in event.data:
|
||||
language = hass.config.language
|
||||
_LOGGER.debug("Loading translations for language: %s", language)
|
||||
await _async_load_state_translations_to_cache(hass, language, None)
|
||||
|
||||
async def load_translations_for_component(event: Event) -> None:
|
||||
component = event.data.get("component")
|
||||
# Platforms don't have their own translations, skip them
|
||||
if component is None or "." in str(component):
|
||||
return
|
||||
language = hass.config.language
|
||||
_LOGGER.debug(
|
||||
"Loading translations for language: %s and component: %s",
|
||||
hass.config.language,
|
||||
component,
|
||||
)
|
||||
await _async_load_state_translations_to_cache(hass, language, component)
|
||||
|
||||
hass.bus.async_listen(EVENT_COMPONENT_LOADED, load_translations_for_component)
|
||||
hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, load_translations)
|
||||
|
||||
|
||||
@callback
|
||||
def async_translate_state(
|
||||
hass: HomeAssistant,
|
||||
state: str,
|
||||
domain: str,
|
||||
platform: str | None,
|
||||
translation_key: str | None,
|
||||
device_class: str | None,
|
||||
) -> str:
|
||||
"""Translate provided state using cached translations for currently selected language."""
|
||||
if state in [STATE_UNAVAILABLE, STATE_UNKNOWN]:
|
||||
return state
|
||||
language = hass.config.language
|
||||
if platform is not None and translation_key is not None:
|
||||
localize_key = (
|
||||
f"component.{platform}.entity.{domain}.{translation_key}.state.{state}"
|
||||
)
|
||||
translations = async_get_cached_translations(hass, language, "entity")
|
||||
if localize_key in translations:
|
||||
return translations[localize_key]
|
||||
|
||||
translations = async_get_cached_translations(hass, language, "entity_component")
|
||||
if device_class is not None:
|
||||
localize_key = (
|
||||
f"component.{domain}.entity_component.{device_class}.state.{state}"
|
||||
)
|
||||
if localize_key in translations:
|
||||
return translations[localize_key]
|
||||
localize_key = f"component.{domain}.entity_component._.state.{state}"
|
||||
if localize_key in translations:
|
||||
return translations[localize_key]
|
||||
|
||||
translations = async_get_cached_translations(hass, language, "state", domain)
|
||||
if device_class is not None:
|
||||
localize_key = f"component.{domain}.state.{device_class}.{state}"
|
||||
if localize_key in translations:
|
||||
return translations[localize_key]
|
||||
localize_key = f"component.{domain}.state._.{state}"
|
||||
if localize_key in translations:
|
||||
return translations[localize_key]
|
||||
|
||||
return state
|
||||
|
|
|
@ -69,6 +69,7 @@ from homeassistant.helpers import (
|
|||
restore_state,
|
||||
restore_state as rs,
|
||||
storage,
|
||||
translation,
|
||||
)
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
|
@ -267,6 +268,11 @@ async def async_test_home_assistant(event_loop, load_registries=True):
|
|||
# Load the registries
|
||||
entity.async_setup(hass)
|
||||
loader.async_setup(hass)
|
||||
|
||||
# setup translation cache instead of calling translation.async_setup(hass)
|
||||
hass.data[translation.TRANSLATION_FLATTEN_CACHE] = translation._TranslationCache(
|
||||
hass
|
||||
)
|
||||
if load_registries:
|
||||
with patch(
|
||||
"homeassistant.helpers.storage.Store.async_load", return_value=None
|
||||
|
|
|
@ -38,6 +38,7 @@ from homeassistant.helpers import (
|
|||
entity,
|
||||
entity_registry as er,
|
||||
template,
|
||||
translation,
|
||||
)
|
||||
from homeassistant.helpers.entity_platform import EntityPlatform
|
||||
from homeassistant.helpers.json import json_dumps
|
||||
|
@ -1955,6 +1956,129 @@ def test_states_function(hass: HomeAssistant) -> None:
|
|||
assert tpl.async_render() == "available"
|
||||
|
||||
|
||||
async def test_state_translated(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
):
|
||||
"""Test state_translated method."""
|
||||
assert await async_setup_component(
|
||||
hass,
|
||||
"binary_sensor",
|
||||
{
|
||||
"binary_sensor": {
|
||||
"platform": "group",
|
||||
"name": "Grouped",
|
||||
"entities": ["binary_sensor.first", "binary_sensor.second"],
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
await translation._async_load_state_translations_to_cache(hass, "en", None)
|
||||
|
||||
hass.states.async_set("switch.without_translations", "on", attributes={})
|
||||
hass.states.async_set("binary_sensor.without_device_class", "on", attributes={})
|
||||
hass.states.async_set(
|
||||
"binary_sensor.with_device_class", "on", attributes={"device_class": "motion"}
|
||||
)
|
||||
hass.states.async_set(
|
||||
"binary_sensor.with_unknown_device_class",
|
||||
"on",
|
||||
attributes={"device_class": "unknown_class"},
|
||||
)
|
||||
hass.states.async_set(
|
||||
"some_domain.with_device_class_1",
|
||||
"off",
|
||||
attributes={"device_class": "some_device_class"},
|
||||
)
|
||||
hass.states.async_set(
|
||||
"some_domain.with_device_class_2",
|
||||
"foo",
|
||||
attributes={"device_class": "some_device_class"},
|
||||
)
|
||||
hass.states.async_set("domain.is_unavailable", "unavailable", attributes={})
|
||||
hass.states.async_set("domain.is_unknown", "unknown", attributes={})
|
||||
|
||||
config_entry = MockConfigEntry(domain="light")
|
||||
entity_registry.async_get_or_create(
|
||||
"light",
|
||||
"hue",
|
||||
"5678",
|
||||
config_entry=config_entry,
|
||||
translation_key="translation_key",
|
||||
)
|
||||
hass.states.async_set("light.hue_5678", "on", attributes={})
|
||||
|
||||
tpl = template.Template(
|
||||
'{{ state_translated("switch.without_translations") }}', hass
|
||||
)
|
||||
assert tpl.async_render() == "on"
|
||||
|
||||
tp2 = template.Template(
|
||||
'{{ state_translated("binary_sensor.without_device_class") }}', hass
|
||||
)
|
||||
assert tp2.async_render() == "On"
|
||||
|
||||
tpl3 = template.Template(
|
||||
'{{ state_translated("binary_sensor.with_device_class") }}', hass
|
||||
)
|
||||
assert tpl3.async_render() == "Detected"
|
||||
|
||||
tpl4 = template.Template(
|
||||
'{{ state_translated("binary_sensor.with_unknown_device_class") }}', hass
|
||||
)
|
||||
assert tpl4.async_render() == "On"
|
||||
|
||||
with pytest.raises(TemplateError):
|
||||
template.Template(
|
||||
'{{ state_translated("contextfunction") }}', hass
|
||||
).async_render()
|
||||
|
||||
tpl6 = template.Template('{{ state_translated("switch.invalid") }}', hass)
|
||||
assert tpl6.async_render() == "unknown"
|
||||
|
||||
with pytest.raises(TemplateError):
|
||||
template.Template('{{ state_translated("-invalid") }}', hass).async_render()
|
||||
|
||||
def mock_get_cached_translations(
|
||||
_hass: HomeAssistant,
|
||||
_language: str,
|
||||
category: str,
|
||||
_integrations: Iterable[str] | None = None,
|
||||
):
|
||||
if category == "entity":
|
||||
return {
|
||||
"component.hue.entity.light.translation_key.state.on": "state_is_on"
|
||||
}
|
||||
if category == "state":
|
||||
return {
|
||||
"component.some_domain.state.some_device_class.off": "state_is_off",
|
||||
"component.some_domain.state._.foo": "state_is_foo",
|
||||
}
|
||||
return {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
side_effect=mock_get_cached_translations,
|
||||
):
|
||||
tpl8 = template.Template('{{ state_translated("light.hue_5678") }}', hass)
|
||||
assert tpl8.async_render() == "state_is_on"
|
||||
|
||||
tpl9 = template.Template(
|
||||
'{{ state_translated("some_domain.with_device_class_1") }}', hass
|
||||
)
|
||||
assert tpl9.async_render() == "state_is_off"
|
||||
|
||||
tpl10 = template.Template(
|
||||
'{{ state_translated("some_domain.with_device_class_2") }}', hass
|
||||
)
|
||||
assert tpl10.async_render() == "state_is_foo"
|
||||
|
||||
tpl11 = template.Template('{{ state_translated("domain.is_unavailable") }}', hass)
|
||||
assert tpl11.async_render() == "unavailable"
|
||||
|
||||
tpl12 = template.Template('{{ state_translated("domain.is_unknown") }}', hass)
|
||||
assert tpl12.async_render() == "unknown"
|
||||
|
||||
|
||||
def test_has_value(hass: HomeAssistant) -> None:
|
||||
"""Test has_value method."""
|
||||
hass.states.async_set("test.value1", 1)
|
||||
|
|
|
@ -2,10 +2,11 @@
|
|||
import asyncio
|
||||
from os import path
|
||||
import pathlib
|
||||
from unittest.mock import Mock, patch
|
||||
from unittest.mock import Mock, call, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.const import EVENT_COMPONENT_LOADED, EVENT_CORE_CONFIG_UPDATE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.generated import config_flows
|
||||
from homeassistant.helpers import translation
|
||||
|
@ -510,3 +511,217 @@ async def test_custom_component_translations(
|
|||
hass.config.components.add("test_embedded")
|
||||
hass.config.components.add("test_package")
|
||||
assert await translation.async_get_translations(hass, "en", "state") == {}
|
||||
|
||||
|
||||
async def test_load_state_translations_to_cache(
|
||||
hass: HomeAssistant, mock_config_flows, enable_custom_integrations: None
|
||||
):
|
||||
"""Test the load state translations to cache helper."""
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation._async_load_translations",
|
||||
) as mock:
|
||||
await translation._async_load_state_translations_to_cache(hass, "en", None)
|
||||
mock.assert_has_calls(
|
||||
[
|
||||
call(hass, "en", "entity", None),
|
||||
call(hass, "en", "state", None),
|
||||
call(hass, "en", "entity_component", None),
|
||||
]
|
||||
)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation._async_load_translations",
|
||||
) as mock:
|
||||
await translation._async_load_state_translations_to_cache(
|
||||
hass, "en", "some_integration"
|
||||
)
|
||||
mock.assert_has_calls(
|
||||
[
|
||||
call(hass, "en", "entity", "some_integration"),
|
||||
call(hass, "en", "state", "some_integration"),
|
||||
call(hass, "en", "entity_component", "some_integration"),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
async def test_get_cached_translations(
|
||||
hass: HomeAssistant, mock_config_flows, enable_custom_integrations: None
|
||||
):
|
||||
"""Test the get cached translations helper."""
|
||||
translations = translation.async_get_cached_translations(hass, "en", "state")
|
||||
assert translations == {}
|
||||
|
||||
assert await async_setup_component(hass, "switch", {"switch": {"platform": "test"}})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await translation._async_load_state_translations_to_cache(hass, "en", None)
|
||||
translations = translation.async_get_cached_translations(hass, "en", "state")
|
||||
|
||||
assert translations["component.switch.state.string1"] == "Value 1"
|
||||
assert translations["component.switch.state.string2"] == "Value 2"
|
||||
|
||||
await translation._async_load_state_translations_to_cache(hass, "de", None)
|
||||
translations = translation.async_get_cached_translations(hass, "de", "state")
|
||||
assert "component.switch.something" not in translations
|
||||
assert translations["component.switch.state.string1"] == "German Value 1"
|
||||
assert translations["component.switch.state.string2"] == "German Value 2"
|
||||
|
||||
# Test a partial translation
|
||||
await translation._async_load_state_translations_to_cache(hass, "es", None)
|
||||
translations = translation.async_get_cached_translations(hass, "es", "state")
|
||||
assert translations["component.switch.state.string1"] == "Spanish Value 1"
|
||||
assert translations["component.switch.state.string2"] == "Value 2"
|
||||
|
||||
# Test that an untranslated language falls back to English.
|
||||
await translation._async_load_state_translations_to_cache(
|
||||
hass, "invalid-language", None
|
||||
)
|
||||
translations = translation.async_get_cached_translations(
|
||||
hass, "invalid-language", "state"
|
||||
)
|
||||
assert translations["component.switch.state.string1"] == "Value 1"
|
||||
assert translations["component.switch.state.string2"] == "Value 2"
|
||||
|
||||
|
||||
async def test_setup(hass: HomeAssistant):
|
||||
"""Test the setup load listeners helper."""
|
||||
translation.async_setup(hass)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation._async_load_state_translations_to_cache",
|
||||
) as mock:
|
||||
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {"component": "loaded_component"})
|
||||
await hass.async_block_till_done()
|
||||
mock.assert_called_once_with(hass, hass.config.language, "loaded_component")
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation._async_load_state_translations_to_cache",
|
||||
) as mock:
|
||||
hass.bus.async_fire(EVENT_COMPONENT_LOADED, {"component": "config.component"})
|
||||
await hass.async_block_till_done()
|
||||
mock.assert_not_called()
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation._async_load_state_translations_to_cache",
|
||||
) as mock:
|
||||
hass.bus.async_fire(EVENT_CORE_CONFIG_UPDATE, {"language": "en"})
|
||||
await hass.async_block_till_done()
|
||||
mock.assert_called_once_with(hass, hass.config.language, None)
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation._async_load_state_translations_to_cache",
|
||||
) as mock:
|
||||
hass.bus.async_fire(EVENT_CORE_CONFIG_UPDATE, {})
|
||||
await hass.async_block_till_done()
|
||||
mock.assert_not_called()
|
||||
|
||||
|
||||
async def test_translate_state(hass: HomeAssistant):
|
||||
"""Test the state translation helper."""
|
||||
result = translation.async_translate_state(
|
||||
hass, "unavailable", "binary_sensor", "platform", "translation_key", None
|
||||
)
|
||||
assert result == "unavailable"
|
||||
|
||||
result = translation.async_translate_state(
|
||||
hass, "unknown", "binary_sensor", "platform", "translation_key", None
|
||||
)
|
||||
assert result == "unknown"
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
return_value={
|
||||
"component.platform.entity.binary_sensor.translation_key.state.on": "TRANSLATED"
|
||||
},
|
||||
) as mock:
|
||||
result = translation.async_translate_state(
|
||||
hass, "on", "binary_sensor", "platform", "translation_key", None
|
||||
)
|
||||
mock.assert_called_once_with(hass, hass.config.language, "entity")
|
||||
assert result == "TRANSLATED"
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
return_value={
|
||||
"component.binary_sensor.entity_component.device_class.state.on": "TRANSLATED"
|
||||
},
|
||||
) as mock:
|
||||
result = translation.async_translate_state(
|
||||
hass, "on", "binary_sensor", "platform", None, "device_class"
|
||||
)
|
||||
mock.assert_called_once_with(hass, hass.config.language, "entity_component")
|
||||
assert result == "TRANSLATED"
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
return_value={
|
||||
"component.binary_sensor.entity_component._.state.on": "TRANSLATED"
|
||||
},
|
||||
) as mock:
|
||||
result = translation.async_translate_state(
|
||||
hass, "on", "binary_sensor", "platform", None, None
|
||||
)
|
||||
mock.assert_called_once_with(hass, hass.config.language, "entity_component")
|
||||
assert result == "TRANSLATED"
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
return_value={"component.binary_sensor.state.device_class.on": "TRANSLATED"},
|
||||
) as mock:
|
||||
result = translation.async_translate_state(
|
||||
hass, "on", "binary_sensor", "platform", None, "device_class"
|
||||
)
|
||||
mock.assert_has_calls(
|
||||
[
|
||||
call(hass, hass.config.language, "entity_component"),
|
||||
call(hass, hass.config.language, "state", "binary_sensor"),
|
||||
]
|
||||
)
|
||||
assert result == "TRANSLATED"
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
return_value={"component.binary_sensor.state._.on": "TRANSLATED"},
|
||||
) as mock:
|
||||
result = translation.async_translate_state(
|
||||
hass, "on", "binary_sensor", "platform", None, None
|
||||
)
|
||||
mock.assert_has_calls(
|
||||
[
|
||||
call(hass, hass.config.language, "entity_component"),
|
||||
call(hass, hass.config.language, "state", "binary_sensor"),
|
||||
]
|
||||
)
|
||||
assert result == "TRANSLATED"
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
return_value={},
|
||||
) as mock:
|
||||
result = translation.async_translate_state(
|
||||
hass, "on", "binary_sensor", "platform", None, None
|
||||
)
|
||||
mock.assert_has_calls(
|
||||
[
|
||||
call(hass, hass.config.language, "entity_component"),
|
||||
call(hass, hass.config.language, "state", "binary_sensor"),
|
||||
]
|
||||
)
|
||||
assert result == "on"
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
return_value={},
|
||||
) as mock:
|
||||
result = translation.async_translate_state(
|
||||
hass, "on", "binary_sensor", "platform", "translation_key", "device_class"
|
||||
)
|
||||
mock.assert_has_calls(
|
||||
[
|
||||
call(hass, hass.config.language, "entity"),
|
||||
call(hass, hass.config.language, "entity_component"),
|
||||
call(hass, hass.config.language, "state", "binary_sensor"),
|
||||
]
|
||||
)
|
||||
assert result == "on"
|
||||
|
|
Loading…
Reference in New Issue