diff --git a/www/community/Filament-Card/filament-card.js b/www/community/Filament-Card/filament-card.js
new file mode 100644
index 00000000..379e9cfc
--- /dev/null
+++ b/www/community/Filament-Card/filament-card.js
@@ -0,0 +1,2576 @@
+var At = Object.defineProperty;
+var wt = (a, t, e) => t in a ? At(a, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : a[t] = e;
+var j = (a, t, e) => wt(a, typeof t != "symbol" ? t + "" : t, e);
+const J = "filament-card", Q = "filament-card-editor", S = {
+ preset: "spoolman",
+ title: "Filament",
+ group_by: "material",
+ group_sort_by: "name",
+ group_sort_direction: "asc",
+ group_order: [],
+ group_icon: "mdi:printer-3d-nozzle",
+ group_title_actions: {},
+ group_title_icons: {},
+ group_title_colors: {},
+ max_weight: 1e3,
+ hide_archived: !0,
+ sort_by: "remaining_weight",
+ sort_direction: "asc",
+ bar_direction: "vertical",
+ name_position: "bottom",
+ show_name: !0,
+ show_group_title: !0,
+ value_position: "center",
+ use_filament_color: !0,
+ // Custom mode
+ custom_attribute_entities: [],
+ custom_items: [],
+ custom_max_value: 1e3,
+ custom_unit: "g",
+ custom_label_id: "",
+ tap_action: {
+ action: "more-info"
+ },
+ double_tap_action: {
+ action: "none"
+ },
+ hold_action: {
+ action: "none"
+ },
+ spool_actions: {}
+}, St = `
+ha-card {
+ padding: 16px;
+}
+
+.title {
+ font-size: 20px;
+ font-weight: 600;
+ margin-bottom: 16px;
+}
+
+.heading {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 18px;
+ font-weight: 600;
+ margin: 18px 0 10px;
+}
+
+.heading:first-child {
+ margin-top: 0;
+}
+
+.heading ha-icon {
+ --mdc-icon-size: 20px;
+ color: var(--secondary-text-color);
+}
+
+.heading.clickable {
+ cursor: pointer;
+ user-select: none;
+}
+
+.heading.clickable:hover {
+ opacity: 0.85;
+}
+
+.spools {
+ display: flex;
+ gap: 0;
+ overflow-x: auto;
+ align-items: flex-end;
+ padding-bottom: 4px;
+}
+
+.spools.horizontal {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 6px;
+}
+
+.spool {
+ width: 70px;
+ min-width: 70px;
+ text-align: center;
+ cursor: pointer;
+ padding: 8px 4px;
+ box-sizing: border-box;
+}
+
+.spool.vertical {
+ width: 70px;
+ min-width: 70px;
+}
+
+.spool.vertical.name-top {
+ display: flex;
+ flex-direction: column;
+}
+
+.spool.vertical.name-top .name {
+ order: -1;
+ margin-top: 0;
+ margin-bottom: 6px;
+}
+
+.spool.horizontal {
+ width: 100%;
+ min-width: 160px;
+ display: grid;
+ grid-template-columns: 1fr;
+}
+
+.spool.horizontal.name-left,
+.spool.horizontal.name-right {
+ display: grid;
+ grid-template-columns: auto 1fr;
+ align-items: center;
+ gap: 8px;
+}
+
+.spool.horizontal.name-right {
+ grid-template-columns: 1fr auto;
+}
+
+.spool.horizontal.name-left .name,
+.spool.horizontal.name-right .name {
+ margin-top: 0;
+ white-space: nowrap;
+}
+
+.bar {
+ height: 200px;
+ border-radius: 10px;
+ background: var(--divider-color);
+ position: relative;
+ overflow: hidden;
+}
+
+.spool.horizontal .bar {
+ height: 28px;
+}
+
+.fill {
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ background: var(--primary-color);
+}
+
+.spool.vertical .fill {
+ width: 100%;
+ height: 0;
+ border-radius: 10px 10px 0 0;
+}
+
+.spool.horizontal .fill {
+ top: 0;
+ bottom: 0;
+ height: 100%;
+ width: 0;
+ border-radius: 10px 0 0 10px;
+}
+
+.value {
+ position: absolute;
+ z-index: 2;
+ left: 0;
+ right: 0;
+ bottom: 20px;
+ font-size: 15px;
+ font-weight: 700;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0,0,0,.25);
+}
+
+.spool.vertical.value-top .value {
+ top: 12px;
+ bottom: auto;
+}
+
+.spool.vertical.value-center .value {
+ top: 50%;
+ bottom: auto;
+ transform: translateY(-50%);
+}
+
+.spool.vertical.value-bottom .value {
+ bottom: 20px;
+}
+
+.spool.horizontal .value {
+ top: 50%;
+ bottom: auto;
+ transform: translateY(-50%);
+}
+
+.spool.horizontal.value-left .value {
+ left: 10px;
+ right: auto;
+ text-align: left;
+}
+
+.spool.horizontal.value-center .value {
+ left: 0;
+ right: 0;
+ text-align: center;
+}
+
+.spool.horizontal.value-right .value {
+ left: auto;
+ right: 10px;
+ text-align: right;
+}
+
+.name {
+ font-size: 12px;
+ font-weight: bold;
+ margin-top: 6px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.spool.horizontal .name {
+ text-align: left;
+ margin-top: 4px;
+}
+
+.empty {
+ color: var(--secondary-text-color);
+ padding: 8px 0;
+}
+
+.expandable-section {
+ border: 1px solid var(--divider-color);
+ border-radius: 8px;
+ overflow: hidden;
+}
+
+.expandable-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ padding: 12px;
+ cursor: pointer;
+ user-select: none;
+ font-weight: 500;
+ background: var(--secondary-background-color);
+}
+
+.expandable-header:hover {
+ filter: brightness(1.03);
+}
+
+.expandable-content {
+ display: grid;
+ gap: 12px;
+ padding: 12px;
+}
+`;
+function Ct(a) {
+ return a ? a.startsWith("#") ? a : `#${a}` : "#999999";
+}
+function A(a) {
+ if (!a || a.startsWith("var("))
+ return "var(--primary-text-color)";
+ const t = a.replace("#", "");
+ if (t.length !== 6)
+ return "var(--primary-text-color)";
+ const e = parseInt(t.substring(0, 2), 16), n = parseInt(t.substring(2, 4), 16), i = parseInt(t.substring(4, 6), 16);
+ return (e * 299 + n * 587 + i * 114) / 1e3 > 140 ? "black" : "white";
+}
+function Et(a, t, e) {
+ const n = a.value_position || "center";
+ return a.use_filament_color ? a.bar_direction === "horizontal" ? n === "left" && e >= 15 || n === "center" && e >= 50 || n === "right" && e >= 85 ? A(t) : "var(--primary-text-color)" : a.bar_direction === "vertical" && (n === "bottom" && e >= 15 || n === "center" && e >= 50 || n === "top" && e >= 85) ? A(t) : "var(--primary-text-color)" : "var(--primary-text-color)";
+}
+function Tt(a, t, e) {
+ return a.bar_direction === "horizontal" ? `width:${t}%; background:${e};` : `height:${t}%; background:${e};`;
+}
+function C(a, t) {
+ const e = t.state.attributes;
+ return a.group_by === "color" ? e.filament_name || e.filament_color_hex || "unknown" : a.group_by === "material" ? e.filament_material || "unknown" : a.group_by === "vendor" ? e.filament_vendor_name || "unknown" : "all";
+}
+function B(a, t, e) {
+ const n = a.sort_by, i = a.sort_direction === "desc" ? -1 : 1, r = t.state.attributes[n] ?? t.state.state ?? "", s = e.state.attributes[n] ?? e.state.state ?? "", o = Number(r), l = Number(s);
+ return !Number.isNaN(o) && !Number.isNaN(l) ? (o - l) * i : String(r).localeCompare(String(s), void 0, { numeric: !0 }) * i;
+}
+function Nt(a, t, e, n) {
+ const i = a.group_sort_direction === "desc" ? -1 : 1, r = n.filter((o) => C(a, o) === t), s = n.filter((o) => C(a, o) === e);
+ switch (a.group_sort_by) {
+ case "total_remaining_weight": {
+ const o = r.reduce((c, h) => c + Number(h.state.attributes.remaining_weight || 0), 0), l = s.reduce((c, h) => c + Number(h.state.attributes.remaining_weight || 0), 0);
+ return (o - l) * i;
+ }
+ case "max_remaining_weight": {
+ const o = Math.max(...r.map((c) => Number(c.state.attributes.remaining_weight || 0))), l = Math.max(...s.map((c) => Number(c.state.attributes.remaining_weight || 0)));
+ return (o - l) * i;
+ }
+ case "spool_count":
+ return (r.length - s.length) * i;
+ case "name":
+ default:
+ return String(t).localeCompare(String(e), void 0, { numeric: !0 }) * i;
+ }
+}
+function gt(a, t, e) {
+ var n;
+ if ((n = a.group_order) != null && n.length) {
+ const i = a.group_order.map((r) => String(r));
+ return t.sort((r, s) => {
+ const o = i.indexOf(String(r)), l = i.indexOf(String(s)), c = o !== -1, h = l !== -1;
+ return c && h ? o - l : c ? -1 : h ? 1 : String(r).localeCompare(String(s), void 0, { numeric: !0 });
+ });
+ }
+ return t.sort((i, r) => Nt(a, i, r, e));
+}
+class Ot extends HTMLElement {
+ static getConfigElement() {
+ return document.createElement(Q);
+ }
+ static getStubConfig() {
+ return { ...S };
+ }
+ setConfig(t) {
+ this.config = {
+ ...S,
+ group_by: t.group_by ?? (t.group_by_material === !1 ? "none" : "material"),
+ group_order: t.group_order ?? t.materials ?? S.group_order,
+ ...t
+ }, this.shadowRoot || this.attachShadow({ mode: "open" });
+ }
+ set hass(t) {
+ const e = this._spoolSignature;
+ this._hass = t, this._spoolSignature = this.createSpoolSignature(t), (!e || e !== this._spoolSignature) && this.render();
+ }
+ createSpoolSignature(t) {
+ var n;
+ const e = ((n = this.config) == null ? void 0 : n.preset) || "spoolman";
+ if (e === "custom_attributes")
+ return (this.config.custom_attribute_entities || []).map((i) => {
+ const r = t.states[i];
+ if (!r) return `${i}|missing`;
+ const s = r.attributes || {};
+ return [
+ i,
+ r.state,
+ s.value,
+ s.remaining_weight,
+ s.max_value,
+ s.max,
+ s.name,
+ s.friendly_name,
+ s.group,
+ s.material,
+ s.vendor,
+ s.color,
+ s.filament_color_hex,
+ s.unit
+ ].join("|");
+ }).sort().join(";");
+ if (e === "custom_entities")
+ return (this.config.custom_items || []).map((i, r) => {
+ const s = [
+ i.value_entity,
+ i.max_entity,
+ i.color_entity,
+ i.group_entity,
+ i.vendor_entity,
+ i.name_entity
+ ].filter(Boolean);
+ return [
+ r,
+ i.name,
+ i.group,
+ i.vendor,
+ i.color,
+ i.max,
+ i.max_value,
+ i.unit,
+ ...s.map((o) => {
+ var c;
+ const l = t.states[o];
+ return l ? `${o}|${l.state}|${((c = l.attributes) == null ? void 0 : c.friendly_name) || ""}` : `${o}|missing`;
+ })
+ ].join("|");
+ }).sort().join(";");
+ if (e === "custom_label") {
+ const i = this.config.custom_label_id;
+ return !i || !t.entities ? "" : Object.entries(t.entities).filter(([, r]) => {
+ var s;
+ return (s = r.labels) == null ? void 0 : s.includes(i);
+ }).map(([r]) => {
+ const s = t.states[r];
+ if (!s) return `${r}|missing`;
+ const o = s.attributes || {};
+ return [
+ r,
+ s.state,
+ o.value,
+ o.remaining_weight,
+ o.max_value,
+ o.max,
+ o.name,
+ o.friendly_name,
+ o.group,
+ o.material,
+ o.vendor,
+ o.color,
+ o.filament_color_hex,
+ o.unit
+ ].join("|");
+ }).sort().join(";");
+ }
+ return Object.entries(t.states).filter(([, i]) => {
+ var r;
+ return (r = i.attributes) == null ? void 0 : r.filament_material;
+ }).map(([i, r]) => {
+ const s = r.attributes;
+ return [
+ i,
+ r.state,
+ s.remaining_weight,
+ s.filament_weight,
+ s.archived,
+ s.filament_material,
+ s.filament_name,
+ s.filament_color_hex,
+ s.filament_vendor_name
+ ].join("|");
+ }).sort().join(";");
+ }
+ render() {
+ if (!this._hass || !this.config) return;
+ const t = this.getItems();
+ this._actionConfigsByEntity = Object.fromEntries(
+ t.map((i) => [i.entity_id, this.getActionConfigsForItem(i)])
+ );
+ const e = Math.max(
+ ...t.map(
+ (i) => Number(i.state.attributes.filament_weight || i.state.attributes.remaining_weight || 0)
+ ),
+ Number(this.config.max_weight || 0),
+ 1e3
+ ), n = this.config.group_by === "none" ? this.renderFlat(t, e) : this.renderGrouped(t, e);
+ this.shadowRoot.innerHTML = `
+
+
+
+ ${this.config.title ? `${this.config.title}
` : ""}
+ ${n}
+
+ `, this.attachActionHandlers(), this.attachGroupTitleActionHandlers();
+ }
+ getItems() {
+ const t = this.config.preset || "spoolman";
+ let e;
+ return t === "custom_attributes" ? e = this.getCustomAttributeItems() : t === "custom_entities" ? e = this.getCustomEntityItems() : t === "custom_label" ? e = this.getCustomLabelItems() : e = this.getSpoolmanItems(), this.applyFilters(e);
+ }
+ applyFilters(t) {
+ if (this.config.group_by === "none")
+ return t;
+ const e = this.config.filters || {}, n = this.normalizeFilterValues(e.include), i = this.normalizeFilterValues(e.exclude);
+ return !n.length && !i.length ? t : t.filter((r) => {
+ const s = C(this.config, r), o = this.normalizeFilterValue(s);
+ return !(n.length && !n.includes(o) || i.includes(o));
+ });
+ }
+ normalizeFilterValues(t) {
+ return Array.isArray(t) ? t.map((e) => this.normalizeFilterValue(e)).filter(Boolean) : [];
+ }
+ normalizeFilterValue(t) {
+ return String(t ?? "").trim().toLocaleLowerCase();
+ }
+ getSpoolmanItems() {
+ return Object.entries(this._hass.states).map(([t, e]) => ({ entity_id: t, state: e })).filter(({ state: t }) => {
+ var e;
+ return (e = t.attributes) == null ? void 0 : e.filament_material;
+ }).filter(({ state: t }) => !this.config.hide_archived || t.attributes.archived === !1).sort((t, e) => B(this.config, t, e));
+ }
+ getCustomAttributeItems() {
+ const t = this.config.custom_attribute_entities || [], e = Number(this.config.custom_max_value || 1e3), n = this.config.custom_unit || "g";
+ return t.map((i) => {
+ const r = this._hass.states[i];
+ if (!r) return null;
+ const s = r.attributes || {}, o = Number(s.value ?? s.remaining_weight ?? r.state);
+ return Number.isNaN(o) ? null : this.createVirtualItem({
+ entity_id: i,
+ value: o,
+ max: Number(s.max_value ?? s.max ?? e),
+ name: s.name || s.friendly_name || i,
+ group: s.group || s.material || "Custom",
+ vendor: s.vendor || "",
+ color: s.color || s.filament_color_hex,
+ unit: s.unit || n,
+ tap_action: s.tap_action,
+ double_tap_action: s.double_tap_action,
+ hold_action: s.hold_action
+ });
+ }).filter(Boolean).sort((i, r) => B(this.config, i, r));
+ }
+ getCustomEntityItems() {
+ const t = this.config.custom_items || [], e = Number(this.config.custom_max_value || 1e3), n = this.config.custom_unit || "g";
+ return t.map((i, r) => {
+ var m, g, N, O, P, H;
+ const s = this._hass.states[i.value_entity];
+ if (!s) return null;
+ const o = Number(s.state);
+ if (Number.isNaN(o)) return null;
+ const l = i.max_entity ? Number(((m = this._hass.states[i.max_entity]) == null ? void 0 : m.state) ?? e) : Number(i.max ?? i.max_value ?? e), c = i.color_entity ? (g = this._hass.states[i.color_entity]) == null ? void 0 : g.state : i.color, h = i.group_entity ? (N = this._hass.states[i.group_entity]) == null ? void 0 : N.state : i.group, d = i.vendor_entity ? (O = this._hass.states[i.vendor_entity]) == null ? void 0 : O.state : i.vendor, _ = i.name_entity ? (P = this._hass.states[i.name_entity]) == null ? void 0 : P.state : i.name;
+ return this.createVirtualItem({
+ entity_id: i.value_entity || `custom_item_${r}`,
+ value: o,
+ max: Number.isNaN(l) ? e : l,
+ name: _ || ((H = s.attributes) == null ? void 0 : H.friendly_name) || i.value_entity,
+ group: h || "Custom",
+ vendor: d || "",
+ color: c,
+ unit: i.unit || n,
+ tap_action: i.tap_action,
+ double_tap_action: i.double_tap_action,
+ hold_action: i.hold_action
+ });
+ }).filter(Boolean).sort((i, r) => B(this.config, i, r));
+ }
+ getCustomLabelItems() {
+ var n;
+ const t = this.config.custom_label_id;
+ if (!t || !((n = this._hass) != null && n.entities)) return [];
+ const e = Object.entries(this._hass.entities).filter(([, i]) => {
+ var r;
+ return (r = i.labels) == null ? void 0 : r.includes(t);
+ }).map(([i]) => i);
+ return this.config.custom_attribute_entities = e, this.getCustomAttributeItems();
+ }
+ getActionConfigsForItem(t) {
+ var r, s;
+ const e = t.entity_id, n = ((r = t.state) == null ? void 0 : r.attributes) || {}, i = ((s = this.config.spool_actions) == null ? void 0 : s[e]) || {};
+ return {
+ tap: n.tap_action || t.tap_action || i.tap_action || this.config.tap_action,
+ double_tap: n.double_tap_action || t.double_tap_action || i.double_tap_action || this.config.double_tap_action,
+ hold: n.hold_action || t.hold_action || i.hold_action || this.config.hold_action
+ };
+ }
+ getGroupTitleActionConfigs(t) {
+ var n, i;
+ const e = ((n = this.config.group_title_actions) == null ? void 0 : n[t]) || ((i = this.config.group_actions) == null ? void 0 : i[t]) || {};
+ return {
+ tap: e.tap_action || { action: "none" },
+ double_tap: e.double_tap_action || { action: "none" },
+ hold: e.hold_action || { action: "none" }
+ };
+ }
+ getGroupTitleIcon(t) {
+ var e, n;
+ return ((e = this.config.group_title_icons) == null ? void 0 : e[t]) || ((n = this.config.group_icons) == null ? void 0 : n[t]) || this.config.group_icon;
+ }
+ getGroupTitleColor(t) {
+ var e, n;
+ return ((e = this.config.group_title_colors) == null ? void 0 : e[t]) || ((n = this.config.group_colors) == null ? void 0 : n[t]);
+ }
+ attachGroupTitleActionHandlers() {
+ this.shadowRoot.querySelectorAll(".heading[data-group]").forEach((t) => {
+ const e = t.dataset.group;
+ if (!e) return;
+ const n = this.getGroupTitleActionConfigs(e);
+ t.addEventListener("click", (i) => {
+ i.stopPropagation(), this.handleTap(e, n);
+ }), t.addEventListener("dblclick", (i) => {
+ i.stopPropagation(), this.handleDoubleTap(e, n);
+ }), t.addEventListener(
+ "pointerdown",
+ () => this.handleHoldStart(e, n)
+ ), t.addEventListener("pointerup", () => this.handleHoldEnd()), t.addEventListener("pointerleave", () => this.handleHoldEnd()), t.addEventListener("pointercancel", () => this.handleHoldEnd());
+ });
+ }
+ createVirtualItem({
+ entity_id: t,
+ value: e,
+ max: n,
+ name: i,
+ group: r,
+ vendor: s,
+ color: o,
+ unit: l,
+ tap_action: c,
+ double_tap_action: h,
+ hold_action: d
+ }) {
+ return {
+ entity_id: t,
+ tap_action: c,
+ double_tap_action: h,
+ hold_action: d,
+ state: {
+ state: String(e),
+ attributes: {
+ friendly_name: i,
+ remaining_weight: e,
+ filament_weight: n,
+ filament_material: r,
+ filament_name: i,
+ filament_vendor_name: s,
+ filament_color_hex: o,
+ archived: !1,
+ custom_unit: l,
+ tap_action: c,
+ double_tap_action: h,
+ hold_action: d
+ }
+ }
+ };
+ }
+ fireHassAction(t, e, n) {
+ this.dispatchEvent(
+ new CustomEvent("hass-action", {
+ detail: {
+ config: {
+ entity: t,
+ tap_action: e.tap || { action: "more-info" },
+ double_tap_action: e.double_tap || { action: "none" },
+ hold_action: e.hold || { action: "none" }
+ },
+ action: n
+ },
+ bubbles: !0,
+ composed: !0
+ })
+ );
+ }
+ handleTap(t, e) {
+ if (this._holdFired) {
+ this._holdFired = !1;
+ return;
+ }
+ clearTimeout(this._tapTimer), this._tapTimer = setTimeout(() => {
+ this.fireHassAction(t, e, "tap");
+ }, 250);
+ }
+ handleDoubleTap(t, e) {
+ clearTimeout(this._tapTimer), this.fireHassAction(t, e, "double_tap");
+ }
+ handleHoldStart(t, e) {
+ this._holdFired = !1, clearTimeout(this._holdTimer), this._holdTimer = setTimeout(() => {
+ this._holdFired = !0, this.fireHassAction(t, e, "hold");
+ }, 500);
+ }
+ handleHoldEnd() {
+ clearTimeout(this._holdTimer);
+ }
+ attachActionHandlers() {
+ this.shadowRoot.querySelectorAll(".spool").forEach((t) => {
+ var i;
+ const e = t.dataset.entity;
+ if (!e) return;
+ const n = ((i = this._actionConfigsByEntity) == null ? void 0 : i[e]) || {
+ tap: this.config.tap_action,
+ double_tap: this.config.double_tap_action,
+ hold: this.config.hold_action
+ };
+ t.addEventListener("click", (r) => {
+ r.stopPropagation(), this.handleTap(e, n);
+ }), t.addEventListener("dblclick", (r) => {
+ r.stopPropagation(), this.handleDoubleTap(e, n);
+ }), t.addEventListener(
+ "pointerdown",
+ () => this.handleHoldStart(e, n)
+ ), t.addEventListener("pointerup", () => this.handleHoldEnd()), t.addEventListener("pointerleave", () => this.handleHoldEnd()), t.addEventListener("pointercancel", () => this.handleHoldEnd());
+ });
+ }
+ renderGrouped(t, e) {
+ let n = [...new Set(t.map((i) => C(this.config, i)))];
+ return n = gt(this.config, n, t), n.map((i) => {
+ const r = this.getGroupTitleIcon(i), s = this.getGroupTitleColor(i);
+ this.getGroupTitleActionConfigs(i);
+ const o = t.filter((l) => C(this.config, l) === i);
+ return o.length ? `
+ ${this.config.show_group_title ? `
+
+ ${r !== "none" ? `` : ""}
+
+ ${i}
+
+
+ ` : ""}
+
+
+ ${o.map((l) => this.renderSpool(l, e)).join("")}
+
+ ` : "";
+ }).join("");
+ }
+ renderFlat(t, e) {
+ return t.length ? `
+
+ ${t.map((n) => this.renderSpool(n, e)).join("")}
+
+ ` : 'Keine Filamente gefunden.
';
+ }
+ renderSpool({ entity_id: t, state: e }, n) {
+ const i = e.attributes, r = Number(i.remaining_weight ?? e.state ?? 0), s = Number(i.filament_weight || n || 1e3), o = Math.max(0, Math.min(100, r / s * 100)), l = this.config.use_filament_color ? Ct(i.filament_color_hex) : "var(--primary-color)", c = Et(this.config, l, o), h = this.config.group_by === "color" ? i.filament_material || i.friendly_name || t : i.filament_name || i.friendly_name || t;
+ return `
+
+ ${this.config.show_name && ["top", "left"].includes(this.config.name_position) ? `
${h}
` : ""}
+
+
+
+
${r.toFixed(1)}${i.custom_unit || "g"}
+
+
+ ${this.config.show_name && ["bottom", "right"].includes(this.config.name_position) ? `
${h}
` : ""}
+
+ `;
+ }
+ getCardSize() {
+ return 4;
+ }
+}
+customElements.get(J) || customElements.define(J, Ot);
+/**
+ * @license
+ * Copyright 2019 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+const M = globalThis, tt = M.ShadowRoot && (M.ShadyCSS === void 0 || M.ShadyCSS.nativeShadow) && "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, et = Symbol(), nt = /* @__PURE__ */ new WeakMap();
+let ft = class {
+ constructor(t, e, n) {
+ if (this._$cssResult$ = !0, n !== et) throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");
+ this.cssText = t, this.t = e;
+ }
+ get styleSheet() {
+ let t = this.o;
+ const e = this.t;
+ if (tt && t === void 0) {
+ const n = e !== void 0 && e.length === 1;
+ n && (t = nt.get(e)), t === void 0 && ((this.o = t = new CSSStyleSheet()).replaceSync(this.cssText), n && nt.set(e, t));
+ }
+ return t;
+ }
+ toString() {
+ return this.cssText;
+ }
+};
+const Pt = (a) => new ft(typeof a == "string" ? a : a + "", void 0, et), Ht = (a, ...t) => {
+ const e = a.length === 1 ? a[0] : t.reduce((n, i, r) => n + ((s) => {
+ if (s._$cssResult$ === !0) return s.cssText;
+ if (typeof s == "number") return s;
+ throw Error("Value passed to 'css' function must be a 'css' function result: " + s + ". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.");
+ })(i) + a[r + 1], a[0]);
+ return new ft(e, a, et);
+}, It = (a, t) => {
+ if (tt) a.adoptedStyleSheets = t.map((e) => e instanceof CSSStyleSheet ? e : e.styleSheet);
+ else for (const e of t) {
+ const n = document.createElement("style"), i = M.litNonce;
+ i !== void 0 && n.setAttribute("nonce", i), n.textContent = e.cssText, a.appendChild(n);
+ }
+}, rt = tt ? (a) => a : (a) => a instanceof CSSStyleSheet ? ((t) => {
+ let e = "";
+ for (const n of t.cssRules) e += n.cssText;
+ return Pt(e);
+})(a) : a;
+/**
+ * @license
+ * Copyright 2017 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+const { is: kt, defineProperty: Vt, getOwnPropertyDescriptor: Gt, getOwnPropertyNames: zt, getOwnPropertySymbols: Ft, getPrototypeOf: Ut } = Object, v = globalThis, st = v.trustedTypes, Lt = st ? st.emptyScript : "", W = v.reactiveElementPolyfillSupport, k = (a, t) => a, X = { toAttribute(a, t) {
+ switch (t) {
+ case Boolean:
+ a = a ? Lt : null;
+ break;
+ case Object:
+ case Array:
+ a = a == null ? a : JSON.stringify(a);
+ }
+ return a;
+}, fromAttribute(a, t) {
+ let e = a;
+ switch (t) {
+ case Boolean:
+ e = a !== null;
+ break;
+ case Number:
+ e = a === null ? null : Number(a);
+ break;
+ case Object:
+ case Array:
+ try {
+ e = JSON.parse(a);
+ } catch {
+ e = null;
+ }
+ }
+ return e;
+} }, vt = (a, t) => !kt(a, t), ot = { attribute: !0, type: String, converter: X, reflect: !1, useDefault: !1, hasChanged: vt };
+Symbol.metadata ?? (Symbol.metadata = Symbol("metadata")), v.litPropertyMetadata ?? (v.litPropertyMetadata = /* @__PURE__ */ new WeakMap());
+let w = class extends HTMLElement {
+ static addInitializer(t) {
+ this._$Ei(), (this.l ?? (this.l = [])).push(t);
+ }
+ static get observedAttributes() {
+ return this.finalize(), this._$Eh && [...this._$Eh.keys()];
+ }
+ static createProperty(t, e = ot) {
+ if (e.state && (e.attribute = !1), this._$Ei(), this.prototype.hasOwnProperty(t) && ((e = Object.create(e)).wrapped = !0), this.elementProperties.set(t, e), !e.noAccessor) {
+ const n = Symbol(), i = this.getPropertyDescriptor(t, n, e);
+ i !== void 0 && Vt(this.prototype, t, i);
+ }
+ }
+ static getPropertyDescriptor(t, e, n) {
+ const { get: i, set: r } = Gt(this.prototype, t) ?? { get() {
+ return this[e];
+ }, set(s) {
+ this[e] = s;
+ } };
+ return { get: i, set(s) {
+ const o = i == null ? void 0 : i.call(this);
+ r == null || r.call(this, s), this.requestUpdate(t, o, n);
+ }, configurable: !0, enumerable: !0 };
+ }
+ static getPropertyOptions(t) {
+ return this.elementProperties.get(t) ?? ot;
+ }
+ static _$Ei() {
+ if (this.hasOwnProperty(k("elementProperties"))) return;
+ const t = Ut(this);
+ t.finalize(), t.l !== void 0 && (this.l = [...t.l]), this.elementProperties = new Map(t.elementProperties);
+ }
+ static finalize() {
+ if (this.hasOwnProperty(k("finalized"))) return;
+ if (this.finalized = !0, this._$Ei(), this.hasOwnProperty(k("properties"))) {
+ const e = this.properties, n = [...zt(e), ...Ft(e)];
+ for (const i of n) this.createProperty(i, e[i]);
+ }
+ const t = this[Symbol.metadata];
+ if (t !== null) {
+ const e = litPropertyMetadata.get(t);
+ if (e !== void 0) for (const [n, i] of e) this.elementProperties.set(n, i);
+ }
+ this._$Eh = /* @__PURE__ */ new Map();
+ for (const [e, n] of this.elementProperties) {
+ const i = this._$Eu(e, n);
+ i !== void 0 && this._$Eh.set(i, e);
+ }
+ this.elementStyles = this.finalizeStyles(this.styles);
+ }
+ static finalizeStyles(t) {
+ const e = [];
+ if (Array.isArray(t)) {
+ const n = new Set(t.flat(1 / 0).reverse());
+ for (const i of n) e.unshift(rt(i));
+ } else t !== void 0 && e.push(rt(t));
+ return e;
+ }
+ static _$Eu(t, e) {
+ const n = e.attribute;
+ return n === !1 ? void 0 : typeof n == "string" ? n : typeof t == "string" ? t.toLowerCase() : void 0;
+ }
+ constructor() {
+ super(), this._$Ep = void 0, this.isUpdatePending = !1, this.hasUpdated = !1, this._$Em = null, this._$Ev();
+ }
+ _$Ev() {
+ var t;
+ this._$ES = new Promise((e) => this.enableUpdating = e), this._$AL = /* @__PURE__ */ new Map(), this._$E_(), this.requestUpdate(), (t = this.constructor.l) == null || t.forEach((e) => e(this));
+ }
+ addController(t) {
+ var e;
+ (this._$EO ?? (this._$EO = /* @__PURE__ */ new Set())).add(t), this.renderRoot !== void 0 && this.isConnected && ((e = t.hostConnected) == null || e.call(t));
+ }
+ removeController(t) {
+ var e;
+ (e = this._$EO) == null || e.delete(t);
+ }
+ _$E_() {
+ const t = /* @__PURE__ */ new Map(), e = this.constructor.elementProperties;
+ for (const n of e.keys()) this.hasOwnProperty(n) && (t.set(n, this[n]), delete this[n]);
+ t.size > 0 && (this._$Ep = t);
+ }
+ createRenderRoot() {
+ const t = this.shadowRoot ?? this.attachShadow(this.constructor.shadowRootOptions);
+ return It(t, this.constructor.elementStyles), t;
+ }
+ connectedCallback() {
+ var t;
+ this.renderRoot ?? (this.renderRoot = this.createRenderRoot()), this.enableUpdating(!0), (t = this._$EO) == null || t.forEach((e) => {
+ var n;
+ return (n = e.hostConnected) == null ? void 0 : n.call(e);
+ });
+ }
+ enableUpdating(t) {
+ }
+ disconnectedCallback() {
+ var t;
+ (t = this._$EO) == null || t.forEach((e) => {
+ var n;
+ return (n = e.hostDisconnected) == null ? void 0 : n.call(e);
+ });
+ }
+ attributeChangedCallback(t, e, n) {
+ this._$AK(t, n);
+ }
+ _$ET(t, e) {
+ var r;
+ const n = this.constructor.elementProperties.get(t), i = this.constructor._$Eu(t, n);
+ if (i !== void 0 && n.reflect === !0) {
+ const s = (((r = n.converter) == null ? void 0 : r.toAttribute) !== void 0 ? n.converter : X).toAttribute(e, n.type);
+ this._$Em = t, s == null ? this.removeAttribute(i) : this.setAttribute(i, s), this._$Em = null;
+ }
+ }
+ _$AK(t, e) {
+ var r, s;
+ const n = this.constructor, i = n._$Eh.get(t);
+ if (i !== void 0 && this._$Em !== i) {
+ const o = n.getPropertyOptions(i), l = typeof o.converter == "function" ? { fromAttribute: o.converter } : ((r = o.converter) == null ? void 0 : r.fromAttribute) !== void 0 ? o.converter : X;
+ this._$Em = i;
+ const c = l.fromAttribute(e, o.type);
+ this[i] = c ?? ((s = this._$Ej) == null ? void 0 : s.get(i)) ?? c, this._$Em = null;
+ }
+ }
+ requestUpdate(t, e, n, i = !1, r) {
+ var s;
+ if (t !== void 0) {
+ const o = this.constructor;
+ if (i === !1 && (r = this[t]), n ?? (n = o.getPropertyOptions(t)), !((n.hasChanged ?? vt)(r, e) || n.useDefault && n.reflect && r === ((s = this._$Ej) == null ? void 0 : s.get(t)) && !this.hasAttribute(o._$Eu(t, n)))) return;
+ this.C(t, e, n);
+ }
+ this.isUpdatePending === !1 && (this._$ES = this._$EP());
+ }
+ C(t, e, { useDefault: n, reflect: i, wrapped: r }, s) {
+ n && !(this._$Ej ?? (this._$Ej = /* @__PURE__ */ new Map())).has(t) && (this._$Ej.set(t, s ?? e ?? this[t]), r !== !0 || s !== void 0) || (this._$AL.has(t) || (this.hasUpdated || n || (e = void 0), this._$AL.set(t, e)), i === !0 && this._$Em !== t && (this._$Eq ?? (this._$Eq = /* @__PURE__ */ new Set())).add(t));
+ }
+ async _$EP() {
+ this.isUpdatePending = !0;
+ try {
+ await this._$ES;
+ } catch (e) {
+ Promise.reject(e);
+ }
+ const t = this.scheduleUpdate();
+ return t != null && await t, !this.isUpdatePending;
+ }
+ scheduleUpdate() {
+ return this.performUpdate();
+ }
+ performUpdate() {
+ var n;
+ if (!this.isUpdatePending) return;
+ if (!this.hasUpdated) {
+ if (this.renderRoot ?? (this.renderRoot = this.createRenderRoot()), this._$Ep) {
+ for (const [r, s] of this._$Ep) this[r] = s;
+ this._$Ep = void 0;
+ }
+ const i = this.constructor.elementProperties;
+ if (i.size > 0) for (const [r, s] of i) {
+ const { wrapped: o } = s, l = this[r];
+ o !== !0 || this._$AL.has(r) || l === void 0 || this.C(r, void 0, s, l);
+ }
+ }
+ let t = !1;
+ const e = this._$AL;
+ try {
+ t = this.shouldUpdate(e), t ? (this.willUpdate(e), (n = this._$EO) == null || n.forEach((i) => {
+ var r;
+ return (r = i.hostUpdate) == null ? void 0 : r.call(i);
+ }), this.update(e)) : this._$EM();
+ } catch (i) {
+ throw t = !1, this._$EM(), i;
+ }
+ t && this._$AE(e);
+ }
+ willUpdate(t) {
+ }
+ _$AE(t) {
+ var e;
+ (e = this._$EO) == null || e.forEach((n) => {
+ var i;
+ return (i = n.hostUpdated) == null ? void 0 : i.call(n);
+ }), this.hasUpdated || (this.hasUpdated = !0, this.firstUpdated(t)), this.updated(t);
+ }
+ _$EM() {
+ this._$AL = /* @__PURE__ */ new Map(), this.isUpdatePending = !1;
+ }
+ get updateComplete() {
+ return this.getUpdateComplete();
+ }
+ getUpdateComplete() {
+ return this._$ES;
+ }
+ shouldUpdate(t) {
+ return !0;
+ }
+ update(t) {
+ this._$Eq && (this._$Eq = this._$Eq.forEach((e) => this._$ET(e, this[e]))), this._$EM();
+ }
+ updated(t) {
+ }
+ firstUpdated(t) {
+ }
+};
+w.elementStyles = [], w.shadowRootOptions = { mode: "open" }, w[k("elementProperties")] = /* @__PURE__ */ new Map(), w[k("finalized")] = /* @__PURE__ */ new Map(), W == null || W({ ReactiveElement: w }), (v.reactiveElementVersions ?? (v.reactiveElementVersions = [])).push("2.1.2");
+/**
+ * @license
+ * Copyright 2017 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+const V = globalThis, at = (a) => a, D = V.trustedTypes, lt = D ? D.createPolicy("lit-html", { createHTML: (a) => a }) : void 0, bt = "$lit$", f = `lit$${Math.random().toFixed(9).slice(2)}$`, $t = "?" + f, Mt = `<${$t}>`, x = document, z = () => x.createComment(""), F = (a) => a === null || typeof a != "object" && typeof a != "function", it = Array.isArray, Dt = (a) => it(a) || typeof (a == null ? void 0 : a[Symbol.iterator]) == "function", q = `[
+\f\r]`, I = /<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g, ct = /-->/g, ut = />/g, b = RegExp(`>|${q}(?:([^\\s"'>=/]+)(${q}*=${q}*(?:[^
+\f\r"'\`<>=]|("|')|))|$)`, "g"), ht = /'/g, dt = /"/g, yt = /^(?:script|style|textarea|title)$/i, Rt = (a) => (t, ...e) => ({ _$litType$: a, strings: t, values: e }), u = Rt(1), E = Symbol.for("lit-noChange"), p = Symbol.for("lit-nothing"), pt = /* @__PURE__ */ new WeakMap(), $ = x.createTreeWalker(x, 129);
+function xt(a, t) {
+ if (!it(a) || !a.hasOwnProperty("raw")) throw Error("invalid template strings array");
+ return lt !== void 0 ? lt.createHTML(t) : t;
+}
+const jt = (a, t) => {
+ const e = a.length - 1, n = [];
+ let i, r = t === 2 ? "" : t === 3 ? "" : "")), n];
+};
+class U {
+ constructor({ strings: t, _$litType$: e }, n) {
+ let i;
+ this.parts = [];
+ let r = 0, s = 0;
+ const o = t.length - 1, l = this.parts, [c, h] = jt(t, e);
+ if (this.el = U.createElement(c, n), $.currentNode = this.el.content, e === 2 || e === 3) {
+ const d = this.el.content.firstChild;
+ d.replaceWith(...d.childNodes);
+ }
+ for (; (i = $.nextNode()) !== null && l.length < o; ) {
+ if (i.nodeType === 1) {
+ if (i.hasAttributes()) for (const d of i.getAttributeNames()) if (d.endsWith(bt)) {
+ const _ = h[s++], m = i.getAttribute(d).split(f), g = /([.?@])?(.*)/.exec(_);
+ l.push({ type: 1, index: r, name: g[2], strings: m, ctor: g[1] === "." ? Wt : g[1] === "?" ? qt : g[1] === "@" ? Yt : R }), i.removeAttribute(d);
+ } else d.startsWith(f) && (l.push({ type: 6, index: r }), i.removeAttribute(d));
+ if (yt.test(i.tagName)) {
+ const d = i.textContent.split(f), _ = d.length - 1;
+ if (_ > 0) {
+ i.textContent = D ? D.emptyScript : "";
+ for (let m = 0; m < _; m++) i.append(d[m], z()), $.nextNode(), l.push({ type: 2, index: ++r });
+ i.append(d[_], z());
+ }
+ }
+ } else if (i.nodeType === 8) if (i.data === $t) l.push({ type: 2, index: r });
+ else {
+ let d = -1;
+ for (; (d = i.data.indexOf(f, d + 1)) !== -1; ) l.push({ type: 7, index: r }), d += f.length - 1;
+ }
+ r++;
+ }
+ }
+ static createElement(t, e) {
+ const n = x.createElement("template");
+ return n.innerHTML = t, n;
+ }
+}
+function T(a, t, e = a, n) {
+ var s, o;
+ if (t === E) return t;
+ let i = n !== void 0 ? (s = e._$Co) == null ? void 0 : s[n] : e._$Cl;
+ const r = F(t) ? void 0 : t._$litDirective$;
+ return (i == null ? void 0 : i.constructor) !== r && ((o = i == null ? void 0 : i._$AO) == null || o.call(i, !1), r === void 0 ? i = void 0 : (i = new r(a), i._$AT(a, e, n)), n !== void 0 ? (e._$Co ?? (e._$Co = []))[n] = i : e._$Cl = i), i !== void 0 && (t = T(a, i._$AS(a, t.values), i, n)), t;
+}
+class Bt {
+ constructor(t, e) {
+ this._$AV = [], this._$AN = void 0, this._$AD = t, this._$AM = e;
+ }
+ get parentNode() {
+ return this._$AM.parentNode;
+ }
+ get _$AU() {
+ return this._$AM._$AU;
+ }
+ u(t) {
+ const { el: { content: e }, parts: n } = this._$AD, i = ((t == null ? void 0 : t.creationScope) ?? x).importNode(e, !0);
+ $.currentNode = i;
+ let r = $.nextNode(), s = 0, o = 0, l = n[0];
+ for (; l !== void 0; ) {
+ if (s === l.index) {
+ let c;
+ l.type === 2 ? c = new L(r, r.nextSibling, this, t) : l.type === 1 ? c = new l.ctor(r, l.name, l.strings, this, t) : l.type === 6 && (c = new Zt(r, this, t)), this._$AV.push(c), l = n[++o];
+ }
+ s !== (l == null ? void 0 : l.index) && (r = $.nextNode(), s++);
+ }
+ return $.currentNode = x, i;
+ }
+ p(t) {
+ let e = 0;
+ for (const n of this._$AV) n !== void 0 && (n.strings !== void 0 ? (n._$AI(t, n, e), e += n.strings.length - 2) : n._$AI(t[e])), e++;
+ }
+}
+class L {
+ get _$AU() {
+ var t;
+ return ((t = this._$AM) == null ? void 0 : t._$AU) ?? this._$Cv;
+ }
+ constructor(t, e, n, i) {
+ this.type = 2, this._$AH = p, this._$AN = void 0, this._$AA = t, this._$AB = e, this._$AM = n, this.options = i, this._$Cv = (i == null ? void 0 : i.isConnected) ?? !0;
+ }
+ get parentNode() {
+ let t = this._$AA.parentNode;
+ const e = this._$AM;
+ return e !== void 0 && (t == null ? void 0 : t.nodeType) === 11 && (t = e.parentNode), t;
+ }
+ get startNode() {
+ return this._$AA;
+ }
+ get endNode() {
+ return this._$AB;
+ }
+ _$AI(t, e = this) {
+ t = T(this, t, e), F(t) ? t === p || t == null || t === "" ? (this._$AH !== p && this._$AR(), this._$AH = p) : t !== this._$AH && t !== E && this._(t) : t._$litType$ !== void 0 ? this.$(t) : t.nodeType !== void 0 ? this.T(t) : Dt(t) ? this.k(t) : this._(t);
+ }
+ O(t) {
+ return this._$AA.parentNode.insertBefore(t, this._$AB);
+ }
+ T(t) {
+ this._$AH !== t && (this._$AR(), this._$AH = this.O(t));
+ }
+ _(t) {
+ this._$AH !== p && F(this._$AH) ? this._$AA.nextSibling.data = t : this.T(x.createTextNode(t)), this._$AH = t;
+ }
+ $(t) {
+ var r;
+ const { values: e, _$litType$: n } = t, i = typeof n == "number" ? this._$AC(t) : (n.el === void 0 && (n.el = U.createElement(xt(n.h, n.h[0]), this.options)), n);
+ if (((r = this._$AH) == null ? void 0 : r._$AD) === i) this._$AH.p(e);
+ else {
+ const s = new Bt(i, this), o = s.u(this.options);
+ s.p(e), this.T(o), this._$AH = s;
+ }
+ }
+ _$AC(t) {
+ let e = pt.get(t.strings);
+ return e === void 0 && pt.set(t.strings, e = new U(t)), e;
+ }
+ k(t) {
+ it(this._$AH) || (this._$AH = [], this._$AR());
+ const e = this._$AH;
+ let n, i = 0;
+ for (const r of t) i === e.length ? e.push(n = new L(this.O(z()), this.O(z()), this, this.options)) : n = e[i], n._$AI(r), i++;
+ i < e.length && (this._$AR(n && n._$AB.nextSibling, i), e.length = i);
+ }
+ _$AR(t = this._$AA.nextSibling, e) {
+ var n;
+ for ((n = this._$AP) == null ? void 0 : n.call(this, !1, !0, e); t !== this._$AB; ) {
+ const i = at(t).nextSibling;
+ at(t).remove(), t = i;
+ }
+ }
+ setConnected(t) {
+ var e;
+ this._$AM === void 0 && (this._$Cv = t, (e = this._$AP) == null || e.call(this, t));
+ }
+}
+class R {
+ get tagName() {
+ return this.element.tagName;
+ }
+ get _$AU() {
+ return this._$AM._$AU;
+ }
+ constructor(t, e, n, i, r) {
+ this.type = 1, this._$AH = p, this._$AN = void 0, this.element = t, this.name = e, this._$AM = i, this.options = r, n.length > 2 || n[0] !== "" || n[1] !== "" ? (this._$AH = Array(n.length - 1).fill(new String()), this.strings = n) : this._$AH = p;
+ }
+ _$AI(t, e = this, n, i) {
+ const r = this.strings;
+ let s = !1;
+ if (r === void 0) t = T(this, t, e, 0), s = !F(t) || t !== this._$AH && t !== E, s && (this._$AH = t);
+ else {
+ const o = t;
+ let l, c;
+ for (t = r[0], l = 0; l < r.length - 1; l++) c = T(this, o[n + l], e, l), c === E && (c = this._$AH[l]), s || (s = !F(c) || c !== this._$AH[l]), c === p ? t = p : t !== p && (t += (c ?? "") + r[l + 1]), this._$AH[l] = c;
+ }
+ s && !i && this.j(t);
+ }
+ j(t) {
+ t === p ? this.element.removeAttribute(this.name) : this.element.setAttribute(this.name, t ?? "");
+ }
+}
+class Wt extends R {
+ constructor() {
+ super(...arguments), this.type = 3;
+ }
+ j(t) {
+ this.element[this.name] = t === p ? void 0 : t;
+ }
+}
+class qt extends R {
+ constructor() {
+ super(...arguments), this.type = 4;
+ }
+ j(t) {
+ this.element.toggleAttribute(this.name, !!t && t !== p);
+ }
+}
+class Yt extends R {
+ constructor(t, e, n, i, r) {
+ super(t, e, n, i, r), this.type = 5;
+ }
+ _$AI(t, e = this) {
+ if ((t = T(this, t, e, 0) ?? p) === E) return;
+ const n = this._$AH, i = t === p && n !== p || t.capture !== n.capture || t.once !== n.once || t.passive !== n.passive, r = t !== p && (n === p || i);
+ i && this.element.removeEventListener(this.name, this, n), r && this.element.addEventListener(this.name, this, t), this._$AH = t;
+ }
+ handleEvent(t) {
+ var e;
+ typeof this._$AH == "function" ? this._$AH.call(((e = this.options) == null ? void 0 : e.host) ?? this.element, t) : this._$AH.handleEvent(t);
+ }
+}
+class Zt {
+ constructor(t, e, n) {
+ this.element = t, this.type = 6, this._$AN = void 0, this._$AM = e, this.options = n;
+ }
+ get _$AU() {
+ return this._$AM._$AU;
+ }
+ _$AI(t) {
+ T(this, t);
+ }
+}
+const Y = V.litHtmlPolyfillSupport;
+Y == null || Y(U, L), (V.litHtmlVersions ?? (V.litHtmlVersions = [])).push("3.3.3");
+const Jt = (a, t, e) => {
+ const n = (e == null ? void 0 : e.renderBefore) ?? t;
+ let i = n._$litPart$;
+ if (i === void 0) {
+ const r = (e == null ? void 0 : e.renderBefore) ?? null;
+ n._$litPart$ = i = new L(t.insertBefore(z(), r), r, void 0, e ?? {});
+ }
+ return i._$AI(a), i;
+};
+/**
+ * @license
+ * Copyright 2017 Google LLC
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+const y = globalThis;
+class G extends w {
+ constructor() {
+ super(...arguments), this.renderOptions = { host: this }, this._$Do = void 0;
+ }
+ createRenderRoot() {
+ var e;
+ const t = super.createRenderRoot();
+ return (e = this.renderOptions).renderBefore ?? (e.renderBefore = t.firstChild), t;
+ }
+ update(t) {
+ const e = this.render();
+ this.hasUpdated || (this.renderOptions.isConnected = this.isConnected), super.update(t), this._$Do = Jt(e, this.renderRoot, this.renderOptions);
+ }
+ connectedCallback() {
+ var t;
+ super.connectedCallback(), (t = this._$Do) == null || t.setConnected(!0);
+ }
+ disconnectedCallback() {
+ var t;
+ super.disconnectedCallback(), (t = this._$Do) == null || t.setConnected(!1);
+ }
+ render() {
+ return E;
+ }
+}
+var _t;
+G._$litElement$ = !0, G.finalized = !0, (_t = y.litElementHydrateSupport) == null || _t.call(y, { LitElement: G });
+const Z = y.litElementPolyfillSupport;
+Z == null || Z({ LitElement: G });
+(y.litElementVersions ?? (y.litElementVersions = [])).push("4.2.2");
+const mt = [
+ ["more-info", "More info"],
+ ["navigate", "Navigate"],
+ ["url", "URL"],
+ ["call-service", "Call service"],
+ ["assist", "Assist"],
+ ["none", "None"]
+];
+class K extends G {
+ setConfig(t) {
+ this._config = {
+ ...S,
+ ...t
+ }, this._openSections || (this._openSections = {
+ general: !0,
+ preset: !0,
+ grouping: !1,
+ filters: !1,
+ groupTitleOverrides: !1,
+ sorting: !1,
+ appearance: !1,
+ actions: !1
+ });
+ }
+ render() {
+ if (!this._config) return u``;
+ const t = this._config.preset || "spoolman";
+ return u`
+
+ ${this.renderExpandableSection(
+ "general",
+ "General",
+ this.renderGeneralSection(t),
+ !0
+ )}
+
+ ${t === "spoolman" ? this.renderSpoolmanOptions() : ""}
+ ${t === "custom_attributes" ? this.renderCustomAttributeOptions() : ""}
+ ${t === "custom_entities" ? this.renderCustomEntityOptions() : ""}
+ ${t === "custom_label" ? this.renderCustomLabelOptions() : ""}
+
+ ${this.renderExpandableSection(
+ "appearance",
+ "Appearance",
+ this.renderAppearanceOptions(),
+ !0
+ )}
+
+ ${this.renderExpandableSection(
+ "actions",
+ "Actions",
+ this.renderActionOptions(),
+ !1
+ )}
+
+ `;
+ }
+ toggleSection(t) {
+ var e;
+ this._openSections = {
+ ...this._openSections || {},
+ [t]: !((e = this._openSections) != null && e[t])
+ };
+ }
+ renderExpandableSection(t, e, n, i = !1) {
+ var s;
+ const r = ((s = this._openSections) == null ? void 0 : s[t]) ?? i;
+ return u`
+
+
this.toggleSection(t)}
+ >
+ ${e}
+
+
+
+ ${r ? u`
+
+ ${n}
+
+ ` : ""}
+
+ `;
+ }
+ renderGeneralSection(t) {
+ return u`
+ General
+
+ ${this.renderSelect(
+ t,
+ "Choose Preset",
+ [
+ ["spoolman", "Spoolman"],
+ ["custom_attributes", "Custom: Attributes"],
+ ["custom_entities", "Custom: Multiple entities"],
+ ["custom_label", "Custom: HA Label"]
+ ],
+ (e) => this.updatePreset(e)
+ )}
+
+ ${this.renderPresetHint(t)}
+
+ ${this.renderTextField("title", "Title")}
+ `;
+ }
+ renderSpoolmanOptions() {
+ return u`
+ ${this.renderExpandableSection(
+ "grouping",
+ "Grouping",
+ u`
+ ${this.renderSelect(
+ this._config.group_by || "material",
+ "Group by",
+ [
+ ["material", "Material"],
+ ["color", "Color"],
+ ["vendor", "Vendor"],
+ ["none", "Don't group"]
+ ],
+ (t) => this.updateGroupBy(t)
+ )}
+
+ ${this.renderGroupingDetails("Spool count")}
+ ${this.renderGroupFilters()}
+ ${this.renderGroupTitleOverrides()}
+ `,
+ !0
+ )}
+
+ ${this.renderExpandableSection(
+ "sorting",
+ "Sorting",
+ u`
+ ${this.renderSelect(
+ this._config.sort_by || "remaining_weight",
+ "Sort by",
+ [
+ ["remaining_weight", "Remaining weight"],
+ ["filament_name", "Filament name"],
+ ["filament_material", "Material"],
+ ["filament_vendor_name", "Vendor"],
+ ["filament_color_hex", "Color"]
+ ],
+ (t) => this.updateConfigValue("sort_by", t)
+ )}
+
+ ${this.renderSortDirection()}
+
+ ${this.renderNumberField("max_weight", "Max weight fallback")}
+ ${this.renderHint("Used only if no filament weight is available.")}
+
+ ${this.renderSwitch("hide_archived", "Hide archived spools")}
+ ${this.renderSwitch("use_filament_color", "Use filament color")}
+ `,
+ !0
+ )}
+ `;
+ }
+ renderCustomAttributeOptions() {
+ return u`
+ ${this.renderExpandableSection(
+ "preset",
+ "Custom Attributes",
+ u`
+ ${this.renderEntityPicker(
+ this._config.custom_attribute_entities || [],
+ "Entities with attributes",
+ (t) => this.updateConfigValue("custom_attribute_entities", t),
+ !0
+ )}
+
+ ${this.renderHint(
+ "Each selected entity should provide attributes like group, vendor, color, max_value and unit."
+ )}
+
+ ${this.renderCustomDefaults()}
+ `,
+ !0
+ )}
+
+ ${this.renderCustomSharedOptions()}
+ `;
+ }
+ renderCustomEntityOptions() {
+ const t = this._config.custom_items || [];
+ return u`
+ ${this.renderExpandableSection(
+ "preset",
+ "Custom Multiple Entities",
+ u`
+ ${this.renderHint(
+ "Create one item per spool. Each item can use separate entities for value, max value, color, group and vendor."
+ )}
+
+ ${t.map((e, n) => this.renderCustomItem(e, n))}
+
+
+
+ ${this.renderCustomDefaults()}
+ `,
+ !0
+ )}
+
+ ${this.renderCustomSharedOptions()}
+ `;
+ }
+ renderCustomItem(t, e) {
+ return u`
+
+
Spool ${e + 1}
+
+ ${this.renderTextForm(
+ t.name || "",
+ "Name",
+ (n) => this.updateCustomItem(e, "name", n)
+ )}
+
+ ${this.renderEntityPicker(
+ t.value_entity || "",
+ "Value entity",
+ (n) => this.updateCustomItem(e, "value_entity", n)
+ )}
+
+ ${this.renderEntityPicker(
+ t.max_entity || "",
+ "Max entity",
+ (n) => this.updateCustomItem(e, "max_entity", n)
+ )}
+
+ ${this.renderEntityPicker(
+ t.color_entity || "",
+ "Color entity",
+ (n) => this.updateCustomItem(e, "color_entity", n)
+ )}
+
+ ${this.renderEntityPicker(
+ t.group_entity || "",
+ "Group entity",
+ (n) => this.updateCustomItem(e, "group_entity", n)
+ )}
+
+ ${this.renderEntityPicker(
+ t.vendor_entity || "",
+ "Vendor entity",
+ (n) => this.updateCustomItem(e, "vendor_entity", n)
+ )}
+
+ ${this.renderEntityPicker(
+ t.name_entity || "",
+ "Name entity",
+ (n) => this.updateCustomItem(e, "name_entity", n)
+ )}
+
+ ${this.renderTextForm(
+ t.unit || this._config.custom_unit || "g",
+ "Unit",
+ (n) => this.updateCustomItem(e, "unit", n)
+ )}
+
+
+
+ `;
+ }
+ renderCustomLabelOptions() {
+ return u`
+ ${this.renderExpandableSection(
+ "preset",
+ "Custom HA Label",
+ u`
+ ${this.renderHint(
+ "All entities with the selected Home Assistant label will be used automatically. They should provide the same attributes as Custom Attributes."
+ )}
+
+ ${this.renderLabelPicker(
+ this._config.custom_label_id || "",
+ "HA Label",
+ (t) => this.updateConfigValue("custom_label_id", t)
+ )}
+
+ ${this.renderCustomDefaults()}
+ `,
+ !0
+ )}
+
+ ${this.renderCustomSharedOptions()}
+ `;
+ }
+ renderCustomDefaults() {
+ return u`
+ ${this.renderTextForm(
+ this._config.custom_max_value ?? 1e3,
+ "Default max value",
+ (t) => this.updateConfigValue("custom_max_value", Number(t))
+ )}
+
+ ${this.renderHint("Used when an item does not provide its own maximum value.")}
+
+ ${this.renderTextForm(
+ this._config.custom_unit || "g",
+ "Default unit",
+ (t) => this.updateConfigValue("custom_unit", t)
+ )}
+ `;
+ }
+ renderCustomSharedOptions() {
+ return u`
+ ${this.renderExpandableSection(
+ "grouping",
+ "Grouping",
+ u`
+ ${this.renderSelect(
+ this._config.group_by || "material",
+ "Group by",
+ [
+ ["material", "Group"],
+ ["color", "Color"],
+ ["vendor", "Vendor"],
+ ["none", "Don't group"]
+ ],
+ (t) => this.updateGroupBy(t)
+ )}
+
+ ${this.renderGroupingDetails("Item count")}
+ ${this.renderGroupFilters()}
+ ${this.renderGroupTitleOverrides()}
+ `,
+ !0
+ )}
+
+ ${this.renderExpandableSection(
+ "sorting",
+ "Sorting",
+ u`
+ ${this.renderSelect(
+ this._config.sort_by || "remaining_weight",
+ "Sort by",
+ [
+ ["remaining_weight", "Value"],
+ ["filament_name", "Name"],
+ ["filament_material", "Group"],
+ ["filament_vendor_name", "Vendor"],
+ ["filament_color_hex", "Color"]
+ ],
+ (t) => this.updateConfigValue("sort_by", t)
+ )}
+
+ ${this.renderSortDirection()}
+
+ ${this.renderSwitch("use_filament_color", "Use item color")}
+ `,
+ !0
+ )}
+ `;
+ }
+ renderGroupFilters() {
+ var r, s;
+ if (this._config.group_by === "none")
+ return u``;
+ const t = this.getAvailableGroups();
+ if (!t.length)
+ return this.renderExpandableSection(
+ "filters",
+ "Filters",
+ u`
+ ${this.renderHint(
+ "No filter values found yet. Values will appear once matching entities are available."
+ )}
+ `,
+ !1
+ );
+ const e = Array.isArray((r = this._config.filters) == null ? void 0 : r.include) ? this._config.filters.include : [], n = Array.isArray((s = this._config.filters) == null ? void 0 : s.exclude) ? this._config.filters.exclude : [], i = this.getFilterTypeLabel();
+ return this.renderExpandableSection(
+ "filters",
+ "Filters",
+ u`
+ ${this.renderHint(
+ `Filter the card by the currently selected grouping: ${i}.`
+ )}
+
+ ${this.renderMultiSelect(
+ e,
+ `Include only ${i}`,
+ t.map((o) => [o, o]),
+ (o) => this.updateFilterValues("include", o)
+ )}
+
+ ${this.renderHint(
+ "When values are selected here, only matching items are displayed."
+ )}
+
+ ${this.renderMultiSelect(
+ n,
+ `Exclude ${i}`,
+ t.map((o) => [o, o]),
+ (o) => this.updateFilterValues("exclude", o)
+ )}
+
+ ${this.renderHint(
+ "Excluded values are hidden. The visual editor automatically removes them from the include list."
+ )}
+ `,
+ !1
+ );
+ }
+ renderGroupTitleOverrides() {
+ if (this._config.group_by === "none") return u``;
+ const t = this.getAvailableGroups();
+ return t.length ? this.renderExpandableSection(
+ "groupTitleOverrides",
+ "Group Title Overrides",
+ u`
+ ${this.renderHint(
+ "Configure individual icons, colors and actions for specific group titles."
+ )}
+
+ ${t.map((e) => this.renderGroupTitleOverride(e))}
+ `,
+ !1
+ ) : this.renderExpandableSection(
+ "groupTitleOverrides",
+ "Group Title Overrides",
+ u`
+ ${this.renderHint(
+ "No groups found yet. Groups will appear here once matching entities are available."
+ )}
+ `,
+ !1
+ );
+ }
+ getFilterTypeLabel() {
+ return {
+ material: "materials",
+ vendor: "vendors",
+ color: "colors"
+ }[this._config.group_by] || "groups";
+ }
+ getAvailableGroups() {
+ if (!this.hass || !this._config || this._config.group_by === "none")
+ return [];
+ const t = this._config.preset || "spoolman", e = this.getPreviewItemsForGroups(t);
+ let n = [...new Set(
+ e.map((r) => C(this._config, r)).filter(Boolean)
+ )];
+ n = gt(this._config, n, e);
+ const i = Array.isArray(this._config.group_order) ? this._config.group_order : [];
+ return [
+ ...i.filter((r) => n.includes(r)),
+ ...n.filter((r) => !i.includes(r))
+ ];
+ }
+ getPreviewItemsForGroups(t) {
+ return t === "custom_entities" ? this.getCustomEntityPreviewItems() : t === "custom_attributes" || t === "custom_label" ? this.getCustomAttributePreviewItems(t) : this.getSpoolmanPreviewItems();
+ }
+ getSpoolmanPreviewItems() {
+ return Object.entries(this.hass.states).map(([t, e]) => ({ entity_id: t, state: e })).filter(({ state: t }) => {
+ var e;
+ return (e = t.attributes) == null ? void 0 : e.filament_material;
+ });
+ }
+ getCustomAttributePreviewItems(t) {
+ const e = t === "custom_label" ? this.getEntitiesFromSelectedLabel() : this._config.custom_attribute_entities || [], n = Number(this._config.custom_max_value || 1e3), i = this._config.custom_unit || "g";
+ return e.map((r) => {
+ const s = this.hass.states[r];
+ if (!s) return null;
+ const o = s.attributes || {}, l = Number(o.value ?? o.remaining_weight ?? s.state);
+ return Number.isNaN(l) ? null : this.createPreviewItem({
+ entity_id: r,
+ value: l,
+ max: Number(o.max_value ?? o.max ?? n),
+ name: o.name || o.friendly_name || r,
+ group: o.group || o.material || "Custom",
+ vendor: o.vendor || "",
+ color: o.color || o.filament_color_hex,
+ unit: o.unit || i
+ });
+ }).filter(Boolean);
+ }
+ getCustomEntityPreviewItems() {
+ const t = this._config.custom_items || [], e = Number(this._config.custom_max_value || 1e3), n = this._config.custom_unit || "g";
+ return t.map((i, r) => {
+ var m, g, N, O, P, H;
+ const s = this.hass.states[i.value_entity];
+ if (!s) return null;
+ const o = Number(s.state);
+ if (Number.isNaN(o)) return null;
+ const l = i.max_entity ? Number(((m = this.hass.states[i.max_entity]) == null ? void 0 : m.state) ?? e) : Number(i.max ?? i.max_value ?? e), c = i.color_entity ? (g = this.hass.states[i.color_entity]) == null ? void 0 : g.state : i.color, h = i.group_entity ? (N = this.hass.states[i.group_entity]) == null ? void 0 : N.state : i.group, d = i.vendor_entity ? (O = this.hass.states[i.vendor_entity]) == null ? void 0 : O.state : i.vendor, _ = i.name_entity ? (P = this.hass.states[i.name_entity]) == null ? void 0 : P.state : i.name;
+ return this.createPreviewItem({
+ entity_id: i.value_entity || `custom_item_${r}`,
+ value: o,
+ max: Number.isNaN(l) ? e : l,
+ name: _ || ((H = s.attributes) == null ? void 0 : H.friendly_name) || i.value_entity,
+ group: h || "Custom",
+ vendor: d || "",
+ color: c,
+ unit: i.unit || n
+ });
+ }).filter(Boolean);
+ }
+ getEntitiesFromSelectedLabel() {
+ var e;
+ const t = this._config.custom_label_id;
+ return !t || !((e = this.hass) != null && e.entities) ? [] : Object.entries(this.hass.entities).filter(([, n]) => {
+ var i;
+ return (i = n.labels) == null ? void 0 : i.includes(t);
+ }).map(([n]) => n);
+ }
+ createPreviewItem({ entity_id: t, value: e, max: n, name: i, group: r, vendor: s, color: o, unit: l }) {
+ return {
+ entity_id: t,
+ state: {
+ state: String(e),
+ attributes: {
+ friendly_name: i,
+ remaining_weight: e,
+ filament_weight: n,
+ filament_material: r,
+ filament_name: i,
+ filament_vendor_name: s,
+ filament_color_hex: o,
+ archived: !1,
+ custom_unit: l
+ }
+ }
+ };
+ }
+ renderGroupTitleOverride(t) {
+ var i, r;
+ const e = ((i = this._config.group_title_icons) == null ? void 0 : i[t]) || "", n = ((r = this._config.group_title_colors) == null ? void 0 : r[t]) || "";
+ return u`
+
+
${t}
+
+ ${this.renderIconPicker(
+ e,
+ "Group title icon",
+ (s) => this.updateGroupTitleValue("group_title_icons", t, s)
+ )}
+
+ ${this.renderTextForm(
+ n,
+ "Group title color",
+ (s) => this.updateGroupTitleValue("group_title_colors", t, s)
+ )}
+
+ ${this.renderGroupTitleActionFields(t, "tap_action", "Tap action")}
+ ${this.renderGroupTitleActionFields(t, "double_tap_action", "Double tap action")}
+ ${this.renderGroupTitleActionFields(t, "hold_action", "Hold action")}
+
+ `;
+ }
+ renderGroupingDetails(t) {
+ return this._config.group_by === "none" ? u`` : u`
+ ${this.renderHint(this.groupOrderHint())}
+
+
+
+ ${this.renderSelect(
+ this._config.group_sort_by || "name",
+ "Group sort by",
+ [
+ ["name", "Name"],
+ ["total_remaining_weight", "Total remaining weight"],
+ ["max_remaining_weight", "Max remaining weight"],
+ ["spool_count", t]
+ ],
+ (e) => this.updateConfigValue("group_sort_by", e)
+ )}
+
+ ${this.renderSelect(
+ this._config.group_sort_direction || "asc",
+ "Group sort direction",
+ [
+ ["asc", "Ascending"],
+ ["desc", "Descending"]
+ ],
+ (e) => this.updateConfigValue("group_sort_direction", e)
+ )}
+
+ ${this.renderSwitchValue(
+ this._config.group_icon !== "none",
+ "Show group icon",
+ (e) => this.updateGroupIconVisibility(e)
+ )}
+
+ ${this._config.group_icon !== "none" ? this.renderIconPicker(
+ this._config.group_icon || "mdi:printer-3d-nozzle",
+ "Group icon",
+ (e) => this.updateConfigValue("group_icon", e || "none")
+ ) : ""}
+
+ ${this.renderSwitch("show_group_title", "Show group title")}
+ `;
+ }
+ renderGroupTitleActionFields(t, e, n) {
+ var o, l;
+ const r = (((o = this._config.group_title_actions) == null ? void 0 : o[t]) || {})[e] || { action: "none" }, s = r.action || "none";
+ return u`
+ ${this.renderSelect(
+ s,
+ n,
+ mt,
+ (c) => {
+ const h = { action: c };
+ c === "assist" && (h.pipeline_id = "preferred", h.start_listening = !0), this.updateGroupTitleAction(t, e, h);
+ }
+ )}
+
+ ${s === "navigate" ? this.renderTextForm(
+ r.navigation_path || "",
+ "Navigation path",
+ (c) => this.updateGroupTitleAction(t, e, {
+ ...r,
+ action: s,
+ navigation_path: c
+ })
+ ) : ""}
+
+ ${s === "url" ? this.renderTextForm(
+ r.url_path || "",
+ "URL path",
+ (c) => this.updateGroupTitleAction(t, e, {
+ ...r,
+ action: s,
+ url_path: c
+ })
+ ) : ""}
+
+ ${s === "call-service" ? u`
+ Service
+
+ this.updateGroupTitleAction(t, e, {
+ ...r,
+ action: s,
+ service: c.detail.value
+ })}
+ >
+
+ Target entity
+
+ this.updateGroupTitleAction(t, e, {
+ ...r,
+ action: s,
+ target: c.detail.value ? { entity_id: c.detail.value } : void 0
+ })}
+ >
+ ` : ""}
+
+ ${s === "assist" ? u`
+ ${this.renderTextForm(
+ r.pipeline_id || "preferred",
+ "Pipeline ID",
+ (c) => this.updateGroupTitleAction(t, e, {
+ ...r,
+ action: s,
+ pipeline_id: c
+ })
+ )}
+
+ ${this.renderSwitchValue(
+ r.start_listening !== !1,
+ "Start listening",
+ (c) => this.updateGroupTitleAction(t, e, {
+ ...r,
+ action: s,
+ start_listening: c
+ })
+ )}
+ ` : ""}
+ `;
+ }
+ renderSortDirection() {
+ return this.renderSelect(
+ this._config.sort_direction || "asc",
+ "Sort direction",
+ [
+ ["asc", "Ascending"],
+ ["desc", "Descending"]
+ ],
+ (t) => this.updateConfigValue("sort_direction", t)
+ );
+ }
+ renderAppearanceOptions() {
+ return u`
+
+ ${this.renderSelect(
+ this._config.bar_direction || "vertical",
+ "Bar direction",
+ [
+ ["vertical", "Vertical"],
+ ["horizontal", "Horizontal"]
+ ],
+ (t) => {
+ const e = {
+ ...this._config,
+ bar_direction: t
+ };
+ this.normalizePositions(e), this.setAndDispatchConfig(e);
+ }
+ )}
+
+ ${this.renderSelect(
+ this._config.name_position || "bottom",
+ "Name position",
+ this.namePositionOptions(),
+ (t) => this.updateConfigValue("name_position", t)
+ )}
+
+ ${this.renderSelect(
+ this._config.value_position || "center",
+ "Value position",
+ this.valuePositionOptions(),
+ (t) => this.updateConfigValue("value_position", t)
+ )}
+
+ ${this.renderSwitch("show_name", "Show name")}
+
+ `;
+ }
+ renderActionOptions() {
+ return u`
+
+ ${this.renderActionSelect("tap_action", "Tap action", "more-info")}
+ ${this.renderActionSelect("double_tap_action", "Double tap action", "none")}
+ ${this.renderActionSelect("hold_action", "Hold action", "none")}
+ `;
+ }
+ renderActionSelect(t, e, n) {
+ const i = this._config[t] || {}, r = i.action || n;
+ return u`
+ ${this.renderSelect(
+ r,
+ e,
+ mt,
+ (s) => {
+ const o = { action: s };
+ s === "assist" && (o.pipeline_id = "preferred", o.start_listening = !0), this.updateConfigValue(t, o);
+ }
+ )}
+
+ ${this.renderActionExtraFields(t, i, r)}
+ `;
+ }
+ renderActionExtraFields(t, e, n) {
+ var i;
+ return n === "navigate" ? this.renderTextForm(
+ e.navigation_path || "",
+ "Navigation path",
+ (r) => this.updateActionConfigValue(t, "navigation_path", r)
+ ) : n === "url" ? this.renderTextForm(
+ e.url_path || "",
+ "URL path",
+ (r) => this.updateActionConfigValue(t, "url_path", r)
+ ) : n === "call-service" ? u`
+ Service
+
+ this.updateActionConfigValue(t, "service", r.detail.value)}
+ >
+
+ Target entity
+
+ this.updateActionTargetEntity(t, r.detail.value)}
+ >
+ ` : n === "assist" ? u`
+ ${this.renderTextForm(
+ e.pipeline_id || "preferred",
+ "Pipeline ID",
+ (r) => this.updateActionConfigValue(t, "pipeline_id", r)
+ )}
+
+ ${this.renderSwitchValue(
+ e.start_listening !== !1,
+ "Start listening",
+ (r) => this.updateActionConfigValue(t, "start_listening", r)
+ )}
+ ` : u``;
+ }
+ updateGroupTitleValue(t, e, n) {
+ const i = { ...this._config }, r = { ...i[t] || {} };
+ n ? r[e] = n : delete r[e], i[t] = r, this.setAndDispatchConfig(i);
+ }
+ updateGroupTitleAction(t, e, n) {
+ const i = { ...this._config }, r = { ...i.group_title_actions || {} }, s = { ...r[t] || {} };
+ !n || n.action === "none" ? delete s[e] : s[e] = n, Object.keys(s).length ? r[t] = s : delete r[t], i.group_title_actions = r, this.setAndDispatchConfig(i);
+ }
+ updateActionConfigValue(t, e, n) {
+ const i = { ...this._config }, r = { ...i[t] || {} }, s = typeof n == "string" ? n.trim() : n;
+ s ? r[e] = s : delete r[e], i[t] = r, this.setAndDispatchConfig(i);
+ }
+ updateActionTargetEntity(t, e) {
+ const n = { ...this._config }, i = { ...n[t] || {} };
+ e ? i.target = {
+ ...i.target || {},
+ entity_id: e
+ } : delete i.target, n[t] = i, this.setAndDispatchConfig(n);
+ }
+ renderPresetHint(t) {
+ const e = {
+ spoolman: "Automatically discovers Spoolman filament entities.",
+ custom_attributes: "Use entities that provide all display data through attributes.",
+ custom_entities: "Build each spool from multiple Home Assistant entities.",
+ custom_label: "Automatically use entities assigned to a Home Assistant label."
+ };
+ return this.renderHint(e[t] || "");
+ }
+ renderHint(t) {
+ return t ? u`
+ ${t}
+ ` : u``;
+ }
+ renderTextField(t, e) {
+ return u`
+ this.updateConfigValue(t, n.target.value)}
+ >
+ `;
+ }
+ renderNumberField(t, e) {
+ return u`
+ this.updateConfigValue(t, Number(n.target.value))}
+ >
+ `;
+ }
+ renderTextForm(t, e, n) {
+ const i = [
+ {
+ name: "value",
+ selector: {
+ text: {}
+ }
+ }
+ ];
+ return u`
+ e}
+ @value-changed=${(r) => {
+ var s;
+ n(((s = r.detail.value) == null ? void 0 : s.value) ?? "");
+ }}
+ >
+ `;
+ }
+ renderSelect(t, e, n, i) {
+ const r = [
+ {
+ name: "value",
+ selector: {
+ select: {
+ mode: "dropdown",
+ options: n.map(([s, o]) => ({
+ value: s,
+ label: o
+ }))
+ }
+ }
+ }
+ ];
+ return u`
+ e}
+ @value-changed=${(s) => {
+ var l;
+ const o = (l = s.detail.value) == null ? void 0 : l.value;
+ o !== void 0 && o !== t && i(o);
+ }}
+ >
+ `;
+ }
+ renderMultiSelect(t, e, n, i) {
+ const r = Array.isArray(t) ? t : [], s = [
+ {
+ name: "value",
+ selector: {
+ select: {
+ multiple: !0,
+ mode: "dropdown",
+ options: n.map(([o, l]) => ({
+ value: o,
+ label: l
+ }))
+ }
+ }
+ }
+ ];
+ return u`
+ e}
+ @value-changed=${(o) => {
+ var c;
+ const l = (c = o.detail.value) == null ? void 0 : c.value;
+ i(Array.isArray(l) ? l : []);
+ }}
+ >
+ `;
+ }
+ renderEntityPicker(t, e, n, i = !1) {
+ const r = [
+ {
+ name: "value",
+ selector: {
+ entity: {
+ multiple: i
+ }
+ }
+ }
+ ];
+ return u`
+ e}
+ @value-changed=${(s) => {
+ var o;
+ n(((o = s.detail.value) == null ? void 0 : o.value) || (i ? [] : ""));
+ }}
+ >
+ `;
+ }
+ renderLabelPicker(t, e, n) {
+ const i = [
+ {
+ name: "value",
+ selector: {
+ label: {}
+ }
+ }
+ ];
+ return u`
+ e}
+ @value-changed=${(r) => {
+ var s;
+ n(((s = r.detail.value) == null ? void 0 : s.value) || "");
+ }}
+ >
+ `;
+ }
+ renderIconPicker(t, e, n) {
+ const i = [
+ {
+ name: "value",
+ selector: {
+ icon: {}
+ }
+ }
+ ];
+ return u`
+ e}
+ @value-changed=${(r) => {
+ var s;
+ n(((s = r.detail.value) == null ? void 0 : s.value) || "");
+ }}
+ >
+ `;
+ }
+ renderSwitch(t, e, n = null) {
+ const i = this._config[t] !== !1;
+ return u`
+
+ {
+ n ? n(r.target.checked) : this.updateConfigValue(t, r.target.checked);
+ }}
+ >
+ ${e}
+
+ `;
+ }
+ renderSwitchValue(t, e, n) {
+ return u`
+
+ n(i.target.checked)}
+ >
+ ${e}
+
+ `;
+ }
+ updatePreset(t) {
+ const e = {
+ ...this._config,
+ preset: t
+ };
+ t === "spoolman" && (delete e.custom_attribute_entities, delete e.custom_items, delete e.custom_label_id, delete e.custom_max_value, delete e.custom_unit), t === "custom_attributes" && (delete e.custom_items, delete e.custom_label_id), t === "custom_entities" && (delete e.custom_attribute_entities, delete e.custom_label_id), t === "custom_label" && (delete e.custom_attribute_entities, delete e.custom_items), this.setAndDispatchConfig(e);
+ }
+ updateConfigValue(t, e) {
+ const n = {
+ ...this._config,
+ [t]: e
+ };
+ this.setAndDispatchConfig(n);
+ }
+ updateCustomItem(t, e, n) {
+ const i = [...this._config.custom_items || []];
+ i[t] = {
+ ...i[t],
+ [e]: n
+ }, this.updateConfigValue("custom_items", i);
+ }
+ updateGroupIconVisibility(t) {
+ const e = {
+ ...this._config,
+ group_icon: t ? "mdi:printer-3d-nozzle" : "none"
+ };
+ this.setAndDispatchConfig(e);
+ }
+ updateGroupBy(t) {
+ const e = {
+ ...this._config,
+ group_by: t
+ };
+ delete e.filters, this.setAndDispatchConfig(e);
+ }
+ updateFilterValues(t, e) {
+ var l, c;
+ const n = Array.isArray(e) ? [...new Set(e.filter(Boolean))] : [], i = t === "include" ? "exclude" : "include", r = {
+ ...this._config.filters || {},
+ [t]: n
+ }, s = Array.isArray(r[i]) ? r[i] : [];
+ r[i] = s.filter(
+ (h) => !n.includes(h)
+ ), (l = r.include) != null && l.length || delete r.include, (c = r.exclude) != null && c.length || delete r.exclude;
+ const o = {
+ ...this._config
+ };
+ Object.keys(r).length ? o.filters = r : delete o.filters, this.setAndDispatchConfig(o);
+ }
+ addCustomItem() {
+ const t = [
+ ...this._config.custom_items || [],
+ {
+ name: "",
+ value_entity: "",
+ max_entity: "",
+ color_entity: "",
+ group_entity: "",
+ vendor_entity: "",
+ name_entity: "",
+ unit: this._config.custom_unit || "g"
+ }
+ ];
+ this.updateConfigValue("custom_items", t);
+ }
+ removeCustomItem(t) {
+ const e = [...this._config.custom_items || []];
+ e.splice(t, 1), this.updateConfigValue("custom_items", e);
+ }
+ handleGroupOrderChanged(t) {
+ const e = t.target.value.split(`
+`).map((n) => n.trim()).filter(Boolean);
+ this.setAndDispatchConfig({
+ ...this._config,
+ group_order: e
+ });
+ }
+ groupOrderValue() {
+ return Array.isArray(this._config.group_order) ? this._config.group_order.join(`
+`) : "";
+ }
+ groupOrderHint() {
+ return this._config.group_by === "color" ? "One color per line. Unknown colors are appended automatically." : this._config.group_by === "vendor" ? "One vendor per line. Unknown vendors are appended automatically." : "One group per line. Unknown groups are appended automatically.";
+ }
+ namePositionOptions() {
+ return this._config.bar_direction === "horizontal" ? [
+ ["top", "Top"],
+ ["bottom", "Bottom"],
+ ["left", "Left"],
+ ["right", "Right"]
+ ] : [
+ ["top", "Top"],
+ ["bottom", "Bottom"]
+ ];
+ }
+ valuePositionOptions() {
+ return this._config.bar_direction === "horizontal" ? [
+ ["left", "Left"],
+ ["center", "Center"],
+ ["right", "Right"]
+ ] : [
+ ["top", "Top"],
+ ["center", "Center"],
+ ["bottom", "Bottom"]
+ ];
+ }
+ normalizePositions(t) {
+ t.bar_direction === "vertical" && (["top", "bottom"].includes(t.name_position) || (t.name_position = "bottom"), ["top", "center", "bottom"].includes(t.value_position) || (t.value_position = "center")), t.bar_direction === "horizontal" && (["top", "bottom", "left", "right"].includes(t.name_position) || (t.name_position = "bottom"), ["left", "center", "right"].includes(t.value_position) || (t.value_position = "center"));
+ }
+ setAndDispatchConfig(t) {
+ this._config = t, this.dispatchEvent(
+ new CustomEvent("config-changed", {
+ detail: { config: t },
+ bubbles: !0,
+ composed: !0
+ })
+ );
+ }
+}
+j(K, "properties", {
+ hass: { attribute: !1 },
+ _config: { state: !0 },
+ _openSections: { state: !0 }
+}), j(K, "styles", Ht`
+ .editor {
+ display: grid;
+ gap: 16px;
+ padding: 16px;
+ }
+
+ .section-title {
+ margin-top: 12px;
+ font-size: 16px;
+ font-weight: 500;
+ }
+
+ .section-title:first-child {
+ margin-top: 0;
+ }
+
+ .hint {
+ color: var(--secondary-text-color);
+ font-size: 12px;
+ line-height: 1.4;
+ margin-top: -6px;
+ }
+
+ ha-textfield,
+ ha-select {
+ width: 100%;
+ }
+
+ textarea {
+ width: 100%;
+ min-height: 80px;
+ box-sizing: border-box;
+ padding: 10px;
+ border-radius: 4px;
+ border: 1px solid var(--divider-color);
+ background: var(--card-background-color);
+ color: var(--primary-text-color);
+ font: inherit;
+ resize: vertical;
+ }
+
+ .switch-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ }
+
+ .switch-label {
+ font-size: 14px;
+ }
+
+ .custom-item {
+ display: grid;
+ gap: 12px;
+ padding: 12px;
+ border: 1px solid var(--divider-color);
+ border-radius: 8px;
+ }
+
+ .button-row {
+ display: flex;
+ gap: 8px;
+ }
+
+ .editor-button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ width: fit-content;
+ padding: 8px 14px;
+ border: none;
+ border-radius: 4px;
+ background: var(--primary-color);
+ color: var(--text-primary-color);
+ font: inherit;
+ font-weight: 500;
+ cursor: pointer;
+ user-select: none;
+ }
+
+ .editor-button:hover {
+ filter: brightness(1.08);
+ }
+
+ .editor-button.secondary {
+ background: var(--secondary-background-color);
+ color: var(--primary-text-color);
+ border: 1px solid var(--divider-color);
+ }
+
+ .editor-button.danger {
+ background: var(--error-color);
+ color: var(--text-primary-color);
+ }
+
+ .expandable-section {
+ border: 1px solid var(--divider-color);
+ border-radius: 8px;
+ overflow: hidden;
+ }
+
+ .expandable-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ padding: 12px;
+ cursor: pointer;
+ user-select: none;
+ font-weight: 500;
+ background: var(--secondary-background-color);
+ }
+
+ .expandable-header:hover {
+ filter: brightness(1.03);
+ }
+
+ .expandable-content {
+ display: grid;
+ gap: 12px;
+ padding: 12px;
+ }
+
+ `);
+customElements.get(Q) || customElements.define(Q, K);
+window.customCards = window.customCards || [];
+window.customCards.push({
+ type: J,
+ name: "Filament Card",
+ description: "Shows filament stock grouped by material, color, or vendor."
+});
diff --git a/zigbee2mqtt/state.json b/zigbee2mqtt/state.json
index adf83a4e..b6c1c1cc 100644
--- a/zigbee2mqtt/state.json
+++ b/zigbee2mqtt/state.json
@@ -4,7 +4,7 @@
"state": "ON",
"led_brightness": 100,
"countdown_to_turn_off": 0,
- "voltage": 120,
+ "voltage": 120.2,
"countdown_to_turn_on": 0,
"ac_frequency": 60,
"power_factor": 0.14,
@@ -27,7 +27,7 @@
"led_brightness": 100,
"countdown_to_turn_off": 0,
"countdown_to_turn_on": 0,
- "power": 2,
+ "power": 1.9,
"current": 0.09,
"energy": 54.08,
"power_factor": 0.2,
@@ -45,7 +45,7 @@
"state": "ON",
"led_brightness": 100,
"countdown_to_turn_off": 0,
- "voltage": 119.6,
+ "voltage": 119.8,
"countdown_to_turn_on": 0,
"energy": 103.94,
"power_factor": 0.2,
@@ -74,13 +74,13 @@
"led_brightness": 100,
"countdown_to_turn_off": 0,
"countdown_to_turn_on": 0,
- "voltage": 118.5,
+ "voltage": 118.2,
"state": "ON",
"ac_frequency": 60,
- "energy": 211.16,
- "power": 0.8,
- "current": 0.87,
- "power_factor": 0.75,
+ "energy": 211.17,
+ "power": 109.4,
+ "current": 0.92,
+ "power_factor": 0.94,
"update": {
"state": "idle",
"installed_version": 268513381,
@@ -95,10 +95,10 @@
"led_brightness": 100,
"countdown_to_turn_off": 0,
"countdown_to_turn_on": 0,
- "voltage": 120,
+ "voltage": 120.2,
"energy": 100.79,
"state": "ON",
- "power": 10.2,
+ "power": 12.7,
"current": 0.18,
"ac_frequency": 60,
"power_factor": 0.51,
@@ -119,7 +119,7 @@
"energy": 14.23,
"current": 0.01,
"power": 0.1,
- "power_factor": 0.16,
+ "power_factor": 0.11,
"linkquality": 116,
"update": {
"state": "idle",
@@ -136,13 +136,13 @@
"0xffffb40e0608864e": {
"led_brightness": 100,
"countdown_to_turn_off": 0,
- "voltage": 120.3,
+ "voltage": 120.6,
"energy": 34.53,
"countdown_to_turn_on": 0,
"state": "ON",
"current": 0.02,
"ac_frequency": 60,
- "power": 0.3,
+ "power": 0.4,
"power_factor": 0.14,
"update": {
"state": "idle",