From 2b9ba7ec2b7c21678d87a64f7270a1f69e07fca5 Mon Sep 17 00:00:00 2001 From: Max Gramser Date: Sat, 19 Sep 2026 20:02:17 +0200 Subject: [PATCH] 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 --- screen_manager/app/server.py | 39 ++++++++++++++++++++++++++--------- tests/test_language_region.py | 16 ++++++++++++++ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/screen_manager/app/server.py b/screen_manager/app/server.py index 6617f67..71edf94 100644 --- a/screen_manager/app/server.py +++ b/screen_manager/app/server.py @@ -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) diff --git a/tests/test_language_region.py b/tests/test_language_region.py index b72849c..421f692 100644 --- a/tests/test_language_region.py +++ b/tests/test_language_region.py @@ -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