fix: 修复 Lovelace 卡片和过时常量警告
修复 1: Lovelace 卡片配置错误 - 重写 sigmesh-gateway-panel.js 遵循 HA 卡片规范 - 添加 setConfig 方法 - 添加 getCardSize 方法 - 添加静态方法 (getStubConfig, getConfigElement) - 使用 ha-card 包装器和 shadow DOM - 修复 hass 设置和渲染逻辑 修复 2: 过时常量警告 - light.py: 将 ATTR_COLOR_TEMP 替换为 ATTR_COLOR_TEMP_KELVIN - 修复 HA Core 2026.1 兼容性警告 部署步骤: 1. cp custom_components/sigmesh_gateway/sigmesh-gateway-panel.js ~/.homeassistant/www/community/sigmesh_gateway/ 2. cp -r custom_components/sigmesh_gateway ~/.homeassistant/custom_components/ 3. ha core restart
This commit is contained in:
parent
04e942992b
commit
e140dd81c4
@ -8,7 +8,7 @@ from typing import Any
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.light import (
|
||||
ATTR_BRIGHTNESS,
|
||||
ATTR_COLOR_TEMP,
|
||||
ATTR_COLOR_TEMP_KELVIN,
|
||||
ATTR_RGB_COLOR,
|
||||
ColorMode,
|
||||
LightEntity,
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/**
|
||||
* SigMesh Gateway 配网控制面板
|
||||
*
|
||||
* 这是一个自定义 Lovelace 卡片,用于管理 SigMesh 网关的配网和分组功能
|
||||
* SigMesh Gateway 配网控制面板 - Lovelace 自定义卡片
|
||||
*
|
||||
* 使用方法:
|
||||
* 1. 将此文件保存到 HA 的 www/community/sigmesh_gateway/ 目录
|
||||
* 1. 将此文件复制到 HA 的 www/community/sigmesh_gateway/ 目录
|
||||
* 2. 在 HA 的 configuration.yaml 中添加:
|
||||
* frontend:
|
||||
* extra_module_url:
|
||||
@ -14,15 +12,32 @@
|
||||
|
||||
// 配置常量
|
||||
const API_BASE = '/api/sigmesh_gateway';
|
||||
const POLL_INTERVAL = 3000; // 3 秒轮询一次
|
||||
const POLL_INTERVAL = 3000;
|
||||
|
||||
/**
|
||||
* 主面板组件
|
||||
* Lovelace 卡片组件
|
||||
*/
|
||||
class SigMeshGatewayPanel extends HTMLElement {
|
||||
class SigMeshGatewayCard extends HTMLElement {
|
||||
static get placeholder() {
|
||||
return {
|
||||
type: 'custom:sigmesh-gateway-panel',
|
||||
label: 'SigMesh Gateway 配网管理'
|
||||
};
|
||||
}
|
||||
|
||||
static getConfigElement() {
|
||||
// 返回配置元素(如果需要配置界面)
|
||||
return document.createElement('div');
|
||||
}
|
||||
|
||||
static getStubConfig() {
|
||||
return {};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._hass = null;
|
||||
this._config = null;
|
||||
this._state = 'idle';
|
||||
this._devices = [];
|
||||
this._groups = [];
|
||||
@ -33,6 +48,19 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
|
||||
set hass(hass) {
|
||||
this._hass = hass;
|
||||
if (!this.hasAttribute('rendered')) {
|
||||
this._render();
|
||||
this.setAttribute('rendered', 'true');
|
||||
}
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
setConfig(config) {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
getCardSize() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@ -56,6 +84,8 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
}
|
||||
|
||||
async _fetchStatus() {
|
||||
if (!this._hass) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_BASE}/status`, {
|
||||
headers: {
|
||||
@ -67,21 +97,52 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
this._state = data.state;
|
||||
this._devices = data.devices || [];
|
||||
this._groups = data.groups || [];
|
||||
this._render();
|
||||
this._updateState();
|
||||
} catch (error) {
|
||||
console.error('获取状态失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
_updateState() {
|
||||
if (!this.shadowRoot) return;
|
||||
|
||||
// 更新状态徽章
|
||||
const statusBadge = this.shadowRoot.querySelector('.status-badge');
|
||||
if (statusBadge) {
|
||||
statusBadge.textContent = this._state;
|
||||
statusBadge.style.background = this._getStateColor();
|
||||
}
|
||||
|
||||
// 更新设备列表
|
||||
this._renderDeviceList();
|
||||
|
||||
// 更新组列表
|
||||
this._renderGroupList();
|
||||
}
|
||||
|
||||
_getStateColor() {
|
||||
const colors = {
|
||||
idle: '#4caf50',
|
||||
scanning: '#2196f3',
|
||||
prov_starting: '#ff9800',
|
||||
prov_in_progress: '#ff9800',
|
||||
prov_completed: '#4caf50',
|
||||
prov_failed: '#f44336',
|
||||
timeout: '#f44336',
|
||||
};
|
||||
return colors[this._state] || '#757575';
|
||||
}
|
||||
|
||||
_render() {
|
||||
this.innerHTML = `
|
||||
this.attachShadow({ mode: 'open' });
|
||||
|
||||
this.shadowRoot.innerHTML = `
|
||||
<style>
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
ha-card {
|
||||
padding: 16px;
|
||||
background: var(--card-background-color);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--ha-card-box-shadow);
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
@ -158,21 +219,18 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
transition: opacity 0.2s;
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
.button:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
.button-primary {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
.button-secondary {
|
||||
background: var(--secondary-background-color);
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
.button-danger {
|
||||
background: var(--error-color);
|
||||
color: white;
|
||||
background: var(--error-color, #f44336);
|
||||
}
|
||||
.button:disabled {
|
||||
opacity: 0.5;
|
||||
@ -210,58 +268,36 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
padding: 24px;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
.log-container {
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
background: #1e1e1e;
|
||||
border-radius: 4px;
|
||||
padding: 8px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
ha-svg-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.log-entry {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.log-info { color: #4caf50; }
|
||||
.log-warning { color: #ff9800; }
|
||||
.log-error { color: #f44336; }
|
||||
</style>
|
||||
|
||||
<div class="header">
|
||||
<h2>SigMesh Gateway 配网管理</h2>
|
||||
<span class="status-badge">${this._state}</span>
|
||||
</div>
|
||||
<ha-card>
|
||||
<div class="header">
|
||||
<h2>SigMesh Gateway 配网管理</h2>
|
||||
<span class="status-badge">${this._state}</span>
|
||||
</div>
|
||||
|
||||
${this._renderScanSection()}
|
||||
${this._renderDeviceList()}
|
||||
${this._renderProvActions()}
|
||||
${this._renderGroupManagement()}
|
||||
${this._renderGroupList()}
|
||||
${this._renderScanSection()}
|
||||
${this._renderDeviceList()}
|
||||
${this._renderProvActions()}
|
||||
${this._renderGroupManagement()}
|
||||
${this._renderGroupList()}
|
||||
</ha-card>
|
||||
`;
|
||||
|
||||
this._attachEventListeners();
|
||||
}
|
||||
|
||||
_getStateColor() {
|
||||
const colors = {
|
||||
idle: '#4caf50',
|
||||
scanning: '#2196f3',
|
||||
prov_starting: '#ff9800',
|
||||
prov_in_progress: '#ff9800',
|
||||
prov_completed: '#4caf50',
|
||||
prov_failed: '#f44336',
|
||||
timeout: '#f44336',
|
||||
};
|
||||
return colors[this._state] || '#757575';
|
||||
}
|
||||
|
||||
_renderScanSection() {
|
||||
const isScanning = this._state === 'scanning';
|
||||
return `
|
||||
<div class="section">
|
||||
<div class="section-title">设备扫描</div>
|
||||
<div class="button-group">
|
||||
<button class="button button-primary" id="btn-scan" ${isScanning ? 'disabled' : ''}>
|
||||
<button class="button" id="btn-scan" ${isScanning ? 'disabled' : ''}>
|
||||
${isScanning ? '扫描中...' : '开始扫描'}
|
||||
</button>
|
||||
<button class="button button-secondary" id="btn-refresh">
|
||||
@ -318,7 +354,7 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
value="${device?.mac || ''}" placeholder="设备地址" readonly>
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<button class="button button-primary" id="btn-prov-start" ${isProvInProgress ? 'disabled' : ''}>
|
||||
<button class="button" id="btn-prov-start" ${isProvInProgress ? 'disabled' : ''}>
|
||||
开始配网
|
||||
</button>
|
||||
<button class="button button-danger" id="btn-prov-stop" ${!isProvInProgress ? 'disabled' : ''}>
|
||||
@ -350,7 +386,7 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
value="4352" placeholder="Model ID" min="0" max="65535">
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<button class="button button-primary" id="btn-add-group">
|
||||
<button class="button" id="btn-add-group">
|
||||
添加到组
|
||||
</button>
|
||||
<button class="button button-danger" id="btn-remove-group">
|
||||
@ -387,13 +423,56 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
`;
|
||||
}
|
||||
|
||||
_renderDeviceList() {
|
||||
const deviceListEl = this.shadowRoot.querySelector('.device-list');
|
||||
if (!deviceListEl) return;
|
||||
|
||||
deviceListEl.innerHTML = this._devices.map((device, index) => `
|
||||
<div class="device-item ${this._selectedDevice === index ? 'selected' : ''}" data-index="${index}">
|
||||
<div class="device-info">
|
||||
<div class="device-name">设备 ${device.mac}</div>
|
||||
<div class="device-details">
|
||||
元素数:${device.elements} |
|
||||
地址:${device.address || '未分配'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
_renderGroupList() {
|
||||
const groupListEl = this.shadowRoot.querySelector('.group-list');
|
||||
if (!groupListEl) return;
|
||||
|
||||
if (this._groups.length === 0) {
|
||||
groupListEl.parentElement.querySelector('.empty-state')?.remove();
|
||||
groupListEl.innerHTML = this._groups.map(group => `
|
||||
<div class="group-item">
|
||||
<span>组地址:<strong>${group.address}</strong></span>
|
||||
<span>元素:${group.element_address}</span>
|
||||
<span>Model: ${group.model_id}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
} else {
|
||||
groupListEl.innerHTML = this._groups.map(group => `
|
||||
<div class="group-item">
|
||||
<span>组地址:<strong>${group.address}</strong></span>
|
||||
<span>元素:${group.element_address}</span>
|
||||
<span>Model: ${group.model_id}</span>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
}
|
||||
|
||||
_attachEventListeners() {
|
||||
if (!this.shadowRoot) return;
|
||||
|
||||
// 扫描按钮
|
||||
this.querySelector('#btn-scan')?.addEventListener('click', () => this._handleScan());
|
||||
this.querySelector('#btn-refresh')?.addEventListener('click', () => this._fetchStatus());
|
||||
this.shadowRoot.querySelector('#btn-scan')?.addEventListener('click', () => this._handleScan());
|
||||
this.shadowRoot.querySelector('#btn-refresh')?.addEventListener('click', () => this._fetchStatus());
|
||||
|
||||
// 设备选择
|
||||
this.querySelectorAll('.device-item').forEach(item => {
|
||||
this.shadowRoot.querySelectorAll('.device-item').forEach(item => {
|
||||
item.addEventListener('click', () => {
|
||||
const index = parseInt(item.dataset.index);
|
||||
this._selectedDevice = index === this._selectedDevice ? null : index;
|
||||
@ -402,16 +481,17 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
});
|
||||
|
||||
// 配网按钮
|
||||
this.querySelector('#btn-prov-start')?.addEventListener('click', () => this._handleProvStart());
|
||||
this.querySelector('#btn-prov-stop')?.addEventListener('click', () => this._handleProvStop());
|
||||
this.querySelector('#btn-bind-key')?.addEventListener('click', () => this._handleBindKey());
|
||||
this.shadowRoot.querySelector('#btn-prov-start')?.addEventListener('click', () => this._handleProvStart());
|
||||
this.shadowRoot.querySelector('#btn-prov-stop')?.addEventListener('click', () => this._handleProvStop());
|
||||
this.shadowRoot.querySelector('#btn-bind-key')?.addEventListener('click', () => this._handleBindKey());
|
||||
|
||||
// 分组按钮
|
||||
this.querySelector('#btn-add-group')?.addEventListener('click', () => this._handleAddGroup());
|
||||
this.querySelector('#btn-remove-group')?.addEventListener('click', () => this._handleRemoveGroup());
|
||||
this.shadowRoot.querySelector('#btn-add-group')?.addEventListener('click', () => this._handleAddGroup());
|
||||
this.shadowRoot.querySelector('#btn-remove-group')?.addEventListener('click', () => this._handleRemoveGroup());
|
||||
}
|
||||
|
||||
async _handleScan() {
|
||||
if (!this._hass) return;
|
||||
try {
|
||||
await fetch(`${API_BASE}/scan`, {
|
||||
method: 'POST',
|
||||
@ -428,7 +508,8 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
}
|
||||
|
||||
async _handleProvStart() {
|
||||
const deviceAddress = this.querySelector('#device-address')?.value;
|
||||
if (!this._hass) return;
|
||||
const deviceAddress = this.shadowRoot?.querySelector('#device-address')?.value;
|
||||
try {
|
||||
await fetch(`${API_BASE}/provisioning`, {
|
||||
method: 'POST',
|
||||
@ -448,6 +529,7 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
}
|
||||
|
||||
async _handleProvStop() {
|
||||
if (!this._hass) return;
|
||||
try {
|
||||
await fetch(`${API_BASE}/provisioning`, {
|
||||
method: 'POST',
|
||||
@ -464,7 +546,8 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
}
|
||||
|
||||
async _handleBindKey() {
|
||||
const deviceAddress = this.querySelector('#device-address')?.value;
|
||||
if (!this._hass) return;
|
||||
const deviceAddress = this.shadowRoot?.querySelector('#device-address')?.value;
|
||||
const elementAddress = 0;
|
||||
try {
|
||||
await fetch(`${API_BASE}/provisioning`, {
|
||||
@ -488,9 +571,10 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
}
|
||||
|
||||
async _handleAddGroup() {
|
||||
const targetAddress = this.querySelector('#target-address')?.value;
|
||||
const groupAddress = this.querySelector('#group-address')?.value;
|
||||
const modelId = parseInt(this.querySelector('#model-id')?.value || '4352');
|
||||
if (!this._hass) return;
|
||||
const targetAddress = this.shadowRoot?.querySelector('#target-address')?.value;
|
||||
const groupAddress = this.shadowRoot?.querySelector('#group-address')?.value;
|
||||
const modelId = parseInt(this.shadowRoot?.querySelector('#model-id')?.value || '4352');
|
||||
|
||||
try {
|
||||
await fetch(`${API_BASE}/group`, {
|
||||
@ -515,9 +599,10 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
}
|
||||
|
||||
async _handleRemoveGroup() {
|
||||
const targetAddress = this.querySelector('#target-address')?.value;
|
||||
const groupAddress = this.querySelector('#group-address')?.value;
|
||||
const modelId = parseInt(this.querySelector('#model-id')?.value || '4352');
|
||||
if (!this._hass) return;
|
||||
const targetAddress = this.shadowRoot?.querySelector('#target-address')?.value;
|
||||
const groupAddress = this.shadowRoot?.querySelector('#group-address')?.value;
|
||||
const modelId = parseInt(this.shadowRoot?.querySelector('#model-id')?.value || '4352');
|
||||
|
||||
try {
|
||||
await fetch(`${API_BASE}/group`, {
|
||||
@ -542,9 +627,9 @@ class SigMeshGatewayPanel extends HTMLElement {
|
||||
}
|
||||
|
||||
// 注册自定义元素
|
||||
customElements.define('sigmesh-gateway-panel', SigMeshGatewayPanel);
|
||||
customElements.define('sigmesh-gateway-panel', SigMeshGatewayCard);
|
||||
|
||||
// 导出到 window 供调试使用
|
||||
window.SigMeshGatewayPanel = SigMeshGatewayPanel;
|
||||
// 添加到 window 供调试
|
||||
window.SigMeshGatewayCard = SigMeshGatewayCard;
|
||||
|
||||
console.log('SigMesh Gateway Panel 已加载');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user