mirror of
https://github.com/alexhopeoconnor/arduino-home-assistant.git
synced 2026-10-04 02:48:13 +10:00
Add fork-driven MQTT and entity feature upgrades.
This integrates configurable MQTT reconnect behavior, the new Text entity, default lambda callback support across command-capable entities, and entity_category support for button/sensor/number families with test coverage.
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include "device-types/HASensorNumber.h"
|
||||
#include "device-types/HASwitch.h"
|
||||
#include "device-types/HATagScanner.h"
|
||||
#include "device-types/HAText.h"
|
||||
#include "utils/HAUtils.h"
|
||||
#include "utils/HANumeric.h"
|
||||
|
||||
|
||||
@@ -20,6 +20,14 @@
|
||||
// #define EX_ARDUINOHA_SENSOR
|
||||
// #define EX_ARDUINOHA_SWITCH
|
||||
// #define EX_ARDUINOHA_TAG_SCANNER
|
||||
// #define EX_ARDUINOHA_TEXT
|
||||
|
||||
// Enables std::function overloads for entity callbacks.
|
||||
// Useful for capturing lambdas, but may increase binary size on constrained boards.
|
||||
// Define ARDUINOHA_DISABLE_STDFUNCTION to opt out.
|
||||
#if !defined(ARDUINOHA_DISABLE_STDFUNCTION)
|
||||
#define ARDUINOHA_ENABLE_STDFUNCTION
|
||||
#endif
|
||||
|
||||
#if defined(ARDUINOHA_DEBUG)
|
||||
#include <Arduino.h>
|
||||
|
||||
+9
-1
@@ -20,6 +20,7 @@
|
||||
_username(nullptr), \
|
||||
_password(nullptr), \
|
||||
_lastConnectionAttemptAt(0), \
|
||||
_reconnectInterval(DefaultReconnectInterval), \
|
||||
_devicesTypesNb(0), \
|
||||
_maxDevicesTypesNb(maxDevicesTypesNb), \
|
||||
_devicesTypes(new HABaseDeviceType*[maxDevicesTypesNb]), \
|
||||
@@ -205,6 +206,13 @@ bool HAMqtt::setBufferSize(uint16_t size)
|
||||
return _mqtt->setBufferSize(size);
|
||||
}
|
||||
|
||||
void HAMqtt::setReconnectInterval(uint16_t interval)
|
||||
{
|
||||
if (interval > 0) {
|
||||
_reconnectInterval = interval;
|
||||
}
|
||||
}
|
||||
|
||||
void HAMqtt::addDeviceType(HABaseDeviceType* deviceType)
|
||||
{
|
||||
if (_devicesTypesNb + 1 > _maxDevicesTypesNb) {
|
||||
@@ -291,7 +299,7 @@ void HAMqtt::processMessage(const char* topic, const uint8_t* payload, uint16_t
|
||||
void HAMqtt::connectToServer()
|
||||
{
|
||||
if (_lastConnectionAttemptAt > 0 &&
|
||||
(millis() - _lastConnectionAttemptAt) < ReconnectInterval) {
|
||||
(millis() - _lastConnectionAttemptAt) < _reconnectInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+19
-2
@@ -262,6 +262,20 @@ public:
|
||||
*/
|
||||
bool setBufferSize(uint16_t size);
|
||||
|
||||
/**
|
||||
* Sets minimal interval between MQTT reconnect attempts.
|
||||
* By default it's 10000 milliseconds.
|
||||
*
|
||||
* @param interval Interval in milliseconds. Zero is ignored.
|
||||
*/
|
||||
void setReconnectInterval(uint16_t interval);
|
||||
|
||||
/**
|
||||
* Returns minimal interval between MQTT reconnect attempts.
|
||||
*/
|
||||
inline uint16_t getReconnectInterval() const
|
||||
{ return _reconnectInterval; }
|
||||
|
||||
/**
|
||||
* Adds a new device's type to the MQTT.
|
||||
* Each time the connection with MQTT broker is acquired, the HAMqtt class
|
||||
@@ -378,8 +392,8 @@ public:
|
||||
#endif
|
||||
|
||||
private:
|
||||
/// Interval between MQTT reconnects (milliseconds).
|
||||
static const uint16_t ReconnectInterval = 10000;
|
||||
/// Default interval between MQTT reconnect attempts (milliseconds).
|
||||
static const uint16_t DefaultReconnectInterval = 10000;
|
||||
|
||||
/// Living instance of the HAMqtt class. It can be nullptr.
|
||||
static HAMqtt* _instance;
|
||||
@@ -440,6 +454,9 @@ private:
|
||||
/// Time of the last connection attemps (milliseconds since boot).
|
||||
uint32_t _lastConnectionAttemptAt;
|
||||
|
||||
/// Interval between MQTT reconnect attempts (milliseconds).
|
||||
uint16_t _reconnectInterval;
|
||||
|
||||
/// The amount of registered devices types.
|
||||
uint8_t _devicesTypesNb;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
HABinarySensor::HABinarySensor(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentBinarySensor), uniqueId),
|
||||
_class(nullptr),
|
||||
_entityCategory(nullptr),
|
||||
_icon(nullptr),
|
||||
_currentState(false)
|
||||
{
|
||||
@@ -42,11 +43,12 @@ void HABinarySensor::buildSerializer()
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 9); // 9 - max properties nb
|
||||
_serializer = new HASerializer(this, 10); // 10 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
|
||||
_serializer->set(HASerializer::WithUniqueId);
|
||||
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
|
||||
_serializer->set(AHATOFSTR(HAStateEntityCategory), _entityCategory);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
|
||||
if (_expireAfter.isSet()) {
|
||||
|
||||
@@ -63,6 +63,15 @@ public:
|
||||
inline void setDeviceClass(const char* deviceClass)
|
||||
{ _class = deviceClass; }
|
||||
|
||||
/**
|
||||
* Sets the entity category for the sensor.
|
||||
* See: https://www.home-assistant.io/integrations/binary_sensor.mqtt/#entity_category
|
||||
*
|
||||
* @param entityCategory The category name.
|
||||
*/
|
||||
inline void setEntityCategory(const char* entityCategory)
|
||||
{ _entityCategory = entityCategory; }
|
||||
|
||||
/**
|
||||
* Sets icon of the sensor.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
@@ -88,6 +97,9 @@ private:
|
||||
/// The device class. It can be nullptr.
|
||||
const char* _class;
|
||||
|
||||
/// The entity category for the sensor. It can be nullptr.
|
||||
const char* _entityCategory;
|
||||
|
||||
/// The icon of the sensor. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
HAButton::HAButton(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentButton), uniqueId),
|
||||
_class(nullptr),
|
||||
_entityCategory(nullptr),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_commandCallback(nullptr)
|
||||
@@ -20,11 +21,12 @@ void HAButton::buildSerializer()
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 9); // 9 - max properties nb
|
||||
_serializer = new HASerializer(this, 10); // 10 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
|
||||
_serializer->set(HASerializer::WithUniqueId);
|
||||
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
|
||||
_serializer->set(AHATOFSTR(HAStateEntityCategory), _entityCategory);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
|
||||
// optional property
|
||||
@@ -61,12 +63,26 @@ void HAButton::onMqttMessage(
|
||||
(void)payload;
|
||||
(void)length;
|
||||
|
||||
if (_commandCallback && HASerializer::compareDataTopics(
|
||||
const bool hasCommandCallback =
|
||||
_commandCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_commandStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (hasCommandCallback && HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
_commandCallback(this);
|
||||
if (_commandCallback) {
|
||||
_commandCallback(this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_commandStdCallback) {
|
||||
_commandStdCallback(this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_BUTTON
|
||||
|
||||
#define HABUTTON_CALLBACK(name) void (*name)(HAButton* sender)
|
||||
@@ -32,6 +36,15 @@ public:
|
||||
inline void setDeviceClass(const char* deviceClass)
|
||||
{ _class = deviceClass; }
|
||||
|
||||
/**
|
||||
* Sets the entity category for the button.
|
||||
* See: https://www.home-assistant.io/integrations/button.mqtt/#entity_category
|
||||
*
|
||||
* @param entityCategory The category name.
|
||||
*/
|
||||
inline void setEntityCategory(const char* entityCategory)
|
||||
{ _entityCategory = entityCategory; }
|
||||
|
||||
/**
|
||||
* Sets icon of the button.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
@@ -57,7 +70,26 @@ public:
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(HABUTTON_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
{
|
||||
_commandCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_commandStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(const std::function<void(HAButton*)>& callback)
|
||||
{
|
||||
_commandCallback = nullptr;
|
||||
_commandStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
@@ -72,6 +104,9 @@ private:
|
||||
/// The device class. It can be nullptr.
|
||||
const char* _class;
|
||||
|
||||
/// The entity category for the button. It can be nullptr.
|
||||
const char* _entityCategory;
|
||||
|
||||
/// The icon of the button. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
@@ -80,6 +115,11 @@ private:
|
||||
|
||||
/// The command callback that will be called once clicking the button in HA panel.
|
||||
HABUTTON_CALLBACK(_commandCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function command callback.
|
||||
std::function<void(HAButton*)> _commandStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -16,6 +16,9 @@ HACover::HACover(const char* uniqueId, const Features features) :
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
_commandCallback(nullptr)
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
, _commandStdCallback()
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
@@ -168,17 +171,42 @@ bool HACover::publishPosition(int16_t position)
|
||||
|
||||
void HACover::handleCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_commandCallback) {
|
||||
const bool hasCommandCallback =
|
||||
_commandCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_commandStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasCommandCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
CoverCommand command;
|
||||
bool handled = false;
|
||||
if (memcmp_P(cmd, HACloseCommand, length) == 0) {
|
||||
_commandCallback(CommandClose, this);
|
||||
command = CommandClose;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAOpenCommand, length) == 0) {
|
||||
_commandCallback(CommandOpen, this);
|
||||
command = CommandOpen;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAStopCommand, length) == 0) {
|
||||
_commandCallback(CommandStop, this);
|
||||
command = CommandStop;
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_commandCallback) {
|
||||
_commandCallback(command, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_commandStdCallback) {
|
||||
_commandStdCallback(command, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_COVER
|
||||
|
||||
#define HACOVER_CALLBACK(name) void (*name)(CoverCommand cmd, HACover* sender)
|
||||
@@ -145,7 +149,26 @@ public:
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(HACOVER_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
{
|
||||
_commandCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_commandStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(const std::function<void(CoverCommand, HACover*)>& callback)
|
||||
{
|
||||
_commandCallback = nullptr;
|
||||
_commandStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
@@ -204,6 +227,11 @@ private:
|
||||
|
||||
/// The command callback that will be called when clicking the cover's button in the HA panel.
|
||||
HACOVER_CALLBACK(_commandCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function callback that will be called when clicking the cover's button in the HA panel.
|
||||
std::function<void(CoverCommand, HACover*)> _commandStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -17,6 +17,10 @@ HAFan::HAFan(const char* uniqueId, const uint8_t features) :
|
||||
_currentSpeed(0),
|
||||
_stateCallback(nullptr),
|
||||
_speedCallback(nullptr)
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
, _stateStdCallback(),
|
||||
_speedStdCallback()
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
@@ -171,23 +175,52 @@ void HAFan::handleStateCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
(void)cmd;
|
||||
|
||||
if (!_stateCallback) {
|
||||
const bool hasStateCallback =
|
||||
_stateCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_stateStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasStateCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool state = length == strlen_P(HAStateOn);
|
||||
_stateCallback(state, this);
|
||||
if (_stateCallback) {
|
||||
_stateCallback(state, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_stateStdCallback) {
|
||||
_stateStdCallback(state, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HAFan::handleSpeedCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_speedCallback) {
|
||||
const bool hasSpeedCallback =
|
||||
_speedCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_speedStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasSpeedCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HANumeric& number = HANumeric::fromStr(cmd, length);
|
||||
if (number.isUInt16()) {
|
||||
_speedCallback(number.toUInt16(), this);
|
||||
const uint16_t speed = number.toUInt16();
|
||||
if (_speedCallback) {
|
||||
_speedCallback(speed, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_speedStdCallback) {
|
||||
_speedStdCallback(speed, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#include "HABaseDeviceType.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_FAN
|
||||
|
||||
#define HAFAN_STATE_CALLBACK(name) void (*name)(bool state, HAFan* sender)
|
||||
@@ -156,7 +160,12 @@ public:
|
||||
* @note In non-optimistic mode, the state must be reported back to HA using the HAFan::setState method.
|
||||
*/
|
||||
inline void onStateCommand(HAFAN_STATE_CALLBACK(callback))
|
||||
{ _stateCallback = callback; }
|
||||
{
|
||||
_stateCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_stateStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the speed command from HA is received.
|
||||
@@ -166,7 +175,38 @@ public:
|
||||
* @note In non-optimistic mode, the speed must be reported back to HA using the HAFan::setSpeed method.
|
||||
*/
|
||||
inline void onSpeedCommand(HAFAN_SPEED_CALLBACK(callback))
|
||||
{ _speedCallback = callback; }
|
||||
{
|
||||
_speedCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_speedStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers state callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onStateCommand(const std::function<void(bool, HAFan*)>& callback)
|
||||
{
|
||||
_stateCallback = nullptr;
|
||||
_stateStdCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers speed callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onSpeedCommand(const std::function<void(uint16_t, HAFan*)>& callback)
|
||||
{
|
||||
_speedCallback = nullptr;
|
||||
_speedStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
@@ -239,6 +279,14 @@ private:
|
||||
|
||||
/// The callback that will be called when the speed command is received from the HA.
|
||||
HAFAN_SPEED_CALLBACK(_speedCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function callback that will be called when the state command is received from the HA.
|
||||
std::function<void(bool, HAFan*)> _stateStdCallback;
|
||||
|
||||
/// The std::function callback that will be called when the speed command is received from the HA.
|
||||
std::function<void(uint16_t, HAFan*)> _speedStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+149
-21
@@ -42,6 +42,14 @@ HAHVAC::HAHVAC(
|
||||
_modeCallback(nullptr),
|
||||
_targetTemperature(),
|
||||
_targetTemperatureCallback(nullptr)
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
, _auxStdCallback(),
|
||||
_powerStdCallback(),
|
||||
_fanModeStdCallback(),
|
||||
_swingModeStdCallback(),
|
||||
_modeStdCallback(),
|
||||
_targetTemperatureStdCallback()
|
||||
#endif
|
||||
{
|
||||
if (_features & FanFeature) {
|
||||
_fanModesSerializer = new HASerializerArray(4);
|
||||
@@ -652,87 +660,207 @@ void HAHVAC::handleAuxStateCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
(void)cmd;
|
||||
|
||||
if (!_auxCallback) {
|
||||
const bool hasAuxCallback =
|
||||
_auxCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_auxStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasAuxCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool state = length == strlen_P(HAStateOn);
|
||||
_auxCallback(state, this);
|
||||
if (_auxCallback) {
|
||||
_auxCallback(state, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_auxStdCallback) {
|
||||
_auxStdCallback(state, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HAHVAC::handlePowerCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
(void)cmd;
|
||||
|
||||
if (!_powerCallback) {
|
||||
const bool hasPowerCallback =
|
||||
_powerCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_powerStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasPowerCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool state = length == strlen_P(HAStateOn);
|
||||
_powerCallback(state, this);
|
||||
if (_powerCallback) {
|
||||
_powerCallback(state, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_powerStdCallback) {
|
||||
_powerStdCallback(state, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HAHVAC::handleFanModeCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_fanModeCallback) {
|
||||
const bool hasFanModeCallback =
|
||||
_fanModeCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_fanModeStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasFanModeCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
FanMode mode = UnknownFanMode;
|
||||
bool handled = false;
|
||||
if (memcmp_P(cmd, HAFanModeAuto, length) == 0) {
|
||||
_fanModeCallback(AutoFanMode, this);
|
||||
mode = AutoFanMode;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAFanModeLow, length) == 0) {
|
||||
_fanModeCallback(LowFanMode, this);
|
||||
mode = LowFanMode;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAFanModeMedium, length) == 0) {
|
||||
_fanModeCallback(MediumFanMode, this);
|
||||
mode = MediumFanMode;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAFanModeHigh, length) == 0) {
|
||||
_fanModeCallback(HighFanMode, this);
|
||||
mode = HighFanMode;
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_fanModeCallback) {
|
||||
_fanModeCallback(mode, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_fanModeStdCallback) {
|
||||
_fanModeStdCallback(mode, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HAHVAC::handleSwingModeCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_swingModeCallback) {
|
||||
const bool hasSwingModeCallback =
|
||||
_swingModeCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_swingModeStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasSwingModeCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
SwingMode mode = UnknownSwingMode;
|
||||
bool handled = false;
|
||||
if (memcmp_P(cmd, HASwingModeOn, length) == 0) {
|
||||
_swingModeCallback(OnSwingMode, this);
|
||||
mode = OnSwingMode;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HASwingModeOff, length) == 0) {
|
||||
_swingModeCallback(OffSwingMode, this);
|
||||
mode = OffSwingMode;
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_swingModeCallback) {
|
||||
_swingModeCallback(mode, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_swingModeStdCallback) {
|
||||
_swingModeStdCallback(mode, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HAHVAC::handleModeCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_modeCallback) {
|
||||
const bool hasModeCallback =
|
||||
_modeCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_modeStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasModeCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
Mode mode = UnknownMode;
|
||||
bool handled = false;
|
||||
if (memcmp_P(cmd, HAModeAuto, length) == 0) {
|
||||
_modeCallback(AutoMode, this);
|
||||
mode = AutoMode;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAModeOff, length) == 0) {
|
||||
_modeCallback(OffMode, this);
|
||||
mode = OffMode;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAModeCool, length) == 0) {
|
||||
_modeCallback(CoolMode, this);
|
||||
mode = CoolMode;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAModeHeat, length) == 0) {
|
||||
_modeCallback(HeatMode, this);
|
||||
mode = HeatMode;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAModeDry, length) == 0) {
|
||||
_modeCallback(DryMode, this);
|
||||
mode = DryMode;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAModeFanOnly, length) == 0) {
|
||||
_modeCallback(FanOnlyMode, this);
|
||||
mode = FanOnlyMode;
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_modeCallback) {
|
||||
_modeCallback(mode, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_modeStdCallback) {
|
||||
_modeStdCallback(mode, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HAHVAC::handleTargetTemperatureCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_targetTemperatureCallback) {
|
||||
const bool hasTargetTemperatureCallback =
|
||||
_targetTemperatureCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_targetTemperatureStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasTargetTemperatureCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
HANumeric number = HANumeric::fromStr(cmd, length);
|
||||
if (number.isSet()) {
|
||||
number.setPrecision(_precision);
|
||||
_targetTemperatureCallback(number, this);
|
||||
if (_targetTemperatureCallback) {
|
||||
_targetTemperatureCallback(number, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_targetTemperatureStdCallback) {
|
||||
_targetTemperatureStdCallback(number, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+122
-6
@@ -4,6 +4,10 @@
|
||||
#include "HABaseDeviceType.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_HVAC
|
||||
|
||||
#define _SET_CURRENT_TEMPERATURE_OVERLOAD(type) \
|
||||
@@ -447,7 +451,12 @@ public:
|
||||
* @note The aux state must be reported back to HA using the HAHVAC::setAuxState method.
|
||||
*/
|
||||
inline void onAuxStateCommand(HAHVAC_CALLBACK_BOOL(callback))
|
||||
{ _auxCallback = callback; }
|
||||
{
|
||||
_auxCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_auxStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the power command from HA is received.
|
||||
@@ -456,7 +465,12 @@ public:
|
||||
* @param callback
|
||||
*/
|
||||
inline void onPowerCommand(HAHVAC_CALLBACK_BOOL(callback))
|
||||
{ _powerCallback = callback; }
|
||||
{
|
||||
_powerCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_powerStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the fan mode command from HA is received.
|
||||
@@ -466,7 +480,12 @@ public:
|
||||
* @note The fan mode must be reported back to HA using the HAHVAC::setFanMode method.
|
||||
*/
|
||||
inline void onFanModeCommand(HAHVAC_CALLBACK_FAN_MODE(callback))
|
||||
{ _fanModeCallback = callback; }
|
||||
{
|
||||
_fanModeCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_fanModeStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the swing mode command from HA is received.
|
||||
@@ -476,7 +495,12 @@ public:
|
||||
* @note The swing mode must be reported back to HA using the HAHVAC::setSwingMode method.
|
||||
*/
|
||||
inline void onSwingModeCommand(HAHVAC_CALLBACK_SWING_MODE(callback))
|
||||
{ _swingModeCallback = callback; }
|
||||
{
|
||||
_swingModeCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_swingModeStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the HVAC mode command from HA is received.
|
||||
@@ -486,7 +510,12 @@ public:
|
||||
* @note The mode must be reported back to HA using the HAHVAC::setMode method.
|
||||
*/
|
||||
inline void onModeCommand(HAHVAC_CALLBACK_MODE(callback))
|
||||
{ _modeCallback = callback; }
|
||||
{
|
||||
_modeCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_modeStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the target temperature is set via HA panel.
|
||||
@@ -496,7 +525,74 @@ public:
|
||||
* @note The target temperature must be reported back to HA using the HAHVAC::setTargetTemperature method.
|
||||
*/
|
||||
inline void onTargetTemperatureCommand(HAHVAC_CALLBACK_TARGET_TEMP(callback))
|
||||
{ _targetTemperatureCallback = callback; }
|
||||
{
|
||||
_targetTemperatureCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_targetTemperatureStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers aux state callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*/
|
||||
inline void onAuxStateCommand(const std::function<void(bool, HAHVAC*)>& callback)
|
||||
{
|
||||
_auxCallback = nullptr;
|
||||
_auxStdCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers power callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*/
|
||||
inline void onPowerCommand(const std::function<void(bool, HAHVAC*)>& callback)
|
||||
{
|
||||
_powerCallback = nullptr;
|
||||
_powerStdCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers fan mode callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*/
|
||||
inline void onFanModeCommand(const std::function<void(FanMode, HAHVAC*)>& callback)
|
||||
{
|
||||
_fanModeCallback = nullptr;
|
||||
_fanModeStdCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers swing mode callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*/
|
||||
inline void onSwingModeCommand(const std::function<void(SwingMode, HAHVAC*)>& callback)
|
||||
{
|
||||
_swingModeCallback = nullptr;
|
||||
_swingModeStdCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers HVAC mode callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*/
|
||||
inline void onModeCommand(const std::function<void(Mode, HAHVAC*)>& callback)
|
||||
{
|
||||
_modeCallback = nullptr;
|
||||
_modeStdCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers target temperature callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*/
|
||||
inline void onTargetTemperatureCommand(const std::function<void(HANumeric, HAHVAC*)>& callback)
|
||||
{
|
||||
_targetTemperatureCallback = nullptr;
|
||||
_targetTemperatureStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
@@ -698,6 +794,26 @@ private:
|
||||
|
||||
/// Callback that will be called when the target temperature is changed via the HA panel.
|
||||
HAHVAC_CALLBACK_TARGET_TEMP(_targetTemperatureCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function callback that will be called when the aux state command is received from the HA.
|
||||
std::function<void(bool, HAHVAC*)> _auxStdCallback;
|
||||
|
||||
/// The std::function callback that will be called when the power command is received from the HA.
|
||||
std::function<void(bool, HAHVAC*)> _powerStdCallback;
|
||||
|
||||
/// The std::function callback that will be called when the fan mode command is received from the HA.
|
||||
std::function<void(FanMode, HAHVAC*)> _fanModeStdCallback;
|
||||
|
||||
/// The std::function callback that will be called when the swing mode command is received from the HA.
|
||||
std::function<void(SwingMode, HAHVAC*)> _swingModeStdCallback;
|
||||
|
||||
/// The std::function callback that will be called when the mode command is received from the HA.
|
||||
std::function<void(Mode, HAHVAC*)> _modeStdCallback;
|
||||
|
||||
/// The std::function callback that will be called when the target temperature is changed via the HA panel.
|
||||
std::function<void(HANumeric, HAHVAC*)> _targetTemperatureStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -62,6 +62,12 @@ HALight::HALight(const char* uniqueId, const uint8_t features) :
|
||||
_brightnessCallback(nullptr),
|
||||
_colorTemperatureCallback(nullptr),
|
||||
_rgbColorCallback(nullptr)
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
, _stateStdCallback(),
|
||||
_brightnessStdCallback(),
|
||||
_colorTemperatureStdCallback(),
|
||||
_rgbColorStdCallback()
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
@@ -321,23 +327,52 @@ void HALight::handleStateCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
(void)cmd;
|
||||
|
||||
if (!_stateCallback) {
|
||||
const bool hasStateCallback =
|
||||
_stateCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_stateStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasStateCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool state = length == strlen_P(HAStateOn);
|
||||
_stateCallback(state, this);
|
||||
if (_stateCallback) {
|
||||
_stateCallback(state, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_stateStdCallback) {
|
||||
_stateStdCallback(state, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HALight::handleBrightnessCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_brightnessCallback) {
|
||||
const bool hasBrightnessCallback =
|
||||
_brightnessCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_brightnessStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasBrightnessCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HANumeric& number = HANumeric::fromStr(cmd, length);
|
||||
if (number.isUInt8()) {
|
||||
_brightnessCallback(number.toUInt8(), this);
|
||||
const uint8_t brightness = number.toUInt8();
|
||||
if (_brightnessCallback) {
|
||||
_brightnessCallback(brightness, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_brightnessStdCallback) {
|
||||
_brightnessStdCallback(brightness, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -346,19 +381,41 @@ void HALight::handleColorTemperatureCommand(
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
if (!_colorTemperatureCallback) {
|
||||
const bool hasColorTemperatureCallback =
|
||||
_colorTemperatureCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_colorTemperatureStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasColorTemperatureCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
const HANumeric& number = HANumeric::fromStr(cmd, length);
|
||||
if (number.isUInt16()) {
|
||||
_colorTemperatureCallback(number.toUInt16(), this);
|
||||
const uint16_t temperature = number.toUInt16();
|
||||
if (_colorTemperatureCallback) {
|
||||
_colorTemperatureCallback(temperature, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_colorTemperatureStdCallback) {
|
||||
_colorTemperatureStdCallback(temperature, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void HALight::handleRGBCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_rgbColorCallback) {
|
||||
const bool hasRgbCallback =
|
||||
_rgbColorCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_rgbColorStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasRgbCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -366,7 +423,14 @@ void HALight::handleRGBCommand(const uint8_t* cmd, const uint16_t length)
|
||||
color.fromBuffer(cmd, length);
|
||||
|
||||
if (color.isSet) {
|
||||
_rgbColorCallback(color, this);
|
||||
if (_rgbColorCallback) {
|
||||
_rgbColorCallback(color, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_rgbColorStdCallback) {
|
||||
_rgbColorStdCallback(color, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#include "HABaseDeviceType.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_LIGHT
|
||||
|
||||
#define HALIGHT_STATE_CALLBACK(name) void (*name)(bool state, HALight* sender)
|
||||
@@ -265,7 +269,12 @@ public:
|
||||
* @note In non-optimistic mode, the state must be reported back to HA using the HALight::setState method.
|
||||
*/
|
||||
inline void onStateCommand(HALIGHT_STATE_CALLBACK(callback))
|
||||
{ _stateCallback = callback; }
|
||||
{
|
||||
_stateCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_stateStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the brightness command from HA is received.
|
||||
@@ -275,7 +284,12 @@ public:
|
||||
* @note In non-optimistic mode, the brightness must be reported back to HA using the HALight::setBrightness method.
|
||||
*/
|
||||
inline void onBrightnessCommand(HALIGHT_BRIGHTNESS_CALLBACK(callback))
|
||||
{ _brightnessCallback = callback; }
|
||||
{
|
||||
_brightnessCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_brightnessStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the color temperature command from HA is received.
|
||||
@@ -285,7 +299,12 @@ public:
|
||||
* @note In non-optimistic mode, the color temperature must be reported back to HA using the HALight::setColorTemperature method.
|
||||
*/
|
||||
inline void onColorTemperatureCommand(HALIGHT_COLOR_TEMP_CALLBACK(callback))
|
||||
{ _colorTemperatureCallback = callback; }
|
||||
{
|
||||
_colorTemperatureCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_colorTemperatureStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time the RGB color command from HA is received.
|
||||
@@ -295,7 +314,62 @@ public:
|
||||
* @note In non-optimistic mode, the color must be reported back to HA using the HALight::setRGBColor method.
|
||||
*/
|
||||
inline void onRGBColorCommand(HALIGHT_RGB_COLOR_CALLBACK(callback))
|
||||
{ _rgbColorCallback = callback; }
|
||||
{
|
||||
_rgbColorCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_rgbColorStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers state callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onStateCommand(const std::function<void(bool, HALight*)>& callback)
|
||||
{
|
||||
_stateCallback = nullptr;
|
||||
_stateStdCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers brightness callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onBrightnessCommand(const std::function<void(uint8_t, HALight*)>& callback)
|
||||
{
|
||||
_brightnessCallback = nullptr;
|
||||
_brightnessStdCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers color temperature callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onColorTemperatureCommand(const std::function<void(uint16_t, HALight*)>& callback)
|
||||
{
|
||||
_colorTemperatureCallback = nullptr;
|
||||
_colorTemperatureStdCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers RGB color callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onRGBColorCommand(const std::function<void(HALight::RGBColor, HALight*)>& callback)
|
||||
{
|
||||
_rgbColorCallback = nullptr;
|
||||
_rgbColorStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
@@ -415,6 +489,20 @@ private:
|
||||
|
||||
/// The callback that will be called when the RGB command is received from the HA.
|
||||
HALIGHT_RGB_COLOR_CALLBACK(_rgbColorCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function callback that will be called when the state command is received from the HA.
|
||||
std::function<void(bool, HALight*)> _stateStdCallback;
|
||||
|
||||
/// The std::function callback that will be called when the brightness command is received from the HA.
|
||||
std::function<void(uint8_t, HALight*)> _brightnessStdCallback;
|
||||
|
||||
/// The std::function callback that will be called when the color temperature command is received from the HA.
|
||||
std::function<void(uint16_t, HALight*)> _colorTemperatureStdCallback;
|
||||
|
||||
/// The std::function callback that will be called when the RGB command is received from the HA.
|
||||
std::function<void(HALight::RGBColor, HALight*)> _rgbColorStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,9 @@ HALock::HALock(const char* uniqueId) :
|
||||
_optimistic(false),
|
||||
_currentState(StateUnknown),
|
||||
_commandCallback(nullptr)
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
, _commandStdCallback()
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
@@ -109,17 +112,42 @@ bool HALock::publishState(const LockState state)
|
||||
|
||||
void HALock::handleCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_commandCallback) {
|
||||
const bool hasCommandCallback =
|
||||
_commandCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_commandStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasCommandCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
LockCommand command;
|
||||
bool handled = false;
|
||||
if (memcmp_P(cmd, HALockCommand, length) == 0) {
|
||||
_commandCallback(CommandLock, this);
|
||||
command = CommandLock;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAUnlockCommand, length) == 0) {
|
||||
_commandCallback(CommandUnlock, this);
|
||||
command = CommandUnlock;
|
||||
handled = true;
|
||||
} else if (memcmp_P(cmd, HAOpenCommand, length) == 0) {
|
||||
_commandCallback(CommandOpen, this);
|
||||
command = CommandOpen;
|
||||
handled = true;
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_commandCallback) {
|
||||
_commandCallback(command, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_commandStdCallback) {
|
||||
_commandStdCallback(command, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_LOCK
|
||||
|
||||
#define HALOCK_CALLBACK(name) void (*name)(LockCommand command, HALock* sender)
|
||||
@@ -100,7 +104,26 @@ public:
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(HALOCK_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
{
|
||||
_commandCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_commandStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(const std::function<void(LockCommand, HALock*)>& callback)
|
||||
{
|
||||
_commandCallback = nullptr;
|
||||
_commandStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
@@ -142,6 +165,11 @@ private:
|
||||
|
||||
/// The callback that will be called when lock/unlock/open command is received from the HA.
|
||||
HALOCK_CALLBACK(_commandCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function callback that will be called when lock/unlock/open command is received from the HA.
|
||||
std::function<void(LockCommand, HALock*)> _commandStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,7 @@ HANumber::HANumber(const char* uniqueId, const NumberPrecision precision) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentNumber), uniqueId),
|
||||
_precision(precision),
|
||||
_class(nullptr),
|
||||
_entityCategory(nullptr),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
@@ -42,11 +43,12 @@ void HANumber::buildSerializer()
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 16); // 16 - max properties nb
|
||||
_serializer = new HASerializer(this, 17); // 17 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
|
||||
_serializer->set(HASerializer::WithUniqueId);
|
||||
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
|
||||
_serializer->set(AHATOFSTR(HAStateEntityCategory), _entityCategory);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
_serializer->set(AHATOFSTR(HAUnitOfMeasurementProperty), _unitOfMeasurement);
|
||||
_serializer->set(
|
||||
@@ -165,17 +167,38 @@ bool HANumber::publishState(const HANumeric& state)
|
||||
|
||||
void HANumber::handleCommand(const uint8_t* cmd, const uint16_t length)
|
||||
{
|
||||
if (!_commandCallback) {
|
||||
const bool hasCommandCallback =
|
||||
_commandCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_commandStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (!hasCommandCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp_P(cmd, HAStateNone, length) == 0) {
|
||||
_commandCallback(HANumeric(), this);
|
||||
if (_commandCallback) {
|
||||
_commandCallback(HANumeric(), this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_commandStdCallback) {
|
||||
_commandStdCallback(HANumeric(), this);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
HANumeric number = HANumeric::fromStr(cmd, length);
|
||||
if (number.isSet()) {
|
||||
number.setPrecision(_precision);
|
||||
_commandCallback(number, this);
|
||||
if (_commandCallback) {
|
||||
_commandCallback(number, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_commandStdCallback) {
|
||||
_commandStdCallback(number, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#include "HABaseDeviceType.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_NUMBER
|
||||
|
||||
#define _SET_STATE_OVERLOAD(type) \
|
||||
@@ -106,6 +110,15 @@ public:
|
||||
inline void setDeviceClass(const char* deviceClass)
|
||||
{ _class = deviceClass; }
|
||||
|
||||
/**
|
||||
* Sets the entity category for the number.
|
||||
* See: https://www.home-assistant.io/integrations/number.mqtt/#entity_category
|
||||
*
|
||||
* @param entityCategory The category name.
|
||||
*/
|
||||
inline void setEntityCategory(const char* entityCategory)
|
||||
{ _entityCategory = entityCategory; }
|
||||
|
||||
/**
|
||||
* Sets icon of the number.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
@@ -184,7 +197,26 @@ public:
|
||||
* @note In non-optimistic mode, the number must be reported back to HA using the HANumber::setState method.
|
||||
*/
|
||||
inline void onCommand(HANUMBER_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
{
|
||||
_commandCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_commandStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(const std::function<void(HANumeric, HANumber*)>& callback)
|
||||
{
|
||||
_commandCallback = nullptr;
|
||||
_commandStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
@@ -228,6 +260,9 @@ private:
|
||||
/// The device class. It can be nullptr.
|
||||
const char* _class;
|
||||
|
||||
/// The entity category for the number. It can be nullptr.
|
||||
const char* _entityCategory;
|
||||
|
||||
/// The icon of the number. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
@@ -257,6 +292,11 @@ private:
|
||||
|
||||
/// The callback that will be called when the command is received from the HA.
|
||||
HANUMBER_CALLBACK(_commandCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function callback that will be called when the command is received from the HA.
|
||||
std::function<void(HANumeric, HANumber*)> _commandStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,9 @@ HAScene::HAScene(const char* uniqueId) :
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_commandCallback(nullptr)
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
, _commandStdCallback()
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
@@ -65,12 +68,26 @@ void HAScene::onMqttMessage(
|
||||
(void)payload;
|
||||
(void)length;
|
||||
|
||||
if (_commandCallback && HASerializer::compareDataTopics(
|
||||
const bool hasCommandCallback =
|
||||
_commandCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_commandStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (hasCommandCallback && HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
_commandCallback(this);
|
||||
if (_commandCallback) {
|
||||
_commandCallback(this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_commandStdCallback) {
|
||||
_commandStdCallback(this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_SCENE
|
||||
|
||||
#define HASCENE_CALLBACK(name) void (*name)(HAScene* sender)
|
||||
@@ -47,7 +51,26 @@ public:
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(HASCENE_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
{
|
||||
_commandCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_commandStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(const std::function<void(HAScene*)>& callback)
|
||||
{
|
||||
_commandCallback = nullptr;
|
||||
_commandStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
@@ -67,6 +90,11 @@ private:
|
||||
|
||||
/// The command callback that will be called when scene is activated from the HA panel.
|
||||
HASCENE_CALLBACK(_commandCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function callback that will be called when scene is activated from the HA panel.
|
||||
std::function<void(HAScene*)> _commandStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -12,6 +12,9 @@ HASelect::HASelect(const char* uniqueId) :
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
_commandCallback(nullptr)
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
, _commandStdCallback()
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
@@ -151,7 +154,14 @@ void HASelect::onMqttMessage(
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
if (_commandCallback && HASerializer::compareDataTopics(
|
||||
const bool hasCommandCallback =
|
||||
_commandCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_commandStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (hasCommandCallback && HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
@@ -161,7 +171,14 @@ void HASelect::onMqttMessage(
|
||||
|
||||
for (uint8_t i = 0; i < optionsNb; i++) {
|
||||
if (memcmp(payload, options[i], length) == 0) {
|
||||
_commandCallback(i, this);
|
||||
if (_commandCallback) {
|
||||
_commandCallback(i, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_commandStdCallback) {
|
||||
_commandStdCallback(i, this);
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_SELECT
|
||||
|
||||
class HASerializerArray;
|
||||
@@ -109,7 +113,26 @@ public:
|
||||
* @note In non-optimistic mode, the selected option must be reported back to HA using the HASelect::setState method.
|
||||
*/
|
||||
inline void onCommand(HASELECT_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
{
|
||||
_commandCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_commandStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(const std::function<void(int8_t, HASelect*)>& callback)
|
||||
{
|
||||
_commandCallback = nullptr;
|
||||
_commandStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINOHA_TEST
|
||||
inline HASerializerArray* getOptions() const
|
||||
@@ -156,6 +179,11 @@ private:
|
||||
|
||||
/// The command callback that will be called when option is changed via the HA panel.
|
||||
HASELECT_CALLBACK(_commandCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function callback that will be called when option is changed via the HA panel.
|
||||
std::function<void(int8_t, HASelect*)> _commandStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,6 +9,7 @@ HASensor::HASensor(const char* uniqueId, const uint16_t features) :
|
||||
_features(features),
|
||||
_deviceClass(nullptr),
|
||||
_stateClass(nullptr),
|
||||
_entityCategory(nullptr),
|
||||
_forceUpdate(false),
|
||||
_icon(nullptr),
|
||||
_unitOfMeasurement(nullptr),
|
||||
@@ -46,12 +47,13 @@ void HASensor::buildSerializer()
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 13); // 13 - max properties nb
|
||||
_serializer = new HASerializer(this, 14); // 14 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
|
||||
_serializer->set(HASerializer::WithUniqueId);
|
||||
_serializer->set(AHATOFSTR(HADeviceClassProperty), _deviceClass);
|
||||
_serializer->set(AHATOFSTR(HAStateClassProperty), _stateClass);
|
||||
_serializer->set(AHATOFSTR(HAStateEntityCategory), _entityCategory);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
_serializer->set(AHATOFSTR(HAUnitOfMeasurementProperty), _unitOfMeasurement);
|
||||
|
||||
|
||||
@@ -73,6 +73,15 @@ public:
|
||||
inline void setStateClass(const char* stateClass)
|
||||
{ _stateClass = stateClass; }
|
||||
|
||||
/**
|
||||
* Sets the entity category for the sensor.
|
||||
* See: https://www.home-assistant.io/integrations/sensor.mqtt/#entity_category
|
||||
*
|
||||
* @param entityCategory The category name.
|
||||
*/
|
||||
inline void setEntityCategory(const char* entityCategory)
|
||||
{ _entityCategory = entityCategory; }
|
||||
|
||||
/**
|
||||
* Forces HA panel to process each incoming value (MQTT message).
|
||||
* It's useful if you want to have meaningful value graphs in history.
|
||||
@@ -113,6 +122,9 @@ private:
|
||||
/// The state class for the long term stats. It can be nullptr. See: https://developers.home-assistant.io/docs/core/entity/sensor/#long-term-statistics
|
||||
const char* _stateClass;
|
||||
|
||||
/// The entity category for the sensor. It can be nullptr.
|
||||
const char* _entityCategory;
|
||||
|
||||
/// The force update flag for the HA panel.
|
||||
bool _forceUpdate;
|
||||
|
||||
|
||||
@@ -90,13 +90,27 @@ void HASwitch::onMqttMessage(
|
||||
{
|
||||
(void)payload;
|
||||
|
||||
if (_commandCallback && HASerializer::compareDataTopics(
|
||||
const bool hasCommandCallback =
|
||||
_commandCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_commandStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (hasCommandCallback && HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
bool state = length == strlen_P(HAStateOn);
|
||||
_commandCallback(state, this);
|
||||
if (_commandCallback) {
|
||||
_commandCallback(state, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_commandStdCallback) {
|
||||
_commandStdCallback(state, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_SWITCH
|
||||
|
||||
#define HASWITCH_CALLBACK(name) void (*name)(bool state, HASwitch* sender)
|
||||
@@ -107,7 +111,26 @@ public:
|
||||
* @note In non-optimistic mode, the state must be reported back to HA using the HASwitch::setState method.
|
||||
*/
|
||||
inline void onCommand(HASWITCH_CALLBACK(callback))
|
||||
{ _commandCallback = callback; }
|
||||
{
|
||||
_commandCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_commandStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(const std::function<void(bool, HASwitch*)>& callback)
|
||||
{
|
||||
_commandCallback = nullptr;
|
||||
_commandStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
@@ -144,6 +167,11 @@ private:
|
||||
|
||||
/// The callback that will be called when switch command is received from the HA.
|
||||
HASWITCH_CALLBACK(_commandCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function callback that will be called when switch command is received from the HA.
|
||||
std::function<void(bool, HASwitch*)> _commandStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
#include "HAText.h"
|
||||
#ifndef EX_ARDUINOHA_TEXT
|
||||
|
||||
#include "../HAMqtt.h"
|
||||
#include "../utils/HASerializer.h"
|
||||
|
||||
HAText::HAText(const char* uniqueId) :
|
||||
HABaseDeviceType(AHATOFSTR(HAComponentText), uniqueId),
|
||||
_icon(nullptr),
|
||||
_retain(false),
|
||||
_optimistic(false),
|
||||
_mode(ModeText),
|
||||
_min(),
|
||||
_max(),
|
||||
_pattern(nullptr),
|
||||
_currentState(nullptr),
|
||||
_commandCallback(nullptr)
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
, _commandStdCallback()
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool HAText::setState(const char* state, const bool force)
|
||||
{
|
||||
if (!state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!force &&
|
||||
_currentState &&
|
||||
strcmp(state, _currentState) == 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (publishState(state)) {
|
||||
_currentState = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void HAText::buildSerializer()
|
||||
{
|
||||
if (_serializer || !uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_serializer = new HASerializer(this, 14); // 14 - max properties nb
|
||||
_serializer->set(AHATOFSTR(HANameProperty), _name);
|
||||
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
|
||||
_serializer->set(HASerializer::WithUniqueId);
|
||||
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAModeProperty),
|
||||
getModeProperty(),
|
||||
HASerializer::ProgmemPropertyValue
|
||||
);
|
||||
_serializer->set(AHATOFSTR(HAPatternProperty), _pattern);
|
||||
|
||||
if (_min.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAMinProperty),
|
||||
&_min,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_max.isSet()) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAMaxProperty),
|
||||
&_max,
|
||||
HASerializer::NumberPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_retain) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HARetainProperty),
|
||||
&_retain,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
if (_optimistic) {
|
||||
_serializer->set(
|
||||
AHATOFSTR(HAOptimisticProperty),
|
||||
&_optimistic,
|
||||
HASerializer::BoolPropertyType
|
||||
);
|
||||
}
|
||||
|
||||
_serializer->set(HASerializer::WithDevice);
|
||||
_serializer->set(HASerializer::WithAvailability);
|
||||
_serializer->topic(AHATOFSTR(HAStateTopic));
|
||||
_serializer->topic(AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HAText::onMqttConnected()
|
||||
{
|
||||
if (!uniqueId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
publishConfig();
|
||||
publishAvailability();
|
||||
|
||||
if (!_retain && _currentState) {
|
||||
publishState(_currentState);
|
||||
}
|
||||
|
||||
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
|
||||
}
|
||||
|
||||
void HAText::onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
)
|
||||
{
|
||||
const bool hasCommandCallback =
|
||||
_commandCallback
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
|| static_cast<bool>(_commandStdCallback)
|
||||
#endif
|
||||
;
|
||||
|
||||
if (hasCommandCallback && HASerializer::compareDataTopics(
|
||||
topic,
|
||||
uniqueId(),
|
||||
AHATOFSTR(HACommandTopic)
|
||||
)) {
|
||||
char value[length + 1];
|
||||
value[length] = 0;
|
||||
memcpy(value, payload, length);
|
||||
if (_commandCallback) {
|
||||
_commandCallback(value, this);
|
||||
}
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
if (_commandStdCallback) {
|
||||
_commandStdCallback(value, this);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool HAText::publishState(const char* state)
|
||||
{
|
||||
return publishOnDataTopic(
|
||||
AHATOFSTR(HAStateTopic),
|
||||
state,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
const __FlashStringHelper* HAText::getModeProperty() const
|
||||
{
|
||||
return _mode == ModePassword
|
||||
? AHATOFSTR(HAModePassword)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,210 @@
|
||||
#ifndef AHA_HATEXT_H
|
||||
#define AHA_HATEXT_H
|
||||
|
||||
#include "HABaseDeviceType.h"
|
||||
#include "../utils/HANumeric.h"
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#ifndef EX_ARDUINOHA_TEXT
|
||||
|
||||
#define HATEXT_CALLBACK(name) void (*name)(const char* value, HAText* sender)
|
||||
|
||||
/**
|
||||
* HAText adds a text input in the Home Assistant panel.
|
||||
*
|
||||
* @note
|
||||
* You can find more information about this entity in the Home Assistant documentation:
|
||||
* https://www.home-assistant.io/integrations/text.mqtt/
|
||||
*/
|
||||
class HAText : public HABaseDeviceType
|
||||
{
|
||||
public:
|
||||
/// Represents mode of the text input.
|
||||
enum Mode {
|
||||
ModeText = 0,
|
||||
ModePassword
|
||||
};
|
||||
|
||||
/**
|
||||
* @param uniqueId The unique ID of the text entity. It needs to be unique in a scope of your device.
|
||||
*/
|
||||
HAText(const char* uniqueId);
|
||||
|
||||
/**
|
||||
* Changes state of the text 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 text.
|
||||
* @param force Forces to update state without comparing it to previous known state.
|
||||
* @returns Returns `true` if MQTT message has been published successfully.
|
||||
*/
|
||||
bool setState(const char* state, const bool force = false);
|
||||
|
||||
/**
|
||||
* Sets current state of the text without publishing it to Home Assistant.
|
||||
* This method may be useful if you want to change state before connection
|
||||
* with MQTT broker is acquired.
|
||||
*
|
||||
* @param state New state of the text.
|
||||
*/
|
||||
inline void setCurrentState(const char* state)
|
||||
{ _currentState = state; }
|
||||
|
||||
/**
|
||||
* Returns last known state of the text.
|
||||
*/
|
||||
inline const char* getCurrentState() const
|
||||
{ return _currentState; }
|
||||
|
||||
/**
|
||||
* Sets icon of the text.
|
||||
* Any icon from MaterialDesignIcons.com (for example: `mdi:home`).
|
||||
*
|
||||
* @param icon The icon name.
|
||||
*/
|
||||
inline void setIcon(const char* icon)
|
||||
{ _icon = icon; }
|
||||
|
||||
/**
|
||||
* Sets retain flag for the text command.
|
||||
* If set to `true` the command produced by Home Assistant will be retained.
|
||||
*
|
||||
* @param retain
|
||||
*/
|
||||
inline void setRetain(const bool retain)
|
||||
{ _retain = retain; }
|
||||
|
||||
/**
|
||||
* Sets optimistic flag for the text state.
|
||||
* In this mode the text state doesn't need to be reported back to the HA panel when a command is received.
|
||||
* By default the optimistic mode is disabled.
|
||||
*
|
||||
* @param optimistic The optimistic mode (`true` - enabled, `false` - disabled).
|
||||
*/
|
||||
inline void setOptimistic(const bool optimistic)
|
||||
{ _optimistic = optimistic; }
|
||||
|
||||
/**
|
||||
* Sets mode of the text input.
|
||||
* By default it's `HAText::ModeText`.
|
||||
*
|
||||
* @param mode Mode to set.
|
||||
*/
|
||||
inline void setMode(const Mode mode)
|
||||
{ _mode = mode; }
|
||||
|
||||
/**
|
||||
* Sets minimum accepted length of the command payload.
|
||||
*
|
||||
* @param min Minimum number of characters.
|
||||
*/
|
||||
inline void setMin(const uint16_t min)
|
||||
{ _min = HANumeric(min, PrecisionP0); }
|
||||
|
||||
/**
|
||||
* Sets maximum accepted length of the command payload.
|
||||
*
|
||||
* @param max Maximum number of characters.
|
||||
*/
|
||||
inline void setMax(const uint16_t max)
|
||||
{ _max = HANumeric(max, PrecisionP0); }
|
||||
|
||||
/**
|
||||
* Sets regex pattern for command payload validation in Home Assistant.
|
||||
*
|
||||
* @param pattern Regex pattern.
|
||||
*/
|
||||
inline void setPattern(const char* pattern)
|
||||
{ _pattern = pattern; }
|
||||
|
||||
/**
|
||||
* Registers callback that will be called each time text command from HA is received.
|
||||
* Please note that it's not possible to register multiple callbacks for the same text entity.
|
||||
*
|
||||
* @param callback
|
||||
* @note In non-optimistic mode, the state should be reported back to HA using HAText::setState.
|
||||
*/
|
||||
inline void onCommand(HATEXT_CALLBACK(callback))
|
||||
{
|
||||
_commandCallback = callback;
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
_commandStdCallback = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/**
|
||||
* Registers callback using std::function.
|
||||
* It allows passing capturing lambdas and std::bind expressions.
|
||||
*
|
||||
* @param callback
|
||||
*/
|
||||
inline void onCommand(const std::function<void(const char*, HAText*)>& callback)
|
||||
{
|
||||
_commandCallback = nullptr;
|
||||
_commandStdCallback = callback;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void buildSerializer() override;
|
||||
virtual void onMqttConnected() override;
|
||||
virtual void onMqttMessage(
|
||||
const char* topic,
|
||||
const uint8_t* payload,
|
||||
const uint16_t length
|
||||
) override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Publishes the MQTT message with the given state.
|
||||
*
|
||||
* @param state The state to publish.
|
||||
* @returns Returns `true` if the MQTT message has been published successfully.
|
||||
*/
|
||||
bool publishState(const char* state);
|
||||
|
||||
/**
|
||||
* Returns progmem string representing mode of the text.
|
||||
*/
|
||||
const __FlashStringHelper* getModeProperty() const;
|
||||
|
||||
/// The icon of the text. It can be nullptr.
|
||||
const char* _icon;
|
||||
|
||||
/// The retain flag for the HA commands.
|
||||
bool _retain;
|
||||
|
||||
/// The optimistic mode of the text (`true` - enabled, `false` - disabled).
|
||||
bool _optimistic;
|
||||
|
||||
/// Controls how the text should be displayed in the UI.
|
||||
Mode _mode;
|
||||
|
||||
/// The minimal number of characters accepted by Home Assistant.
|
||||
HANumeric _min;
|
||||
|
||||
/// The maximum number of characters accepted by Home Assistant.
|
||||
HANumeric _max;
|
||||
|
||||
/// Regular expression pattern accepted by Home Assistant. It can be nullptr.
|
||||
const char* _pattern;
|
||||
|
||||
/// The current state of the text. It can be nullptr if state wasn't set.
|
||||
const char* _currentState;
|
||||
|
||||
/// The callback that will be called when command is received from the HA.
|
||||
HATEXT_CALLBACK(_commandCallback);
|
||||
|
||||
#if defined(ARDUINOHA_ENABLE_STDFUNCTION)
|
||||
/// The std::function callback that will be called when command is received from the HA.
|
||||
std::function<void(const char*, HAText*)> _commandStdCallback;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -9,6 +9,7 @@ PubSubClientMock::PubSubClientMock() :
|
||||
_keepAlive(15),
|
||||
_bufferSize(256),
|
||||
_state(-1),
|
||||
_connectCallsNb(0),
|
||||
_flushedMessagesNb(0),
|
||||
_subscriptions(nullptr),
|
||||
_subscriptionsNb(0),
|
||||
@@ -56,6 +57,7 @@ bool PubSubClientMock::connect(
|
||||
(void)willQos;
|
||||
(void)cleanSession;
|
||||
|
||||
_connectCallsNb++;
|
||||
_connection.connected = true;
|
||||
_connection.id = id;
|
||||
_connection.user = user;
|
||||
|
||||
@@ -145,6 +145,9 @@ public:
|
||||
inline int16_t state() const
|
||||
{ return _state; }
|
||||
|
||||
inline uint16_t getConnectCallsNb() const
|
||||
{ return _connectCallsNb; }
|
||||
|
||||
inline uint8_t getFlushedMessagesNb() const
|
||||
{ return _flushedMessagesNb; }
|
||||
|
||||
@@ -175,6 +178,7 @@ private:
|
||||
uint16_t _keepAlive;
|
||||
uint16_t _bufferSize;
|
||||
int16_t _state;
|
||||
uint16_t _connectCallsNb;
|
||||
uint8_t _flushedMessagesNb;
|
||||
MqttSubscription** _subscriptions;
|
||||
uint8_t _subscriptionsNb;
|
||||
|
||||
@@ -14,6 +14,7 @@ const char HAComponentSelect[] PROGMEM = {"select"};
|
||||
const char HAComponentSensor[] PROGMEM = {"sensor"};
|
||||
const char HAComponentSwitch[] PROGMEM = {"switch"};
|
||||
const char HAComponentTag[] PROGMEM = {"tag"};
|
||||
const char HAComponentText[] PROGMEM = {"text"};
|
||||
const char HAComponentScene[] PROGMEM = {"scene"};
|
||||
const char HAComponentFan[] PROGMEM = {"fan"};
|
||||
const char HAComponentLight[] PROGMEM = {"light"};
|
||||
@@ -42,6 +43,7 @@ const char HAUniqueIdProperty[] PROGMEM = {"uniq_id"};
|
||||
const char HAObjectIdProperty[] PROGMEM = {"obj_id"};
|
||||
const char HADeviceProperty[] PROGMEM = {"dev"};
|
||||
const char HADeviceClassProperty[] PROGMEM = {"dev_cla"};
|
||||
const char HAStateEntityCategory[] PROGMEM = {"ent_cat"};
|
||||
const char HAStateClassProperty[] PROGMEM = {"stat_cla"};
|
||||
const char HAIconProperty[] PROGMEM = {"ic"};
|
||||
const char HARetainProperty[] PROGMEM = {"ret"};
|
||||
@@ -59,6 +61,7 @@ const char HAMinProperty[] PROGMEM = {"min"};
|
||||
const char HAMaxProperty[] PROGMEM = {"max"};
|
||||
const char HAStepProperty[] PROGMEM = {"step"};
|
||||
const char HAModeProperty[] PROGMEM = {"mode"};
|
||||
const char HAPatternProperty[] PROGMEM = {"pattern"};
|
||||
const char HACommandTemplateProperty[] PROGMEM = {"cmd_tpl"};
|
||||
const char HASpeedRangeMaxProperty[] PROGMEM = {"spd_rng_max"};
|
||||
const char HASpeedRangeMinProperty[] PROGMEM = {"spd_rng_min"};
|
||||
@@ -121,6 +124,7 @@ const char HANotHome[] PROGMEM = {"not_home"};
|
||||
const char HATrigger[] PROGMEM = {"trigger"};
|
||||
const char HAModeBox[] PROGMEM = {"box"};
|
||||
const char HAModeSlider[] PROGMEM = {"slider"};
|
||||
const char HAModePassword[] PROGMEM = {"password"};
|
||||
|
||||
// covers
|
||||
const char HAClosedState[] PROGMEM = {"closed"};
|
||||
|
||||
@@ -14,6 +14,7 @@ extern const char HAComponentSelect[];
|
||||
extern const char HAComponentSensor[];
|
||||
extern const char HAComponentSwitch[];
|
||||
extern const char HAComponentTag[];
|
||||
extern const char HAComponentText[];
|
||||
extern const char HAComponentScene[];
|
||||
extern const char HAComponentFan[];
|
||||
extern const char HAComponentLight[];
|
||||
@@ -42,6 +43,7 @@ extern const char HAUniqueIdProperty[];
|
||||
extern const char HAObjectIdProperty[];
|
||||
extern const char HADeviceProperty[];
|
||||
extern const char HADeviceClassProperty[];
|
||||
extern const char HAStateEntityCategory[];
|
||||
extern const char HAStateClassProperty[];
|
||||
extern const char HAIconProperty[];
|
||||
extern const char HARetainProperty[];
|
||||
@@ -59,6 +61,7 @@ extern const char HAMinProperty[];
|
||||
extern const char HAMaxProperty[];
|
||||
extern const char HAStepProperty[];
|
||||
extern const char HAModeProperty[];
|
||||
extern const char HAPatternProperty[];
|
||||
extern const char HACommandTemplateProperty[];
|
||||
extern const char HASpeedRangeMaxProperty[];
|
||||
extern const char HASpeedRangeMinProperty[];
|
||||
@@ -121,6 +124,7 @@ extern const char HANotHome[];
|
||||
extern const char HATrigger[];
|
||||
extern const char HAModeBox[];
|
||||
extern const char HAModeSlider[];
|
||||
extern const char HAModePassword[];
|
||||
|
||||
// covers
|
||||
extern const char HAClosedState[];
|
||||
|
||||
@@ -140,6 +140,26 @@ AHA_TEST(BinarySensorTest, device_class) {
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(BinarySensorTest, entity_category_setter) {
|
||||
initMqttTest(testDeviceId)
|
||||
|
||||
HABinarySensor sensor(testUniqueId);
|
||||
sensor.setEntityCategory("diagnostic");
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
sensor,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"uniqueSensor\","
|
||||
"\"ent_cat\":\"diagnostic\","
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueSensor/stat_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(BinarySensorTest, icon_setter) {
|
||||
initMqttTest(testDeviceId)
|
||||
|
||||
|
||||
@@ -169,6 +169,26 @@ AHA_TEST(ButtonTest, device_class) {
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(ButtonTest, entity_category_setter) {
|
||||
prepareTest
|
||||
|
||||
HAButton button(testUniqueId);
|
||||
button.setEntityCategory("diagnostic");
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
button,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"uniqueButton\","
|
||||
"\"ent_cat\":\"diagnostic\","
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueButton/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(ButtonTest, icon_setter) {
|
||||
prepareTest
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ tests:
|
||||
set -e; \
|
||||
for i in *Test/Makefile; do \
|
||||
echo '==== Making:' $$(dirname $$i); \
|
||||
$(MAKE) -C $$(dirname $$i) clean; \
|
||||
$(MAKE) -C $$(dirname $$i) -j; \
|
||||
done
|
||||
|
||||
|
||||
@@ -29,6 +29,44 @@ AHA_TEST(MqttTest, maximum_number_of_device_types) {
|
||||
assertEqual(&deviceType, mqtt.getDevicesTypes()[0]);
|
||||
}
|
||||
|
||||
AHA_TEST(MqttTest, reconnect_interval_default_value) {
|
||||
HADevice device(testDeviceId);
|
||||
HAMqtt mqtt(nullptr, device);
|
||||
|
||||
assertEqual((uint16_t)10000, mqtt.getReconnectInterval());
|
||||
}
|
||||
|
||||
AHA_TEST(MqttTest, reconnect_interval_setter) {
|
||||
HADevice device(testDeviceId);
|
||||
HAMqtt mqtt(nullptr, device);
|
||||
|
||||
mqtt.setReconnectInterval(5000);
|
||||
assertEqual((uint16_t)5000, mqtt.getReconnectInterval());
|
||||
|
||||
mqtt.setReconnectInterval(0); // ignored
|
||||
assertEqual((uint16_t)5000, mqtt.getReconnectInterval());
|
||||
}
|
||||
|
||||
AHA_TEST(MqttTest, reconnect_interval_throttles_attempts) {
|
||||
PubSubClientMock* mock = new PubSubClientMock();
|
||||
HADevice device(testDeviceId);
|
||||
HAMqtt mqtt(mock, device);
|
||||
mqtt.begin("testHost", "testUser", "testPass");
|
||||
mqtt.setReconnectInterval(50);
|
||||
|
||||
assertEqual((uint16_t)0, mock->getConnectCallsNb());
|
||||
mqtt.loop();
|
||||
assertEqual((uint16_t)1, mock->getConnectCallsNb());
|
||||
|
||||
mock->disconnect();
|
||||
mqtt.loop();
|
||||
assertEqual((uint16_t)1, mock->getConnectCallsNb());
|
||||
|
||||
delay(60);
|
||||
mqtt.loop();
|
||||
assertEqual((uint16_t)2, mock->getConnectCallsNb());
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
delay(1000);
|
||||
|
||||
@@ -377,6 +377,27 @@ AHA_TEST(NumberTest, device_class) {
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(NumberTest, entity_category_setter) {
|
||||
prepareTest
|
||||
|
||||
HANumber number(testUniqueId);
|
||||
number.setEntityCategory("diagnostic");
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
number,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"uniqueNumber\","
|
||||
"\"ent_cat\":\"diagnostic\","
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueNumber/stat_t\","
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueNumber/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(NumberTest, icon_setter) {
|
||||
prepareTest
|
||||
|
||||
|
||||
@@ -150,6 +150,26 @@ AHA_TEST(SensorTest, state_class_setter) {
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(SensorTest, entity_category_setter) {
|
||||
initMqttTest(testDeviceId)
|
||||
|
||||
HASensor sensor(testUniqueId);
|
||||
sensor.setEntityCategory("diagnostic");
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
sensor,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"uniqueSensor\","
|
||||
"\"ent_cat\":\"diagnostic\","
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueSensor/stat_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(SensorTest, force_update_setter) {
|
||||
initMqttTest(testDeviceId)
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
APP_NAME := StdFunctionCallbacksTest
|
||||
ARDUINO_LIBS := AUnit arduino-home-assistant
|
||||
EXTRA_CPPFLAGS := -D ARDUINOHA_TEST
|
||||
EXTRA_CXXFLAGS := -g
|
||||
include ../../../EpoxyDuino/EpoxyDuino.mk
|
||||
@@ -0,0 +1,369 @@
|
||||
#include <AUnit.h>
|
||||
#include <ArduinoHA.h>
|
||||
#include <string.h>
|
||||
|
||||
#define prepareTest \
|
||||
initMqttTest(testDeviceId)
|
||||
|
||||
using aunit::TestRunner;
|
||||
|
||||
static const char* testDeviceId = "testDevice";
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, button_command_callback) {
|
||||
prepareTest
|
||||
|
||||
HAButton button("button");
|
||||
bool called = false;
|
||||
HAButton* caller = nullptr;
|
||||
|
||||
button.onCommand([&](HAButton* sender) {
|
||||
called = true;
|
||||
caller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/button/cmd_t"), F("PRESS"));
|
||||
|
||||
assertTrue(called);
|
||||
assertEqual(&button, caller);
|
||||
}
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, switch_command_callback) {
|
||||
prepareTest
|
||||
|
||||
HASwitch testSwitch("switch");
|
||||
bool called = false;
|
||||
bool state = false;
|
||||
HASwitch* caller = nullptr;
|
||||
|
||||
testSwitch.onCommand([&](bool cmdState, HASwitch* sender) {
|
||||
called = true;
|
||||
state = cmdState;
|
||||
caller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/switch/cmd_t"), F("ON"));
|
||||
|
||||
assertTrue(called);
|
||||
assertTrue(state);
|
||||
assertEqual(&testSwitch, caller);
|
||||
}
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, number_command_callback) {
|
||||
prepareTest
|
||||
|
||||
HANumber number("number");
|
||||
bool called = false;
|
||||
HANumeric value;
|
||||
HANumber* caller = nullptr;
|
||||
|
||||
number.onCommand([&](HANumeric cmdValue, HANumber* sender) {
|
||||
called = true;
|
||||
value = cmdValue;
|
||||
caller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/number/cmd_t"), F("1234"));
|
||||
|
||||
assertTrue(called);
|
||||
assertTrue(HANumeric(1234, 0) == value);
|
||||
assertEqual(&number, caller);
|
||||
}
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, text_command_callback) {
|
||||
prepareTest
|
||||
|
||||
HAText text("text");
|
||||
bool called = false;
|
||||
bool valueMatched = false;
|
||||
HAText* caller = nullptr;
|
||||
|
||||
text.onCommand([&](const char* value, HAText* sender) {
|
||||
called = true;
|
||||
valueMatched = strcmp(value, "hello") == 0;
|
||||
caller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/text/cmd_t"), F("hello"));
|
||||
|
||||
assertTrue(called);
|
||||
assertTrue(valueMatched);
|
||||
assertEqual(&text, caller);
|
||||
}
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, select_command_callback) {
|
||||
prepareTest
|
||||
|
||||
HASelect select("select");
|
||||
select.setOptions("Option A;Option B");
|
||||
bool called = false;
|
||||
int8_t selectedIndex = -1;
|
||||
HASelect* caller = nullptr;
|
||||
|
||||
select.onCommand([&](int8_t index, HASelect* sender) {
|
||||
called = true;
|
||||
selectedIndex = index;
|
||||
caller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/select/cmd_t"), F("Option B"));
|
||||
|
||||
assertTrue(called);
|
||||
assertEqual((int8_t)1, selectedIndex);
|
||||
assertEqual(&select, caller);
|
||||
}
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, scene_command_callback) {
|
||||
prepareTest
|
||||
|
||||
HAScene scene("scene");
|
||||
bool called = false;
|
||||
HAScene* caller = nullptr;
|
||||
|
||||
scene.onCommand([&](HAScene* sender) {
|
||||
called = true;
|
||||
caller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/scene/cmd_t"), F("ON"));
|
||||
|
||||
assertTrue(called);
|
||||
assertEqual(&scene, caller);
|
||||
}
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, cover_command_callback) {
|
||||
prepareTest
|
||||
|
||||
HACover cover("cover");
|
||||
bool called = false;
|
||||
HACover::CoverCommand command = static_cast<HACover::CoverCommand>(0);
|
||||
HACover* caller = nullptr;
|
||||
|
||||
cover.onCommand([&](HACover::CoverCommand cmd, HACover* sender) {
|
||||
called = true;
|
||||
command = cmd;
|
||||
caller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/cover/cmd_t"), F("OPEN"));
|
||||
|
||||
assertTrue(called);
|
||||
assertEqual(HACover::CommandOpen, command);
|
||||
assertEqual(&cover, caller);
|
||||
}
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, lock_command_callback) {
|
||||
prepareTest
|
||||
|
||||
HALock lock("lock");
|
||||
bool called = false;
|
||||
HALock::LockCommand command = static_cast<HALock::LockCommand>(0);
|
||||
HALock* caller = nullptr;
|
||||
|
||||
lock.onCommand([&](HALock::LockCommand cmd, HALock* sender) {
|
||||
called = true;
|
||||
command = cmd;
|
||||
caller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/lock/cmd_t"), F("UNLOCK"));
|
||||
|
||||
assertTrue(called);
|
||||
assertEqual(HALock::CommandUnlock, command);
|
||||
assertEqual(&lock, caller);
|
||||
}
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, fan_command_callbacks) {
|
||||
prepareTest
|
||||
|
||||
HAFan fan("fan", HAFan::SpeedsFeature);
|
||||
bool stateCalled = false;
|
||||
bool state = false;
|
||||
HAFan* stateCaller = nullptr;
|
||||
bool speedCalled = false;
|
||||
uint16_t speed = 0;
|
||||
HAFan* speedCaller = nullptr;
|
||||
|
||||
fan.onStateCommand([&](bool cmdState, HAFan* sender) {
|
||||
stateCalled = true;
|
||||
state = cmdState;
|
||||
stateCaller = sender;
|
||||
});
|
||||
fan.onSpeedCommand([&](uint16_t cmdSpeed, HAFan* sender) {
|
||||
speedCalled = true;
|
||||
speed = cmdSpeed;
|
||||
speedCaller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/fan/cmd_t"), F("ON"));
|
||||
mock->fakeMessage(F("testData/testDevice/fan/pct_cmd_t"), F("50"));
|
||||
|
||||
assertTrue(stateCalled);
|
||||
assertTrue(state);
|
||||
assertEqual(&fan, stateCaller);
|
||||
assertTrue(speedCalled);
|
||||
assertEqual((uint16_t)50, speed);
|
||||
assertEqual(&fan, speedCaller);
|
||||
}
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, light_command_callbacks) {
|
||||
prepareTest
|
||||
|
||||
HALight light(
|
||||
"light",
|
||||
HALight::BrightnessFeature | HALight::ColorTemperatureFeature | HALight::RGBFeature
|
||||
);
|
||||
bool stateCalled = false;
|
||||
bool state = false;
|
||||
HALight* stateCaller = nullptr;
|
||||
bool brightnessCalled = false;
|
||||
uint8_t brightness = 0;
|
||||
HALight* brightnessCaller = nullptr;
|
||||
bool colorTempCalled = false;
|
||||
uint16_t colorTemperature = 0;
|
||||
HALight* colorTempCaller = nullptr;
|
||||
bool rgbCalled = false;
|
||||
HALight::RGBColor rgbColor;
|
||||
HALight* rgbCaller = nullptr;
|
||||
|
||||
light.onStateCommand([&](bool cmdState, HALight* sender) {
|
||||
stateCalled = true;
|
||||
state = cmdState;
|
||||
stateCaller = sender;
|
||||
});
|
||||
light.onBrightnessCommand([&](uint8_t cmdBrightness, HALight* sender) {
|
||||
brightnessCalled = true;
|
||||
brightness = cmdBrightness;
|
||||
brightnessCaller = sender;
|
||||
});
|
||||
light.onColorTemperatureCommand([&](uint16_t cmdTemperature, HALight* sender) {
|
||||
colorTempCalled = true;
|
||||
colorTemperature = cmdTemperature;
|
||||
colorTempCaller = sender;
|
||||
});
|
||||
light.onRGBColorCommand([&](HALight::RGBColor cmdColor, HALight* sender) {
|
||||
rgbCalled = true;
|
||||
rgbColor = cmdColor;
|
||||
rgbCaller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/light/cmd_t"), F("ON"));
|
||||
mock->fakeMessage(F("testData/testDevice/light/bri_cmd_t"), F("42"));
|
||||
mock->fakeMessage(F("testData/testDevice/light/clr_temp_cmd_t"), F("250"));
|
||||
mock->fakeMessage(F("testData/testDevice/light/rgb_cmd_t"), F("1,2,3"));
|
||||
|
||||
assertTrue(stateCalled);
|
||||
assertTrue(state);
|
||||
assertEqual(&light, stateCaller);
|
||||
assertTrue(brightnessCalled);
|
||||
assertEqual((uint8_t)42, brightness);
|
||||
assertEqual(&light, brightnessCaller);
|
||||
assertTrue(colorTempCalled);
|
||||
assertEqual((uint16_t)250, colorTemperature);
|
||||
assertEqual(&light, colorTempCaller);
|
||||
assertTrue(rgbCalled);
|
||||
assertTrue(rgbColor.isSet);
|
||||
assertTrue(rgbColor == HALight::RGBColor(1, 2, 3));
|
||||
assertEqual(&light, rgbCaller);
|
||||
}
|
||||
|
||||
AHA_TEST(StdFunctionCallbacksTest, hvac_command_callbacks) {
|
||||
prepareTest
|
||||
|
||||
HAHVAC hvac(
|
||||
"hvac",
|
||||
HAHVAC::AuxHeatingFeature |
|
||||
HAHVAC::PowerFeature |
|
||||
HAHVAC::FanFeature |
|
||||
HAHVAC::SwingFeature |
|
||||
HAHVAC::ModesFeature |
|
||||
HAHVAC::TargetTemperatureFeature
|
||||
);
|
||||
bool auxCalled = false;
|
||||
bool auxState = false;
|
||||
HAHVAC* auxCaller = nullptr;
|
||||
bool powerCalled = false;
|
||||
bool powerState = true;
|
||||
HAHVAC* powerCaller = nullptr;
|
||||
bool fanModeCalled = false;
|
||||
HAHVAC::FanMode fanMode = HAHVAC::UnknownFanMode;
|
||||
HAHVAC* fanModeCaller = nullptr;
|
||||
bool swingModeCalled = false;
|
||||
HAHVAC::SwingMode swingMode = HAHVAC::UnknownSwingMode;
|
||||
HAHVAC* swingModeCaller = nullptr;
|
||||
bool modeCalled = false;
|
||||
HAHVAC::Mode mode = HAHVAC::UnknownMode;
|
||||
HAHVAC* modeCaller = nullptr;
|
||||
bool targetTempCalled = false;
|
||||
HANumeric targetTemp;
|
||||
HAHVAC* targetTempCaller = nullptr;
|
||||
|
||||
hvac.onAuxStateCommand([&](bool state, HAHVAC* sender) {
|
||||
auxCalled = true;
|
||||
auxState = state;
|
||||
auxCaller = sender;
|
||||
});
|
||||
hvac.onPowerCommand([&](bool state, HAHVAC* sender) {
|
||||
powerCalled = true;
|
||||
powerState = state;
|
||||
powerCaller = sender;
|
||||
});
|
||||
hvac.onFanModeCommand([&](HAHVAC::FanMode cmdMode, HAHVAC* sender) {
|
||||
fanModeCalled = true;
|
||||
fanMode = cmdMode;
|
||||
fanModeCaller = sender;
|
||||
});
|
||||
hvac.onSwingModeCommand([&](HAHVAC::SwingMode cmdMode, HAHVAC* sender) {
|
||||
swingModeCalled = true;
|
||||
swingMode = cmdMode;
|
||||
swingModeCaller = sender;
|
||||
});
|
||||
hvac.onModeCommand([&](HAHVAC::Mode cmdMode, HAHVAC* sender) {
|
||||
modeCalled = true;
|
||||
mode = cmdMode;
|
||||
modeCaller = sender;
|
||||
});
|
||||
hvac.onTargetTemperatureCommand([&](HANumeric cmdTemperature, HAHVAC* sender) {
|
||||
targetTempCalled = true;
|
||||
targetTemp = cmdTemperature;
|
||||
targetTempCaller = sender;
|
||||
});
|
||||
|
||||
mock->fakeMessage(F("testData/testDevice/hvac/aux_cmd_t"), F("ON"));
|
||||
mock->fakeMessage(F("testData/testDevice/hvac/pow_cmd_t"), F("OFF"));
|
||||
mock->fakeMessage(F("testData/testDevice/hvac/fan_mode_cmd_t"), F("high"));
|
||||
mock->fakeMessage(F("testData/testDevice/hvac/swing_mode_cmd_t"), F("on"));
|
||||
mock->fakeMessage(F("testData/testDevice/hvac/mode_cmd_t"), F("heat"));
|
||||
mock->fakeMessage(F("testData/testDevice/hvac/temp_cmd_t"), F("215"));
|
||||
|
||||
assertTrue(auxCalled);
|
||||
assertTrue(auxState);
|
||||
assertEqual(&hvac, auxCaller);
|
||||
assertTrue(powerCalled);
|
||||
assertFalse(powerState);
|
||||
assertEqual(&hvac, powerCaller);
|
||||
assertTrue(fanModeCalled);
|
||||
assertEqual(HAHVAC::HighFanMode, fanMode);
|
||||
assertEqual(&hvac, fanModeCaller);
|
||||
assertTrue(swingModeCalled);
|
||||
assertEqual(HAHVAC::OnSwingMode, swingMode);
|
||||
assertEqual(&hvac, swingModeCaller);
|
||||
assertTrue(modeCalled);
|
||||
assertEqual(HAHVAC::HeatMode, mode);
|
||||
assertEqual(&hvac, modeCaller);
|
||||
assertTrue(targetTempCalled);
|
||||
assertTrue(HANumeric(21.5f, 1) == targetTemp);
|
||||
assertEqual(&hvac, targetTempCaller);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
delay(1000);
|
||||
Serial.begin(115200);
|
||||
while (!Serial);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
TestRunner::run();
|
||||
delay(1);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
APP_NAME := TextTest
|
||||
ARDUINO_LIBS := AUnit arduino-home-assistant
|
||||
EXTRA_CPPFLAGS := "-D ARDUINOHA_TEST"
|
||||
EXTRA_CXXFLAGS := -g
|
||||
include ../../../EpoxyDuino/EpoxyDuino.mk
|
||||
@@ -0,0 +1,351 @@
|
||||
#include <AUnit.h>
|
||||
#include <ArduinoHA.h>
|
||||
|
||||
#define prepareTest \
|
||||
initMqttTest(testDeviceId) \
|
||||
lastCommandCallbackCall.reset();
|
||||
|
||||
#define assertCommandCallbackCalled(expectedValue, callerPtr) \
|
||||
assertTrue(lastCommandCallbackCall.called); \
|
||||
assertEqual(expectedValue, lastCommandCallbackCall.value); \
|
||||
assertEqual(callerPtr, lastCommandCallbackCall.caller);
|
||||
|
||||
#define assertCommandCallbackNotCalled() \
|
||||
assertFalse(lastCommandCallbackCall.called);
|
||||
|
||||
using aunit::TestRunner;
|
||||
|
||||
struct CommandCallback {
|
||||
bool called = false;
|
||||
char value[64] = {0};
|
||||
HAText* caller = nullptr;
|
||||
|
||||
void reset() {
|
||||
called = false;
|
||||
value[0] = 0;
|
||||
caller = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
static const char* testDeviceId = "testDevice";
|
||||
static const char* testUniqueId = "uniqueText";
|
||||
static CommandCallback lastCommandCallbackCall;
|
||||
|
||||
const char ConfigTopic[] PROGMEM = {"homeassistant/text/testDevice/uniqueText/config"};
|
||||
const char StateTopic[] PROGMEM = {"testData/testDevice/uniqueText/stat_t"};
|
||||
const char CommandTopic[] PROGMEM = {"testData/testDevice/uniqueText/cmd_t"};
|
||||
|
||||
void onCommandReceived(const char* value, HAText* caller)
|
||||
{
|
||||
lastCommandCallbackCall.called = true;
|
||||
strncpy(
|
||||
lastCommandCallbackCall.value,
|
||||
value,
|
||||
sizeof(lastCommandCallbackCall.value) - 1
|
||||
);
|
||||
lastCommandCallbackCall.value[sizeof(lastCommandCallbackCall.value) - 1] = 0;
|
||||
lastCommandCallbackCall.caller = caller;
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, invalid_unique_id) {
|
||||
prepareTest
|
||||
|
||||
HAText text(nullptr);
|
||||
text.buildSerializerTest();
|
||||
HASerializer* serializer = text.getSerializer();
|
||||
|
||||
assertTrue(serializer == nullptr);
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, default_params) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
text,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"uniqueText\","
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueText/stat_t\","
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueText/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, extended_unique_id) {
|
||||
prepareTest
|
||||
|
||||
device.enableExtendedUniqueIds();
|
||||
HAText text(testUniqueId);
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
text,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"testDevice_uniqueText\","
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueText/stat_t\","
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueText/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, command_subscription) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
mqtt.loop();
|
||||
|
||||
assertEqual(1, mock->getSubscriptionsNb());
|
||||
assertEqual(AHATOFSTR(CommandTopic), mock->getSubscriptions()[0]->topic);
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, availability) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.setAvailability(true);
|
||||
mqtt.loop();
|
||||
|
||||
// availability is published after config in HAText
|
||||
assertMqttMessage(
|
||||
1,
|
||||
F("testData/testDevice/uniqueText/avty_t"),
|
||||
"online",
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, publish_last_known_state) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.setCurrentState("initial");
|
||||
mqtt.loop();
|
||||
|
||||
assertEqual(2, mock->getFlushedMessagesNb());
|
||||
assertMqttMessage(1, AHATOFSTR(StateTopic), "initial", true)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, publish_nothing_if_retained) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.setRetain(true);
|
||||
text.setCurrentState("initial");
|
||||
mqtt.loop();
|
||||
|
||||
assertEqual(1, mock->getFlushedMessagesNb()); // only config should be pushed
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, name_setter) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.setName("testName");
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
text,
|
||||
(
|
||||
"{"
|
||||
"\"name\":\"testName\","
|
||||
"\"uniq_id\":\"uniqueText\","
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueText/stat_t\","
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueText/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, object_id_setter) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.setObjectId("testId");
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
text,
|
||||
(
|
||||
"{"
|
||||
"\"obj_id\":\"testId\","
|
||||
"\"uniq_id\":\"uniqueText\","
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueText/stat_t\","
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueText/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, icon_setter) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.setIcon("testIcon");
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
text,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"uniqueText\","
|
||||
"\"ic\":\"testIcon\","
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueText/stat_t\","
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueText/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, retain_setter) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.setRetain(true);
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
text,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"uniqueText\","
|
||||
"\"ret\":true,"
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueText/stat_t\","
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueText/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, optimistic_setter) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.setOptimistic(true);
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
text,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"uniqueText\","
|
||||
"\"opt\":true,"
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueText/stat_t\","
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueText/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, mode_setter_password) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.setMode(HAText::ModePassword);
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
text,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"uniqueText\","
|
||||
"\"mode\":\"password\","
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueText/stat_t\","
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueText/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, min_max_pattern_setters) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.setMin(2);
|
||||
text.setMax(40);
|
||||
text.setPattern("^[a-zA-Z0-9]+$");
|
||||
|
||||
assertEntityConfig(
|
||||
mock,
|
||||
text,
|
||||
(
|
||||
"{"
|
||||
"\"uniq_id\":\"uniqueText\","
|
||||
"\"pattern\":\"^[a-zA-Z0-9]+$\","
|
||||
"\"min\":2,"
|
||||
"\"max\":40,"
|
||||
"\"dev\":{\"ids\":\"testDevice\"},"
|
||||
"\"stat_t\":\"testData/testDevice/uniqueText/stat_t\","
|
||||
"\"cmd_t\":\"testData/testDevice/uniqueText/cmd_t\""
|
||||
"}"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, publish_state) {
|
||||
prepareTest
|
||||
|
||||
mock->connectDummy();
|
||||
HAText text(testUniqueId);
|
||||
|
||||
assertTrue(text.setState("new-value"));
|
||||
assertSingleMqttMessage(AHATOFSTR(StateTopic), "new-value", true)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, publish_state_debounce) {
|
||||
prepareTest
|
||||
|
||||
mock->connectDummy();
|
||||
HAText text(testUniqueId);
|
||||
text.setCurrentState("new-value");
|
||||
|
||||
assertTrue(text.setState("new-value"));
|
||||
assertEqual(0, mock->getFlushedMessagesNb());
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, command_callback) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.onCommand(onCommandReceived);
|
||||
mock->fakeMessage(AHATOFSTR(CommandTopic), F("hello"));
|
||||
|
||||
assertCommandCallbackCalled("hello", &text)
|
||||
}
|
||||
|
||||
AHA_TEST(TextTest, different_text_command) {
|
||||
prepareTest
|
||||
|
||||
HAText text(testUniqueId);
|
||||
text.onCommand(onCommandReceived);
|
||||
mock->fakeMessage(
|
||||
F("testData/testDevice/uniqueTextDifferent/cmd_t"),
|
||||
F("hello")
|
||||
);
|
||||
|
||||
assertCommandCallbackNotCalled()
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
delay(1000);
|
||||
Serial.begin(115200);
|
||||
while (!Serial);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
TestRunner::run();
|
||||
delay(1);
|
||||
}
|
||||
Reference in New Issue
Block a user