Tasmota: add SML-IR reader phase readings (#26920)
This commit is contained in:
parent
7b8256f93a
commit
5a8607fbba
7 changed files with 210 additions and 67 deletions
|
|
@ -65,13 +65,19 @@ func NewTasmota(embed embed, uri, user, password, usage string, channels []int,
|
|||
|
||||
c.switchSocket = NewSwitchSocket(&embed, c.Enabled, c.conn.CurrentPower, standbypower)
|
||||
|
||||
var currents, voltages func() (float64, float64, float64, error)
|
||||
if len(channels) == 3 {
|
||||
currents = c.currents
|
||||
voltages = c.voltages
|
||||
// check if phase specific readings are supported by the device, if not return the base meter implementation without decorators
|
||||
var hasPhases bool
|
||||
if len(channels) == 1 {
|
||||
if l1, l2, l3, err := c.conn.Voltages(); err == nil && l1*l2*l3 > 0 {
|
||||
hasPhases = true
|
||||
}
|
||||
}
|
||||
|
||||
return decorateTasmota(c, currents, voltages), nil
|
||||
if hasPhases || len(channels) == 3 {
|
||||
return decorateTasmota(c, c.voltages, c.currents), nil
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Enabled implements the api.Charger interface
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ func init() {
|
|||
registry.Add("tasmota", NewTasmotaFromConfig)
|
||||
}
|
||||
|
||||
//go:generate go tool decorate -f decorateTasmota -b *Tasmota -r api.Meter -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)"
|
||||
//go:generate go tool decorate -f decorateTasmota -b *Tasmota -r api.Meter -t "api.PhaseVoltages,Voltages,func() (float64, float64, float64, error)" -t "api.PhaseCurrents,Currents,func() (float64, float64, float64, error)" -t "api.PhasePowers,Powers,func() (float64, float64, float64, error)"
|
||||
|
||||
// NewTasmotaFromConfig creates a Tasmota meter from generic config
|
||||
func NewTasmotaFromConfig(other map[string]any) (api.Meter, error) {
|
||||
|
|
@ -56,13 +56,19 @@ func NewTasmota(uri, user, password, usage string, channels []int, cache time.Du
|
|||
usage: usage,
|
||||
}
|
||||
|
||||
var currents, voltages func() (float64, float64, float64, error)
|
||||
if usage != "grid" && len(channels) == 3 {
|
||||
currents = c.currents
|
||||
voltages = c.voltages
|
||||
// check for SML readings
|
||||
var hasPhases bool
|
||||
if len(channels) == 1 {
|
||||
if l1, l2, l3, err := c.conn.Voltages(); err == nil && l1*l2*l3 > 0 {
|
||||
hasPhases = true
|
||||
}
|
||||
}
|
||||
|
||||
return decorateTasmota(c, voltages, currents), nil
|
||||
if hasPhases || len(channels) == 3 {
|
||||
return decorateTasmota(c, c.voltages, c.currents, c.powers), nil
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
var _ api.Meter = (*Tasmota)(nil)
|
||||
|
|
@ -87,12 +93,17 @@ func (c *Tasmota) TotalEnergy() (float64, error) {
|
|||
return c.conn.TotalEnergy()
|
||||
}
|
||||
|
||||
// currents implements the api.PhaseCurrents interface
|
||||
func (c *Tasmota) currents() (float64, float64, float64, error) {
|
||||
return c.conn.Currents()
|
||||
// powers implements the api.PhasePowers interface
|
||||
func (c *Tasmota) powers() (float64, float64, float64, error) {
|
||||
return c.conn.Powers()
|
||||
}
|
||||
|
||||
// voltages implements the api.PhaseVoltages interface
|
||||
func (c *Tasmota) voltages() (float64, float64, float64, error) {
|
||||
return c.conn.Voltages()
|
||||
}
|
||||
|
||||
// currents implements the api.PhaseCurrents interface
|
||||
func (c *Tasmota) currents() (float64, float64, float64, error) {
|
||||
return c.conn.Currents()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ func NewConnection(uri, user, password string, channels []int, cache time.Durati
|
|||
return nil, errors.New("missing uri")
|
||||
}
|
||||
|
||||
if l := len(channels); l != 1 && l != 3 {
|
||||
return nil, fmt.Errorf("invalid number of channels: %d", l)
|
||||
}
|
||||
|
||||
used := make(map[int]bool)
|
||||
for _, c := range channels {
|
||||
if c < 1 || c > 8 {
|
||||
|
|
@ -200,49 +204,91 @@ func (c *Connection) CurrentPower() (float64, error) {
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// SML power available
|
||||
if sml := s.StatusSNS.SML.PowerCurr; sml != nil {
|
||||
return *sml, nil
|
||||
}
|
||||
|
||||
var res float64
|
||||
for _, channel := range c.channels {
|
||||
power, err := s.StatusSNS.Energy.Power.Channel(channel)
|
||||
power, err := s.StatusSNS.Energy.Power.Value(channel)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
res += power
|
||||
}
|
||||
return res + float64(s.StatusSNS.SML.PowerCurr), nil
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// TotalEnergy implements the api.MeterEnergy interface
|
||||
func (c *Connection) TotalEnergy() (float64, error) {
|
||||
res, err := c.statusSnsG.Get()
|
||||
return res.StatusSNS.Energy.Total + res.StatusSNS.SML.TotalIn, err
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// SML total energy available
|
||||
if sml := res.StatusSNS.SML.TotalIn; sml != nil {
|
||||
return *sml, err
|
||||
}
|
||||
|
||||
return res.StatusSNS.Energy.Total, err
|
||||
}
|
||||
|
||||
// Currents implements the api.PhaseCurrents interface
|
||||
func (c *Connection) Currents() (float64, float64, float64, error) {
|
||||
return c.getPhaseValues(func(s StatusSNSResponse) Channels {
|
||||
return s.StatusSNS.Energy.Current
|
||||
})
|
||||
}
|
||||
|
||||
// Voltages implements the api.PhaseVoltages interface
|
||||
func (c *Connection) Voltages() (float64, float64, float64, error) {
|
||||
return c.getPhaseValues(func(s StatusSNSResponse) Channels {
|
||||
return s.StatusSNS.Energy.Voltage
|
||||
})
|
||||
}
|
||||
|
||||
// getPhaseValues returns 3 sequential phase values
|
||||
func (c *Connection) getPhaseValues(fun func(StatusSNSResponse) Channels) (float64, float64, float64, error) {
|
||||
// Powers implements the api.PhasePowers interface
|
||||
func (c *Connection) Powers() (float64, float64, float64, error) {
|
||||
s, err := c.statusSnsG.Get()
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
all := fun(s)
|
||||
// SML powers available
|
||||
if sml := s.StatusSNS.SML; sml.PowerL1 != nil && sml.PowerL2 != nil && sml.PowerL3 != nil {
|
||||
return *sml.PowerL1, *sml.PowerL2, *sml.PowerL3, nil
|
||||
}
|
||||
|
||||
return c.getPhaseValues(s.StatusSNS.Energy.Power)
|
||||
}
|
||||
|
||||
// Voltages implements the api.PhaseVoltages interface
|
||||
func (c *Connection) Voltages() (float64, float64, float64, error) {
|
||||
s, err := c.statusSnsG.Get()
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
// SML voltages available
|
||||
if sml := s.StatusSNS.SML; sml.VoltageL1 != nil && sml.VoltageL2 != nil && sml.VoltageL3 != nil {
|
||||
return *sml.VoltageL1, *sml.VoltageL2, *sml.VoltageL3, nil
|
||||
}
|
||||
|
||||
return c.getPhaseValues(s.StatusSNS.Energy.Voltage)
|
||||
}
|
||||
|
||||
// Currents implements the api.PhaseCurrents interface
|
||||
func (c *Connection) Currents() (float64, float64, float64, error) {
|
||||
s, err := c.statusSnsG.Get()
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
// SML currents available
|
||||
if sml := s.StatusSNS.SML; sml.CurrentL1 != nil && sml.CurrentL2 != nil && sml.CurrentL3 != nil {
|
||||
return *sml.CurrentL1, *sml.CurrentL2, *sml.CurrentL3, nil
|
||||
}
|
||||
|
||||
return c.getPhaseValues(s.StatusSNS.Energy.Current)
|
||||
}
|
||||
|
||||
// getPhaseValues returns 3 sequential phase values
|
||||
func (c *Connection) getPhaseValues(all Channels) (float64, float64, float64, error) {
|
||||
var res [3]float64
|
||||
for i := range res {
|
||||
res[i], err = all.Channel(c.channels[i])
|
||||
|
||||
for i, cc := range c.channels {
|
||||
var err error
|
||||
res[i], err = all.Value(cc)
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,9 +85,18 @@ type StatusSNSResponse struct {
|
|||
|
||||
// SML sensor readings
|
||||
SML struct {
|
||||
TotalIn float64 `json:"total_in"`
|
||||
TotalOut float64 `json:"total_out"`
|
||||
PowerCurr int `json:"power_curr"`
|
||||
TotalIn *float64 `json:"total_in"`
|
||||
TotalOut *float64 `json:"total_out"`
|
||||
PowerCurr *float64 `json:"power_curr"`
|
||||
PowerL1 *float64 `json:"power_l1"`
|
||||
PowerL2 *float64 `json:"power_l2"`
|
||||
PowerL3 *float64 `json:"power_l3"`
|
||||
VoltageL1 *float64 `json:"voltage_l1"`
|
||||
VoltageL2 *float64 `json:"voltage_l2"`
|
||||
VoltageL3 *float64 `json:"voltage_l3"`
|
||||
CurrentL1 *float64 `json:"current_l1"`
|
||||
CurrentL2 *float64 `json:"current_l2"`
|
||||
CurrentL3 *float64 `json:"current_l3"`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -95,7 +104,7 @@ type StatusSNSResponse struct {
|
|||
// Channels is a Tasmota specific helper type to handle meter value lists and single meter values
|
||||
type Channels []float64
|
||||
|
||||
func (ch *Channels) Channel(channel int) (float64, error) {
|
||||
func (ch *Channels) Value(channel int) (float64, error) {
|
||||
if *ch == nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ package tasmota
|
|||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Test StatusSNS response of all known Tasmota flavours
|
||||
|
|
@ -11,28 +14,34 @@ func TestUnmarshalStatusSNSResponse(t *testing.T) {
|
|||
|
||||
// Test cases for #6082
|
||||
jsonstr := `{"StatusSNS":{"Time":"2023-02-05T20:31:48","ENERGY":{"TotalStartTime":"2023-02-05T11:04:13","Total":1290.3960,"Yesterday":0.8540,"Today":0.1730,"Power":47.11,"ApparentPower":0.0,"ReactivePower":0.0,"Factor":0.00,"Voltage":0.00,"Current":0.000}}}`
|
||||
if err := json.Unmarshal([]byte(jsonstr), &res); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if power, err := res.StatusSNS.Energy.Power.Channel(1); err == nil && power != 47.11 {
|
||||
t.Error("StatusSNS.Energy.Power.Channel(1) != 47.11")
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
if power, err := res.StatusSNS.Energy.Power.Value(1); err == nil && power != 47.11 {
|
||||
t.Error("StatusSNS.Energy.Power.Value(1) != 47.11")
|
||||
}
|
||||
|
||||
// Test case for #5731
|
||||
jsonstr = `{"StatusSNS":{"Time":"2023-01-09T18:57:39","Switch1":"ON","Switch2":"OFF","ANALOG":{"Temperature":49.6},"ENERGY":{"TotalStartTime":"2023-01-09T13:59:15","Total":0.077,"Yesterday":0.000,"Today":0.077,"Power":[8.15,0],"ApparentPower":[5,0],"ReactivePower":[4,0],"Factor":[0.00,0.00],"Frequency":50,"Voltage":237,"Current":[0.000,0.000]},"TempUnit":"C"}}`
|
||||
if err := json.Unmarshal([]byte(jsonstr), &res); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if power, err := res.StatusSNS.Energy.Power.Channel(1); err == nil && power != 8.15 {
|
||||
t.Error("StatusSNS.Energy.Power.Channel(1) != 8.15")
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
if power, err := res.StatusSNS.Energy.Power.Value(1); err == nil && power != 8.15 {
|
||||
t.Error("StatusSNS.Energy.Power.Value(1) != 8.15")
|
||||
}
|
||||
|
||||
// Test case for #3787
|
||||
jsonstr = `{"StatusSNS":{"Time":"2022-07-07T13:01:11","HTU21":{"Temperature":25.2,"Humidity":45.5},"SML":{"Total_in":34507.4761,"Total_out":14737.1422,"Power_curr":-894,"Meter_number":"0901454d48000000000"},"Gas":{"Count":1.84},"TempUnit":"C"}}`
|
||||
if err := json.Unmarshal([]byte(jsonstr), &res); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if res.StatusSNS.SML.PowerCurr != -894 {
|
||||
t.Error("res.StatusSNS.SML.PowerCurr != -894")
|
||||
}
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
assert.Equal(t, float64(-894), *res.StatusSNS.SML.PowerCurr)
|
||||
|
||||
// Test case for #26857
|
||||
jsonstr = `{"StatusSNS":{"Time":"2026-01-21T11:07:31","SML":{"Total_in":2687.3687,"Total_out":582.4569,"Power_curr":-36,"Meter_Id":"0a01454652240487bd2a","power_l1":60,"power_l2":-111,"power_l3":14,"voltage_l1":235.3,"voltage_l2":235.7,"voltage_l3":235.7,"current_l1":0.98,"current_l2":1.47,"current_l3":1.18,"phase_angle_L2_L1":238,"phase_angle_L3_L1":118,"phase_angle_L1":23.0,"phase_angle_L2":108.0,"phase_angle_L3":350.0,"Frequenz":49.9}}}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
assert.Equal(t, float64(-36), *res.StatusSNS.SML.PowerCurr)
|
||||
assert.Equal(t, float64(60), *res.StatusSNS.SML.PowerL1)
|
||||
assert.Equal(t, float64(-111), *res.StatusSNS.SML.PowerL2)
|
||||
assert.Equal(t, float64(14), *res.StatusSNS.SML.PowerL3)
|
||||
assert.Equal(t, float64(235.3), *res.StatusSNS.SML.VoltageL1)
|
||||
assert.Equal(t, float64(235.7), *res.StatusSNS.SML.VoltageL2)
|
||||
assert.Equal(t, float64(235.7), *res.StatusSNS.SML.VoltageL3)
|
||||
assert.Equal(t, float64(0.98), *res.StatusSNS.SML.CurrentL1)
|
||||
assert.Equal(t, float64(1.47), *res.StatusSNS.SML.CurrentL2)
|
||||
assert.Equal(t, float64(1.18), *res.StatusSNS.SML.CurrentL3)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/evcc-io/evcc/api"
|
||||
)
|
||||
|
||||
func decorateTasmota(base *Tasmota, phaseVoltages func() (float64, float64, float64, error), phaseCurrents func() (float64, float64, float64, error)) api.Meter {
|
||||
func decorateTasmota(base *Tasmota, phaseVoltages func() (float64, float64, float64, error), phaseCurrents func() (float64, float64, float64, error), phasePowers func() (float64, float64, float64, error)) api.Meter {
|
||||
switch {
|
||||
case phaseCurrents == nil && phaseVoltages == nil:
|
||||
return base
|
||||
|
|
@ -22,7 +22,7 @@ func decorateTasmota(base *Tasmota, phaseVoltages func() (float64, float64, floa
|
|||
},
|
||||
}
|
||||
|
||||
case phaseCurrents != nil && phaseVoltages == nil:
|
||||
case phaseCurrents != nil && phasePowers == nil && phaseVoltages == nil:
|
||||
return &struct {
|
||||
*Tasmota
|
||||
api.PhaseCurrents
|
||||
|
|
@ -33,7 +33,7 @@ func decorateTasmota(base *Tasmota, phaseVoltages func() (float64, float64, floa
|
|||
},
|
||||
}
|
||||
|
||||
case phaseCurrents != nil && phaseVoltages != nil:
|
||||
case phaseCurrents != nil && phasePowers == nil && phaseVoltages != nil:
|
||||
return &struct {
|
||||
*Tasmota
|
||||
api.PhaseCurrents
|
||||
|
|
@ -47,6 +47,40 @@ func decorateTasmota(base *Tasmota, phaseVoltages func() (float64, float64, floa
|
|||
phaseVoltages: phaseVoltages,
|
||||
},
|
||||
}
|
||||
|
||||
case phaseCurrents != nil && phasePowers != nil && phaseVoltages == nil:
|
||||
return &struct {
|
||||
*Tasmota
|
||||
api.PhaseCurrents
|
||||
api.PhasePowers
|
||||
}{
|
||||
Tasmota: base,
|
||||
PhaseCurrents: &decorateTasmotaPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
PhasePowers: &decorateTasmotaPhasePowersImpl{
|
||||
phasePowers: phasePowers,
|
||||
},
|
||||
}
|
||||
|
||||
case phaseCurrents != nil && phasePowers != nil && phaseVoltages != nil:
|
||||
return &struct {
|
||||
*Tasmota
|
||||
api.PhaseCurrents
|
||||
api.PhasePowers
|
||||
api.PhaseVoltages
|
||||
}{
|
||||
Tasmota: base,
|
||||
PhaseCurrents: &decorateTasmotaPhaseCurrentsImpl{
|
||||
phaseCurrents: phaseCurrents,
|
||||
},
|
||||
PhasePowers: &decorateTasmotaPhasePowersImpl{
|
||||
phasePowers: phasePowers,
|
||||
},
|
||||
PhaseVoltages: &decorateTasmotaPhaseVoltagesImpl{
|
||||
phaseVoltages: phaseVoltages,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -60,6 +94,14 @@ func (impl *decorateTasmotaPhaseCurrentsImpl) Currents() (float64, float64, floa
|
|||
return impl.phaseCurrents()
|
||||
}
|
||||
|
||||
type decorateTasmotaPhasePowersImpl struct {
|
||||
phasePowers func() (float64, float64, float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateTasmotaPhasePowersImpl) Powers() (float64, float64, float64, error) {
|
||||
return impl.phasePowers()
|
||||
}
|
||||
|
||||
type decorateTasmotaPhaseVoltagesImpl struct {
|
||||
phaseVoltages func() (float64, float64, float64, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ requirements:
|
|||
- **Total_in** für den Gesamtverbrauch in KWh (4 Nachkommastellen)
|
||||
- **Total_out** für den Gesamteinspeisung in KWh mit (4 Nachkommastellen)
|
||||
- **Power_curr** für den aktuellen Verbrauch bzw. die aktuelle Einspeisung (0 Nachkommastellen)
|
||||
Optional werden auch Phasenwerte unterstützt.
|
||||
- **power_l1**, **power_l2**, **power_l3** für die Leistung der einzelnen Phasen in W (0 Nachkommastellen)
|
||||
- **voltage_l1**, **voltage_l2**, **voltage_l3** für die Spannung der einzelnen Phasen in V (4 Nachkommastellen)
|
||||
- **current_l1**, **current_l2**, **current_l3** für die Stromstärke der einzelnen Phasen in A (4 Nachkommastellen)
|
||||
|
||||
Ein entsprechendes Lesekopf-Script sieht wie folgt aus:
|
||||
```
|
||||
|
|
@ -19,15 +23,21 @@ requirements:
|
|||
>B
|
||||
=>sensor53 r
|
||||
>M 1
|
||||
// highlight-next-line
|
||||
+1,3,s,16,9600,SML
|
||||
// highlight-next-line
|
||||
1,77070100010800ff@1000,Gesamtverbrauch,KWh,Total_in,4
|
||||
// highlight-next-line
|
||||
1,77070100020800ff@1000,Gesamteinspeisung,KWh,Total_out,4
|
||||
// highlight-next-line
|
||||
1,77070100100700ff@1,Verbrauch,W,Power_curr,0
|
||||
1,77070100600100ff@#,Zählernummer,,Meter_Id,0
|
||||
# Optional
|
||||
1,77070100240700ff@1,Leistung_L1,W,power_l1,0
|
||||
1,77070100380700ff@1,Leistung_L2,W,power_l2,0
|
||||
1,770701004c0700ff@1,Leistung_L3,W,power_l3,0
|
||||
1,77070100200700ff@1,Spannung L1,V,voltage_l1,4
|
||||
1,77070100340700ff@1,Spannung L2,V,voltage_l2,4
|
||||
1,77070100480700ff@1,Spannung L3,V,voltage_l3,4
|
||||
1,770701001f0700ff@1,Strom L1,A,current_l1,4
|
||||
1,77070100330700ff@1,Strom L2,A,current_l2,4
|
||||
1,77070100470700ff@1,Strom L3,A,current_l3,4
|
||||
#
|
||||
```
|
||||
en: |
|
||||
|
|
@ -36,6 +46,10 @@ requirements:
|
|||
- **Total_in** for the total consumption in KWh (4 decimal places)
|
||||
- **Total_out** for the total feed-in in KWh (4 decimal places)
|
||||
- **Power_curr** for the current consumption or the current feed-in in W (0 decimal places)
|
||||
As an option, phase values are also supported.
|
||||
- **power_l1**, **power_l2**, **power_l3** for the power of the individual phases in W (0 decimal places)
|
||||
- **voltage_l1**, **voltage_l2**, **voltage_l3** for the voltage of the individual phases in V (4 decimal places)
|
||||
- **current_l1**, **current_l2**, **current_l3** for the current of the individual phases in A (4 decimal places)
|
||||
|
||||
A corresponding IR reader script looks like this:
|
||||
```
|
||||
|
|
@ -43,15 +57,21 @@ requirements:
|
|||
>B
|
||||
=>sensor53 r
|
||||
>M 1
|
||||
// highlight-next-line
|
||||
+1,3,s,16,9600,SML
|
||||
// highlight-next-line
|
||||
1,77070100010800ff@1000,Gesamtverbrauch,KWh,Total_in,4
|
||||
// highlight-next-line
|
||||
1,77070100020800ff@1000,Gesamteinspeisung,KWh,Total_out,4
|
||||
// highlight-next-line
|
||||
1,77070100100700ff@1,Verbrauch,W,Power_curr,0
|
||||
1,77070100600100ff@#,Zählernummer,,Meter_Id,0
|
||||
# Optional
|
||||
1,77070100240700ff@1,Leistung_L1,W,power_l1,0
|
||||
1,77070100380700ff@1,Leistung_L2,W,power_l2,0
|
||||
1,770701004c0700ff@1,Leistung_L3,W,power_l3,0
|
||||
1,77070100200700ff@1,Spannung L1,V,voltage_l1,4
|
||||
1,77070100340700ff@1,Spannung L2,V,voltage_l2,4
|
||||
1,77070100480700ff@1,Spannung L3,V,voltage_l3,4
|
||||
1,770701001f0700ff@1,Strom L1,A,current_l1,4
|
||||
1,77070100330700ff@1,Strom L2,A,current_l2,4
|
||||
1,77070100470700ff@1,Strom L3,A,current_l3,4
|
||||
#
|
||||
```
|
||||
params:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue