Files
homeassistant_espscreen/tools/generate_packages.py
T
Max Gramser 3e959b3df9 Put ESP Screens in English and keep layouts when entity IDs change (0.2.40)
Everything a user sees or reads is now English: both boards' labels,
status replies and logs, the ESP Screens editor and Alerts cheatsheet,
and the README and guides (docs/TILES.md, CALIBRATING.md, ACCEPTANCE.md
and TROUBLESHOOTING.md replace the Dutch file names). Labels follow
Home Assistant's own English names (Heat/Cool modes, Heating/Cooling
actions, Away, Returning to dock, weather conditions), dates use English
abbreviations and the top bar writes numbers the English way. Docs use
HA 2026.9's menu names (Settings -> Apps, App store, Settings -> Tools).
Unused legacy files (home-like.yaml, buttons.yaml, a.out) are removed.

Firmware 0.2.34 names its entities in English. Home Assistant registers
a renamed ESPHome entity under a new entity ID, so the inbox
text.<screen>_tegelinstellingen becomes text.<screen>_tile_settings and
the layout stored under the old ID would be lost. The manager now
recognises the screen by its device, during a run or after a restart,
and moves its layout and update history along; the Update button and
the nightly round follow the screen through the rename. Screens that
still run 0.2.33 or older keep working: both languages are matched.

Main is the only release branch from now on.

Tested on a Home Assistant Yellow (HA 2026.9.1) with both bench boards:
see docs/TEST_RESULTS_0240.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 02:08:43 +02:00

69 lines
3.6 KiB
Python

"""Generate self-contained remote packages from the maintained board profiles."""
import argparse
from pathlib import Path
import re
ROOT = Path(__file__).resolve().parents[1]
REPO = 'https://github.com/MaxGramser/homeassistant_espscreen'
REFS = {'cyd': 'main', 'guition': 'main'}
def generate(board):
source = ROOT / ('home-like-2432s028.yaml' if board == 'cyd' else 'guition-4848s040.yaml')
s = source.read_text()
defaults = (ROOT / ('device.example.yaml' if board == 'cyd' else 'guition-device.example.yaml')).read_text()
defaults += '\n' + (ROOT / 'calibration.example.yaml').read_text()
replacements = dict(re.findall(r'^ (\w+): ("[^"\n]*")', defaults, re.M))
replacements.update(DYNAMIC_TILES='"true"', DASHBOARD_INBOX_INTERNAL='"false"', DIRECT_ACTIONS='"true"',
ROOM_NAME='"Choose tiles in HA"', TILE_COUNT='"1"')
s = re.sub(r'^ (\w+): ("[^"\n]*")', lambda m: f' {m[1]}: {replacements.get(m[1],m[2])}', s, flags=re.M)
# The owner's wizard YAML supplies wifi/API/OTA credentials. No shared keys.
s = re.sub(r'^ encryption:\n key: !secret api_encryption_key\n', '', s, flags=re.M)
s = re.sub(r'^ota:\n.*?(?=^\w)', '', s, flags=re.M | re.S)
s = re.sub(r'^wifi:\n.*?(?=^\w)', '', s, flags=re.M | re.S)
# Referenced files are fetched as an external component or over HTTPS.
s = re.sub(r'^ - (cyd_ui.h|light_controls.h|guition_diagnostics.h|backlight_fade.h|alert_overlay.h)$', r' - <esphome/components/smart_display/\1>', s, flags=re.M)
components = '[xpt2046, smart_display]' if board == 'cyd' else '[smart_display]'
external = f'''external_components:
- source:
type: git
url: {REPO}.git
ref: {REFS[board]}
path: components
refresh: 0s
components: {components}
'''
s = re.sub(r'^external_components:\n.*?(?=^\w)', lambda m: external, s, flags=re.M | re.S)
s = re.sub(r'file: ["\']fonts/([^"\']+)["\']', lambda m: 'file: "https://raw.githubusercontent.com/MaxGramser/homeassistant_espscreen/main/fonts/' + m[1] + '"', s)
# Keep the established card bindings, but remove all fixed HA subscriptions.
# Runtime values arrive through ESP Screen Manager, including after changing entities.
for section in ['sensor', 'text_sensor']:
match = re.search(r'^' + section + r':\n.*?(?=^[a-zA-Z_]+:|\Z)', s, re.M | re.S)
if not match: continue
block = match[0]
def sensor(m):
body = re.sub(r'^ (entity_id|attribute):.*\n', '', m[1], flags=re.M)
return ' - platform: template\n update_interval: never\n' + body
block = re.sub(r'^ - platform: homeassistant\n(.*?)(?=^ - platform:|\Z)', sensor, block, flags=re.M | re.S)
s = s[:match.start()] + block + s[match.end():]
if '!secret' in s: raise ValueError('Remote package still depends on local secrets')
return '# GENERATED by tools/generate_packages.py; edit the source board profile.\n' + s
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--check', action='store_true')
args = parser.parse_args()
for board in REFS:
source = ROOT / ('home-like-2432s028.yaml' if board == 'cyd' else 'guition-4848s040.yaml')
if not source.exists(): continue
path = ROOT / 'packages' / f'{board}.yaml'
expected = generate(board)
if args.check:
if not path.exists() or path.read_text() != expected: raise SystemExit(f'Outdated: {path}')
else:
path.parent.mkdir(exist_ok=True)
path.write_text(expected)
print(f'{board}: package ' + ('verified' if args.check else 'generated'))
if __name__ == '__main__': main()