MySensors
MySensors
配置
要将 MySensors integration 添加到您的 Home Assistant 实例中,请使用此 My 按钮:
手动配置步骤
如果上述 My 按钮不起作用,您也可以手动执行以下步骤:
-
浏览到您的 Home Assistant 实例。
-
转到
设置 > 设备与服务。 -
在右下角,选择
Add Integration 按钮。 -
从列表中选择 MySensors。
-
按照屏幕上的说明完成设置。
配置取决于您使用的网关类型:
串行网关
如果您使用原始 Arduino 作为串行网关,则端口名称将为 ttyACM*
。可以使用下面显示的命令确定确切的号码。
ls /dev/ttyACM*
除了串行设备外,您还需要输入波特率。
MQTT 网关
如果您使用 MQTT 网关,则需要输入输入和输出的主题前缀。这些需要与网关的设置进行互换。即,Home Assistant 的输入主题需要是网关的输出(发布)主题。
MQTT 网关需要 MySensors 版本 2.0+ 并且仅支持 MQTT 客户端网关。
以太网网关
要使用以太网网关,您需要配置网关的 IP 地址和端口。
常规选项
所有网关都提供一些选项:
-
持久化文件: Home Assistant 将在文件中存储检测到的节点。这意味着重启 Home Assistant 不需要重新发现所有节点。持久化文件选项允许设置文件的路径。如将选项留空,Home Assistant 将在配置目录中自动生成文件名。
-
版本: 输入您用于网关的 MySensors 版本。
展示
通过以下步骤展示 MySensors 传感器或执行器:
输入您使用的 MySensors 版本
- 启动 Home Assistant 并配置 MySensors 集成
- 编写并上传您的 MySensors 草图到传感器。确保您:
- 发送草图名称。
- 展示传感器的
S_TYPE
。 - 每个
V_TYPE
发送至少一个初始值。在 MySensors 版本 2.x 中,必须在循环函数中完成此操作。请参见下面的示例,以确保控制器已收到初始值。
- 启动传感器。
/*
* 文档: https://www.mysensors.org
* 支持论坛: https://forum.mysensors.org
*
* https://www.mysensors.org/build/relay
*/
#define MY_DEBUG
#define MY_RADIO_NRF24
#define MY_REPEATER_FEATURE
#define MY_NODE_ID 1
#include <SPI.h>
#include <MySensors.h>
#include <Bounce2.h>
#define RELAY_PIN 5
#define BUTTON_PIN 3
#define CHILD_ID 1
#define RELAY_ON 1
#define RELAY_OFF 0
Bounce debouncer = Bounce();
bool state = false;
bool initialValueSent = false;
MyMessage msg(CHILD_ID, V_STATUS);
void setup()
{
pinMode(BUTTON_PIN, INPUT_PULLUP);
debouncer.attach(BUTTON_PIN);
debouncer.interval(10);
// 启动时确保继电器是关闭的
digitalWrite(RELAY_PIN, RELAY_OFF);
pinMode(RELAY_PIN, OUTPUT);
}
void presentation() {
sendSketchInfo("Relay+button", "1.0");
present(CHILD_ID, S_BINARY);
}
void loop()
{
if (!initialValueSent) {
Serial.println("发送初始值");
send(msg.set(state?RELAY_ON:RELAY_OFF));
Serial.println("请求控制器的初始值");
request(CHILD_ID, V_STATUS);
wait(2000, C_SET, V_STATUS);
}
if (debouncer.update()) {
if (debouncer.read()==LOW) {
state = !state;
// 发送新状态并请求确认
send(msg.set(state?RELAY_ON:RELAY_OFF), true);
}
}
}
void receive(const MyMessage &message) {
if (message.isAck()) {
Serial.println("这是来自网关的确认");
}
if (message.type == V_STATUS) {
if (!initialValueSent) {
Serial.println("接收到来自控制器的初始值");
initialValueSent = true;
}
// 更改继电器状态
state = (bool)message.getInt();
digitalWrite(RELAY_PIN, state?RELAY_ON:RELAY_OFF);
send(msg.set(state?RELAY_ON:RELAY_OFF));
}
}
SmartSleep
从 MySensors 设备向 Home Assistant 发送一个心跳 I_HEARTBEAT_RESPONSE
,使用 MySensors 版本 2.0 - 2.1,将激活 Home Assistant 中的 SmartSleep 功能。这意味着消息会被缓冲,并且仅在接收到来自设备的心跳时才会发送状态更改。状态更改会被存储,以便仅将最后请求的状态更改发送到设备。其他类型的消息将在 FIFO 队列中排队。SmartSleep 对于等待命令的电池供电执行器非常有用。有关如何发送心跳和使设备进入睡眠状态的信息,请参见 MySensors 库 API。
在 MySensors 版本 2.2 中,串行 API 从使用 I_HEARTBEAT_RESPONSE
切换到使用 I_PRE_SLEEP_NOTIFICATION
和 I_POST_SLEEP_NOTIFICATION
来信号通知 SmartSleep。Home Assistant 已被升级以支持新的消息类型,当接收到 I_PRE_SLEEP_NOTIFICATION
类型的消息时,如使用 MySensors 版本 2.2.x 或更高版本时,将激活 SmartSleep。如果 Home Assistant 配置为使用 MySensors 版本 2.0 - 2.1,则保留旧的 SmartSleep 行为。
消息验证
从或向 MySensors 设备发送到 Home Assistant 的消息将根据 MySensors 串行 API
日志应当警告您验证失败的消息,或缺少特定子类型所需的子值。如果某个平台接收到一个所需值类型,但其他所需值类型缺失,Home Assistant 将以警告级别记录子值的验证失败。
消息验证是在 Home Assistant 版本 0.52 中引入的。
调试日志
如果您遇到消息丢失或设备未添加到 Home Assistant 的情况,请为 mysensors
集成和 mysensors
包开启调试日志。这将帮助您了解发生了什么。如果您在我们的 GitHub 问题追踪器中报告有关 mysensors
集成的问题,请确保使用以下日志设置收集日志样本。
logger:
default: info
logs:
homeassistant.components.mysensors: debug
mysensors: debug
访问 MySensors 库 API
二进制传感器
支持以下二进制传感器类型:
MySensors 版本 1.4 及更高版本
S_TYPE | V_TYPE |
---|---|
S_DOOR | V_TRIPPED |
S_MOTION | V_TRIPPED |
S_SMOKE | V_TRIPPED |
MySensors 版本 1.5 及更高版本
S_TYPE | V_TYPE |
---|---|
S_SPRINKLER | V_TRIPPED |
S_WATER_LEAK | V_TRIPPED |
S_SOUND | V_TRIPPED |
S_VIBRATION | V_TRIPPED |
S_MOISTURE | V_TRIPPED |
二进制传感器示例草图
/**
* 文档: https://www.mysensors.org
* 支持论坛: https://forum.mysensors.org
*
* https://www.mysensors.org/build/binary
*/
#include <MySensor.h>
#include <SPI.h>
#include <Bounce2.h>
#define SN "BinarySensor"
#define SV "1.0"
#define CHILD_ID 1
#define BUTTON_PIN 3 // Arduino 数字 I/O 引脚用于按钮/磁簧开关。
MySensor gw;
Bounce debouncer = Bounce();
MyMessage msg(CHILD_ID, V_TRIPPED);
void setup()
{
gw.begin();
gw.sendSketchInfo(SN, SV);
// 设置按钮。
pinMode(BUTTON_PIN, INPUT_PULLUP);
// 设置按钮后,设置去抖器。
debouncer.attach(BUTTON_PIN);
debouncer.interval(5);
gw.present(CHILD_ID, S_DOOR);
gw.send(msg.set(0));
}
void loop()
{
if (debouncer.update()) {
// 获取更新值。
int value = debouncer.read();
// 发送新值。
gw.send(msg.set(value == LOW ? 1 : 0));
}
}
气候
支持以下执行器类型:
MySensors 版本 1.5 及更高版本
S_TYPE | V_TYPE |
---|---|
S_HVAC | V_HVAC_FLOW_STATE*, V_HVAC_SETPOINT_HEAT, V_HVAC_SETPOINT_COOL, V_HVAC_SPEED, V_TEMP |
V_HVAC_FLOW_STATE 被映射为 Home Assistant 中气候集成的状态,如下所示:
Home Assistant 状态 | MySensors 状态 |
---|---|
HVAC_MODE_COOL | CoolOn |
HVAC_MODE_HEAT | HeatOn |
HVAC_MODE_AUTO | AutoChangeOver |
HVAC_MODE_OFF | Off |
当前湿度、away_mode、aux_heat、swing_mode 不受支持。这将在后续版本中包含。
在制热模式中使用 V_HVAC_SETPOINT_HEAT 设置目标温度,在制冷模式中使用 V_HVAC_SETPOINT_COOL。在任何自动换季模式下,您可以使用 V_HVAC_SETPOINT_HEAT 和 V_HVAC_SETPOINT_COOL 设置设备的低值和高值。
您可以使用 V_HVAC_SPEED 控制 HVAC 中风扇的速度设置。
您可以使用 V_TEMP 将当前温度从节点发送到 Home Assistant。
MySensors 2.x 的气候示例草图
/*
* 文档: https://www.mysensors.org
* 支持论坛: https://forum.mysensors.org
*/
// 启用调试打印到串行监视器
#define MY_DEBUG
#define MY_RADIO_NRF24
#include <MySensors.h> // 测试版本 2.3.2
#define SENSOR_NAME "Heatpump Sensor"
#define SENSOR_VERSION "2.3"
#define CHILD_ID_HVAC 0
#define IR_PIN 3 // Arduino 引脚连接到 IR LED,使用 PWM
// 取消注释您的热泵型号
//#include <FujitsuHeatpumpIR.h>
//#include <PanasonicCKPHeatpumpIR.h>
//#include <PanasonicHeatpumpIR.h>
//#include <CarrierHeatpumpIR.h>
//#include <MideaHeatpumpIR.h>
//#include <MitsubishiHeatpumpIR.h>
//#include <SamsungHeatpumpIR.h> // 测试版本 1.0.15,见 http://librarymanager#HeatpumpIR by Toni Arte
//#include <SharpHeatpumpIR.h>
//#include <DaikinHeatpumpIR.h>
//一些全局变量用于保存发送到空调单元的数值状态
int POWER_STATE;
int TEMP_STATE;
int FAN_STATE;
int MODE_STATE;
int VDIR_STATE;
int HDIR_STATE;
// 一些全局变量用于保存发送到 Home Assistant 控制器的文本状态
String FAN_STATE_TXT; // 可能值(“Min”,“Normal”,“Max”,“Auto”)
String MODE_STATE_TXT; // 可能值(“Off”,“HeatOn”,“CoolOn”或“AutoChangeOver”)
IRSenderPWM irSender(IR_PIN);
// 更改为您自己的热泵
//HeatpumpIR *heatpumpIR = new SamsungAQV12MSANHeatpumpIR();
/*
new PanasonicDKEHeatpumpIR()
new PanasonicJKEHeatpumpIR()
new PanasonicNKEHeatpumpIR()
new CarrierHeatpumpIR()
new MideaHeatpumpIR()
new FujitsuHeatpumpIR()
new MitsubishiFDHeatpumpIR()
new MitsubishiFEHeatpumpIR()
new SamsungAQVHeatpumpIR()
new SamsungFJMHeatpumpIR()
// new SamsungHeatpumpIR() 是一个受保护的通用方法,无法直接创建
new SharpHeatpumpIR()
new DaikinHeatpumpIR()
*/
MyMessage msgHVACSetPointC(CHILD_ID_HVAC, V_HVAC_SETPOINT_COOL);
MyMessage msgHVACSpeed(CHILD_ID_HVAC, V_HVAC_SPEED);
MyMessage msgHVACFlowState(CHILD_ID_HVAC, V_HVAC_FLOW_STATE);
bool initialValueSent = false;
void presentation() {
// 向网关和控制器发送草图版本信息
sendSketchInfo(SENSOR_NAME, SENSOR_VERSION);
// 按照其 ID 和 S_TYPE 将所有传感器注册到网关(它们将作为子设备创建)
present(CHILD_ID_HVAC, S_HVAC, "Thermostat");
}
void setup() {
}
void loop() {
// 在此处放置您的主代码,以便重复运行:
if (!initialValueSent) {
FAN_STATE_TXT = "Auto"; // 默认风扇启动状态
TEMP_STATE = 20; // 默认开始温度
MODE_STATE_TXT = "Off"; // 默认模式状态
send(msgHVACSetPointC.set(TEMP_STATE));
send(msgHVACSpeed.set(FAN_STATE_TXT.c_str()));
send(msgHVACFlowState.set(MODE_STATE_TXT.c_str()));
initialValueSent = true;
}
}
void receive(const MyMessage &message) {
if (message.isAck()) {
Serial.println("这是来自网关的确认");
return;
}
Serial.print("接收到的消息: ");
Serial.print(message.sensor);
String recvData = message.data;
recvData.trim();
Serial.print(", 新状态: ");
Serial.println(recvData);
switch (message.type) {
case V_HVAC_SPEED:
Serial.println("V_HVAC_SPEED");
if(recvData.equalsIgnoreCase("auto")) FAN_STATE = 0;
else if(recvData.equalsIgnoreCase("min")) FAN_STATE = 1;
else if(recvData.equalsIgnoreCase("normal")) FAN_STATE = 2;
else if(recvData.equalsIgnoreCase("max")) FAN_STATE = 3;
FAN_STATE_TXT = recvData;
break;
case V_HVAC_SETPOINT_COOL:
Serial.println("V_HVAC_SETPOINT_COOL");
TEMP_STATE = message.getFloat();
Serial.println(TEMP_STATE);
break;
case V_HVAC_FLOW_STATE:
Serial.println("V_HVAC_FLOW_STATE");
if (recvData.equalsIgnoreCase("coolon")) {
POWER_STATE = 1;
MODE_STATE = MODE_COOL;
}
else if (recvData.equalsIgnoreCase("heaton")) {
POWER_STATE = 1;
MODE_STATE = MODE_HEAT;
}
else if (recvData.equalsIgnoreCase("autochangeover")) {
POWER_STATE = 1;
MODE_STATE = MODE_AUTO;
}
else if (recvData.equalsIgnoreCase("off")){
POWER_STATE = 0;
}
MODE_STATE_TXT = recvData;
break;
}
sendHeatpumpCommand();
sendNewStateToGateway();
}
void sendNewStateToGateway() {
Serial.println("状态更新发送到 HA:");
Serial.println("*************************");
Serial.println("模式 = " + MODE_STATE_TXT + "(" + (String)MODE_STATE + ")");
Serial.println("风扇 = " + FAN_STATE_TXT + "(" + (String)FAN_STATE + ")");
Serial.println("温度 = " + (String)TEMP_STATE);
send(msgHVACFlowState.set(MODE_STATE_TXT.c_str()));
send(msgHVACSpeed.set(FAN_STATE_TXT.c_str()));
send(msgHVACSetPointC.set(TEMP_STATE));
}
void sendHeatpumpCommand() {
Serial.println("热泵命令发送到空调:");
Serial.println("********************************");
Serial.println("电源 = " + (String)POWER_STATE);
Serial.println("模式 = " + (String)MODE_STATE);
Serial.println("风扇 = " + (String)FAN_STATE);
Serial.println("温度 = " + (String)TEMP_STATE);
heatpumpIR->send(irSender, POWER_STATE, MODE_STATE, FAN_STATE, TEMP_STATE, VDIR_AUTO, HDIR_AUTO);
}
遮罩
支持以下执行器类型:
MySensors 版本 1.4
S_TYPE | V_TYPE |
---|---|
S_COVER | V_UP, V_DOWN, V_STOP, [V_DIMMER 或 V_LIGHT] |
MySensors 版本 1.5 及更高版本
S_TYPE | V_TYPE |
---|---|
S_COVER | V_UP, V_DOWN, V_STOP, [V_PERCENTAGE 或 V_STATUS] |
以上所有 V_TYPES 都是必需的。如果您知道遮罩的确切位置,请使用 V_PERCENTAGE(或 V_DIMMER),如果您不知道,请使用 V_STATUS(或 V_LIGHT)。
遮罩示例草图
/*
* 文档: https://www.mysensors.org
* 支持论坛: https://forum.mysensors.org
*/
// 启用调试打印到串行监视器
#define MY_DEBUG
#define MY_RADIO_NRF24
#include <MySensors.h>
#define SN "Cover"
#define SV "1.1"
// 用于上下移动遮罩的执行器。
#define COVER_UP_ACTUATOR_PIN 2
#define COVER_DOWN_ACTUATOR_PIN 3
// 用于查找遮罩何时达到其上/下位置的传感器。
// 这些可以是简单的按钮或线性霍尔传感器。
#define COVER_UP_SENSOR_PIN 4
#define COVER_DOWN_SENSOR_PIN 5
#define CHILD_ID 0
// 遮罩状态的内部表示。
enum State {
IDLE,
UP, // 窗户遮盖。向上。
DOWN, // 窗户遮盖。向下。
};
static int state = IDLE;
static int status = 0; // 0=遮罩在下,1=遮罩在上
static bool initial_state_sent = false;
MyMessage upMessage(CHILD_ID, V_UP);
MyMessage downMessage(CHILD_ID, V_DOWN);
MyMessage stopMessage(CHILD_ID, V_STOP);
MyMessage statusMessage(CHILD_ID, V_STATUS);
void sendState() {
// 将当前状态和状态发送到网关。
send(upMessage.set(state == UP));
send(downMessage.set(state == DOWN));
send(stopMessage.set(state == IDLE));
send(statusMessage.set(status));
}
void setup() {
pinMode(COVER_UP_SENSOR_PIN, INPUT);
pinMode(COVER_DOWN_SENSOR_PIN, INPUT);
}
void presentation() {
sendSketchInfo(SN, SV);
present(CHILD_ID, S_COVER);
}
void loop() {
if (!initial_state_sent) {
sendState();
initial_state_sent = true;
}
if (state == IDLE) {
digitalWrite(COVER_UP_ACTUATOR_PIN, LOW);
digitalWrite(COVER_DOWN_ACTUATOR_PIN, LOW);
}
if (state == UP && digitalRead(COVER_UP_SENSOR_PIN) == HIGH) {
Serial.println("遮罩向上。");
// 更新状态并状态;发送到网关。
status = 1;
state = IDLE;
sendState();
// 执行器将在下一个 loop() 迭代中被禁用。
}
if (state == DOWN && digitalRead(COVER_DOWN_SENSOR_PIN) == HIGH) {
Serial.println("遮罩向下。");
// 更新状态并状态;发送到网关。
status = 0;
state = IDLE;
sendState();
// 执行器将在下一个 loop() 迭代中被禁用。
}
}
void receive(const MyMessage &message) {
if (message.type == V_UP) {
// 设置状态为遮罩向上并发送回网关。
state = UP;
sendState();
Serial.println("正在移动遮罩向上。");
// 激活执行器,直到传感器在 loop() 中返回 HIGH。
digitalWrite(COVER_UP_ACTUATOR_PIN, HIGH);
}
if (message.type == V_DOWN) {
// 设置状态为遮罩向下并发送到网关。
state = DOWN;
sendState();
Serial.println("正在移动遮罩向下。");
// 激活执行器,直到传感器在 loop() 中返回 HIGH。
digitalWrite(COVER_DOWN_ACTUATOR_PIN, HIGH);
}
if (message.type == V_STOP) {
// 设置状态为闲置并发送回网关。
state = IDLE;
sendState();
Serial.println("停止遮罩。");
// 执行器将在 loop() 中关闭。
}
}
基于电机运行时间进行位置测量的遮罩示例草图
此草图理想用于星形拓扑布线。您可以使用一个 Arduino Mega 板和一些继电器运行多达 12 个遮罩。您只需为一个遮罩设置一行参数。不过,您也可以在基于 Arduino Nano 或 ESP8266 板的单个遮罩上使用它。
设备追踪器
支持以下传感器类型:
MySensors 版本 2.0 及更高版本
S_TYPE | V_TYPE |
---|---|
S_GPS | V_POSITION |
MySensors 2.x 的设备追踪器示例草图
/**
* 文档: https://www.mysensors.org
* 支持论坛: https://forum.mysensors.org
*
* https://www.mysensors.org/build/gps
*/
// 启用调试打印到串行监视器
#define MY_DEBUG
#define MY_RADIO_NRF24
#include <MySensors.h>
#define SN "GPS Sensor"
#define SV "1.0"
// GPS 位置发送间隔(以毫秒为单位)
#define GPS_SEND_INTERVAL 30000
// 用于 GPS 传感器的儿童 ID
#define CHILD_ID_GPS 1
MyMessage msg(CHILD_ID_GPS, V_POSITION);
// 上次 GPS 位置发送到控制器的时间
unsigned long lastGPSSent = -31000;
// 一些缓存
char latBuf[11];
char lngBuf[11];
char altBuf[6];
char payload[30];
// 虚拟值。真实 GPS 设备的实现尚未完成。
float gpsLocationLat = 40.741895;
float gpsLocationLng = -73.989308;
float gpsAltitudeMeters = 12.0;
void setup() {
}
void presentation() {
sendSketchInfo(SN, SV);
present(CHILD_ID_GPS, S_GPS);
}
void loop()
{
unsigned long currentTime = millis();
// 评估是否是发送新位置的时间
bool timeToSend = currentTime - lastGPSSent > GPS_SEND_INTERVAL;
if (timeToSend) {
// 发送当前 GPS 位置
// 构建要发送的位置和高度字符串
dtostrf(gpsLocationLat, 1, 6, latBuf);
dtostrf(gpsLocationLng, 1, 6, lngBuf);
dtostrf(gpsAltitudeMeters, 1, 0, altBuf);
sprintf(payload, "%s,%s,%s", latBuf, lngBuf, altBuf);
Serial.print(F("位置: "));
Serial.println(payload);
send(msg.set(payload));
lastGPSSent = currentTime;
}
}
灯光
支持以下执行器类型:
MySensors 版本 1.4
S_TYPE | V_TYPE |
---|---|
S_DIMMER | V_DIMMER*, V_LIGHT* |
MySensors 版本 1.5 及更高版本
S_TYPE | V_TYPE |
---|---|
S_DIMMER | [V_DIMMER* 或 V_PERCENTAGE*], [V_LIGHT* 或 V_STATUS*] |
S_RGB_LIGHT | V_RGB*, [V_LIGHT* 或 V_STATUS*], [V_DIMMER 或 V_PERCENTAGE] |
S_RGBW_LIGHT | V_RGBW*, [V_LIGHT* 或 V_STATUS*], [V_DIMMER 或 V_PERCENTAGE] |
带星号(*)的 V_TYPES 表示应在草图启动时发送的 V_TYPES。对于 S_DIMMER,发送 V_DIMMER/V_PERCENTAGE 和 V_LIGHT/V_STATUS 消息。对于 S_RGB_LIGHT,发送 V_RGB 和 V_LIGHT/V_STATUS 消息,V_DIMMER/V_PERCENTAGE 消息可选。相同的原则适用于 S_RGBW_LIGHT 和 V_RGBW。
草图应该以相同类型确认来自控制器的命令。如果命令引发关闭状态更改(包括 V_PERCENTAGE,V_RGB 或 V_RGBW 消息值为零),则应该仅发送 V_STATUS 为零的消息。请参见下面的草图示例。
MySensors 2.x 的灯光示例草图
/*
* 示例可调光灯
* 代码来自 https://github.com/mysensors/MySensors/tree/master/examples/DimmableLight
*
* 文档: https://www.mysensors.org
* 支持论坛: https://forum.mysensors.org
*
*/
// 启用调试打印
#define MY_DEBUG
#define MY_RADIO_NRF24
#include <MySensors.h>
#define CHILD_ID_LIGHT 1
#define LIGHT_OFF 0
#define LIGHT_ON 1
#define SN "Dimmable Light"
#define SV "1.0"
int16_t last_state = LIGHT_ON;
int16_t last_dim = 100;
MyMessage light_msg( CHILD_ID_LIGHT, V_STATUS );
MyMessage dimmer_msg( CHILD_ID_LIGHT, V_PERCENTAGE );
void setup()
{
update_light();
Serial.println( "节点准备接收消息..." );
}
void loop()
{
// 在 MySensors2.x 中,第一条消息必须来自 loop() 内部
static bool first_message_sent = false;
if ( first_message_sent == false ) {
Serial.println( "发送初始状态..." );
send_dimmer_message();
send_status_message();
first_message_sent = true;
}
}
void presentation()
{
// 将草图版本信息发送到网关
sendSketchInfo( SN, SV );
present( CHILD_ID_LIGHT, S_DIMMER );
}
void receive(const MyMessage &message)
{
// 接收到 V_STATUS 命令时,在关闭状态与最后接收到的调光值间切换灯光
if ( message.type == V_STATUS ) {
Serial.println( "接收到 V_STATUS 命令..." );
int lstate = message.getInt();
if (( lstate < 0 ) || ( lstate > 1 )) {
Serial.println( "V_STATUS 数据无效(应为 0/1)" );
return;
}
last_state = lstate;
// 如果最后调光状态为零,则将调光器设置为 100
if (( last_state == LIGHT_ON ) && ( last_dim == 0 )) {
last_dim=100;
}
// 更新控制器状态
send_status_message();
} else if ( message.type == V_PERCENTAGE ) {
Serial.println( "接收到 V_PERCENTAGE 命令..." );
int dim_value = constrain( message.getInt(), 0, 100 );
if ( dim_value == 0 ) {
last_state = LIGHT_OFF;
// 更新控制器的调光值和状态
send_dimmer_message();
send_status_message();
} else {
last_state = LIGHT_ON;
last_dim = dim_value;
// 更新控制器的调光值
send_dimmer_message();
}
} else {
Serial.println( "接收到的命令无效..." );
return;
}
// 在这里设置实际的灯光状态/级别
update_light();
}
void update_light()
{
// 对于此示例,仅将灯光状态打印到控制台。
if ( last_state == LIGHT_OFF ) {
Serial.println( "灯光状态:关闭" );
} else {
Serial.print( "灯光状态:开启,级别: " );
Serial.println( last_dim );
}
}
void send_dimmer_message()
{
send( dimmer_msg.set( last_dim ) );
}
void send_status_message()
{
if ( last_state == LIGHT_OFF ) {
send( light_msg.set( (int16_t)0) );
} else {
send( light_msg.set( (int16_t)1) );
}
}
遥控器
支持以下类型组合:
MySensors 版本 1.4 和更高版本
S_TYPE | V_TYPE |
---|---|
S_IR | V_IR_SEND, V_LIGHT |
MySensors 版本 1.5 和更高版本
S_TYPE | V_TYPE |
---|---|
S_IR | V_IR_SEND, V_STATUS |
V_LIGHT 或 V_STATUS 是报告遥控器开 / 关状态所必需的。根据库版本使用 V_LIGHT 或 V_STATUS。
IR 收发器示例草图
/*
* 文档: https://www.mysensors.org
* 支持论坛: https://forum.mysensors.org
*
* https://www.mysensors.org/build/ir
*/
#include <MySensor.h>
#include <SPI.h>
#include <IRLib.h>
#define SN "IR Sensor"
#define SV "1.0"
#define CHILD_ID 1
MySensor gw;
char code[10] = "abcd01234";
char oldCode[10] = "abcd01234";
MyMessage msgCodeRec(CHILD_ID, V_IR_RECEIVE);
MyMessage msgCode(CHILD_ID, V_IR_SEND);
MyMessage msgSendCode(CHILD_ID, V_LIGHT);
void setup()
{
gw.begin(incomingMessage);
gw.sendSketchInfo(SN, SV);
gw.present(CHILD_ID, S_IR);
// 发送初始值。
gw.send(msgCodeRec.set(code));
gw.send(msgCode.set(code));
gw.send(msgSendCode.set(0));
}
void loop()
{
gw.process();
// IR 接收器未实现,仅在代码更改时虚拟报告
if (String(code) != String(oldCode)) {
Serial.print("接收到代码 ");
Serial.println(code);
gw.send(msgCodeRec.set(code));
strcpy(oldCode, code);
}
}
void incomingMessage(const MyMessage &message) {
if (message.type==V_LIGHT) {
// IR 发送器未实现,仅打印虚拟信息。
if (message.getBool()) {
Serial.print("发送代码 ");
Serial.println(code);
}
gw.send(msgSendCode.set(message.getBool() ? 1 : 0));
// 始终关闭设备
gw.wait(100);
gw.send(msgSendCode.set(0));
}
if (message.type == V_IR_SEND) {
// 从传入消息中检索 IR 代码值。
String codestring = message.getString();
codestring.toCharArray(code, sizeof(code));
Serial.print("更改代码为 ");
Serial.println(code);
gw.send(msgCode.set(code));
}
}
传感器
支持以下传感器类型:
MySensors 版本 1.4 及更高版本
S_TYPE | V_TYPE |
---|---|
S_TEMP | V_TEMP |
S_HUM | V_HUM |
S_BARO | V_PRESSURE, V_FORECAST |
S_WIND | V_WIND, V_GUST, V_DIRECTION |
S_RAIN | V_RAIN, V_RAINRATE |
S_UV | V_UV |
S_WEIGHT | V_WEIGHT, V_IMPEDANCE |
S_POWER | V_WATT, V_KWH |
S_DISTANCE | V_DISTANCE |
S_LIGHT_LEVEL | V_LIGHT_LEVEL |
S_IR | V_IR_RECEIVE |
S_WATER | V_FLOW, V_VOLUME |
S_AIR_QUALITY | V_DUST_LEVEL |
S_CUSTOM | V_VAR1, V_VAR2, V_VAR3, V_VAR4, V_VAR5 |
S_DUST | V_DUST_LEVEL |
S_SCENE_CONTROLLER | V_SCENE_ON, V_SCENE_OFF |
MySensors 版本 1.5 及更高版本
S_TYPE | V_TYPE |
---|---|
S_COLOR_SENSOR | V_RGB |
S_MULTIMETER | V_VOLTAGE, V_CURRENT, V_IMPEDANCE |
S_SOUND | V_LEVEL |
S_VIBRATION | V_LEVEL |
S_MOISTURE | V_LEVEL |
S_LIGHT_LEVEL | V_LEVEL |
S_AIR_QUALITY | V_LEVEL (替代 V_DUST_LEVEL) |
S_DUST | V_LEVEL (替代 V_DUST_LEVEL) |
MySensors 版本 2.0 及更高版本
S_TYPE | V_TYPE |
---|---|
S_INFO | V_TEXT |
S_GAS | V_FLOW, V_VOLUME |
S_GPS | V_POSITION |
S_IR | V_IR_RECORD |
S_WATER_QUALITY | V_TEMP, V_PH, V_ORP, V_EC |
自定义测量单位
某些传感器值类型并不特定于某种传感器类型。这些没有 Home Assistant 中的默认测量单位。例如,V_LEVEL 类型可以用于不同的传感器类型,如灰尘、声音、振动等。
通过使用 V_UNIT_PREFIX,可以为任何传感器设置自定义单位。发送 V_UNIT_PREFIX 的字符串值将优先于为定义的传感器使用任何其他测量单位。V_UNIT_PREFIX 不能作为独立的传感器值类型使用。还必须从上述表中发送受支持的值类型和值。V_UNIT_PREFIX 从 MySensors 版本 1.5 开始可用。
MySensors 2.x 的传感器示例草图
/**
* 文档: https://www.mysensors.org
* 支持论坛: https://forum.mysensors.org
*
* https://www.mysensors.org/build/light
*/
// 启用调试打印到串行监视器
#define MY_DEBUG
#define MY_RADIO_NRF24
#include <BH1750.h>
#include <Wire.h>
#include <MySensors.h>
#define SN "LightLuxSensor"
#define SV "1.0"
#define CHILD_ID 1
unsigned long SLEEP_TIME = 30000; // 读取之间的睡眠时间(以毫秒为单位)
BH1750 lightSensor;
MyMessage msg(CHILD_ID, V_LEVEL);
MyMessage msgPrefix(CHILD_ID, V_UNIT_PREFIX); // 自定义单位消息。
uint16_t lastlux = 0;
bool initialValueSent = false;
void setup()
{
sendSketchInfo(SN, SV);
present(CHILD_ID, S_LIGHT_LEVEL);
lightSensor.begin();
}
void loop()
{
if (!initialValueSent) {
Serial.println("发送初始值");
send(msgPrefix.set("custom_lux")); // 设置自定义单位。
send(msg.set(lastlux));
Serial.println("请求控制器的初始值");
request(CHILD_ID, V_LEVEL);
wait(2000, C_SET, V_LEVEL);
}
uint16_t lux = lightSensor.readLightLevel(); // 获取 Lux 值
if (lux != lastlux) {
send(msg.set(lux));
lastlux = lux;
}
sleep(SLEEP_TIME);
}
void receive(const MyMessage &message) {
if (message.type == V_LEVEL) {
if (!initialValueSent) {
Serial.println("接收到来自控制器的初始值");
initialValueSent = true;
}
}
}
开关
支持以下执行器类型:
MySensors 版本 1.4 及更高版本
S_TYPE | V_TYPE |
---|---|
S_DOOR | V_ARMED |
S_MOTION | V_ARMED |
S_SMOKE | V_ARMED |
S_LIGHT | V_LIGHT |
S_LOCK | V_LOCK_STATUS |
MySensors 版本 1.5 及更高版本
S_TYPE | V_TYPE |
---|---|
S_LIGHT | V_STATUS |
S_BINARY | [V_STATUS 或 V_LIGHT] |
S_SPRINKLER | V_STATUS |
S_WATER_LEAK | V_ARMED |
S_SOUND | V_ARMED |
S_VIBRATION | V_ARMED |
S_MOISTURE | V_ARMED |
MySensors 版本 2.0 及更高版本
S_TYPE | V_TYPE |
---|---|
S_WATER_QUALITY | V_STATUS |
以上每个 S_TYPE 的所有 V_TYPES 都是必需的,以便激活该平台的执行器。在需要此 V_TYPE 的情况下,根据库版本使用 V_LIGHT 或 V_STATUS。
开关示例草图
/*
* 文档: https://www.mysensors.org
* 支持论坛: https://forum.mysensors.org
*
* https://www.mysensors.org/build/relay
*/
#include <MySensor.h>
#include <SPI.h>
#define SN "Relay"
#define SV "1.0"
#define CHILD_ID 1
#define RELAY_PIN 3
MySensor gw;
MyMessage msgRelay(CHILD_ID, V_STATUS);
void setup()
{
gw.begin(incomingMessage);
gw.sendSketchInfo(SN, SV);
// 初始化数字引脚为输出。
pinMode(RELAY_PIN, OUTPUT);
gw.present(CHILD_ID, S_BINARY);
gw.send(msgRelay.set(0));
}
void loop()
{
gw.process();
}
void incomingMessage(const MyMessage &message)
{
if (message.type == V_STATUS) {
// 更改继电器状态。
digitalWrite(RELAY_PIN, message.getBool() ? 1 : 0);
gw.send(msgRelay.set(message.getBool() ? 1 : 0));
}
}
文本
支持以下传感器类型:
MySensors 版本 2.0 及更高版本
S_TYPE | V_TYPE |
---|---|
S_INFO | V_TEXT |
文本示例草图
/*
* 文档: https://www.mysensors.org
* 支持论坛: https://forum.mysensors.org
*/
// 启用调试打印到串行监视器
#define MY_DEBUG
#define MY_RADIO_NRF24
#include <MySensors.h>
#include <SPI.h>
#define SN "TextSensor"
#define SV "1.0"
#define CHILD_ID 1
MyMessage textMsg(CHILD_ID, V_TEXT);
bool initialValueSent = false;
void setup(void) {
}
void presentation() {
sendSketchInfo(SN, SV);
present(CHILD_ID, S_INFO, "TextSensor1");
}
void loop() {
if (!initialValueSent) {
Serial.println("发送初始值");
// 发送初始值。
send(textMsg.set("-"));
Serial.println("请求控制器的初始值");
request(CHILD_ID, V_TEXT);
wait(2000, C_SET, V_TEXT);
}
}
void receive(const MyMessage &message) {
if (message.type == V_TEXT) {
if (!initialValueSent) {
Serial.println("接收到来自控制器的初始值");
initialValueSent = true;
}
// 虚拟打印
Serial.print("消息: ");
Serial.print(message.sensor);
Serial.print(", 消息: ");
Serial.println(message.getString());
// 发送消息到控制器
send(textMsg.set(message.getString()));
}
}