2022-02-07 19:39:46 +00:00
# $Id$
2020-12-10 11:33:17 +00:00
# 93_InfluxDBLogger.pm
#
package main ;
use strict ;
use warnings ;
use HttpUtils ;
my $ total_writes_name = "total_writes" ;
2020-12-14 09:14:34 +00:00
my $ total_events_name = "total_events" ;
2020-12-10 11:33:17 +00:00
my $ succeeded_writes_name = "succeeded_writes" ;
my $ failed_writes_name = "failed_writes" ;
my $ dropped_writes_name = "dropped_writes" ;
my $ droppeed_writes_last_message_name = "dropped_writes_last_message" ;
my $ failed_writes_last_error_name = "failed_writes_last_error" ;
# FHEM Modulfunktionen
sub InfluxDBLogger_Initialize ($) {
my ( $ hash ) = @ _ ;
$ hash - > { DefFn } = "InfluxDBLogger_Define" ;
$ hash - > { NotifyFn } = "InfluxDBLogger_Notify" ;
$ hash - > { SetFn } = "InfluxDBLogger_Set" ;
$ hash - > { RenameFn } = "InfluxDBLogger_Rename" ;
2022-02-07 19:39:46 +00:00
$ hash - > { AttrList } = "readingTimeStamps:1,0 stringValuesAllowed:1,0 disable:1,0 security:basic_auth,none,token username readingInclude readingExclude conversions deviceTagName measurement tags fields api:v1,v2 org precision:ms,s,us,ns " . $ readingFnAttributes ;
2020-12-10 11:33:17 +00:00
Log3 undef , 2 , "InfluxDBLogger: Initialized new" ;
}
sub InfluxDBLogger_Define ($$) {
my ( $ hash , $ def ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my @ a = split ( "[ \t][ \t]*" , $ def ) ;
return "Usage: define devname InfluxDBLogger [http|https]://IP_or_Hostname:port dbname devspec" if ( scalar ( @ a ) < 4 ) ;
$ hash - > { URL } = $ a [ 2 ] ;
$ hash - > { DATABASE } = $ a [ 3 ] ;
$ hash - > { NOTIFYDEV } = $ a [ 4 ] ;
if ( "THIS_WONT_USUALY_MATCH" =~ /$hash->{NOTIFYDEV}/ && $ init_done ) {
$ attr { $ name } { disable } = "1" ;
Log3 $ name , 2 , "InfluxDBLogger: [$name] You specified a very loose device spec. To avoid a lot of events in your influx database this module is disabled on default. You might want to use the readingRegEx-attribute and enable the device afterwards." ;
}
Log3 $ name , 3 , "InfluxDBLogger: [$name] defined with server " . $ hash - > { URL } . " database " . $ hash - > { DATABASE } . " notifydev " . $ hash - > { NOTIFYDEV } ;
}
sub InfluxDBLogger_Notify ($$)
{
my ( $ own_hash , $ dev_hash ) = @ _ ;
2021-02-06 20:08:37 +00:00
my $ name = $ own_hash - > { NAME } ; # own name
2020-12-10 11:33:17 +00:00
return "" if ( IsDisabled ( $ name ) ) ; # Return without any further action if the module is disabled
return "" if ( ! $ init_done ) ;
my $ devName = $ dev_hash - > { NAME } ; # Device that created the events
my $ events = deviceEvents ( $ dev_hash , 1 ) ;
return "" if ( $ devName eq "global" && grep ( m/^INITIALIZED|REREADCFG$/ , @ { $ events } ) ) ;
return "" if ( $ own_hash - > { TYPE } eq $ dev_hash - > { TYPE } ) ; # avoid endless loops from logger to logger
Log3 $ name , 4 , "InfluxDBLogger: [$name] notified from device $devName" ;
2021-02-06 20:08:37 +00:00
InfluxDBLogger_BuildAndSend ( $ own_hash , $ dev_hash , $ events ) ;
}
sub InfluxDBLogger_BuildAndSend ($$$)
{
my ( $ own_hash , $ dev_hash , $ events ) = @ _ ;
my $ name = $ own_hash - > { NAME } ; # own name
2020-12-10 11:33:17 +00:00
my % map = InfluxDBLogger_BuildMap ( $ own_hash , $ dev_hash , $ events ) ;
my @ incompatible = ( ) ;
my ( $ data , $ rows ) = InfluxDBLogger_BuildData ( $ own_hash , $ dev_hash , \ % map , \ @ incompatible ) ;
InfluxDBLogger_Send ( $ own_hash , $ data , $ rows ) ;
if ( scalar ( @ incompatible ) > 0 ) {
InfluxDBLogger_DroppedIncompatibleValues ( $ own_hash , $ name , @ incompatible ) ;
}
}
2021-02-06 20:08:37 +00:00
sub InfluxDBLogger_Write ($$)
{
my ( $ hash , $ name ) = @ _ ;
return "" if ( IsDisabled ( $ name ) ) ; # Return without any further action if the module is disabled
my @ devices = devspec2array ( $ hash - > { NOTIFYDEV } ) ;
foreach my $ deviceName ( @ devices ) {
my @ events = ( ) ;
Log3 $ name , 4 , "DEVNAME $deviceName" ;
my $ dev_hash = $ defs { $ deviceName } ;
Log3 $ name , 4 , "DEVHASH $dev_hash" ;
my $ readings = $ dev_hash - > { READINGS } ;
Log3 $ name , 4 , "BEFORE READING $readings" ;
foreach my $ key ( keys % { $ readings } ) {
Log3 $ name , 4 , "READING $key" ;
my $ value = ReadingsVal ( $ deviceName , $ key , undef ) ;
push ( @ events , $ key . ": " . $ value ) ;
}
InfluxDBLogger_BuildAndSend ( $ hash , $ dev_hash , \ @ events ) ;
}
}
2020-12-10 11:33:17 +00:00
sub InfluxDBLogger_Send ($$$)
{
my ( $ own_hash , $ data , $ rows ) = @ _ ;
my $ name = $ own_hash - > { NAME } ;
if ( $ data ne "" ) {
my $ total_writes = ReadingsVal ( $ name , $ total_writes_name , 0 ) ;
2020-12-14 09:14:34 +00:00
my $ total_events = ReadingsVal ( $ name , $ total_events_name , 0 ) ;
$ total_writes += 1 ;
$ total_events += $ rows ;
readingsBeginUpdate ( $ own_hash ) ;
readingsBulkUpdate ( $ own_hash , $ total_writes_name , $ total_writes ) ;
readingsBulkUpdate ( $ own_hash , $ total_events_name , $ total_events ) ;
readingsBulkUpdate ( $ own_hash , "state" , InfluxDBLogger_BuildState ( $ own_hash , $ name ) ) ;
readingsEndUpdate ( $ own_hash , 1 ) ;
2020-12-10 11:33:17 +00:00
my $ reqpar = {
url = > InfluxDBLogger_BuildUrl ( $ own_hash ) ,
method = > "POST" ,
data = > $ data ,
hideurl = > 1 ,
hash = > $ own_hash ,
callback = > \ & InfluxDBLogger_HttpCallback
} ;
InfluxDBLogger_AddSecurity ( $ own_hash , $ name , $ reqpar ) ;
Log3 $ name , 4 , "InfluxDBLogger: [$name] Sending data " . $ reqpar - > { data } . " to " . $ reqpar - > { url } ;
HttpUtils_NonblockingGet ( $ reqpar ) ;
}
}
sub InfluxDBLogger_AddSecurity ($$$)
{
my ( $ own_hash , $ name , $ reqpar ) = @ _ ;
my $ security = AttrVal ( $ name , "security" , "" ) ;
if ( $ security eq "basic_auth" )
{
my $ user = AttrVal ( $ name , "username" , undef ) ;
$ reqpar - > { user } = $ user ;
$ reqpar - > { pwd } = InfluxDBLogger_GetPassword ( $ own_hash , $ name ) ;
}
elsif ( $ security eq "token" )
{
my $ token = InfluxDBLogger_ReadSecret ( $ own_hash , $ name , "token" ) ;
$ reqpar - > { header } = { "Authorization" = > "Token " . $ token } ;
}
}
sub InfluxDBLogger_BuildMap ($$$)
{
my ( $ own_hash , $ dev_hash , $ events ) = @ _ ;
my $ name = $ own_hash - > { NAME } ;
my $ devName = $ dev_hash - > { NAME } ;
my % map = ( ) ;
2022-02-07 19:39:46 +00:00
my $ readingInclude = AttrVal ( $ name , "readingInclude" , undef ) ;
my $ readingExclude = AttrVal ( $ name , "readingExclude" , undef ) ;
2020-12-10 11:33:17 +00:00
foreach my $ event ( @ { $ events } ) {
$ event = "" if ( ! defined ( $ event ) ) ;
if ( ( ! defined ( $ readingInclude ) || $ event =~ /$readingInclude/ ) && ( ! defined ( $ readingExclude ) || ! ( $ event =~ /$readingExclude/ ) ) ) {
Log3 $ name , 4 , "InfluxDBLogger: [$name] notified from device $devName about $event" ;
InfluxDBLogger_Map ( $ own_hash , $ dev_hash , $ event , \ % map ) ;
}
}
return % map ;
}
sub InfluxDBLogger_BuildData ($$$$)
{
my ( $ own_hash , $ dev_hash , $ map , $ incompatible ) = @ _ ;
my $ name = $ own_hash - > { NAME } ;
my $ data = "" ;
2022-02-07 19:39:46 +00:00
my $ stringValuesAllowed = AttrVal ( $ name , "stringValuesAllowed" , 0 ) ;
2020-12-10 11:33:17 +00:00
my $ rows = 0 ;
my % m = % { $ map } ;
2022-02-07 19:39:46 +00:00
my $ readingTimeStamps = AttrVal ( $ name , "readingTimeStamps" , 0 ) ;
if ( $ readingTimeStamps ) {
foreach my $ device ( keys % m ) {
my $ readings = $ m { $ device } ;
my % r = % { $ readings } ;
foreach my $ reading ( keys % r ) {
my $ value_map = $ r { $ reading } ;
my $ value = $ value_map - > { "value" } ;
my $ numeric = $ value_map - > { "numeric" } ;
if ( ( $ numeric ) || ( $ stringValuesAllowed ) ) {
my ( $ measurementAndTagSet , $ fieldset , $ timestamp ) = InfluxDBLogger_BuildDataDynamic ( $ own_hash , $ dev_hash , $ device , $ reading , $ value , $ numeric ) ;
$ data . = $ measurementAndTagSet . " " . $ fieldset ;
if ( defined ( $ timestamp ) ) {
$ data . = " " . $ timestamp . "000000000" # nanoseconds
}
$ data . = "\n" ;
$ rows + + ;
}
else {
push ( @ { $ incompatible } , $ device . " " . $ reading . " " . $ value ) ;
2020-12-10 11:33:17 +00:00
}
}
}
}
2022-02-07 19:39:46 +00:00
else {
my % measuremnts = ( ) ;
foreach my $ device ( keys % m ) {
my $ readings = $ m { $ device } ;
my % r = % { $ readings } ;
foreach my $ reading ( keys % r ) {
my $ value_map = $ r { $ reading } ;
my $ value = $ value_map - > { "value" } ;
my $ numeric = $ value_map - > { "numeric" } ;
if ( ( $ numeric ) || ( $ stringValuesAllowed ) ) {
my ( $ measurementAndTagSet , $ fieldset , $ timestamp ) = InfluxDBLogger_BuildDataDynamic ( $ own_hash , $ dev_hash , $ device , $ reading , $ value , $ numeric ) ;
if ( defined $ measuremnts { $ measurementAndTagSet } ) {
$ measuremnts { $ measurementAndTagSet } . = "," . $ fieldset ;
} else {
$ measuremnts { $ measurementAndTagSet } = $ fieldset ;
}
$ rows + + ;
}
else {
push ( @ { $ incompatible } , $ device . " " . $ reading . " " . $ value ) ;
}
}
}
foreach my $ measurementAndTagSet ( keys % measuremnts ) {
$ data . = $ measurementAndTagSet . " " . $ measuremnts { $ measurementAndTagSet } . "\n" ;
}
2020-12-10 11:33:17 +00:00
}
return $ data , $ rows ;
}
2022-02-07 19:39:46 +00:00
sub InfluxDBLogger_BuildDataDynamic ($$$$$$)
2020-12-10 11:33:17 +00:00
{
2022-02-07 19:39:46 +00:00
my ( $ hash , $ dev_hash , $ device , $ reading , $ value , $ numeric ) = @ _ ;
2020-12-10 11:33:17 +00:00
my $ name = $ hash - > { NAME } ;
my $ measurement = InfluxDBLogger_GetMeasurement ( $ hash , $ dev_hash , $ device , $ reading , $ value ) ;
my $ tag_set = InfluxDBLogger_GetTagSet ( $ hash , $ dev_hash , $ device , $ reading , $ value ) ;
2022-02-07 19:39:46 +00:00
my $ field_set = InfluxDBLogger_GetFieldSet ( $ hash , $ dev_hash , $ device , $ reading , $ value , $ numeric ) ;
my $ timestamp = InfluxDBLogger_GetTimeStamp ( $ hash , $ dev_hash , $ device , $ reading , $ value ) ;
2020-12-10 11:33:17 +00:00
my $ measurementAndTagSet = defined $ tag_set ? $ measurement . "," . $ tag_set : $ measurement ;
2022-02-07 19:39:46 +00:00
return ( $ measurementAndTagSet , $ field_set , $ timestamp ) ;
2020-12-10 11:33:17 +00:00
}
2021-01-26 21:42:00 +00:00
sub InfluxDBLogger_GetMeasurement ($$$$$)
2020-12-10 11:33:17 +00:00
{
my ( $ hash , $ dev_hash , $ device , $ reading , $ value ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2021-01-18 11:32:18 +00:00
my $ measurement = AttrVal ( $ name , "measurement" , undef ) ;
if ( defined $ measurement ) {
$ measurement =~ s/\{(.*)\}/eval($1)/ei ;
$ measurement =~ s/\$DEVICE/$device/ei ;
$ measurement =~ s/\$READINGNAME/$reading/ei ;
}
else {
$ measurement = $ reading ;
}
2020-12-10 11:33:17 +00:00
return $ measurement ;
}
2021-01-26 21:42:00 +00:00
sub InfluxDBLogger_GetTagSet ($$$$$)
2020-12-10 11:33:17 +00:00
{
my ( $ hash , $ dev_hash , $ device , $ reading , $ value ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my $ tags_set = AttrVal ( $ name , "tags" , undef ) ;
if ( defined $ tags_set ) {
if ( $ tags_set eq "-" ) {
$ tags_set = undef ;
} else {
2021-01-18 11:32:18 +00:00
$ tags_set =~ s/\{(.*)\}/eval($1)/ei ;
$ tags_set =~ s/\$DEVICE/$device/ei ;
2020-12-10 11:33:17 +00:00
}
} else {
$ tags_set = AttrVal ( $ name , "deviceTagName" , "site_name" ) . "=" . $ device ;
}
return $ tags_set ;
}
2022-02-07 19:39:46 +00:00
sub InfluxDBLogger_GetFieldSet ($$$$$$)
2020-12-10 11:33:17 +00:00
{
2022-02-07 19:39:46 +00:00
my ( $ hash , $ dev_hash , $ device , $ reading , $ value , $ numeric ) = @ _ ;
2020-12-10 11:33:17 +00:00
my $ name = $ hash - > { NAME } ;
my $ field_set = AttrVal ( $ name , "fields" , "value=\$READINGVALUE" ) ;
$ field_set =~ s/\$READINGNAME/$reading/ei ;
2022-02-07 19:39:46 +00:00
if ( ! $ numeric ) {
$ value = "\"" . $ value . "\""
}
2020-12-10 11:33:17 +00:00
$ field_set =~ s/\$READINGVALUE/$value/ei ;
return $ field_set ;
}
2022-02-07 19:39:46 +00:00
sub InfluxDBLogger_GetTimeStamp ($$$$$)
{
my ( $ hash , $ dev_hash , $ device , $ reading , $ value ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my $ readingTimeStamps = AttrVal ( $ name , "readingTimeStamps" , 0 ) ;
my $ timeStamp = undef ;
if ( $ readingTimeStamps ) {
my $ readingsTimestamp = ReadingsTimestamp ( $ device , $ reading , undef ) ;
if ( defined ( $ readingsTimestamp ) )
{
my $ readingsTimestampNum = time_str2num ( $ readingsTimestamp ) ;
# ?? $timeStamp= $readingsTimestampNum+-2208992400
$ timeStamp = $ readingsTimestampNum
}
}
return $ timeStamp ;
}
2020-12-10 11:33:17 +00:00
sub InfluxDBLogger_BuildUrl ($)
{
my ( $ hash ) = @ _ ;
2020-12-14 09:14:34 +00:00
my $ name = $ hash - > { NAME } ;
my $ url = $ hash - > { URL } ;
my $ api = AttrVal ( $ name , "api" , "v1" ) ;
if ( $ api eq "v1" ) {
$ url . = "/write?db=" . urlEncode ( $ hash - > { DATABASE } ) ;
} elsif ( $ api eq "v2" ) {
my $ org = AttrVal ( $ name , "org" , "privat" ) ;
my $ bucket = $ hash - > { DATABASE } ;
$ url . = "/api/v2/write?org=" . urlEncode ( $ org ) . "&bucket=" . urlEncode ( $ bucket ) ;
} else {
Log3 $ name , 1 , "InfluxDBLogger: [$name] unsupported api" ;
$ url = undef ;
}
my $ precision = AttrVal ( $ name , "precision" , undef ) ;
if ( defined ( $ url ) && defined ( $ precision ) )
{
$ url . = "&precision=" . $ precision ;
}
return $ url ;
}
2020-12-10 11:33:17 +00:00
sub InfluxDBLogger_Map ($$$$)
{
my ( $ hash , $ dev_hash , $ event , $ map ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my $ deviceName = $ dev_hash - > { NAME } ;
2022-02-07 19:39:46 +00:00
my @ readingAndValue = split ( ":[ \t]*" , $ event , 2 ) ;
2020-12-10 11:33:17 +00:00
my $ readingName = $ readingAndValue [ 0 ] ;
my $ readingValue = $ readingAndValue [ 1 ] ;
my $ conversions = AttrVal ( $ name , "conversions" , undef ) ;
if ( defined ( $ conversions ) ) {
my @ conversions = split ( "," , $ conversions ) ;
foreach ( @ conversions ) {
my @ ab = split ( "=" , $ _ ) ;
$ readingValue =~ s/$ab[0]/eval($ab[1])/ei ;
}
}
$ map - > { $ deviceName } - > { $ readingName } - > { "value" } = $ readingValue ;
2023-02-11 20:33:46 +00:00
$ map - > { $ deviceName } - > { $ readingName } - > { "numeric" } = $ readingValue =~ /^[-+]?[0-9]*[\.\,]?[0-9]+([eE][-+]?[0-9]+)?$/ ;
2020-12-10 11:33:17 +00:00
}
sub InfluxDBLogger_HttpCallback ($$$)
{
my ( $ param , $ err , $ data ) = @ _ ;
my $ hash = $ param - > { hash } ;
my $ name = $ hash - > { NAME } ;
if ( $ err ne "" ) {
InfluxDBLogger_HttpCallback_Error ( $ hash , $ name , $ err ) ;
}
else {
my $ header = $ param - > { httpheader } ;
my $ influx_db_error = undef ;
my $ http_error = undef ;
while ( $ header =~ /X-Influxdb-Error:\s*(.*+)/g ) {
$ influx_db_error = $ 1 ;
}
while ( $ header =~ /HTTP\/1\.0\s*([4|5]\d\d\s*.*+)/g ) {
$ http_error = $ 1 ;
}
if ( defined ( $ influx_db_error ) )
{
InfluxDBLogger_HttpCallback_Error ( $ hash , $ name , $ influx_db_error ) ;
}
elsif ( defined ( $ http_error ) )
{
InfluxDBLogger_HttpCallback_Error ( $ hash , $ name , $ http_error ) ;
}
else
{
my $ succeeded_writes = ReadingsVal ( $ name , $ succeeded_writes_name , 0 ) ;
$ succeeded_writes + + ;
my $ rv = readingsSingleUpdate ( $ hash , $ succeeded_writes_name , $ succeeded_writes , 1 ) ;
Log3 $ name , 4 , "InfluxDBLogger: [$name] HTTP Succeeded " . $ rv ;
}
}
InfluxDBLogger_UpdateState ( $ hash , $ name ) ;
}
2020-12-14 09:14:34 +00:00
sub InfluxDBLogger_BuildState ($$)
{
my ( $ hash , $ name ) = @ _ ;
return "Statistics: t=" . ReadingsVal ( $ name , $ total_writes_name , 0 ) . " s=" . ReadingsVal ( $ name , $ succeeded_writes_name , 0 ) . " f=" . ReadingsVal ( $ name , $ failed_writes_name , 0 ) . " e=" . ReadingsVal ( $ name , $ total_events_name , 0 ) ;
}
2020-12-10 11:33:17 +00:00
sub InfluxDBLogger_UpdateState ($$)
{
my ( $ hash , $ name ) = @ _ ;
2020-12-14 09:14:34 +00:00
my $ new_state = InfluxDBLogger_BuildState ( $ hash , $ name ) ;
2020-12-10 11:33:17 +00:00
readingsSingleUpdate ( $ hash , "state" , $ new_state , 1 ) ;
}
sub InfluxDBLogger_HttpCallback_Error ($$$)
{
my ( $ hash , $ name , $ err ) = @ _ ;
Log3 $ name , 1 , "InfluxDBLogger: [$name] Error = $err" ;
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdate ( $ hash , $ failed_writes_last_error_name , $ err ) ;
my $ failed_writes = ReadingsVal ( $ name , $ failed_writes_name , 0 ) ;
$ failed_writes + + ;
readingsBulkUpdate ( $ hash , $ failed_writes_name , $ failed_writes ) ;
readingsEndUpdate ( $ hash , 1 ) ;
}
sub InfluxDBLogger_DroppedIncompatibleValues ($$@)
{
my ( $ hash , $ name , @ warnings ) = @ _ ;
readingsBeginUpdate ( $ hash ) ;
my $ dropped_writes = ReadingsVal ( $ name , $ dropped_writes_name , 0 ) ;
my $ warn = "" ;
foreach ( @ warnings ) {
$ warn = $ _ ;
Log3 $ name , 4 , "InfluxDBLogger: [$name] Warning, incompatible non numeric value: $warn" ;
}
readingsBulkUpdate ( $ hash , $ droppeed_writes_last_message_name , $ warn ) ;
$ dropped_writes += scalar ( @ warnings ) ;
readingsBulkUpdate ( $ hash , $ dropped_writes_name , $ dropped_writes ) ;
readingsEndUpdate ( $ hash , 1 ) ;
}
2021-01-26 21:42:00 +00:00
sub InfluxDBLogger_Set ($$$@)
2020-12-10 11:33:17 +00:00
{
my ( $ hash , $ name , $ cmd , @ args ) = @ _ ;
Log3 $ name , 5 , "InfluxDBLogger: [$name] set $cmd" ;
if ( lc $ cmd eq 'password' ) {
my $ pwd = $ args [ 0 ] ;
InfluxDBLogger_StoreSecret ( $ hash , $ name , "passwd" , $ pwd ) ;
return ( undef , 1 ) ;
}
elsif ( lc $ cmd eq 'token' ) {
my $ token = $ args [ 0 ] ;
InfluxDBLogger_StoreSecret ( $ hash , $ name , "token" , $ token ) ;
return ( undef , 1 ) ;
}
elsif ( lc $ cmd eq 'resetstatistics' ) {
InfluxDBLogger_ResetStatistics ( $ hash , $ name ) ;
return ( undef , 1 ) ;
}
2021-02-06 20:08:37 +00:00
elsif ( lc $ cmd eq 'write' ) {
InfluxDBLogger_Write ( $ hash , $ name ) ;
return ( undef , 1 ) ;
}
2020-12-10 11:33:17 +00:00
else {
2021-02-06 20:08:37 +00:00
return "Unknown argument $cmd, choose one of resetStatistics:noArg password token write:noArg" ;
2020-12-10 11:33:17 +00:00
}
}
2021-01-26 21:42:00 +00:00
sub InfluxDBLogger_ResetStatistics ($$)
2020-12-10 11:33:17 +00:00
{
my ( $ hash , $ name ) = @ _ ;
readingsBeginUpdate ( $ hash ) ;
readingsBulkUpdate ( $ hash , $ total_writes_name , 0 ) ;
2020-12-14 09:14:34 +00:00
readingsBulkUpdate ( $ hash , $ total_events_name , 0 ) ;
2020-12-10 11:33:17 +00:00
readingsBulkUpdate ( $ hash , $ succeeded_writes_name , 0 ) ;
readingsBulkUpdate ( $ hash , $ failed_writes_name , 0 ) ;
readingsBulkUpdate ( $ hash , $ dropped_writes_name , 0 ) ;
readingsBulkUpdate ( $ hash , $ droppeed_writes_last_message_name , "<none>" ) ;
readingsBulkUpdate ( $ hash , $ failed_writes_last_error_name , "<none>" ) ;
2020-12-14 09:14:34 +00:00
readingsBulkUpdate ( $ hash , "state" , InfluxDBLogger_BuildState ( $ hash , $ name ) ) ;
2020-12-10 11:33:17 +00:00
readingsEndUpdate ( $ hash , 1 ) ;
}
sub InfluxDBLogger_IsBasicAuth ($)
{
my $ name = shift ;
return AttrVal ( $ name , "security" , "" ) eq "basic_auth" ;
}
2021-01-26 21:42:00 +00:00
sub InfluxDBLogger_GetPassword ($$)
2020-12-10 11:33:17 +00:00
{
my $ hash = shift ;
my $ name = shift ;
if ( InfluxDBLogger_IsBasicAuth ( $ name ) )
{
return InfluxDBLogger_ReadSecret ( $ hash , $ name , "passwd" ) ;
}
else
{
return undef ;
}
}
2021-01-26 21:42:00 +00:00
sub InfluxDBLogger_StoreSecret ($$$$) {
2020-12-10 11:33:17 +00:00
my $ hash = shift ;
my $ name = shift ;
my $ ref = shift ;
my $ password = shift ;
my $ index = $ hash - > { TYPE } . "_" . $ name . "_" . $ ref ;
my $ key = getUniqueId ( ) . $ index ;
my $ enc_pwd = "" ;
$ hash - > { $ ref } = undef ;
if ( eval "use Digest::MD5;1" ) {
$ key = Digest::MD5:: md5_hex ( unpack "H*" , $ key ) ;
$ key . = Digest::MD5:: md5_hex ( $ key ) ;
}
for my $ char ( split // , $ password ) {
my $ encode = chop ( $ key ) ;
$ enc_pwd . = sprintf ( "%.2x" , ord ( $ char ) ^ ord ( $ encode ) ) ;
$ key = $ encode . $ key ;
}
Log3 $ name , 5 , "InfluxDBLogger: [$name] storing new $ref" ;
my $ err = setKeyValue ( $ index , $ enc_pwd ) ;
return "error while saving the $ref - $err" if ( defined ( $ err ) ) ;
$ hash - > { $ ref } = "saved" ;
return "$ref successfully saved" ;
}
2021-01-26 21:42:00 +00:00
sub InfluxDBLogger_ReadSecret ($$$) {
2020-12-10 11:33:17 +00:00
my $ hash = shift ;
my $ name = shift ;
my $ ref = shift ;
my $ index = $ hash - > { TYPE } . "_" . $ name . "_" . $ ref ;
my $ key = getUniqueId ( ) . $ index ;
my ( $ password , $ err ) ;
Log3 $ name , 4 , "InfluxDBLogger [$name] - Read $ref from file" ;
( $ err , $ password ) = getKeyValue ( $ index ) ;
if ( defined ( $ err ) ) {
Log3 $ name , 3 , "InfluxDBLogger [$name] - unable to read $ref from file: $err" ;
return undef ;
}
if ( defined ( $ password ) ) {
if ( eval "use Digest::MD5;1" ) {
$ key = Digest::MD5:: md5_hex ( unpack "H*" , $ key ) ;
$ key . = Digest::MD5:: md5_hex ( $ key ) ;
}
my $ dec_pwd = '' ;
for my $ char ( map { pack ( 'C' , hex ( $ _ ) ) } ( $ password =~ /(..)/g ) ) {
my $ decode = chop ( $ key ) ;
$ dec_pwd . = chr ( ord ( $ char ) ^ ord ( $ decode ) ) ;
$ key = $ decode . $ key ;
}
return $ dec_pwd ;
}
else {
Log3 $ name , 3 , "InfluxDBLogger [$name] - No $ref in file" ;
return undef ;
}
return ;
}
sub InfluxDBLogger_Rename ($$) {
my ( $ new_name , $ old_name ) = @ _ ;
my $ hash = $ defs { $ new_name } ;
InfluxDBLogger_StorePassword ( $ hash , $ new_name , InfluxDBLogger_ReadPassword ( $ hash , $ old_name ) ) ;
setKeyValue ( $ hash - > { TYPE } . "_" . $ old_name . "_passwd" , undef ) ;
return ;
}
# Eval-Rückgabewert für erfolgreiches
# Laden des Moduls
1 ;
# Beginn der Commandref
= pod
= item helper
= item summary Logs numeric readings into InfluxDB time - series databases
= item summary_DE Schreibt numerische Readings in eine InfluxDB Zeitreihendatenbank
= begin html
< a name = "InfluxDBLogger" > </a>
<h3> InfluxDBLogger </h3>
<ul>
Module for logging to InfluxDB time - series databases .
According to the <i> InfluxDB terminology </i> , here is the default mapping , which can be changed via attributes:
<ul>
<li> A <i> measurement </i> will be created for each reading name . </li>
<li> A <i> tag </i> called 'site_name' will be used for each device </li>
<li> A <i> field </i> called 'value' will be used for each reading value </li>
</ul>
2020-12-14 09:14:34 +00:00
The default API version is v1 . To change this , set the api - attribute .
Another way to use this module with InfluxDB 2 is to map the buckets to databases . < a href = "https://docs.influxdata.com/influxdb/v2.0/query-data/influxql/#map-unmapped-buckets" > see here </a>
2020-12-10 11:33:17 +00:00
< a name = "InfluxDBLogger_Define" > </a>
<h4> Define </h4>
<ul>
<code> define & lt ; name & gt ; InfluxDBLogger [ http | https ] : // IP_or_Hostname:port dbname devspec </code>
< br /><br / > For details about devspec see < a href = "fhem/docs/commandref.html#devspec" > here </a> . If you use a wildcard devspec , the device will be disabled on default in order to let you specify a readings - regex to reduce the log - amount . < br / >
</ul>
< a name = "InfluxDBLogger_Set" > </a>
<h4> Set </h4>
<ul>
<li> <b> password </b>
<code> set & lt ; name & gt ; password & lt ; password & gt ; </code> < br / >
2021-02-06 20:08:37 +00:00
Securely stores the password for basic authentication . It is only used if security attribute is set to basic_auth .
2020-12-10 11:33:17 +00:00
</li>
<li> <b> token </b>
<code> set & lt ; name & gt ; token & lt ; token & gt ; </code> < br / >
2021-02-06 20:08:37 +00:00
Securely stores the token for token based authentication . It is only used if security attribute is set to token .
2020-12-10 11:33:17 +00:00
</li>
<li> <b> resetStatistics </b>
<code> set & lt ; name & gt ; resetStatistics </code> < br / >
Sets all statistical counters to zero and removes the last error message .
</li>
2021-02-06 20:08:37 +00:00
<li> <b> write </b>
2022-02-07 19:39:46 +00:00
<code> set & lt ; name & gt ; write </code> < br / >
2021-02-06 20:08:37 +00:00
Writes the current values of the configured readings ( readingInclude , readingExclude ) of the configured devices ( devspec ) to the database .
This is useful e . g . for clean start of the day and end of the day values .
Note that the timestamp of the readings are not stored in the database , but the timestamp of the write operation .
</li>
2020-12-10 11:33:17 +00:00
</ul>
< a name = "InfluxDBLogger_Attr" > </a>
<h4> Attributes </h4>
<ul>
<li> <b> disable </b> <code> attr & lt ; name & gt ; disable [ 0 | 1 ] </code> < br / >
If disabled no readings will be written to the database
</li>
<li> <b> security </b> <code> attr & lt ; name & gt ; security [ basic_auth | none | token ] </code> < br / >
If basic_auth is used , you have to define an user attribute and set a password <br>
If token is used , you have to set a token <br>
</li>
<li> <b> username </b> <code> attr & lt ; name & gt ; username & lt ; username & gt ; </code> < br / >
Only used if security attribute is set to basic_auth <br>
</li>
<li> <b> readingInclude </b> <code> attr & lt ; name & gt ; readingInclude & lt ; regex & gt ; </code> < br / >
2021-02-06 20:08:37 +00:00
Only reading events that match the regex will be logged . Note that readings usually have the format : 'state: on' <br> </li>
2020-12-10 11:33:17 +00:00
<li> <b> readingExclude </b> <code> attr & lt ; name & gt ; readingInclude & lt ; regex & gt ; </code> < br / >
2021-02-06 20:08:37 +00:00
Only reading events that do not match the regex will be logged . Note that readings usually have the format : 'state: on' <br> </li>
2020-12-10 11:33:17 +00:00
<li> <b> deviceTagName </b> <code> attr & lt ; name & gt ; deviceTagName & lt ; deviceTagName & gt ; </code> < br / >
This will be the name of the device tag . default is 'site_name'
</li>
<li> <b> conversions </b> <code> attr & lt ; name & gt ; conversions & lt ; conv1 , conv2 & gt ; </code> < br / >
Comma seperated list of replacements e . g . open = 1 , closed = 0 , tilted = 2 or true | on | yes = 1 , false | off | no = 0
2022-02-07 19:39:46 +00:00
Right side can be any perl expression and thus used to replace regular expression groups like this: ( [ 0 - 9 ] + ) % = $ 1
which extract the number out of a percentage value .
2020-12-10 11:33:17 +00:00
</li>
<li> <b> measurement </b> <code> attr & lt ; name & gt ; measurement & lt ; string & gt ; </code> < br / >
Name of the measurement to use . This can be any string .
The keyword $ DEVICE will be replaced by the device - name .
The keyword $ READINGNAME will be replaced by the reading - name
Default is $ READINGNAME .
2021-01-18 11:32:18 +00:00
Perl - Expressions can be used in curly braces . $ name , $ device , $ reading , $ value are available as variables .
attr influx measurement { AttrVal ( $ device , "influx_measurement" , $ reading ) }
2020-12-10 11:33:17 +00:00
</li>
<li> <b> tags </b> <code> attr & lt ; name & gt ; tags & lt ; x , y & gt ; </code> < br / >
This is the list of tags that will be sent to InfluxDB . The keyword $ DEVICE will be replaced by the device - name .
If this attribute is set it will override the attribute deviceTagName . If the attribute is set to "-"
no tags will be written ( useful if measurement is set to $ DEVICE and fields to $ READINGNAME = $ READINGVALUE )
Default is site_name = $ DEVICE .
2021-01-18 11:32:18 +00:00
Perl - Expressions can be used in curly braces to evaluate the alias - attribute as a tag for example . $ name , $ device , $ reading , $ value are available as variables .
2020-12-10 11:33:17 +00:00
attr influx tags device = { AttrVal ( $ device , "alias" , "fallback" ) }
</li>
<li> <b> fields </b> <code> attr & lt ; name & gt ; fields & lt ; val = $ READINGVALUE & gt ; </code> < br / >
This is the list of fields that will be sent to InfluxDB .
The keyword $ READINGNAME will be replaced by the reading - name .
The keyword $ READINGVALUE will be replaced by the reading - value .
Default is value = $ READINGVALUE .
</li>
2020-12-14 09:14:34 +00:00
<li> <b> api </b> <code> attr & lt ; name & gt ; api [ v1 | v2 ] </code> < br / >
The api - Version to use . Default is v1 .
Using API Version v2 the database name is used as the bucket - name .
</li>
<li> <b> org </b> <code> attr & lt ; name & gt ; org & lt ; org & gt ; </code> < br / >
Using API Version v2 the organisation is specified by this . Default is "privat" .
</li>
<li> <b> precision </b> <code> attr & lt ; name & gt ; precision [ ms | s | us | ns ] </code> < br / >
The time precision is specified by this . Default is none .
</li>
2022-02-07 19:39:46 +00:00
<li> <b> stringValuesAllowed </b> <code> attr & lt ; name & gt ; stringValuesAllowed [ 0 | 1 ] </code> < br / >
If enabled ( 1 ) it allows to write strings as value , if disabled ( 0 ) non - numeric values will be blocked ( dropped ) .
Conversions are always processed first . Put conversion to string in double quotes like: 1 = "open"
Note: InfluxDB cannot change the datatype of a field . Changing this attribute after some data is already written might lead to error messages .
Default: 0 ( non - numeric values are blocked )
</li>
<li> <b> readingTimeStamps </b> <code> attr & lt ; name & gt ; readingTimeStamps [ 0 | 1 ] </code> < br / >
If enabled ( 1 ) the ReadingTimestamp from FHEM is send to InfluxDB , Default is off ( 0 ) .
This is useful for devices that publish the values later with the original timestamp delayed .
Default: 0 ( InfluxDB determines the timestamp on its own )
</li>
2020-12-10 11:33:17 +00:00
</ul>
< a name = "InfluxDBLogger_Readings" > </a>
<h4> Readings </h4>
<ul>
<li> <b> total_writes </b> < br / >
Total number of initiated log events . These are the attempts to write , not the completed ones .
</li>
<li> <b> succeeded_writes </b> < br / >
Total number of successfully completed log events . These are the writes that you will find in the database .
</li>
<li> <b> failed_writes </b> < br / >
Total number of failed log events . These are failed due to some error which is captured in the log and in the reading failed_writes_last_error
</li>
<li> <b> failed_writes_last_error </b> < br / >
The last captured error . This is very useful for systematic problems , like wrong DNS or a wrong port , aso .
</li>
<li> <b> dropped_writes </b> < br / >
Total number of dropped writes due to non numeric value . See conversions to fix .
</li>
<li> <b> state </b> < br / >
2020-12-14 09:14:34 +00:00
Statistics: t = total_writes s = succeeded_writes f = failed_writes e = events
2020-12-10 11:33:17 +00:00
</li>
</ul>
</ul>
= end html
= begin html_DE
< a name = "InfluxDBLogger" > </a>
<h3> InfluxDBLogger </h3>
<ul>
2020-12-14 09:14:34 +00:00
Modul zum Loggen in eine InfluxDB Zeitreihendatenbank .
2020-12-10 11:33:17 +00:00
Entsprechend der <i> InfluxDB Terminologie </i> , hier die Standardzuordnung , die mittels Attribute frei verändert werden kann:
<ul>
<li> Ein <i> measurement </i> wird für jeden reading namen erzeugt . </li>
<li> Ein <i> tag </i> namens 'site_name' wird für jedes device verwendet </li>
<li> Ein <i> field </i> namens 'value' wird für die reading Werte genutzt </li>
</ul>
2020-12-14 09:14:34 +00:00
Das Modul arbeitet standardmäßig mit der InfluxDB API v1 , dies kann mit dem Attribut api geändert werden .
Ein weiterer Weg um das Modul mit InfluxDB 2 zu nutzen ist es , die Buckets auf Datenbanken zu mappen . < a href = "https://docs.influxdata.com/influxdb/v2.0/query-data/influxql/#map-unmapped-buckets" > Siehe hier </a>
2020-12-10 11:33:17 +00:00
< a name = "InfluxDBLogger_Define" > </a>
<h4> Define </h4>
<ul>
<code> define & lt ; name & gt ; InfluxDBLogger [ http | https ] : // IP_or_Hostname:port dbname devspec </code>
< br /><br / > Für details zur devspec bitte < a href = "fhem/docs/commandref_DE.html#devspec" > hier </a> informieren . Wenn eine devspec verwendet wird , die auf ALLE Geräe zutrifft , wird der Logger sich initial erstmal selber abschalten ( disable 1 ) . Wenn man eine RegEx für die Readings gesetzt hat , kann disable wieder auf 0 gesetzt werden . < br / >
</ul>
< a name = "InfluxDBLogger_Set" > </a>
<h4> Set </h4>
<ul>
<li> <b> password </b>
<code> set & lt ; name & gt ; password & lt ; password & gt ; </code> < br / >
Speichert das Passwort verschlüsselt für basic authentication . Es wird nur genutzt , wenn das security Attribut auf basic_auth gesetzt wurde .
</li>
<li> <b> token </b>
<code> set & lt ; name & gt ; token & lt ; token & gt ; </code> < br / >
Speichert das Token verschlüsselt für Token Authentification . Es wird nur genutzt , wenn das security Attribut auf token gesetzt wurde .
</li>
<li> <b> resetStatistics </b>
<code> set & lt ; name & gt ; resetStatistics </code> < br / >
Setzt alle statistischen Zähler auf 0 und entfernt die letzte Fehlermeldung
</li>
2021-02-06 20:08:37 +00:00
<li> <b> write </b>
2022-02-07 19:39:46 +00:00
<code> set & lt ; name & gt ; write </code> < br / >
2021-02-06 20:08:37 +00:00
Schreibt die aktuellen Werte der konfigurierten Readings ( readingInclude , readingExclude ) der konfigurierten Geräte ( devspec ) in die Datenbank .
Dies ist zum Beispiel nützlich für saubere Tagesstart und Tagesendwerte .
Hinweis: Der Zeitstempel des Readings wird nicht in der Datenbank gespeichert , sondern der Zeitstempel des Schreibzeitpunktes .
</li>
2020-12-10 11:33:17 +00:00
</ul>
< a name = "InfluxDBLogger_Attr" > </a>
<h4> Attributes </h4>
<ul>
<li> <b> disable </b> <code> attr & lt ; name & gt ; disable [ 0 | 1 ] </code> < br / >
Wenn disable 1 ist , werden keine Ereignisse in die Datenbank geschrieben
</li>
<li> <b> security </b> <code> attr & lt ; name & gt ; security [ basic_auth | none | token ] </code> < br / >
Wenn basic_auth genutzt wird , muss ein Benutzer per Attribut und ein Passwort per set gesetzt werden <br>
Wenn token genutzt wird , muss ein token über set gesetzt werden <br>
</li>
<li> <b> username </b> <code> attr & lt ; name & gt ; username & lt ; username & gt ; </code> < br / >
Wird nur genutzt wenn das security Attribut auf basic_auth gesetzt wurde <br>
</li>
<li> <b> readingInclude </b> <code> attr & lt ; name & gt ; readingInclude & lt ; regex & gt ; </code> < br / >
2021-02-06 20:08:37 +00:00
Nur Ereignisse die zutreffen werden geschrieben . Hinweis - das Format eines Ereignisses sieht so aus: 'state: on' <br> </li>
2020-12-10 11:33:17 +00:00
<li> <b> readingExclude </b> <code> attr & lt ; name & gt ; readingExclude & lt ; regex & gt ; </code> < br / >
2021-02-06 20:08:37 +00:00
Nur Ereignisse die nicht zutreffen werden geschrieben . Hinweis - das Format eines Ereignisses sieht so aus: 'state: on' <br> </li>
2020-12-10 11:33:17 +00:00
<li> <b> deviceTagName </b> <code> attr & lt ; name & gt ; deviceTagName & lt ; deviceTagName & gt ; </code> < br / >
2020-12-14 09:14:34 +00:00
Das ist der Name des tags , in dem der Gerätename gespeichert wird . Standard ist 'site_name'
2020-12-10 11:33:17 +00:00
</li>
<li> <b> conversions </b> <code> attr & lt ; name & gt ; conversions & lt ; conv1 , conv2 & gt ; </code> < br / >
Kommagetrennte Liste von Ersetzungen e . g . open = 1 , closed = 0 , tilted = 2 oder true | on | yes = 1 , false | off | no = 0
2022-02-07 19:39:46 +00:00
Die rechte Seite kann ein Perlausdruck und dadurch auch genutzt werden um Gruppen in regulären Ausdrücken zu nutzen .
z . B . ( [ 0 - 9 ] + ) % = $ 1
Dies extrahiert die Zahlen aus einer Prozentangabe .
2020-12-10 11:33:17 +00:00
</li>
<li> <b> measurement </b> <code> attr & lt ; name & gt ; measurement & lt ; string & gt ; </code> < br / >
Name des zu verwendenen measurements . Dies kann ein freie Zeichenkette sein .
Das Schlüsselwort $ DEVICE wird ersetzt durch den Gerätenamen .
Das Schlüsselwort $ READINGNAME wird ersetzt durch den Readingnamen .
Standard ist $ READINGNAME .
2021-01-18 11:32:18 +00:00
Es können Perl - Ausdrücke in geschweiften Klammern verwendet werden . $ name , $ device , $ reading , $ value stehen dabei als Variable zur Verfügung .
attr influx measurement { AttrVal ( $ device , "influx_measurement" , $ reading ) }
2020-12-10 11:33:17 +00:00
</li>
<li> <b> tags </b> <code> attr & lt ; name & gt ; tags & lt ; x , y & gt ; </code> < br / >
Dies ist the Liste der tags die an InfluxDB mitgesendet werden . Das Schlüsselwort $ DEVICE wird ersetzt durch den Gerätenamen .
Wenn dieses Attribut gesetzt ist wird das Attribut deviceTagName nicht berücksichtigt .
Standard ist site_name = $ DEVICE . Um keine Tags zu schreiben ( insbesondere , weil measurement auf $ DEVICE und
fields auf $ READINGNAME = $ READINGVALUE steht ) bitte ein "-" eintragen .
Es können Perl - Ausdrücke in geschweiften Klammern verwendet werden um z . B . Attribute als tag zu nutzen . $ name , $ device , $ reading , $ value stehen dabei als Variable zur Verfügung .
attr influx tags device = { AttrVal ( $ device , "alias" , "fallback" ) }
</li>
<li> <b> fields </b> <code> attr & lt ; name & gt ; fields & lt ; val = $ READINGVALUE & gt ; </code> < br / >
Dies ist the Liste der fields die an InfluxDB gesendet werden .
2020-12-14 09:14:34 +00:00
Das Schlüsselwort $ READINGNAME wird ersetzt durch den Readingnamen .
Das Schlüsselwort $ READINGVALUE wird ersetzt durch den Readingwert .
Standard ist value = $ READINGVALUE .
</li>
<li> <b> api </b> <code> attr & lt ; name & gt ; api [ v1 | v2 ] </code> < br / >
Die zu verwendende API Version von InfluxDB . Standard ist v1 .
Bei v2 wird als bucket der Datenbankname verwendet .
</li>
<li> <b> org </b> <code> attr & lt ; name & gt ; org & lt ; org & gt ; </code> < br / >
Bei API Version v2 wird hiermit die Organisation angegeben . Standard ist "privat" .
</li>
<li> <b> precision </b> <code> attr & lt ; name & gt ; precision [ ms | s | us | ns ] </code> < br / >
Die Zeit - Präzision wird hiermit angegeben . Standard ist keine Vorgabe .
2020-12-10 11:33:17 +00:00
</li>
2022-02-07 19:39:46 +00:00
<li> <b> stringValuesAllowed </b> <code> attr & lt ; name & gt ; stringValuesAllowed [ 0 | 1 ] </code> < br / >
Erlaubt bei 1 das Senden von Zeichenketten an die Datenbank , oder verhindert es bei 0 aktiv .
Konvertierung werden immer zuerst verarbeitet . Setze Konvertierungen in Zeichenkette in doppele Anfürungszeichen . z . B . 1 = "offen"
Hinweis: Influx kann den Datentyp eines Felder nicht wechseln . Das Ändern dieses Attributes kann somit zu Fehlermeldungen führen .
Standard: 0 ( keine Zeichenketten , nur Zahlen )
</li>
<li> <b> readingTimeStamps </b> <code> attr & lt ; name & gt ; readingTimeStamps [ 0 | 1 ] </code> < br / >
Sendet wenn angeschaltet ( 1 ) den Reading Zeitstempel vom FHEM an die InfluxDB , andernfalls ( 0 ) wird der Zeitstempel von der InfluxDB bestimmt .
Diese Funktion ist nützlich für Geräte die Werte nachmelden mit dem Originalzeitstempel .
Default: 0 ( InfluxDB bestimmt den Zeitstempel , es wird keiner mitgesendet )
</li>
2020-12-10 11:33:17 +00:00
</ul>
< a name = "InfluxDBLogger_Readings" > </a>
<h4> Readings </h4>
<ul>
<li> <b> total_writes </b> < br / >
Anzahl der versuchten Schreibvorgänge . Dies sind nicht die Abgeschlossenen .
</li>
<li> <b> succeeded_writes </b> < br / >
2021-02-06 20:08:37 +00:00
Anzahl der erfolgreich geschriebenen Ereignisse . Dies sind die Ereignisse die man in der Datenbank finden wird .
2020-12-10 11:33:17 +00:00
</li>
<li> <b> failed_writes </b> < br / >
Anzahl der fehlgeschlagenen Ereignisse . Die letzte Fehlermeldung findet man im Reading failed_writes_last_error
</li>
<li> <b> failed_writes_last_error </b> < br / >
Die Fehlermeldung , was recht nützlich ist für systematische Fehler , wie falsche DNS Einträge usw .
</li>
<li> <b> dropped_writes </b> < br / >
2021-02-06 20:08:37 +00:00
Anzahl von nicht getätigten Schreibvorgängen da nicht numerisch . Siehe conversions um es zu beheben .
2020-12-10 11:33:17 +00:00
</li>
<li> <b> state </b> < br / >
2020-12-14 09:14:34 +00:00
Statistics: t = total_writes s = succeeded_writes f = failed_writes e = events
2020-12-10 11:33:17 +00:00
</li>
</ul>
</ul>
= end html_DE
# Ende der Commandref
= cut