Give firmware from before the languages Home Assistant's English words, not the screens' new language

The app fetched Home Assistant's state words in the screens' language only, so a screen still on 0.2.75 showed
"Speelt" among its own English words (and would get letters it can't draw). It now keeps them per language, the
screens' and English, and picks the set of the screen it writes for. Found in the second test pass on the boards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-19 20:02:17 +02:00
parent f7c452e7a7
commit 2b9ba7ec2b
2 changed files with 45 additions and 10 deletions
+29 -10
View File
@@ -144,9 +144,10 @@ class HomeAssistant:
# action (app 0.2.67); `get_services` has carried no translated names since 2025.10. Per language (app 0.2.90): the
# screens' one first, an editor in another language asks for its own (service_names_in).
self.service_names, self.service_names_by_language = {}, {}
# Home Assistant's words for states (frontend/get_translations `entity_component` and `entity`), in the screens'
# language (app 0.2.90), which a tile shows where it would show the raw state (app 0.2.67).
self.state_words = {}
# Home Assistant's words for states (frontend/get_translations `entity_component` and `entity`), which a tile shows
# where it would show the raw state (app 0.2.67): per language, the screens' and English (app 0.2.90), so a screen
# whose firmware predates the languages keeps English words (state_words).
self.words_by_language = {}
self.esphome_services = False
self._platforms_source, self._platforms = None, {}
# History a detail card asks for when it opens (firmware 0.2.51+): {inbox, entity, hours}, for Manager.card_history_loop.
@@ -230,6 +231,20 @@ class HomeAssistant:
return ' after %d s, close code %s%s' % (time.monotonic() - connected, ws.close_code if ws is not None else None,
f', {type(reason).__name__}' if reason else '')
@property
def state_words(self):
"""Home Assistant's words for states in the language of the screen being written for (i18n.SCREEN), else in the
screens' language."""
context = i18n.SCREEN.get()
language = context.get('language') if context else None
words = self.words_by_language
return words.get(language) or words.get(self.language_of()) or words.get('*') or {}
@state_words.setter
def state_words(self, words):
"""One set of words for every language (tests, a Home Assistant that answers in one language only)."""
self.words_by_language = {'*': dict(words or {})}
async def registries(self):
self.registry, self.devices, self.areas = await asyncio.gather(
self.request('config/entity_registry/list'), self.request('config/device_registry/list'), self.request('config/area_registry/list'))
@@ -248,13 +263,17 @@ class HomeAssistant:
if isinstance((names or {}).get('resources'), dict):
self.service_names = names['resources']
self.service_names_by_language = {language: names['resources']}
words = {}
for category in ('entity_component', 'entity'):
found = await self.request('frontend/get_translations', language=language, category=category)
if isinstance((found or {}).get('resources'), dict):
words.update(found['resources'])
if words:
self.state_words = words
by_language = {}
for code in dict.fromkeys((language, 'en')):
words = {}
for category in ('entity_component', 'entity'):
found = await self.request('frontend/get_translations', language=code, category=category)
if isinstance((found or {}).get('resources'), dict):
words.update(found['resources'])
if words:
by_language[code] = words
if by_language:
self.words_by_language = by_language
icons = {}
for category in ('entity_component', 'entity'):
found = await self.request('frontend/get_icons', category=category)
+16
View File
@@ -240,6 +240,22 @@ class ClockPinTests(unittest.IsolatedAsyncioTestCase):
chosen.region.change({'clock': 'auto'})
self.assertFalse(chosen.region.pinned, 'the owner chose a clock')
async def test_home_assistants_words_follow_the_screen(self):
"""Old firmware keeps English words from Home Assistant too, a new one its language's (found in the second test pass:
a playing media player said "Speelt" among English words on firmware 0.2.75)."""
from server import HomeAssistant
ha = HomeAssistant(None, 'http://127.0.0.1:1/api', 'token')
ha.language_of = lambda: 'nl'
ha.words_by_language = {'nl': {'state': 'Speelt'}, 'en': {'state': 'Playing'}}
self.assertEqual(ha.state_words, {'state': 'Speelt'})
for context, word in ((i18n.LEGACY, 'Playing'), ({'language': 'nl', 'legacy': False}, 'Speelt'),
({'language': 'de', 'legacy': False}, 'Speelt')):
token = i18n.SCREEN.set(context)
try:
self.assertEqual(ha.state_words, {'state': word})
finally:
i18n.SCREEN.reset(token)
async def test_language_and_region_over_http(self):
from server import create_app
from aiohttp.test_utils import TestClient, TestServer