impress_sig_mesh_hacs/custom_components/sigmesh_gateway/services.py
impressionyang d21e7f1b3f feat: 添加配网和分组管理功能
新增功能:
- 配网管理模块 (provisioning.py): 支持设备扫描、配网、超时处理
- 配网配置步骤: UI 配置流程增加配网参数配置(Network Key, App Key 等)
- 分组管理:支持 SIG 分组和 VENDOR 分组的加入/删除操作
- HA 服务调用:7 个配网和分组相关的服务

文件变更:
- const.py: 添加配网相关常量(CONF_NETWORK_KEY, PROV_TIMEOUT 等)
- config_flow.py: 增加 prov_config 配置步骤和 OptionsFlow 菜单
- provisioning.py: 新建配网管理器(ProvisioningManager 类)
- coordinator.py: 集成配网管理器,添加配网状态管理方法
- services.py: 新建服务定义和注册
- services.yaml: HA 服务定义文件
- __init__.py: 集成服务注册和卸载
- PRD.md: 更新服务调用接口和配置参数文档

配网功能说明:
- 首次使用需配置 Network Key, App Key, Network ID, IV Index
- 配网超时时间:180 秒
- 组地址范围:0xC000 - 0xCFFF
- 支持 SIG 标准分组和 VENDOR 自定义分组
2026-04-16 12:05:13 +08:00

187 lines
6.2 KiB
Python

"""SigMesh Gateway 服务定义。"""
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 服务。
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),
)