// 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 }