Rework API input CSS: segments, remove led_count, HAOS light, test preview

API Input CSS rework:
- Remove led_count field from ApiInputColorStripSource (always auto-sizes)
- Add segment-based payload: solid, per_pixel, gradient modes
- Segments applied in order (last wins on overlap), auto-grow buffer
- Backward compatible: legacy {"colors": [...]} still works
- Pydantic validation: mode-specific field requirements

Test preview:
- Enable test preview button on api_input cards
- Hide LED/FPS controls for api_input (sender controls those)
- Show input source selector for all CSS tests (preselected)
- FPS sparkline chart using shared createFpsSparkline (same as target cards)
- Server only sends frames when push_generation changes (no idle frames)

HAOS integration:
- New light.py: ApiInputLight entity per api_input source (RGB + brightness)
- turn_on pushes solid segment, turn_off pushes fallback color
- Register wled_screen_controller.set_leds service for arbitrary segments
- New services.yaml with field definitions
- Coordinator: push_colors() and push_segments() methods
- Platform.LIGHT added to platforms list

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-17 14:47:42 +03:00
parent 823cb90d2d
commit 8a6ffca446
25 changed files with 1085 additions and 326 deletions
@@ -243,6 +243,7 @@ export async function saveScenePreset() {
scenePresetModal.forceClose();
showToast(_editingId ? t('scenes.updated') : t('scenes.captured'), 'success');
scenePresetsCache.invalidate();
_reloadScenesTab();
} catch (error) {
if (error.isAuth) return;
@@ -348,6 +349,7 @@ export async function recaptureScenePreset(presetId) {
});
if (resp.ok) {
showToast(t('scenes.recaptured'), 'success');
scenePresetsCache.invalidate();
_reloadScenesTab();
} else {
showToast(t('scenes.error.recapture_failed'), 'error');
@@ -420,6 +422,7 @@ export async function deleteScenePreset(presetId) {
});
if (resp.ok) {
showToast(t('scenes.deleted'), 'success');
scenePresetsCache.invalidate();
_reloadScenesTab();
} else {
showToast(t('scenes.error.delete_failed'), 'error');