This commit is contained in:
Stephan Mühl
2023-03-22 12:15:18 +01:00
committed by GitHub
parent 3e12414a87
commit adb5102869
203 changed files with 35010 additions and 0 deletions

View File

@@ -0,0 +1,195 @@
#include <Arduino.h>
#include "HADictionary.h"
// components
const char HAComponentBinarySensor[] PROGMEM = {"binary_sensor"};
const char HAComponentButton[] PROGMEM = {"button"};
const char HAComponentCamera[] PROGMEM = {"camera"};
const char HAComponentCover[] PROGMEM = {"cover"};
const char HAComponentDeviceTracker[] PROGMEM = {"device_tracker"};
const char HAComponentDeviceAutomation[] PROGMEM = {"device_automation"};
const char HAComponentLock[] PROGMEM = {"lock"};
const char HAComponentNumber[] PROGMEM = {"number"};
const char HAComponentSelect[] PROGMEM = {"select"};
const char HAComponentSensor[] PROGMEM = {"sensor"};
const char HAComponentSwitch[] PROGMEM = {"switch"};
const char HAComponentTag[] PROGMEM = {"tag"};
const char HAComponentScene[] PROGMEM = {"scene"};
const char HAComponentFan[] PROGMEM = {"fan"};
const char HAComponentLight[] PROGMEM = {"light"};
const char HAComponentClimate[] PROGMEM = {"climate"};
// decorators
const char HASerializerSlash[] PROGMEM = {"/"};
const char HASerializerJsonDataPrefix[] PROGMEM = {"{"};
const char HASerializerJsonDataSuffix[] PROGMEM = {"}"};
const char HASerializerJsonPropertyPrefix[] PROGMEM = {"\""};
const char HASerializerJsonPropertySuffix[] PROGMEM = {"\":"};
const char HASerializerJsonEscapeChar[] PROGMEM = {"\""};
const char HASerializerJsonPropertiesSeparator[] PROGMEM = {","};
const char HASerializerJsonArrayPrefix[] PROGMEM = {"["};
const char HASerializerJsonArraySuffix[] PROGMEM = {"]"};
const char HASerializerUnderscore[] PROGMEM = {"_"};
// properties
const char HADeviceIdentifiersProperty[] PROGMEM = {"ids"};
const char HADeviceManufacturerProperty[] PROGMEM = {"mf"};
const char HADeviceModelProperty[] PROGMEM = {"mdl"};
const char HADeviceSoftwareVersionProperty[] PROGMEM = {"sw"};
const char HANameProperty[] PROGMEM = {"name"};
const char HAUniqueIdProperty[] PROGMEM = {"uniq_id"};
const char HADeviceProperty[] PROGMEM = {"dev"};
const char HADeviceClassProperty[] PROGMEM = {"dev_cla"};
const char HAIconProperty[] PROGMEM = {"ic"};
const char HARetainProperty[] PROGMEM = {"ret"};
const char HASourceTypeProperty[] PROGMEM = {"src_type"};
const char HAEncodingProperty[] PROGMEM = {"e"};
const char HAOptimisticProperty[] PROGMEM = {"opt"};
const char HAAutomationTypeProperty[] PROGMEM = {"atype"};
const char HATypeProperty[] PROGMEM = {"type"};
const char HASubtypeProperty[] PROGMEM = {"stype"};
const char HAForceUpdateProperty[] PROGMEM = {"frc_upd"};
const char HAUnitOfMeasurementProperty[] PROGMEM = {"unit_of_meas"};
const char HAValueTemplateProperty[] PROGMEM = {"val_tpl"};
const char HAOptionsProperty[] PROGMEM = {"options"};
const char HAMinProperty[] PROGMEM = {"min"};
const char HAMaxProperty[] PROGMEM = {"max"};
const char HAStepProperty[] PROGMEM = {"step"};
const char HAModeProperty[] PROGMEM = {"mode"};
const char HACommandTemplateProperty[] PROGMEM = {"cmd_tpl"};
const char HASpeedRangeMaxProperty[] PROGMEM = {"spd_rng_max"};
const char HASpeedRangeMinProperty[] PROGMEM = {"spd_rng_min"};
const char HABrightnessScaleProperty[] PROGMEM = {"bri_scl"};
const char HAMinMiredsProperty[] PROGMEM = {"min_mirs"};
const char HAMaxMiredsProperty[] PROGMEM = {"max_mirs"};
const char HATemperatureUnitProperty[] PROGMEM = {"temp_unit"};
const char HAMinTempProperty[] PROGMEM = {"min_temp"};
const char HAMaxTempProperty[] PROGMEM = {"max_temp"};
const char HATempStepProperty[] PROGMEM = {"temp_step"};
const char HAFanModesProperty[] PROGMEM = {"fan_modes"};
const char HASwingModesProperty[] PROGMEM = {"swing_modes"};
const char HAModesProperty[] PROGMEM = {"modes"};
const char HATemperatureCommandTemplateProperty[] PROGMEM = {"temp_cmd_tpl"};
const char HAPayloadOnProperty[] PROGMEM = {"pl_on"};
// topics
const char HAConfigTopic[] PROGMEM = {"config"};
const char HAAvailabilityTopic[] PROGMEM = {"avty_t"};
const char HATopic[] PROGMEM = {"t"};
const char HAStateTopic[] PROGMEM = {"stat_t"};
const char HACommandTopic[] PROGMEM = {"cmd_t"};
const char HAPositionTopic[] PROGMEM = {"pos_t"};
const char HAPercentageStateTopic[] PROGMEM = {"pct_stat_t"};
const char HAPercentageCommandTopic[] PROGMEM = {"pct_cmd_t"};
const char HABrightnessCommandTopic[] PROGMEM = {"bri_cmd_t"};
const char HABrightnessStateTopic[] PROGMEM = {"bri_stat_t"};
const char HAColorTemperatureCommandTopic[] PROGMEM = {"clr_temp_cmd_t"};
const char HAColorTemperatureStateTopic[] PROGMEM = {"clr_temp_stat_t"};
const char HACurrentTemperatureTopic[] PROGMEM = {"curr_temp_t"};
const char HAActionTopic[] PROGMEM = {"act_t"};
const char HAAuxCommandTopic[] PROGMEM = {"aux_cmd_t"};
const char HAAuxStateTopic[] PROGMEM = {"aux_stat_t"};
const char HAPowerCommandTopic[] PROGMEM = {"pow_cmd_t"};
const char HAFanModeCommandTopic[] PROGMEM = {"fan_mode_cmd_t"};
const char HAFanModeStateTopic[] PROGMEM = {"fan_mode_stat_t"};
const char HASwingModeCommandTopic[] PROGMEM = {"swing_mode_cmd_t"};
const char HASwingModeStateTopic[] PROGMEM = {"swing_mode_stat_t"};
const char HAModeCommandTopic[] PROGMEM = {"mode_cmd_t"};
const char HAModeStateTopic[] PROGMEM = {"mode_stat_t"};
const char HATemperatureCommandTopic[] PROGMEM = {"temp_cmd_t"};
const char HATemperatureStateTopic[] PROGMEM = {"temp_stat_t"};
const char HARGBCommandTopic[] PROGMEM = {"rgb_cmd_t"};
const char HARGBStateTopic[] PROGMEM = {"rgb_stat_t"};
// misc
const char HAOnline[] PROGMEM = {"online"};
const char HAOffline[] PROGMEM = {"offline"};
const char HAStateOn[] PROGMEM = {"ON"};
const char HAStateOff[] PROGMEM = {"OFF"};
const char HAStateLocked[] PROGMEM = {"LOCKED"};
const char HAStateUnlocked[] PROGMEM = {"UNLOCKED"};
const char HAStateNone[] PROGMEM = {"None"};
const char HATrue[] PROGMEM = {"true"};
const char HAFalse[] PROGMEM = {"false"};
const char HAHome[] PROGMEM = {"home"};
const char HANotHome[] PROGMEM = {"not_home"};
const char HATrigger[] PROGMEM = {"trigger"};
const char HAModeBox[] PROGMEM = {"box"};
const char HAModeSlider[] PROGMEM = {"slider"};
// covers
const char HAClosedState[] PROGMEM = {"closed"};
const char HAClosingState[] PROGMEM = {"closing"};
const char HAOpenState[] PROGMEM = {"open"};
const char HAOpeningState[] PROGMEM = {"opening"};
const char HAStoppedState[] PROGMEM = {"stopped"};
// commands
const char HAOpenCommand[] PROGMEM = {"OPEN"};
const char HACloseCommand[] PROGMEM = {"CLOSE"};
const char HAStopCommand[] PROGMEM = {"STOP"};
const char HALockCommand[] PROGMEM = {"LOCK"};
const char HAUnlockCommand[] PROGMEM = {"UNLOCK"};
// device tracker
const char HAGPSType[] PROGMEM = {"gps"};
const char HARouterType[] PROGMEM = {"router"};
const char HABluetoothType[] PROGMEM = {"bluetooth"};
const char HABluetoothLEType[] PROGMEM = {"bluetooth_le"};
// camera
const char HAEncodingBase64[] PROGMEM = {"b64"};
// trigger
const char HAButtonShortPressType[] PROGMEM = {"button_short_press"};
const char HAButtonShortReleaseType[] PROGMEM = {"button_short_release"};
const char HAButtonLongPressType[] PROGMEM = {"button_long_press"};
const char HAButtonLongReleaseType[] PROGMEM = {"button_long_release"};
const char HAButtonDoublePressType[] PROGMEM = {"button_double_press"};
const char HAButtonTriplePressType[] PROGMEM = {"button_triple_press"};
const char HAButtonQuadruplePressType[] PROGMEM = {"button_quadruple_press"};
const char HAButtonQuintuplePressType[] PROGMEM = {"button_quintuple_press"};
const char HATurnOnSubtype[] PROGMEM = {"turn_on"};
const char HATurnOffSubtype[] PROGMEM = {"turn_off"};
const char HAButton1Subtype[] PROGMEM = {"button_1"};
const char HAButton2Subtype[] PROGMEM = {"button_2"};
const char HAButton3Subtype[] PROGMEM = {"button_3"};
const char HAButton4Subtype[] PROGMEM = {"button_4"};
const char HAButton5Subtype[] PROGMEM = {"button_5"};
const char HAButton6Subtype[] PROGMEM = {"button_6"};
// actions
const char HAActionOff[] PROGMEM = {"off"};
const char HAActionHeating[] PROGMEM = {"heating"};
const char HAActionCooling[] PROGMEM = {"cooling"};
const char HAActionDrying[] PROGMEM = {"drying"};
const char HAActionIdle[] PROGMEM = {"idle"};
const char HAActionFan[] PROGMEM = {"fan"};
// fan modes
const char HAFanModeAuto[] PROGMEM = {"auto"};
const char HAFanModeLow[] PROGMEM = {"low"};
const char HAFanModeMedium[] PROGMEM = {"medium"};
const char HAFanModeHigh[] PROGMEM = {"high"};
// swing modes
const char HASwingModeOn[] PROGMEM = {"on"};
const char HASwingModeOff[] PROGMEM = {"off"};
// HVAC modes
const char HAModeAuto[] PROGMEM = {"auto"};
const char HAModeOff[] PROGMEM = {"off"};
const char HAModeCool[] PROGMEM = {"cool"};
const char HAModeHeat[] PROGMEM = {"heat"};
const char HAModeDry[] PROGMEM = {"dry"};
const char HAModeFanOnly[] PROGMEM = {"fan_only"};
// other
const char HAHexMap[] PROGMEM = {"0123456789abcdef"};
// value templates
const char HAValueTemplateFloatP1[] PROGMEM = {"{{int(float(value)*10**1)}}"};
const char HAValueTemplateFloatP2[] PROGMEM = {"{{int(float(value)*10**2)}}"};
const char HAValueTemplateFloatP3[] PROGMEM = {"{{int(float(value)*10**3)}}"};
const char HATemperatureUnitC[] PROGMEM = {"C"};
const char HATemperatureUnitF[] PROGMEM = {"F"};

View File

@@ -0,0 +1,197 @@
#ifndef AHA_HADICTIONARY_H
#define AHA_HADICTIONARY_H
// components
extern const char HAComponentBinarySensor[];
extern const char HAComponentButton[];
extern const char HAComponentCamera[];
extern const char HAComponentCover[];
extern const char HAComponentDeviceTracker[];
extern const char HAComponentDeviceAutomation[];
extern const char HAComponentLock[];
extern const char HAComponentNumber[];
extern const char HAComponentSelect[];
extern const char HAComponentSensor[];
extern const char HAComponentSwitch[];
extern const char HAComponentTag[];
extern const char HAComponentScene[];
extern const char HAComponentFan[];
extern const char HAComponentLight[];
extern const char HAComponentClimate[];
// decorators
extern const char HASerializerSlash[];
extern const char HASerializerJsonDataPrefix[];
extern const char HASerializerJsonDataSuffix[];
extern const char HASerializerJsonPropertyPrefix[];
extern const char HASerializerJsonPropertySuffix[];
extern const char HASerializerJsonEscapeChar[];
extern const char HASerializerJsonPropertiesSeparator[];
extern const char HASerializerJsonArrayPrefix[];
extern const char HASerializerJsonArraySuffix[];
extern const char HASerializerUnderscore[];
// properties
extern const char HADeviceIdentifiersProperty[];
extern const char HADeviceManufacturerProperty[];
extern const char HADeviceModelProperty[];
extern const char HADeviceSoftwareVersionProperty[];
extern const char HANameProperty[];
extern const char HAUniqueIdProperty[];
extern const char HADeviceProperty[];
extern const char HADeviceClassProperty[];
extern const char HAIconProperty[];
extern const char HARetainProperty[];
extern const char HASourceTypeProperty[];
extern const char HAEncodingProperty[];
extern const char HAOptimisticProperty[];
extern const char HAAutomationTypeProperty[];
extern const char HATypeProperty[];
extern const char HASubtypeProperty[];
extern const char HAForceUpdateProperty[];
extern const char HAUnitOfMeasurementProperty[];
extern const char HAValueTemplateProperty[];
extern const char HAOptionsProperty[];
extern const char HAMinProperty[];
extern const char HAMaxProperty[];
extern const char HAStepProperty[];
extern const char HAModeProperty[];
extern const char HACommandTemplateProperty[];
extern const char HASpeedRangeMaxProperty[];
extern const char HASpeedRangeMinProperty[];
extern const char HABrightnessScaleProperty[];
extern const char HAMinMiredsProperty[];
extern const char HAMaxMiredsProperty[];
extern const char HATemperatureUnitProperty[];
extern const char HAMinTempProperty[];
extern const char HAMaxTempProperty[];
extern const char HATempStepProperty[];
extern const char HAFanModesProperty[];
extern const char HASwingModesProperty[];
extern const char HAModesProperty[];
extern const char HATemperatureCommandTemplateProperty[];
extern const char HAPayloadOnProperty[];
// topics
extern const char HAConfigTopic[];
extern const char HAAvailabilityTopic[];
extern const char HATopic[];
extern const char HAStateTopic[];
extern const char HACommandTopic[];
extern const char HAPositionTopic[];
extern const char HAPercentageStateTopic[];
extern const char HAPercentageCommandTopic[];
extern const char HABrightnessCommandTopic[];
extern const char HABrightnessStateTopic[];
extern const char HAColorTemperatureCommandTopic[];
extern const char HAColorTemperatureStateTopic[];
extern const char HACurrentTemperatureTopic[];
extern const char HAActionTopic[];
extern const char HAAuxCommandTopic[];
extern const char HAAuxStateTopic[];
extern const char HAPowerCommandTopic[];
extern const char HAFanModeCommandTopic[];
extern const char HAFanModeStateTopic[];
extern const char HASwingModeCommandTopic[];
extern const char HASwingModeStateTopic[];
extern const char HAModeCommandTopic[];
extern const char HAModeStateTopic[];
extern const char HATemperatureCommandTopic[];
extern const char HATemperatureStateTopic[];
extern const char HARGBCommandTopic[];
extern const char HARGBStateTopic[];
// misc
extern const char HAOnline[];
extern const char HAOffline[];
extern const char HAStateOn[];
extern const char HAStateOff[];
extern const char HAStateLocked[];
extern const char HAStateUnlocked[];
extern const char HAStateNone[];
extern const char HATrue[];
extern const char HAFalse[];
extern const char HAHome[];
extern const char HANotHome[];
extern const char HATrigger[];
extern const char HAModeBox[];
extern const char HAModeSlider[];
// covers
extern const char HAClosedState[];
extern const char HAClosingState[];
extern const char HAOpenState[];
extern const char HAOpeningState[];
extern const char HAStoppedState[];
// commands
extern const char HAOpenCommand[];
extern const char HACloseCommand[];
extern const char HAStopCommand[];
extern const char HALockCommand[];
extern const char HAUnlockCommand[];
// device tracker
extern const char HAGPSType[];
extern const char HARouterType[];
extern const char HABluetoothType[];
extern const char HABluetoothLEType[];
// camera
extern const char HAEncodingBase64[];
// trigger
extern const char HAButtonShortPressType[];
extern const char HAButtonShortReleaseType[];
extern const char HAButtonLongPressType[];
extern const char HAButtonLongReleaseType[];
extern const char HAButtonDoublePressType[];
extern const char HAButtonTriplePressType[];
extern const char HAButtonQuadruplePressType[];
extern const char HAButtonQuintuplePressType[];
extern const char HATurnOnSubtype[];
extern const char HATurnOffSubtype[];
extern const char HAButton1Subtype[];
extern const char HAButton2Subtype[];
extern const char HAButton3Subtype[];
extern const char HAButton4Subtype[];
extern const char HAButton5Subtype[];
extern const char HAButton6Subtype[];
// actions
extern const char HAActionOff[];
extern const char HAActionHeating[];
extern const char HAActionCooling[];
extern const char HAActionDrying[];
extern const char HAActionIdle[];
extern const char HAActionFan[];
// fan modes
extern const char HAFanModeAuto[];
extern const char HAFanModeLow[];
extern const char HAFanModeMedium[];
extern const char HAFanModeHigh[];
// swing modes
extern const char HASwingModeOn[];
extern const char HASwingModeOff[];
// HVAC modes
extern const char HAModeAuto[];
extern const char HAModeOff[];
extern const char HAModeCool[];
extern const char HAModeHeat[];
extern const char HAModeDry[];
extern const char HAModeFanOnly[];
// other
extern const char HAHexMap[];
// value templates
extern const char HAValueTemplateFloatP1[];
extern const char HAValueTemplateFloatP2[];
extern const char HAValueTemplateFloatP3[];
extern const char HATemperatureUnitC[];
extern const char HATemperatureUnitF[];
#endif

View File

@@ -0,0 +1,214 @@
#include "HANumeric.h"
const uint8_t HANumeric::MaxDigitsNb = 19;
HANumeric HANumeric::fromStr(const uint8_t* buffer, const uint16_t length)
{
if (length == 0) {
return HANumeric();
}
const uint8_t* firstCh = &buffer[0];
int64_t out = 0;
bool isSigned = false;
if (*firstCh == '-') {
isSigned = true;
firstCh++;
}
uint8_t digitsNb = isSigned ? length - 1 : length;
if (digitsNb > MaxDigitsNb) {
return HANumeric();
}
uint64_t base = 1;
const uint8_t* ptr = &buffer[length - 1];
while (ptr >= firstCh) {
uint8_t digit = *ptr - '0';
if (digit > 9) {
return HANumeric();
}
out += digit * base;
ptr--;
base *= 10;
}
return HANumeric(isSigned ? out * -1 : out);
}
HANumeric::HANumeric():
_isSet(false),
_value(0),
_precision(0)
{
}
HANumeric::HANumeric(const float value, const uint8_t precision):
_isSet(true),
_precision(precision)
{
_value = value * static_cast<float>(getPrecisionBase());
}
HANumeric::HANumeric(const int8_t value, const uint8_t precision):
_isSet(true),
_precision(precision)
{
_value = value * static_cast<int32_t>(getPrecisionBase());
}
HANumeric::HANumeric(const int16_t value, const uint8_t precision):
_isSet(true),
_precision(precision)
{
_value = value * static_cast<int32_t>(getPrecisionBase());
}
HANumeric::HANumeric(const int32_t value, const uint8_t precision):
_isSet(true),
_precision(precision)
{
_value = value * static_cast<int32_t>(getPrecisionBase());
}
HANumeric::HANumeric(const uint8_t value, const uint8_t precision):
_isSet(true),
_precision(precision)
{
_value = value * getPrecisionBase();
}
HANumeric::HANumeric(const uint16_t value, const uint8_t precision):
_isSet(true),
_precision(precision)
{
_value = value * getPrecisionBase();
}
HANumeric::HANumeric(const uint32_t value, const uint8_t precision):
_isSet(true),
_precision(precision)
{
_value = value * getPrecisionBase();
}
#ifdef __SAMD21G18A__
HANumeric::HANumeric(const int value, const uint8_t precision):
_isSet(true),
_precision(precision)
{
_value = value * static_cast<int>(getPrecisionBase());
}
#endif
HANumeric::HANumeric(const int64_t value):
_isSet(true),
_value(value),
_precision(0)
{
}
uint32_t HANumeric::getPrecisionBase() const
{
// using pow() increases the flash size by ~2KB
switch (_precision) {
case 1:
return 10;
case 2:
return 100;
case 3:
return 1000;
default:
return 1;
}
}
uint8_t HANumeric::calculateSize() const
{
if (!_isSet) {
return 0;
}
int64_t value = _value;
const bool isSigned = value < 0;
if (isSigned) {
value *= -1;
}
uint8_t digitsNb = 1;
while (value > 9) {
value /= 10;
digitsNb++;
}
if (isSigned) {
digitsNb++; // sign
}
if (_precision > 0) {
if (value == 0) {
return 1;
}
// one digit + dot + decimal digits (+ sign)
const uint8_t minValue = isSigned ? _precision + 3 : _precision + 2;
return digitsNb >= minValue ? digitsNb + 1 : minValue;
}
return digitsNb;
}
uint16_t HANumeric::toStr(char* dst) const
{
char* prefixCh = &dst[0];
if (!_isSet || _value == 0) {
*prefixCh = '0';
return 1;
}
int64_t value = _value;
const uint8_t numberLength = calculateSize();
if (value < 0) {
value *= -1;
*prefixCh = '-';
prefixCh++;
}
if (_precision > 0) {
uint8_t i = _precision;
char* dotPtr = prefixCh + 1;
do {
*prefixCh = '0';
prefixCh++;
} while(i-- > 0);
*dotPtr = '.';
}
char* ch = &dst[numberLength - 1];
char* lastCh = ch;
char* dotPos = _precision > 0 ? &dst[numberLength - 1 - _precision] : nullptr;
while (value != 0) {
if (ch == dotPos) {
*dotPos = '.';
ch--;
continue;
}
*ch = (value % 10) + '0';
value /= 10;
ch--;
}
return lastCh - &dst[0] + 1;
}

View File

@@ -0,0 +1,233 @@
#ifndef AHA_NUMERIC_H
#define AHA_NUMERIC_H
#include <stdint.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 __SAMD21G18A__
/**
* 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

View File

@@ -0,0 +1,526 @@
#include <Arduino.h>
#ifdef ARDUINO_ARCH_SAMD
#include <avr/dtostrf.h>
#endif
#include "HASerializer.h"
#include "../ArduinoHADefines.h"
#include "../HADevice.h"
#include "../HAMqtt.h"
#include "../utils/HAUtils.h"
#include "../utils/HANumeric.h"
#include "../device-types/HABaseDeviceType.h"
uint16_t HASerializer::calculateConfigTopicLength(
const __FlashStringHelper* componentName,
const char* objectId
)
{
const HAMqtt* mqtt = HAMqtt::instance();
if (
!componentName ||
!objectId ||
!mqtt ||
!mqtt->getDiscoveryPrefix() ||
!mqtt->getDevice()
) {
return 0;
}
return
strlen(mqtt->getDiscoveryPrefix()) + 1 + // prefix with slash
strlen_P(AHAFROMFSTR(componentName)) + 1 + // component name with slash
strlen(mqtt->getDevice()->getUniqueId()) + 1 + // device ID with slash
strlen(objectId) + 1 + // object ID with slash
strlen_P(HAConfigTopic) + 1; // including null terminator
}
bool HASerializer::generateConfigTopic(
char* output,
const __FlashStringHelper* componentName,
const char* objectId
)
{
const HAMqtt* mqtt = HAMqtt::instance();
if (
!output ||
!componentName ||
!objectId ||
!mqtt ||
!mqtt->getDiscoveryPrefix() ||
!mqtt->getDevice()
) {
return false;
}
strcpy(output, mqtt->getDiscoveryPrefix());
strcat_P(output, HASerializerSlash);
strcat_P(output, AHAFROMFSTR(componentName));
strcat_P(output, HASerializerSlash);
strcat(output, mqtt->getDevice()->getUniqueId());
strcat_P(output, HASerializerSlash);
strcat(output, objectId);
strcat_P(output, HASerializerSlash);
strcat_P(output, HAConfigTopic);
return true;
}
uint16_t HASerializer::calculateDataTopicLength(
const char* objectId,
const __FlashStringHelper* topic
)
{
const HAMqtt* mqtt = HAMqtt::instance();
if (
!topic ||
!mqtt ||
!mqtt->getDataPrefix() ||
!mqtt->getDevice()
) {
return 0;
}
uint16_t size =
strlen(mqtt->getDataPrefix()) + 1 + // prefix with slash
strlen(mqtt->getDevice()->getUniqueId()) + 1 + // device ID with slash
strlen_P(AHAFROMFSTR(topic));
if (objectId) {
size += strlen(objectId) + 1; // object ID with slash;
}
return size + 1; // including null terminator
}
bool HASerializer::generateDataTopic(
char* output,
const char* objectId,
const __FlashStringHelper* topic
)
{
const HAMqtt* mqtt = HAMqtt::instance();
if (
!output ||
!topic ||
!mqtt ||
!mqtt->getDataPrefix() ||
!mqtt->getDevice()
) {
return false;
}
strcpy(output, mqtt->getDataPrefix());
strcat_P(output, HASerializerSlash);
strcat(output, mqtt->getDevice()->getUniqueId());
strcat_P(output, HASerializerSlash);
if (objectId) {
strcat(output, objectId);
strcat_P(output, HASerializerSlash);
}
strcat_P(output, AHAFROMFSTR(topic));
return true;
}
bool HASerializer::compareDataTopics(
const char* actualTopic,
const char* objectId,
const __FlashStringHelper* topic
)
{
if (!actualTopic) {
return false;
}
const uint16_t topicLength = calculateDataTopicLength(objectId, topic);
if (topicLength == 0) {
return false;
}
char expectedTopic[topicLength];
if (!generateDataTopic(expectedTopic, objectId, topic)) {
return false;
}
return memcmp(actualTopic, expectedTopic, topicLength) == 0;
}
HASerializer::HASerializer(
HABaseDeviceType* deviceType,
const uint8_t maxEntriesNb
) :
_deviceType(deviceType),
_entriesNb(0),
_maxEntriesNb(maxEntriesNb),
_entries(new SerializerEntry[maxEntriesNb])
{
}
HASerializer::~HASerializer()
{
delete[] _entries;
}
void HASerializer::set(
const __FlashStringHelper* property,
const void* value,
PropertyValueType valueType
)
{
if (!property || !value) {
return;
}
SerializerEntry* entry = addEntry();
entry->type = PropertyEntryType;
entry->subtype = static_cast<uint8_t>(valueType);
entry->property = property;
entry->value = value;
}
void HASerializer::set(const FlagType flag)
{
if (flag == WithDevice) {
SerializerEntry* entry = addEntry();
entry->type = FlagEntryType;
entry->subtype = static_cast<uint8_t>(WithDevice);
entry->property = nullptr;
entry->value = nullptr;
} else if (flag == WithAvailability) {
HAMqtt* mqtt = HAMqtt::instance();
const bool isSharedAvailability = mqtt->getDevice()->isSharedAvailabilityEnabled();
const bool isAvailabilityConfigured = _deviceType->isAvailabilityConfigured();
if (!isSharedAvailability && !isAvailabilityConfigured) {
return; // not configured
}
SerializerEntry* entry = addEntry();
entry->type = TopicEntryType;
entry->property = AHATOFSTR(HAAvailabilityTopic);
entry->value = isSharedAvailability
? mqtt->getDevice()->getAvailabilityTopic()
: nullptr;
}
}
void HASerializer::topic(const __FlashStringHelper* topic)
{
if (!_deviceType || !topic) {
return;
}
SerializerEntry* entry = addEntry();
entry->type = TopicEntryType;
entry->property = topic;
}
HASerializer::SerializerEntry* HASerializer::addEntry()
{
return &_entries[_entriesNb++]; // intentional lack of protection against overflow
}
uint16_t HASerializer::calculateSize() const
{
uint16_t size =
strlen_P(HASerializerJsonDataPrefix) +
strlen_P(HASerializerJsonDataSuffix);
for (uint8_t i = 0; i < _entriesNb; i++) {
const uint16_t entrySize = calculateEntrySize(&_entries[i]);
if (entrySize == 0) {
continue;
}
size += entrySize;
// items separator
if (i > 0) {
size += strlen_P(HASerializerJsonPropertiesSeparator);
}
}
return size;
}
bool HASerializer::flush() const
{
HAMqtt* mqtt = HAMqtt::instance();
if (!mqtt || (_deviceType && !mqtt->getDevice())) {
return false;
}
mqtt->writePayload(AHATOFSTR(HASerializerJsonDataPrefix));
for (uint8_t i = 0; i < _entriesNb; i++) {
if (i > 0) {
mqtt->writePayload(AHATOFSTR(HASerializerJsonPropertiesSeparator));
}
if (!flushEntry(&_entries[i])) {
return false;
}
}
mqtt->writePayload(AHATOFSTR(HASerializerJsonDataSuffix));
return true;
}
uint16_t HASerializer::calculateEntrySize(const SerializerEntry* entry) const
{
switch (entry->type) {
case PropertyEntryType:
return
// property name
strlen_P(HASerializerJsonPropertyPrefix) +
strlen_P(AHAFROMFSTR(entry->property)) +
strlen_P(HASerializerJsonPropertySuffix) +
// property value
calculatePropertyValueSize(entry);
case TopicEntryType:
return calculateTopicEntrySize(entry);
case FlagEntryType:
return calculateFlagSize(
static_cast<FlagType>(entry->subtype)
);
default:
return 0;
}
}
uint16_t HASerializer::calculateTopicEntrySize(
const SerializerEntry* entry
) const
{
uint16_t size = 0;
// property name
size +=
strlen_P(HASerializerJsonPropertyPrefix) +
strlen_P(AHAFROMFSTR(entry->property)) +
strlen_P(HASerializerJsonPropertySuffix);
// topic escape
size += 2 * strlen_P(HASerializerJsonEscapeChar);
// topic
if (entry->value) {
size += strlen(static_cast<const char*>(entry->value));
} else {
if (!_deviceType) {
return 0;
}
size += calculateDataTopicLength(
_deviceType->uniqueId(),
entry->property
) - 1; // exclude null terminator
}
return size;
}
uint16_t HASerializer::calculateFlagSize(const FlagType flag) const
{
const HAMqtt* mqtt = HAMqtt::instance();
const HADevice* device = mqtt->getDevice();
if (flag == WithDevice && device->getSerializer()) {
const uint16_t deviceLength = device->getSerializer()->calculateSize();
if (deviceLength == 0) {
return 0;
}
return
strlen_P(HASerializerJsonPropertyPrefix) +
strlen_P(HADeviceProperty) +
strlen_P(HASerializerJsonPropertySuffix) +
deviceLength;
}
return 0;
}
uint16_t HASerializer::calculatePropertyValueSize(
const SerializerEntry* entry
) const
{
switch (entry->subtype) {
case ConstCharPropertyValue:
case ProgmemPropertyValue: {
const char* value = static_cast<const char*>(entry->value);
const uint16_t len =
entry->subtype == ConstCharPropertyValue ? strlen(value) : strlen_P(value);
return 2 * strlen_P(HASerializerJsonEscapeChar) + len;
}
case BoolPropertyType: {
const bool value = *static_cast<const bool*>(entry->value);
return value ? strlen_P(HATrue) : strlen_P(HAFalse);
}
case NumberPropertyType: {
const HANumeric* value = static_cast<const HANumeric*>(
entry->value
);
return value->calculateSize();
}
case ArrayPropertyType: {
const HASerializerArray* array = static_cast<const HASerializerArray*>(
entry->value
);
return array->calculateSize();
}
default:
return 0;
}
}
bool HASerializer::flushEntry(const SerializerEntry* entry) const
{
HAMqtt* mqtt = HAMqtt::instance();
switch (entry->type) {
case PropertyEntryType: {
mqtt->writePayload(AHATOFSTR(HASerializerJsonPropertyPrefix));
mqtt->writePayload(entry->property);
mqtt->writePayload(AHATOFSTR(HASerializerJsonPropertySuffix));
return flushEntryValue(entry);
}
case TopicEntryType:
return flushTopic(entry);
case FlagEntryType:
return flushFlag(entry);
default:
return true;
}
}
bool HASerializer::flushEntryValue(const SerializerEntry* entry) const
{
HAMqtt* mqtt = HAMqtt::instance();
switch (entry->subtype) {
case ConstCharPropertyValue:
case ProgmemPropertyValue: {
const char* value = static_cast<const char*>(entry->value);
mqtt->writePayload(AHATOFSTR(HASerializerJsonEscapeChar));
if (entry->subtype == ConstCharPropertyValue) {
mqtt->writePayload(value, strlen(value));
} else {
mqtt->writePayload(AHATOFSTR(value));
}
mqtt->writePayload(AHATOFSTR(HASerializerJsonEscapeChar));
return true;
}
case BoolPropertyType: {
const bool value = *static_cast<const bool*>(entry->value);
mqtt->writePayload(AHATOFSTR(value ? HATrue : HAFalse));
return true;
}
case NumberPropertyType: {
const HANumeric* value = static_cast<const HANumeric*>(
entry->value
);
char tmp[HANumeric::MaxDigitsNb + 1];
const uint16_t length = value->toStr(tmp);
mqtt->writePayload(tmp, length);
return true;
}
case ArrayPropertyType: {
const HASerializerArray* array = static_cast<const HASerializerArray*>(
entry->value
);
const uint16_t size = array->calculateSize();
char tmp[size + 1]; // including null terminator
tmp[0] = 0;
array->serialize(tmp);
mqtt->writePayload(tmp, size);
return true;
}
default:
return false;
}
}
bool HASerializer::flushTopic(const SerializerEntry* entry) const
{
HAMqtt* mqtt = HAMqtt::instance();
// property name
mqtt->writePayload(AHATOFSTR(HASerializerJsonPropertyPrefix));
mqtt->writePayload(entry->property);
mqtt->writePayload(AHATOFSTR(HASerializerJsonPropertySuffix));
// value (escaped)
mqtt->writePayload(AHATOFSTR(HASerializerJsonEscapeChar));
if (entry->value) {
const char* topic = static_cast<const char*>(entry->value);
mqtt->writePayload(topic, strlen(topic));
} else {
const uint16_t length = calculateDataTopicLength(
_deviceType->uniqueId(),
entry->property
);
if (length == 0) {
return false;
}
char topic[length];
generateDataTopic(
topic,
_deviceType->uniqueId(),
entry->property
);
mqtt->writePayload(topic, length - 1);
}
mqtt->writePayload(AHATOFSTR(HASerializerJsonEscapeChar));
return true;
}
bool HASerializer::flushFlag(const SerializerEntry* entry) const
{
HAMqtt* mqtt = HAMqtt::instance();
const HADevice* device = mqtt->getDevice();
const FlagType flag = static_cast<FlagType>(entry->subtype);
if (flag == WithDevice && device) {
mqtt->writePayload(AHATOFSTR(HASerializerJsonPropertyPrefix));
mqtt->writePayload(AHATOFSTR(HADeviceProperty));
mqtt->writePayload(AHATOFSTR(HASerializerJsonPropertySuffix));
return device->getSerializer()->flush();
}
return false;
}

View File

@@ -0,0 +1,269 @@
#ifndef AHA_SERIALIZER_H
#define AHA_SERIALIZER_H
#include <stdint.h>
#include "HADictionary.h"
#include "HASerializerArray.h"
class HAMqtt;
class HABaseDeviceType;
/**
* This class allows to create JSON objects easily.
* Its main purpose is to handle configuration of a device type that's going to
* be published to the MQTT broker.
*/
class HASerializer
{
public:
/// Type of the object's entry.
enum EntryType {
UnknownEntryType = 0,
PropertyEntryType,
TopicEntryType,
FlagEntryType
};
/// The type of a flag for a FlagEntryType.
enum FlagType {
WithDevice = 1,
WithAvailability
};
/// Available data types of entries.
enum PropertyValueType {
UnknownPropertyValueType = 0,
ConstCharPropertyValue,
ProgmemPropertyValue,
BoolPropertyType,
NumberPropertyType,
ArrayPropertyType
};
/// Representation of a single entry in the object.
struct SerializerEntry {
/// Type of the entry.
EntryType type;
/// Subtype of the entry. It can be `FlagType`, `PropertyValueType` or `TopicType`.
uint8_t subtype;
/// Pointer to the property name (progmem string).
const __FlashStringHelper* property;
/// Pointer to the property value. The value type is determined by `subtype`.
const void* value;
SerializerEntry():
type(UnknownEntryType),
subtype(0),
property(nullptr),
value(nullptr)
{ }
};
/**
* Calculates the size of a configuration topic for the given component and object ID.
* The configuration topic has structure as follows: `[discovery prefix]/[component]/[device ID]_[objectId]/config`
*
* @param component The name of the HA component (e.g. `binary_sensor`).
* @param objectId The unique ID of a device type that's going to publish the config.
*/
static uint16_t calculateConfigTopicLength(
const __FlashStringHelper* component,
const char* objectId
);
/**
* Generates the configuration topic for the given component and object ID.
* The topic will be stored in the `output` variable.
*
* @param output Buffer where the topic will be written.
* @param component The name of the HA component (e.g. `binary_sensor`).
* @param objectId The unique ID of a device type that's going to publish the config.
*/
static bool generateConfigTopic(
char* output,
const __FlashStringHelper* component,
const char* objectId
);
/**
* Calculates the size of the given data topic for the given objectId.
* The data topic has structure as follows: `[data prefix]/[device ID]_[objectId]/[topic]`
*
* @param objectId The unique ID of a device type that's going to publish the data.
* @param topic The topic name (progmem string).
*/
static uint16_t calculateDataTopicLength(
const char* objectId,
const __FlashStringHelper* topic
);
/**
* Generates the data topic for the given object ID.
* The topic will be stored in the `output` variable.
*
* @param output Buffer where the topic will be written.
* @param objectId The unique ID of a device type that's going to publish the data.
* @param topic The topic name (progmem string).
*/
static bool generateDataTopic(
char* output,
const char* objectId,
const __FlashStringHelper* topic
);
/**
* Checks whether the given topic matches the data topic that can be generated
* using the given objectId and topicP.
* This method can be used to check if the received message matches some data topic.
*
* @param actualTopic The actual topic to compare.
* @param objectId The unique ID of a device type that may be the owner of the topic.
* @param topic The topic name (progmem string).
*/
static bool compareDataTopics(
const char* actualTopic,
const char* objectId,
const __FlashStringHelper* topic
);
/**
* Creates instance of the serializer for the given device type.
* Please note that the number JSON object's entries needs to be known upfront.
* This approach reduces number of memory allocations.
*
* @param deviceType The device type that owns the serializer.
* @param maxEntriesNb Maximum number of the output object entries.
*/
HASerializer(HABaseDeviceType* deviceType, const uint8_t maxEntriesNb);
/**
* Frees the dynamic memory allocated by the class.
*/
~HASerializer();
/**
* Returns the number of items that were added to the serializer.
*/
inline uint8_t getEntriesNb() const
{ return _entriesNb; }
/**
* Returns pointer to the serializer's entries.
*/
inline SerializerEntry* getEntries() const
{ return _entries; }
/**
* Adds a new entry to the serialized with a type of `PropertyEntryType`.
*
* @param property Pointer to the name of the property (progmem string).
* @param value Pointer to the value that's being set.
* @param valueType The type of the value that's passed to the method.
*/
void set(
const __FlashStringHelper* property,
const void* value,
PropertyValueType valueType = ConstCharPropertyValue
);
/**
* Adds a new entry to the serializer with a type of `FlagEntryType`.
*
* @param flag Flag to add.
*/
void set(const FlagType flag);
/**
* Adds a new entry to the serialize with a type of `TopicEntryType`.
*
* @param topic The topic name to add (progmem string).
*/
void topic(const __FlashStringHelper* topic);
/**
* Calculates the output size of the serialized JSON object.
*/
uint16_t calculateSize() const;
/**
* Flushes the JSON object to the MQTT stream.
* Please note that this method only writes the MQTT payload.
* The MQTT session needs to be opened before.
*/
bool flush() const;
private:
/// Pointer to the device type that owns the serializer.
HABaseDeviceType* _deviceType;
/// The number of entries added to the serializer.
uint8_t _entriesNb;
/// Maximum number of entries that can be added to the serializer.
uint8_t _maxEntriesNb;
/// Pointer to the serializer entries.
SerializerEntry* _entries;
/**
* Creates a new entry in the serializer's memory.
* If the limit of entries is hit, the nullptr is returned.
*/
SerializerEntry* addEntry();
/**
* Calculates the serialized size of the given entry.
* Internally, this method recognizes the type of the entry and calls
* a proper calculate method listed below.
*/
uint16_t calculateEntrySize(const SerializerEntry* entry) const;
/**
* Calculates the size of the entry of type `TopicEntryType`.
*/
uint16_t calculateTopicEntrySize(const SerializerEntry* entry) const;
/**
* Calculates the size of the entry of type `FlagEntryType`.
*/
uint16_t calculateFlagSize(const FlagType flag) const;
/**
* Calculates the size of the entry's value if the entry is `PropertyEntryType`.
*/
uint16_t calculatePropertyValueSize(const SerializerEntry* entry) const;
/**
* Calculates the size of the array if the property's value is a type of `ArrayPropertyType`.
*/
uint16_t calculateArraySize(const HASerializerArray* array) const;
/**
* Flushes the given entry to the MQTT.
* Internally this method recognizes the type of the entry and calls
* a proper flush method listed below.
*/
bool flushEntry(const SerializerEntry* entry) const;
/**
* Flushes the value of the `PropertyEntryType` entry.
*/
bool flushEntryValue(const SerializerEntry* entry) const;
/**
* Flushes the entry of type `TopicEntryType` to the MQTT.
*/
bool flushTopic(const SerializerEntry* entry) const;
/**
* Flushes the entry of type `FlagEntryType` to the MQTT.
*/
bool flushFlag(const SerializerEntry* entry) const;
};
#endif

View File

@@ -0,0 +1,84 @@
#include <Arduino.h>
#include "HASerializerArray.h"
#include "HADictionary.h"
HASerializerArray::HASerializerArray(const uint8_t size, const bool progmemItems) :
_progmemItems(progmemItems),
_size(size),
_itemsNb(0),
_items(new ItemType[size])
{
}
HASerializerArray::~HASerializerArray()
{
delete[] _items;
}
bool HASerializerArray::add(ItemType item)
{
if (_itemsNb >= _size) {
return false;
}
_items[_itemsNb++] = item;
return true;
}
uint16_t HASerializerArray::calculateSize() const
{
uint16_t size =
strlen_P(HASerializerJsonArrayPrefix) +
strlen_P(HASerializerJsonArraySuffix);
if (_itemsNb == 0) {
return size;
}
// separators between elements
size += (_itemsNb - 1) * strlen_P(HASerializerJsonPropertiesSeparator);
for (uint8_t i = 0; i < _itemsNb; i++) {
size +=
2 * strlen_P(HASerializerJsonEscapeChar)
+ (_progmemItems ? strlen_P(_items[i]) : strlen(_items[i]));
}
return size;
}
bool HASerializerArray::serialize(char* output) const
{
if (!output) {
return false;
}
strcat_P(output, HASerializerJsonArrayPrefix);
for (uint8_t i = 0; i < _itemsNb; i++) {
if (i > 0) {
strcat_P(output, HASerializerJsonPropertiesSeparator);
}
strcat_P(output, HASerializerJsonEscapeChar);
if (_progmemItems) {
strcat_P(output, _items[i]);
} else {
strcat(output, _items[i]);
}
strcat_P(output, HASerializerJsonEscapeChar);
}
strcat_P(output, HASerializerJsonArraySuffix);
return true;
}
void HASerializerArray::clear()
{
_itemsNb = 0;
}

View File

@@ -0,0 +1,75 @@
#ifndef AHA_SERIALIZERARRAY_H
#define AHA_SERIALIZERARRAY_H
#include <stdint.h>
/**
* HASerializerArray represents array of items that can be used as a HASerializer property.
*/
class HASerializerArray
{
public:
typedef const char* ItemType;
/**
* Constructs HASerializerArray with the static size (number of elements).
* The array is allocated dynamically in the memory based on the given size.
*
* @param size The desired number of elements that will be stored in the array.
* @param progmemItems Specifies whether items are going to be stored in the flash memory.
*/
HASerializerArray(const uint8_t size, const bool progmemItems = true);
~HASerializerArray();
/**
* Returns the number of elements that were added to the array.
* It can be lower than size of the array.
*/
inline uint8_t getItemsNb() const
{ return _itemsNb; }
/**
* Returns pointer to the array.
*/
inline ItemType* getItems() const
{ return _items; }
/**
* Adds a new element to the array.
*
* @param itemP Item to add (string).
* @returns Returns `true` if item has been added to the array successfully.
*/
bool add(ItemType item);
/**
* Calculates the size of the serialized array (JSON representation).
*/
uint16_t calculateSize() const;
/**
* Serializes array as JSON to the given output.
*/
bool serialize(char* output) const;
/**
* Clears the array.
*/
void clear();
private:
/// Specifies whether items are stored in the flash memory.
const bool _progmemItems;
/// The maximum size of the array.
const uint8_t _size;
/// The number of items that were added to the array.
uint8_t _itemsNb;
/// Pointer to the array elements.
ItemType* _items;
};
#endif

View File

@@ -0,0 +1,48 @@
#ifdef ARDUINO_ARCH_SAMD
#include <avr/dtostrf.h>
#endif
#include <Arduino.h>
#include "HAUtils.h"
#include "HADictionary.h"
bool HAUtils::endsWith(const char* str, const char* suffix)
{
if (str == nullptr || suffix == nullptr) {
return false;
}
const uint16_t lenstr = strlen(str);
const uint16_t lensuffix = strlen(suffix);
if (lensuffix > lenstr || lenstr == 0 || lensuffix == 0) {
return false;
}
return (strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0);
}
void HAUtils::byteArrayToStr(
char* dst,
const byte* src,
const uint16_t length
)
{
for (uint8_t i = 0; i < length; i++) {
dst[i*2] = pgm_read_byte(&HAHexMap[((char)src[i] & 0XF0) >> 4]);
dst[i*2+1] = pgm_read_byte(&HAHexMap[((char)src[i] & 0x0F)]);
}
dst[length * 2] = 0;
}
char* HAUtils::byteArrayToStr(
const byte* src,
const uint16_t length
)
{
char* dst = new char[(length * 2) + 1]; // include null terminator
byteArrayToStr(dst, src, length);
return dst;
}

View File

@@ -0,0 +1,52 @@
#ifndef AHA_HAUTILS_H
#define AHA_HAUTILS_H
#include <stdint.h>
/**
* This class provides some useful methods to make life easier.
*/
class HAUtils
{
public:
/**
* Checks whether the given `str` ends with the given `suffix`.
*
* @param str Input string to check.
* @param suffix Suffix to find
* @returns True if the given suffix is present at the end of the given string.
*/
static bool endsWith(
const char* str,
const char* suffix
);
/**
* Converts the given byte array into hex string.
* Each byte will be represented by two bytes, so the output size will be `length * 2`
*
* @param dst Destination where the string will be saved.
* @param src Bytes array to convert.
* @param length Length of the bytes array.
*/
static void byteArrayToStr(
char* dst,
const byte* src,
const uint16_t length
);
/**
* Converts the given byte array into hex string.
* This method allocates a new memory.
*
* @param src Bytes array to convert.
* @param length Length of the bytes array.
* @returns Newly allocated string containing the hex representation.
*/
static char* byteArrayToStr(
const byte* src,
const uint16_t length
);
};
#endif