first commit

This commit is contained in:
David R.
2025-09-05 18:58:05 +02:00
parent fc13e0779b
commit ed05c83ac6
3678 changed files with 832193 additions and 0 deletions
@@ -0,0 +1,211 @@
# 物联网控制模块
> ⚠️ **注意:本模块已不推荐使用。请使用"MCP协议"来实现物联网控制,获得更好的兼容性与功能支持。**
本模块实现了小智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. **不产生歧义的方法描述**:为每个方法提供明确的自然语言描述,帮助大模型更准确地理解和调用
@@ -0,0 +1,92 @@
#include "thing.h"
#include "application.h"
#include <esp_log.h>
#define TAG "Thing"
namespace iot {
#if CONFIG_IOT_PROTOCOL_XIAOZHI
static std::map<std::string, std::function<Thing*()>>* thing_creators = nullptr;
#endif
void RegisterThing(const std::string& type, std::function<Thing*()> creator) {
#if CONFIG_IOT_PROTOCOL_XIAOZHI
if (thing_creators == nullptr) {
thing_creators = new std::map<std::string, std::function<Thing*()>>();
}
(*thing_creators)[type] = creator;
#endif
}
Thing* CreateThing(const std::string& type) {
#if CONFIG_IOT_PROTOCOL_XIAOZHI
auto creator = thing_creators->find(type);
if (creator == thing_creators->end()) {
ESP_LOGE(TAG, "Thing type not found: %s", type.c_str());
return nullptr;
}
return creator->second();
#else
return nullptr;
#endif
}
std::string Thing::GetDescriptorJson() {
std::string json_str = "{";
json_str += "\"name\":\"" + name_ + "\",";
json_str += "\"description\":\"" + description_ + "\",";
json_str += "\"properties\":" + properties_.GetDescriptorJson() + ",";
json_str += "\"methods\":" + methods_.GetDescriptorJson();
json_str += "}";
return json_str;
}
std::string Thing::GetStateJson() {
std::string json_str = "{";
json_str += "\"name\":\"" + name_ + "\",";
json_str += "\"state\":" + properties_.GetStateJson();
json_str += "}";
return json_str;
}
void Thing::Invoke(const cJSON* command) {
auto method_name = cJSON_GetObjectItem(command, "method");
auto input_params = cJSON_GetObjectItem(command, "parameters");
try {
auto& method = methods_[method_name->valuestring];
for (auto& param : method.parameters()) {
auto input_param = cJSON_GetObjectItem(input_params, param.name().c_str());
if (param.required() && input_param == nullptr) {
throw std::runtime_error("Parameter " + param.name() + " is required");
}
if (param.type() == kValueTypeNumber) {
if (cJSON_IsNumber(input_param)) {
param.set_number(input_param->valueint);
}
} else if (param.type() == kValueTypeString) {
if (cJSON_IsString(input_param) || cJSON_IsObject(input_param) || cJSON_IsArray(input_param)) {
std::string value_str = input_param->valuestring;
param.set_string(value_str);
}
} else if (param.type() == kValueTypeBoolean) {
if (cJSON_IsBool(input_param)) {
param.set_boolean(input_param->valueint == 1);
}
}
}
Application::GetInstance().Schedule([&method]() {
method.Invoke();
});
} catch (const std::runtime_error& e) {
ESP_LOGE(TAG, "Method not found: %s", method_name->valuestring);
return;
}
}
} // namespace iot
@@ -0,0 +1,300 @@
#ifndef THING_H
#define THING_H
#include <string>
#include <map>
#include <functional>
#include <vector>
#include <stdexcept>
#include <cJSON.h>
namespace iot {
enum ValueType {
kValueTypeBoolean,
kValueTypeNumber,
kValueTypeString
};
class Property {
private:
std::string name_;
std::string description_;
ValueType type_;
std::function<bool()> boolean_getter_;
std::function<int()> number_getter_;
std::function<std::string()> string_getter_;
public:
Property(const std::string& name, const std::string& description, std::function<bool()> getter) :
name_(name), description_(description), type_(kValueTypeBoolean), boolean_getter_(getter) {}
Property(const std::string& name, const std::string& description, std::function<int()> getter) :
name_(name), description_(description), type_(kValueTypeNumber), number_getter_(getter) {}
Property(const std::string& name, const std::string& description, std::function<std::string()> getter) :
name_(name), description_(description), type_(kValueTypeString), string_getter_(getter) {}
const std::string& name() const { return name_; }
const std::string& description() const { return description_; }
ValueType type() const { return type_; }
bool boolean() const { return boolean_getter_(); }
int number() const { return number_getter_(); }
std::string string() const { return string_getter_(); }
std::string GetDescriptorJson() {
std::string json_str = "{";
json_str += "\"description\":\"" + description_ + "\",";
if (type_ == kValueTypeBoolean) {
json_str += "\"type\":\"boolean\"";
} else if (type_ == kValueTypeNumber) {
json_str += "\"type\":\"number\"";
} else if (type_ == kValueTypeString) {
json_str += "\"type\":\"string\"";
}
json_str += "}";
return json_str;
}
std::string GetStateJson() {
if (type_ == kValueTypeBoolean) {
return boolean_getter_() ? "true" : "false";
} else if (type_ == kValueTypeNumber) {
return std::to_string(number_getter_());
} else if (type_ == kValueTypeString) {
return "\"" + string_getter_() + "\"";
}
return "null";
}
};
class PropertyList {
private:
std::vector<Property> properties_;
public:
PropertyList() = default;
PropertyList(const std::vector<Property>& properties) : properties_(properties) {}
void AddBooleanProperty(const std::string& name, const std::string& description, std::function<bool()> getter) {
properties_.push_back(Property(name, description, getter));
}
void AddNumberProperty(const std::string& name, const std::string& description, std::function<int()> getter) {
properties_.push_back(Property(name, description, getter));
}
void AddStringProperty(const std::string& name, const std::string& description, std::function<std::string()> getter) {
properties_.push_back(Property(name, description, getter));
}
const Property& operator[](const std::string& name) const {
for (auto& property : properties_) {
if (property.name() == name) {
return property;
}
}
throw std::runtime_error("Property not found: " + name);
}
std::string GetDescriptorJson() {
std::string json_str = "{";
for (auto& property : properties_) {
json_str += "\"" + property.name() + "\":" + property.GetDescriptorJson() + ",";
}
if (json_str.back() == ',') {
json_str.pop_back();
}
json_str += "}";
return json_str;
}
std::string GetStateJson() {
std::string json_str = "{";
for (auto& property : properties_) {
json_str += "\"" + property.name() + "\":" + property.GetStateJson() + ",";
}
if (json_str.back() == ',') {
json_str.pop_back();
}
json_str += "}";
return json_str;
}
};
class Parameter {
private:
std::string name_;
std::string description_;
ValueType type_;
bool required_;
bool boolean_;
int number_;
std::string string_;
public:
Parameter(const std::string& name, const std::string& description, ValueType type, bool required = true) :
name_(name), description_(description), type_(type), required_(required) {}
const std::string& name() const { return name_; }
const std::string& description() const { return description_; }
ValueType type() const { return type_; }
bool required() const { return required_; }
bool boolean() const { return boolean_; }
int number() const { return number_; }
const std::string& string() const { return string_; }
void set_boolean(bool value) { boolean_ = value; }
void set_number(int value) { number_ = value; }
void set_string(const std::string& value) { string_ = value; }
std::string GetDescriptorJson() {
std::string json_str = "{";
json_str += "\"description\":\"" + description_ + "\",";
if (type_ == kValueTypeBoolean) {
json_str += "\"type\":\"boolean\"";
} else if (type_ == kValueTypeNumber) {
json_str += "\"type\":\"number\"";
} else if (type_ == kValueTypeString) {
json_str += "\"type\":\"string\"";
}
json_str += "}";
return json_str;
}
};
class ParameterList {
private:
std::vector<Parameter> parameters_;
public:
ParameterList() = default;
ParameterList(const std::vector<Parameter>& parameters) : parameters_(parameters) {}
void AddParameter(const Parameter& parameter) {
parameters_.push_back(parameter);
}
const Parameter& operator[](const std::string& name) const {
for (auto& parameter : parameters_) {
if (parameter.name() == name) {
return parameter;
}
}
throw std::runtime_error("Parameter not found: " + name);
}
// iterator
auto begin() { return parameters_.begin(); }
auto end() { return parameters_.end(); }
std::string GetDescriptorJson() {
std::string json_str = "{";
for (auto& parameter : parameters_) {
json_str += "\"" + parameter.name() + "\":" + parameter.GetDescriptorJson() + ",";
}
if (json_str.back() == ',') {
json_str.pop_back();
}
json_str += "}";
return json_str;
}
};
class Method {
private:
std::string name_;
std::string description_;
ParameterList parameters_;
std::function<void(const ParameterList&)> callback_;
public:
Method(const std::string& name, const std::string& description, const ParameterList& parameters, std::function<void(const ParameterList&)> callback) :
name_(name), description_(description), parameters_(parameters), callback_(callback) {}
const std::string& name() const { return name_; }
const std::string& description() const { return description_; }
ParameterList& parameters() { return parameters_; }
std::string GetDescriptorJson() {
std::string json_str = "{";
json_str += "\"description\":\"" + description_ + "\",";
json_str += "\"parameters\":" + parameters_.GetDescriptorJson();
json_str += "}";
return json_str;
}
void Invoke() {
callback_(parameters_);
}
};
class MethodList {
private:
std::vector<Method> methods_;
public:
MethodList() = default;
MethodList(const std::vector<Method>& methods) : methods_(methods) {}
void AddMethod(const std::string& name, const std::string& description, const ParameterList& parameters, std::function<void(const ParameterList&)> callback) {
methods_.push_back(Method(name, description, parameters, callback));
}
Method& operator[](const std::string& name) {
for (auto& method : methods_) {
if (method.name() == name) {
return method;
}
}
throw std::runtime_error("Method not found: " + name);
}
std::string GetDescriptorJson() {
std::string json_str = "{";
for (auto& method : methods_) {
json_str += "\"" + method.name() + "\":" + method.GetDescriptorJson() + ",";
}
if (json_str.back() == ',') {
json_str.pop_back();
}
json_str += "}";
return json_str;
}
};
class Thing {
public:
Thing(const std::string& name, const std::string& description) :
name_(name), description_(description) {}
virtual ~Thing() = default;
virtual std::string GetDescriptorJson();
virtual std::string GetStateJson();
virtual void Invoke(const cJSON* command);
const std::string& name() const { return name_; }
const std::string& description() const { return description_; }
protected:
PropertyList properties_;
MethodList methods_;
private:
std::string name_;
std::string description_;
};
void RegisterThing(const std::string& type, std::function<Thing*()> creator);
Thing* CreateThing(const std::string& type);
#define DECLARE_THING(TypeName) \
static iot::Thing* Create##TypeName() { \
return new iot::TypeName(); \
} \
static bool Register##TypeNameHelper = []() { \
RegisterThing(#TypeName, Create##TypeName); \
return true; \
}();
} // namespace iot
#endif // THING_H
@@ -0,0 +1,63 @@
#include "thing_manager.h"
#include <esp_log.h>
#define TAG "ThingManager"
namespace iot {
void ThingManager::AddThing(Thing* thing) {
things_.push_back(thing);
}
std::string ThingManager::GetDescriptorsJson() {
std::string json_str = "[";
for (auto& thing : things_) {
json_str += thing->GetDescriptorJson() + ",";
}
if (json_str.back() == ',') {
json_str.pop_back();
}
json_str += "]";
return json_str;
}
bool ThingManager::GetStatesJson(std::string& json, bool delta) {
if (!delta) {
last_states_.clear();
}
bool changed = false;
json = "[";
// 枚举thing,获取每个thing的state,如果发生变化,则更新,保存到last_states_
// 如果delta为true,则只返回变化的部分
for (auto& thing : things_) {
std::string state = thing->GetStateJson();
if (delta) {
// 如果delta为true,则只返回变化的部分
auto it = last_states_.find(thing->name());
if (it != last_states_.end() && it->second == state) {
continue;
}
changed = true;
last_states_[thing->name()] = state;
}
json += state + ",";
}
if (json.back() == ',') {
json.pop_back();
}
json += "]";
return changed;
}
void ThingManager::Invoke(const cJSON* command) {
auto name = cJSON_GetObjectItem(command, "name");
for (auto& thing : things_) {
if (thing->name() == name->valuestring) {
thing->Invoke(command);
return;
}
}
}
} // namespace iot
@@ -0,0 +1,42 @@
#ifndef THING_MANAGER_H
#define THING_MANAGER_H
#include "thing.h"
#include <cJSON.h>
#include <vector>
#include <memory>
#include <functional>
#include <map>
namespace iot {
class ThingManager {
public:
static ThingManager& GetInstance() {
static ThingManager instance;
return instance;
}
ThingManager(const ThingManager&) = delete;
ThingManager& operator=(const ThingManager&) = delete;
void AddThing(Thing* thing);
std::string GetDescriptorsJson();
bool GetStatesJson(std::string& json, bool delta = false);
void Invoke(const cJSON* command);
private:
ThingManager() = default;
~ThingManager() = default;
std::vector<Thing*> things_;
std::map<std::string, std::string> last_states_;
};
} // namespace iot
#endif // THING_MANAGER_H
@@ -0,0 +1,35 @@
#include "iot/thing.h"
#include "board.h"
#include <esp_log.h>
#define TAG "Battery"
namespace iot {
// 这里仅定义 Battery 的属性和方法,不包含具体的实现
class Battery : public Thing {
private:
int level_ = 0;
bool charging_ = false;
bool discharging_ = false;
public:
Battery() : Thing("Battery", "The battery of the device") {
// 定义设备的属性
properties_.AddNumberProperty("level", "Current battery level", [this]() -> int {
auto& board = Board::GetInstance();
if (board.GetBatteryLevel(level_, charging_, discharging_)) {
return level_;
}
return 0;
});
properties_.AddBooleanProperty("charging", "Whether the battery is charging", [this]() -> int {
return charging_;
});
}
};
} // namespace iot
DECLARE_THING(Battery);
@@ -0,0 +1,58 @@
#include "iot/thing.h"
#include "board.h"
#include "audio_codec.h"
#include <driver/gpio.h>
#include <esp_log.h>
#define TAG "Lamp"
namespace iot {
// 这里仅定义 Lamp 的属性和方法,不包含具体的实现
class Lamp : public Thing {
private:
#ifdef CONFIG_IDF_TARGET_ESP32
gpio_num_t gpio_num_ = GPIO_NUM_12;
#else
gpio_num_t gpio_num_ = GPIO_NUM_18;
#endif
bool power_ = false;
void InitializeGpio() {
gpio_config_t config = {
.pin_bit_mask = (1ULL << gpio_num_),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ESP_ERROR_CHECK(gpio_config(&config));
gpio_set_level(gpio_num_, 0);
}
public:
Lamp() : Thing("Lamp", "A test lamp"), power_(false) {
InitializeGpio();
// 定义设备的属性
properties_.AddBooleanProperty("power", "Whether the lamp is on", [this]() -> bool {
return power_;
});
// 定义设备可以被远程执行的指令
methods_.AddMethod("turn_on", "Turn on the lamp", ParameterList(), [this](const ParameterList& parameters) {
power_ = true;
gpio_set_level(gpio_num_, 1);
});
methods_.AddMethod("turn_off", "Turn off the lamp", ParameterList(), [this](const ParameterList& parameters) {
power_ = false;
gpio_set_level(gpio_num_, 0);
});
}
};
} // namespace iot
DECLARE_THING(Lamp);
@@ -0,0 +1,54 @@
#include "iot/thing.h"
#include "board.h"
#include "display/lcd_display.h"
#include "settings.h"
#include <esp_log.h>
#include <string>
#define TAG "Screen"
namespace iot {
// 这里仅定义 Screen 的属性和方法,不包含具体的实现
class Screen : public Thing {
public:
Screen() : Thing("Screen", "A screen that can set theme and brightness") {
// 定义设备的属性
properties_.AddStringProperty("theme", "Current theme", [this]() -> std::string {
auto theme = Board::GetInstance().GetDisplay()->GetTheme();
return theme;
});
properties_.AddNumberProperty("brightness", "Current brightness percentage", [this]() -> int {
// 这里可以添加获取当前亮度的逻辑
auto backlight = Board::GetInstance().GetBacklight();
return backlight ? backlight->brightness() : 100;
});
// 定义设备可以被远程执行的指令
methods_.AddMethod("set_theme", "Set the screen theme", ParameterList({
Parameter("theme_name", "Valid string values are 'light' and 'dark'", kValueTypeString, true)
}), [this](const ParameterList& parameters) {
std::string theme_name = static_cast<std::string>(parameters["theme_name"].string());
auto display = Board::GetInstance().GetDisplay();
if (display) {
display->SetTheme(theme_name);
}
});
methods_.AddMethod("set_brightness", "Set the brightness", ParameterList({
Parameter("brightness", "An integer between 0 and 100", kValueTypeNumber, true)
}), [this](const ParameterList& parameters) {
uint8_t brightness = static_cast<uint8_t>(parameters["brightness"].number());
auto backlight = Board::GetInstance().GetBacklight();
if (backlight) {
backlight->SetBrightness(brightness, true);
}
});
}
};
} // namespace iot
DECLARE_THING(Screen);
@@ -0,0 +1,33 @@
#include "iot/thing.h"
#include "board.h"
#include "audio_codec.h"
#include <esp_log.h>
#define TAG "Speaker"
namespace iot {
// 这里仅定义 Speaker 的属性和方法,不包含具体的实现
class Speaker : public Thing {
public:
Speaker() : Thing("AudioSpeaker", "The audio speaker of the device") {
// 定义设备的属性
properties_.AddNumberProperty("volume", "Current audio volume value", [this]() -> int {
auto codec = Board::GetInstance().GetAudioCodec();
return codec->output_volume();
});
// 定义设备可以被远程执行的指令
methods_.AddMethod("set_volume", "Set the audio volume", ParameterList({
Parameter("volume", "An integer between 0 and 100", kValueTypeNumber, true)
}), [this](const ParameterList& parameters) {
auto codec = Board::GetInstance().GetAudioCodec();
codec->SetOutputVolume(static_cast<uint8_t>(parameters["volume"].number()));
});
}
};
} // namespace iot
DECLARE_THING(Speaker);