Support twenty runtime tiles and opt-in native LVGL page swipes
This commit is contained in:
@@ -23,6 +23,7 @@ SETTING_RULES = {
|
||||
'show_clock': (True, None, None),
|
||||
'clock_24h': (True, None, None),
|
||||
'home_on_standby': (False, None, None),
|
||||
'swipe_pages': (False, None, None),
|
||||
}
|
||||
|
||||
def validate_settings(data):
|
||||
@@ -54,8 +55,8 @@ def validate_layout(data):
|
||||
title, tiles = data.get('title'), data.get('tiles')
|
||||
if not isinstance(title, str) or not title.strip() or len(title.encode()) > 96:
|
||||
raise ValueError('Geef het scherm een titel van maximaal 96 bytes.')
|
||||
if not isinstance(tiles, list) or len(tiles) > 10:
|
||||
raise ValueError('Kies maximaal 10 tegels.')
|
||||
if not isinstance(tiles, list) or len(tiles) > 20:
|
||||
raise ValueError('Kies maximaal 20 tegels.')
|
||||
clean, seen = [], set()
|
||||
for tile in tiles:
|
||||
if not isinstance(tile, dict) or not entity_id(tile.get('entity')):
|
||||
|
||||
@@ -165,13 +165,25 @@ class Manager:
|
||||
def inventory(self):
|
||||
return discover(self.ha.registry, self.ha.states, self.ha.devices, self.ha.areas)
|
||||
|
||||
def supports_twenty(self, inbox):
|
||||
screen=next((s for s in self.inventory()[0] if s['id']==inbox), {})
|
||||
try:
|
||||
version=tuple(int(part) for part in screen.get('firmware','').split('.'))
|
||||
return len(version)==3 and version >= (0,2,7)
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
|
||||
def save(self, inbox, data):
|
||||
if inbox not in {s['id'] for s in self.inventory()[0]}:
|
||||
raise ValueError('Dit is geen gekoppeld ESP-scherm. Vernieuw het overzicht.')
|
||||
layout = validate_layout(data)
|
||||
if len(layout["tiles"])>10 and not self.supports_twenty(inbox):
|
||||
raise ValueError("Installeer eerst schermfirmware 0.2.7 of nieuwer voor meer dan tien tegels.")
|
||||
# A still-open older UI may save tiles without the new optional settings.
|
||||
if 'settings' not in layout and 'settings' in self.layouts.get(inbox, {}):
|
||||
layout['settings'] = self.layouts[inbox]['settings'].copy()
|
||||
if 'settings' in layout and 'swipe_pages' not in data.get('settings',{}):
|
||||
layout['settings']['swipe_pages']=self.layouts.get(inbox,{}).get('settings',{}).get('swipe_pages',False)
|
||||
old_tiles = {t['entity']:t for t in self.layouts.get(inbox,{}).get('tiles',[])}
|
||||
for tile in layout['tiles']:
|
||||
if 'options' not in tile and 'options' in old_tiles.get(tile['entity'],{}):
|
||||
@@ -194,9 +206,13 @@ class Manager:
|
||||
self.ha.changed.set()
|
||||
|
||||
async def sync_one(self, inbox, layout, force=False):
|
||||
if len(layout["tiles"])>10 and not self.supports_twenty(inbox):
|
||||
self.status[inbox]="Indeling bewaard; firmware 0.2.7+ nodig voor meer dan tien tegels"
|
||||
return
|
||||
messages = [{'v': 1, 'op': 'layout', 'inbox': inbox, 'title': layout['title'], 'entities': [t['entity'] for t in layout['tiles']]}]
|
||||
if 'settings' in layout:
|
||||
messages[0]['settings'] = layout['settings']
|
||||
messages[0]['settings'] = {k:v for k,v in layout['settings'].items() if k!='swipe_pages'}
|
||||
messages[0]['swipe_pages'] = layout['settings'].get('swipe_pages',False)
|
||||
for i,tile in enumerate(layout['tiles']):
|
||||
message=state_message(i,tile,self.ha.states)
|
||||
if tile['entity'].startswith('sensor.') and hasattr(self.ha,'history'):
|
||||
|
||||
@@ -81,6 +81,7 @@ const settingDefinitions = [
|
||||
["show_clock", "Klok tonen", "check", true],
|
||||
["clock_24h", "24-uursklok (uit = 12 uur)", "check", true],
|
||||
["home_on_standby", "Na standby terug naar pagina 1", "check", false],
|
||||
["swipe_pages", "Vegen tussen pagina’s (firmware 0.2.7+)", "check", false],
|
||||
];
|
||||
function renderSettings() {
|
||||
const values = {
|
||||
@@ -223,6 +224,12 @@ function domainBadge(id) {
|
||||
badge.style.background = background;
|
||||
return badge;
|
||||
}
|
||||
function tileLimit() {
|
||||
const version=inventory.screens.find(s=>s.id===selected)?.firmware || "";
|
||||
const match=/^(\d+)\.(\d+)\.(\d+)$/.exec(version);
|
||||
if(!match)return 10;
|
||||
return Number(match[1])>0 || Number(match[2])>2 || Number(match[2])===2 && Number(match[3])>=7 ? 20 : 10;
|
||||
}
|
||||
function renderPreview() {
|
||||
const root = $("#layout-preview");
|
||||
root.replaceChildren();
|
||||
@@ -231,7 +238,7 @@ function renderPreview() {
|
||||
const frame = node("section", undefined, "screen-preview");
|
||||
frame.append(node("small", `Pagina ${page + 1} · ${$("#title").value || "Thuis"}`, "preview-heading"));
|
||||
const grid = node("div", undefined, "preview-grid");
|
||||
for (let slot = page * 6; slot < Math.min(page * 6 + 6, 10); slot++) {
|
||||
for (let slot = page * 6; slot < Math.min(page * 6 + 6, tileLimit()); slot++) {
|
||||
const tile = layout.tiles[slot];
|
||||
const card = node("button", undefined, "preview-tile");
|
||||
if (tile) {
|
||||
@@ -265,7 +272,7 @@ function renderPreview() {
|
||||
function renderTiles() {
|
||||
renderPreview();
|
||||
$("#tiles").replaceChildren();
|
||||
$("#count").textContent = `${layout.tiles.length} / 10`;
|
||||
$("#count").textContent = `${layout.tiles.length} / ${tileLimit()}${tileLimit()===10?" · update firmware voor 20":""}`;
|
||||
$("#no-tiles").hidden = layout.tiles.length > 0;
|
||||
layout.tiles.forEach((tile, i) => {
|
||||
if (i === 6) $("#tiles").append(node("li", "Pagina 2", "page-break"));
|
||||
@@ -472,7 +479,7 @@ function renderResults() {
|
||||
description,
|
||||
node("span", chosen.has(entity.id) ? "✓" : "+", "plus"),
|
||||
);
|
||||
b.disabled = chosen.has(entity.id) || layout.tiles.length >= 10;
|
||||
b.disabled = chosen.has(entity.id) || layout.tiles.length >= tileLimit();
|
||||
b.onclick = () => {
|
||||
layout.tiles.push({ entity: entity.id, name: "" });
|
||||
selectedTile = entity.id;
|
||||
|
||||
Reference in New Issue
Block a user