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() { const L = this.config.spool_location; 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 }) => !L || String((t.attributes || {}).location || "").toLowerCase() === String(L).toLowerCase()).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 ? "" : "", s = I; for (let o = 0; o < e; o++) { const l = a[o]; let c, h, d = -1, _ = 0; for (; _ < l.length && (s.lastIndex = _, h = s.exec(l), h !== null); ) _ = s.lastIndex, s === I ? h[1] === "!--" ? s = ct : h[1] !== void 0 ? s = ut : h[2] !== void 0 ? (yt.test(h[2]) && (i = RegExp("" ? (s = i ?? I, d = -1) : h[1] === void 0 ? d = -2 : (d = s.lastIndex - h[2].length, c = h[1], s = h[3] === void 0 ? b : h[3] === '"' ? dt : ht) : s === dt || s === ht ? s = b : s === ct || s === ut ? s = I : (s = b, i = void 0); const m = s === b && a[o + 1].startsWith("/>") ? " " : ""; r += s === I ? l + Mt : d >= 0 ? (n.push(c), l.slice(0, d) + bt + l.slice(d) + f + m) : l + f + (d === -2 ? o : m); } return [xt(a, r + (a[e] || "") + (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." });