impress_sig_mesh_hacs/custom_components/sigmesh_gateway/services.py
impressionyang 4c3eb62dfb feat: 实现完整的 Web UI 配网管理功能
新增 Web UI 组件:
- web_ui.py: RESTful API 端点(状态、扫描、配网、分组、设备)
- sigmesh-gateway-panel.js: Lovelace Dashboard 自定义卡片
  - 设备扫描和发现
- 配网操作(开始/停止/绑定 App Key)
  - 分组管理(添加/移除)
  - 实时状态监控

配置更新:
- __init__.py: 集成 Web UI 和服务注册
- const.py: 添加服务常量定义
- services.py: 保留服务调用用于向后兼容
- README.md: 添加 Web UI 配置说明
- docs/UI 使用指南.md: 详细的 UI 使用文档

使用方式:
1. 配置 frontend.extra_module_url 加载 JS 面板
2. 在 Lovelace Dashboard 添加 custom:sigmesh-gateway-panel 卡片
3. 通过 UI 完成所有配网和分组操作

API 端点:
- GET /api/sigmesh_gateway/status - 获取配网状态
- POST /api/sigmesh_gateway/scan - 开始扫描
- POST /api/sigmesh_gateway/provisioning - 配网操作
- POST /api/sigmesh_gateway/group - 分组管理
- GET /api/sigmesh_gateway/devices - 设备列表
2026-04-16 13:41:28 +08:00

193 lines
6.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""SigMesh Gateway 服务定义(保留用于向后兼容)。
注意:配网功能已迁移到 Web UI服务调用接口保留用于自动化脚本。
"""
import voluptuous as vol
from homeassistant.helpers import config_validation as cv
from .const import CONF_GROUP_ADDRESS, CONF_NETWORK_KEY, DOMAIN
# 服务常量
SERVICE_START_SCAN = "start_scan"
SERVICE_STOP_PROVISIONING = "stop_provisioning"
SERVICE_START_PROVISIONING = "start_provisioning"
SERVICE_BIND_APPKEY = "bind_appkey"
SERVICE_ADD_TO_GROUP = "add_to_group"
SERVICE_REMOVE_FROM_GROUP = "remove_from_group"
SERVICE_SEND_VENDOR_COMMAND = "send_vendor_command"
# 服务 Schema
SERVICE_SCHEMA_START_SCAN = {}
SERVICE_SCHEMA_STOP_PROVISIONING = {}
SERVICE_SCHEMA_START_PROVISIONING = {
vol.Required("device_address"): cv.string,
}
SERVICE_SCHEMA_BIND_APPKEY = {
vol.Required("device_address"): cv.string,
vol.Required("element_address", default=0): vol.Coerce(int),
}
SERVICE_SCHEMA_ADD_TO_GROUP = {
vol.Required("target_address"): cv.string,
vol.Required("element_address", default=0): vol.Coerce(int),
vol.Required("group_address"): cv.string,
vol.Required("model_id", default=4352): vol.Coerce(int),
vol.Optional("is_sig", default=True): cv.boolean,
}
SERVICE_SCHEMA_REMOVE_FROM_GROUP = {
vol.Required("target_address"): cv.string,
vol.Required("element_address", default=0): vol.Coerce(int),
vol.Required("group_address"): cv.string,
vol.Required("model_id", default=4352): vol.Coerce(int),
vol.Optional("is_sig", default=True): cv.boolean,
}
SERVICE_SCHEMA_SEND_VENDOR_COMMAND = {
vol.Required("target_address"): cv.string,
vol.Required("element_address", default=0): vol.Coerce(int),
vol.Required("opcode"): cv.string,
vol.Required("payload"): cv.string,
}
def setup_services(hass, coordinators: dict) -> None:
"""设置 HA 服务。
注意:配网功能已迁移到 Web UI服务调用接口保留用于自动化脚本。
推荐使用 Web UI 进行配网和分组管理操作。
Args:
hass: HomeAssistant 实例
coordinators: 协调器字典 {entry_id: coordinator}
"""
async def handle_start_scan(call) -> None:
"""处理开始扫描服务调用."""
for coordinator in coordinators.values():
await coordinator.start_scanning()
async def handle_stop_provisioning(call) -> None:
"""处理停止配网服务调用."""
for coordinator in coordinators.values():
await coordinator.stop_provisioning()
async def handle_start_provisioning(call) -> None:
"""处理开始配网服务调用."""
device_address = call.data.get("device_address")
for coordinator in coordinators.values():
await coordinator.start_provisioning(device_address)
async def handle_bind_appkey(call) -> None:
"""处理绑定 App Key 服务调用."""
device_address = call.data.get("device_address")
element_address = call.data.get("element_address", 0)
for coordinator in coordinators.values():
await coordinator.bind_app_key(device_address, element_address)
async def handle_add_to_group(call) -> None:
"""处理添加设备到组服务调用."""
target_address = call.data.get("target_address")
element_address = call.data.get("element_address", 0)
group_address = call.data.get("group_address")
model_id = call.data.get("model_id", 4352)
is_sig = call.data.get("is_sig", True)
# 解析组地址(支持 hex 字符串)
if isinstance(group_address, str):
group_address = int(group_address, 16)
for coordinator in coordinators.values():
await coordinator.add_device_to_group(
target_address, element_address, group_address, model_id, is_sig
)
async def handle_remove_from_group(call) -> None:
"""处理从组中移除设备服务调用."""
target_address = call.data.get("target_address")
element_address = call.data.get("element_address", 0)
group_address = call.data.get("group_address")
model_id = call.data.get("model_id", 4352)
is_sig = call.data.get("is_sig", True)
# 解析组地址(支持 hex 字符串)
if isinstance(group_address, str):
group_address = int(group_address, 16)
for coordinator in coordinators.values():
await coordinator.remove_device_from_group(
target_address, element_address, group_address, model_id, is_sig
)
async def handle_send_vendor_command(call) -> None:
"""处理发送 VENDOR 命令服务调用."""
target_address = call.data.get("target_address")
element_address = call.data.get("element_address", 0)
opcode = call.data.get("opcode")
payload = call.data.get("payload")
# 解析操作码和负载
if isinstance(opcode, str):
opcode = int(opcode, 16)
if isinstance(payload, str):
payload = bytes.fromhex(payload)
for coordinator in coordinators.values():
await coordinator.prov_manager.send_vendor_command(
target_address, element_address, opcode, payload
)
# 注册服务
hass.services.async_register(
DOMAIN,
SERVICE_START_SCAN,
handle_start_scan,
schema=vol.Schema(SERVICE_SCHEMA_START_SCAN),
)
hass.services.async_register(
DOMAIN,
SERVICE_STOP_PROVISIONING,
handle_stop_provisioning,
schema=vol.Schema(SERVICE_SCHEMA_STOP_PROVISIONING),
)
hass.services.async_register(
DOMAIN,
SERVICE_START_PROVISIONING,
handle_start_provisioning,
schema=vol.Schema(SERVICE_SCHEMA_START_PROVISIONING),
)
hass.services.async_register(
DOMAIN,
SERVICE_BIND_APPKEY,
handle_bind_appkey,
schema=vol.Schema(SERVICE_SCHEMA_BIND_APPKEY),
)
hass.services.async_register(
DOMAIN,
SERVICE_ADD_TO_GROUP,
handle_add_to_group,
schema=vol.Schema(SERVICE_SCHEMA_ADD_TO_GROUP),
)
hass.services.async_register(
DOMAIN,
SERVICE_REMOVE_FROM_GROUP,
handle_remove_from_group,
schema=vol.Schema(SERVICE_SCHEMA_REMOVE_FROM_GROUP),
)
hass.services.async_register(
DOMAIN,
SERVICE_SEND_VENDOR_COMMAND,
handle_send_vendor_command,
schema=vol.Schema(SERVICE_SCHEMA_SEND_VENDOR_COMMAND),
)