d76461d774
The two board profiles (5,000 lines each, 87 % the same text) become packages/core.yaml and one file per board under packages/boards/: the hardware, a table of 84 sizes and font sizes, 18 hooks with the lines of C++ only that board adds inside a shared lambda, and the board's own sections. packages/cyd.yaml and packages/guition.yaml keep their names and places (a screen installed from ESP Screens builds as before); home-like-2432s028.yaml and guition-4848s040.yaml become the same entries for a checkout. The firmware stays 0.2.70: the C++ ESPHome generates for each board, through the checkout profile and through the GitHub package route, was compared line by line with the old profiles' (docs/TEST_RESULTS_0284.md). tools/check_packages.py replaces tools/generate_packages.py; tools/profiles.py gives the tests and tools the three files as one; docs/PROFILES.md says how the files fit together and how a board is added. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
98 lines
5.6 KiB
Python
98 lines
5.6 KiB
Python
"""The packages a screen builds from: one shared core plus a board file, runtime tiles only (app 0.2.84+)."""
|
|
import importlib.util
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / 'tools'))
|
|
import profiles # noqa: E402
|
|
|
|
BOARDS = {'cyd': 'home-like-2432s028.yaml', 'guition': 'guition-4848s040.yaml'}
|
|
# The old manual profile bound tiles to fixed entities in the YAML. ESP Screens sends the tiles now, so
|
|
# none of this may come back: the fixed subscriptions, its tap handlers and scripts, its own vacuum card.
|
|
GONE = ['ha_state_tile1', 'tile1_brightness', 'tile6_climate_humidity', 'tile6_cover_state', 'smartdisplay_action',
|
|
'publish_action', 'do_tile_action', 'open_vacuum_overlay', 'vacuum_action', 'vacuum_fan_speed', 'vacuum_overlay',
|
|
'vacspd_quiet', 'vacuum_actions', 'DYNAMIC_TILES', 'TILE_COUNT', 'DIRECT_ACTIONS', 'TILE1_ENTITY',
|
|
'light_controls::subscribe']
|
|
|
|
|
|
def load_checker():
|
|
spec = importlib.util.spec_from_file_location('check_packages', ROOT / 'tools/check_packages.py')
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
class PackageTests(unittest.TestCase):
|
|
def test_the_packages_fit_together(self):
|
|
result = subprocess.run([sys.executable, str(ROOT / 'tools/check_packages.py')], capture_output=True, text=True)
|
|
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
|
|
|
def test_every_entry_is_the_shared_core_plus_its_board(self):
|
|
checker = load_checker()
|
|
for board, profile in BOARDS.items():
|
|
for name in (profile, f'packages/{board}.yaml'):
|
|
self.assertEqual(sorted(checker.included(ROOT / name)), sorted(['packages/core.yaml', str(profiles.BOARDS[board].relative_to(ROOT))]), name)
|
|
|
|
def test_the_published_entry_takes_everything_from_github(self):
|
|
for board in BOARDS:
|
|
package = (ROOT / 'packages' / f'{board}.yaml').read_text()
|
|
self.assertNotIn('!secret', package)
|
|
self.assertNotIn('type: local', package)
|
|
self.assertIn('url: https://github.com/MaxGramser/homeassistant_espscreen.git', package)
|
|
self.assertIn('FONT_DIR: "https://raw.githubusercontent.com/MaxGramser/homeassistant_espscreen/main/fonts"', package)
|
|
# The fonts of the shared core come from that place; a checkout takes them from fonts/.
|
|
self.assertIn('file: "${FONT_DIR}/Roboto-500.ttf"', profiles.CORE.read_text())
|
|
self.assertIn('FONT_DIR: "fonts"', (ROOT / BOARDS[board]).read_text())
|
|
|
|
def test_no_secret_or_local_path_under_packages(self):
|
|
for path in sorted((ROOT / 'packages').rglob('*.yaml')):
|
|
text = '\n'.join(line for line in path.read_text().split('\n') if not line.lstrip().startswith('#'))
|
|
self.assertNotIn('!secret', text, path.name)
|
|
self.assertNotIn('type: local', text, path.name)
|
|
for section in ('sensor', 'binary_sensor', 'text_sensor'):
|
|
block = re.search(r'^' + section + r':\n.*?(?=^[a-zA-Z_]+:|\Z)', text, re.M | re.S)
|
|
self.assertFalse(block and re.search(r'^ - platform: homeassistant$', block[0], re.M), f'{path.name}: a {section} subscribes to Home Assistant')
|
|
|
|
def test_the_manual_profile_is_gone_from_profiles_and_packages(self):
|
|
for board, name in BOARDS.items():
|
|
for entry in (name, f'packages/{board}.yaml'):
|
|
text = '\n'.join(line for line in profiles.text(entry).split('\n') if not line.lstrip().startswith('#'))
|
|
for key in GONE:
|
|
self.assertNotIn(key, text, f'{entry} still carries {key}')
|
|
|
|
def test_runtime_tiles_keep_what_they_bind_and_open(self):
|
|
for board in BOARDS:
|
|
package = profiles.text(f'packages/{board}.yaml')
|
|
for key in ['runtime_tiles::bind(9, id(tile10)', 'runtime_tiles::enabled = true;', 'id: open_value_overlay',
|
|
'id: ui_refresh', 'runtime_tiles::render(id(lbl_room));', 'id: climate_detail_overlay',
|
|
'id: color_detail_overlay']:
|
|
self.assertIn(key, package, board)
|
|
# The CYD keeps its resistive calibration on the screen itself; the Guition's GT911 needs none.
|
|
self.assertIn('screen_calibration::setup(', profiles.resolved('packages/cyd.yaml'))
|
|
self.assertNotIn('screen_calibration::setup(', profiles.resolved('packages/guition.yaml'))
|
|
|
|
def test_the_core_carries_no_board_number_and_every_board_the_same_names(self):
|
|
core = profiles.CORE.read_text()
|
|
for needle in ('esp32:', 'platform: xpt2046', 'platform: gt911', 'platform: st7701s', 'platform: mipi_spi', 'psram:',
|
|
'set_raw_correction', 'camera_image', 'GPIO'):
|
|
self.assertNotIn(needle, core, f'{needle} is a board\'s, not the core\'s')
|
|
names = {board: set(profiles.substitutions_of(path)) for board, path in profiles.BOARDS.items()}
|
|
shared = set.intersection(*names.values())
|
|
self.assertGreater(len(shared), 100)
|
|
for board, defined in names.items():
|
|
own = defined - shared
|
|
self.assertTrue(all(re.match(r'(TOUCH_AFFINE_|TOUCH_CAL_|EDGE_SWIPE_|ALERT_\w*IMAGE|CAMERA_)', n) for n in own), f'{board}: {sorted(own)}')
|
|
|
|
def test_the_checker_refuses_a_fixed_home_assistant_subscription(self):
|
|
source = (ROOT / 'tools/check_packages.py').read_text()
|
|
self.assertIn("re.search(r'^ - platform: homeassistant$', match[0], re.M)", source)
|
|
self.assertIn("for section in ('sensor', 'binary_sensor', 'text_sensor'):", source)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|