Files
nixie-clock/firmware/uniclock/lib/arduino-home-assistant/src/utils/HANumeric.h
T
mangavalkandClaude Opus 5.5 9a7061383c Add Home Assistant controls over MQTT
Upgrade ArduinoHA from 1.3.0 to 2.1.0 and rewrite mqtt.ino for it.
The clock now shows up in Home Assistant (discovery prefix
"homeassistant") with:
- switches Tubes (blank the display, runtime only) and Auto dim
- numbers Day/Night brightness
- light Backlight (on/off, brightness, RGB mapped to the colour wheel)
- select LED effect, button Cathode protect
- sensors Light (LDR) and WiFi signal, plus the upstream sensors when
  the clock has them; master-clock subscriptions are kept

Settings changed from HA are saved to EEPROM 3 s after the last change.
Fixes the unbounded memcpy in the MQTT message handler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 21:37:25 +01:00

235 lines
7.5 KiB
C++

#ifndef AHA_NUMERIC_H
#define AHA_NUMERIC_H
#include <stdint.h>
#include "../ArduinoHADefines.h"
/**
* This class represents a numeric value that simplifies use of different types of numbers across the library.
*/
class HANumeric
{
public:
/// The maximum number of digits that the base value can have (int64_t).
static const uint8_t MaxDigitsNb;
/**
* Deserializes number from the given buffer.
* Please note that the class expected buffer to contain the base number.
* For example, deserializing `1234` number and setting precision to `1`
* results in representation of `123.4` float.
*
* @param buffer The buffer that contains the number.
* @param length The length of the buffer.
*/
static HANumeric fromStr(const uint8_t* buffer, const uint16_t length);
/**
* Creates an empty number representation.
*/
HANumeric();
/**
* Converts the given float into number representation of the given precision.
* If the precision is set to zero the given float will be converted into integer.
*
* @param value The value that should be used as a base.
* @param precision The number of digits in the decimal part.
*/
HANumeric(const float value, const uint8_t precision);
/**
* Converts the given int8_t into number representation of the given precision.
* If the precision is greater than zero the given value will be converted to float.
*
* @param value The value that should be used as a base.
* @param precision The number of digits in the decimal part.
*/
HANumeric(const int8_t value, const uint8_t precision);
/**
* Converts the given int16_t into number representation of the given precision.
* If the precision is greater than zero the given value will be converted to float.
*
* @param value The value that should be used as a base.
* @param precision The number of digits in the decimal part.
*/
HANumeric(const int16_t value, const uint8_t precision);
/**
* Converts the given int32_t into number representation of the given precision.
* If the precision is greater than zero the given value will be converted to float.
*
* @param value The value that should be used as a base.
* @param precision The number of digits in the decimal part.
*/
HANumeric(const int32_t value, const uint8_t precision);
/**
* Converts the given uint8_t into number representation of the given precision.
* If the precision is greater than zero the given value will be converted to float.
*
* @param value The value that should be used as a base.
* @param precision The number of digits in the decimal part.
*/
HANumeric(const uint8_t value, const uint8_t precision);
/**
* Converts the given uint16_t into number representation of the given precision.
* If the precision is greater than zero the given value will be converted to float.
*
* @param value The value that should be used as a base.
* @param precision The number of digits in the decimal part.
*/
HANumeric(const uint16_t value, const uint8_t precision);
/**
* Converts the given uint32_t into number representation of the given precision.
* If the precision is greater than zero the given value will be converted to float.
*
* @param value The value that should be used as a base.
* @param precision The number of digits in the decimal part.
*/
HANumeric(const uint32_t value, const uint8_t precision);
#ifdef ARDUINOHA_INT_OVERLOAD
/**
* Converts the given int into number representation of the given precision.
* If the precision is greater than zero the given value will be converted to float.
*
* @param value The value that should be used as a base.
* @param precision The number of digits in the decimal part.
*/
HANumeric(const int value, const uint8_t precision);
#endif
void operator= (const HANumeric& a) {
if (!a.isSet()) {
reset();
} else {
_isSet = a.isSet();
_value = a.getBaseValue();
_precision = a.getPrecision();
}
}
bool operator== (const HANumeric& a) const {
return (
isSet() == a.isSet() &&
getBaseValue() == a.getBaseValue() &&
getPrecision() == a.getPrecision()
);
}
/**
* Returns multiplier that used to generate base value based on the precision.
* The multiplier is generated using the formula: `pow(precision, 10)`.
*/
uint32_t getPrecisionBase() const;
/**
* Returns size of the number
*/
uint8_t calculateSize() const;
/**
* Converts the number to the string.
*
* @param dst Destination where the number will be saved.
* The null terminator is not added at the end.
* @return The number of written characters.
* @note The `dst` size should be calculated using HANumeric::calculateSize() method plus 1 extra byte for the null terminator.
*/
uint16_t toStr(char* dst) const;
/**
* Returns true if the base value is set.
*/
inline bool isSet() const
{ return _isSet; }
/**
* Sets the base value without converting it to the proper precision.
*/
inline void setBaseValue(int64_t value)
{ _isSet = true; _value = value; }
/**
* Returns the base value of the number.
*/
inline int64_t getBaseValue() const
{ return _value; }
/**
* Sets the precision of the number (number of digits in the decimal part).
*
* @param precision The precision to use.
*/
inline void setPrecision(const uint8_t precision)
{ _precision = precision; }
/**
* Returns the precision of the number.
*/
inline uint8_t getPrecision() const
{ return _precision; }
/**
* Resets the number to the defaults.
*/
inline void reset()
{ _isSet = false; _value = 0; _precision = 0; }
inline bool isUInt8() const
{ return _isSet && _precision == 0 && _value >= 0 && _value <= UINT8_MAX; }
inline bool isUInt16() const
{ return _isSet && _precision == 0 && _value >= 0 && _value <= UINT16_MAX; }
inline bool isUInt32() const
{ return _isSet && _precision == 0 && _value >= 0 && _value <= UINT32_MAX; }
inline bool isInt8() const
{ return _isSet && _precision == 0 && _value >= INT8_MIN && _value <= INT8_MAX; }
inline bool isInt16() const
{ return _isSet && _precision == 0 && _value >= INT16_MIN && _value <= INT16_MAX; }
inline bool isInt32() const
{ return _isSet && _precision == 0 && _value >= INT32_MIN && _value <= INT32_MAX; }
inline bool isFloat() const
{ return _isSet && _precision > 0; }
inline uint8_t toUInt8() const
{ return static_cast<uint8_t>(_value); }
inline uint16_t toUInt16() const
{ return static_cast<uint16_t>(_value); }
inline uint32_t toUInt32() const
{ return static_cast<uint32_t>(_value); }
inline int8_t toInt8() const
{ return static_cast<int8_t>(_value); }
inline int16_t toInt16() const
{ return static_cast<int16_t>(_value); }
inline int32_t toInt32() const
{ return static_cast<int32_t>(_value); }
inline float toFloat() const
{ return _value / (float)getPrecisionBase(); }
private:
bool _isSet;
int64_t _value;
uint8_t _precision;
explicit HANumeric(const int64_t value);
};
#endif