From 4b7da24066bd2f3014a47dce090d7c25c9f0ee7e Mon Sep 17 00:00:00 2001 From: xiaoshi <115949669+xiaoshi930@users.noreply.github.com> Date: Sat, 29 Nov 2025 23:55:48 +0800 Subject: [PATCH] Update xiaoshi-device-consumables-button.js --- xiaoshi-device-consumables-button.js | 248 ++++++++++++++++++++++++--- 1 file changed, 224 insertions(+), 24 deletions(-) diff --git a/xiaoshi-device-consumables-button.js b/xiaoshi-device-consumables-button.js index a91db8e..e83ef27 100644 --- a/xiaoshi-device-consumables-button.js +++ b/xiaoshi-device-consumables-button.js @@ -291,9 +291,17 @@ class XiaoshiConsumablesButtonEditor extends LitElement {
- - - + +
@@ -301,11 +309,19 @@ class XiaoshiConsumablesButtonEditor extends LitElement { type="checkbox" class="checkbox-input" @change=${this._entityChanged} - .checked=${this.config.show_preview !== false} - name="show_preview" - id="show_preview" + .checked=${this.config.no_preview === true} + name="no_preview" + id="no_preview" /> - + +
+ +
+ + +
@@ -532,17 +548,15 @@ class XiaoshiConsumablesButtonEditor extends LitElement {
搜索并选择要显示的设备耗材实体,支持多选。每个实体可以配置:
- • 特殊实体显示:binary_sensor(off→正常,on→缺少), event(unknown→正常,其他→低电量)
• 属性名:留空使用实体状态,或输入属性名
• 名称重定义:勾选后可自定义显示名称
• 图标重定义:勾选后可自定义图标(如 mdi:phone)
• 单位重定义:勾选后可自定义单位(如 元、$、kWh 等)
• 预警条件:勾选后设置预警条件,支持 >10, >=10, <10, <=10, ==10, ==on, ==off, =="hello world" 等
• 换算:对数值进行数学运算,支持 +10, -10, *1.5, /2 等
- • 未勾选重定义时,将使用实体的原始属性值 +
- `; } @@ -553,7 +567,6 @@ class XiaoshiConsumablesButtonEditor extends LitElement { const { name, value, type, checked } = e.target; let finalValue; - // 处理复选框 if (type === 'checkbox') { finalValue = checked; @@ -769,10 +782,12 @@ class XiaoshiConsumablesButtonEditor extends LitElement { this._filteredEntities = []; this._showEntityList = false; } - + + /*button新按钮方法 开始*/ setConfig(config) { - this.config = config; + this.config = config || {}; } + /*button新按钮方法 结束*/ } customElements.define('xiaoshi-consumables-button-editor', XiaoshiConsumablesButtonEditor); @@ -1290,19 +1305,52 @@ class XiaoshiConsumablesButton extends LitElement { const tapAction = this.config.tap_action; if (!tapAction || tapAction !== 'none') { - // 默认 tap_action 行为:弹出耗材卡片 + // 默认 tap_action 行为:弹出垂直堆叠卡片 const excludedParams = ['type', 'button_height', 'button_width', 'button_font_size', 'button_icon_size', 'show_preview', 'tap_action']; - const cardConfig = {}; + // 构建垂直堆叠卡片的内容 + const cards = []; + + // 1. 添加耗材卡片 + const consumablesCardConfig = {}; Object.keys(this.config).forEach(key => { - if (!excludedParams.includes(key)) { - cardConfig[key] = this.config[key]; + if (!excludedParams.includes(key) && key !== 'other_cards' && key !== 'no_preview') { + consumablesCardConfig[key] = this.config[key]; } }); - - const popupContent = { + + cards.push({ type: 'custom:xiaoshi-consumables-card', - ...cardConfig + ...consumablesCardConfig + }); + + // 2. 添加附加卡片 + if (this.config.other_cards && this.config.other_cards.trim()) { + try { + const additionalCardsConfig = this._parseYamlCards(this.config.other_cards); + + // 为每个附加卡片传递 theme 值 + const cardsWithTheme = additionalCardsConfig.map(card => { + // 如果卡片没有 theme 配置,则从当前卡片配置中传递 + if (!card.theme && this.config.theme) { + return { + ...card, + theme: this.config.theme + }; + } + return card; + }); + + cards.push(...cardsWithTheme); + } catch (error) { + console.error('解析附加卡片配置失败:', error); + } + } + + // 创建垂直堆叠卡片 + const popupContent = { + type: 'vertical-stack', + cards: cards }; const popupStyle = this.config.popup_style || ` @@ -1320,6 +1368,156 @@ class XiaoshiConsumablesButton extends LitElement { } this._handleClick(); } + + _parseYamlCards(yamlString) { + try { + const lines = yamlString.split('\n'); + const cards = []; + let currentCard = null; + let indentStack = []; + let contextStack = []; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const trimmed = line.trim(); + + if (!trimmed || trimmed.startsWith('#')) continue; + + const indentLevel = line.length - line.trimStart().length; + if (trimmed.startsWith('- type')) { + if (currentCard) { + cards.push(currentCard); + currentCard = null; + indentStack = []; + contextStack = []; + } + const content = trimmed.substring(1).trim(); + if (content.includes(':')) { + const [key, ...valueParts] = content.split(':'); + const value = valueParts.join(':').trim(); + currentCard = {}; + this._setNestedValue(currentCard, key.trim(), this._parseValue(value)); + } else { + currentCard = { type: content }; + } + + indentStack = [indentLevel]; + contextStack = [currentCard]; + } else if (currentCard && trimmed.startsWith('-')) { + while (indentStack.length > 1 && indentLevel <= indentStack[indentStack.length - 1]) { + indentStack.pop(); + contextStack.pop(); + } + + let currentContext = contextStack[contextStack.length - 1]; + const itemValue = trimmed.substring(1).trim(); + + if (!Array.isArray(currentContext)) { + if (contextStack.length > 1) { + const parentContext = contextStack[contextStack.length - 2]; + for (let key in parentContext) { + if (parentContext[key] === currentContext) { + parentContext[key] = []; + contextStack[contextStack.length - 1] = parentContext[key]; + currentContext = parentContext[key]; + break; + } + } + } + } + if (Array.isArray(currentContext)) { + if (itemValue.includes(':')) { + const [key, ...valueParts] = itemValue.split(':'); + const value = valueParts.join(':').trim(); + const obj = {}; + obj[key.trim()] = this._parseValue(value); + currentContext.push(obj); + } else { + currentContext.push(this._parseValue(itemValue)); + } + } + } else if (currentCard && trimmed.includes(':')) { + const [key, ...valueParts] = trimmed.split(':'); + const value = valueParts.join(':').trim(); + const keyName = key.trim(); + + while (indentStack.length > 1 && indentLevel <= indentStack[indentStack.length - 1]) { + indentStack.pop(); + contextStack.pop(); + } + + const currentContext = contextStack[contextStack.length - 1]; + + if (value) { + this._setNestedValue(currentContext, keyName, this._parseValue(value)); + } else { + let nextLine = null, nextIndent = null; + for (let j = i + 1; j < lines.length; j++) { + const nextTrimmed = lines[j].trim(); + if (nextTrimmed && !nextTrimmed.startsWith('#')) { + nextLine = nextTrimmed; + nextIndent = lines[j].length - lines[j].trimStart().length; + break; + } + } + + currentContext[keyName] = (nextLine && nextLine.startsWith('-') && nextIndent > indentLevel) + ? [] : (currentContext[keyName] || {}); + + indentStack.push(indentLevel); + contextStack.push(currentContext[keyName]); + } + } + } + + if (currentCard) cards.push(currentCard); + + return cards; + } catch (error) { + console.error('YAML解析错误:', error); + return []; + } + } + + _parseValue(value) { + if (!value) return ''; + + // 移除引号 + if ((value.startsWith('"') && value.endsWith('"')) || + (value.startsWith("'") && value.endsWith("'"))) { + return value.slice(1, -1); + } + + // 尝试解析为数字 + if (!isNaN(value) && value.trim() !== '') { + return Number(value); + } + + // 尝试解析为布尔值 + if (value === 'true') return true; + if (value === 'false') return false; + if (value === 'null') return null; + + // 返回字符串 + return value; + } + + _setNestedValue(obj, path, value) { + // 支持嵌套路径,如 "styles.card" + const keys = path.split('.'); + let current = obj; + + for (let i = 0; i < keys.length - 1; i++) { + const key = keys[i]; + if (!current[key] || typeof current[key] !== 'object') { + current[key] = {}; + } + current = current[key]; + } + + current[keys[keys.length - 1]] = value; + } + /*button新元素 结束*/ _renderDeviceItem(consumablesData) { @@ -1448,7 +1646,6 @@ class XiaoshiConsumablesButton extends LitElement { return false; } - render() { if (!this.hass) { return html`
等待Home Assistant连接...
`; @@ -1483,7 +1680,7 @@ class XiaoshiConsumablesButton extends LitElement { }).length; /*button新元素 前9行和最后1行开始*/ - const showPreview = this.config.show_preview !== false; + const showPreview = this.config.no_preview === true; return html`
@@ -1528,9 +1725,11 @@ class XiaoshiConsumablesButton extends LitElement { } setConfig(config) { - this.config = config; - /*button新元素 开始*/ + // 不设置默认值,只有明确配置时才添加 no_preview + this.config = { + ...config + }; if (config.button_width) { this.style.setProperty('--button-width', config.button_width); } else { @@ -1578,3 +1777,4 @@ class XiaoshiConsumablesButton extends LitElement { } } customElements.define('xiaoshi-consumables-button', XiaoshiConsumablesButton); +