22from configparser import SectionProxy
33from typing import TextIO
44import time
5+ import logging
56
67from defs .common import strtobool
78
@@ -21,6 +22,7 @@ class influxdb_out(transport_base):
2122 include_device_info : bool = True
2223 batch_size : int = 100
2324 batch_timeout : float = 10.0
25+ force_float : bool = True # Force all numeric fields to be floats to avoid InfluxDB type conflicts
2426
2527 client = None
2628 batch_points = []
@@ -37,6 +39,7 @@ def __init__(self, settings: SectionProxy):
3739 self .include_device_info = strtobool (settings .get ("include_device_info" , fallback = self .include_device_info ))
3840 self .batch_size = settings .getint ("batch_size" , fallback = self .batch_size )
3941 self .batch_timeout = settings .getfloat ("batch_timeout" , fallback = self .batch_timeout )
42+ self .force_float = strtobool (settings .get ("force_float" , fallback = self .force_float ))
4043
4144 self .write_enabled = True # InfluxDB output is always write-enabled
4245 super ().__init__ (settings )
@@ -103,14 +106,17 @@ def write_data(self, data: dict[str, str], from_transport: transport_base):
103106 for key , value in data .items ():
104107 # Check if we should force float formatting based on protocol settings
105108 should_force_float = False
109+ unit_mod_found = None
106110
107111 # Try to get registry entry from protocol settings to check unit_mod
108112 if hasattr (from_transport , 'protocolSettings' ) and from_transport .protocolSettings :
109113 # Check both input and holding registries
110114 for registry_type in [Registry_Type .INPUT , Registry_Type .HOLDING ]:
111115 registry_map = from_transport .protocolSettings .get_registry_map (registry_type )
112116 for entry in registry_map :
113- if entry .variable_name == key :
117+ # Match by variable_name (which is lowercase)
118+ if entry .variable_name .lower () == key .lower ():
119+ unit_mod_found = entry .unit_mod
114120 # If unit_mod is not 1.0, this value should be treated as float
115121 if entry .unit_mod != 1.0 :
116122 should_force_float = True
@@ -124,14 +130,28 @@ def write_data(self, data: dict[str, str], from_transport: transport_base):
124130 # Try to convert to float first
125131 float_val = float (value )
126132
127- # If it's an integer but should be forced to float, or if it's already a float
128- if should_force_float or not float_val .is_integer ():
133+ # Always use float for InfluxDB to avoid type conflicts
134+ # InfluxDB is strict about field types - once a field is created as integer,
135+ # it must always be integer. Using float avoids this issue.
136+ if self .force_float :
129137 fields [key ] = float_val
130138 else :
131- fields [key ] = int (float_val )
139+ # Only use integer if it's actually an integer and we're not forcing floats
140+ if float_val .is_integer ():
141+ fields [key ] = int (float_val )
142+ else :
143+ fields [key ] = float_val
144+
145+ # Log data type conversion for debugging
146+ if self ._log .isEnabledFor (logging .DEBUG ):
147+ original_type = type (value ).__name__
148+ final_type = type (fields [key ]).__name__
149+ self ._log .debug (f"Field { key } : { value } ({ original_type } ) -> { fields [key ]} ({ final_type } ) [unit_mod: { unit_mod_found } ]" )
150+
132151 except (ValueError , TypeError ):
133152 # If conversion fails, store as string
134153 fields [key ] = str (value )
154+ self ._log .debug (f"Field { key } : { value } -> string (conversion failed)" )
135155
136156 # Create InfluxDB point
137157 point = {
0 commit comments