One-window install: profile, wifi, USB flash and pairing hints (0.2.27)

"Nieuw scherm" used to span two dialogs plus a copy/download of the
installation YAML. It is now one dialog: board, name (node name derived,
editable), wifi only when secrets.yaml lacks it, the detected USB port,
and Installeren. The add-on writes the profile with unique API/OTA keys,
merges just the missing wifi lines into secrets.yaml (comments and other
secrets untouched, result re-parsed before writing), then builds and
flashes with the live ESPHome log and stage in the same dialog; a failed
build offers a retry without a second profile. The YAML download
endpoint is gone: the profile already lives in the ESPHome folder.

Pairing happens in Home Assistant itself, so the done view and a sidebar
card per not-yet-paired profile say so, with a button to Apparaten &
diensten, the API key to copy and the reminder to allow HA actions.
One profile per screen; the dialog says that too.

Tests: wifi merge cases, install pre-checks, pending list, profile
metadata; the date-anchored hourly forecast test now follows the clock.
Firmware 0.2.22 stays current.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-14 11:11:23 +02:00
parent 5d6651d68b
commit 2327d31261
16 changed files with 848 additions and 287 deletions
+5 -3
View File
@@ -61,8 +61,10 @@ class ProfileNameTests(unittest.TestCase):
'packages:\n a: !include other.yaml\napi:\n encryption:\n key: !secret api\n')
(Path(tmp) / 'broken.yaml').write_text('esphome: [\n')
names = f.profile_names()
self.assertEqual(names['woonkamer.yaml'], {'node': 'woonkamer', 'friendly': 'Woonkamer'})
self.assertEqual(names['manual.yaml'], {'node': 'keuken', 'friendly': 'Keuken'})
self.assertEqual(names['woonkamer.yaml'], {'node': 'woonkamer', 'friendly': 'Woonkamer', 'screen': True, 'api_key': names['woonkamer.yaml']['api_key']})
self.assertEqual(len(names['woonkamer.yaml']['api_key']), 44)
# A manual profile (no board package from this repo, key behind !secret) is not an ESP Screens profile.
self.assertEqual(names['manual.yaml'], {'node': 'keuken', 'friendly': 'Keuken', 'screen': False, 'api_key': None})
self.assertNotIn('broken.yaml', names)
self.assertNotIn('secrets.yaml', names)
@@ -84,7 +86,7 @@ class ProfileNameTests(unittest.TestCase):
os.utime(b, ns=(b.stat().st_atime_ns, b.stat().st_mtime_ns + 1_000_000))
names = f.profile_names()
self.assertEqual(len(parsed), 3, 'only the changed file is parsed')
self.assertEqual(names['b.yaml'], {'node': 'b2', 'friendly': 'B2'})
self.assertEqual(names['b.yaml'], {'node': 'b2', 'friendly': 'B2', 'screen': False, 'api_key': None})
a.unlink()
self.assertEqual(set(f.profile_names()), {'b.yaml'})
self.assertEqual(len(parsed), 3)