# 物联网控制模块

本模块实现了小智AI语音聊天机器人的物联网控制功能，使用户可以通过语音指令控制接入到ESP32开发板的各种物联网设备。

## 工作原理

整个物联网控制模块的工作流程如下：

1. **设备注册**：在开发板初始化阶段（如在`compact_wifi_board.cc`中），各种物联网设备通过`ThingManager`注册到系统中
2. **设备描述**：系统将设备描述信息（包括名称、属性、方法等）通过通信协议（如MQTT或WebSocket）发送给AI服务器
3. **用户交互**：用户通过语音与小智AI对话，表达控制物联网设备的意图
4. **命令执行**：AI服务器解析用户意图，生成控制命令，通过协议发送回ESP32，由`ThingManager`分发给对应的设备执行
5. **状态更新**：设备执行命令后，状态变化会通过`ThingManager`收集并发送回AI服务器，保持状态同步

## 核心组件

### ThingManager

`ThingManager`是物联网控制模块的核心管理类，采用单例模式实现：

- `AddThing`：注册物联网设备
- `GetDescriptorsJson`：获取所有设备的描述信息，用于向AI服务器报告设备能力
- `GetStatesJson`：获取所有设备的当前状态，可以选择只返回变化的部分
- `Invoke`：根据AI服务器下发的命令，调用对应设备的方法

### Thing

`Thing`是所有物联网设备的基类，提供了以下核心功能：

- 属性管理：通过`PropertyList`定义设备的可查询状态
- 方法管理：通过`MethodList`定义设备可执行的操作
- JSON序列化：将设备描述和状态转换为JSON格式，便于网络传输
- 命令执行：解析和执行来自AI服务器的指令

## 设备设计示例

### 灯（Lamp）

灯是一个简单的物联网设备示例，通过GPIO控制LED的开关状态：

```cpp
class Lamp : public Thing {
private:
    gpio_num_t gpio_num_ = GPIO_NUM_18;  // GPIO引脚
    bool power_ = false;                 // 灯的开关状态

public:
    Lamp() : Thing("Lamp", "一个测试用的灯") {
        // 初始化GPIO
        InitializeGpio();

        // 定义属性：power（表示灯的开关状态）
        properties_.AddBooleanProperty("power", "灯是否打开", [this]() -> bool {
            return power_;
        });

        // 定义方法：TurnOn（打开灯）
        methods_.AddMethod("TurnOn", "打开灯", ParameterList(), [this](const ParameterList& parameters) {
            power_ = true;
            gpio_set_level(gpio_num_, 1);
        });

        // 定义方法：TurnOff（关闭灯）
        methods_.AddMethod("TurnOff", "关闭灯", ParameterList(), [this](const ParameterList& parameters) {
            power_ = false;
            gpio_set_level(gpio_num_, 0);
        });
    }
};
```

用户可以通过语音指令如"小智，请打开灯"来控制灯的开关。

### 扬声器（Speaker）

扬声器控制实现了音量调节功能：

```cpp
class Speaker : public Thing {
public:
    Speaker() : Thing("Speaker", "扬声器") {
        // 定义属性：volume（当前音量值）
        properties_.AddNumberProperty("volume", "当前音量值", [this]() -> int {
            auto codec = Board::GetInstance().GetAudioCodec();
            return codec->output_volume();
        });

        // 定义方法：SetVolume（设置音量）
        methods_.AddMethod("SetVolume", "设置音量", ParameterList({
            Parameter("volume", "0到100之间的整数", kValueTypeNumber, true)
        }), [this](const ParameterList& parameters) {
            auto codec = Board::GetInstance().GetAudioCodec();
            codec->SetOutputVolume(static_cast<uint8_t>(parameters["volume"].number()));
        });
    }
};
```

用户可以通过语音指令如"小智，把音量调到50"来控制扬声器的音量。

## 设计自定义物联网设备

要设计一个新的物联网设备，需要以下步骤：

1. **创建设备类**：继承`Thing`基类
2. **定义属性**：使用`properties_`添加设备的可查询状态
3. **定义方法**：使用`methods_`添加设备可执行的操作
4. **实现硬件控制**：在方法回调中实现对硬件的控制
5. **注册设备**：注册设备有两种方式（见下文），并在板级初始化中添加设备实例

### 两种设备注册方式

1. **使用DECLARE_THING宏**：适合通用设备类型
   ```cpp
   // 在设备实现文件末尾添加
   DECLARE_THING(MyDevice);
   
   // 然后在板级初始化中
   thing_manager.AddThing(iot::CreateThing("MyDevice"));
   ```

2. **直接创建设备实例**：适合特定于板级的设备
   ```cpp
   // 在板级初始化中
   auto my_device = new iot::MyDevice();
   thing_manager.AddThing(my_device);
   ```

### 设备实现位置建议

您可以根据设备的通用性选择不同的实现位置：

1. **通用设备**：放在`main/iot/things/`目录下，使用`DECLARE_THING`注册
2. **板级特定设备**：直接在板级目录(如`main/boards/your_board/`)中实现，使用直接创建实例的方式注册

这种灵活性允许您为不同的板设计特定的设备实现，同时保持通用设备的可重用性。

### 属性类型

物联网设备支持以下属性类型：

- **布尔值**（`kValueTypeBoolean`）：开关状态等
- **数值**（`kValueTypeNumber`）：温度、音量等
- **字符串**（`kValueTypeString`）：设备名称、状态描述等

### 方法参数

设备方法可以定义参数，支持以下参数类型：

- **布尔值**：开关等
- **数值**：温度、音量等
- **字符串**：命令、模式等

## 使用示例

在板级初始化代码（如`compact_wifi_board.cc`）中注册物联网设备：

```cpp
void InitializeIot() {
    auto& thing_manager = iot::ThingManager::GetInstance();
    thing_manager.AddThing(iot::CreateThing("Speaker"));
    thing_manager.AddThing(iot::CreateThing("Lamp"));
}
```

之后，用户可以通过语音指令控制这些设备，例如：

- "小智，打开灯"
- "我要睡觉了，帮我关灯"
- "音量有点太小了"
- "把音量设置为80%"

AI服务器会将这些语音指令解析为对应的设备控制命令，通过协议发送给ESP32执行。

## 注意事项

### 大模型控制的随机性

由于语音控制由大型语言模型(LLM)处理，控制过程可能存在一定的随机性和不确定性。为了增强安全性和可靠性，请考虑以下建议：

1. **关键操作添加警示信息**：对于潜在危险或不可逆的操作，在方法描述中添加警示信息
   ```cpp
   methods_.AddMethod("PowerOff", "关闭系统电源[警告:此操作将导致设备完全关闭，请慎重使用]", 
                     ParameterList(), [this](const ParameterList& parameters) {
       // 关闭电源的实现
   });
   ```

2. **二次确认机制**：重要操作应在描述中明确要求二次确认
   ```cpp
   methods_.AddMethod("ResetToFactory", "恢复出厂设置[必须要用户二次确认]", 
                     ParameterList(), [this](const ParameterList& parameters) {
       // 恢复出厂设置的实现
   });
   ```

### 通信协议限制

当前IoT协议(1.0版本)存在以下限制：

1. **单向控制流**：大模型只能下发指令，无法立即获取指令执行结果
2. **状态更新延迟**：设备状态变更需要等到下一轮对话时，通过读取property属性值才能获知
3. **异步反馈**：如果需要操作结果反馈，必须通过设备属性的方式间接实现

### 最佳实践

1. **使用有意义的属性名称**：属性名称应清晰表达其含义，便于大模型理解和使用

2. **不产生歧义的方法描述**：为每个方法提供明确的自然语言描述，帮助大模型更准确地理解和调用