2020-01-01 16:17:10 +00:00
/*
2020-06-27 10:52:44 +01:00
xsns_75_prometheus . ino - Web based information for Tasmota
2020-01-01 16:17:10 +00:00
Copyright ( C ) 2020 Theo Arends
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# ifdef USE_PROMETHEUS
/*********************************************************************************************\
* Prometheus support
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-12-16 22:51:02 +00:00
# define XSNS_75 75
2020-12-16 22:39:33 +00:00
const char * UnitfromType ( const char * type )
{
if ( strcmp ( type , " time " ) = = 0 )
{
return " _seconds " ;
}
if ( strcmp ( type , " temperature " ) = = 0 | | strcmp ( type , " dewpoint " ) = = 0 )
{
return " _celsius " ;
}
if ( strcmp ( type , " pressure " ) = = 0 )
{
return " _hpa " ;
}
if ( strcmp ( type , " voltage " ) = = 0 )
{
return " _volts " ;
}
if ( strcmp ( type , " current " ) = = 0 )
{
return " _amperes " ;
}
if ( strcmp ( type , " mass " ) = = 0 )
{
return " _grams " ;
}
if ( strcmp ( type , " carbondioxide " ) = = 0 )
{
return " _ppm " ;
}
if ( strcmp ( type , " humidity " ) = = 0 )
{
return " _percentage " ;
}
return " " ;
}
const char * FormatMetricName ( const char * metric )
{
char * formated = ( char * ) malloc ( strlen ( metric ) + 1 ) ;
uint32_t cnt = 0 ;
for ( cnt ; cnt < strlen ( metric ) + 1 ; cnt + + )
{
if ( metric [ cnt ] = = ' ' )
{ //replace space with
formated [ cnt ] = ' _ ' ;
}
else
{
formated [ cnt ] = tolower ( metric [ cnt ] ) ;
}
}
return formated ;
}
2020-01-01 16:17:10 +00:00
void HandleMetrics ( void )
{
2020-12-16 22:51:02 +00:00
if ( ! HttpCheckPriviledgedAccess ( ) ) { return ; }
2020-01-01 16:17:10 +00:00
2020-11-06 16:09:13 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_HTTP " Prometheus " ) ) ;
2020-01-01 16:17:10 +00:00
WSContentBegin ( 200 , CT_PLAIN ) ;
2020-12-16 22:51:02 +00:00
2020-01-01 16:17:10 +00:00
char parameter [ FLOATSZ ] ;
2020-06-27 12:21:11 +01:00
// Pseudo-metric providing metadata about the running firmware version.
WSContentSend_P ( PSTR ( " # TYPE tasmota_info gauge \n tasmota_info{version= \" %s \" ,image= \" %s \" ,build_timestamp= \" %s \" } 1 \n " ) ,
2020-10-30 11:29:48 +00:00
TasmotaGlobal . version , TasmotaGlobal . image_name , GetBuildDateAndTime ( ) . c_str ( ) ) ;
2020-10-28 16:32:07 +00:00
WSContentSend_P ( PSTR ( " # TYPE tasmota_uptime_seconds gauge \n tasmota_uptime_seconds %d \n " ) , TasmotaGlobal . uptime ) ;
2020-06-27 03:10:00 +01:00
WSContentSend_P ( PSTR ( " # TYPE tasmota_boot_count counter \n tasmota_boot_count %d \n " ) , Settings . bootcount ) ;
2020-06-29 01:35:27 +01:00
WSContentSend_P ( PSTR ( " # TYPE tasmota_flash_writes_total counter \n tasmota_flash_writes_total %d \n " ) , Settings . save_flag ) ;
2020-06-25 11:06:24 +01:00
2020-12-16 22:51:02 +00:00
2020-06-28 02:43:29 +01:00
// Pseudo-metric providing metadata about the WiFi station.
WSContentSend_P ( PSTR ( " # TYPE tasmota_wifi_station_info gauge \n tasmota_wifi_station_info{bssid= \" %s \" ,ssid= \" %s \" } 1 \n " ) , WiFi . BSSIDstr ( ) . c_str ( ) , WiFi . SSID ( ) . c_str ( ) ) ;
// Wi-Fi Signal strength
WSContentSend_P ( PSTR ( " # TYPE tasmota_wifi_station_signal_dbm gauge \n tasmota_wifi_station_signal_dbm{mac_address= \" %s \" } %d \n " ) , WiFi . BSSIDstr ( ) . c_str ( ) , WiFi . RSSI ( ) ) ;
2020-12-16 22:51:02 +00:00
if ( ! isnan ( TasmotaGlobal . temperature_celsius ) ) {
2020-10-28 18:03:39 +00:00
dtostrfd ( TasmotaGlobal . temperature_celsius , Settings . flag2 . temperature_resolution , parameter ) ;
WSContentSend_P ( PSTR ( " # TYPE tasmotaglobal_temperature_celsius gauge \n tasmotaglobal_temperature_celsius %s \n " ) , parameter ) ;
2020-01-01 16:17:10 +00:00
}
2020-12-16 22:51:02 +00:00
if ( TasmotaGlobal . humidity ! = 0 ) {
2020-10-28 18:03:39 +00:00
dtostrfd ( TasmotaGlobal . humidity , Settings . flag2 . humidity_resolution , parameter ) ;
WSContentSend_P ( PSTR ( " # TYPE tasmotaglobal_humidity gauge \n tasmotaglobal_humidity %s \n " ) , parameter ) ;
2020-01-01 16:17:10 +00:00
}
2020-12-16 22:51:02 +00:00
if ( TasmotaGlobal . pressure_hpa ! = 0 ) {
2020-10-28 18:03:39 +00:00
dtostrfd ( TasmotaGlobal . pressure_hpa , Settings . flag2 . pressure_resolution , parameter ) ;
WSContentSend_P ( PSTR ( " # TYPE tasmotaglobal_pressure_hpa gauge \n tasmotaglobal_pressure_hpa %s \n " ) , parameter ) ;
2020-01-01 16:17:10 +00:00
}
# ifdef USE_ENERGY_SENSOR
dtostrfd ( Energy . voltage [ 0 ] , Settings . flag2 . voltage_resolution , parameter ) ;
2020-08-19 19:57:28 +01:00
WSContentSend_P ( PSTR ( " # TYPE energy_voltage_volts gauge \n energy_voltage_volts %s \n " ) , parameter ) ;
2020-01-01 16:17:10 +00:00
dtostrfd ( Energy . current [ 0 ] , Settings . flag2 . current_resolution , parameter ) ;
2020-08-19 19:57:28 +01:00
WSContentSend_P ( PSTR ( " # TYPE energy_current_amperes gauge \n energy_current_amperes %s \n " ) , parameter ) ;
2020-01-01 16:17:10 +00:00
dtostrfd ( Energy . active_power [ 0 ] , Settings . flag2 . wattage_resolution , parameter ) ;
2020-08-19 19:57:28 +01:00
WSContentSend_P ( PSTR ( " # TYPE energy_power_active_watts gauge \n energy_power_active_watts %s \n " ) , parameter ) ;
2020-01-01 16:17:10 +00:00
dtostrfd ( Energy . daily , Settings . flag2 . energy_resolution , parameter ) ;
2020-08-19 19:57:28 +01:00
WSContentSend_P ( PSTR ( " # TYPE energy_power_kilowatts_daily counter \n energy_power_kilowatts_daily %s \n " ) , parameter ) ;
2020-01-01 16:17:10 +00:00
dtostrfd ( Energy . total , Settings . flag2 . energy_resolution , parameter ) ;
2020-08-19 19:57:28 +01:00
WSContentSend_P ( PSTR ( " # TYPE energy_power_kilowatts_total counter \n energy_power_kilowatts_total %s \n " ) , parameter ) ;
2020-01-01 16:17:10 +00:00
# endif
2020-12-16 22:51:02 +00:00
for ( uint32_t device = 0 ; device < TasmotaGlobal . devices_present ; device + + ) {
2020-12-12 09:35:57 +00:00
power_t mask = 1 < < device ;
2020-12-16 22:51:02 +00:00
WSContentSend_P ( PSTR ( " # TYPE relay%d_state gauge \n relay%d_state %d \n " ) , device + 1 , device + 1 , ( TasmotaGlobal . power & mask ) ) ;
2020-12-12 09:35:57 +00:00
}
2020-10-30 11:29:48 +00:00
ResponseClear ( ) ;
2020-01-02 13:17:49 +00:00
MqttShowSensor ( ) ;
2020-12-16 22:39:33 +00:00
char json [ strlen ( TasmotaGlobal . mqtt_data ) + 1 ] ;
2020-10-30 11:29:48 +00:00
snprintf_P ( json , sizeof ( json ) , TasmotaGlobal . mqtt_data ) ;
2020-12-16 22:39:33 +00:00
String jsonStr = json ;
JsonParser parser ( ( char * ) jsonStr . c_str ( ) ) ;
JsonParserObject root = parser . getRootObject ( ) ;
if ( root )
{ // did JSON parsing went ok?
for ( auto key1 : root )
{
JsonParserToken value1 = key1 . getValue ( ) ;
if ( value1 . isObject ( ) )
{
JsonParserObject Object2 = value1 . getObject ( ) ;
for ( auto key2 : Object2 )
{
JsonParserToken value2 = key2 . getValue ( ) ;
if ( value2 . isObject ( ) )
{
JsonParserObject Object3 = value2 . getObject ( ) ;
for ( auto key3 : Object3 )
{
const char * value = key3 . getValue ( ) . getStr ( nullptr ) ;
if ( value ! = nullptr & & isdigit ( value [ 0 ] ) )
{
const char * sensor = FormatMetricName ( key2 . getStr ( ) ) ; //cleanup sensor name
const char * type = FormatMetricName ( key3 . getStr ( ) ) ; //cleanup sensor type
const char * unit = UnitfromType ( type ) ; //grab base unit corresponding to type
WSContentSend_P ( PSTR ( " # TYPE tasmota_sensors_%s%s gauge \n tasmota_sensors_%s%s{sensor= \" %s \" } %s \n " ) , type , unit , type , unit , sensor , value ) ; //build metric as "# TYPE tasmota_sensors_%type%_%unit% gauge\ntasmotasensors_%type%_%unit%{sensor=%sensor%"} %value%""
}
}
}
else
{
const char * value = value2 . getStr ( nullptr ) ;
if ( value ! = nullptr & & isdigit ( value [ 0 ] ) )
{
const char * sensor = FormatMetricName ( key1 . getStr ( ) ) ;
const char * type = FormatMetricName ( key2 . getStr ( ) ) ;
const char * unit = UnitfromType ( type ) ;
WSContentSend_P ( PSTR ( " # TYPE tasmota_sensors_%s%s gauge \n tasmota_sensors_%s%s{sensor= \" %s \" } %s \n " ) , type , unit , type , unit , sensor , value ) ;
}
}
}
}
else
{
const char * value = value1 . getStr ( nullptr ) ;
if ( value ! = nullptr & & isdigit ( value [ 0 ] & & strcmp ( key1 . getStr ( ) , " Time " ) ! = 0 ) )
{ //remove false 'time' metric
const char * sensor = FormatMetricName ( key1 . getStr ( ) ) ;
WSContentSend_P ( PSTR ( " # TYPE tasmota_sensors_%s gauge \n tasmota_sensors{sensor= \" %s \" } %s \n " ) , sensor , sensor , value ) ;
}
}
}
}
2020-01-02 13:17:49 +00:00
2020-01-01 16:17:10 +00:00
WSContentEnd ( ) ;
}
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-06-27 10:52:44 +01:00
bool Xsns75 ( uint8_t function )
2020-01-01 16:17:10 +00:00
{
bool result = false ;
2020-12-16 22:51:02 +00:00
switch ( function ) {
2020-12-16 22:39:33 +00:00
case FUNC_WEB_ADD_HANDLER :
WebServer_on ( PSTR ( " /metrics " ) , HandleMetrics ) ;
break ;
2020-01-01 16:17:10 +00:00
}
return result ;
}
2020-12-16 22:51:02 +00:00
# endif // USE_PROMETHEUS