Browse Source

## 2022-11-16
- added unit conversion so that on a gas meter (which measures m³) the usage can directly be displayed in kWh (only for MQTT output)
- added basic cost today/yesterday calculation (only for MQTT output)

FloKra 1 year ago
parent
commit
8aacfee272
4 changed files with 196 additions and 27 deletions
  1. 3 0
      S0Meters_py/CHANGELOG.md
  2. 49 8
      S0Meters_py/README.md
  3. 126 18
      S0Meters_py/s0meters.py
  4. 18 1
      S0Meters_py/s0meters.yml

+ 3 - 0
S0Meters_py/CHANGELOG.md

@@ -0,0 +1,3 @@
+## 2022-11-16
+ - added unit conversion so that on a gas meter (which measures m³) the usage can directly be displayed in kWh (only for MQTT output)
+ - added basic cost today/yesterday calculation (only for MQTT output)

+ 49 - 8
S0Meters_py/README.md

@@ -154,17 +154,34 @@ In this YAML styled config file, all meters/counters are declared. The main iden
   unit: "m3"
   digits: 2
   type: "usage"
+  conv_unit: "kWh"
+  conv_factor: 10.73184
+  conv_digits: 2
+  cost_unit: "EUR"
+  cost_per_unit: 0.33
+  cost_from_conv: True
   momType: "momUsage"
   momUnit: "m3/h"
   momDigits: 2
   momFactor: 1
+  momType_conv1: "momUsage__kW"
+  momUnit_conv1: "kW"
+  momDigits_conv1: 2
+  momFactor_conv1: 10.73184
+  momType_conv2: "momUsage__l_min"
+  momUnit_conv2: "l/min"
+  momDigits_conv2: 1
+  momFactor_conv2: 16.6667
   statTopic: "Gasmeter"
   influxInstance_energy: "energymeters"
   influxInstance_mom: "energy_momentary"
   influxMeasurement_energy: "gas"
   influxMeasurement_mom: "gas"
   influxFieldName_energy: "Gas_Total__m3"
+  ##influxFieldName_energy_conv: "Gas_Total__kWh"
   influxFieldName_mom: "Gas_Usage__m3_h"
+  #influxFieldName_mom_conv1: "Gas_Usage__kW"
+  #influxFieldName_mom_conv2: "Gas_Usage__l_min"
   influxMinWriteInterval_energy: 300
 ```
 
@@ -183,19 +200,40 @@ In this YAML styled config file, all meters/counters are declared. The main iden
 - **momDigits**: integer, number of digits in output of momentary value
 
 - **momFactor**: factor for calculation of momentary value
-
-  Formula for calculation: momValue = (3600000 / dTime / impPerUnit) * momFactor
-  where dTime = time between impulses in milliseconds
-
-  Normally 1, except if the unit of momentary value should be different - for example:  
-
-  - power meter, 1000 imp per unit = 1 kWh: 
+    Formula for calculation: momValue = (3600000 / dTime / impPerUnit) * momFactor
+    where dTime = time between impulses in milliseconds. 
+    Normally 1, except if the unit of momentary value should be different - for example:  
+    - power meter, 1000 imp per unit = 1 kWh: 
     momFactor=1000 -> output in Watts
     momFactor=1 -> output in kW
-  - gas meter, 100 imp per unit = 1m³:
+    - gas meter, 100 imp per unit = 1m³:
     momFactor=1 --> output in m³/h
     momFactor=1000 --> output in dm³/h (l/h)
 
+- **momType_conv1**: type of converted momentary reading. used as MQTT sub-topic
+
+- **momUnit_conv1**: free text, currently only used in MQTT JSON output
+
+- **momDigits_conv1**: integer, number of digits in output of converted momentary value
+
+- **momFactor_conv1**: factor for calculation of converted momentary value
+    Formula for calculation: momValue_conv1 = momValue * momFactor_conv1 (where momValue is the origin unconverted value)
+    For example: 
+    - gas meter, convert m³/h to kW: factor of ~10
+    - gas meter, convert m³/h to l/min: factor of 16.6667
+
+- **momType_conv2**: type of 2nd converted momentary reading. used as MQTT sub-topic
+
+- **momUnit_conv2**: free text, currently only used in MQTT JSON output
+
+- **momDigits_conv2**: integer, number of digits in output of 2nd converted momentary value
+
+- **momFactor_conv2**: factor for calculation of 2nd converted momentary value
+    Formula for calculation: momValue_conv2 = momValue * momFactor_conv2 (where momValue is the origin unconverted value)
+    For example: 
+    - gas meter, convert m³/h to kW: factor of ~10
+    - gas meter, convert m³/h to l/min: factor of 16.6667
+
 - **statTopic**: MQTT topic prefix to publish all values of this meter
   i.E. if statTopic = "Powermeter", following topics will be published to: 
   - Powermeter/reading = current counter reading (total)
@@ -208,7 +246,10 @@ In this YAML styled config file, all meters/counters are declared. The main iden
 - **influxMeasurement_energy**: InfluxDB measurement name for energy (total counter value)
 - **influxMeasurement_mom**: InfluxDB measurement name for momentary values
 - **influxFieldName_energy**: InfluxDB field name for energy (total counter value)
+- **influxFieldName_energy_conv**: InfluxDB field name for converted energy (total counter value) - **CURRENTLY NOT IMPLEMENTED**
 - **influxFieldName_mom**: InfluxDB measurement name for momentary value
+- **influxFieldName_mom_conv1**: InfluxDB measurement name for converted momentary value
+- **influxFieldName_mom_conv2**: InfluxDB measurement name for 2nd converted momentary value
 - **influxMinWriteInterval**_energy: minimal InfluxDB write interval for energy readings
   If no interval is configured, EVERY impulse received will be written to InfluxDB!
   There is no such option for the momentary value, as there a high resolution is desired in the logs. 

+ 126 - 18
S0Meters_py/s0meters.py

@@ -154,26 +154,59 @@ def processMeterData(data):
         write_energy_to_influxdb = False
         
         cName = meters_yaml[cNum].get('name', False)
-        statTopic = meters_yaml[cNum].get('statTopic', False)
-        unit = meters_yaml[cNum].get('unit', False)
+        statTopic = meters_yaml[cNum].get('statTopic', None)
+        unit = meters_yaml[cNum].get('unit', None)
         
-        momUnit = meters_yaml[cNum].get('momUnit', False)
-        momType = meters_yaml[cNum].get('momType', False)
         
-        impPerUnit = meters_yaml[cNum].get('impPerUnit', False)
+        conv_unit = meters_yaml[cNum].get('conv_unit', None)
+        conv_factor = meters_yaml[cNum].get('conv_factor', None)
+        conv_digits = meters_yaml[cNum].get('conv_digits', None)
+        if conv_digits is None: conv_digits = 2
+        
+        cost_unit = meters_yaml[cNum].get('cost_unit', None)
+        cost_per_unit = meters_yaml[cNum].get('cost_per_unit', None)
+        cost_from_conv = meters_yaml[cNum].get('cost_from_conv', False)
+        
+        
+        momUnit = meters_yaml[cNum].get('momUnit', None)
+        momType = meters_yaml[cNum].get('momType', None)
+        
+        momUnit_conv1 = meters_yaml[cNum].get('momUnit_conv1', None)
+        momType_conv1 = meters_yaml[cNum].get('momType_conv1', None)
+        
+        momUnit_conv2 = meters_yaml[cNum].get('momUnit_conv2', None)
+        momType_conv2 = meters_yaml[cNum].get('momType_conv2', None)
+        
+        impPerUnit = meters_yaml[cNum].get('impPerUnit', None)
         
         if cName:
             cJson['name'] = cName
         
-        momFactor = meters_yaml[cNum].get('momFactor', False)
+        momFactor = meters_yaml[cNum].get('momFactor', None)
         if not momFactor:
             momFactor = 1
         
         momDigits = meters_yaml[cNum].get('momDigits', None)
         if momDigits == None:
             momDigits = 3
+            
+        momFactor_conv1 = meters_yaml[cNum].get('momFactor_conv1', None)
+        if not momFactor_conv1:
+            momFactor_conv1 = 1
         
-        digits = meters_yaml[cNum].get('digits', False)
+        momDigits_conv1 = meters_yaml[cNum].get('momDigits_conv1', None)
+        if momDigits_conv1 == None:
+            momDigits_conv1 = 3
+            
+        momFactor_conv2 = meters_yaml[cNum].get('momFactor_conv2', None)
+        if not momFactor_conv2:
+            momFactor_conv2 = 1    
+        
+        momDigits_conv2 = meters_yaml[cNum].get('momDigits_conv2', None)
+        if momDigits_conv2 == None:
+            momDigits_conv2 = 3
+        
+        digits = meters_yaml[cNum].get('digits', None)
         
         influxMinWriteInterval = meters_yaml[cNum].get('influxMinWriteInterval_energy', None)
         if influxMinWriteInterval == None: influxMinWriteInterval = 0
@@ -211,23 +244,60 @@ def processMeterData(data):
                 momValue = 0.0
             else:
                 momValue = (3600000 / dTime / impPerUnit) * momFactor
+                
+            # conversions of momValue
+            momValue_conv1 = 0.0
+            momValue_conv2 = 0.0
             
+            if momType_conv1 is not None:
+                momValue_conv1 = momValue * momFactor_conv1
+                
+            if momType_conv2 is not None:
+                momValue_conv2 = momValue * momFactor_conv2
+            
+            # round value of momValue
             if momDigits > 0:
                 momValue = round(momValue, momDigits)
             else:
                 momValue = round(momValue)
                 
-            cJson['momValue'] = momValue
+            if momDigits_conv1 > 0:
+                momValue_conv1 = round(momValue_conv1, momDigits_conv1)
+            else:
+                momValue_conv1 = round(momValue_conv1)
+                
+            if momDigits_conv2 > 0:
+                momValue_conv2 = round(momValue_conv2, momDigits_conv2)
+            else:
+                momValue_conv2 = round(momValue_conv2)
+                
+                
+            #cJson['momValue'] = momValue
+            #if momType:
+            #    cJson['momType'] = momType
+            #if momUnit:
+            #    cJson['momUnit'] = momUnit
+            
             if momType:
-                cJson['momType'] = momType
-            if momUnit:
-                cJson['momUnit'] = momUnit
+                cJson[momType] = momValue
+            
+            if momType_conv1:
+                cJson[momType_conv1] = momValue_conv1
+            
+            if momType_conv2:
+                cJson[momType_conv2] = momValue_conv2
             
             if statTopic:
-                if momType and momValue is not None:
+                if momType is not None:
                     if MQTTenabled: 
-                        subtop = momType
-                        mqttc.publish(statTopic + "/" + subtop, str(momValue), qos=0, retain=False)
+                        mqttc.publish(statTopic + "/" + momType, str(momValue), qos=0, retain=False)
+                        
+                        if momType_conv1 is not None:
+                            mqttc.publish(statTopic + "/" + momType_conv1, str(momValue_conv1), qos=0, retain=False)
+                        
+                        if momType_conv2 is not None:
+                            mqttc.publish(statTopic + "/" + momType_conv2, str(momValue_conv2), qos=0, retain=False)
+                        
             
         if statTopic:
             if cReading_formatted != None:
@@ -236,7 +306,22 @@ def processMeterData(data):
         
         
         data_energy[cNum][meters_yaml[cNum].get('influxFieldName_energy', 'energyTotal')] = round(float(cReading), digits)
+        #if conv_unit is not None and conv_factor is not None and meters_yaml[cNum].get('influxFieldName_energy_conv', None) is not None::
+        #    data_energy[cNum][meters_yaml[cNum].get('influxFieldName_energy_conv', 'energyTotal_conv')] = round(momValue_conv1, digits)
+        
         data_momentary[cNum][meters_yaml[cNum].get('influxFieldName_mom', 'momentaryUsage')] = round(float(momValue), momDigits)
+        if momType_conv2 is not None and meters_yaml[cNum].get('influxFieldName_mom_conv1', None) is not None:
+            data_momentary[cNum][meters_yaml[cNum].get('influxFieldName_mom_conv1', 'momentaryUsage_conv1')] = round(momValue_conv1, momDigits_conv1)
+            
+        if momType_conv2 is not None and meters_yaml[cNum].get('influxFieldName_mom_conv2', None) is not None:
+            data_momentary[cNum][meters_yaml[cNum].get('influxFieldName_mom_conv2', 'momentaryUsage_conv2')] = round(momValue_conv2, momDigits_conv2)
+        
+        print()
+        print("data_energy[cNum]")
+        print(data_energy[cNum])
+        print("data_momentary[cNum]")
+        print(data_momentary[cNum])
+        print()
         
         # InfluxDB
         t_utc = datetime.datetime.utcnow()
@@ -426,14 +511,37 @@ def processMeterData(data):
                 print( "<p>Error in file log: %s</p>" % e )
             
             if energy_today_total is not None:
-                cJson['Today'] = round(energy_today_total, digits)
+                cJson['Today__' + unit] = round(energy_today_total, digits)
+                
                 if MQTTenabled: 
-                    mqttc.publish(statTopic + "/today", str(round(energy_today_total, digits)), qos=0, retain=False)
+                    mqttc.publish(statTopic + "/today__" + unit, str(round(energy_today_total, digits)), qos=0, retain=False)
+                    if conv_unit and conv_factor is not None:
+                        conv_value = energy_today_total * conv_factor
+                        mqttc.publish(statTopic + "/today__" + conv_unit, str(round(conv_value, conv_digits)), qos=0, retain=False)
+                        cJson['Today__' + conv_unit] = round(conv_value, conv_digits)
+                        if cost_unit and cost_per_unit is not None:
+                            if cost_from_conv:
+                                cost_value = round(conv_value * cost_per_unit, 2)
+                            else:
+                                cost_value = round(energy_today_total * cost_per_unit, 2)                                
+                            mqttc.publish(statTopic + "/cost_today__" + cost_unit, str(cost_value), qos=0, retain=False)
+                            cJson['cost_today__' + cost_unit] = round(cost_value, 2)
                 
             if energy_yesterday_total is not None:
-                cJson['Yesterday'] = round(energy_yesterday_total, digits)
+                cJson['Yesterday__' + unit] = round(energy_yesterday_total, digits)
                 if MQTTenabled: 
-                    mqttc.publish(statTopic + "/yesterday", str(round(energy_yesterday_total, digits)), qos=0, retain=False)
+                    mqttc.publish(statTopic + "/yesterday__" + unit, str(round(energy_yesterday_total, digits)), qos=0, retain=False)
+                    if conv_unit and conv_factor is not None:
+                        conv_value = energy_yesterday_total * conv_factor
+                        mqttc.publish(statTopic + "/yesterday__" + conv_unit, str(round(conv_value, conv_digits)), qos=0, retain=False)
+                        cJson['Yesterday__' + conv_unit] = round(conv_value, conv_digits)
+                        if cost_unit and cost_per_unit is not None:
+                            if cost_from_conv:
+                                cost_value = round(conv_value * cost_per_unit, 2)
+                            else:
+                                cost_value = round(energy_yesterday_total * cost_per_unit, 2)                                
+                            mqttc.publish(statTopic + "/cost_yesterday__" + cost_unit, str(cost_value), qos=0, retain=False)
+                            cJson['cost_yesterday__' + cost_unit] = round(cost_value, 2)
         # END file log 
         
         if verbose:

+ 18 - 1
S0Meters_py/s0meters.yml

@@ -24,15 +24,32 @@
   unit: "m3"
   digits: 2
   type: "usage"
-  momType: "momUsage"
+  conv_unit: "kWh"
+  conv_factor: 10.73184
+  conv_digits: 2
+  cost_unit: "EUR"
+  cost_per_unit: 0.33
+  cost_from_conv: True
+  momType: "momUsage__m3_h"
   momUnit: "m3/h"
   momDigits: 2
   momFactor: 1
+  momType_conv1: "momUsage__kW"
+  momUnit_conv1: "kW"
+  momDigits_conv1: 2
+  momFactor_conv1: 10.73184
+  momType_conv2: "momUsage__l_min"
+  momUnit_conv2: "l/min"
+  momDigits_conv2: 1
+  momFactor_conv2: 16.6667
   statTopic: "Gasmeter"
   influxInstance_energy: "energymeters"
   influxInstance_mom: "energy_momentary"
   influxMeasurement_energy: "gas"
   influxMeasurement_mom: "gas"
   influxFieldName_energy: "Gas_Total__m3"
+  ##influxFieldName_energy_conv: "Gas_Total__kWh"
   influxFieldName_mom: "Gas_Usage__m3_h"
+  #influxFieldName_mom_conv1: "Gas_Usage__kW"
+  #influxFieldName_mom_conv2: "Gas_Usage__l_min"
   influxMinWriteInterval_energy: 300