mirror of
https://github.com/mrcory/arduino-home-assistant.git
synced 2026-08-02 15:24:26 -04:00
added support for binary sensor
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user