99 lines
4.6 KiB
Go
99 lines
4.6 KiB
Go
// Package iot 提供 Cyrene v2 的 IoT 设备来源抽象。
|
||
//
|
||
// 它把智能家居设备(灯、空调、窗帘、传感器、门锁)建模为强类型设备,交由可插拔的
|
||
// Provider 管理。Provider 即设备来源;Manager 依据配置选定 Provider,对外提供
|
||
// List/Control/Status,并把设备状态跃迁作为事件发布到进程内 bus,使其他子系统
|
||
// (前端、记忆、推送)无需轮询即可感知设备变化(对照
|
||
// DevDocs/02-development-constraints.md "IoT 设备状态 bus 事件驱动")。
|
||
//
|
||
// 里程碑 M5 交付可插拔 Provider 骨架,外加一个 mock Provider:内置八台模拟设备,
|
||
// 以及界定每种设备类型支持范围的 control_rules。真实 Provider 集成(MQTT、
|
||
// Home Assistant)后续通过同一套 Provider 接口接入。
|
||
package iot
|
||
|
||
import (
|
||
"errors"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/urgency"
|
||
)
|
||
|
||
// DeviceUpdateSubject 是设备状态跃迁事件发布所用的 bus 主题。订阅方使用该主题
|
||
// (或 "iot.>")观察设备变化。
|
||
const DeviceUpdateSubject = "iot.device_update"
|
||
|
||
// iot 子系统返回的哨兵错误,判定时请用 errors.Is。
|
||
var (
|
||
// ErrDeviceNotFound 表示该设备 ID 对全部已注册 Provider 均未知。
|
||
ErrDeviceNotFound = errors.New("iot: device not found")
|
||
// ErrUnsupported 表示某动作不被该设备类型支持(受该类型的 capability 约束),
|
||
// 或动作参数取值超出该类型的规则集合。
|
||
ErrUnsupported = errors.New("iot: unsupported action")
|
||
// ErrOffline 表示 Control 调用指向了离线设备。
|
||
ErrOffline = errors.New("iot: device offline")
|
||
// ErrInvalidParam 表示动作参数缺失或类型错误。
|
||
ErrInvalidParam = errors.New("iot: invalid parameter")
|
||
// ErrUnknownProvider 表示配置指定了未知的 Provider。
|
||
ErrUnknownProvider = errors.New("iot: unknown provider")
|
||
// ErrProviderNotRegistered 表示尝试执行设备操作,但没有任何已注册的 Provider
|
||
// (例如 "none" Provider)。
|
||
ErrProviderNotRegistered = errors.New("iot: no provider registered")
|
||
// ErrInvalidArgument 表示传入参数为 nil 或空。
|
||
ErrInvalidArgument = errors.New("iot: invalid argument")
|
||
// ErrDuplicate 表示同名 Provider 被注册了两次。
|
||
ErrDuplicate = errors.New("iot: provider already registered")
|
||
)
|
||
|
||
// Device 是由某个 Provider 暴露的单台智能家居设备。
|
||
type Device struct {
|
||
// ID 是设备全局唯一标识,Control 与 Status 调用据此定位设备。
|
||
ID string `json:"id"`
|
||
// Name 是供人阅读的设备名称。
|
||
Name string `json:"name"`
|
||
// Type 是设备种类,例如 "light"、"ac"、"curtain"、"sensor"、"lock";
|
||
// 类型决定 Provider 施加的 control_rules。
|
||
Type string `json:"type"`
|
||
// State 保存设备当前的键值状态;具体键取决于设备的 Type。
|
||
State map[string]any `json:"state"`
|
||
// Online 表示设备当前是否可达。
|
||
Online bool `json:"online"`
|
||
}
|
||
|
||
// Clone 返回 d 的副本,其中 State 为独立 map,因此调用方可以随意修改返回的设备
|
||
// 而不影响 Provider 的内部状态。State 中的值按引用复制;mock Provider 只存放
|
||
// 基本类型值。
|
||
func (d Device) Clone() Device {
|
||
cp := d
|
||
if d.State != nil {
|
||
cp.State = make(map[string]any, len(d.State))
|
||
for k, v := range d.State {
|
||
cp.State[k] = v
|
||
}
|
||
}
|
||
return cp
|
||
}
|
||
|
||
// DeviceUpdate 是设备状态跃迁时发布到 DeviceUpdateSubject 的载荷,通常由一次
|
||
// Control 调用产生。
|
||
type DeviceUpdate struct {
|
||
// Provider 是拥有该设备的 Provider 名称。
|
||
Provider string `json:"provider"`
|
||
// Device 是跃迁后的设备快照。
|
||
Device Device `json:"device"`
|
||
// Action 是触发该次跃迁的控制动作。
|
||
Action string `json:"action"`
|
||
// Params 是可选的控制参数。
|
||
Params map[string]any `json:"params,omitempty"`
|
||
// Message 是跃迁的可读摘要,会呈现到前端的 device_update 信息流中。
|
||
Message string `json:"message,omitempty"`
|
||
// Severity 是本次跃迁的显式严重度(通用紧急度词汇 urgency.Level 的别名)。
|
||
// 零值 SeverityRoutine 表示例行变化,因此既有订阅方与既有的普通设备变化行为
|
||
// 逐字不变;命中安全规则(见 safetySeverity)的跃迁才被标成安全域
|
||
// (design/12 §6 安全域豁免)。
|
||
Severity AlertSeverity `json:"severity"`
|
||
}
|
||
|
||
// Urgency 实现 urgency.Declarer:DeviceUpdate 因此在**通用通道**上声明紧急度,
|
||
// 让 backthink(以及任何订阅方)无需认识 IoT 的专有字段就能承认安全域事件。
|
||
// 接线点见 backthink 的 triggerForEvent。
|
||
func (d DeviceUpdate) Urgency() urgency.Level { return d.Severity }
|