Modernize MQTT discovery compatibility and runtime refreshes.

Align ArduinoHA with current Home Assistant MQTT discovery expectations by adding default entity IDs, device discovery support, safer runtime discovery refresh behavior, expanded entity category coverage, and the tests/docs needed to support the migration away from legacy object_id payloads.
This commit is contained in:
2026-04-16 19:49:50 +10:00
parent 7894f0c621
commit 7d5d70d525
57 changed files with 2212 additions and 109 deletions
+1
View File
@@ -1,3 +1,4 @@
docsrc/xml
docsrc/build
.DS_Store
tmp/
+19
View File
@@ -1,5 +1,24 @@
# Changelog
## Unreleased
**New features:**
* Added support for the `default_entity_id` (`def_ent_id`) discovery property across device types. New code should prefer `setDefaultEntityId()` over `setObjectId()`.
* Added opt-in MQTT device discovery payload support through `HAMqtt::enableDeviceDiscovery()`, while keeping single-component discovery as the default behavior.
* Added `HABaseDeviceType::removeFromDiscovery()` and `HABaseDeviceType::republishDiscovery()` helpers for explicit discovery lifecycle management.
* Added support for the `entity_category` property across the exposed MQTT device types.
**Fixes:**
* Fixed discovery refresh behavior for runtime config updates so device discovery mode clears stale retained per-entity configs before republishing device payloads.
* Fixed `HANumber::updateMinMaxStep()` so discovery updates follow the active discovery mode instead of always writing a retained per-entity config.
* Ignore empty `unit_of_measurement` values in discovery payloads.
* Allow `HANumber` configurations where `min` is equal to `max`.
**Migration notes:**
* Home Assistant deprecated MQTT `object_id` in favor of `default_entity_id`, and newer Home Assistant versions may warn on or remove `object_id` handling in discovery payloads.
* Existing code using `setObjectId()` remains supported as a legacy fallback, but new projects should migrate to `setDefaultEntityId()`.
* If you enable device discovery mode, avoid publishing per-entity discovery topics manually. Use `republishDiscovery()` when a runtime config change needs to refresh discovery state.
## 2.1.0
**New features:**
+18 -1
View File
@@ -20,6 +20,23 @@ but I successfully use it on ESP8266/ESP8255 boards in my projects.
* Doxygen documentation for all classes
* Covered by unit tests (AUnit + EpoxyDuino + AUniter)
## Discovery Notes
ArduinoHA supports two MQTT discovery modes:
* Single-component discovery, which remains the default behavior and publishes one retained discovery payload per entity.
* Device discovery, which can be enabled explicitly with `HAMqtt::enableDeviceDiscovery()` and publishes a single retained `homeassistant/device/.../config` payload with component mappings under `cmps`.
For entity ID suggestions in Home Assistant, prefer `setDefaultEntityId()` over `setObjectId()`.
`setObjectId()` is still available as a legacy fallback, but newer Home Assistant versions are moving toward `default_entity_id`.
If you need to manage discovery at runtime:
* Use `HABaseDeviceType::republishDiscovery()` after changing discovery-relevant config at runtime.
* Use `HABaseDeviceType::removeFromDiscovery()` to clear the retained discovery payload for a single entity.
When device discovery mode is enabled, runtime discovery refreshes automatically clear any stale retained per-entity config before republishing the device discovery payload.
## Supported HA types
| Home Assistant type | Supported |
@@ -47,7 +64,7 @@ but I successfully use it on ESP8266/ESP8255 boards in my projects.
| Switch | ✅ |
| Update | ❌ |
| Tag scanner | ✅ |
| Text | ❌ |
| Text | ✅ |
| Vacuum | ❌ |
| Valve | ❌ |
| Water heater | ❌ |
+3
View File
@@ -29,6 +29,9 @@
#define ARDUINOHA_ENABLE_STDFUNCTION
#endif
// Current library version used in discovery origin metadata.
#define ARDUINOHA_LIBRARY_VERSION "2.1.0"
#if defined(ARDUINOHA_DEBUG)
#include <Arduino.h>
+150
View File
@@ -1,5 +1,7 @@
#include "HAMqtt.h"
#include <cstdio>
#ifndef ARDUINOHA_TEST
#include <PubSubClient.h>
#endif
@@ -7,6 +9,8 @@
#include "HADevice.h"
#include "device-types/HABaseDeviceType.h"
#include "mocks/PubSubClientMock.h"
#include "utils/HADictionary.h"
#include "utils/HASerializer.h"
#define HAMQTT_INIT \
_device(device), \
@@ -17,6 +21,7 @@
_initialized(false), \
_discoveryPrefix(DefaultDiscoveryPrefix), \
_dataPrefix(DefaultDataPrefix), \
_deviceDiscoveryEnabled(false), \
_username(nullptr), \
_password(nullptr), \
_lastConnectionAttemptAt(0), \
@@ -31,6 +36,7 @@
static const char* DefaultDiscoveryPrefix = "homeassistant";
static const char* DefaultDataPrefix = "aha";
static const char* DeviceDiscoveryOriginName = "ArduinoHA";
HAMqtt* HAMqtt::_instance = nullptr;
@@ -332,6 +338,10 @@ void HAMqtt::connectToServer()
void HAMqtt::onConnectedLogic()
{
if (_deviceDiscoveryEnabled) {
publishDeviceDiscovery();
}
if (_connectedCallback) {
_connectedCallback();
}
@@ -343,6 +353,146 @@ void HAMqtt::onConnectedLogic()
}
}
bool HAMqtt::publishDeviceDiscovery()
{
if (!_device.getUniqueId()) {
return false;
}
const HASerializer* deviceSerializer = _device.getSerializer();
if (!deviceSerializer) {
return false;
}
HABaseDeviceType* componentTypes[_devicesTypesNb];
HASerializer* componentSerializers[_devicesTypesNb];
uint8_t componentSerializerCount = 0;
uint16_t componentsPayloadLength = 2; // {}
for (uint8_t i = 0; i < _devicesTypesNb; i++) {
HABaseDeviceType* deviceType = _devicesTypes[i];
if (!deviceType || !deviceType->supportsDeviceDiscovery()) {
continue;
}
HASerializer* serializer = deviceType->buildDeviceDiscoverySerializer();
if (!serializer) {
continue;
}
if (componentSerializerCount > 0) {
componentsPayloadLength += strlen_P(HASerializerJsonPropertiesSeparator);
}
componentsPayloadLength +=
strlen_P(HASerializerJsonPropertyPrefix) +
strlen(deviceType->uniqueId()) +
strlen_P(HASerializerJsonPropertySuffix) +
serializer->calculateSize();
componentTypes[componentSerializerCount] = deviceType;
componentSerializers[componentSerializerCount++] = serializer;
}
if (componentSerializerCount == 0) {
return false;
}
char originPayload[64];
originPayload[0] = 0;
snprintf(
originPayload,
sizeof(originPayload),
"{\"name\":\"%s\",\"sw\":\"%s\"}",
DeviceDiscoveryOriginName,
ARDUINOHA_LIBRARY_VERSION
);
const uint16_t originPayloadLength = strlen(originPayload);
const uint16_t topicLength =
strlen(_discoveryPrefix) + 1 +
strlen_P(HAComponentDevice) + 1 +
strlen(_device.getUniqueId()) + 1 +
strlen_P(HAConfigTopic) + 1;
if (topicLength == 0) {
for (uint8_t i = 0; i < componentSerializerCount; i++) {
delete componentSerializers[i];
}
return false;
}
const uint16_t payloadLength =
strlen_P(HASerializerJsonDataPrefix) +
strlen_P(HASerializerJsonPropertyPrefix) +
strlen_P(HADeviceProperty) +
strlen_P(HASerializerJsonPropertySuffix) +
deviceSerializer->calculateSize() +
strlen_P(HASerializerJsonPropertiesSeparator) +
strlen_P(HASerializerJsonPropertyPrefix) +
strlen_P(HAOriginProperty) +
strlen_P(HASerializerJsonPropertySuffix) +
originPayloadLength +
strlen_P(HASerializerJsonPropertiesSeparator) +
strlen_P(HASerializerJsonPropertyPrefix) +
strlen_P(HAComponentsProperty) +
strlen_P(HASerializerJsonPropertySuffix) +
componentsPayloadLength +
strlen_P(HASerializerJsonDataSuffix);
char topic[topicLength];
strcpy(topic, _discoveryPrefix);
strcat_P(topic, HASerializerSlash);
strcat_P(topic, HAComponentDevice);
strcat_P(topic, HASerializerSlash);
strcat(topic, _device.getUniqueId());
strcat_P(topic, HASerializerSlash);
strcat_P(topic, HAConfigTopic);
if (!_mqtt->beginPublish(topic, payloadLength, true)) {
for (uint8_t i = 0; i < componentSerializerCount; i++) {
delete componentSerializers[i];
}
return false;
}
writePayload(AHATOFSTR(HASerializerJsonDataPrefix));
writePayload(AHATOFSTR(HASerializerJsonPropertyPrefix));
writePayload(AHATOFSTR(HADeviceProperty));
writePayload(AHATOFSTR(HASerializerJsonPropertySuffix));
deviceSerializer->flush();
writePayload(AHATOFSTR(HASerializerJsonPropertiesSeparator));
writePayload(AHATOFSTR(HASerializerJsonPropertyPrefix));
writePayload(AHATOFSTR(HAOriginProperty));
writePayload(AHATOFSTR(HASerializerJsonPropertySuffix));
writePayload(originPayload, originPayloadLength);
writePayload(AHATOFSTR(HASerializerJsonPropertiesSeparator));
writePayload(AHATOFSTR(HASerializerJsonPropertyPrefix));
writePayload(AHATOFSTR(HAComponentsProperty));
writePayload(AHATOFSTR(HASerializerJsonPropertySuffix));
writePayload(AHATOFSTR(HASerializerJsonDataPrefix));
for (uint8_t i = 0; i < componentSerializerCount; i++) {
if (i > 0) {
writePayload(AHATOFSTR(HASerializerJsonPropertiesSeparator));
}
writePayload(AHATOFSTR(HASerializerJsonPropertyPrefix));
writePayload(componentTypes[i]->uniqueId(), strlen(componentTypes[i]->uniqueId()));
writePayload(AHATOFSTR(HASerializerJsonPropertySuffix));
componentSerializers[i]->flush();
delete componentSerializers[i];
}
writePayload(AHATOFSTR(HASerializerJsonDataSuffix));
writePayload(AHATOFSTR(HASerializerJsonDataSuffix));
return endPublish();
}
void HAMqtt::setState(ConnectionState state)
{
ConnectionState previousState = _currentState;
+22
View File
@@ -117,6 +117,25 @@ public:
inline const char* getDataPrefix() const
{ return _dataPrefix; }
/**
* Enables MQTT device discovery payloads.
* Single-component discovery remains the default when this is not enabled.
*/
inline void enableDeviceDiscovery()
{ _deviceDiscoveryEnabled = true; }
/**
* Returns true when MQTT device discovery payloads are enabled.
*/
inline bool isDeviceDiscoveryEnabled() const
{ return _deviceDiscoveryEnabled; }
/**
* Republishes the current MQTT device discovery payload when device
* discovery is enabled.
*/
bool publishDeviceDiscovery();
/**
* Returns instance of the device assigned to the HAMqtt class.
* It's the same object (pointer) that was passed to the HAMqtt constructor.
@@ -448,6 +467,9 @@ private:
/// The data prefix that's used for publishing data messages.
const char* _dataPrefix;
/// Enables MQTT device discovery mode when set to true.
bool _deviceDiscoveryEnabled;
/// The username used for the authentication. It's set in the HAMqtt::begin method.
const char* _username;
+85 -3
View File
@@ -12,6 +12,8 @@ HABaseDeviceType::HABaseDeviceType(
_uniqueId(uniqueId),
_name(nullptr),
_objectId(nullptr),
_defaultEntityId(nullptr),
_entityCategory(nullptr),
_serializer(nullptr),
_availability(AvailabilityDefault)
{
@@ -31,6 +33,46 @@ void HABaseDeviceType::setAvailability(bool online)
publishAvailability();
}
bool HABaseDeviceType::removeFromDiscovery()
{
const uint16_t topicLength = HASerializer::calculateConfigTopicLength(
componentName(),
uniqueId()
);
if (topicLength == 0) {
return false;
}
char topic[topicLength];
if (!HASerializer::generateConfigTopic(topic, componentName(), uniqueId())) {
return false;
}
destroySerializer();
if (!mqtt()->beginPublish(topic, 0, true)) {
return false;
}
return mqtt()->endPublish();
}
bool HABaseDeviceType::republishDiscovery()
{
HAMqtt* mqttInstance = mqtt();
if (!mqttInstance) {
return false;
}
if (shouldPublishSingleComponentConfig()) {
return publishConfig();
}
// Clear any stale per-entity retained config so device discovery remains
// the single source of truth for supported entities.
removeFromDiscovery();
return mqttInstance->publishDeviceDiscovery();
}
HAMqtt* HABaseDeviceType::mqtt()
{
return HAMqtt::instance();
@@ -80,12 +122,12 @@ void HABaseDeviceType::destroySerializer()
}
}
void HABaseDeviceType::publishConfig()
bool HABaseDeviceType::publishConfig()
{
buildSerializer();
if (_serializer == nullptr) {
return;
return false;
}
const uint16_t topicLength = HASerializer::calculateConfigTopicLength(
@@ -94,6 +136,7 @@ void HABaseDeviceType::publishConfig()
);
const uint16_t dataLength = _serializer->calculateSize();
bool published = false;
if (topicLength > 0 && dataLength > 0) {
char topic[topicLength];
HASerializer::generateConfigTopic(
@@ -104,11 +147,12 @@ void HABaseDeviceType::publishConfig()
if (mqtt()->beginPublish(topic, dataLength, true)) {
_serializer->flush();
mqtt()->endPublish();
published = mqtt()->endPublish();
}
}
destroySerializer();
return published;
}
void HABaseDeviceType::publishAvailability()
@@ -209,3 +253,41 @@ bool HABaseDeviceType::publishOnDataTopic(
return false;
}
bool HABaseDeviceType::shouldPublishSingleComponentConfig() const
{
return !mqtt() || !mqtt()->isDeviceDiscoveryEnabled() || !supportsDeviceDiscovery();
}
void HABaseDeviceType::setEntityIdProperty(HASerializer* serializer) const
{
if (!serializer) {
return;
}
const char* defaultEntityId = nonEmptyString(_defaultEntityId);
if (defaultEntityId) {
serializer->set(AHATOFSTR(HADefaultEntityIdProperty), defaultEntityId);
return;
}
const char* objectId = nonEmptyString(_objectId);
if (objectId) {
serializer->set(AHATOFSTR(HAObjectIdProperty), objectId);
}
}
const char* HABaseDeviceType::nonEmptyString(const char* value)
{
return (value && value[0] != '\0') ? value : nullptr;
}
HASerializer* HABaseDeviceType::buildDeviceDiscoverySerializer()
{
return nullptr;
}
bool HABaseDeviceType::supportsDeviceDiscovery() const
{
return false;
}
+79 -2
View File
@@ -81,9 +81,26 @@ public:
{ return _name; }
/**
* Sets object ID that will be used by HA to generate entity ID.
* Sets the default entity ID that Home Assistant should use when creating
* the entity for the first time.
* Keep the ID short to save the resources.
*
* @param entityId The default entity ID.
*/
inline void setDefaultEntityId(const char* entityId)
{ _defaultEntityId = entityId; }
/**
* Returns the default entity ID that was assigned via setDefaultEntityId.
* It can be nullptr if there is no ID assigned.
*/
inline const char* getDefaultEntityId() const
{ return _defaultEntityId; }
/**
* Legacy alias for the MQTT `object_id` discovery property.
* Prefer setDefaultEntityId() for new code.
*
* @param objectId The object ID.
*/
inline void setObjectId(const char* objectId)
@@ -96,6 +113,20 @@ public:
inline const char* getObjectId() const
{ return _objectId; }
/**
* Sets the entity category for this entity.
*
* @param entityCategory The category name.
*/
inline void setEntityCategory(const char* entityCategory)
{ _entityCategory = entityCategory; }
/**
* Returns the entity category for this entity.
*/
inline const char* getEntityCategory() const
{ return _entityCategory; }
/**
* Sets availability of the device type.
* Setting the initial availability enables availability reporting for this device type.
@@ -106,6 +137,17 @@ public:
*/
virtual void setAvailability(bool online);
/**
* Removes this entity from MQTT discovery by publishing an empty retained
* payload on its config topic.
*/
bool removeFromDiscovery();
/**
* Republishes MQTT discovery config for this entity.
*/
bool republishDiscovery();
#ifdef ARDUINOHA_TEST
inline HASerializer* getSerializer() const
{ return _serializer; }
@@ -131,6 +173,12 @@ protected:
const __FlashStringHelper* topic
);
/**
* Returns true if this entity should publish the single-component
* discovery config on connect.
*/
bool shouldPublishSingleComponentConfig() const;
/**
* This method should build serializer that will be used for publishing the configuration.
* The serializer is built each time the MQTT connection is acquired.
@@ -167,7 +215,7 @@ protected:
/**
* Publishes configuration of this device type on the HA discovery topic.
*/
void publishConfig();
bool publishConfig();
/**
* Publishes current availability of the device type.
@@ -218,6 +266,29 @@ protected:
bool isProgmemData = false
);
/**
* Adds the preferred entity ID property to the serializer.
* `default_entity_id` takes precedence and the legacy `object_id` is only
* emitted when no default entity ID was configured.
*/
void setEntityIdProperty(HASerializer* serializer) const;
/**
* Returns a string pointer if it is non-empty, otherwise nullptr.
*/
static const char* nonEmptyString(const char* value);
/**
* Builds a serializer used as a device discovery component payload.
* Unsupported entities return nullptr.
*/
virtual HASerializer* buildDeviceDiscoverySerializer();
/**
* Returns true when the entity can be included in MQTT device discovery.
*/
virtual bool supportsDeviceDiscovery() const;
/// The component name that was assigned via the constructor.
const __FlashStringHelper* const _componentName;
@@ -230,6 +301,12 @@ protected:
/// The object ID that was set using setObjectId method. It can be nullptr.
const char* _objectId;
/// The default entity ID used by Home Assistant on first discovery.
const char* _defaultEntityId;
/// The entity category for the entity. It can be nullptr.
const char* _entityCategory;
/// HASerializer that belongs to this device type. It can be nullptr.
HASerializer* _serializer;
+37 -4
View File
@@ -7,7 +7,6 @@
HABinarySensor::HABinarySensor(const char* uniqueId) :
HABaseDeviceType(AHATOFSTR(HAComponentBinarySensor), uniqueId),
_class(nullptr),
_entityCategory(nullptr),
_icon(nullptr),
_currentState(false)
{
@@ -45,10 +44,10 @@ void HABinarySensor::buildSerializer()
_serializer = new HASerializer(this, 10); // 10 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
_serializer->set(AHATOFSTR(HAStateEntityCategory), _entityCategory);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_expireAfter.isSet()) {
@@ -64,13 +63,47 @@ void HABinarySensor::buildSerializer()
_serializer->topic(AHATOFSTR(HAStateTopic));
}
HASerializer* HABinarySensor::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 10);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentBinarySensor),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_expireAfter.isSet()) {
serializer->set(
AHATOFSTR(HAExpireAfterProperty),
&_expireAfter,
HASerializer::NumberPropertyType
);
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
return serializer;
}
void HABinarySensor::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
publishState(_currentState);
}
+3 -12
View File
@@ -63,15 +63,6 @@ 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`).
@@ -83,6 +74,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
private:
@@ -97,9 +91,6 @@ 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;
+37 -4
View File
@@ -7,7 +7,6 @@
HAButton::HAButton(const char* uniqueId) :
HABaseDeviceType(AHATOFSTR(HAComponentButton), uniqueId),
_class(nullptr),
_entityCategory(nullptr),
_icon(nullptr),
_retain(false),
_commandCallback(nullptr)
@@ -23,10 +22,10 @@ void HAButton::buildSerializer()
_serializer = new HASerializer(this, 10); // 10 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
_serializer->set(AHATOFSTR(HAStateEntityCategory), _entityCategory);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
// optional property
@@ -43,13 +42,47 @@ void HAButton::buildSerializer()
_serializer->topic(AHATOFSTR(HACommandTopic));
}
HASerializer* HAButton::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 10);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentButton),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HACommandTopic));
return serializer;
}
void HAButton::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
}
+3 -12
View File
@@ -36,15 +36,6 @@ 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`).
@@ -93,6 +84,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
@@ -104,9 +98,6 @@ 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;
+33 -3
View File
@@ -27,10 +27,11 @@ void HACamera::buildSerializer()
return;
}
_serializer = new HASerializer(this, 8); // 8 - max properties nb
_serializer = new HASerializer(this, 9); // 9 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
_serializer->set(
AHATOFSTR(HAEncodingProperty),
@@ -42,13 +43,42 @@ void HACamera::buildSerializer()
_serializer->topic(AHATOFSTR(HATopic));
}
HASerializer* HACamera::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 9);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentCamera),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
serializer->set(
AHATOFSTR(HAEncodingProperty),
getEncodingProperty(),
HASerializer::ProgmemPropertyValue
);
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HATopic));
return serializer;
}
void HACamera::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
}
+3
View File
@@ -57,6 +57,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
private:
+52 -3
View File
@@ -57,11 +57,12 @@ void HACover::buildSerializer()
return;
}
_serializer = new HASerializer(this, 12); // 12 - max properties nb
_serializer = new HASerializer(this, 13); // 13 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
@@ -90,13 +91,61 @@ void HACover::buildSerializer()
}
}
HASerializer* HACover::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 13);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentCover),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
if (_optimistic) {
serializer->set(
AHATOFSTR(HAOptimisticProperty),
&_optimistic,
HASerializer::BoolPropertyType
);
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
serializer->topic(AHATOFSTR(HACommandTopic));
if (_features & PositionFeature) {
serializer->topic(AHATOFSTR(HAPositionTopic));
}
return serializer;
}
void HACover::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
if (!_retain) {
+3
View File
@@ -172,6 +172,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
+33 -3
View File
@@ -33,10 +33,11 @@ void HADeviceTracker::buildSerializer()
return;
}
_serializer = new HASerializer(this, 8); // 8 - max properties nb
_serializer = new HASerializer(this, 9); // 9 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
_serializer->set(
AHATOFSTR(HASourceTypeProperty),
@@ -48,13 +49,42 @@ void HADeviceTracker::buildSerializer()
_serializer->topic(AHATOFSTR(HAStateTopic));
}
HASerializer* HADeviceTracker::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 9);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentDeviceTracker),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
serializer->set(
AHATOFSTR(HASourceTypeProperty),
getSourceTypeProperty(),
HASerializer::ProgmemPropertyValue
);
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
return serializer;
}
void HADeviceTracker::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
publishState(_currentState);
}
+3
View File
@@ -84,6 +84,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
private:
+38 -1
View File
@@ -90,13 +90,50 @@ void HADeviceTrigger::buildSerializer()
_serializer->topic(AHATOFSTR(HATopic));
}
HASerializer* HADeviceTrigger::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 6);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentDeviceAutomation),
HASerializer::ProgmemPropertyValue
);
serializer->set(
AHATOFSTR(HAAutomationTypeProperty),
AHATOFSTR(HATrigger),
HASerializer::ProgmemPropertyValue
);
serializer->set(
AHATOFSTR(HATypeProperty),
_type,
_isProgmemType
? HASerializer::ProgmemPropertyValue
: HASerializer::ConstCharPropertyValue
);
serializer->set(
AHATOFSTR(HASubtypeProperty),
_subtype,
_isProgmemSubtype
? HASerializer::ProgmemPropertyValue
: HASerializer::ConstCharPropertyValue
);
serializer->topic(AHATOFSTR(HATopic));
return serializer;
}
void HADeviceTrigger::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
}
uint16_t HADeviceTrigger::calculateIdSize() const
+3
View File
@@ -127,6 +127,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
private:
+67 -3
View File
@@ -59,10 +59,11 @@ void HAFan::buildSerializer()
return;
}
_serializer = new HASerializer(this, 14); // 14 - max properties nb
_serializer = new HASerializer(this, 15); // 15 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
@@ -108,13 +109,76 @@ void HAFan::buildSerializer()
_serializer->topic(AHATOFSTR(HACommandTopic));
}
HASerializer* HAFan::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 15);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentFan),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
if (_optimistic) {
serializer->set(
AHATOFSTR(HAOptimisticProperty),
&_optimistic,
HASerializer::BoolPropertyType
);
}
if (_features & SpeedsFeature) {
serializer->topic(AHATOFSTR(HAPercentageStateTopic));
serializer->topic(AHATOFSTR(HAPercentageCommandTopic));
if (_speedRangeMax.isSet()) {
serializer->set(
AHATOFSTR(HASpeedRangeMaxProperty),
&_speedRangeMax,
HASerializer::NumberPropertyType
);
}
if (_speedRangeMin.isSet()) {
serializer->set(
AHATOFSTR(HASpeedRangeMinProperty),
&_speedRangeMin,
HASerializer::NumberPropertyType
);
}
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
serializer->topic(AHATOFSTR(HACommandTopic));
return serializer;
}
void HAFan::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
if (!_retain) {
+3
View File
@@ -210,6 +210,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
+189 -3
View File
@@ -191,10 +191,11 @@ void HAHVAC::buildSerializer()
return;
}
_serializer = new HASerializer(this, 28); // 28 - max properties nb
_serializer = new HASerializer(this, 29); // 29 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
@@ -362,13 +363,198 @@ void HAHVAC::buildSerializer()
_serializer->set(HASerializer::WithAvailability);
}
HASerializer* HAHVAC::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 29);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentClimate),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
if (_features & ActionFeature) {
serializer->topic(AHATOFSTR(HAActionTopic));
}
if (_features & AuxHeatingFeature) {
serializer->topic(AHATOFSTR(HAAuxCommandTopic));
serializer->topic(AHATOFSTR(HAAuxStateTopic));
}
if (_features & PowerFeature) {
serializer->topic(AHATOFSTR(HAPowerCommandTopic));
}
if (_features & FanFeature) {
serializer->topic(AHATOFSTR(HAFanModeCommandTopic));
serializer->topic(AHATOFSTR(HAFanModeStateTopic));
if (_fanModes != DefaultFanModes) {
_fanModesSerializer->clear();
if (_fanModes & AutoFanMode) {
_fanModesSerializer->add(HAFanModeAuto);
}
if (_fanModes & LowFanMode) {
_fanModesSerializer->add(HAFanModeLow);
}
if (_fanModes & MediumFanMode) {
_fanModesSerializer->add(HAFanModeMedium);
}
if (_fanModes & HighFanMode) {
_fanModesSerializer->add(HAFanModeHigh);
}
serializer->set(
AHATOFSTR(HAFanModesProperty),
_fanModesSerializer,
HASerializer::ArrayPropertyType
);
}
}
if (_features & SwingFeature) {
serializer->topic(AHATOFSTR(HASwingModeCommandTopic));
serializer->topic(AHATOFSTR(HASwingModeStateTopic));
if (_swingModes != DefaultSwingModes) {
_swingModesSerializer->clear();
if (_swingModes & OnSwingMode) {
_swingModesSerializer->add(HASwingModeOn);
}
if (_swingModes & OffSwingMode) {
_swingModesSerializer->add(HASwingModeOff);
}
serializer->set(
AHATOFSTR(HASwingModesProperty),
_swingModesSerializer,
HASerializer::ArrayPropertyType
);
}
}
if (_features & ModesFeature) {
serializer->topic(AHATOFSTR(HAModeCommandTopic));
serializer->topic(AHATOFSTR(HAModeStateTopic));
if (_modes != DefaultModes) {
_modesSerializer->clear();
if (_modes & AutoMode) {
_modesSerializer->add(HAModeAuto);
}
if (_modes & OffMode) {
_modesSerializer->add(HAModeOff);
}
if (_modes & CoolMode) {
_modesSerializer->add(HAModeCool);
}
if (_modes & HeatMode) {
_modesSerializer->add(HAModeHeat);
}
if (_modes & DryMode) {
_modesSerializer->add(HAModeDry);
}
if (_modes & FanOnlyMode) {
_modesSerializer->add(HAModeFanOnly);
}
serializer->set(
AHATOFSTR(HAModesProperty),
_modesSerializer,
HASerializer::ArrayPropertyType
);
}
}
if (_features & TargetTemperatureFeature) {
serializer->topic(AHATOFSTR(HATemperatureCommandTopic));
serializer->topic(AHATOFSTR(HATemperatureStateTopic));
serializer->set(
AHATOFSTR(HATemperatureCommandTemplateProperty),
getCommandWithFloatTemplate(),
HASerializer::ProgmemPropertyValue
);
}
if (_temperatureUnit != DefaultUnit) {
const __FlashStringHelper *unitStr = _temperatureUnit == CelsiusUnit
? AHATOFSTR(HATemperatureUnitC)
: AHATOFSTR(HATemperatureUnitF);
serializer->set(
AHATOFSTR(HATemperatureUnitProperty),
unitStr,
HASerializer::ProgmemPropertyValue
);
}
if (_minTemp.isSet()) {
serializer->set(
AHATOFSTR(HAMinTempProperty),
&_minTemp,
HASerializer::NumberPropertyType
);
}
if (_maxTemp.isSet()) {
serializer->set(
AHATOFSTR(HAMaxTempProperty),
&_maxTemp,
HASerializer::NumberPropertyType
);
}
if (_tempStep.isSet()) {
serializer->set(
AHATOFSTR(HATempStepProperty),
&_tempStep,
HASerializer::NumberPropertyType
);
}
serializer->topic(AHATOFSTR(HACurrentTemperatureTopic));
serializer->set(HASerializer::WithAvailability);
return serializer;
}
void HAHVAC::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
if (!_retain) {
+3
View File
@@ -596,6 +596,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
+85 -3
View File
@@ -134,10 +134,11 @@ void HALight::buildSerializer()
return;
}
_serializer = new HASerializer(this, 19); // 19 - max properties nb
_serializer = new HASerializer(this, 20); // 20 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
@@ -201,13 +202,94 @@ void HALight::buildSerializer()
_serializer->topic(AHATOFSTR(HACommandTopic));
}
HASerializer* HALight::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 20);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentLight),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
if (_optimistic) {
serializer->set(
AHATOFSTR(HAOptimisticProperty),
&_optimistic,
HASerializer::BoolPropertyType
);
}
if (_features & BrightnessFeature) {
serializer->topic(AHATOFSTR(HABrightnessStateTopic));
serializer->topic(AHATOFSTR(HABrightnessCommandTopic));
if (_brightnessScale.isSet()) {
serializer->set(
AHATOFSTR(HABrightnessScaleProperty),
&_brightnessScale,
HASerializer::NumberPropertyType
);
}
}
if (_features & ColorTemperatureFeature) {
serializer->topic(AHATOFSTR(HAColorTemperatureStateTopic));
serializer->topic(AHATOFSTR(HAColorTemperatureCommandTopic));
if (_minMireds.isSet()) {
serializer->set(
AHATOFSTR(HAMinMiredsProperty),
&_minMireds,
HASerializer::NumberPropertyType
);
}
if (_maxMireds.isSet()) {
serializer->set(
AHATOFSTR(HAMaxMiredsProperty),
&_maxMireds,
HASerializer::NumberPropertyType
);
}
}
if (_features & RGBFeature) {
serializer->topic(AHATOFSTR(HARGBCommandTopic));
serializer->topic(AHATOFSTR(HARGBStateTopic));
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
serializer->topic(AHATOFSTR(HACommandTopic));
return serializer;
}
void HALight::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
if (!_retain) {
+3
View File
@@ -373,6 +373,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
+46 -3
View File
@@ -38,10 +38,11 @@ void HALock::buildSerializer()
return;
}
_serializer = new HASerializer(this, 10); // 10 - max properties nb
_serializer = new HASerializer(this, 11); // 11 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
@@ -66,13 +67,55 @@ void HALock::buildSerializer()
_serializer->topic(AHATOFSTR(HACommandTopic));
}
HASerializer* HALock::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 11);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentLock),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
if (_optimistic) {
serializer->set(
AHATOFSTR(HAOptimisticProperty),
&_optimistic,
HASerializer::BoolPropertyType
);
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
serializer->topic(AHATOFSTR(HACommandTopic));
return serializer;
}
void HALock::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
if (!_retain) {
+3
View File
@@ -127,6 +127,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
+83 -6
View File
@@ -8,7 +8,6 @@ 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,7 +41,7 @@ void HANumber::updateMinMaxStep(const float min, const float max, const float st
_minValue = HANumeric(min, _precision);
_maxValue = HANumeric(max, _precision);
_step = HANumeric(step, _precision);
publishConfig();
republishDiscovery();
}
void HANumber::buildSerializer()
@@ -53,12 +52,12 @@ void HANumber::buildSerializer()
_serializer = new HASerializer(this, 17); // 17 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
_serializer->set(AHATOFSTR(HAStateEntityCategory), _entityCategory);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
_serializer->set(AHATOFSTR(HAUnitOfMeasurementProperty), _unitOfMeasurement);
_serializer->set(AHATOFSTR(HAUnitOfMeasurementProperty), nonEmptyString(_unitOfMeasurement));
_serializer->set(
AHATOFSTR(HAModeProperty),
getModeProperty(),
@@ -116,13 +115,91 @@ void HANumber::buildSerializer()
_serializer->topic(AHATOFSTR(HACommandTopic));
}
HASerializer* HANumber::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 17);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentNumber),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
serializer->set(AHATOFSTR(HAUnitOfMeasurementProperty), nonEmptyString(_unitOfMeasurement));
serializer->set(
AHATOFSTR(HAModeProperty),
getModeProperty(),
HASerializer::ProgmemPropertyValue
);
serializer->set(
AHATOFSTR(HACommandTemplateProperty),
getCommandTemplate(),
HASerializer::ProgmemPropertyValue
);
if (_minValue.isSet()) {
serializer->set(
AHATOFSTR(HAMinProperty),
&_minValue,
HASerializer::NumberPropertyType
);
}
if (_maxValue.isSet()) {
serializer->set(
AHATOFSTR(HAMaxProperty),
&_maxValue,
HASerializer::NumberPropertyType
);
}
if (_step.isSet()) {
serializer->set(
AHATOFSTR(HAStepProperty),
&_step,
HASerializer::NumberPropertyType
);
}
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
if (_optimistic) {
serializer->set(
AHATOFSTR(HAOptimisticProperty),
&_optimistic,
HASerializer::BoolPropertyType
);
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
serializer->topic(AHATOFSTR(HACommandTopic));
return serializer;
}
void HANumber::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
if (!_retain) {
+3 -12
View File
@@ -110,15 +110,6 @@ 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`).
@@ -230,6 +221,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
@@ -270,9 +264,6 @@ 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;
+42 -3
View File
@@ -22,10 +22,11 @@ void HAScene::buildSerializer()
return;
}
_serializer = new HASerializer(this, 8); // 8 - max properties nb
_serializer = new HASerializer(this, 9); // 9 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
// optional property
@@ -48,13 +49,51 @@ void HAScene::buildSerializer()
_serializer->topic(AHATOFSTR(HACommandTopic));
}
HASerializer* HAScene::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 9);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentScene),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
serializer->set(
AHATOFSTR(HAPayloadOnProperty),
AHATOFSTR(HAStateOn),
HASerializer::ProgmemPropertyValue
);
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HACommandTopic));
return serializer;
}
void HAScene::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
subscribeTopic(uniqueId(), AHATOFSTR(HACommandTopic));
}
+3
View File
@@ -74,6 +74,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
+51 -3
View File
@@ -102,10 +102,11 @@ void HASelect::buildSerializer()
return;
}
_serializer = new HASerializer(this, 11); // 11 - max properties nb
_serializer = new HASerializer(this, 12); // 12 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
_serializer->set(
AHATOFSTR(HAOptionsProperty),
@@ -135,13 +136,60 @@ void HASelect::buildSerializer()
_serializer->topic(AHATOFSTR(HACommandTopic));
}
HASerializer* HASelect::buildDeviceDiscoverySerializer()
{
if (!uniqueId() || !_options) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 12);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentSelect),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
serializer->set(
AHATOFSTR(HAOptionsProperty),
_options,
HASerializer::ArrayPropertyType
);
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
if (_optimistic) {
serializer->set(
AHATOFSTR(HAOptimisticProperty),
&_optimistic,
HASerializer::BoolPropertyType
);
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
serializer->topic(AHATOFSTR(HACommandTopic));
return serializer;
}
void HASelect::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
if (!_retain) {
+3
View File
@@ -141,6 +141,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
+52 -5
View File
@@ -9,7 +9,6 @@ HASensor::HASensor(const char* uniqueId, const uint16_t features) :
_features(features),
_deviceClass(nullptr),
_stateClass(nullptr),
_entityCategory(nullptr),
_forceUpdate(false),
_icon(nullptr),
_unitOfMeasurement(nullptr),
@@ -49,13 +48,13 @@ void HASensor::buildSerializer()
_serializer = new HASerializer(this, 14); // 14 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HADeviceClassProperty), _deviceClass);
_serializer->set(AHATOFSTR(HAStateClassProperty), _stateClass);
_serializer->set(AHATOFSTR(HAStateEntityCategory), _entityCategory);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
_serializer->set(AHATOFSTR(HAUnitOfMeasurementProperty), _unitOfMeasurement);
_serializer->set(AHATOFSTR(HAUnitOfMeasurementProperty), nonEmptyString(_unitOfMeasurement));
if (_forceUpdate) {
_serializer->set(
@@ -82,13 +81,61 @@ void HASensor::buildSerializer()
_serializer->topic(AHATOFSTR(HAStateTopic));
}
HASerializer* HASensor::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 14);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentSensor),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HADeviceClassProperty), _deviceClass);
serializer->set(AHATOFSTR(HAStateClassProperty), _stateClass);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
serializer->set(AHATOFSTR(HAUnitOfMeasurementProperty), nonEmptyString(_unitOfMeasurement));
if (_forceUpdate) {
serializer->set(
AHATOFSTR(HAForceUpdateProperty),
&_forceUpdate,
HASerializer::BoolPropertyType
);
}
if (_expireAfter.isSet()) {
serializer->set(
AHATOFSTR(HAExpireAfterProperty),
&_expireAfter,
HASerializer::NumberPropertyType
);
}
if (_features & JsonAttributesFeature) {
serializer->topic(AHATOFSTR(HAJsonAttributesTopic));
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
return serializer;
}
void HASensor::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
}
+3 -12
View File
@@ -73,15 +73,6 @@ 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.
@@ -110,6 +101,9 @@ public:
protected:
virtual void buildSerializer() override final;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
private:
@@ -122,9 +116,6 @@ 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;
+47 -3
View File
@@ -36,11 +36,12 @@ void HASwitch::buildSerializer()
return;
}
_serializer = new HASerializer(this, 11); // 11 - max properties nb
_serializer = new HASerializer(this, 12); // 12 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
// optional property
@@ -66,13 +67,56 @@ void HASwitch::buildSerializer()
_serializer->topic(AHATOFSTR(HACommandTopic));
}
HASerializer* HASwitch::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 12);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentSwitch),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HADeviceClassProperty), _class);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
if (_optimistic) {
serializer->set(
AHATOFSTR(HAOptimisticProperty),
&_optimistic,
HASerializer::BoolPropertyType
);
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
serializer->topic(AHATOFSTR(HACommandTopic));
return serializer;
}
void HASwitch::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
if (!_retain) {
+3
View File
@@ -134,6 +134,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
+19 -1
View File
@@ -30,13 +30,31 @@ void HATagScanner::buildSerializer()
_serializer->topic(AHATOFSTR(HATopic));
}
HASerializer* HATagScanner::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 2);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentTag),
HASerializer::ProgmemPropertyValue
);
serializer->topic(AHATOFSTR(HATopic));
return serializer;
}
void HATagScanner::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
}
#endif
+3
View File
@@ -31,6 +31,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
};
+68 -3
View File
@@ -50,10 +50,11 @@ void HAText::buildSerializer()
return;
}
_serializer = new HASerializer(this, 14); // 14 - max properties nb
_serializer = new HASerializer(this, 15); // 15 - max properties nb
_serializer->set(AHATOFSTR(HANameProperty), _name);
_serializer->set(AHATOFSTR(HAObjectIdProperty), _objectId);
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
_serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
_serializer->set(AHATOFSTR(HAIconProperty), _icon);
_serializer->set(
AHATOFSTR(HAModeProperty),
@@ -100,13 +101,77 @@ void HAText::buildSerializer()
_serializer->topic(AHATOFSTR(HACommandTopic));
}
HASerializer* HAText::buildDeviceDiscoverySerializer()
{
if (!uniqueId()) {
return nullptr;
}
HASerializer* serializer = new HASerializer(this, 15);
serializer->set(
AHATOFSTR(HAPlatformProperty),
AHATOFSTR(HAComponentText),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HANameProperty), _name);
setEntityIdProperty(serializer);
serializer->set(HASerializer::WithUniqueId);
serializer->set(AHATOFSTR(HAStateEntityCategory), nonEmptyString(_entityCategory));
serializer->set(AHATOFSTR(HAIconProperty), _icon);
serializer->set(
AHATOFSTR(HAModeProperty),
getModeProperty(),
HASerializer::ProgmemPropertyValue
);
serializer->set(AHATOFSTR(HAPatternProperty), _pattern);
if (_minValue.isSet()) {
serializer->set(
AHATOFSTR(HAMinProperty),
&_minValue,
HASerializer::NumberPropertyType
);
}
if (_maxValue.isSet()) {
serializer->set(
AHATOFSTR(HAMaxProperty),
&_maxValue,
HASerializer::NumberPropertyType
);
}
if (_retain) {
serializer->set(
AHATOFSTR(HARetainProperty),
&_retain,
HASerializer::BoolPropertyType
);
}
if (_optimistic) {
serializer->set(
AHATOFSTR(HAOptimisticProperty),
&_optimistic,
HASerializer::BoolPropertyType
);
}
serializer->set(HASerializer::WithAvailability);
serializer->topic(AHATOFSTR(HAStateTopic));
serializer->topic(AHATOFSTR(HACommandTopic));
return serializer;
}
void HAText::onMqttConnected()
{
if (!uniqueId()) {
return;
}
publishConfig();
if (shouldPublishSingleComponentConfig()) {
publishConfig();
}
publishAvailability();
if (!_retain && _currentState) {
+3
View File
@@ -152,6 +152,9 @@ public:
protected:
virtual void buildSerializer() override;
virtual HASerializer* buildDeviceDiscoverySerializer() override;
virtual bool supportsDeviceDiscovery() const override
{ return true; }
virtual void onMqttConnected() override;
virtual void onMqttMessage(
const char* topic,
+6
View File
@@ -8,6 +8,7 @@ const char HAComponentCamera[] PROGMEM = {"camera"};
const char HAComponentCover[] PROGMEM = {"cover"};
const char HAComponentDeviceTracker[] PROGMEM = {"device_tracker"};
const char HAComponentDeviceAutomation[] PROGMEM = {"device_automation"};
const char HAComponentDevice[] PROGMEM = {"device"};
const char HAComponentLock[] PROGMEM = {"lock"};
const char HAComponentNumber[] PROGMEM = {"number"};
const char HAComponentSelect[] PROGMEM = {"select"};
@@ -40,8 +41,13 @@ const char HADeviceSoftwareVersionProperty[] PROGMEM = {"sw"};
const char HADeviceConfigurationUrlProperty[] PROGMEM = {"cu"};
const char HANameProperty[] PROGMEM = {"name"};
const char HAUniqueIdProperty[] PROGMEM = {"uniq_id"};
const char HADefaultEntityIdProperty[] PROGMEM = {"def_ent_id"};
const char HAObjectIdProperty[] PROGMEM = {"obj_id"};
const char HADeviceProperty[] PROGMEM = {"dev"};
const char HAOriginProperty[] PROGMEM = {"o"};
const char HAComponentsProperty[] PROGMEM = {"cmps"};
const char HAPlatformProperty[] PROGMEM = {"p"};
const char HAGroupProperty[] PROGMEM = {"grp"};
const char HADeviceClassProperty[] PROGMEM = {"dev_cla"};
const char HAStateEntityCategory[] PROGMEM = {"ent_cat"};
const char HAStateClassProperty[] PROGMEM = {"stat_cla"};
+6
View File
@@ -8,6 +8,7 @@ extern const char HAComponentCamera[];
extern const char HAComponentCover[];
extern const char HAComponentDeviceTracker[];
extern const char HAComponentDeviceAutomation[];
extern const char HAComponentDevice[];
extern const char HAComponentLock[];
extern const char HAComponentNumber[];
extern const char HAComponentSelect[];
@@ -40,8 +41,13 @@ extern const char HADeviceSoftwareVersionProperty[];
extern const char HADeviceConfigurationUrlProperty[];
extern const char HANameProperty[];
extern const char HAUniqueIdProperty[];
extern const char HADefaultEntityIdProperty[];
extern const char HAObjectIdProperty[];
extern const char HADeviceProperty[];
extern const char HAOriginProperty[];
extern const char HAComponentsProperty[];
extern const char HAPlatformProperty[];
extern const char HAGroupProperty[];
extern const char HADeviceClassProperty[];
extern const char HAStateEntityCategory[];
extern const char HAStateClassProperty[];
@@ -12,6 +12,7 @@ static const char* testUniqueId = "uniqueId";
const char AvailabilityTopic[] PROGMEM = {"testData/testDevice/uniqueId/avty_t"};
const char SharedAvailabilityTopic[] PROGMEM = {"testData/testDevice/avty_t"};
const char ConfigTopic[] PROGMEM = {"homeassistant/componentName/testDevice/uniqueId/config"};
const char ComponentNameStr[] PROGMEM = {"componentName"};
class DummyDeviceType : public HABaseDeviceType
@@ -21,6 +22,17 @@ public:
HABaseDeviceType(componentName, uniqueId) { }
protected:
virtual void buildSerializer() override {
if (_serializer || !uniqueId()) {
return;
}
_serializer = new HASerializer(this, 3);
_serializer->set(AHATOFSTR(HANameProperty), "testName");
setEntityIdProperty(_serializer);
_serializer->set(HASerializer::WithUniqueId);
}
virtual void onMqttConnected() override {
publishAvailability();
}
@@ -60,6 +72,13 @@ AHA_TEST(BaseDeviceTypeTest, object_id_setter) {
assertEqual(objectId, deviceType.getObjectId());
}
AHA_TEST(BaseDeviceTypeTest, default_entity_id_setter) {
const char* entityId = "sensor.test_id";
DummyDeviceType deviceType(AHATOFSTR(ComponentNameStr), testUniqueId);
deviceType.setDefaultEntityId(entityId);
assertEqual(entityId, deviceType.getDefaultEntityId());
}
AHA_TEST(BaseDeviceTypeTest, default_availability) {
DummyDeviceType deviceType(AHATOFSTR(ComponentNameStr), testUniqueId);
assertFalse(deviceType.isAvailabilityConfigured());
@@ -106,6 +125,26 @@ AHA_TEST(BaseDeviceTypeTest, publish_shared_availability_runtime) {
assertSingleMqttMessage(AHATOFSTR(SharedAvailabilityTopic), "offline", true)
}
AHA_TEST(BaseDeviceTypeTest, republish_discovery) {
prepareTest
mock->connectDummy();
assertTrue(deviceType.republishDiscovery());
assertSingleMqttMessage(
AHATOFSTR(ConfigTopic),
"{\"name\":\"testName\",\"uniq_id\":\"uniqueId\"}",
true
)
}
AHA_TEST(BaseDeviceTypeTest, remove_from_discovery) {
prepareTest
mock->connectDummy();
assertTrue(deviceType.removeFromDiscovery());
assertSingleMqttMessage(AHATOFSTR(ConfigTopic), "", true)
}
void setup()
{
delay(1000);
+67
View File
@@ -7,6 +7,7 @@ static const char* testDeviceId = "testDevice";
static const char* testUniqueId = "uniqueCamera";
const char ConfigTopic[] PROGMEM = {"homeassistant/camera/testDevice/uniqueCamera/config"};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
const char DataTopic[] PROGMEM = {"testData/testDevice/uniqueCamera/t"};
AHA_TEST(CameraTest, invalid_unique_id) {
@@ -54,6 +55,32 @@ AHA_TEST(CameraTest, extended_unique_id) {
)
}
AHA_TEST(CameraTest, device_discovery_payload) {
initMqttTest(testDeviceId)
mqtt.enableDeviceDiscovery();
HACamera camera(testUniqueId);
mqtt.loop();
assertSingleMqttMessage(
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"uniqueCamera\":{"
"\"p\":\"camera\","
"\"uniq_id\":\"uniqueCamera\","
"\"t\":\"testData/testDevice/uniqueCamera/t\""
"}"
"}"
"}"
),
true
)
}
AHA_TEST(CameraTest, availability) {
initMqttTest(testDeviceId)
@@ -110,6 +137,46 @@ AHA_TEST(CameraTest, object_id_setter) {
)
}
AHA_TEST(CameraTest, default_entity_id_setter) {
initMqttTest(testDeviceId)
HACamera camera(testUniqueId);
camera.setDefaultEntityId("camera.test_camera");
assertEntityConfig(
mock,
camera,
(
"{"
"\"def_ent_id\":\"camera.test_camera\","
"\"uniq_id\":\"uniqueCamera\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"t\":\"testData/testDevice/uniqueCamera/t\""
"}"
)
)
}
AHA_TEST(CameraTest, entity_category_setter) {
initMqttTest(testDeviceId)
HACamera camera(testUniqueId);
camera.setEntityCategory("diagnostic");
assertEntityConfig(
mock,
camera,
(
"{"
"\"uniq_id\":\"uniqueCamera\","
"\"ent_cat\":\"diagnostic\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"t\":\"testData/testDevice/uniqueCamera/t\""
"}"
)
)
}
AHA_TEST(CameraTest, icon_setter) {
initMqttTest(testDeviceId)
+70
View File
@@ -32,6 +32,7 @@ static const char* testUniqueId = "uniqueCover";
static CommandCallback lastCommandCallbackCall;
const char ConfigTopic[] PROGMEM = {"homeassistant/cover/testDevice/uniqueCover/config"};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
const char StateTopic[] PROGMEM = {"testData/testDevice/uniqueCover/stat_t"};
const char PositionTopic[] PROGMEM = {"testData/testDevice/uniqueCover/pos_t"};
const char CommandTopic[] PROGMEM = {"testData/testDevice/uniqueCover/cmd_t"};
@@ -92,6 +93,33 @@ AHA_TEST(CoverTest, extended_unique_id) {
assertEqual(1, mock->getFlushedMessagesNb()); // only config should be pushed
}
AHA_TEST(CoverTest, device_discovery_payload) {
prepareTest
mqtt.enableDeviceDiscovery();
HACover cover(testUniqueId);
mqtt.loop();
assertSingleMqttMessage(
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"uniqueCover\":{"
"\"p\":\"cover\","
"\"uniq_id\":\"uniqueCover\","
"\"stat_t\":\"testData/testDevice/uniqueCover/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueCover/cmd_t\""
"}"
"}"
"}"
),
true
)
}
AHA_TEST(CoverTest, default_params_with_position) {
prepareTest
@@ -217,6 +245,27 @@ AHA_TEST(CoverTest, object_id_setter) {
)
}
AHA_TEST(CoverTest, default_entity_id_setter) {
prepareTest
HACover cover(testUniqueId);
cover.setDefaultEntityId("cover.test_cover");
assertEntityConfig(
mock,
cover,
(
"{"
"\"def_ent_id\":\"cover.test_cover\","
"\"uniq_id\":\"uniqueCover\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueCover/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueCover/cmd_t\""
"}"
)
)
}
AHA_TEST(CoverTest, device_class) {
prepareTest
@@ -238,6 +287,27 @@ AHA_TEST(CoverTest, device_class) {
)
}
AHA_TEST(CoverTest, entity_category_setter) {
prepareTest
HACover cover(testUniqueId);
cover.setEntityCategory("diagnostic");
assertEntityConfig(
mock,
cover,
(
"{"
"\"uniq_id\":\"uniqueCover\","
"\"ent_cat\":\"diagnostic\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueCover/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueCover/cmd_t\""
"}"
)
)
}
AHA_TEST(CoverTest, icon_setter) {
prepareTest
@@ -9,6 +9,7 @@ static const char* testUniqueId = "uniqueTracker";
const char ConfigTopic[] PROGMEM = {
"homeassistant/device_tracker/testDevice/uniqueTracker/config"
};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
const char StateTopic[] PROGMEM = {"testData/testDevice/uniqueTracker/stat_t"};
AHA_TEST(DeviceTrackerTest, invalid_unique_id) {
@@ -56,6 +57,32 @@ AHA_TEST(DeviceTrackerTest, extended_unique_id) {
)
}
AHA_TEST(DeviceTrackerTest, device_discovery_payload) {
initMqttTest(testDeviceId)
mqtt.enableDeviceDiscovery();
HADeviceTracker tracker(testUniqueId);
mqtt.loop();
assertSingleMqttMessage(
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"uniqueTracker\":{"
"\"p\":\"device_tracker\","
"\"uniq_id\":\"uniqueTracker\","
"\"stat_t\":\"testData/testDevice/uniqueTracker/stat_t\""
"}"
"}"
"}"
),
true
)
}
AHA_TEST(DeviceTrackerTest, source_type_gps) {
initMqttTest(testDeviceId)
@@ -198,6 +225,46 @@ AHA_TEST(DeviceTrackerTest, object_id_setter) {
)
}
AHA_TEST(DeviceTrackerTest, default_entity_id_setter) {
initMqttTest(testDeviceId)
HADeviceTracker tracker(testUniqueId);
tracker.setDefaultEntityId("device_tracker.test_tracker");
assertEntityConfig(
mock,
tracker,
(
"{"
"\"def_ent_id\":\"device_tracker.test_tracker\","
"\"uniq_id\":\"uniqueTracker\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueTracker/stat_t\""
"}"
)
)
}
AHA_TEST(DeviceTrackerTest, entity_category_setter) {
initMqttTest(testDeviceId)
HADeviceTracker tracker(testUniqueId);
tracker.setEntityCategory("diagnostic");
assertEntityConfig(
mock,
tracker,
(
"{"
"\"uniq_id\":\"uniqueTracker\","
"\"ent_cat\":\"diagnostic\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueTracker/stat_t\""
"}"
)
)
}
AHA_TEST(DeviceTrackerTest, icon_setter) {
initMqttTest(testDeviceId)
@@ -10,6 +10,7 @@ static const char* triggerSubtype = "mySubtype";
const char ConfigTopic[] PROGMEM = {
"homeassistant/device_automation/testDevice/myType_mySubtype/config"
};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
AHA_TEST(DeviceTriggerTest, invalid_type) {
initMqttTest(testDeviceId)
@@ -87,6 +88,34 @@ AHA_TEST(DeviceTriggerTest, string_type_string_subtype) {
)
}
AHA_TEST(DeviceTriggerTest, device_discovery_payload) {
initMqttTest(testDeviceId)
mqtt.enableDeviceDiscovery();
HADeviceTrigger trigger(triggerType, triggerSubtype);
mqtt.loop();
assertSingleMqttMessage(
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"myType_mySubtype\":{"
"\"p\":\"device_automation\","
"\"atype\":\"trigger\","
"\"type\":\"myType\","
"\"stype\":\"mySubtype\","
"\"t\":\"testData/testDevice/myType_mySubtype/t\""
"}"
"}"
"}"
),
true
)
}
AHA_TEST(DeviceTriggerTest, progmem_type_string_subtype) {
initMqttTest(testDeviceId)
+73
View File
@@ -54,6 +54,7 @@ static StateCallback lastStateCallbackCall;
static SpeedCallback lastSpeedCallbackCall;
const char ConfigTopic[] PROGMEM = {"homeassistant/fan/testDevice/uniqueFan/config"};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
const char StateTopic[] PROGMEM = {"testData/testDevice/uniqueFan/stat_t"};
const char SpeedPercentageTopic[] PROGMEM = {"testData/testDevice/uniqueFan/pct_stat_t"};
const char StateCommandTopic[] PROGMEM = {"testData/testDevice/uniqueFan/cmd_t"};
@@ -122,6 +123,36 @@ AHA_TEST(FanTest, extended_unique_id) {
assertEqual(2, mock->getFlushedMessagesNb()); // config + default state
}
AHA_TEST(FanTest, device_discovery_payload) {
prepareTest
mqtt.enableDeviceDiscovery();
HAFan fan(testUniqueId);
mqtt.loop();
assertEqual(2, mock->getFlushedMessagesNb());
assertMqttMessage(
0,
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"uniqueFan\":{"
"\"p\":\"fan\","
"\"uniq_id\":\"uniqueFan\","
"\"stat_t\":\"testData/testDevice/uniqueFan/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueFan/cmd_t\""
"}"
"}"
"}"
),
true
)
assertMqttMessage(1, AHATOFSTR(StateTopic), "OFF", true)
}
AHA_TEST(FanTest, default_params_with_speed) {
prepareTest
@@ -261,6 +292,48 @@ AHA_TEST(FanTest, object_id_setter) {
)
}
AHA_TEST(FanTest, default_entity_id_setter) {
prepareTest
HAFan fan(testUniqueId);
fan.setDefaultEntityId("fan.test_fan");
assertEntityConfig(
mock,
fan,
(
"{"
"\"def_ent_id\":\"fan.test_fan\","
"\"uniq_id\":\"uniqueFan\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueFan/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueFan/cmd_t\""
"}"
)
)
}
AHA_TEST(FanTest, entity_category_setter) {
prepareTest
HAFan fan(testUniqueId);
fan.setEntityCategory("diagnostic");
assertEntityConfig(
mock,
fan,
(
"{"
"\"uniq_id\":\"uniqueFan\","
"\"ent_cat\":\"diagnostic\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueFan/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueFan/cmd_t\""
"}"
)
)
}
AHA_TEST(FanTest, icon_setter) {
prepareTest
+67
View File
@@ -142,6 +142,7 @@ static ModeCallback lastModeCallbackCall;
static TargetTempCallback lastTargetTempCallbackCall;
const char ConfigTopic[] PROGMEM = {"homeassistant/climate/testDevice/uniqueHVAC/config"};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
const char CurrentTemperatureTopic[] PROGMEM = {"testData/testDevice/uniqueHVAC/curr_temp_t"};
const char ActionTopic[] PROGMEM = {"testData/testDevice/uniqueHVAC/act_t"};
const char AuxStateTopic[] PROGMEM = {"testData/testDevice/uniqueHVAC/aux_stat_t"};
@@ -245,6 +246,32 @@ AHA_TEST(HVACTest, extended_unique_id) {
assertEqual(1, mock->getFlushedMessagesNb()); // config
}
AHA_TEST(HVACTest, device_discovery_payload) {
prepareTest
mqtt.enableDeviceDiscovery();
HAHVAC hvac(testUniqueId);
mqtt.loop();
assertSingleMqttMessage(
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"uniqueHVAC\":{"
"\"p\":\"climate\","
"\"uniq_id\":\"uniqueHVAC\","
"\"curr_temp_t\":\"testData/testDevice/uniqueHVAC/curr_temp_t\""
"}"
"}"
"}"
),
true
)
}
AHA_TEST(HVACTest, config_with_action) {
prepareTest
@@ -568,6 +595,46 @@ AHA_TEST(HVACTest, object_id_setter) {
)
}
AHA_TEST(HVACTest, default_entity_id_setter) {
prepareTest
HAHVAC hvac(testUniqueId);
hvac.setDefaultEntityId("climate.test_hvac");
assertEntityConfig(
mock,
hvac,
(
"{"
"\"def_ent_id\":\"climate.test_hvac\","
"\"uniq_id\":\"uniqueHVAC\","
"\"curr_temp_t\":\"testData/testDevice/uniqueHVAC/curr_temp_t\","
"\"dev\":{\"ids\":\"testDevice\"}"
"}"
)
)
}
AHA_TEST(HVACTest, entity_category_setter) {
prepareTest
HAHVAC hvac(testUniqueId);
hvac.setEntityCategory("diagnostic");
assertEntityConfig(
mock,
hvac,
(
"{"
"\"uniq_id\":\"uniqueHVAC\","
"\"ent_cat\":\"diagnostic\","
"\"curr_temp_t\":\"testData/testDevice/uniqueHVAC/curr_temp_t\","
"\"dev\":{\"ids\":\"testDevice\"}"
"}"
)
)
}
AHA_TEST(HVACTest, icon_setter) {
prepareTest
+70
View File
@@ -32,6 +32,7 @@ static const char* testUniqueId = "uniqueLock";
static CommandCallback lastCommandCallbackCall;
const char ConfigTopic[] PROGMEM = {"homeassistant/lock/testDevice/uniqueLock/config"};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
const char CommandTopic[] PROGMEM = {"testData/testDevice/uniqueLock/cmd_t"};
const char StateTopic[] PROGMEM = {"testData/testDevice/uniqueLock/stat_t"};
@@ -91,6 +92,33 @@ AHA_TEST(LockTest, extended_unique_id) {
assertEqual(1, mock->getFlushedMessagesNb()); // only config should be pushed
}
AHA_TEST(LockTest, device_discovery_payload) {
prepareTest
mqtt.enableDeviceDiscovery();
HALock lock(testUniqueId);
mqtt.loop();
assertSingleMqttMessage(
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"uniqueLock\":{"
"\"p\":\"lock\","
"\"uniq_id\":\"uniqueLock\","
"\"stat_t\":\"testData/testDevice/uniqueLock/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueLock/cmd_t\""
"}"
"}"
"}"
),
true
)
}
AHA_TEST(LockTest, command_subscription) {
prepareTest
@@ -181,6 +209,48 @@ AHA_TEST(LockTest, object_id_setter) {
)
}
AHA_TEST(LockTest, default_entity_id_setter) {
prepareTest
HALock lock(testUniqueId);
lock.setDefaultEntityId("lock.test_lock");
assertEntityConfig(
mock,
lock,
(
"{"
"\"def_ent_id\":\"lock.test_lock\","
"\"uniq_id\":\"uniqueLock\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueLock/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueLock/cmd_t\""
"}"
)
)
}
AHA_TEST(LockTest, entity_category_setter) {
prepareTest
HALock lock(testUniqueId);
lock.setEntityCategory("diagnostic");
assertEntityConfig(
mock,
lock,
(
"{"
"\"uniq_id\":\"uniqueLock\","
"\"ent_cat\":\"diagnostic\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueLock/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueLock/cmd_t\""
"}"
)
)
}
AHA_TEST(LockTest, icon_setter) {
prepareTest
+104
View File
@@ -32,6 +32,7 @@ static const char* testUniqueId = "uniqueNumber";
static CommandCallback lastCommandCallbackCall;
const char ConfigTopic[] PROGMEM = {"homeassistant/number/testDevice/uniqueNumber/config"};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
const char CommandTopic[] PROGMEM = {"testData/testDevice/uniqueNumber/cmd_t"};
const char StateTopic[] PROGMEM = {"testData/testDevice/uniqueNumber/stat_t"};
@@ -356,6 +357,27 @@ AHA_TEST(NumberTest, object_id_setter) {
)
}
AHA_TEST(NumberTest, default_entity_id_setter) {
prepareTest
HANumber number(testUniqueId);
number.setDefaultEntityId("number.test_number");
assertEntityConfig(
mock,
number,
(
"{"
"\"def_ent_id\":\"number.test_number\","
"\"uniq_id\":\"uniqueNumber\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueNumber/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueNumber/cmd_t\""
"}"
)
)
}
AHA_TEST(NumberTest, device_class) {
prepareTest
@@ -524,6 +546,26 @@ AHA_TEST(NumberTest, unit_of_measurement_setter) {
)
}
AHA_TEST(NumberTest, empty_unit_of_measurement_is_ignored) {
initMqttTest(testDeviceId)
HANumber number(testUniqueId);
number.setUnitOfMeasurement("");
assertEntityConfig(
mock,
number,
(
"{"
"\"uniq_id\":\"uniqueNumber\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueNumber/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueNumber/cmd_t\""
"}"
)
)
}
AHA_TEST(NumberTest, min_setter_p0) {
initMqttTest(testDeviceId)
@@ -808,6 +850,29 @@ AHA_TEST(NumberTest, update_min_max_step_before_connect) {
)
}
AHA_TEST(NumberTest, min_can_equal_max) {
prepareTest
HANumber number(testUniqueId);
number.setMin(5);
number.setMax(5);
assertEntityConfig(
mock,
number,
(
"{"
"\"uniq_id\":\"uniqueNumber\","
"\"min\":5,"
"\"max\":5,"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueNumber/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueNumber/cmd_t\""
"}"
)
)
}
AHA_TEST(NumberTest, update_min_max_step_republishes_config) {
prepareTest
@@ -836,6 +901,45 @@ AHA_TEST(NumberTest, update_min_max_step_republishes_config) {
)
}
AHA_TEST(NumberTest, update_min_max_step_republishes_device_discovery_when_enabled) {
prepareTest
mqtt.enableDeviceDiscovery();
HANumber number(testUniqueId);
mqtt.loop();
assertEqual(2, mock->getFlushedMessagesNb());
number.updateMinMaxStep(1, 99, 5);
assertEqual(4, mock->getFlushedMessagesNb());
MqttMessage* clearedConfig = mock->getFlushedMessages()[2];
assertEqual(AHATOFSTR(ConfigTopic), clearedConfig->topic);
assertEqual((size_t)1, clearedConfig->bufferSize);
assertTrue(clearedConfig->retained);
assertMqttMessage(
3,
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"uniqueNumber\":{"
"\"p\":\"number\","
"\"uniq_id\":\"uniqueNumber\","
"\"min\":1,"
"\"max\":99,"
"\"step\":5,"
"\"stat_t\":\"testData/testDevice/uniqueNumber/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueNumber/cmd_t\""
"}"
"}"
"}"
),
true
)
}
AHA_TEST(NumberTest, command_none) {
prepareTest
+68
View File
@@ -29,6 +29,7 @@ static const char* testUniqueId = "uniqueScene";
static ActivateCallback lastActivateCallbackCall;
const char ConfigTopic[] PROGMEM = {"homeassistant/scene/testDevice/uniqueScene/config"};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
const char CommandTopic[] PROGMEM = {"testData/testDevice/uniqueScene/cmd_t"};
const char CommandMessage[] PROGMEM = {"on"};
@@ -83,6 +84,33 @@ AHA_TEST(SceneTest, extended_unique_id) {
)
}
AHA_TEST(SceneTest, device_discovery_payload) {
prepareTest
mqtt.enableDeviceDiscovery();
HAScene scene(testUniqueId);
mqtt.loop();
assertSingleMqttMessage(
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"uniqueScene\":{"
"\"p\":\"scene\","
"\"uniq_id\":\"uniqueScene\","
"\"pl_on\":\"ON\","
"\"cmd_t\":\"testData/testDevice/uniqueScene/cmd_t\""
"}"
"}"
"}"
),
true
)
}
AHA_TEST(SceneTest, command_subscription) {
prepareTest
@@ -149,6 +177,46 @@ AHA_TEST(SceneTest, object_id_setter) {
)
}
AHA_TEST(SceneTest, default_entity_id_setter) {
prepareTest
HAScene scene(testUniqueId);
scene.setDefaultEntityId("scene.test_scene");
assertEntityConfig(
mock,
scene,
(
"{"
"\"def_ent_id\":\"scene.test_scene\","
"\"uniq_id\":\"uniqueScene\","
"\"pl_on\":\"ON\","
"\"cmd_t\":\"testData/testDevice/uniqueScene/cmd_t\""
"}"
)
)
}
AHA_TEST(SceneTest, entity_category_setter) {
prepareTest
HAScene scene(testUniqueId);
scene.setEntityCategory("diagnostic");
assertEntityConfig(
mock,
scene,
(
"{"
"\"uniq_id\":\"uniqueScene\","
"\"ent_cat\":\"diagnostic\","
"\"pl_on\":\"ON\","
"\"cmd_t\":\"testData/testDevice/uniqueScene/cmd_t\""
"}"
)
)
}
AHA_TEST(SceneTest, icon_setter) {
prepareTest
+39
View File
@@ -110,6 +110,26 @@ AHA_TEST(SensorTest, object_id_setter) {
)
}
AHA_TEST(SensorTest, default_entity_id_setter) {
initMqttTest(testDeviceId)
HASensor sensor(testUniqueId);
sensor.setDefaultEntityId("sensor.test_sensor");
assertEntityConfig(
mock,
sensor,
(
"{"
"\"def_ent_id\":\"sensor.test_sensor\","
"\"uniq_id\":\"uniqueSensor\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueSensor/stat_t\""
"}"
)
)
}
AHA_TEST(SensorTest, device_class_setter) {
initMqttTest(testDeviceId)
@@ -230,6 +250,25 @@ AHA_TEST(SensorTest, unit_of_measurement_setter) {
)
}
AHA_TEST(SensorTest, empty_unit_of_measurement_is_ignored) {
initMqttTest(testDeviceId)
HASensor sensor(testUniqueId);
sensor.setUnitOfMeasurement("");
assertEntityConfig(
mock,
sensor,
(
"{"
"\"uniq_id\":\"uniqueSensor\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueSensor/stat_t\""
"}"
)
)
}
AHA_TEST(SensorTest, expire_after_setter) {
initMqttTest(testDeviceId)
+73
View File
@@ -32,6 +32,7 @@ static const char* testUniqueId = "uniqueSwitch";
static CommandCallback lastCommandCallbackCall;
const char ConfigTopic[] PROGMEM = {"homeassistant/switch/testDevice/uniqueSwitch/config"};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
const char StateTopic[] PROGMEM = {"testData/testDevice/uniqueSwitch/stat_t"};
const char CommandTopic[] PROGMEM = {"testData/testDevice/uniqueSwitch/cmd_t"};
@@ -91,6 +92,36 @@ AHA_TEST(SwitchTest, extended_unique_id) {
assertEqual(2, mock->getFlushedMessagesNb());
}
AHA_TEST(SwitchTest, device_discovery_payload) {
prepareTest
mqtt.enableDeviceDiscovery();
HASwitch testSwitch(testUniqueId);
mqtt.loop();
assertEqual(2, mock->getFlushedMessagesNb());
assertMqttMessage(
0,
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"uniqueSwitch\":{"
"\"p\":\"switch\","
"\"uniq_id\":\"uniqueSwitch\","
"\"stat_t\":\"testData/testDevice/uniqueSwitch/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueSwitch/cmd_t\""
"}"
"}"
"}"
),
true
)
assertMqttMessage(1, AHATOFSTR(StateTopic), "OFF", true)
}
AHA_TEST(SwitchTest, command_subscription) {
prepareTest
@@ -181,6 +212,27 @@ AHA_TEST(SwitchTest, object_id_setter) {
)
}
AHA_TEST(SwitchTest, default_entity_id_setter) {
prepareTest
HASwitch testSwitch(testUniqueId);
testSwitch.setDefaultEntityId("switch.test_switch");
assertEntityConfig(
mock,
testSwitch,
(
"{"
"\"def_ent_id\":\"switch.test_switch\","
"\"uniq_id\":\"uniqueSwitch\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueSwitch/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueSwitch/cmd_t\""
"}"
)
)
}
AHA_TEST(SwitchTest, device_class) {
prepareTest
@@ -202,6 +254,27 @@ AHA_TEST(SwitchTest, device_class) {
)
}
AHA_TEST(SwitchTest, entity_category_setter) {
prepareTest
HASwitch testSwitch(testUniqueId);
testSwitch.setEntityCategory("diagnostic");
assertEntityConfig(
mock,
testSwitch,
(
"{"
"\"uniq_id\":\"uniqueSwitch\","
"\"ent_cat\":\"diagnostic\","
"\"dev\":{\"ids\":\"testDevice\"},"
"\"stat_t\":\"testData/testDevice/uniqueSwitch/stat_t\","
"\"cmd_t\":\"testData/testDevice/uniqueSwitch/cmd_t\""
"}"
)
)
}
AHA_TEST(SwitchTest, icon_setter) {
prepareTest
+26
View File
@@ -7,6 +7,7 @@ static const char* testDeviceId = "testDevice";
static const char* testUniqueId = "uniqueScanner";
const char ConfigTopic[] PROGMEM = {"homeassistant/tag/testDevice/uniqueScanner/config"};
const char DeviceConfigTopic[] PROGMEM = {"homeassistant/device/testDevice/config"};
AHA_TEST(TagScannerTest, invalid_unique_id) {
initMqttTest(testDeviceId)
@@ -34,6 +35,31 @@ AHA_TEST(TagScannerTest, default_params) {
)
}
AHA_TEST(TagScannerTest, device_discovery_payload) {
initMqttTest(testDeviceId)
mqtt.enableDeviceDiscovery();
HATagScanner scanner(testUniqueId);
mqtt.loop();
assertSingleMqttMessage(
AHATOFSTR(DeviceConfigTopic),
(
"{"
"\"dev\":{\"ids\":\"testDevice\"},"
"\"o\":{\"name\":\"ArduinoHA\",\"sw\":\"2.1.0\"},"
"\"cmps\":{"
"\"uniqueScanner\":{"
"\"p\":\"tag\","
"\"t\":\"testData/testDevice/uniqueScanner/t\""
"}"
"}"
"}"
),
true
)
}
AHA_TEST(TagScannerTest, nullptr_tag_scanned) {
initMqttTest(testDeviceId)