#!/usr/bin/env python3 import json import tempfile import threading import urllib.error import urllib.request from pathlib import Path from led_panel import Handler, ThreadingHTTPServer import led_panel def req(port, path, body=None): data = None headers = {} if body is not None: data = json.dumps(body).encode() headers['Content-Type'] = 'application/json' r = urllib.request.Request(f'http://127.0.0.1:{port}{path}', data=data, headers=headers) with urllib.request.urlopen(r, timeout=5) as resp: return json.loads(resp.read().decode()) def main(): with tempfile.TemporaryDirectory() as td: root = Path(td) d = root / 'blue:wan' d.mkdir() (d / 'brightness').write_text('0') (d / 'max_brightness').write_text('1') (d / 'trigger').write_text('[none] timer') (d / 'delay_on').write_text('500') (d / 'delay_off').write_text('500') d2 = root / 'red:power' d2.mkdir() (d2 / 'brightness').write_text('1') (d2 / 'max_brightness').write_text('1') (d2 / 'trigger').write_text('[none] timer') d3 = root / 'green:wlan' d3.mkdir() (d3 / 'brightness').write_text('1') (d3 / 'max_brightness').write_text('1') (d3 / 'trigger').write_text('[none] timer') hidden = root / 'sim:en' hidden.mkdir() (hidden / 'brightness').write_text('1') (hidden / 'max_brightness').write_text('1') (hidden / 'trigger').write_text('[none] timer') config_path = root / 'config.json' led_panel.LED_ROOT = root led_panel.CONFIG_PATH = config_path httpd = ThreadingHTTPServer(('127.0.0.1', 0), Handler) port = httpd.server_address[1] t = threading.Thread(target=httpd.serve_forever, daemon=True) t.start() try: import urllib.request as _urlreq html = _urlreq.urlopen(f'http://127.0.0.1:{port}/', timeout=5).read().decode() assert 'RGB LED 岛屿控制台' in html and 'Animal-Island 风格面板' in html and '关闭全部' in html and 'oncount' in html leds = req(port, '/api/leds') names = [led['name'] for led in leds['leds']] assert leds['ok'] and names == ['blue:wan', 'green:wlan', 'red:power'] try: req(port, '/api/brightness', {'name':'sim:en','value':0}) raise AssertionError('hidden LED unexpectedly writable') except urllib.error.HTTPError as e: assert e.code == 400 b = req(port, '/api/brightness', {'name':'blue:wan','value':1}) assert b['ok'] and (d / 'brightness').read_text() == '1' tr = req(port, '/api/trigger', {'name':'blue:wan','trigger':'timer'}) assert tr['ok'] assert (d / 'trigger').read_text() == 'timer' delay = req(port, '/api/delay', {'name':'blue:wan','field':'delay_on','value':123}) assert delay['ok'] and (d / 'delay_on').read_text() == '123' saved = req(port, '/api/save', {'name':'blue:wan'}) assert saved['ok'] and config_path.exists() cfg = json.loads(config_path.read_text()) assert cfg['leds']['blue:wan']['delay_on'] == 123 # Simulate reboot/default reset, then apply persisted config. (d / 'brightness').write_text('0') (d / 'trigger').write_text('[none] timer') (d / 'delay_on').write_text('500') applied = req(port, '/api/apply-saved', {}) assert applied['ok'] and applied['applied'] == 1 assert (d / 'brightness').read_text() == '1' assert (d / 'delay_on').read_text() == '123' all_off = req(port, '/api/all-off', {}) assert all_off['ok'] and all_off['changed'] == 3 assert (d / 'brightness').read_text() == '0' assert (d2 / 'brightness').read_text() == '0' assert (d3 / 'brightness').read_text() == '0' assert (hidden / 'brightness').read_text() == '1' assert '[none]' in (d / 'trigger').read_text() or (d / 'trigger').read_text() == 'none' assert '[none]' in (d2 / 'trigger').read_text() or (d2 / 'trigger').read_text() == 'none' assert '[none]' in (d3 / 'trigger').read_text() or (d3 / 'trigger').read_text() == 'none' print('OK: RGB filter + API write + persistence + all-off regression passed') finally: httpd.shutdown() if __name__ == '__main__': main()