mirror of
https://github.com/home-assistant/core.git
synced 2026-03-12 05:51:59 +01:00
Compare commits
1 Commits
copilot/su
...
windows-98
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df06a5878c |
@@ -19,7 +19,7 @@
|
||||
],
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"integration_type": "system",
|
||||
"preview_features": { "winter_mode": {} },
|
||||
"preview_features": { "windows_98": {}, "winter_mode": {} },
|
||||
"quality_scale": "internal",
|
||||
"requirements": ["home-assistant-frontend==20260304.0"]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
{
|
||||
"preview_features": {
|
||||
"windows_98": {
|
||||
"description": "Transforms your dashboard with a nostalgic Windows 98 look.",
|
||||
"disable_confirmation": "Your dashboard will return to its normal look. You can re-enable this at any time in Labs settings.",
|
||||
"enable_confirmation": "Your dashboard will be transformed with a Windows 98 theme. You can turn this off at any time in Labs settings.",
|
||||
"name": "Windows 98"
|
||||
},
|
||||
"winter_mode": {
|
||||
"description": "Adds falling snowflakes on your screen. Get your home ready for winter! ❄️\n\nIf you have animations disabled in your device accessibility settings, this feature will not work.",
|
||||
"disable_confirmation": "Snowflakes will no longer fall on your screen. You can re-enable this at any time in Labs settings.",
|
||||
|
||||
5
homeassistant/generated/labs.py
generated
5
homeassistant/generated/labs.py
generated
@@ -19,6 +19,11 @@ LABS_PREVIEW_FEATURES = {
|
||||
},
|
||||
},
|
||||
"frontend": {
|
||||
"windows_98": {
|
||||
"feedback_url": "",
|
||||
"learn_more_url": "",
|
||||
"report_issue_url": "",
|
||||
},
|
||||
"winter_mode": {
|
||||
"feedback_url": "",
|
||||
"learn_more_url": "",
|
||||
|
||||
@@ -4,7 +4,6 @@ from __future__ import annotations
|
||||
|
||||
from ast import literal_eval
|
||||
import asyncio
|
||||
from enum import Enum
|
||||
import collections.abc
|
||||
from collections.abc import Callable, Generator, Iterable
|
||||
from copy import deepcopy
|
||||
@@ -58,10 +57,7 @@ from homeassistant.core import (
|
||||
from homeassistant.exceptions import TemplateError
|
||||
from homeassistant.helpers import entity_registry as er, location as loc_helper
|
||||
from homeassistant.helpers.singleton import singleton
|
||||
from homeassistant.helpers.translation import (
|
||||
async_translate_state,
|
||||
async_translate_state_attr,
|
||||
)
|
||||
from homeassistant.helpers.translation import async_translate_state
|
||||
from homeassistant.helpers.typing import TemplateVarsType
|
||||
from homeassistant.util import convert, location as location_util
|
||||
from homeassistant.util.async_ import run_callback_threadsafe
|
||||
@@ -811,48 +807,6 @@ class StateTranslated:
|
||||
return "<template StateTranslated>"
|
||||
|
||||
|
||||
class StateAttrTranslated:
|
||||
"""Class to represent a translated state attribute value in a template."""
|
||||
|
||||
def __init__(self, hass: HomeAssistant) -> None:
|
||||
"""Initialize."""
|
||||
self._hass = hass
|
||||
|
||||
def __call__(self, entity_id: str, attribute: str) -> Any:
|
||||
"""Retrieve translated state attribute value if available."""
|
||||
state = _get_state_if_valid(self._hass, entity_id)
|
||||
|
||||
if state is None:
|
||||
return None
|
||||
|
||||
attr_value = state.attributes.get(attribute)
|
||||
if attr_value is None:
|
||||
return None
|
||||
|
||||
if not isinstance(attr_value, str | Enum):
|
||||
return attr_value
|
||||
|
||||
domain = state.domain
|
||||
device_class = state.attributes.get("device_class")
|
||||
entry = er.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_attr(
|
||||
self._hass,
|
||||
str(attr_value),
|
||||
domain,
|
||||
platform,
|
||||
translation_key,
|
||||
device_class,
|
||||
attribute,
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
"""Representation of Translated state attribute."""
|
||||
return "<template StateAttrTranslated>"
|
||||
|
||||
|
||||
class DomainStates:
|
||||
"""Class to expose a specific HA domain as attributes."""
|
||||
|
||||
@@ -2035,7 +1989,6 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
||||
"is_state_attr",
|
||||
"is_state",
|
||||
"state_attr",
|
||||
"state_attr_translated",
|
||||
"state_translated",
|
||||
"states",
|
||||
]
|
||||
@@ -2083,11 +2036,9 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
||||
self.globals["is_state_attr"] = hassfunction(is_state_attr)
|
||||
self.globals["is_state"] = hassfunction(is_state)
|
||||
self.globals["state_attr"] = hassfunction(state_attr)
|
||||
self.globals["state_attr_translated"] = StateAttrTranslated(hass)
|
||||
self.globals["state_translated"] = StateTranslated(hass)
|
||||
self.globals["states"] = AllStates(hass)
|
||||
self.filters["state_attr"] = self.globals["state_attr"]
|
||||
self.filters["state_attr_translated"] = self.globals["state_attr_translated"]
|
||||
self.filters["state_translated"] = self.globals["state_translated"]
|
||||
self.filters["states"] = self.globals["states"]
|
||||
self.tests["is_state_attr"] = hassfunction(is_state_attr, pass_eval_context)
|
||||
@@ -2096,7 +2047,7 @@ class TemplateEnvironment(ImmutableSandboxedEnvironment):
|
||||
def is_safe_callable(self, obj):
|
||||
"""Test if callback is safe."""
|
||||
return isinstance(
|
||||
obj, (AllStates, StateAttrTranslated, StateTranslated)
|
||||
obj, (AllStates, StateTranslated)
|
||||
) or super().is_safe_callable(obj)
|
||||
|
||||
def is_safe_attribute(self, obj, attr, value):
|
||||
|
||||
@@ -492,43 +492,3 @@ def async_translate_state(
|
||||
return translations[localize_key]
|
||||
|
||||
return state
|
||||
|
||||
|
||||
@callback
|
||||
def async_translate_state_attr(
|
||||
hass: HomeAssistant,
|
||||
attr_value: str,
|
||||
domain: str,
|
||||
platform: str | None,
|
||||
translation_key: str | None,
|
||||
device_class: str | None,
|
||||
attribute_name: str,
|
||||
) -> str:
|
||||
"""Translate provided state attribute value using cached translations for currently selected language."""
|
||||
language = hass.config.language
|
||||
if platform is not None and translation_key is not None:
|
||||
localize_key = (
|
||||
f"component.{platform}.entity.{domain}"
|
||||
f".{translation_key}.state_attributes.{attribute_name}"
|
||||
f".state.{attr_value}"
|
||||
)
|
||||
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}"
|
||||
f".state_attributes.{attribute_name}.state.{attr_value}"
|
||||
)
|
||||
if localize_key in translations:
|
||||
return translations[localize_key]
|
||||
localize_key = (
|
||||
f"component.{domain}.entity_component._"
|
||||
f".state_attributes.{attribute_name}.state.{attr_value}"
|
||||
)
|
||||
if localize_key in translations:
|
||||
return translations[localize_key]
|
||||
|
||||
return attr_value
|
||||
|
||||
@@ -1043,113 +1043,6 @@ async def test_state_translated(
|
||||
assert result == "unknown"
|
||||
|
||||
|
||||
async def test_state_attr_translated(
|
||||
hass: HomeAssistant, entity_registry: er.EntityRegistry
|
||||
) -> None:
|
||||
"""Test state_attr_translated method."""
|
||||
await translation._async_get_translations_cache(hass).async_load("en", set())
|
||||
|
||||
hass.states.async_set(
|
||||
"climate.living_room",
|
||||
"heat",
|
||||
attributes={"fan_mode": "auto", "hvac_action": "heating"},
|
||||
)
|
||||
hass.states.async_set(
|
||||
"switch.test",
|
||||
"on",
|
||||
attributes={"some_attr": "some_value", "numeric_attr": 42, "bool_attr": True},
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain="climate")
|
||||
config_entry.add_to_hass(hass)
|
||||
entity_registry.async_get_or_create(
|
||||
"climate",
|
||||
"test_platform",
|
||||
"5678",
|
||||
config_entry=config_entry,
|
||||
translation_key="my_climate",
|
||||
)
|
||||
hass.states.async_set(
|
||||
"climate.test_platform_5678",
|
||||
"heat",
|
||||
attributes={"fan_mode": "auto"},
|
||||
)
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
'{{ state_attr_translated("switch.test", "some_attr") }}',
|
||||
)
|
||||
assert result == "some_value"
|
||||
|
||||
# Non-string attributes should be returned as-is without type conversion
|
||||
result = render(
|
||||
hass,
|
||||
'{{ state_attr_translated("switch.test", "numeric_attr") }}',
|
||||
)
|
||||
assert result == 42
|
||||
assert isinstance(result, int)
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
'{{ state_attr_translated("switch.test", "bool_attr") }}',
|
||||
)
|
||||
assert result is True
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
'{{ state_attr_translated("climate.non_existent", "fan_mode") }}',
|
||||
)
|
||||
assert result is None
|
||||
|
||||
with pytest.raises(TemplateError):
|
||||
render(hass, '{{ state_attr_translated("-invalid", "fan_mode") }}')
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
'{{ state_attr_translated("climate.living_room", "non_existent") }}',
|
||||
)
|
||||
assert result is None
|
||||
|
||||
def mock_get_cached_translations(
|
||||
_hass: HomeAssistant,
|
||||
_language: str,
|
||||
category: str,
|
||||
_integrations: Iterable[str] | None = None,
|
||||
):
|
||||
if category == "entity":
|
||||
return {
|
||||
"component.test_platform.entity.climate.my_climate.state_attributes.fan_mode.state.auto": "Platform Automatic",
|
||||
}
|
||||
if category == "entity_component":
|
||||
return {
|
||||
"component.climate.entity_component._.state_attributes.fan_mode.state.auto": "Automatic",
|
||||
"component.climate.entity_component._.state_attributes.hvac_action.state.heating": "Heating",
|
||||
}
|
||||
return {}
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
side_effect=mock_get_cached_translations,
|
||||
):
|
||||
result = render(
|
||||
hass,
|
||||
'{{ state_attr_translated("climate.living_room", "fan_mode") }}',
|
||||
)
|
||||
assert result == "Automatic"
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
'{{ state_attr_translated("climate.living_room", "hvac_action") }}',
|
||||
)
|
||||
assert result == "Heating"
|
||||
|
||||
result = render(
|
||||
hass,
|
||||
'{{ state_attr_translated("climate.test_platform_5678", "fan_mode") }}',
|
||||
)
|
||||
assert result == "Platform Automatic"
|
||||
|
||||
|
||||
def test_has_value(hass: HomeAssistant) -> None:
|
||||
"""Test has_value method."""
|
||||
hass.states.async_set("test.value1", 1)
|
||||
|
||||
@@ -683,92 +683,6 @@ async def test_translate_state(hass: HomeAssistant) -> None:
|
||||
assert result == "on"
|
||||
|
||||
|
||||
async def test_translate_state_attr(hass: HomeAssistant) -> None:
|
||||
"""Test the state attribute translation helper."""
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
return_value={
|
||||
"component.platform.entity.climate.translation_key.state_attributes.fan_mode.state.auto": "TRANSLATED"
|
||||
},
|
||||
) as mock:
|
||||
result = translation.async_translate_state_attr(
|
||||
hass,
|
||||
"auto",
|
||||
"climate",
|
||||
"platform",
|
||||
"translation_key",
|
||||
None,
|
||||
"fan_mode",
|
||||
)
|
||||
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.climate.entity_component.device_class.state_attributes.fan_mode.state.auto": "TRANSLATED"
|
||||
},
|
||||
) as mock:
|
||||
result = translation.async_translate_state_attr(
|
||||
hass,
|
||||
"auto",
|
||||
"climate",
|
||||
"platform",
|
||||
None,
|
||||
"device_class",
|
||||
"fan_mode",
|
||||
)
|
||||
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.climate.entity_component._.state_attributes.fan_mode.state.auto": "TRANSLATED"
|
||||
},
|
||||
) as mock:
|
||||
result = translation.async_translate_state_attr(
|
||||
hass, "auto", "climate", "platform", None, None, "fan_mode"
|
||||
)
|
||||
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={},
|
||||
) as mock:
|
||||
result = translation.async_translate_state_attr(
|
||||
hass, "auto", "climate", "platform", None, None, "fan_mode"
|
||||
)
|
||||
mock.assert_has_calls(
|
||||
[
|
||||
call(hass, hass.config.language, "entity_component"),
|
||||
]
|
||||
)
|
||||
assert result == "auto"
|
||||
|
||||
with patch(
|
||||
"homeassistant.helpers.translation.async_get_cached_translations",
|
||||
return_value={},
|
||||
) as mock:
|
||||
result = translation.async_translate_state_attr(
|
||||
hass,
|
||||
"auto",
|
||||
"climate",
|
||||
"platform",
|
||||
"translation_key",
|
||||
"device_class",
|
||||
"fan_mode",
|
||||
)
|
||||
mock.assert_has_calls(
|
||||
[
|
||||
call(hass, hass.config.language, "entity"),
|
||||
call(hass, hass.config.language, "entity_component"),
|
||||
]
|
||||
)
|
||||
assert result == "auto"
|
||||
|
||||
|
||||
async def test_get_translations_still_has_title_without_translations_files(
|
||||
hass: HomeAssistant, mock_config_flows
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user