added support for binary sensor

This commit is contained in:
Dawid Chyrzynski
2020-12-04 22:44:29 +01:00
parent 44cad44512
commit 3c9cf1005e
6 changed files with 355 additions and 1 deletions
+1
View File
@@ -1,5 +1,6 @@
#include "HADevice.h"
#include "HAMqtt.h"
#include "device-types/HABinarySensor.h"
#include "device-types/HASwitch.h"
#include "device-types/HATagScanner.h"
#include "device-types/HATriggers.h"
+1
View File
@@ -6,6 +6,7 @@
// You can easily reduce amount of required resources by removing unused components from the library.
// Here is the list of available defines:
// #define NO_HA_BINARY_SENSOR
// #define NO_HA_TRIGGERS
// #define NO_HA_TAG_SCANNER
// #define NO_HA_SWITCH
+1 -1
View File
@@ -12,7 +12,7 @@ class BaseDeviceType;
class HAMqtt
{
public:
static const uint16_t ReconnectInterval = 3000; // ms
static const uint16_t ReconnectInterval = 5000; // ms
HAMqtt(const char* clientId, Client& netClient, HADevice& device);
+270
View File
@@ -0,0 +1,270 @@
#ifndef NO_HA_BINARY_SENSOR
#include "HABinarySensor.h"
#include "../ArduinoHADefines.h"
#include "../HAMqtt.h"
#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),
_class(nullptr),
_currentState(initialState)
{
}
HABinarySensor::HABinarySensor(
const char* name,
const char* deviceClass,
bool initialState,
HAMqtt& mqtt
) :
BaseDeviceType(mqtt),
_name(name),
_class(deviceClass),
_currentState(initialState)
{
}
HABinarySensor::~HABinarySensor()
{
}
void HABinarySensor::onMqttConnected()
{
if (strlen(_name) == 0) {
return;
}
publishConfig();
publishState(_currentState);
}
bool HABinarySensor::setState(bool state)
{
if (state == _currentState) {
return true;
}
if (strlen(_name) == 0) {
return false;
}
if (publishState(state)) {
_currentState = state;
return true;
}
return false;
}
void HABinarySensor::publishConfig()
{
const HADevice* device = mqtt()->getDevice();
if (device == nullptr) {
return;
}
const uint16_t& deviceLength = device->calculateSerializedLength();
if (deviceLength == 0) {
return;
}
char serializedDevice[deviceLength];
if (device->serialize(serializedDevice) == 0) {
return;
}
const uint16_t& topicLength = calculateTopicLength(HAComponentName, _name, ConfigTopic);
const uint16_t& dataLength = calculateSerializedLength(serializedDevice);
if (topicLength == 0 || dataLength == 0) {
return;
}
char topic[topicLength];
generateTopic(topic, HAComponentName, _name, ConfigTopic);
if (strlen(topic) == 0) {
return;
}
if (mqtt()->beginPublish(topic, dataLength, true)) {
writeSerializedTrigger(serializedDevice);
mqtt()->endPublish();
}
}
bool HABinarySensor::publishState(bool state)
{
if (strlen(_name) == 0) {
return false;
}
const uint16_t& topicSize = calculateTopicLength(
HAComponentName,
_name,
StateTopic
);
if (topicSize == 0) {
return false;
}
char topic[topicSize];
generateTopic(
topic,
HAComponentName,
_name,
StateTopic
);
if (strlen(topic) == 0) {
return false;
}
return mqtt()->publish(topic, (state ? StateOn : StateOff), true);
}
uint16_t HABinarySensor::calculateSerializedLength(
const char* serializedDevice
) const
{
if (serializedDevice == nullptr) {
return 0;
}
const uint16_t& stateTopicLength = calculateTopicLength(
HAComponentName,
_name,
StateTopic,
false
);
if (stateTopicLength == 0) {
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
// unique ID
size += strlen(mqtt()->getDevice()->getUniqueId());
size += strlen(_name) + 14; // 14 - length of the JSON data for this field
// device class
if (_class != nullptr) {
size += strlen(_class) + 13; // 13 - length of the JSON data for this field
}
// device
size += strlen(serializedDevice) + 7; // 7 - length of the JSON data for this field
return size;
}
bool HABinarySensor::writeSerializedTrigger(const char* serializedDevice) const
{
if (serializedDevice == nullptr) {
return false;
}
static const char QuotationSign[] PROGMEM = {"\""};
// state topic
{
const uint16_t& topicSize = calculateTopicLength(
HAComponentName,
_name,
StateTopic
);
if (topicSize == 0) {
return false;
}
char stateTopic[topicSize];
generateTopic(
stateTopic,
HAComponentName,
_name,
StateTopic
);
if (strlen(stateTopic) == 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));
}
// 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 Data[] PROGMEM = {"}"};
mqtt()->writePayload_P(Data);
}
return true;
}
#endif
+75
View File
@@ -0,0 +1,75 @@
#ifndef HABINARYSENSOR_H
#ifndef NO_HA_BINARY_SENSOR
#define HABINARYSENSOR_H
#include "BaseDeviceType.h"
class HABinarySensor : public BaseDeviceType
{
public:
/**
* Initializes binary sensor.
*
* @param name Name of the sensor. Recommendes characters: [a-z0-9\-_]
* @param initialState Initial state of the sensor.
It will be published right after "config" message in order to update HA state.
*/
HABinarySensor(
const char* name,
bool initialState,
HAMqtt& mqtt
);
/**
* Initializes binary sensor with the specified class.
* You can find list of available values here: https://www.home-assistant.io/integrations/binary_sensor/#device-class
*
* @param name Name of the sensor. Recommendes characters: [a-z0-9\-_]
* @param deviceClass Name of the class (lower case).
* @param initialState Initial state of the sensor.
It will be published right after "config" message in order to update HA state.
*/
HABinarySensor(
const char* name,
const char* deviceClass,
bool initialState,
HAMqtt& mqtt
);
virtual ~HABinarySensor();
/**
* Publishes configuration of the sensor to the MQTT.
*/
virtual void onMqttConnected() override;
/**
* Changes state of the sensor and publishes MQTT message.
* Please note that if a new value is the same as previous one,
* the MQTT message won't be published.
*
* @param state New state of the sensor.
* @returns Returns true if MQTT message has been published successfully.
*/
bool setState(bool state);
/**
* Returns last known state of the sensor.
* If setState method wasn't called the initial value will be returned.
*/
inline bool getState() const
{ return _currentState; }
private:
void publishConfig();
bool publishState(bool state);
uint16_t calculateSerializedLength(const char* serializedDevice) const;
bool writeSerializedTrigger(const char* serializedDevice) const;
const char* _name;
const char* _class;
bool _currentState;
};
#endif
#endif
+7
View File
@@ -66,6 +66,13 @@ public:
inline bool turnOff()
{ return setState(false); }
/**
* Returns last known state of the switch.
* If setState method wasn't called the initial value will be returned.
*/
inline bool getState() const
{ return _currentState; }
/**
* Registers callback that will be called each time the value of the switch changes.
* Please note that it's not possible to register multiple callbacks for the same switch.