diff --git a/src/device-types/HANumber.cpp b/src/device-types/HANumber.cpp index abab620..5aad230 100644 --- a/src/device-types/HANumber.cpp +++ b/src/device-types/HANumber.cpp @@ -37,6 +37,14 @@ bool HANumber::setState(const HANumeric& state, const bool force) return false; } +void HANumber::updateMinMaxStep(const float min, const float max, const float step) +{ + _minValue = HANumeric(min, _precision); + _maxValue = HANumeric(max, _precision); + _step = HANumeric(step, _precision); + publishConfig(); +} + void HANumber::buildSerializer() { if (_serializer || !uniqueId()) { diff --git a/src/device-types/HANumber.h b/src/device-types/HANumber.h index 6bd26bc..2374c36 100644 --- a/src/device-types/HANumber.h +++ b/src/device-types/HANumber.h @@ -189,6 +189,16 @@ public: inline void setStep(const float step) { _step = HANumeric(step, _precision); } + /** + * Updates min/max/step and republishes discovery config. + * This can be used when limits need to change at runtime. + * + * @param min The minimal value. + * @param max The maximal value. + * @param step The slider step value. + */ + void updateMinMaxStep(const float min, const float max, const float step); + /** * Registers callback that will be called each time the number is changed in the HA panel. * Please note that it's not possible to register multiple callbacks for the same number. diff --git a/src/device-types/HASelect.cpp b/src/device-types/HASelect.cpp index 3f26e17..6800bf4 100644 --- a/src/device-types/HASelect.cpp +++ b/src/device-types/HASelect.cpp @@ -54,7 +54,7 @@ void HASelect::setOptions(const char* options) return; } - uint8_t optionLen = 0; + uint16_t optionLen = 0; for (uint16_t i = 0; i < optionsLen; i++) { if (options[i] == ';' || options[i] == 0) { if (optionLen == 0) { @@ -65,7 +65,10 @@ void HASelect::setOptions(const char* options) option[optionLen] = 0; memcpy(option, &options[i - optionLen], optionLen); - _options->add(option); + if (!_options->add(option)) { + delete[] option; + break; + } optionLen = 0; continue; } @@ -213,8 +216,12 @@ uint8_t HASelect::countOptionsInString(const char* options) const return 0; } - for (uint8_t i = 0; i < optionsLen; i++) { + for (uint16_t i = 0; i < optionsLen; i++) { if (options[i] == ';') { + if (optionsNb == 255) { + break; + } + optionsNb++; } } diff --git a/src/device-types/HAText.cpp b/src/device-types/HAText.cpp index d5d14ee..1c8b489 100644 --- a/src/device-types/HAText.cpp +++ b/src/device-types/HAText.cpp @@ -10,8 +10,8 @@ HAText::HAText(const char* uniqueId) : _retain(false), _optimistic(false), _mode(ModeText), - _min(), - _max(), + _minValue(), + _maxValue(), _pattern(nullptr), _currentState(nullptr), _commandCallback(nullptr) @@ -62,18 +62,18 @@ void HAText::buildSerializer() ); _serializer->set(AHATOFSTR(HAPatternProperty), _pattern); - if (_min.isSet()) { + if (_minValue.isSet()) { _serializer->set( AHATOFSTR(HAMinProperty), - &_min, + &_minValue, HASerializer::NumberPropertyType ); } - if (_max.isSet()) { + if (_maxValue.isSet()) { _serializer->set( AHATOFSTR(HAMaxProperty), - &_max, + &_maxValue, HASerializer::NumberPropertyType ); } diff --git a/src/device-types/HAText.h b/src/device-types/HAText.h index 9643408..7cfcf88 100644 --- a/src/device-types/HAText.h +++ b/src/device-types/HAText.h @@ -103,7 +103,7 @@ public: * @param min Minimum number of characters. */ inline void setMin(const uint16_t min) - { _min = HANumeric(min, PrecisionP0); } + { _minValue = HANumeric(min, PrecisionP0); } /** * Sets maximum accepted length of the command payload. @@ -111,7 +111,7 @@ public: * @param max Maximum number of characters. */ inline void setMax(const uint16_t max) - { _max = HANumeric(max, PrecisionP0); } + { _maxValue = HANumeric(max, PrecisionP0); } /** * Sets regex pattern for command payload validation in Home Assistant. @@ -186,10 +186,10 @@ private: Mode _mode; /// The minimal number of characters accepted by Home Assistant. - HANumeric _min; + HANumeric _minValue; /// The maximum number of characters accepted by Home Assistant. - HANumeric _max; + HANumeric _maxValue; /// Regular expression pattern accepted by Home Assistant. It can be nullptr. const char* _pattern; diff --git a/tests/NumberTest/NumberTest.ino b/tests/NumberTest/NumberTest.ino index 108afeb..1c9742f 100644 --- a/tests/NumberTest/NumberTest.ino +++ b/tests/NumberTest/NumberTest.ino @@ -785,6 +785,57 @@ AHA_TEST(NumberTest, step_setter_p3) { ) } +AHA_TEST(NumberTest, update_min_max_step_before_connect) { + prepareTest + + HANumber number(testUniqueId); + number.updateMinMaxStep(1, 99, 5); + + assertEntityConfig( + mock, + number, + ( + "{" + "\"uniq_id\":\"uniqueNumber\"," + "\"min\":1," + "\"max\":99," + "\"step\":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 + + HANumber number(testUniqueId); + mqtt.loop(); + assertEqual(2, mock->getFlushedMessagesNb()); // config + default state + + number.updateMinMaxStep(1, 99, 5); + + assertEqual(3, mock->getFlushedMessagesNb()); + assertMqttMessage( + 2, + AHATOFSTR(ConfigTopic), + ( + "{" + "\"uniq_id\":\"uniqueNumber\"," + "\"min\":1," + "\"max\":99," + "\"step\":5," + "\"dev\":{\"ids\":\"testDevice\"}," + "\"stat_t\":\"testData/testDevice/uniqueNumber/stat_t\"," + "\"cmd_t\":\"testData/testDevice/uniqueNumber/cmd_t\"" + "}" + ), + true + ) +} + AHA_TEST(NumberTest, command_none) { prepareTest diff --git a/tests/SelectTest/SelectTest.ino b/tests/SelectTest/SelectTest.ino index d58addf..7dba7a3 100644 --- a/tests/SelectTest/SelectTest.ino +++ b/tests/SelectTest/SelectTest.ino @@ -157,6 +157,26 @@ AHA_TEST(SelectTest, multiple_options) { assertEqual(2, mock->getFlushedMessagesNb()); } +AHA_TEST(SelectTest, long_options_string_over_255_chars) { + prepareTest + + char options[273]; + for (uint16_t i = 0; i < 270; i++) { + options[i] = 'A'; + } + + options[270] = ';'; + options[271] = 'B'; + options[272] = 0; + + HASelect select(testUniqueId); + select.setOptions(options); + + assertTrue(select.getOptions() != nullptr); + assertEqual((uint8_t)2, select.getOptions()->getItemsNb()); + assertEqual("B", select.getOptions()->getItem(1)); +} + AHA_TEST(SelectTest, command_subscription) { prepareTest