Add TQ Energy Manager EM420 (#4083)
Co-authored-by: David Claybourne <claybourne.david@gmail.com>
This commit is contained in:
parent
2b823686c7
commit
b9f7c77dc0
3 changed files with 213 additions and 0 deletions
167
meter/tq-em420.go
Normal file
167
meter/tq-em420.go
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/evcc-io/evcc/api"
|
||||
"github.com/evcc-io/evcc/provider"
|
||||
"github.com/evcc-io/evcc/util"
|
||||
"github.com/evcc-io/evcc/util/request"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Add("tq-em420", NewTqEm420FromConfig)
|
||||
}
|
||||
|
||||
type TqEm420Data struct {
|
||||
SmartMeter struct {
|
||||
ConfigurationID string `json:"configuration_id"`
|
||||
Status string `json:"status"`
|
||||
Timestamp struct {
|
||||
Seconds float64 `json:"seconds"`
|
||||
Nanos float64 `json:"nanos"`
|
||||
} `json:"timestamp"`
|
||||
Values struct {
|
||||
ActiveEnergyP float64 `json:"active_energy_+"`
|
||||
ActiveEnergyL1P float64 `json:"active_energy_+_L1"`
|
||||
ActiveEnergyL2P float64 `json:"active_energy_+_L2"`
|
||||
ActiveEnergyL3P float64 `json:"active_energy_+_L3"`
|
||||
ActiveEnergyM float64 `json:"active_energy_-"`
|
||||
ActiveEnergyL1M float64 `json:"active_energy_-_L1"`
|
||||
ActiveEnergyL2M float64 `json:"active_energy_-_L2"`
|
||||
ActiveEnergyL3M float64 `json:"active_energy_-_L3"`
|
||||
ActivePowerP float64 `json:"active_power_+"`
|
||||
ActivePowerL1P float64 `json:"active_power_+_L1"`
|
||||
ActivePowerL2P float64 `json:"active_power_+_L2"`
|
||||
ActivePowerL3P float64 `json:"active_power_+_L3"`
|
||||
ActivePowerM float64 `json:"active_power_-"`
|
||||
ActivePowerL1M float64 `json:"active_power_-_L1"`
|
||||
ActivePowerL2M float64 `json:"active_power_-_L2"`
|
||||
ActivePowerL3M float64 `json:"active_power_-_L3"`
|
||||
ApparentEnergyP float64 `json:"apparent_energy_+"`
|
||||
ApparentEnergyL1P float64 `json:"apparent_energy_+_L1"`
|
||||
ApparentEnergyL2P float64 `json:"apparent_energy_+_L2"`
|
||||
ApparentEnergyL3P float64 `json:"apparent_energy_+_L3"`
|
||||
ApparentEnergyM float64 `json:"apparent_energy_-"`
|
||||
ApparentEnergyL1M float64 `json:"apparent_energy_-_L1"`
|
||||
ApparentEnergyL2M float64 `json:"apparent_energy_-_L2"`
|
||||
ApparentEnergyL3M float64 `json:"apparent_energy_-_L3"`
|
||||
ApparentPowerP float64 `json:"apparent_power_+"`
|
||||
ApparentPowerL1P float64 `json:"apparent_power_+_L1"`
|
||||
ApparentPowerL2P float64 `json:"apparent_power_+_L2"`
|
||||
ApparentPowerL3P float64 `json:"apparent_power_+_L3"`
|
||||
ApparentPowerM float64 `json:"apparent_power_-"`
|
||||
ApparentPowerL1M float64 `json:"apparent_power_-_L1"`
|
||||
ApparentPowerL2M float64 `json:"apparent_power_-_L2"`
|
||||
ApparentPowerL3M float64 `json:"apparent_power_-_L3"`
|
||||
CurrentL1 float64 `json:"current_L1"`
|
||||
CurrentL2 float64 `json:"current_L2"`
|
||||
CurrentL3 float64 `json:"current_L3"`
|
||||
PowerFactor float64 `json:"power_factor"`
|
||||
PowerFactorL1 float64 `json:"power_factor_L1"`
|
||||
PowerFactorL2 float64 `json:"power_factor_L2"`
|
||||
PowerFactorL3 float64 `json:"power_factor_L3"`
|
||||
ReactiveEnergyP float64 `json:"reactive_energy_+"`
|
||||
ReactiveEnergyL1P float64 `json:"reactive_energy_+_L1"`
|
||||
ReactiveEnergyL2P float64 `json:"reactive_energy_+_L2"`
|
||||
ReactiveEnergyL3P float64 `json:"reactive_energy_+_L3"`
|
||||
ReactiveEnergyM float64 `json:"reactive_energy_-"`
|
||||
ReactiveEnergyL1M float64 `json:"reactive_energy_-_L1"`
|
||||
ReactiveEnergyL2M float64 `json:"reactive_energy_-_L2"`
|
||||
ReactiveEnergyL3M float64 `json:"reactive_energy_-_L3"`
|
||||
ReactivePowerP float64 `json:"reactive_power_+"`
|
||||
ReactivePowerL1P float64 `json:"reactive_power_+_L1"`
|
||||
ReactivePowerL2P float64 `json:"reactive_power_+_L2"`
|
||||
ReactivePowerL3P float64 `json:"reactive_power_+_L3"`
|
||||
ReactivePowerM float64 `json:"reactive_power_-"`
|
||||
ReactivePowerL1M float64 `json:"reactive_power_-_L1"`
|
||||
ReactivePowerL2M float64 `json:"reactive_power_-_L2"`
|
||||
ReactivePowerL3M float64 `json:"reactive_power_-_L3"`
|
||||
SupplyFrequency float64 `json:"supply_frequency"`
|
||||
VoltageL1 float64 `json:"voltage_L1"`
|
||||
VoltageL2 float64 `json:"voltage_L2"`
|
||||
VoltageL3 float64 `json:"voltage_L3"`
|
||||
} `json:"values"`
|
||||
} `json:"smart-meter"`
|
||||
}
|
||||
|
||||
type TqEM420 struct {
|
||||
dataG func() (TqEm420Data, error)
|
||||
}
|
||||
|
||||
// NewTqEm420FromConfig creates a new configurable meter
|
||||
func NewTqEm420FromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
cc := struct {
|
||||
URI string
|
||||
Token string
|
||||
Device string
|
||||
Cache time.Duration
|
||||
}{
|
||||
Cache: time.Second,
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log := util.NewLogger("tq-em420").Redact(cc.Token)
|
||||
|
||||
client := request.NewHelper(log)
|
||||
client.Jar, _ = cookiejar.New(nil)
|
||||
|
||||
base := util.DefaultScheme(strings.TrimRight(cc.URI, "/"), "http")
|
||||
|
||||
dataG := provider.Cached(func() (TqEm420Data, error) {
|
||||
var res TqEm420Data
|
||||
|
||||
headers := map[string]string{
|
||||
"Accept": "application/json",
|
||||
"Authorization": "Bearer " + cc.Token,
|
||||
}
|
||||
|
||||
req, err := request.New(http.MethodGet, fmt.Sprintf("%s/api/json/"+cc.Device+"/values/smart-meter", base), nil, headers)
|
||||
|
||||
if err == nil {
|
||||
err = client.DoJSON(req, &res)
|
||||
}
|
||||
|
||||
return res, err
|
||||
}, cc.Cache)
|
||||
|
||||
m := &TqEM420{
|
||||
dataG: dataG,
|
||||
}
|
||||
|
||||
_, err := dataG()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *TqEM420) CurrentPower() (float64, error) {
|
||||
res, err := m.dataG()
|
||||
return (res.SmartMeter.Values.ActivePowerP - res.SmartMeter.Values.ActivePowerM) / 1e3, err
|
||||
}
|
||||
|
||||
var _ api.MeterEnergy = (*TqEM420)(nil)
|
||||
|
||||
func (m *TqEM420) TotalEnergy() (float64, error) {
|
||||
res, err := m.dataG()
|
||||
return res.SmartMeter.Values.ActiveEnergyP / 1e3, err
|
||||
}
|
||||
|
||||
var _ api.MeterCurrent = (*TqEM420)(nil)
|
||||
|
||||
func (m *TqEM420) Currents() (float64, float64, float64, error) {
|
||||
res, err := m.dataG()
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
return res.SmartMeter.Values.CurrentL1 / 1e3, res.SmartMeter.Values.CurrentL2 / 1e3, res.SmartMeter.Values.CurrentL3 / 1e3, nil
|
||||
}
|
||||
33
templates/definition/meter/tq-em420.yaml
Normal file
33
templates/definition/meter/tq-em420.yaml
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
template: tq-em420
|
||||
products:
|
||||
- brand: TQ
|
||||
description:
|
||||
generic: Energy Manager EM420
|
||||
params:
|
||||
- name: usage
|
||||
choice: ["grid"]
|
||||
- name: host
|
||||
- name: port
|
||||
default: 80
|
||||
- name: device
|
||||
default: "local"
|
||||
description:
|
||||
de: API Gerätepfad
|
||||
en: API Device Path
|
||||
help:
|
||||
de: JSON-Schnittstelle -> Datenendpunkt
|
||||
en: JSON-API -> Data Endpoint
|
||||
- name: token
|
||||
required: true
|
||||
example: ey...
|
||||
description:
|
||||
de: Accesstoken
|
||||
en: Access token
|
||||
help:
|
||||
de: Token des EM420 (Erstellen unter Profil -> Zugangsschlüssel)
|
||||
en: Access token for EM420 (Create in Profile -> Access tokens)
|
||||
render: |
|
||||
type: tq-em420
|
||||
uri: http://{{ .host }}:{{ .port }}
|
||||
token: {{ .token }}
|
||||
device: {{ .device }}
|
||||
13
templates/docs/meter/tq-em420_0.yaml
Normal file
13
templates/docs/meter/tq-em420_0.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
product:
|
||||
brand: TQ
|
||||
description: Energy Manager EM420
|
||||
render:
|
||||
- usage: grid
|
||||
default: |
|
||||
type: template
|
||||
template: tq-em420
|
||||
usage: grid
|
||||
host: 192.0.2.2 # IP-Adresse oder Hostname
|
||||
port: 80 # Port # Optional
|
||||
device: local # JSON-Schnittstelle -> Datenendpunkt # Optional
|
||||
token: ey... # Token des EM420 (Erstellen unter Profil -> Zugangsschlüssel)
|
||||
Loading…
Add table
Add a link
Reference in a new issue