新增 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 - 设备列表
193 lines
6.5 KiB
Python
193 lines
6.5 KiB
Python
"""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),
|
||
)
|