mirror of
https://github.com/mrcory/arduino-home-assistant.git
synced 2026-08-02 15:24:26 -04:00
Add support for "availability" for sensors and switch (#4)
* hide onMqttConnected and onMqttMessage methods in BaseDeviceType class * bump up version * replace HAComponentName with initialization inside constructor * add missing documentation to begin() method * add support for availability in BaseDeviceType * add support for availability in HABinarySensor, HASensor and HASwitch * move serialization to separate class * wip: DeviceTypeSerializer * update device types * refactored all devices types to use common serializer * update examples * update availability example README.md * bugs fixes * fix build for NodeMCU * cleanup
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
# Home Assistant availability example
|
||||
|
||||
This example shows how to use "availability" feature of the Home Assistant.
|
||||
It's supported only by following device types:
|
||||
* Binary sensor
|
||||
* Sensor
|
||||
* Switch
|
||||
|
||||
## Initialization and usage
|
||||
|
||||
Availability feature is turned off by default. In order to turn it on for the specific
|
||||
sensor/switch you need to set initial state of the unit by calling `setAvailability` method.
|
||||
|
||||
Example:
|
||||
```cpp
|
||||
...
|
||||
HABinarySensor sensor("input", "door", true, mqtt);
|
||||
|
||||
void setup() {
|
||||
// ...
|
||||
|
||||
// method must be called before `mqtt.begin(...)`
|
||||
sensor.setAvailability(false); // offline
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
After availability is set for the very first time you can call `setAvailability` in any time.
|
||||
Each time the method is called, the device will publish MQTT message to the broker.
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <Ethernet.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
#define INPUT_PIN 9
|
||||
#define BROKER_ADDR IPAddress(192,168,0,17)
|
||||
|
||||
byte mac[] = {0x00, 0x10, 0xFA, 0x6E, 0x38, 0x4A};
|
||||
unsigned long lastReadAt = millis();
|
||||
unsigned long lastAvailabilityToggleAt = millis();
|
||||
bool lastInputState = false;
|
||||
|
||||
EthernetClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HAMqtt mqtt(client, device);
|
||||
|
||||
// "input" may be anything you want to be displayed in HA panel
|
||||
// "door" is device class (based on the class HA displays different icons in the panel)
|
||||
// "true" is initial state of the sensor. In this example it's "true" as we use pullup resistor
|
||||
HABinarySensor sensor("input", "door", true, mqtt);
|
||||
|
||||
void setup() {
|
||||
pinMode(INPUT_PIN, INPUT_PULLUP);
|
||||
lastInputState = digitalRead(INPUT_PIN);
|
||||
|
||||
// you don't need to verify return status
|
||||
Ethernet.begin(mac);
|
||||
|
||||
// turn on "availability" feature
|
||||
sensor.setAvailability(false);
|
||||
|
||||
lastReadAt = millis();
|
||||
lastAvailabilityToggleAt = millis();
|
||||
|
||||
// set device's details (optional)
|
||||
device.setName("Arduino");
|
||||
device.setSoftwareVersion("1.0.0");
|
||||
|
||||
mqtt.begin(BROKER_ADDR);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Ethernet.maintain();
|
||||
mqtt.loop();
|
||||
|
||||
if ((millis() - lastReadAt) > 30) { // read in 30ms interval
|
||||
// library produces MQTT message if a new state is different than the previous one
|
||||
sensor.setState(digitalRead(INPUT_PIN));
|
||||
lastInputState = sensor.getState();
|
||||
lastReadAt = millis();
|
||||
}
|
||||
|
||||
if ((millis() - lastAvailabilityToggleAt) > 5000) {
|
||||
sensor.setAvailability(!sensor.isOnline());
|
||||
lastAvailabilityToggleAt = millis();
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,8 @@ HAMqtt mqtt(client, device);
|
||||
|
||||
// "input" may be anything you want to be displayed in HA panel
|
||||
// "door" is device class (based on the class HA displays different icons in the panel)
|
||||
HABinarySensor sensor("input", "door", false, mqtt);
|
||||
// "true" is initial state of the sensor. In this example it's "true" as we use pullup resistor
|
||||
HABinarySensor sensor("input", "door", true, mqtt);
|
||||
|
||||
void setup() {
|
||||
pinMode(INPUT_PIN, INPUT_PULLUP);
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
#define WIFI_SSID "MyNetwork"
|
||||
#define WIFI_PASSWORD "MyPassword"
|
||||
|
||||
byte mac[6];
|
||||
WiFiClient client;
|
||||
HADevice device(mac, sizeof(mac));
|
||||
HADevice device;
|
||||
HAMqtt mqtt(client, device);
|
||||
HASwitch led("led", false, mqtt); // you can use custom name in place of "led"
|
||||
|
||||
@@ -21,7 +20,11 @@ void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println("Starting...");
|
||||
|
||||
// Unique ID must be set!
|
||||
byte mac[WL_MAC_ADDR_LENGTH];
|
||||
WiFi.macAddress(mac);
|
||||
device.setUniqueId(mac, sizeof(mac));
|
||||
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ HAMqtt mqtt(client, device);
|
||||
* - double
|
||||
* - float
|
||||
*/
|
||||
HASensor<double> temp("temp", 0, mqtt); // you can use custom name in place of "temp"
|
||||
// you can use custom name in place of "temp"
|
||||
// "0" is initial value of the sensor
|
||||
HASensor<double> temp("temp", 0, mqtt);
|
||||
|
||||
void setup() {
|
||||
// you don't need to verify return status
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name=home-assistant-integration
|
||||
version=1.0.1
|
||||
version=1.1.0
|
||||
author=Dawid Chyrzynski <dawid.chyrzynski@gmail.com>
|
||||
maintainer=Dawid Chyrzynski <dawid.chyrzynski@gmail.com>
|
||||
sentence=Home Assistant MQTT integration for Arduino
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
_name(nullptr), \
|
||||
_softwareVersion(nullptr)
|
||||
|
||||
HADevice::HADevice() :
|
||||
_uniqueId(nullptr),
|
||||
HADEVICE_INIT
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HADevice::HADevice(const char* uniqueId) :
|
||||
_uniqueId(uniqueId),
|
||||
HADEVICE_INIT
|
||||
@@ -23,6 +30,16 @@ HADevice::HADevice(const byte* uniqueId, const uint16_t& length) :
|
||||
|
||||
}
|
||||
|
||||
bool HADevice::setUniqueId(const byte* uniqueId, const uint16_t& length)
|
||||
{
|
||||
if (_uniqueId != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_uniqueId = HAUtils::byteArrayToStr(uniqueId, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t HADevice::calculateSerializedLength() const
|
||||
{
|
||||
uint16_t size =
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
class HADevice
|
||||
{
|
||||
public:
|
||||
HADevice();
|
||||
HADevice(const char* uniqueId);
|
||||
HADevice(const byte* uniqueId, const uint16_t& length);
|
||||
|
||||
@@ -24,6 +25,7 @@ public:
|
||||
inline void setSoftwareVersion(const char* softwareVersion)
|
||||
{ _softwareVersion = softwareVersion; }
|
||||
|
||||
bool setUniqueId(const byte* uniqueId, const uint16_t& length);
|
||||
uint16_t calculateSerializedLength() const;
|
||||
uint16_t serialize(char* dst) const;
|
||||
|
||||
|
||||
+11
-5
@@ -34,14 +34,12 @@ void onMessageReceived(char* topic, uint8_t* payload, uint16_t length)
|
||||
}
|
||||
|
||||
HAMqtt::HAMqtt(Client& netClient, HADevice& device) :
|
||||
_clientId(device.getUniqueId()),
|
||||
HAMQTT_INIT
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
HAMqtt::HAMqtt(const char* clientId, Client& netClient, HADevice& device) :
|
||||
_clientId(clientId),
|
||||
HAMQTT_INIT
|
||||
{
|
||||
instance = this;
|
||||
@@ -63,6 +61,14 @@ bool HAMqtt::begin(
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
if (_device.getUniqueId() == nullptr) {
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("Failed to initialize ArduinoHA. Missing device's unique ID."));
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_initialized) {
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.println(F("ArduinoHA is already initialized"));
|
||||
@@ -213,14 +219,14 @@ void HAMqtt::connectToServer()
|
||||
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
Serial.print(F("Connecting to the MQTT broker... Client ID: "));
|
||||
Serial.print(_clientId);
|
||||
Serial.print(_device.getUniqueId());
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
if (_username == nullptr || _password == nullptr) {
|
||||
_mqtt->connect(_clientId);
|
||||
_mqtt->connect(_device.getUniqueId());
|
||||
} else {
|
||||
_mqtt->connect(_clientId, _username, _password);
|
||||
_mqtt->connect(_device.getUniqueId(), _username, _password);
|
||||
}
|
||||
|
||||
if (isConnected()) {
|
||||
|
||||
+10
-1
@@ -54,6 +54,16 @@ public:
|
||||
const char* password = nullptr
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets parameters of the connection to the MQTT broker on default port.
|
||||
* The library will try to connect to the broker in first loop cycle.
|
||||
* Please note that the library automatically reconnects to the broker if connection is lost.
|
||||
*
|
||||
* @param serverIp IP address of the MQTT broker.
|
||||
* @param serverPort Port of the MQTT broker.
|
||||
* @param username Username for authentication.
|
||||
* @param password Password for authentication.
|
||||
*/
|
||||
bool begin(
|
||||
const IPAddress& serverIp,
|
||||
const char* username,
|
||||
@@ -128,7 +138,6 @@ private:
|
||||
*/
|
||||
void onConnected();
|
||||
|
||||
const char* _clientId;
|
||||
Client& _netClient;
|
||||
HADevice& _device;
|
||||
bool _hasDevice;
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
|
||||
const char* BaseDeviceType::ConfigTopic = "config"; // todo: move to progmem
|
||||
const char* BaseDeviceType::EventTopic = "event";
|
||||
|
||||
BaseDeviceType::BaseDeviceType(HAMqtt& mqtt) :
|
||||
_mqtt(mqtt)
|
||||
BaseDeviceType::BaseDeviceType(
|
||||
HAMqtt& mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
) :
|
||||
_mqtt(mqtt),
|
||||
_componentName(componentName),
|
||||
_name(name),
|
||||
_availability(AvailabilityDefault)
|
||||
{
|
||||
_mqtt.addDeviceType(this);
|
||||
}
|
||||
@@ -16,63 +20,51 @@ BaseDeviceType::~BaseDeviceType()
|
||||
|
||||
}
|
||||
|
||||
uint16_t BaseDeviceType::calculateTopicLength(
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix,
|
||||
bool includeNullTerminator
|
||||
) const
|
||||
void BaseDeviceType::setAvailability(bool online)
|
||||
{
|
||||
// [discovery prefix]/[namespace]/[device id - optional]/subtype_type/[suffix]
|
||||
const char* prefix = _mqtt.getDiscoveryPrefix();
|
||||
if (prefix == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t size =
|
||||
strlen(prefix) + 1 + // with slash
|
||||
strlen(component) + 1 + // with slash
|
||||
strlen(suffix); // with null terminator
|
||||
|
||||
if (objectId != nullptr) {
|
||||
size += strlen(objectId) + 1; // with slash
|
||||
} else {
|
||||
size += 1; // slash
|
||||
}
|
||||
|
||||
if (_mqtt.getDevice() != nullptr) {
|
||||
size += strlen(_mqtt.getDevice()->getUniqueId()) + 1; // with slash
|
||||
}
|
||||
|
||||
if (includeNullTerminator) {
|
||||
size += 1;
|
||||
}
|
||||
|
||||
return size;
|
||||
_availability = (online ? AvailabilityOnline : AvailabilityOffline);
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
uint16_t BaseDeviceType::generateTopic(
|
||||
char* output,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix
|
||||
) const
|
||||
void BaseDeviceType::publishAvailability()
|
||||
{
|
||||
static const char Slash[] PROGMEM = {"/"};
|
||||
|
||||
const char* prefix = _mqtt.getDiscoveryPrefix();
|
||||
strcpy(output, prefix);
|
||||
strcat_P(output, Slash);
|
||||
strcat(output, component);
|
||||
strcat_P(output, Slash);
|
||||
|
||||
if (_mqtt.getDevice() != nullptr) {
|
||||
strcat(output, _mqtt.getDevice()->getUniqueId());
|
||||
strcat_P(output, Slash);
|
||||
if (_availability == AvailabilityDefault ||
|
||||
!_mqtt.isConnected() ||
|
||||
strlen(_name) == 0 ||
|
||||
strlen(_componentName) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
strcat(output, objectId);
|
||||
strcat_P(output, Slash);
|
||||
strcat(output, suffix);
|
||||
return strlen(output) + 1; // size with null terminator
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
_componentName,
|
||||
_name,
|
||||
DeviceTypeSerializer::AvailabilityTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
_componentName,
|
||||
_name,
|
||||
DeviceTypeSerializer::AvailabilityTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mqtt()->publish(
|
||||
topic,
|
||||
(
|
||||
_availability == AvailabilityOnline ?
|
||||
DeviceTypeSerializer::Online :
|
||||
DeviceTypeSerializer::Offline
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,17 +3,38 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "DeviceTypeSerializer.h"
|
||||
|
||||
class HAMqtt;
|
||||
|
||||
class BaseDeviceType
|
||||
{
|
||||
public:
|
||||
static const char* ConfigTopic;
|
||||
static const char* EventTopic;
|
||||
|
||||
BaseDeviceType(HAMqtt& mqtt);
|
||||
BaseDeviceType(
|
||||
HAMqtt& mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
);
|
||||
virtual ~BaseDeviceType();
|
||||
|
||||
inline bool isOnline() const
|
||||
{ return (_availability == AvailabilityOnline); }
|
||||
|
||||
virtual void setAvailability(bool online);
|
||||
|
||||
protected:
|
||||
inline HAMqtt* mqtt() const
|
||||
{ return &_mqtt; }
|
||||
|
||||
inline const char* name() const
|
||||
{ return _name; }
|
||||
|
||||
inline const char* componentName() const
|
||||
{ return _componentName; }
|
||||
|
||||
inline bool isAvailabilityConfigured() const
|
||||
{ return (_availability != AvailabilityDefault); }
|
||||
|
||||
virtual void onMqttConnected() = 0;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
@@ -21,26 +42,22 @@ public:
|
||||
const uint16_t& length
|
||||
) { };
|
||||
|
||||
protected:
|
||||
inline HAMqtt* mqtt() const
|
||||
{ return &_mqtt; }
|
||||
virtual void publishAvailability();
|
||||
|
||||
virtual uint16_t calculateTopicLength(
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix,
|
||||
bool includeNullTerminator = true
|
||||
) const final;
|
||||
|
||||
virtual uint16_t generateTopic(
|
||||
char* output,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix
|
||||
) const final;
|
||||
const char* const _componentName;
|
||||
const char* const _name;
|
||||
|
||||
private:
|
||||
enum Availability {
|
||||
AvailabilityDefault = 0,
|
||||
AvailabilityOnline,
|
||||
AvailabilityOffline
|
||||
};
|
||||
|
||||
HAMqtt& _mqtt;
|
||||
Availability _availability;
|
||||
|
||||
friend class HAMqtt;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "DeviceTypeSerializer.h"
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
|
||||
static const char CharSlash[] PROGMEM = {"/"};
|
||||
static const char CharUnderscore[] PROGMEM = {"_"};
|
||||
static const char CharQuotation[] PROGMEM = {"\""};
|
||||
|
||||
const char* DeviceTypeSerializer::ConfigTopic = "config";
|
||||
const char* DeviceTypeSerializer::EventTopic = "event";
|
||||
const char* DeviceTypeSerializer::AvailabilityTopic = "avail";
|
||||
const char* DeviceTypeSerializer::StateTopic = "state";
|
||||
const char* DeviceTypeSerializer::CommandTopic = "cmd";
|
||||
const char* DeviceTypeSerializer::Online = "online";
|
||||
const char* DeviceTypeSerializer::Offline = "offline";
|
||||
const char* DeviceTypeSerializer::StateOn = "ON";
|
||||
const char* DeviceTypeSerializer::StateOff = "OFF";
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateTopicLength(
|
||||
const HAMqtt* mqtt,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix,
|
||||
bool includeNullTerminator
|
||||
)
|
||||
{
|
||||
const char* prefix = mqtt->getDiscoveryPrefix();
|
||||
if (prefix == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t size =
|
||||
strlen(prefix) + 1 + // with slash
|
||||
strlen(component) + 1 + // with slash
|
||||
strlen(suffix);
|
||||
|
||||
if (objectId != nullptr) {
|
||||
size += strlen(objectId) + 1; // with slash
|
||||
} else {
|
||||
size += 1; // slash
|
||||
}
|
||||
|
||||
if (mqtt->getDevice() != nullptr) {
|
||||
size += strlen(mqtt->getDevice()->getUniqueId()) + 1; // with slash
|
||||
}
|
||||
|
||||
if (includeNullTerminator) {
|
||||
size += 1;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::generateTopic(
|
||||
const HAMqtt* mqtt,
|
||||
char* output,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix
|
||||
)
|
||||
{
|
||||
const char* prefix = mqtt->getDiscoveryPrefix();
|
||||
strcpy(output, prefix);
|
||||
strcat_P(output, CharSlash);
|
||||
strcat(output, component);
|
||||
strcat_P(output, CharSlash);
|
||||
|
||||
if (mqtt->getDevice() != nullptr) {
|
||||
strcat(output, mqtt->getDevice()->getUniqueId());
|
||||
strcat_P(output, CharSlash);
|
||||
}
|
||||
|
||||
strcat(output, objectId);
|
||||
strcat_P(output, CharSlash);
|
||||
strcat(output, suffix);
|
||||
return strlen(output) + 1; // size with null terminator
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateBaseJsonDataSize()
|
||||
{
|
||||
return 2; // opening and closing brackets of the JSON data
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateNameFieldSize(const char* name)
|
||||
{
|
||||
if (name == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"name":"[NAME]"
|
||||
return strlen(name) + 10; // 10 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateUniqueIdFieldSize(
|
||||
const HADevice* device,
|
||||
const char* name
|
||||
)
|
||||
{
|
||||
if (device == nullptr || name == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"uniq_id":"[DEVICE ID]_[NAME]"
|
||||
return (
|
||||
strlen(device->getUniqueId()) +
|
||||
strlen(name) +
|
||||
14 // 14 - length of the JSON decorators for this field
|
||||
);
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateAvailabilityFieldSize(
|
||||
const HAMqtt* mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
)
|
||||
{
|
||||
if (mqtt == nullptr || componentName == nullptr || name == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint16_t& availabilityTopicLength = calculateTopicLength(
|
||||
mqtt,
|
||||
componentName,
|
||||
name,
|
||||
AvailabilityTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (availabilityTopicLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"avty_t":"[TOPIC]"
|
||||
return availabilityTopicLength + 12; // 12 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
uint16_t DeviceTypeSerializer::calculateDeviceFieldSize(
|
||||
const char* serializedDevice
|
||||
)
|
||||
{
|
||||
if (serializedDevice == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"dev":[DEVICE]
|
||||
return strlen(serializedDevice) + 7; // 7 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteBeginningJson(HAMqtt* mqtt)
|
||||
{
|
||||
if (mqtt == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Data[] PROGMEM = {"{"};
|
||||
mqtt->writePayload_P(Data);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteEndJson(HAMqtt* mqtt)
|
||||
{
|
||||
if (mqtt == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Data[] PROGMEM = {"}"};
|
||||
mqtt->writePayload_P(Data);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteConstCharField(
|
||||
HAMqtt* mqtt,
|
||||
const char* prefix,
|
||||
const char* value
|
||||
)
|
||||
{
|
||||
if (mqtt == nullptr || prefix == nullptr || value == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
mqtt->writePayload_P(prefix);
|
||||
mqtt->writePayload(value, strlen(value));
|
||||
mqtt->writePayload_P(CharQuotation);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteNameField(HAMqtt* mqtt, const char* name)
|
||||
{
|
||||
if (mqtt == nullptr || name == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"name\":\""};
|
||||
mqttWriteConstCharField(mqtt, Prefix, name);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteUniqueIdField(
|
||||
HAMqtt* mqtt,
|
||||
const char* name
|
||||
)
|
||||
{
|
||||
if (mqtt == nullptr || name == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"uniq_id\":\""};
|
||||
|
||||
uint8_t uniqueIdLength = strlen(name) + strlen(mqtt->getDevice()->getUniqueId()) + 2; // underscore and null temrinator
|
||||
char uniqueId[uniqueIdLength];
|
||||
strcpy(uniqueId, name);
|
||||
strcat_P(uniqueId, CharUnderscore);
|
||||
strcat(uniqueId, mqtt->getDevice()->getUniqueId());
|
||||
|
||||
mqttWriteConstCharField(mqtt, Prefix, uniqueId);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
HAMqtt* mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
)
|
||||
{
|
||||
if (mqtt == nullptr || componentName == nullptr || name == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
mqtt,
|
||||
componentName,
|
||||
name,
|
||||
AvailabilityTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char availabilityTopic[topicSize];
|
||||
generateTopic(
|
||||
mqtt,
|
||||
availabilityTopic,
|
||||
componentName,
|
||||
name,
|
||||
AvailabilityTopic
|
||||
);
|
||||
|
||||
if (strlen(availabilityTopic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Prefix[] PROGMEM = {",\"avty_t\":\""};
|
||||
mqttWriteConstCharField(mqtt, Prefix, availabilityTopic);
|
||||
}
|
||||
|
||||
void DeviceTypeSerializer::mqttWriteDeviceField(
|
||||
HAMqtt* mqtt,
|
||||
const char* serializedDevice
|
||||
)
|
||||
{
|
||||
if (mqtt == nullptr || serializedDevice == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Data[] PROGMEM = {",\"dev\":"};
|
||||
|
||||
mqtt->writePayload_P(Data);
|
||||
mqtt->writePayload(serializedDevice, strlen(serializedDevice));
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#ifndef AHA_DEVICETYPESERIALIZER_H
|
||||
#define AHA_DEVICETYPESERIALIZER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class HAMqtt;
|
||||
class HADevice;
|
||||
|
||||
class DeviceTypeSerializer
|
||||
{
|
||||
public:
|
||||
static const char* ConfigTopic;
|
||||
static const char* EventTopic;
|
||||
static const char* AvailabilityTopic;
|
||||
static const char* StateTopic;
|
||||
static const char* CommandTopic;
|
||||
static const char* Online;
|
||||
static const char* Offline;
|
||||
static const char* StateOn;
|
||||
static const char* StateOff;
|
||||
|
||||
/**
|
||||
* Calculates length of the topic with given parameters.
|
||||
* Topic format: [discovery prefix]/[component]/[objectId]/[suffix]
|
||||
*
|
||||
* @param component
|
||||
* @param objectId
|
||||
* @param suffix
|
||||
* @param includeNullTerminator
|
||||
*/
|
||||
static uint16_t calculateTopicLength(
|
||||
const HAMqtt* mqtt,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix,
|
||||
bool includeNullTerminator = true
|
||||
);
|
||||
|
||||
/**
|
||||
* Generates topic and saves it to the given buffer.
|
||||
* Please note that size of the buffer must be calculated by `calculateTopicLength` method first.
|
||||
* Topic format: [discovery prefix]/[component]/[objectId]/[suffix]
|
||||
*
|
||||
* @param output
|
||||
* @param component
|
||||
* @param objectId
|
||||
* @param suffix
|
||||
* @param includeNullTerminator
|
||||
*/
|
||||
static uint16_t generateTopic(
|
||||
const HAMqtt* mqtt,
|
||||
char* output,
|
||||
const char* component,
|
||||
const char* objectId,
|
||||
const char* suffix
|
||||
);
|
||||
|
||||
static uint16_t calculateBaseJsonDataSize();
|
||||
static uint16_t calculateNameFieldSize(
|
||||
const char* name
|
||||
);
|
||||
static uint16_t calculateUniqueIdFieldSize(
|
||||
const HADevice* device,
|
||||
const char* name
|
||||
);
|
||||
static uint16_t calculateAvailabilityFieldSize(
|
||||
const HAMqtt* mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
);
|
||||
static uint16_t calculateDeviceFieldSize(
|
||||
const char* serializedDevice
|
||||
);
|
||||
|
||||
static void mqttWriteBeginningJson(HAMqtt* mqtt);
|
||||
static void mqttWriteEndJson(HAMqtt* mqtt);
|
||||
static void mqttWriteConstCharField(
|
||||
HAMqtt* mqtt,
|
||||
const char* prefix,
|
||||
const char* value
|
||||
);
|
||||
static void mqttWriteNameField(HAMqtt* mqtt, const char* name);
|
||||
static void mqttWriteUniqueIdField(
|
||||
HAMqtt* mqtt,
|
||||
const char* name
|
||||
);
|
||||
static void mqttWriteAvailabilityField(
|
||||
HAMqtt* mqtt,
|
||||
const char* componentName,
|
||||
const char* name
|
||||
);
|
||||
static void mqttWriteDeviceField(
|
||||
HAMqtt* mqtt,
|
||||
const char* serializedDevice
|
||||
);
|
||||
};
|
||||
|
||||
#endif
|
||||
+105
-110
@@ -4,19 +4,12 @@
|
||||
#include "../HADevice.h"
|
||||
#include "../HAUtils.h"
|
||||
|
||||
// todo: move all variables to progmem
|
||||
static const char* HAComponentName = "binary_sensor";
|
||||
static const char* StateTopic = "state";
|
||||
static const char* StateOn = "ON";
|
||||
static const char* StateOff = "OFF";
|
||||
|
||||
HABinarySensor::HABinarySensor(
|
||||
const char* name,
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
BaseDeviceType(mqtt),
|
||||
_name(name),
|
||||
BaseDeviceType(mqtt, "binary_sensor", name),
|
||||
_class(nullptr),
|
||||
_currentState(initialState)
|
||||
{
|
||||
@@ -29,27 +22,22 @@ HABinarySensor::HABinarySensor(
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
BaseDeviceType(mqtt),
|
||||
_name(name),
|
||||
BaseDeviceType(mqtt, "binary_sensor", name),
|
||||
_class(deviceClass),
|
||||
_currentState(initialState)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HABinarySensor::~HABinarySensor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HABinarySensor::onMqttConnected()
|
||||
{
|
||||
if (strlen(_name) == 0) {
|
||||
if (strlen(name()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishState(_currentState);
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
bool HABinarySensor::setState(bool state)
|
||||
@@ -58,7 +46,7 @@ bool HABinarySensor::setState(bool state)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strlen(_name) == 0) {
|
||||
if (strlen(name()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -87,7 +75,12 @@ void HABinarySensor::publishConfig()
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = calculateTopicLength(HAComponentName, _name, ConfigTopic);
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
@@ -95,46 +88,62 @@ void HABinarySensor::publishConfig()
|
||||
}
|
||||
|
||||
char topic[topicLength];
|
||||
generateTopic(topic, HAComponentName, _name, ConfigTopic);
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mqtt()->beginPublish(topic, dataLength, true)) {
|
||||
writeSerializedTrigger(serializedDevice);
|
||||
writeSerializedData(serializedDevice);
|
||||
mqtt()->endPublish();
|
||||
}
|
||||
}
|
||||
|
||||
bool HABinarySensor::publishState(bool state)
|
||||
{
|
||||
if (strlen(_name) == 0) {
|
||||
if (strlen(name()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
StateTopic
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
generateTopic(
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
HAComponentName,
|
||||
_name,
|
||||
StateTopic
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mqtt()->publish(topic, (state ? StateOn : StateOff), true);
|
||||
return mqtt()->publish(
|
||||
topic,
|
||||
(
|
||||
state ?
|
||||
DeviceTypeSerializer::StateOn :
|
||||
DeviceTypeSerializer::StateOff
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
uint16_t HABinarySensor::calculateSerializedLength(
|
||||
@@ -145,122 +154,108 @@ uint16_t HABinarySensor::calculateSerializedLength(
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint16_t& stateTopicLength = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
StateTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (stateTopicLength == 0) {
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t size =
|
||||
2 + // opening and closing bracket (without null terminator)
|
||||
stateTopicLength + 11 + // 11 - length of the JSON data for this field
|
||||
strlen(_name) + 10; // 10 - length of the JSON data for this field
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(device, name());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
|
||||
// unique ID
|
||||
size += strlen(mqtt()->getDevice()->getUniqueId());
|
||||
size += strlen(_name) + 14; // 14 - length of the JSON data for this field
|
||||
if (isAvailabilityConfigured()) {
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
);
|
||||
}
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (topicLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: "stat_t":"[TOPIC]"
|
||||
size += topicLength + 11; // 11 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// device class
|
||||
if (_class != nullptr) {
|
||||
size += strlen(_class) + 13; // 13 - length of the JSON data for this field
|
||||
// Field format: ,"dev_cla":"[CLASS]"
|
||||
size += strlen(_class) + 13; // 13 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// device
|
||||
size += strlen(serializedDevice) + 7; // 7 - length of the JSON data for this field
|
||||
|
||||
return size;
|
||||
return size; // exludes null terminator
|
||||
}
|
||||
|
||||
bool HABinarySensor::writeSerializedTrigger(const char* serializedDevice) const
|
||||
bool HABinarySensor::writeSerializedData(const char* serializedDevice) const
|
||||
{
|
||||
if (serializedDevice == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char QuotationSign[] PROGMEM = {"\""};
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson(mqtt());
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
StateTopic
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char stateTopic[topicSize];
|
||||
generateTopic(
|
||||
stateTopic,
|
||||
HAComponentName,
|
||||
_name,
|
||||
StateTopic
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
if (strlen(stateTopic) == 0) {
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char DataBefore[] PROGMEM = {"{\"stat_t\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(stateTopic, strlen(stateTopic));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
}
|
||||
|
||||
// name
|
||||
{
|
||||
static const char DataBefore[] PROGMEM = {",\"name\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(_name, strlen(_name));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
}
|
||||
|
||||
// unique ID
|
||||
{
|
||||
static const char Underscore[] PROGMEM = {"_"};
|
||||
static const char DataBefore[] PROGMEM = {",\"uniq_id\":\""};
|
||||
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
uint8_t uniqueIdLength = strlen(_name) + strlen(device->getUniqueId()) + 2; // underscore and null temrinator
|
||||
char uniqueId[uniqueIdLength];
|
||||
strcpy(uniqueId, _name);
|
||||
strcat_P(uniqueId, Underscore);
|
||||
strcat(uniqueId, device->getUniqueId());
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(uniqueId, strlen(uniqueId));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
}
|
||||
|
||||
// device
|
||||
{
|
||||
static const char Data[] PROGMEM = {",\"dev\":"};
|
||||
|
||||
mqtt()->writePayload_P(Data);
|
||||
mqtt()->writePayload(serializedDevice, strlen(serializedDevice));
|
||||
static const char Prefix[] PROGMEM = {"\"stat_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
}
|
||||
|
||||
// device class
|
||||
if (_class != nullptr) {
|
||||
static const char DataBefore[] PROGMEM = {",\"dev_cla\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(_class, strlen(_class));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
static const char Prefix[] PROGMEM = {",\"dev_cla\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, _class);
|
||||
}
|
||||
|
||||
{
|
||||
static const char Data[] PROGMEM = {"}"};
|
||||
mqtt()->writePayload_P(Data);
|
||||
DeviceTypeSerializer::mqttWriteNameField(mqtt(), name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(mqtt(), name());
|
||||
|
||||
if (isAvailabilityConfigured()) {
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(mqtt(), serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson(mqtt());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -35,8 +35,6 @@ public:
|
||||
HAMqtt& mqtt
|
||||
);
|
||||
|
||||
virtual ~HABinarySensor();
|
||||
|
||||
/**
|
||||
* Publishes configuration of the sensor to the MQTT.
|
||||
*/
|
||||
@@ -63,9 +61,8 @@ private:
|
||||
void publishConfig();
|
||||
bool publishState(bool state);
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const;
|
||||
bool writeSerializedTrigger(const char* serializedDevice) const;
|
||||
bool writeSerializedData(const char* serializedDevice) const;
|
||||
|
||||
const char* _name;
|
||||
const char* _class;
|
||||
bool _currentState;
|
||||
};
|
||||
|
||||
+100
-114
@@ -4,18 +4,13 @@
|
||||
#include "../HADevice.h"
|
||||
#include "../HAUtils.h"
|
||||
|
||||
// todo: move all variables to progmem
|
||||
static const char* HAComponentName = "sensor";
|
||||
static const char* ValueTopic = "value";
|
||||
|
||||
template <typename T>
|
||||
HASensor<T>::HASensor(
|
||||
const char* name,
|
||||
T initialValue,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
BaseDeviceType(mqtt),
|
||||
_name(name),
|
||||
BaseDeviceType(mqtt, "sensor", name),
|
||||
_class(nullptr),
|
||||
_units(nullptr),
|
||||
_valueType(HAUtils::determineValueType<T>()),
|
||||
@@ -31,8 +26,7 @@ HASensor<T>::HASensor(
|
||||
T initialValue,
|
||||
HAMqtt& mqtt
|
||||
) :
|
||||
BaseDeviceType(mqtt),
|
||||
_name(name),
|
||||
BaseDeviceType(mqtt, "sensor", name),
|
||||
_class(deviceClass),
|
||||
_units(nullptr),
|
||||
_valueType(HAUtils::determineValueType<T>()),
|
||||
@@ -41,27 +35,23 @@ HASensor<T>::HASensor(
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HASensor<T>::~HASensor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void HASensor<T>::onMqttConnected()
|
||||
{
|
||||
if (strlen(_name) == 0 || _valueType == HAUtils::ValueTypeUnknown) {
|
||||
if (strlen(name()) == 0 ||
|
||||
_valueType == HAUtils::ValueTypeUnknown) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishValue(_currentValue);
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HASensor<T>::setValue(T value)
|
||||
{
|
||||
if (strlen(_name) == 0 ||
|
||||
if (strlen(name()) == 0 ||
|
||||
_valueType == HAUtils::ValueTypeUnknown) {
|
||||
return false;
|
||||
}
|
||||
@@ -100,7 +90,12 @@ void HASensor<T>::publishConfig()
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = calculateTopicLength(HAComponentName, _name, ConfigTopic);
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
@@ -108,14 +103,20 @@ void HASensor<T>::publishConfig()
|
||||
}
|
||||
|
||||
char topic[topicLength];
|
||||
generateTopic(topic, HAComponentName, _name, ConfigTopic);
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mqtt()->beginPublish(topic, dataLength, true)) {
|
||||
writeSerializedTrigger(serializedDevice);
|
||||
writeSerializedData(serializedDevice);
|
||||
mqtt()->endPublish();
|
||||
}
|
||||
}
|
||||
@@ -123,26 +124,28 @@ void HASensor<T>::publishConfig()
|
||||
template <typename T>
|
||||
bool HASensor<T>::publishValue(T value)
|
||||
{
|
||||
if (strlen(_name) == 0 ||
|
||||
if (strlen(name()) == 0 ||
|
||||
_valueType == HAUtils::ValueTypeUnknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
ValueTopic
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
generateTopic(
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
HAComponentName,
|
||||
_name,
|
||||
ValueTopic
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
@@ -172,139 +175,122 @@ uint16_t HASensor<T>::calculateSerializedLength(
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint16_t& valueTopicLength = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
ValueTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (valueTopicLength == 0) {
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t size =
|
||||
2 + // opening and closing bracket (without null terminator)
|
||||
valueTopicLength + 11 + // 11 - length of the JSON data for this field
|
||||
strlen(_name) + 10; // 10 - length of the JSON data for this field
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(device, name());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
|
||||
// unique ID
|
||||
size += strlen(mqtt()->getDevice()->getUniqueId());
|
||||
size += strlen(_name) + 14; // 14 - length of the JSON data for this field
|
||||
if (isAvailabilityConfigured()) {
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (topicSize == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Format: "stat_t":"[TOPIC]"
|
||||
size += topicSize + 11; // 11 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// device class
|
||||
if (_class != nullptr) {
|
||||
size += strlen(_class) + 13; // 13 - length of the JSON data for this field
|
||||
// Field format: ,"dev_cla":"[CLASS]"
|
||||
size += strlen(_class) + 13; // 13 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// units of measurement
|
||||
if (_units != nullptr) {
|
||||
size += strlen(_units) + 18;
|
||||
// Format: ,"unit_of_meas":"[UNITS]"
|
||||
size += strlen(_units) + 18; // 18 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// device
|
||||
size += strlen(serializedDevice) + 7; // 7 - length of the JSON data for this field
|
||||
|
||||
return size;
|
||||
return size; // exludes null terminator
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HASensor<T>::writeSerializedTrigger(const char* serializedDevice) const
|
||||
bool HASensor<T>::writeSerializedData(const char* serializedDevice) const
|
||||
{
|
||||
if (serializedDevice == nullptr ||
|
||||
_valueType == HAUtils::ValueTypeUnknown) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char QuotationSign[] PROGMEM = {"\""};
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson(mqtt());
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
ValueTopic
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char stateTopic[topicSize];
|
||||
generateTopic(
|
||||
stateTopic,
|
||||
HAComponentName,
|
||||
_name,
|
||||
ValueTopic
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
if (strlen(stateTopic) == 0) {
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char DataBefore[] PROGMEM = {"{\"stat_t\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(stateTopic, strlen(stateTopic));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
}
|
||||
|
||||
// name
|
||||
{
|
||||
static const char DataBefore[] PROGMEM = {",\"name\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(_name, strlen(_name));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
}
|
||||
|
||||
// unique ID
|
||||
{
|
||||
static const char Underscore[] PROGMEM = {"_"};
|
||||
static const char DataBefore[] PROGMEM = {",\"uniq_id\":\""};
|
||||
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
uint8_t uniqueIdLength = strlen(_name) + strlen(device->getUniqueId()) + 2; // underscore and null temrinator
|
||||
char uniqueId[uniqueIdLength];
|
||||
strcpy(uniqueId, _name);
|
||||
strcat_P(uniqueId, Underscore);
|
||||
strcat(uniqueId, device->getUniqueId());
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(uniqueId, strlen(uniqueId));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
}
|
||||
|
||||
// device
|
||||
{
|
||||
static const char Data[] PROGMEM = {",\"dev\":"};
|
||||
|
||||
mqtt()->writePayload_P(Data);
|
||||
mqtt()->writePayload(serializedDevice, strlen(serializedDevice));
|
||||
static const char Prefix[] PROGMEM = {"\"stat_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
}
|
||||
|
||||
// device class
|
||||
if (_class != nullptr) {
|
||||
static const char DataBefore[] PROGMEM = {",\"dev_cla\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(_class, strlen(_class));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
static const char Prefix[] PROGMEM = {",\"dev_cla\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, _class);
|
||||
}
|
||||
|
||||
// units of measurement
|
||||
if (_units != nullptr) {
|
||||
static const char DataBefore[] PROGMEM = {",\"unit_of_meas\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(_units, strlen(_units));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
static const char Prefix[] PROGMEM = {",\"unit_of_meas\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, _units);
|
||||
}
|
||||
|
||||
{
|
||||
static const char Data[] PROGMEM = {"}"};
|
||||
mqtt()->writePayload_P(Data);
|
||||
DeviceTypeSerializer::mqttWriteNameField(mqtt(), name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(mqtt(), name());
|
||||
|
||||
if (isAvailabilityConfigured()) {
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
);
|
||||
}
|
||||
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(mqtt(), serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson(mqtt());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,6 @@ public:
|
||||
HAMqtt& mqtt
|
||||
);
|
||||
|
||||
virtual ~HASensor();
|
||||
|
||||
/**
|
||||
* Publishes configuration of the sensor to the MQTT.
|
||||
*/
|
||||
@@ -73,11 +71,10 @@ private:
|
||||
void publishConfig();
|
||||
bool publishValue(T value);
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const;
|
||||
bool writeSerializedTrigger(const char* serializedDevice) const;
|
||||
bool writeSerializedData(const char* serializedDevice) const;
|
||||
uint16_t calculateValueLength() const;
|
||||
bool valueToStr(char* dst, T value) const;
|
||||
|
||||
const char* _name;
|
||||
const char* _class;
|
||||
const char* _units;
|
||||
HAUtils::ValueType _valueType;
|
||||
|
||||
+146
-141
@@ -4,36 +4,24 @@
|
||||
#include "../HADevice.h"
|
||||
#include "../HAUtils.h"
|
||||
|
||||
// todo: move all variables to progmem
|
||||
static const char* HAComponentName = "switch";
|
||||
static const char* CommandTopic = "cmd";
|
||||
static const char* StateTopic = "state";
|
||||
static const char* StateOn = "ON";
|
||||
static const char* StateOff = "OFF";
|
||||
|
||||
HASwitch::HASwitch(const char* name, bool initialState, HAMqtt& mqtt) :
|
||||
BaseDeviceType(mqtt),
|
||||
_name(name),
|
||||
BaseDeviceType(mqtt, "switch", name),
|
||||
_stateCallback(nullptr),
|
||||
_currentState(initialState)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HASwitch::~HASwitch()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HASwitch::onMqttConnected()
|
||||
{
|
||||
if (strlen(_name) == 0) {
|
||||
if (strlen(name()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishState(_currentState);
|
||||
subscribeCommandTopic();
|
||||
publishAvailability();
|
||||
}
|
||||
|
||||
void HASwitch::onMqttMessage(
|
||||
@@ -42,21 +30,22 @@ void HASwitch::onMqttMessage(
|
||||
const uint16_t& length
|
||||
)
|
||||
{
|
||||
if (strlen(_name) == 0) {
|
||||
if (strlen(name()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const char Slash[] PROGMEM = {"/"};
|
||||
uint8_t suffixLength = strlen(_name) + strlen(CommandTopic) + 3; // two slashes + null terminator
|
||||
// name + cmd topic + two slashes + null terminator
|
||||
uint8_t suffixLength = strlen(name()) + strlen(DeviceTypeSerializer::CommandTopic) + 3;
|
||||
char suffix[suffixLength];
|
||||
|
||||
strcpy_P(suffix, Slash);
|
||||
strcat(suffix, _name);
|
||||
strcat(suffix, name());
|
||||
strcat_P(suffix, Slash);
|
||||
strcat(suffix, CommandTopic);
|
||||
strcat(suffix, DeviceTypeSerializer::CommandTopic);
|
||||
|
||||
if (HAUtils::endsWith(topic, suffix)) {
|
||||
bool onState = (length == strlen(StateOn));
|
||||
bool onState = (length == strlen(DeviceTypeSerializer::StateOn));
|
||||
setState(onState);
|
||||
}
|
||||
}
|
||||
@@ -67,7 +56,7 @@ bool HASwitch::setState(bool state)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strlen(_name) == 0) {
|
||||
if (strlen(name()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -80,11 +69,6 @@ bool HASwitch::setState(bool state)
|
||||
return false;
|
||||
}
|
||||
|
||||
void HASwitch::onStateChanged(HASWITCH_CALLBACK)
|
||||
{
|
||||
_stateCallback = callback;
|
||||
}
|
||||
|
||||
void HASwitch::triggerCallback(bool state)
|
||||
{
|
||||
if (_stateCallback == nullptr) {
|
||||
@@ -111,7 +95,12 @@ void HASwitch::publishConfig()
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = calculateTopicLength(HAComponentName, _name, ConfigTopic);
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
@@ -119,65 +108,83 @@ void HASwitch::publishConfig()
|
||||
}
|
||||
|
||||
char topic[topicLength];
|
||||
generateTopic(topic, HAComponentName, _name, ConfigTopic);
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mqtt()->beginPublish(topic, dataLength, true)) {
|
||||
writeSerializedTrigger(serializedDevice);
|
||||
writeSerializedData(serializedDevice);
|
||||
mqtt()->endPublish();
|
||||
}
|
||||
}
|
||||
|
||||
bool HASwitch::publishState(bool state)
|
||||
{
|
||||
if (strlen(_name) == 0) {
|
||||
if (strlen(name()) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
StateTopic
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
generateTopic(
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
HAComponentName,
|
||||
_name,
|
||||
StateTopic
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mqtt()->publish(topic, (state ? StateOn : StateOff), true);
|
||||
return mqtt()->publish(
|
||||
topic,
|
||||
(
|
||||
state ?
|
||||
DeviceTypeSerializer::StateOn :
|
||||
DeviceTypeSerializer::StateOff
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
void HASwitch::subscribeCommandTopic()
|
||||
{
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
CommandTopic
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
generateTopic(
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
HAComponentName,
|
||||
_name,
|
||||
CommandTopic
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
@@ -193,145 +200,143 @@ uint16_t HASwitch::calculateSerializedLength(const char* serializedDevice) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint16_t& cmdTopicLength = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
CommandTopic,
|
||||
false
|
||||
);
|
||||
const uint16_t& stateTopicLength = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
StateTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (cmdTopicLength == 0 || stateTopicLength == 0) {
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
if (device == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t size =
|
||||
2 + // opening and closing bracket (without null terminator)
|
||||
cmdTopicLength + 10 + // 10 - length of the JSON data for this field
|
||||
stateTopicLength + 12 + // 12 - length of the JSON data for this field
|
||||
strlen(_name) + 10; // 10 - length of the JSON data for this field
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateNameFieldSize(name());
|
||||
size += DeviceTypeSerializer::calculateUniqueIdFieldSize(device, name());
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
|
||||
// unique ID
|
||||
size += strlen(mqtt()->getDevice()->getUniqueId());
|
||||
size += strlen(_name) + 14; // 14 - length of the JSON data for this field
|
||||
if (isAvailabilityConfigured()) {
|
||||
size += DeviceTypeSerializer::calculateAvailabilityFieldSize(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
);
|
||||
}
|
||||
|
||||
// device
|
||||
size += strlen(serializedDevice) + 7; // 7 - length of the JSON data for this field
|
||||
// cmd topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic,
|
||||
false
|
||||
);
|
||||
|
||||
return size;
|
||||
if (topicLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: "cmd_t":"[TOPIC]"
|
||||
size += topicLength + 10; // 10 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic,
|
||||
false
|
||||
);
|
||||
|
||||
if (topicLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Field format: ,"stat_t":"[TOPIC]"
|
||||
size += topicLength + 12; // 12 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
return size; // exludes null terminator
|
||||
}
|
||||
|
||||
bool HASwitch::writeSerializedTrigger(const char* serializedDevice) const
|
||||
bool HASwitch::writeSerializedData(const char* serializedDevice) const
|
||||
{
|
||||
if (serializedDevice == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char QuotationSign[] PROGMEM = {"\""};
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson(mqtt());
|
||||
|
||||
// command topic
|
||||
{
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
CommandTopic
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char cmdTopic[topicSize];
|
||||
generateTopic(
|
||||
cmdTopic,
|
||||
HAComponentName,
|
||||
_name,
|
||||
CommandTopic
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::CommandTopic
|
||||
);
|
||||
|
||||
if (strlen(cmdTopic) == 0) {
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char DataBefore[] PROGMEM = {"{\"cmd_t\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(cmdTopic, strlen(cmdTopic));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
static const char Prefix[] PROGMEM = {"\"cmd_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
}
|
||||
|
||||
// state topic
|
||||
{
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
StateTopic
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char stateTopic[topicSize];
|
||||
generateTopic(
|
||||
stateTopic,
|
||||
HAComponentName,
|
||||
_name,
|
||||
StateTopic
|
||||
char topic[topicSize];
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::StateTopic
|
||||
);
|
||||
|
||||
if (strlen(stateTopic) == 0) {
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char DataBefore[] PROGMEM = {",\"stat_t\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(stateTopic, strlen(stateTopic));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
static const char Prefix[] PROGMEM = {",\"stat_t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
}
|
||||
|
||||
// name
|
||||
{
|
||||
static const char DataBefore[] PROGMEM = {",\"name\":\""};
|
||||
DeviceTypeSerializer::mqttWriteNameField(mqtt(), name());
|
||||
DeviceTypeSerializer::mqttWriteUniqueIdField(mqtt(), name());
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(_name, strlen(_name));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
if (isAvailabilityConfigured()) {
|
||||
DeviceTypeSerializer::mqttWriteAvailabilityField(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name()
|
||||
);
|
||||
}
|
||||
|
||||
// unique ID
|
||||
{
|
||||
static const char Underscore[] PROGMEM = {"_"};
|
||||
static const char DataBefore[] PROGMEM = {",\"uniq_id\":\""};
|
||||
|
||||
const HADevice* device = mqtt()->getDevice();
|
||||
uint8_t uniqueIdLength = strlen(_name) + strlen(device->getUniqueId()) + 2; // underscore and null temrinator
|
||||
char uniqueId[uniqueIdLength];
|
||||
strcpy(uniqueId, _name);
|
||||
strcat_P(uniqueId, Underscore);
|
||||
strcat(uniqueId, device->getUniqueId());
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(uniqueId, strlen(uniqueId));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
}
|
||||
|
||||
// device
|
||||
{
|
||||
static const char Data[] PROGMEM = {",\"dev\":"};
|
||||
|
||||
mqtt()->writePayload_P(Data);
|
||||
mqtt()->writePayload(serializedDevice, strlen(serializedDevice));
|
||||
}
|
||||
|
||||
{
|
||||
static const char Data[] PROGMEM = {"}"};
|
||||
mqtt()->writePayload_P(Data);
|
||||
}
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(mqtt(), serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson(mqtt());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ public:
|
||||
bool initialState,
|
||||
HAMqtt& mqtt
|
||||
);
|
||||
virtual ~HASwitch();
|
||||
|
||||
/**
|
||||
* Publishes configuration of the sensor to the MQTT.
|
||||
@@ -78,7 +77,8 @@ public:
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
void onStateChanged(HASWITCH_CALLBACK);
|
||||
inline void onStateChanged(HASWITCH_CALLBACK)
|
||||
{ _stateCallback = callback; }
|
||||
|
||||
private:
|
||||
void triggerCallback(bool state);
|
||||
@@ -86,9 +86,8 @@ private:
|
||||
bool publishState(bool state);
|
||||
void subscribeCommandTopic();
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const;
|
||||
bool writeSerializedTrigger(const char* serializedDevice) const;
|
||||
bool writeSerializedData(const char* serializedDevice) const;
|
||||
|
||||
const char* _name;
|
||||
void (*_stateCallback)(bool, HASwitch*);
|
||||
bool _currentState;
|
||||
};
|
||||
|
||||
@@ -4,24 +4,15 @@
|
||||
#include "../HADevice.h"
|
||||
#include "../HAUtils.h"
|
||||
|
||||
// todo: move to progmem
|
||||
static const char* HAComponentName = "tag";
|
||||
|
||||
HATagScanner::HATagScanner(const char* name, HAMqtt& mqtt) :
|
||||
BaseDeviceType(mqtt),
|
||||
_name(name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HATagScanner::~HATagScanner()
|
||||
BaseDeviceType(mqtt, "tag", name)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HATagScanner::onMqttConnected()
|
||||
{
|
||||
if (strlen(_name) == 0) {
|
||||
if (strlen(name()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -34,21 +25,23 @@ bool HATagScanner::tagScanned(const char* tag)
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
EventTopic
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
generateTopic(
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
HAComponentName,
|
||||
_name,
|
||||
EventTopic
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
@@ -75,7 +68,12 @@ void HATagScanner::publishConfig()
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = calculateTopicLength(HAComponentName, _name, ConfigTopic);
|
||||
const uint16_t& topicLength = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
@@ -83,14 +81,20 @@ void HATagScanner::publishConfig()
|
||||
}
|
||||
|
||||
char topic[topicLength];
|
||||
generateTopic(topic, HAComponentName, _name, ConfigTopic);
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mqtt()->beginPublish(topic, dataLength, true)) {
|
||||
writeSerializedTrigger(serializedDevice);
|
||||
writeSerializedData(serializedDevice);
|
||||
mqtt()->endPublish();
|
||||
}
|
||||
}
|
||||
@@ -103,63 +107,58 @@ uint16_t HATagScanner::calculateSerializedLength(
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint16_t& eventTopicLength = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
EventTopic,
|
||||
false
|
||||
);
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
|
||||
uint16_t size =
|
||||
2 + // opening and closing bracket (without null terminator)
|
||||
eventTopicLength + 6 + // 6 - length of the JSON data for this field
|
||||
strlen(serializedDevice) + 7; // 7 - length of the JSON data for this field
|
||||
// event topic
|
||||
{
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::EventTopic,
|
||||
false
|
||||
);
|
||||
|
||||
return size;
|
||||
// Format: "t":"[TOPIC]"
|
||||
size += topicSize + 6; // 6 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
return size; // exludes null terminator
|
||||
}
|
||||
|
||||
bool HATagScanner::writeSerializedTrigger(const char* serializedDevice) const
|
||||
bool HATagScanner::writeSerializedData(const char* serializedDevice) const
|
||||
{
|
||||
if (serializedDevice == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char QuotationSign[] PROGMEM = {"\""};
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson(mqtt());
|
||||
|
||||
// topic
|
||||
{
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
_name,
|
||||
EventTopic
|
||||
const uint16_t& topicSize = DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
char topic[topicSize];
|
||||
generateTopic(
|
||||
DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
topic,
|
||||
HAComponentName,
|
||||
_name,
|
||||
EventTopic
|
||||
componentName(),
|
||||
name(),
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
|
||||
static const char DataBefore[] PROGMEM = {"{\"t\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(topic, strlen(topic));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
static const char Prefix[] PROGMEM = {"\"t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
}
|
||||
|
||||
// device
|
||||
{
|
||||
static const char Data[] PROGMEM = {",\"dev\":"};
|
||||
|
||||
mqtt()->writePayload_P(Data);
|
||||
mqtt()->writePayload(serializedDevice, strlen(serializedDevice));
|
||||
}
|
||||
|
||||
{
|
||||
static const char Data[] PROGMEM = {"}"};
|
||||
mqtt()->writePayload_P(Data);
|
||||
}
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(mqtt(), serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson(mqtt());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,13 +12,17 @@ public:
|
||||
* @param name Name of the scanner. Recommendes characters: [a-z0-9\-_]
|
||||
*/
|
||||
HATagScanner(const char* name, HAMqtt& mqtt);
|
||||
virtual ~HATagScanner();
|
||||
|
||||
/**
|
||||
* Publishes configuration of the sensor to the MQTT.
|
||||
*/
|
||||
virtual void onMqttConnected() override;
|
||||
|
||||
/**
|
||||
* Tag scanner doesn't support availability. Nothing to do here.
|
||||
*/
|
||||
virtual void setAvailability(bool online) override { }
|
||||
|
||||
/**
|
||||
* Sends "tag scanned" event to the MQTT (Home Assistant).
|
||||
* Based on this event HA may perform user-defined automation.
|
||||
@@ -30,9 +34,7 @@ public:
|
||||
private:
|
||||
void publishConfig();
|
||||
uint16_t calculateSerializedLength(const char* serializedDevice) const;
|
||||
bool writeSerializedTrigger(const char* serializedDevice) const;
|
||||
|
||||
const char* _name;
|
||||
bool writeSerializedData(const char* serializedDevice) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,10 +3,8 @@
|
||||
#include "../HAMqtt.h"
|
||||
#include "../HADevice.h"
|
||||
|
||||
static const char* HAComponentName = "device_automation"; // todo: move to progmem
|
||||
|
||||
HATriggers::HATriggers(HAMqtt& mqtt) :
|
||||
BaseDeviceType(mqtt),
|
||||
BaseDeviceType(mqtt, "device_automation", nullptr),
|
||||
_triggers(nullptr),
|
||||
_triggersNb(0)
|
||||
{
|
||||
@@ -68,9 +66,9 @@ bool HATriggers::trigger(const char* type, const char* subtype)
|
||||
}
|
||||
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
componentName(),
|
||||
trigger,
|
||||
EventTopic
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
|
||||
if (topicSize == 0) {
|
||||
@@ -78,7 +76,12 @@ bool HATriggers::trigger(const char* type, const char* subtype)
|
||||
}
|
||||
|
||||
char topic[topicSize];
|
||||
generateTopic(topic, HAComponentName, trigger, EventTopic);
|
||||
generateTopic(
|
||||
topic,
|
||||
componentName(),
|
||||
trigger,
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
@@ -110,15 +113,27 @@ void HATriggers::publishConfig()
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = calculateTopicLength(HAComponentName, trigger, ConfigTopic);
|
||||
const uint16_t& dataLength = calculateSerializedLength(trigger, serializedDevice);
|
||||
const uint16_t& topicLength = calculateTopicLength(
|
||||
componentName(),
|
||||
trigger,
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
const uint16_t& dataLength = calculateSerializedLength(
|
||||
trigger,
|
||||
serializedDevice
|
||||
);
|
||||
|
||||
if (topicLength == 0 || dataLength == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char topic[topicLength];
|
||||
generateTopic(topic, HAComponentName, trigger, ConfigTopic);
|
||||
generateTopic(
|
||||
topic,
|
||||
componentName(),
|
||||
trigger,
|
||||
DeviceTypeSerializer::ConfigTopic
|
||||
);
|
||||
|
||||
if (strlen(topic) == 0) {
|
||||
continue;
|
||||
@@ -139,7 +154,8 @@ uint16_t HATriggers::calculateTopicLength(
|
||||
) const
|
||||
{
|
||||
uint8_t length = strlen(trigger->type) + strlen(trigger->subtype) + 1; // + underscore
|
||||
return BaseDeviceType::calculateTopicLength(
|
||||
return DeviceTypeSerializer::calculateTopicLength(
|
||||
mqtt(),
|
||||
component,
|
||||
nullptr,
|
||||
suffix,
|
||||
@@ -162,7 +178,8 @@ uint16_t HATriggers::generateTopic(
|
||||
strcat_P(objectId, Underscore);
|
||||
strcat(objectId, trigger->type);
|
||||
|
||||
return BaseDeviceType::generateTopic(
|
||||
return DeviceTypeSerializer::generateTopic(
|
||||
mqtt(),
|
||||
output,
|
||||
component,
|
||||
objectId,
|
||||
@@ -179,28 +196,45 @@ uint16_t HATriggers::calculateSerializedLength(
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint16_t& topicLength = calculateTopicLength(
|
||||
HAComponentName,
|
||||
trigger,
|
||||
EventTopic,
|
||||
false
|
||||
);
|
||||
if (topicLength == 0) {
|
||||
return 0;
|
||||
uint16_t size = 0;
|
||||
size += DeviceTypeSerializer::calculateBaseJsonDataSize();
|
||||
size += DeviceTypeSerializer::calculateDeviceFieldSize(serializedDevice);
|
||||
|
||||
// automation type
|
||||
{
|
||||
// Format: "atype":"trigger"
|
||||
size += 17;
|
||||
}
|
||||
|
||||
uint16_t size =
|
||||
2 + // opening and closing bracket (without null terminator)
|
||||
17 + // automation type
|
||||
topicLength + 7 + // 7 - length of the JSON data for this field
|
||||
strlen(trigger->type) + 10 + // 10 - length of the JSON data for this field
|
||||
strlen(trigger->subtype) + 11; // 11 - length of the JSON data for this field
|
||||
// topic
|
||||
{
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
componentName(),
|
||||
trigger,
|
||||
DeviceTypeSerializer::EventTopic,
|
||||
false
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (serializedDevice != nullptr) {
|
||||
size += strlen(serializedDevice) + 7; // 7 - length of the JSON data for this field
|
||||
// Format: ,"t":"[TOPIC]"
|
||||
size += topicSize + 7; // 7 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
return size;
|
||||
// type
|
||||
{
|
||||
// Format: ,"type":"[TYPE]"
|
||||
size += strlen(trigger->type) + 10; // 10 - length of the JSON decorators for this field
|
||||
}
|
||||
|
||||
// subtype
|
||||
{
|
||||
// Format: ,"stype":"[SUBTYPE]"
|
||||
size += strlen(trigger->subtype) + 11; // 11 - length of the JSON decorators for this field;
|
||||
}
|
||||
|
||||
return size; // exludes null terminator
|
||||
}
|
||||
|
||||
bool HATriggers::writeSerializedTrigger(
|
||||
@@ -212,74 +246,55 @@ bool HATriggers::writeSerializedTrigger(
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char QuotationSign[] PROGMEM = {"\""};
|
||||
DeviceTypeSerializer::mqttWriteBeginningJson(mqtt());
|
||||
|
||||
// automation type
|
||||
{
|
||||
static const char Data[] PROGMEM = {"{\"atype\":\"trigger\""};
|
||||
static const char Data[] PROGMEM = {"\"atype\":\"trigger\""};
|
||||
mqtt()->writePayload_P(Data);
|
||||
}
|
||||
|
||||
// topic
|
||||
{
|
||||
const uint16_t& topicSize = calculateTopicLength(
|
||||
HAComponentName,
|
||||
componentName(),
|
||||
trigger,
|
||||
EventTopic
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
if (topicSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char eventTopic[topicSize];
|
||||
char topic[topicSize];
|
||||
generateTopic(
|
||||
eventTopic,
|
||||
HAComponentName,
|
||||
topic,
|
||||
componentName(),
|
||||
trigger,
|
||||
EventTopic
|
||||
DeviceTypeSerializer::EventTopic
|
||||
);
|
||||
|
||||
if (strlen(eventTopic) == 0) {
|
||||
if (strlen(topic) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char DataBefore[] PROGMEM = {",\"t\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(eventTopic, strlen(eventTopic));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
static const char Prefix[] PROGMEM = {",\"t\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, topic);
|
||||
}
|
||||
|
||||
// type
|
||||
{
|
||||
static const char DataBefore[] PROGMEM = {",\"type\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(trigger->type, strlen(trigger->type));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
static const char Prefix[] PROGMEM = {",\"type\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, trigger->type);
|
||||
}
|
||||
|
||||
// subtype
|
||||
{
|
||||
static const char DataBefore[] PROGMEM = {",\"stype\":\""};
|
||||
|
||||
mqtt()->writePayload_P(DataBefore);
|
||||
mqtt()->writePayload(trigger->subtype, strlen(trigger->subtype));
|
||||
mqtt()->writePayload_P(QuotationSign);
|
||||
static const char Prefix[] PROGMEM = {",\"stype\":\""};
|
||||
DeviceTypeSerializer::mqttWriteConstCharField(mqtt(), Prefix, trigger->subtype);
|
||||
}
|
||||
|
||||
// device
|
||||
{
|
||||
static const char Data[] PROGMEM = {",\"dev\":"};
|
||||
|
||||
mqtt()->writePayload_P(Data);
|
||||
mqtt()->writePayload(serializedDevice, strlen(serializedDevice));
|
||||
}
|
||||
|
||||
{
|
||||
static const char Data[] PROGMEM = {"}"};
|
||||
mqtt()->writePayload_P(Data);
|
||||
}
|
||||
DeviceTypeSerializer::mqttWriteDeviceField(mqtt(), serializedDevice);
|
||||
DeviceTypeSerializer::mqttWriteEndJson(mqtt());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,11 @@ public:
|
||||
|
||||
virtual void onMqttConnected() override;
|
||||
|
||||
/**
|
||||
* Triggers dont't support availability. Nothing to do here.
|
||||
*/
|
||||
virtual void setAvailability(bool online) override { }
|
||||
|
||||
bool add(const char* type, const char* subtype);
|
||||
bool trigger(const char* type, const char* subtype);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user