migrate to PubSubClient

This commit is contained in:
Dawid Chyrzynski
2020-12-02 21:55:32 +01:00
parent 0e16d16eab
commit 070a437284
6 changed files with 224 additions and 109 deletions
+1 -1
View File
@@ -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
View File
@@ -1,2 +1,3 @@
#include "HADevice.h"
#include "HAMqtt.h"
#include "device-types/HATriggers.h"
+62 -26
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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