找回密码
 立即注册
查看: 143|回复: 7

[求助] 我的ha为什么不能自动发现MQTT设备

[复制链接]

1

主题

0

回帖

57

积分

注册会员

积分
57
金钱
56
HASS币
0
发表于 昨天 17:46 | 显示全部楼层 |阅读模式

/*
ESP32通过wifi连接MQTT,并订阅发布信息控制相应的设备
*/

#include <WiFi.h>
#include <PubSubClient.h>

//Wifi参数
const char* ssid = "finance office";               //设置Wi-Fi名
const char* password = "XXX";           //设置Wi-Fi密码
// Mqtt Broker参数
const char* mqtt_server = "192.168.9.XXX"; //设置MQTT服务器地址
const char* mqtt_username = "mqtt" ;
const char* mqtt_password = "XXX" ;
const int mqtt_port = 1883 ;
// 定义MQTT主题前缀
// 设备标识
const char* deviceName = "Esp32_Mqtt_Controller";  //设备名
const char* deviceId = "Esp32S3_16R8-2BB77"; //设备ID
const char* discoveryPrefix = "homeassistant";  // HA自动发现前缀

// 定义开关数量和引脚
const int switchCount = 2;  // 开关数量
// 定义MQTT 实体参数
struct SwitchConfig {
  const char* name;
  const char* uniqueId;
  const char* commandTopic;
  const char* stateTopic;
  int gpioPin;
  bool state;
};
SwitchConfig switches[2] = {
  {
    "Esp32_Switch1",         // 开关1名称
    "Esp32_Mqtt_Switch1",         // 唯一ID
    "homeassistant/switch/switch1/cmd",// 命令主题
    "homeassistant/switch/switch1/state",// 状态主题
    1,                      // GPIO引脚
    false                    // 初始状态
  },
  {
    "Esp32_Switch2",       // 开关2名称
    "Esp32_Mqtt_Switch2",         // 唯一ID
    "homeassistant/switch/switch2/cmd",// 命令主题
    "homeassistant/switch/switch2/state",// 状态主题
    2,                      // GPIO引脚
    false                    // 初始状态
  }
};
//

WiFiClient espClient;
PubSubClient client(espClient);


//连接wifi
void setup_wifi() {
  delay(10);
  Serial.println("Connect WiFi: " + String(ssid));
  WiFi.begin(ssid, password);
  //等待WiFi连接
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print("connect Wifi... ");
  }
  //wifi连接成功后输出成功信息
  Serial.println("WiFi Connected! ");   //显示wifi连接成功
  Serial.println("IP: " + String(WiFi.localIP()));       //返回wifi分配的IP
  Serial.println("MAC: " + String(WiFi.macAddress()));  //返回设备的MAC地址
  Serial.println("");
  randomSeed(micros());
}

//回调函数
void callback(char* topic, byte* payload, unsigned int length) {
   // 将payload转换为字符串
  String message;
  for (int i = 0; i < length; i++) {
    message += (char)payload;
  }
  Serial.print("Received[");
  Serial.print(topic);
  Serial.print("] ");
  Serial.println(message);

// 检查所有控制主题
  for (int i = 0; i < switchCount; i++) {
    if (String(topic) == switches.commandTopic) {
      if (message == "on" || message == "1") {
        digitalWrite(switches.gpioPin, HIGH);
        switches.state = true;
        Serial.println("开关" + String(switches.name) + " 打开");
        client.publish(switches.stateTopic, (String(switches.uniqueId) + ": ON").c_str());
      }
      else if (message == "off" || message == "0") {
        digitalWrite(switches.gpioPin, LOW);
        switches.state = false;        
        Serial.println("开关" + String(switches.name) + " 关闭");
        client.publish(switches.stateTopic, (String(switches.uniqueId) + ": OFF").c_str());
      }
      else if (message == "toggle") {
        digitalWrite(switches.gpioPin, !digitalRead(switches.gpioPin));
        String state = digitalRead(switches.gpioPin) ? "ON" : "OFF";
        Serial.println("开关" + String(switches.name) + " 切换: " + state);
        client.publish(switches.stateTopic, (String(switches.uniqueId) + ": " + state).c_str());
      }
      break; // 找到匹配主题后退出循环
    }
  }

}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Try Mqtt Connect... ");
    if (client.connect(deviceId, mqtt_username, mqtt_password)) {
      Serial.println("Mqtt Connected");

      // 订阅所有控制主题
      for (int i = 0; i < switchCount; i++) {
        client.subscribe(switches.commandTopic);
        Serial.println("Subscribe topic: " + String(switches.commandTopic));
      }
      
      // 发送自动发现配置
      sendAutoDiscoveryConfig();

      // 发布初始状态
      for (int i = 0; i < 2; i++) {
        client.publish(switches.stateTopic, switches.state ? "ON" : "OFF", true);
      }
    } else {
      Serial.print("失败, rc=");
      Serial.print(client.state());
      Serial.println(" 5秒后重试...");
      delay(5000);
    }
  }
}

void sendAutoDiscoveryConfig() {
  for (int i = 0; i < switchCount; i++) {
    char discoveryTopic[150];
    snprintf(discoveryTopic, sizeof(discoveryTopic), "%s/switch/%s/%s/config",
             discoveryPrefix, deviceId, switches.name);
   
    String payload = "{";
    payload += "\"name\":\"" + String(switches.name) + "\",";
    payload += "\"unique_id\":\"" + String(switches.uniqueId) + "\",";
    payload += "\"command_topic\":\"" + String(switches.commandTopic) + "\",";
    payload += "\"state_topic\":\"" + String(switches.stateTopic) + "\",";
    payload += "\"availability_topic\":\"homeassistant/status\",";
    payload += "\"payload_available\":\"online\",";
    payload += "\"payload_not_available\":\"offline\",";
    payload += "\"device\":{";
    payload += "\"identifiers\":[\"" + String(deviceId) + "\"],";
    payload += "\"name\":\"" + String(deviceName) + "\",";
    payload += "\"model\":\"ESP32S3\",";   
    payload += "\"sw_version\":\"1.0\"";
    payload += "}";
    payload += "}";
   
    Serial.println(discoveryTopic);
    Serial.println(payload.c_str());   
    client.publish(discoveryTopic, payload.c_str(), true);
    Serial.printf("Discovery config sent for %s\n", switches.name);
  }
  // 发布在线状态
  client.publish("homeassistant/status", "online", true);
}

void setup() {
  // 初始化所有GPIO引脚
  for (int i = 0; i < switchCount; i++) {
    pinMode(switches.gpioPin, OUTPUT);
    digitalWrite(switches.gpioPin, LOW); // 初始状态为关闭
  }

  Serial.begin(115200);
  setup_wifi();                        //连接wifi

  client.setServer(mqtt_server, mqtt_port);     //设置MQTT服务器
  client.setCallback(callback);            //设置回调函数
}

void loop() {
  if (!client.connected()) {
    reconnect();              //尝试连接MQTT服务器并接收信息
  }
  client.loop();
}


回复

使用道具 举报

22

主题

551

回帖

3650

积分

论坛元老

积分
3650
金钱
3077
HASS币
30
发表于 昨天 18:37 | 显示全部楼层
要安装mqtt这个集成,然后填入你的mqtt服务器地址,用户名密码等,然后才能监听到吧
回复

使用道具 举报

44

主题

1856

回帖

7904

积分

元老级技术达人

积分
7904
金钱
5979
HASS币
146
发表于 昨天 18:58 | 显示全部楼层
学会监听调试,你贴的代码,不是给大家布置作业,帮你找问题的
这些代码需要环境进行测试的。
1、论坛中习惯性使用封装好的esphome进行开发。
2、C的这些,要帮你到idf下调试才行。
冬瓜版HAOS从现在开始!
回复

使用道具 举报

jjcs 手机认证

52

主题

1934

回帖

7860

积分

论坛元老

积分
7860
金钱
5864
HASS币
40
发表于 昨天 19:31 | 显示全部楼层
自动发现需要严格按照ha的格式来,日志debuag把
折腾精神永存,感恩感谢论坛每一位愿意分享和帮助过我的大佬,论坛有你更精彩
回复

使用道具 举报

1

主题

164

回帖

2064

积分

金牌会员

积分
2064
金钱
1899
HASS币
0
发表于 昨天 20:35 | 显示全部楼层
这个是我用谷歌的那个ai优化过后的red流程中合并数据和发送mqtt的,你看下对你有没有帮助吧,我不懂,但是ai会给我写。。
const usersData = msg.payload;
const baseTopic = "homeassistant/sensor";
const commonDeviceClass = "monetary";
const commonUnit = "元";

function createSensorConfig(userName, userData) {
    if (!userData || userData.error) {
        node.warn(`No data or error for ${userName}: ${JSON.stringify(userData)}`);
        return null; // Skip config if no valid data
    }
    const uniqueId = `sensor_water_usage_${userName}`;
    const stateTopic = `${baseTopic}/water_${userName}/state`;
    const name = `Water Usage ${userName.charAt(0).toUpperCase() + userName.slice(1)}`; // e.g., Water Usage User1

    return {
        topic: `${baseTopic}/water_${userName}/config`,
        qos: 0,
        retain: true,
        payload: {
            name: name,
            stat_t: stateTopic, // state_topic
            json_attr_t: stateTopic, // json_attributes_topic
            dev_cla: commonDeviceClass, // device_class
            unit_of_meas: commonUnit, // unit_of_measurement
            val_tpl: "{{ value_json.total_amount }}", // value_template
            uniq_id: uniqueId, // unique_id
            // Optional: Device information for HA
            dev: {
                ids: [`water_meter_${userData.user_no || userName}`],
                name: `Water Meter ${userData.user_no || userName}`,
                mf: "WaterCMS Scraper",
                mdl: "Virtual Water Meter"
            }
        }
    };
}

function createStateMessage(userName, userData) {
    if (!userData || userData.error) {
        return null; // Skip state if no valid data
    }
    const stateTopic = `${baseTopic}/water_${userName}/state`;
    return {
        topic: stateTopic,
        qos: 0,
        retain: true, // Retain state so HA gets it on restart
        payload: userData // Payload is already an object, MQTT node will stringify if not configured for objects
    };
}

// Send config and state for each user
for (const userName in usersData) {
    if (usersData.hasOwnProperty(userName)) {
        const userData = usersData[userName];
        
        const configMsg = createSensorConfig(userName, userData);
        const stateMsg = createStateMessage(userName, userData);

        if (configMsg) {
            // Ensure payload is stringified for config if not done by MQTT node
            configMsg.payload = JSON.stringify(configMsg.payload);
            node.send(configMsg);
        }
        if (stateMsg) {
            // Ensure payload is stringified for state if not done by MQTT node
            stateMsg.payload = JSON.stringify(stateMsg.payload);
            node.send(stateMsg);
        }
    }
}

return null; // All messages sent via node.send()
回复

使用道具 举报

innx 手机认证

25

主题

190

回帖

2814

积分

论坛DIY达人

积分
2814
金钱
2594
HASS币
30
发表于 昨天 21:10 | 显示全部楼层
用mqtt客户端监听下收到了哪些,并不是看代码能解决的。客户端,mqtt配置,服务器,权限等等都有可能
又是一个错误的提问方式
回复

使用道具 举报

2

主题

83

回帖

1244

积分

金牌会员

积分
1244
金钱
1159
HASS币
0
发表于 昨天 21:17 | 显示全部楼层
按冬瓜大佬说的 设置mqtt,填入你的mqtt服务器地址,用户名密码等,还有设置好tasmota就OK了
回复

使用道具 举报

shay 手机认证

14

主题

230

回帖

1490

积分

金牌会员

积分
1490
金钱
1241
HASS币
20
发表于 2 小时前 | 显示全部楼层
冬瓜HA 发表于 2025-6-12 18:58
学会监听调试,你贴的代码,不是给大家布置作业,帮你找问题的
这些代码需要环境进行测试的。
1、论坛中习 ...

虽然自己写代码成就感十足,但基本功能还是用冬瓜佬说的esphome吧,esphome和ha的结合度更好。即使使用了esphome不支持的传感器,开发也简单。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Hassbian ( 晋ICP备17001384号-1 )

GMT+8, 2025-6-13 17:24 , Processed in 1.458526 second(s), 22 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表