@feezal/feezal-element 3.0.5 → 3.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/feezal-element.js +107 -0
- package/package.json +1 -1
package/feezal-element.js
CHANGED
|
@@ -106,6 +106,14 @@ export class FeezalElement extends LitElement {
|
|
|
106
106
|
connectedCallback() {
|
|
107
107
|
super.connectedCallback();
|
|
108
108
|
this.classList.add('feezal-element');
|
|
109
|
+
// N37: an element stamped into an ALREADY-PAUSED view must not
|
|
110
|
+
// subscribe — pause state is a precondition, not only an event. The
|
|
111
|
+
// visibility controller resumes it via a reconnect cycle later.
|
|
112
|
+
if (window.feezal?.visibility?.isPaused?.(this)) {
|
|
113
|
+
this.__n37Paused = true;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
this.__n37Paused = false;
|
|
109
117
|
if (this.visible || !this.dynamicSubscriptions) {
|
|
110
118
|
this._subscribe();
|
|
111
119
|
this._conditions.connect();
|
|
@@ -117,6 +125,37 @@ export class FeezalElement extends LitElement {
|
|
|
117
125
|
this._subscribeAvailability();
|
|
118
126
|
}
|
|
119
127
|
|
|
128
|
+
/**
|
|
129
|
+
* N37 — pause this element's MQTT traffic while its view is hidden.
|
|
130
|
+
* Teardown is uniform for EVERY element: all primary subscriptions live
|
|
131
|
+
* in `_subscriptions` (addSubscription), availability in the N31 arrays,
|
|
132
|
+
* conditions in the engine — regardless of how the element wired them.
|
|
133
|
+
*/
|
|
134
|
+
pauseSubscriptions() {
|
|
135
|
+
if (this.__n37Paused) return;
|
|
136
|
+
this.__n37Paused = true;
|
|
137
|
+
this._unsubscribe();
|
|
138
|
+
this._unsubscribeAvailability();
|
|
139
|
+
this._conditions.disconnect();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* N37 — resume by re-running the element's OWN wiring exactly as a fresh
|
|
144
|
+
* mount would: a detach/re-attach cycle runs disconnected/connectedCallback,
|
|
145
|
+
* the one path every element (including manual wirers like the device
|
|
146
|
+
* cards) implements correctly. Retained values repaint instantly from the
|
|
147
|
+
* B40 cache replay.
|
|
148
|
+
*/
|
|
149
|
+
resumeSubscriptions() {
|
|
150
|
+
if (!this.__n37Paused) return;
|
|
151
|
+
this.__n37Paused = false;
|
|
152
|
+
const parent = this.parentNode;
|
|
153
|
+
if (!parent) return;
|
|
154
|
+
const next = this.nextSibling;
|
|
155
|
+
this.remove();
|
|
156
|
+
parent.insertBefore(this, next);
|
|
157
|
+
}
|
|
158
|
+
|
|
120
159
|
disconnectedCallback() {
|
|
121
160
|
super.disconnectedCallback();
|
|
122
161
|
this._unsubscribe();
|
|
@@ -239,6 +278,11 @@ export class FeezalElement extends LitElement {
|
|
|
239
278
|
|
|
240
279
|
/** Subscribe to a single topic — convenience wrapper for subclasses. */
|
|
241
280
|
addSubscription(topic, callback) {
|
|
281
|
+
// N37: wiring attempted while the element's view is paused is dropped
|
|
282
|
+
// — device cards wire in their own connectedCallback AFTER the base
|
|
283
|
+
// guard returned, so the gate must live here to be universal. The
|
|
284
|
+
// resume reconnect cycle re-runs the wiring when the view returns.
|
|
285
|
+
if (this.__n37Paused) return;
|
|
242
286
|
this._subscriptions.push(feezal.connection.sub(topic, callback));
|
|
243
287
|
}
|
|
244
288
|
|
|
@@ -414,6 +458,34 @@ export function availabilityBadge(available) {
|
|
|
414
458
|
return available ? '' : html`<span class="feezal-unavail-badge" title="Device unavailable">⚠</span>`;
|
|
415
459
|
}
|
|
416
460
|
|
|
461
|
+
// E124: shared low-battery badge — a crisp, self-drawn tiny horizontal battery
|
|
462
|
+
// showing a single low charge bar (far more recognizable at small sizes than the
|
|
463
|
+
// Material `battery_alert` ligature, and no exclamation clutter). Bottom-right by
|
|
464
|
+
// default; families whose bottom-right corner is taken override via
|
|
465
|
+
// `--feezal-battery-*` (or plain right/left).
|
|
466
|
+
export const feezalBatteryStyles = css`
|
|
467
|
+
.feezal-batt-badge {
|
|
468
|
+
position: absolute;
|
|
469
|
+
bottom: var(--feezal-battery-bottom, 6px);
|
|
470
|
+
right: var(--feezal-battery-right, 6px);
|
|
471
|
+
width: var(--feezal-battery-size, 20px);
|
|
472
|
+
height: var(--feezal-battery-size, 20px);
|
|
473
|
+
color: var(--feezal-battery-color, var(--warning-color, #f59e0b));
|
|
474
|
+
opacity: 0.95; pointer-events: none; z-index: 2;
|
|
475
|
+
}
|
|
476
|
+
.feezal-batt-badge svg { width: 100%; height: 100%; display: block; }
|
|
477
|
+
`;
|
|
478
|
+
|
|
479
|
+
/** Low-battery badge template partial: renders nothing unless the battery is low.
|
|
480
|
+
* A tiny battery outline + terminal with one short bar of charge remaining. */
|
|
481
|
+
export function batteryLowBadge(low) {
|
|
482
|
+
return low ? html`<span class="feezal-batt-badge" title="Battery low"><svg viewBox="0 0 24 24" aria-hidden="true">
|
|
483
|
+
<rect x="2.5" y="8.5" width="15" height="7" rx="1.8" fill="none" stroke="currentColor" stroke-width="1.6"/>
|
|
484
|
+
<rect x="18.6" y="10.6" width="2.1" height="2.8" rx="0.7" fill="currentColor"/>
|
|
485
|
+
<rect x="4.3" y="10.3" width="3" height="3.4" rx="0.6" fill="currentColor"/>
|
|
486
|
+
</svg></span>` : '';
|
|
487
|
+
}
|
|
488
|
+
|
|
417
489
|
/**
|
|
418
490
|
* E117: shared descriptor for the `publish-local` attribute — spread into an
|
|
419
491
|
* element's `feezal.attributes` right after `publish` so the label and help
|
|
@@ -430,5 +502,40 @@ export const publishLocalAttribute = {
|
|
|
430
502
|
help: 'Publish page-locally instead of to the broker: the payload reaches only subscribers in THIS browser tab (dialog triggers, view switches, wiring elements together). Nothing is sent over MQTT, nothing is retained, and it works while disconnected.'
|
|
431
503
|
};
|
|
432
504
|
|
|
505
|
+
/**
|
|
506
|
+
* E137 — payload comparison, cross-controller shared machinery: string
|
|
507
|
+
* coercion (case-insensitive) plus boolean true/false matching the HA/z2m
|
|
508
|
+
* ON/OFF conventions. Single source — the copies in feezal-glass and the
|
|
509
|
+
* contact card consolidated here (feezal-glass re-exports for back-compat).
|
|
510
|
+
*/
|
|
511
|
+
export function payloadMatch(value, configured) {
|
|
512
|
+
if (String(value).toLowerCase() === String(configured).toLowerCase()) return true;
|
|
513
|
+
if (value === true && /^(on|true|1|yes)$/i.test(String(configured))) return true;
|
|
514
|
+
if (value === false && /^(off|false|0|no)$/i.test(String(configured))) return true;
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Boolean-attribute converter for DEFAULT-TRUE booleans.
|
|
520
|
+
*
|
|
521
|
+
* Lit's built-in Boolean is *present = true / absent = false*, which cannot
|
|
522
|
+
* represent an explicit "false" for an attribute that defaults to true: the
|
|
523
|
+
* inspector removes the attribute on "off", it serialises as absent via
|
|
524
|
+
* outerHTML, and the viewer's fresh element re-reads its constructor default
|
|
525
|
+
* (true) — so the "off" choice is silently lost on save/deploy.
|
|
526
|
+
*
|
|
527
|
+
* This converter reads the literal "false" / "0" as false (any other present
|
|
528
|
+
* value → true; absent → the constructor default stands), and reflects the
|
|
529
|
+
* value as an explicit "true" / "false" so an OFF state persists. Pair it with
|
|
530
|
+
* the inspector's default-aware boolean write. Apply to any boolean whose
|
|
531
|
+
* descriptor `default` is true:
|
|
532
|
+
*
|
|
533
|
+
* loop: {type: Boolean, reflect: true, converter: feezalBoolean, attribute: 'loop'}
|
|
534
|
+
*/
|
|
535
|
+
export const feezalBoolean = {
|
|
536
|
+
fromAttribute: v => v !== null && v !== 'false' && v !== '0',
|
|
537
|
+
toAttribute: v => (v ? 'true' : 'false')
|
|
538
|
+
};
|
|
539
|
+
|
|
433
540
|
// Re-export so element files can do a single import.
|
|
434
541
|
export {html, css};
|
package/package.json
CHANGED