mirror of
https://github.com/mrcory/arduino-home-assistant.git
synced 2026-08-02 15:24:26 -04:00
migrate to PubSubClient
This commit is contained in:
+1
-1
@@ -7,4 +7,4 @@ paragraph=This library provides easy to use API for integrating your Arduin-base
|
||||
category=Communication
|
||||
url=https://github.com/dawidchyrzynski/arduino-home-assistant
|
||||
architectures=avr
|
||||
depends=MQTT
|
||||
depends=PubSubClient
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
#include "HADevice.h"
|
||||
#include "HAMqtt.h"
|
||||
#include "device-types/HATriggers.h"
|
||||
|
||||
+62
-26
@@ -1,3 +1,5 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "HADevice.h"
|
||||
|
||||
HADevice::HADevice(const char* uniqueId) :
|
||||
@@ -5,50 +7,84 @@ HADevice::HADevice(const char* uniqueId) :
|
||||
_manufacturer(nullptr),
|
||||
_model(nullptr),
|
||||
_name(nullptr),
|
||||
_softwareVersion(nullptr),
|
||||
_viaDevice(nullptr)
|
||||
_softwareVersion(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
String HADevice::serialize() const
|
||||
uint16_t HADevice::calculateSerializedLength() const
|
||||
{
|
||||
String output;
|
||||
output.concat("{");
|
||||
output.concat("{\"identifiers\":\"");
|
||||
output.concat(_uniqueId);
|
||||
output.concat("\"}");
|
||||
uint16_t size =
|
||||
3 + // opening and closing bracket + null terminator
|
||||
strlen(_uniqueId) + 8; // 8 - length of the JSON data for this field
|
||||
|
||||
if (_manufacturer != nullptr) {
|
||||
output.concat(",{\"manufacturer\":\"");
|
||||
output.concat(_manufacturer);
|
||||
output.concat("\"}");
|
||||
size += strlen(_manufacturer) + 8; // 8 - length of the JSON data for this field
|
||||
}
|
||||
|
||||
if (_model != nullptr) {
|
||||
output.concat(",{\"model\":\"");
|
||||
output.concat(_model);
|
||||
output.concat("\"}");
|
||||
size += strlen(_model) + 9; // 9 - length of the JSON data for this field
|
||||
}
|
||||
|
||||
if (_name != nullptr) {
|
||||
output.concat(",{\"name\":\"");
|
||||
output.concat(_name);
|
||||
output.concat("\"}");
|
||||
size += strlen(_name) + 10; // 10 - length of the JSON data for this field
|
||||
}
|
||||
|
||||
if (_softwareVersion != nullptr) {
|
||||
output.concat(",{\"sw_version\":\"");
|
||||
output.concat(_softwareVersion);
|
||||
output.concat("\"}");
|
||||
size += strlen(_softwareVersion) + 8; // 8 - length of the JSON data for this field
|
||||
}
|
||||
|
||||
if (_viaDevice != nullptr) {
|
||||
output.concat(",{\"via_device\":\"");
|
||||
output.concat(_viaDevice);
|
||||
output.concat("\"}");
|
||||
return size;
|
||||
}
|
||||
|
||||
uint16_t HADevice::serialize(char* output) const
|
||||
{
|
||||
static const char QuotationSign[] PROGMEM = {"\""};
|
||||
|
||||
{
|
||||
static const char DataBefore[] PROGMEM = {"{\"ids\":\""};
|
||||
|
||||
strcpy_P(output, DataBefore);
|
||||
strcat(output, _uniqueId);
|
||||
strcat_P(output, QuotationSign);
|
||||
}
|
||||
|
||||
if (_manufacturer != nullptr) {
|
||||
static const char DataBefore[] PROGMEM = {",\"mf\":\""};
|
||||
|
||||
strcat_P(output, DataBefore);
|
||||
strcat(output, _manufacturer);
|
||||
strcat_P(output, QuotationSign);
|
||||
}
|
||||
|
||||
if (_model != nullptr) {
|
||||
static const char DataBefore[] PROGMEM = {",\"mdl\":\""};
|
||||
|
||||
strcat_P(output, DataBefore);
|
||||
strcat(output, _model);
|
||||
strcat_P(output, QuotationSign);
|
||||
}
|
||||
|
||||
if (_name != nullptr) {
|
||||
static const char DataBefore[] PROGMEM = {",\"name\":\""};
|
||||
|
||||
strcat_P(output, DataBefore);
|
||||
strcat(output, _name);
|
||||
strcat_P(output, QuotationSign);
|
||||
}
|
||||
|
||||
if (_softwareVersion != nullptr) {
|
||||
static const char DataBefore[] PROGMEM = {",\"sw\":\""};
|
||||
|
||||
strcat_P(output, DataBefore);
|
||||
strcat(output, _softwareVersion);
|
||||
strcat_P(output, QuotationSign);
|
||||
}
|
||||
|
||||
{
|
||||
static const char Data[] PROGMEM = {"}"};
|
||||
strcat_P(output, Data);
|
||||
}
|
||||
|
||||
output += "}";
|
||||
return output;
|
||||
return strlen(output) + 1; // size with null terminator
|
||||
}
|
||||
|
||||
+3
-6
@@ -1,7 +1,7 @@
|
||||
#ifndef HADEVICE_H
|
||||
#define HADEVICE_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class HADevice
|
||||
{
|
||||
@@ -23,10 +23,8 @@ public:
|
||||
inline void setSoftwareVersion(const char* softwareVersion)
|
||||
{ _softwareVersion = softwareVersion; }
|
||||
|
||||
inline void setViaDevice(const char* viaDevice)
|
||||
{ _viaDevice = viaDevice; }
|
||||
|
||||
String serialize() const;
|
||||
uint16_t calculateSerializedLength() const;
|
||||
uint16_t serialize(char* output) const;
|
||||
|
||||
private:
|
||||
const char* _uniqueId;
|
||||
@@ -34,7 +32,6 @@ private:
|
||||
const char* _model;
|
||||
const char* _name;
|
||||
const char* _softwareVersion;
|
||||
const char* _viaDevice;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+91
-63
@@ -1,53 +1,27 @@
|
||||
#include <IPAddress.h>
|
||||
#include <MQTT.h>
|
||||
#include <PubSubClient.h>
|
||||
|
||||
#include "HAMqtt.h"
|
||||
#include "HADevice.h"
|
||||
#include "ArduinoHADefines.h"
|
||||
#include "device-types/BaseDeviceType.h"
|
||||
|
||||
#define HAMQTT_INIT \
|
||||
_initialized(false), \
|
||||
_discoveryPrefix(DefaultDiscoveryPrefix), \
|
||||
_mqtt(new MQTTClient()), \
|
||||
_mqtt(new PubSubClient(netClient)), \
|
||||
_serverIp(new IPAddress()), \
|
||||
_serverPort(0), \
|
||||
_username(nullptr), \
|
||||
_password(nullptr), \
|
||||
_connecting(false), \
|
||||
_randomNumber(0), \
|
||||
_lastConnectionAttemptAt(0)
|
||||
|
||||
// Author: https://rheingoldheavy.com/better-arduino-random-values/
|
||||
uint32_t generateRandomSeed()
|
||||
{
|
||||
uint8_t seedBitValue = 0;
|
||||
uint8_t seedByteValue = 0;
|
||||
uint32_t seedWordValue = 0;
|
||||
|
||||
for (uint8_t wordShift = 0; wordShift < 4; wordShift++)
|
||||
{
|
||||
for (uint8_t byteShift = 0; byteShift < 8; byteShift++)
|
||||
{
|
||||
for (uint8_t bitSum = 0; bitSum <= 8; bitSum++)
|
||||
{
|
||||
seedBitValue = seedBitValue + (analogRead(A0) & 0x01);
|
||||
}
|
||||
|
||||
delay(1);
|
||||
seedByteValue = seedByteValue | ((seedBitValue & 0x01) << byteShift);
|
||||
seedBitValue = 0;
|
||||
}
|
||||
|
||||
seedWordValue = seedWordValue | (uint32_t)seedByteValue << (8 * wordShift);
|
||||
seedByteValue = 0;
|
||||
}
|
||||
|
||||
return (seedWordValue);
|
||||
}
|
||||
_lastConnectionAttemptAt(0), \
|
||||
_devicesTypesNb(0), \
|
||||
_devicesTypes(nullptr)
|
||||
|
||||
const char* HAMqtt::DefaultDiscoveryPrefix = "homeassistant";
|
||||
|
||||
HAMqtt::HAMqtt(Client& netClient) :
|
||||
HAMqtt::HAMqtt(const char* clientId, Client& netClient) :
|
||||
_clientId(clientId),
|
||||
_netClient(netClient),
|
||||
_hasDevice(false),
|
||||
HAMQTT_INIT
|
||||
@@ -55,7 +29,8 @@ HAMqtt::HAMqtt(Client& netClient) :
|
||||
|
||||
}
|
||||
|
||||
HAMqtt::HAMqtt(Client& netClient, HADevice& device) :
|
||||
HAMqtt::HAMqtt(const char* clientId, Client& netClient, HADevice& device) :
|
||||
_clientId(clientId),
|
||||
_netClient(netClient),
|
||||
_device(device),
|
||||
_hasDevice(true),
|
||||
@@ -71,7 +46,7 @@ bool HAMqtt::begin(
|
||||
const char* password
|
||||
)
|
||||
{
|
||||
#ifdef ARDUINOHA_DEBUG
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("Initializing ArduinoHA"));
|
||||
Serial.print(F("Server address: "));
|
||||
Serial.print(serverIp);
|
||||
@@ -81,23 +56,22 @@ bool HAMqtt::begin(
|
||||
#endif
|
||||
|
||||
if (_initialized) {
|
||||
#ifdef ARDUINOHA_DEBUG
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("ArduinoHA is already initialized"));
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_mqtt->begin(serverIp, serverPort, _netClient);
|
||||
|
||||
*_serverIp = serverIp;
|
||||
_serverPort = serverPort;
|
||||
_username = username;
|
||||
_password = password;
|
||||
_randomNumber = generateRandomSeed();
|
||||
_initialized = true;
|
||||
|
||||
connectToServer();
|
||||
_mqtt->setServer(*_serverIp, _serverPort);
|
||||
// _mqtt->setCallback(...);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -107,9 +81,7 @@ void HAMqtt::loop()
|
||||
return;
|
||||
}
|
||||
|
||||
_mqtt->loop();
|
||||
|
||||
if (!_mqtt->connected()) {
|
||||
if (!_mqtt->loop()) {
|
||||
connectToServer();
|
||||
}
|
||||
}
|
||||
@@ -119,48 +91,104 @@ bool HAMqtt::isConnected()
|
||||
return _mqtt->connected();
|
||||
}
|
||||
|
||||
void HAMqtt::addDeviceType(BaseDeviceType* deviceType)
|
||||
{
|
||||
BaseDeviceType** data = realloc(_devicesTypes, sizeof(BaseDeviceType*) * (_devicesTypesNb + 1));
|
||||
if (data != nullptr) {
|
||||
_devicesTypes = data;
|
||||
_devicesTypes[_devicesTypesNb] = deviceType;
|
||||
_devicesTypesNb++;
|
||||
}
|
||||
}
|
||||
|
||||
void HAMqtt::removeDeviceType(BaseDeviceType* deviceType)
|
||||
{
|
||||
// to do
|
||||
}
|
||||
|
||||
bool HAMqtt::publish(const char* topic, const char* payload, bool retained)
|
||||
{
|
||||
if (!isConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.print(F("Publishing message with topic: "));
|
||||
Serial.print(topic);
|
||||
Serial.print(F(", payload length: "));
|
||||
Serial.print(strlen(payload));
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
_mqtt->beginPublish(topic, strlen(payload), retained);
|
||||
_mqtt->write(payload, strlen(payload));
|
||||
return _mqtt->endPublish();
|
||||
}
|
||||
|
||||
bool HAMqtt::beginPublish(
|
||||
const char* topic,
|
||||
uint16_t payloadLength,
|
||||
bool retained
|
||||
)
|
||||
{
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.print(F("Publishing message with topic: "));
|
||||
Serial.print(topic);
|
||||
Serial.print(F(", payload length: "));
|
||||
Serial.print(payloadLength);
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
return _mqtt->beginPublish(topic, payloadLength, retained);
|
||||
}
|
||||
|
||||
bool HAMqtt::writePayload(const char* data, uint16_t length)
|
||||
{
|
||||
return (_mqtt->write(data, length) > 0);
|
||||
}
|
||||
|
||||
bool HAMqtt::endPublish()
|
||||
{
|
||||
return _mqtt->endPublish();
|
||||
}
|
||||
|
||||
void HAMqtt::connectToServer()
|
||||
{
|
||||
if (_lastConnectionAttemptAt > 0 &&
|
||||
abs(millis() - _lastConnectionAttemptAt) < ReconnectInterval) {
|
||||
(millis() - _lastConnectionAttemptAt) < ReconnectInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool result;
|
||||
String clientId = generateClientId();
|
||||
_lastConnectionAttemptAt = millis();
|
||||
|
||||
#ifdef ARDUINOHA_DEBUG
|
||||
Serial.print("Connecting to the MQTT broker... Client ID: ");
|
||||
Serial.print(clientId);
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.print(F("Connecting to the MQTT broker... Client ID: "));
|
||||
Serial.print(_clientId);
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
if (_username == nullptr || _password == nullptr) {
|
||||
result = _mqtt->connect(clientId.c_str());
|
||||
_mqtt->connect(_clientId);
|
||||
} else {
|
||||
result = _mqtt->connect(clientId.c_str(), _username, _password);
|
||||
_mqtt->connect(_clientId, _username, _password);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
#ifdef ARDUINOHA_DEBUG
|
||||
if (isConnected()) {
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("Connected to the broker"));
|
||||
#endif
|
||||
|
||||
// to do: discovery
|
||||
onConnected();
|
||||
} else {
|
||||
#ifdef ARDUINOHA_DEBUG
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("Failed to connect to the broker"));
|
||||
#endif
|
||||
}
|
||||
|
||||
_lastConnectionAttemptAt = millis();
|
||||
}
|
||||
|
||||
String HAMqtt::generateClientId() const
|
||||
void HAMqtt::onConnected()
|
||||
{
|
||||
if (_hasDevice) {
|
||||
//return _device.getUniqueId();
|
||||
for (uint8_t i = 0; i < _devicesTypesNb; i++) {
|
||||
_devicesTypes[i]->onMqttConnected();
|
||||
}
|
||||
|
||||
return "aha-" + String(_randomNumber, DEC);
|
||||
}
|
||||
|
||||
+66
-13
@@ -1,10 +1,13 @@
|
||||
#ifndef HAMQTT_H
|
||||
#define HAMQTT_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
class IPAddress;
|
||||
class Client;
|
||||
class MQTTClient;
|
||||
class PubSubClient;
|
||||
class HADevice;
|
||||
class BaseDeviceType;
|
||||
|
||||
class HAMqtt
|
||||
{
|
||||
@@ -12,8 +15,28 @@ public:
|
||||
static const char* DefaultDiscoveryPrefix;
|
||||
static const uint16_t ReconnectInterval = 3000; // ms
|
||||
|
||||
HAMqtt(Client& netClient);
|
||||
HAMqtt(Client& netClient, HADevice& device);
|
||||
HAMqtt(const char* clientId, Client& netClient);
|
||||
HAMqtt(const char* clientId, Client& netClient, HADevice& device);
|
||||
|
||||
/**
|
||||
* Sets prefix for Home Assistant discovery.
|
||||
* It needs to match prefix set in the HA admin panel.
|
||||
* The default prefix is "homeassistant".
|
||||
*/
|
||||
inline void setDiscoveryPrefix(const char* prefix)
|
||||
{ _discoveryPrefix = prefix; }
|
||||
|
||||
/**
|
||||
* Returns discovery prefix.
|
||||
*/
|
||||
inline const char* getDiscoveryPrefix() const
|
||||
{ return _discoveryPrefix; }
|
||||
|
||||
/**
|
||||
* Returns instance of the device assigned to the HAMqtt class.
|
||||
*/
|
||||
inline HADevice const* getDevice() const
|
||||
{ return (_hasDevice ? &_device : nullptr); }
|
||||
|
||||
/**
|
||||
* Sets parameters of the connection to the MQTT broker.
|
||||
@@ -27,7 +50,7 @@ public:
|
||||
*/
|
||||
bool begin(
|
||||
const IPAddress& serverIp,
|
||||
const uint16_t& serverPort,
|
||||
const uint16_t& serverPort = 1883,
|
||||
const char* username = nullptr,
|
||||
const char* password = nullptr
|
||||
);
|
||||
@@ -43,30 +66,60 @@ public:
|
||||
bool isConnected();
|
||||
|
||||
/**
|
||||
* Sets prefix for Home Assistant discovery.
|
||||
* It needs to match prefix set in the HA admin panel.
|
||||
* The default prefix is "homeassistant".
|
||||
* Adds a new device's type to the MQTT.
|
||||
* Each time the connection with MQTT broker is acquired, the HAMqtt class
|
||||
* calls "onMqttConnected" method in all devices' types instances.
|
||||
*
|
||||
* @param deviceType Instance of the device's type (eg. HATriggers).
|
||||
*/
|
||||
inline void setDiscoveryPrefix(const char* prefix)
|
||||
{ _discoveryPrefix = prefix; }
|
||||
void addDeviceType(BaseDeviceType* deviceType);
|
||||
|
||||
/**
|
||||
* Removes device's type from the MQTT.
|
||||
*/
|
||||
void removeDeviceType(BaseDeviceType* deviceType);
|
||||
|
||||
/**
|
||||
* Publishes MQTT message with given topic and payload.
|
||||
* Message won't be published if connection with MQTT broker is not established.
|
||||
* In this case method returns false.
|
||||
*
|
||||
* @param topic Topic to publish.
|
||||
* @param payload Payload to publish (it may be empty const char).
|
||||
* @param retained Determines whether message should be retained.
|
||||
*/
|
||||
bool publish(const char* topic, const char* payload, bool retained = false);
|
||||
|
||||
bool beginPublish(const char* topic, uint16_t payloadLength, bool retained = false);
|
||||
bool writePayload(const char* data, uint16_t length);
|
||||
bool endPublish();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Attempts to connect to the MQTT broker.
|
||||
* The method uses properties passed to the "begin" method.
|
||||
*/
|
||||
void connectToServer();
|
||||
String generateClientId() const;
|
||||
|
||||
/**
|
||||
* This method is called each time the connection with MQTT broker is acquired.
|
||||
*/
|
||||
void onConnected();
|
||||
|
||||
const char* _clientId;
|
||||
Client& _netClient;
|
||||
HADevice& _device;
|
||||
bool _hasDevice;
|
||||
bool _initialized;
|
||||
const char* _discoveryPrefix;
|
||||
MQTTClient* _mqtt;
|
||||
PubSubClient* _mqtt;
|
||||
IPAddress* _serverIp;
|
||||
uint16_t _serverPort;
|
||||
const char* _username;
|
||||
const char* _password;
|
||||
bool _connecting;
|
||||
uint32_t _randomNumber;
|
||||
uint32_t _lastConnectionAttemptAt;
|
||||
uint8_t _devicesTypesNb;
|
||||
BaseDeviceType** _devicesTypes;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user