// Package iot provides the IoT device source abstraction for Cyrene v2. // // It models smart-home devices (light, AC, curtain, sensor, lock) as typed // devices managed by pluggable providers. A Provider is a device source; the // Manager selects a provider from configuration, exposes List/Control/Status, // and publishes device state transitions as events on the in-process bus so // other subsystems (frontend, memory, push) observe changes without polling // (DevDocs/02-development-constraints.md "IoT 设备状态 bus 事件驱动"). // // Milestone M5 delivers the pluggable provider skeleton plus a mock provider // with eight simulated devices and the control_rules that bound what each device // type supports. Real provider integrations (MQTT, Home Assistant) plug in later // through the same Provider interface. package iot import "errors" // DeviceUpdateSubject is the bus subject on which device state transitions are // published. Subscribers use this subject (or "iot.>") to observe device // changes. const DeviceUpdateSubject = "iot.device_update" // Sentinel errors returned by the iot subsystem. Use errors.Is to test for them. var ( // ErrDeviceNotFound is returned when a device ID is unknown to every // registered provider. ErrDeviceNotFound = errors.New("iot: device not found") // ErrUnsupported is returned when an action is not supported for a device's // type (the device type's capability constraint), or when an action // parameter value is outside the type's rule set. ErrUnsupported = errors.New("iot: unsupported action") // ErrOffline is returned when a Control call targets an offline device. ErrOffline = errors.New("iot: device offline") // ErrInvalidParam is returned when an action parameter is missing or has the // wrong type. ErrInvalidParam = errors.New("iot: invalid parameter") // ErrUnknownProvider is returned when configuration names an unknown // provider. ErrUnknownProvider = errors.New("iot: unknown provider") // ErrProviderNotRegistered is returned when a device operation is attempted // but no provider is registered (for example the "none" provider). ErrProviderNotRegistered = errors.New("iot: no provider registered") // ErrInvalidArgument is returned when an argument is nil or empty. ErrInvalidArgument = errors.New("iot: invalid argument") // ErrDuplicate is returned when a provider with the same name is registered // twice. ErrDuplicate = errors.New("iot: provider already registered") ) // Device is a single smart-home device exposed by a Provider. type Device struct { // ID is the globally unique device identifier used to target Control and // Status calls. ID string `json:"id"` // Name is the human-readable device label. Name string `json:"name"` // Type is the device kind, for example "light", "ac", "curtain", "sensor" or // "lock". The type determines the control_rules a Provider enforces. Type string `json:"type"` // State holds the device's current key/value state; the keys depend on the // device Type. State map[string]any `json:"state"` // Online reports whether the device is currently reachable. Online bool `json:"online"` } // Clone returns a copy of d with an independent State map, so callers can mutate // the returned device without affecting the provider's internal state. Values in // State are copied by reference; the mock provider stores only primitives. 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 is the payload published to DeviceUpdateSubject when a device // state transitions, typically as the result of a Control call. type DeviceUpdate struct { // Provider is the name of the provider that owns the device. Provider string // Device is the post-transition device snapshot. Device Device // Action is the control action that produced the transition. Action string // Params are the optional control parameters. Params map[string]any }