2327d31261
"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>
85 lines
4.8 KiB
Python
85 lines
4.8 KiB
Python
import json
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
import yaml
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'screen_manager/app'))
|
|
from firmware import Firmware
|
|
|
|
class WifiReuse(unittest.TestCase):
|
|
def test_existing_wifi_reused_without_browser_values(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
f = Firmware(tmp, tmp)
|
|
path = Path(tmp) / 'secrets.yaml'
|
|
original = '# keep comments\nwifi_ssid: example-private-network\nwifi_password: example-private-password\nother_secret: leave-me\n'
|
|
path.write_text(original)
|
|
self.assertEqual(f.wifi_status()['state'], 'ready')
|
|
self.assertNotIn('example-private', json.dumps(f.status()))
|
|
f.create({'board': 'guition', 'name': 'second-screen', 'friendly_name': 'Second'})
|
|
self.assertEqual(path.read_text(), original)
|
|
profile = f.profile('second-screen.yaml').read_text()
|
|
self.assertIn('ssid: !secret wifi_ssid', profile)
|
|
self.assertIn('password: !secret wifi_password', profile)
|
|
self.assertNotIn('example-private', profile)
|
|
|
|
def test_missing_keys_are_added_without_touching_other_secrets(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
f = Firmware(tmp, tmp)
|
|
path = Path(tmp) / 'secrets.yaml'
|
|
original = '# keep comments\nother: keep\nwifi_ssid: ""\n'
|
|
path.write_text(original)
|
|
self.assertEqual(f.wifi_status(), {'state': 'missing', 'missing': ['wifi_password', 'wifi_ssid']})
|
|
# Without values nothing is written: no profile, secrets untouched.
|
|
with self.assertRaises(ValueError):
|
|
f.create({'board': 'cyd', 'name': 'screen', 'friendly_name': 'Screen'})
|
|
self.assertEqual(path.read_text(), original)
|
|
self.assertFalse((Path(tmp) / 'screen.yaml').exists())
|
|
f.create({'board': 'cyd', 'name': 'screen', 'friendly_name': 'Screen',
|
|
'wifi_ssid': 'home: net', 'wifi_password': 'p\\ss "word"'})
|
|
text = path.read_text()
|
|
self.assertTrue(text.startswith('# keep comments\nother: keep\n'))
|
|
self.assertEqual(text.count('wifi_ssid'), 1)
|
|
self.assertEqual(yaml.safe_load(text), {'other': 'keep', 'wifi_ssid': 'home: net', 'wifi_password': 'p\\ss "word"'})
|
|
self.assertEqual(f.wifi_status()['state'], 'ready')
|
|
self.assertTrue((Path(tmp) / 'screen.yaml').exists())
|
|
|
|
def test_invalid_secrets_are_never_rewritten(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
f = Firmware(tmp, tmp)
|
|
path = Path(tmp) / 'secrets.yaml'
|
|
path.write_text('wifi_ssid: [invalid')
|
|
with self.assertRaises(ValueError):
|
|
f.create({'board': 'cyd', 'name': 'screen', 'friendly_name': 'Screen',
|
|
'wifi_ssid': 'net', 'wifi_password': 'pw'})
|
|
self.assertEqual(path.read_text(), 'wifi_ssid: [invalid')
|
|
self.assertFalse((Path(tmp) / 'screen.yaml').exists())
|
|
|
|
def test_install_checks_port_and_busy_slot_before_writing(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
f = Firmware(tmp, tmp)
|
|
data = {'board': 'cyd', 'name': 'screen', 'friendly_name': 'Screen', 'wifi_ssid': 'net', 'wifi_password': 'pw'}
|
|
with self.assertRaises(ValueError):
|
|
f.install({**data, 'target': '/dev/ttyUSB0'})
|
|
self.assertFalse((Path(tmp) / 'screen.yaml').exists())
|
|
self.assertFalse((Path(tmp) / 'secrets.yaml').exists())
|
|
result = f.install(data)
|
|
self.assertEqual(result['file'], 'screen.yaml')
|
|
self.assertEqual(len(result['api_key']), 44)
|
|
self.assertNotIn('job', result)
|
|
self.assertIn(result['api_key'], f.profile('screen.yaml').read_text())
|
|
meta = f.profile_names()['screen.yaml']
|
|
self.assertEqual((meta['screen'], meta['api_key']), (True, result['api_key']))
|
|
(Path(tmp) / 'other.yaml').write_text('esphome:\n name: other\napi:\n encryption:\n key: !secret k\n')
|
|
self.assertEqual((f.profile_names()['other.yaml']['screen'], f.profile_names()['other.yaml']['api_key']), (False, None))
|
|
|
|
def test_first_install_and_invalid_file(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
f = Firmware(tmp, tmp)
|
|
self.assertEqual(f.wifi_status()['state'], 'new')
|
|
f.create({'board': 'cyd', 'name': 'screen', 'friendly_name': 'Screen',
|
|
'wifi_ssid': 'network', 'wifi_password': ''})
|
|
self.assertEqual(f.wifi_status()['state'], 'ready')
|
|
(Path(tmp) / 'secrets.yaml').write_text('wifi_ssid: [invalid')
|
|
self.assertEqual(f.wifi_status(), {'state': 'invalid'})
|