建立连接后,MQTT 设备在 Home Assistant 中显示为离线

物联网 MQTT 阿杜伊诺 蚊子 家庭助理
2021-06-26 13:03:08

我正在尝试创建一个车库门传感器/电机控制器并将其连接到我的 Home Assistant 实例。我的经纪人是树莓派上的本地蚊子。

该设备是 Wemos D1 mini,继电器连接到 D1 引脚和模拟输入以确定门的当前状态。

问题在于,一旦与代理建立 MQTT 连接,设备就会一直出现不可用状态。我在日志中看不到任何提示问题的内容。最重要的是,当它是offline.

这是我用于 LWT/可用性主题的特定代码。

String clientId = "GarageDoorSensor-" + String(random(0xffff), HEX);
if (client.connect(clientId.c_str(), mqtt_user, mqtt_password, availabilityTopic, 0, true, payloadNotAvailable)) {
  Serial.println("connected");
  client.publish(availabilityTopic, payloadAvailable, true);  
          
  client.subscribe(commandTopic);
          
  Serial.println("Subscribed to: ");
  Serial.println(commandTopic);
  Serial.println(availabilityTopic);
   
  getAndSendDoorStatus();
}

这是完整的代码:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "My_Helper.h"

WiFiClient espClient;
PubSubClient client(espClient);

void setup_wifi() {

  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  //Set WiFi mode so we don't create an access point.
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  Serial.println(WiFi.mode(WIFI_STA));
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  randomSeed(micros());
  
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    String clientId = "GarageDoorSensor-" + String(random(0xffff), HEX);
    if (client.connect(clientId.c_str(), mqtt_user, mqtt_password, availabilityTopic, 0, true, payloadNotAvailable)) {
      Serial.println("connected");
      client.publish(availabilityTopic, payloadAvailable, true);  
      
      client.subscribe(commandTopic);
      
      Serial.println("Subscribed to: ");
      Serial.println(commandTopic);
      Serial.println(availabilityTopic);

      getAndSendDoorStatus();
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


void setup() {

  pinMode(relaySwitch, OUTPUT);

  pinMode(doorInput, INPUT);
  
  Serial.begin(115200);
  // put your setup code here, to run once:
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  while (!client.connected()) {
    reconnect();
  }

  StaticJsonDocument<1024> mqttConfig;
  mqttConfig["name"] = mqttDeviceName;
  mqttConfig["device_class"] = mqttDeviceClass; 
  mqttConfig["state_topic"] = stateTopic;
  mqttConfig["command_topic"] = commandTopic; 
  mqttConfig["state_open"] = opened;
  mqttConfig["state_closed"] = closed;
  mqttConfig["state_closing"] = closing;
  mqttConfig["state_opening"] = opening;
  mqttConfig["payload_open"] = payloadOpen;
  mqttConfig["payload_close"] = payloadClose;
  mqttConfig["payload_stop"] = payloadStop;
  mqttConfig["optimistic"] = false;
  mqttConfig["retain"] = true;
  mqttConfig["availability_topic"] = availabilityTopic;
  mqttConfig["payload_available"] = payloadAvailable;
  mqttConfig["payload_not_available"] = payloadNotAvailable;
  char json[1024];
  serializeJsonPretty(mqttConfig, json);
  client.publish(configTopic, json, true); 
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  
  delay(1000);
  getAndSendDoorStatus();
}

void callback(char* topic, byte* message, unsigned int length) {

  String messageStr;
  
  for (int i = 0; i < length; i++) {
    messageStr += (char)message[i];
  }
  
  if (String(topic) == commandTopic) {
     Serial.print("Home Assistant Command: ");
     Serial.println(messageStr);

     if((messageStr == payloadOpen && doorStatus != opened) || messageStr == "forceOpen"){
        //open door is not already open or we are running a test.
        openTheDoor();
     }else if((messageStr == payloadClose && doorStatus != closed) || messageStr == "forceClose"){
        //close door is not already closed or we are running a test
        closeTheDoor(); 
     }else if(messageStr == payloadStop){

        //make sure we undo the relevant switches to stop the motion based on the previous status
        if(doorStatus == opened){
          closeTheDoor();
        }else if(doorStatus == closed){
          openTheDoor();
        }
    }

    prevDoorStatus = doorStatus;

    if(messageStr == "incrementOpenThreshold"){
      openThreshold = openThreshold + 1;
      String msg = "Set open threshold to: " + openThreshold;
      client.publish(stateTopic, msg.c_str(), true); 
    }

    if(messageStr == "decrementOpenThreshold"){
      openThreshold = openThreshold - 1;
      String msg = "Set open threshold to: " + openThreshold;
      client.publish(stateTopic, msg.c_str(), true); 
    }

  }
}

void getAndSendDoorStatus(){
  int doorState = analogRead(doorInput);

  //Door fully open?
  if(doorState < openThreshold){
    statusToOpen();
  } else {
    statusToClosed();
  }
 
  if(prevDoorStatus != doorStatus){
    Serial.print("Door ");
    Serial.println(doorStatus);
    client.publish(stateTopic, doorStatus, true); 

    delay(500);
    if(doorStatus == opened){
       openTheDoor(); 
    } else {
       closeTheDoor();
    }
    
    prevDoorStatus = doorStatus;
  } 
  
}

void closeTheDoor(){
  digitalWrite(relaySwitch, closeDoor);
}

void statusToClosed(){
  doorStatus = closed;
}

void openTheDoor(){
  digitalWrite(relaySwitch, openDoor);
}

void statusToOpen(){
  doorStatus = opened;  
}

MY_HELPER.H

#ifndef MY_HELPER_H
#define MY_HELPER_H

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
const char* mqtt_user = "YOUR_MQTT_USER";
const char* mqtt_password = "YOUR_MQTT_PASSWORD";
const char* mqtt_server = "YOUR_MQTT_SERVER";
const char* stateTopic = "homeassistant/cover/garage/door/state";
const char* commandTopic = "homeassistant/cover/garage/door/set";
const char* configTopic = "homeassistant/cover/garage/door/config";
const char* availabilityTopic = "homeassistant/cover/garage/door/availability";
const char* doorStatus = "";
const char* prevDoorStatus = "";

const char* mqttDeviceName = "Garage Door";
const char* mqttDeviceClass = "garage";

int prevDoorState;
int openThreshold = 15;

const int relaySwitch = D1;

const int doorInput = A0;

const int conexT1 = LOW;
const int conexT2 = HIGH;

const int openDoor = conexT1;
const int closeDoor = conexT2;

//Statuses
const char* opened = "open";
const char* closed = "closed";
const char* closing = "closing";
const char* opening = "opening";
const char* stopped = "stopped";

//pay loads
const char* payloadOpen = "OPEN";
const char* payloadClose = "CLOSE";
const char* payloadStop = "STOP";
const char* payloadAvailable = "online";
const char* payloadNotAvailable = "offline";

#endif
1个回答

首先,如果您只有 1 个车库门开启器,为什么每次都生成一个随机的客户端 ID?在这里使用固定的 clientID 是正确的做法。

唯一有意义的随机客户端 ID 是当您使用临时客户端时,例如通过 Websocket 使用 MQTT 的网页。

其次,您似乎在双重循环重新连接功能。一旦setup()loop()然后再在reconnect()您应该只需要client.connected()reconnect()函数中可能的条件上循环一次

在代理上打开详细日志记录并查看代理在发布 LWT 时认为发生了什么。

同时跟随与mosquitto_sub将是有用的。