' +
- '
' + escHtml(temp) + '' + escHtml(PT.mideaCurrent || '') + '
' + outdoor + '
' +
- '
' +
- '
' + escHtml(PT.mideaTarget || '') + '
' +
- '
' + escHtml(tgtDisp) + '
' +
- '
' + escHtml(PT.mideaMode || '') + '
' + modeBtns + '
' + fan + extras +
+ '
' +
+ '
' + escHtml(ringBig) + '°
' + escHtml(PT.mideaTarget || '') + '
' +
+ '
' +
+ '
' + escHtml(PT.mideaCurrent || '') + '' + escHtml(temp) + '
' +
+ outdoor +
+ '
' + escHtml(PT.mideaTarget || '') + '' +
+ '
' +
+ '' + escHtml(tgtDisp) + '' +
+ '
' +
+ '
' + modeBtns + '
' +
'
' +
'
' +
+ '
' + fan + extras + '
' +
'' + login +
'
';
}
@@ -692,22 +697,55 @@
var msg = document.getElementById('smarthomeMsg');
if (msg) { msg.style.display = 'block'; msg.textContent = PT.smarthomeLoginToControl || 'Login required'; }
}
+ // Static, hard-coded SVG per device kind — no user data interpolated, safe as innerHTML.
+ var SH_ICONS = {
+ light: '
',
+ group: '
',
+ plug: '
',
+ switch: '
',
+ scene: '
'
+ };
+ function shStatusText(st, caps) {
+ var onLabel = st.on ? (PT.smarthomeOn || 'On') : (PT.smarthomeOff || 'Off');
+ if (st.on && caps.bri && st.bri != null) return onLabel + ' · ' + Math.round(Number(st.bri)) + ' %';
+ return onLabel;
+ }
function renderSmarthomeCard(d) {
- var el = document.createElement('div'); el.className = 'c-sh-card';
+ var el = document.createElement('div'); el.className = 'sh-tile';
var st = d.state || {}, caps = d.capabilities || {};
- var name = document.createElement('div'); name.className = 'c-sh-name'; name.textContent = d.name || ''; el.appendChild(name);
+ var ic = document.createElement('span'); ic.className = 'sh-ic'; ic.innerHTML = SH_ICONS[d.kind] || SH_ICONS.light; el.appendChild(ic);
+ var nm = document.createElement('div'); nm.className = 'nm';
+ var b = document.createElement('b'); b.textContent = d.name || ''; nm.appendChild(b);
+ var sub = document.createElement('span'); nm.appendChild(sub);
+ el.appendChild(nm);
if (d.kind === 'scene') {
- var b = document.createElement('button'); b.className = 'btn btn-sm'; b.textContent = PT.smarthomeActivate || 'Activate';
- b.addEventListener('click', function () { shControl(d.id, {}, el); });
- el.appendChild(b); return el;
+ var btn = document.createElement('button'); btn.className = 'btn btn-sm'; btn.textContent = PT.smarthomeActivate || 'Activate';
+ btn.addEventListener('click', function () { shControl(d.id, {}, el); });
+ el.appendChild(btn);
+ return el;
}
- var sw = document.createElement('button'); sw.className = 'c-sh-sw' + (st.on ? ' on' : '');
- sw.textContent = PT.smarthomePower || 'Power';
- sw.addEventListener('click', function () { var on = !sw.classList.contains('on'); sw.classList.toggle('on', on); shControl(d.id, { on: on }, el); });
+ sub.textContent = shStatusText(st, caps);
+ el.classList.toggle('on', !!st.on);
+ // a11y: toggle pill exposes role="switch" + aria-checked reflecting on/off state (WAI-ARIA switch pattern)
+ var sw = document.createElement('span');
+ sw.className = 'toggle';
+ sw.setAttribute('role', 'switch');
+ sw.setAttribute('tabindex', '0');
+ sw.setAttribute('aria-checked', st.on ? 'true' : 'false');
+ sw.setAttribute('aria-label', d.name || '');
el.appendChild(sw);
+ var flip = function () {
+ var on = !el.classList.contains('on');
+ el.classList.toggle('on', on);
+ sw.setAttribute('aria-checked', on ? 'true' : 'false');
+ sub.textContent = shStatusText({ on: on, bri: st.bri }, caps);
+ shControl(d.id, { on: on }, el);
+ };
+ sw.addEventListener('click', flip);
+ sw.addEventListener('keydown', function (ev) { if (ev.key === ' ' || ev.key === 'Enter') { ev.preventDefault(); flip(); } });
if (caps.bri) {
var range = document.createElement('input'); range.type = 'range'; range.min = 0; range.max = 100; range.value = (st.bri != null ? st.bri : 0); range.className = 'c-sh-bri';
- range.addEventListener('change', function () { shControl(d.id, { bri: Number(range.value) }, el); });
+ range.addEventListener('change', function () { st.bri = Number(range.value); sub.textContent = shStatusText({ on: el.classList.contains('on'), bri: st.bri }, caps); shControl(d.id, { bri: Number(range.value) }, el); });
el.appendChild(range);
}
return el;
@@ -727,10 +765,10 @@
}
}
function renderSensorCard(s) {
- var el = document.createElement('div'); el.className = 'c-sh-sensor-card';
+ var el = document.createElement('div'); el.className = 'sensor';
var st = s.state || {};
- var name = document.createElement('div'); name.className = 'c-sh-sensor-name'; name.textContent = s.name || ''; el.appendChild(name);
- var val = document.createElement('div'); val.className = 'c-sh-sensor-val'; val.textContent = formatSensor(st.type, st.value); el.appendChild(val);
+ var val = document.createElement('div'); val.className = 'v'; val.textContent = formatSensor(st.type, st.value); el.appendChild(val);
+ var lbl = document.createElement('div'); lbl.className = 'l'; lbl.textContent = s.name || ''; el.appendChild(lbl);
return el;
}
function hydrateSmarthome() {
@@ -746,9 +784,8 @@
if (sensorBox) {
sensorBox.innerHTML = '';
var sensors = (j && j.data && j.data.sensors) || [];
+ sensorBox.setAttribute('aria-label', PT.smarthomeSensors || 'Sensors');
if (sensors.length) {
- var head = document.createElement('div'); head.className = 'c-sh-sensor-head'; head.textContent = PT.smarthomeSensors || 'Sensors';
- sensorBox.appendChild(head);
sensors.forEach(function (s) { sensorBox.appendChild(renderSensorCard(s)); });
sensorBox.style.display = '';
} else {
diff --git a/src/i18n/de.json b/src/i18n/de.json
index 071c6928..b575d068 100644
--- a/src/i18n/de.json
+++ b/src/i18n/de.json
@@ -2195,6 +2195,8 @@
"portal.midea.turbo": "Turbo",
"portal.midea.eco": "Eco",
"portal.midea.outdoor": "Außen",
+ "portal.midea.warmer": "wärmer",
+ "portal.midea.cooler": "kälter",
"portal.smarthome.title": "Smart Home",
"portal.smarthome.power": "Ein/Aus",
"portal.smarthome.brightness": "Helligkeit",
@@ -2206,5 +2208,7 @@
"portal.smarthome.motion": "Bewegung",
"portal.smarthome.no_motion": "keine Bewegung",
"portal.smarthome.wet": "nass",
- "portal.smarthome.dry": "trocken"
+ "portal.smarthome.dry": "trocken",
+ "portal.smarthome.on": "An",
+ "portal.smarthome.off": "Aus"
}
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 2eb4ab12..f0f9b99a 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -2251,6 +2251,8 @@
"portal.midea.turbo": "Turbo",
"portal.midea.eco": "Eco",
"portal.midea.outdoor": "Outdoor",
+ "portal.midea.warmer": "warmer",
+ "portal.midea.cooler": "cooler",
"portal.smarthome.title": "Smart Home",
"portal.smarthome.power": "Power",
"portal.smarthome.brightness": "Brightness",
@@ -2262,5 +2264,7 @@
"portal.smarthome.motion": "Motion",
"portal.smarthome.no_motion": "No motion",
"portal.smarthome.wet": "Wet",
- "portal.smarthome.dry": "Dry"
+ "portal.smarthome.dry": "Dry",
+ "portal.smarthome.on": "On",
+ "portal.smarthome.off": "Off"
}
diff --git a/templates/portal/portal.njk b/templates/portal/portal.njk
index 8b554191..759c56fb 100644
--- a/templates/portal/portal.njk
+++ b/templates/portal/portal.njk
@@ -58,6 +58,8 @@
mideaTurbo: t('portal.midea.turbo'),
mideaEco: t('portal.midea.eco'),
mideaOutdoor: t('portal.midea.outdoor'),
+ mideaWarmer: t('portal.midea.warmer'),
+ mideaCooler: t('portal.midea.cooler'),
smarthomePower: t('portal.smarthome.power'),
smarthomeBrightness: t('portal.smarthome.brightness'),
smarthomeActivate: t('portal.smarthome.activate'),
@@ -68,7 +70,9 @@
smarthomeMotion: t('portal.smarthome.motion'),
smarthomeNoMotion: t('portal.smarthome.no_motion'),
smarthomeWet: t('portal.smarthome.wet'),
- smarthomeDry: t('portal.smarthome.dry')
+ smarthomeDry: t('portal.smarthome.dry'),
+ smarthomeOn: t('portal.smarthome.on'),
+ smarthomeOff: t('portal.smarthome.off')
} | dump | safe }}
@@ -113,51 +117,36 @@
{{ t('portal.greeting_sub') }}
+ {% if widgets.device %}
+
+
+
+ {{ t('portal.device.vpn_address') }}
+ {{ t('portal.device.dns') }}
+ {{ t('portal.device.last_handshake') }}
+
+
+ {% endif %}
+
- {% if widgets.device %}
-
-
- {{ t('portal.device.title') }}
-
-
{{ t('portal.device.status') }}
-
{{ t('portal.device.last_handshake') }}
-
{{ t('portal.device.vpn_address') }}
-
{{ t('portal.device.dns') }}
-
-
-
{{ t('portal.device.received') }}
-
{{ t('portal.device.sent') }}
-
-
- {% endif %}
-
- {% if widgets.traffic %}
-
-
- {{ t('portal.traffic.title') }}
- {{ t('portal.traffic.legend_rx') }}{{ t('portal.traffic.legend_tx') }}
-
-
-
-
-
-
-
-
+ {% if widgets.midea %}
+
+
{% endif %}
- {% if widgets.services %}
-
-
- {{ t('portal.services.title') }}{{ t('portal.services.sub') }}
-
+ {% if widgets.smarthome %}
+
+
+ {{ t('portal.smarthome.title') }}
+
+
+
{% endif %}
@@ -172,8 +161,10 @@
-
–%
-
+
–{{ t('portal.pihole.total') }}
–{{ t('portal.pihole.blocked') }}
@@ -187,22 +178,31 @@
{% endif %}
- {% if widgets.midea %}
-
-