2024-05-13 18:09:31 +00:00
# pragma once
# include "wled.h"
# include <AHT10.h>
# define AHT10_SUCCESS 1
2024-05-14 15:54:43 +00:00
class UsermodAHT10 : public Usermod
{
private :
static const char _name [ ] ;
unsigned long _lastLoopCheck = 0 ;
2024-05-14 21:15:59 +00:00
2024-05-15 19:32:08 +00:00
bool _settingEnabled : 1 ; // Enable the usermod
bool _mqttPublish : 1 ; // Publish mqtt values
bool _mqttPublishAlways : 1 ; // Publish always, regardless if there is a change
bool _mqttHomeAssistant : 1 ; // Enable Home Assistant docs
bool _initDone : 1 ; // Initialization is done
2024-05-14 15:54:43 +00:00
// Settings. Some of these are stored in a different format than they're user settings - so we don't have to convert at runtime
uint8_t _i2cAddress = AHT10_ADDRESS_0X38 ;
ASAIR_I2C_SENSOR _ahtType = AHT10_SENSOR ;
uint16_t _checkInterval = 60000 ; // milliseconds, user settings is in seconds
float _decimalFactor = 100 ; // a power of 10 factor. 1 would be no change, 10 is one decimal, 100 is two etc. User sees a power of 10 (0, 1, 2, ..)
uint8_t _lastStatus = 0 ;
float _lastHumidity = 0 ;
float _lastTemperature = 0 ;
2024-05-15 19:26:30 +00:00
# ifndef WLED_MQTT_DISABLE
float _lastHumiditySent = 0 ;
float _lastTemperatureSent = 0 ;
# endif
2024-05-14 20:53:03 +00:00
AHT10 * _aht = nullptr ;
2024-05-14 15:54:43 +00:00
float truncateDecimals ( float val )
{
return roundf ( val * _decimalFactor ) / _decimalFactor ;
}
void initializeAht ( )
{
if ( _aht ! = nullptr )
{
delete _aht ;
2024-05-13 18:09:31 +00:00
}
2024-05-14 15:54:43 +00:00
_aht = new AHT10 ( _i2cAddress , _ahtType ) ;
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
_lastStatus = 0 ;
_lastHumidity = 0 ;
_lastTemperature = 0 ;
}
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
~ UsermodAHT10 ( )
{
delete _aht ;
_aht = nullptr ;
}
2024-05-13 18:09:31 +00:00
2024-05-14 20:53:03 +00:00
# ifndef WLED_DISABLE_MQTT
void mqttInitialize ( )
{
// This is a generic "setup mqtt" function, So we must abort if we're not to do mqtt
2024-05-15 19:32:08 +00:00
if ( ! WLED_MQTT_CONNECTED | | ! _mqttPublish | | ! _mqttHomeAssistant )
2024-05-14 20:53:03 +00:00
return ;
char topic [ 128 ] ;
snprintf_P ( topic , 127 , " %s/temperature " , mqttDeviceTopic ) ;
mqttCreateHassSensor ( F ( " Temperature " ) , topic , F ( " temperature " ) , F ( " °C " ) ) ;
snprintf_P ( topic , 127 , " %s/humidity " , mqttDeviceTopic ) ;
mqttCreateHassSensor ( F ( " Humidity " ) , topic , F ( " humidity " ) , F ( " % " ) ) ;
}
2024-05-15 19:26:30 +00:00
void mqttPublishIfChanged ( const __FlashStringHelper * topic , float & lastState , float state , float minChange )
2024-05-14 20:53:03 +00:00
{
// Check if MQTT Connected, otherwise it will crash the 8266
2024-05-15 16:56:45 +00:00
// Only report if the change is larger than the required diff
2024-05-15 19:32:08 +00:00
if ( WLED_MQTT_CONNECTED & & _mqttPublish & & ( _mqttPublishAlways | | fabsf ( lastState - state ) > minChange ) )
2024-05-14 20:53:03 +00:00
{
char subuf [ 128 ] ;
snprintf_P ( subuf , 127 , PSTR ( " %s/%s " ) , mqttDeviceTopic , ( const char * ) topic ) ;
mqtt - > publish ( subuf , 0 , false , String ( state ) . c_str ( ) ) ;
2024-05-15 19:26:30 +00:00
lastState = state ;
2024-05-14 20:53:03 +00:00
}
}
// Create an MQTT Sensor for Home Assistant Discovery purposes, this includes a pointer to the topic that is published to in the Loop.
void mqttCreateHassSensor ( const String & name , const String & topic , const String & deviceClass , const String & unitOfMeasurement )
{
2024-05-15 16:35:33 +00:00
String t = String ( F ( " homeassistant/sensor/ " ) ) + mqttClientID + " / " + name + F ( " /config " ) ;
2024-05-14 20:53:03 +00:00
StaticJsonDocument < 600 > doc ;
doc [ F ( " name " ) ] = name ;
doc [ F ( " state_topic " ) ] = topic ;
doc [ F ( " unique_id " ) ] = String ( mqttClientID ) + name ;
if ( unitOfMeasurement ! = " " )
doc [ F ( " unit_of_measurement " ) ] = unitOfMeasurement ;
if ( deviceClass ! = " " )
doc [ F ( " device_class " ) ] = deviceClass ;
doc [ F ( " expire_after " ) ] = 1800 ;
JsonObject device = doc . createNestedObject ( F ( " device " ) ) ; // attach the sensor to the same device
device [ F ( " name " ) ] = serverDescription ;
device [ F ( " identifiers " ) ] = " wled-sensor- " + String ( mqttClientID ) ;
device [ F ( " manufacturer " ) ] = F ( WLED_BRAND ) ;
device [ F ( " model " ) ] = F ( WLED_PRODUCT_NAME ) ;
device [ F ( " sw_version " ) ] = versionString ;
String temp ;
serializeJson ( doc , temp ) ;
DEBUG_PRINTLN ( t ) ;
DEBUG_PRINTLN ( temp ) ;
mqtt - > publish ( t . c_str ( ) , 0 , true , temp . c_str ( ) ) ;
}
# endif
2024-05-14 15:54:43 +00:00
public :
void setup ( )
{
initializeAht ( ) ;
}
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
void loop ( )
{
// if usermod is disabled or called during strip updating just exit
// NOTE: on very long strips strip.isUpdating() may always return true so update accordingly
2024-05-15 19:32:08 +00:00
if ( ! _settingEnabled | | strip . isUpdating ( ) )
2024-05-14 15:54:43 +00:00
return ;
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
// do your magic here
unsigned long currentTime = millis ( ) ;
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
if ( currentTime - _lastLoopCheck < _checkInterval )
return ;
_lastLoopCheck = currentTime ;
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
_lastStatus = _aht - > readRawData ( ) ;
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
if ( _lastStatus = = AHT10_ERROR )
{
// Perform softReset and retry
2024-05-15 16:35:33 +00:00
DEBUG_PRINTLN ( F ( " AHTxx returned error, doing softReset " ) ) ;
2024-05-14 15:54:43 +00:00
if ( ! _aht - > softReset ( ) )
2024-05-13 18:09:31 +00:00
{
2024-05-15 16:35:33 +00:00
DEBUG_PRINTLN ( F ( " softReset failed " ) ) ;
2024-05-14 15:54:43 +00:00
return ;
2024-05-13 18:09:31 +00:00
}
2024-05-14 15:54:43 +00:00
_lastStatus = _aht - > readRawData ( ) ;
}
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
if ( _lastStatus = = AHT10_SUCCESS )
{
float temperature = truncateDecimals ( _aht - > readTemperature ( AHT10_USE_READ_DATA ) ) ;
float humidity = truncateDecimals ( _aht - > readHumidity ( AHT10_USE_READ_DATA ) ) ;
2024-05-13 18:09:31 +00:00
2024-05-14 20:53:03 +00:00
# ifndef WLED_DISABLE_MQTT
2024-05-14 15:54:43 +00:00
// Push to MQTT
2024-05-15 16:56:45 +00:00
// We can avoid reporting if the change is insignificant. The threshold chosen is below the level of accuracy, but way above 0.01 which is the precision of the value provided.
// The AHT10/15/20 has an accuracy of 0.3C in the temperature readings
2024-05-15 19:26:30 +00:00
mqttPublishIfChanged ( F ( " temperature " ) , _lastTemperatureSent , temperature , 0.1f ) ;
2024-05-15 16:56:45 +00:00
// The AHT10/15/20 has an accuracy in the humidity sensor of 2%
2024-05-15 19:26:30 +00:00
mqttPublishIfChanged ( F ( " humidity " ) , _lastHumiditySent , humidity , 0.5f ) ;
2024-05-14 20:53:03 +00:00
# endif
2024-05-14 15:54:43 +00:00
// Store
_lastTemperature = temperature ;
2024-05-14 20:53:03 +00:00
_lastHumidity = humidity ;
2024-05-13 18:09:31 +00:00
}
2024-05-14 15:54:43 +00:00
}
2024-05-13 18:09:31 +00:00
2024-05-14 20:53:03 +00:00
# ifndef WLED_DISABLE_MQTT
void onMqttConnect ( bool sessionPresent )
{
mqttInitialize ( ) ;
}
# endif
2024-05-14 15:54:43 +00:00
uint16_t getId ( )
{
return USERMOD_ID_AHT10 ;
}
2024-05-14 20:53:03 +00:00
void addToJsonInfo ( JsonObject & root ) override
2024-05-14 15:54:43 +00:00
{
// if "u" object does not exist yet wee need to create it
2024-05-15 16:35:33 +00:00
JsonObject user = root [ " u " ] ;
2024-05-14 15:54:43 +00:00
if ( user . isNull ( ) )
2024-05-15 16:35:33 +00:00
user = root . createNestedObject ( " u " ) ;
2024-05-13 18:09:31 +00:00
# ifdef USERMOD_AHT10_DEBUG
2024-05-14 20:53:03 +00:00
JsonArray temp = user . createNestedArray ( F ( " AHT last loop " ) ) ;
2024-05-14 15:54:43 +00:00
temp . add ( _lastLoopCheck ) ;
2024-05-14 20:53:03 +00:00
temp = user . createNestedArray ( F ( " AHT last status " ) ) ;
2024-05-14 15:54:43 +00:00
temp . add ( _lastStatus ) ;
2024-05-13 18:09:31 +00:00
# endif
2024-05-14 20:53:03 +00:00
JsonArray jsonTemp = user . createNestedArray ( F ( " Temperature " ) ) ;
JsonArray jsonHumidity = user . createNestedArray ( F ( " Humidity " ) ) ;
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
if ( _lastLoopCheck = = 0 )
{
// Before first run
jsonTemp . add ( F ( " Not read yet " ) ) ;
jsonHumidity . add ( F ( " Not read yet " ) ) ;
return ;
2024-05-13 18:09:31 +00:00
}
2024-05-14 15:54:43 +00:00
if ( _lastStatus ! = AHT10_SUCCESS )
2024-05-13 18:09:31 +00:00
{
2024-05-14 15:54:43 +00:00
jsonTemp . add ( F ( " An error occurred " ) ) ;
jsonHumidity . add ( F ( " An error occurred " ) ) ;
return ;
2024-05-13 18:09:31 +00:00
}
2024-05-14 15:54:43 +00:00
jsonTemp . add ( _lastTemperature ) ;
jsonTemp . add ( F ( " °C " ) ) ;
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
jsonHumidity . add ( _lastHumidity ) ;
jsonHumidity . add ( F ( " % " ) ) ;
}
2024-05-13 18:09:31 +00:00
2024-05-14 20:53:03 +00:00
void addToConfig ( JsonObject & root )
2024-05-14 15:54:43 +00:00
{
JsonObject top = root . createNestedObject ( FPSTR ( _name ) ) ;
2024-05-15 19:32:08 +00:00
top [ F ( " Enabled " ) ] = _settingEnabled ;
2024-05-14 15:54:43 +00:00
top [ F ( " I2CAddress " ) ] = static_cast < uint8_t > ( _i2cAddress ) ;
top [ F ( " SensorType " ) ] = _ahtType ;
top [ F ( " CheckInterval " ) ] = _checkInterval / 1000 ;
top [ F ( " Decimals " ) ] = log10f ( _decimalFactor ) ;
2024-05-14 20:53:03 +00:00
# ifndef WLED_DISABLE_MQTT
2024-05-15 19:32:08 +00:00
top [ F ( " MqttPublish " ) ] = _mqttPublish ;
top [ F ( " MqttPublishAlways " ) ] = _mqttPublishAlways ;
top [ F ( " MqttHomeAssistantDiscovery " ) ] = _mqttHomeAssistant ;
2024-05-14 20:53:03 +00:00
# endif
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
DEBUG_PRINTLN ( F ( " AHT10 config saved. " ) ) ;
}
2024-05-13 18:09:31 +00:00
2024-05-14 20:53:03 +00:00
bool readFromConfig ( JsonObject & root ) override
2024-05-14 15:54:43 +00:00
{
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
JsonObject top = root [ FPSTR ( _name ) ] ;
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
bool configComplete = ! top . isNull ( ) ;
if ( ! configComplete )
return false ;
2024-05-14 21:15:59 +00:00
bool tmpBool = false ;
configComplete & = getJsonValue ( top [ F ( " Enabled " ) ] , tmpBool ) ;
if ( configComplete )
2024-05-15 19:32:08 +00:00
_settingEnabled = tmpBool ;
2024-05-14 21:15:59 +00:00
2024-05-14 15:54:43 +00:00
configComplete & = getJsonValue ( top [ F ( " I2CAddress " ) ] , _i2cAddress ) ;
configComplete & = getJsonValue ( top [ F ( " CheckInterval " ) ] , _checkInterval ) ;
if ( configComplete )
{
if ( 1 < = _checkInterval & & _checkInterval < = 600 )
_checkInterval * = 1000 ;
else
// Invalid input
_checkInterval = 60000 ;
}
configComplete & = getJsonValue ( top [ F ( " Decimals " ) ] , _decimalFactor ) ;
if ( configComplete )
{
if ( 0 < = _decimalFactor & & _decimalFactor < = 5 )
_decimalFactor = pow10f ( _decimalFactor ) ;
else
// Invalid input
_decimalFactor = 100 ;
}
2024-05-13 18:09:31 +00:00
2024-05-14 15:54:43 +00:00
uint8_t tmpAhtType ;
configComplete & = getJsonValue ( top [ F ( " SensorType " ) ] , tmpAhtType ) ;
if ( configComplete )
{
if ( 0 < = tmpAhtType & & tmpAhtType < = 2 )
_ahtType = static_cast < ASAIR_I2C_SENSOR > ( tmpAhtType ) ;
else
// Invalid input
_ahtType = ASAIR_I2C_SENSOR : : AHT10_SENSOR ;
2024-05-13 18:09:31 +00:00
}
2024-05-14 15:54:43 +00:00
2024-05-14 20:53:03 +00:00
# ifndef WLED_DISABLE_MQTT
2024-05-14 21:15:59 +00:00
configComplete & = getJsonValue ( top [ F ( " MqttPublish " ) ] , tmpBool ) ;
if ( configComplete )
2024-05-15 19:32:08 +00:00
_mqttPublish = tmpBool ;
2024-05-14 21:15:59 +00:00
configComplete & = getJsonValue ( top [ F ( " MqttPublishAlways " ) ] , tmpBool ) ;
if ( configComplete )
2024-05-15 19:32:08 +00:00
_mqttPublishAlways = tmpBool ;
2024-05-14 21:15:59 +00:00
configComplete & = getJsonValue ( top [ F ( " MqttHomeAssistantDiscovery " ) ] , tmpBool ) ;
if ( configComplete )
2024-05-15 19:32:08 +00:00
_mqttHomeAssistant = tmpBool ;
2024-05-14 20:53:03 +00:00
# endif
2024-05-15 19:32:08 +00:00
if ( _initDone )
2024-05-14 15:54:43 +00:00
{
// Reloading config
initializeAht ( ) ;
2024-05-14 20:53:03 +00:00
# ifndef WLED_DISABLE_MQTT
mqttInitialize ( ) ;
# endif
2024-05-14 15:54:43 +00:00
}
2024-05-15 19:32:08 +00:00
_initDone = true ;
2024-05-14 15:54:43 +00:00
return configComplete ;
}
2024-05-13 18:09:31 +00:00
} ;
2024-05-14 15:54:43 +00:00
const char UsermodAHT10 : : _name [ ] PROGMEM = " AHTxx " ;