Add TQ-EM 2xx/3xx meter (#3174)
Co-authored-by: premultiply <dev@foo.bar>
This commit is contained in:
parent
904eca0dc7
commit
631706cb37
7 changed files with 239 additions and 2 deletions
|
|
@ -87,8 +87,6 @@ func matchesIdentifier(id string, m discovergy.Meter) bool {
|
|||
return id == m.MeterID || id == m.SerialNumber || id == m.FullSerialNumber
|
||||
}
|
||||
|
||||
var _ api.Meter = (*Discovergy)(nil)
|
||||
|
||||
func (m *Discovergy) CurrentPower() (float64, error) {
|
||||
res, err := m.dataG()
|
||||
return m.scale * float64(res.Values.Power) / 1e3, err
|
||||
|
|
|
|||
160
meter/tq-em.go
Normal file
160
meter/tq-em.go
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
package meter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"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-em", NewTqEmFromConfig)
|
||||
}
|
||||
|
||||
type tqemData struct {
|
||||
Authentication *bool
|
||||
Serial string
|
||||
Obis1_4_0 float64 `json:"1-0:1.4.0*255"`
|
||||
Obis1_8_0 float64 `json:"1-0:1.8.0*255"`
|
||||
Obis2_4_0 float64 `json:"1-0:2.4.0*255"`
|
||||
Obis2_8_0 float64 `json:"1-0:2.8.0*255"`
|
||||
Obis13_4_0 float64 `json:"1-0:13.4.0*255"`
|
||||
Obis14_4_0 float64 `json:"1-0:14.4.0*255"`
|
||||
Obis21_4_0 float64 `json:"1-0:21.4.0*255"`
|
||||
Obis21_8_0 float64 `json:"1-0:21.8.0*255"`
|
||||
Obis22_4_0 float64 `json:"1-0:22.4.0*255"`
|
||||
Obis22_8_0 float64 `json:"1-0:22.8.0*255"`
|
||||
Obis31_4_0 *float64 `json:"1-0:31.4.0*255"` // optional currents
|
||||
Obis32_4_0 float64 `json:"1-0:32.4.0*255"`
|
||||
Obis33_4_0 float64 `json:"1-0:33.4.0*255"`
|
||||
Obis41_4_0 float64 `json:"1-0:41.4.0*255"`
|
||||
Obis41_8_0 float64 `json:"1-0:41.8.0*255"`
|
||||
Obis42_4_0 float64 `json:"1-0:42.4.0*255"`
|
||||
Obis42_8_0 float64 `json:"1-0:42.8.0*255"`
|
||||
Obis51_4_0 *float64 `json:"1-0:51.4.0*255"` // optional currents
|
||||
Obis52_4_0 float64 `json:"1-0:52.4.0*255"`
|
||||
Obis53_4_0 float64 `json:"1-0:53.4.0*255"`
|
||||
Obis61_4_0 float64 `json:"1-0:61.4.0*255"`
|
||||
Obis61_8_0 float64 `json:"1-0:61.8.0*255"`
|
||||
Obis62_4_0 float64 `json:"1-0:62.4.0*255"`
|
||||
Obis62_8_0 float64 `json:"1-0:62.8.0*255"`
|
||||
Obis71_4_0 *float64 `json:"1-0:71.4.0*255"` // optional currents
|
||||
Obis72_4_0 float64 `json:"1-0:72.4.0*255"`
|
||||
Obis73_4_0 float64 `json:"1-0:73.4.0*255"`
|
||||
}
|
||||
|
||||
type TqEm struct {
|
||||
dataG func() (tqemData, error)
|
||||
scale float64
|
||||
}
|
||||
|
||||
//go:generate go run ../cmd/tools/decorate.go -f decorateTqEm -b api.Meter -t "api.MeterCurrent,Currents,func() (float64, float64, float64, error)"
|
||||
|
||||
// NewTqEmFromConfig creates a new configurable meter
|
||||
func NewTqEmFromConfig(other map[string]interface{}) (api.Meter, error) {
|
||||
cc := struct {
|
||||
URI string
|
||||
Password string
|
||||
Cache time.Duration
|
||||
}{
|
||||
Cache: time.Second,
|
||||
}
|
||||
|
||||
if err := util.DecodeOther(other, &cc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log := util.NewLogger("tq-em").Redact(cc.Password)
|
||||
|
||||
client := request.NewHelper(log)
|
||||
client.Jar, _ = cookiejar.New(nil)
|
||||
|
||||
base := util.DefaultScheme(strings.TrimRight(cc.URI, "/"), "http")
|
||||
|
||||
// get serial number
|
||||
var meter tqemData
|
||||
|
||||
uri := fmt.Sprintf("%s/start.php", base)
|
||||
err := client.GetJSON(uri, &meter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if meter.Serial == "" {
|
||||
return nil, errors.New("no serial")
|
||||
}
|
||||
|
||||
dataG := provider.Cached(func() (tqemData, error) {
|
||||
var res tqemData
|
||||
|
||||
uri := fmt.Sprintf("%s/mum-webservice/data.php", base)
|
||||
err := client.GetJSON(uri, &res)
|
||||
|
||||
if err == nil && res.Serial == "" {
|
||||
data := url.Values{
|
||||
"login": {meter.Serial},
|
||||
"password": {cc.Password},
|
||||
}
|
||||
|
||||
var req *http.Request
|
||||
req, err = request.New(http.MethodPost, fmt.Sprintf("%s/start.php", base), strings.NewReader(data.Encode()), request.URLEncoding)
|
||||
|
||||
if err == nil {
|
||||
_, err = client.DoBody(req)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = client.GetJSON(uri, &res)
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && res.Serial == "" {
|
||||
err = errors.New("authentication failed")
|
||||
}
|
||||
|
||||
return res, err
|
||||
}, cc.Cache)
|
||||
|
||||
m := &TqEm{
|
||||
dataG: dataG,
|
||||
}
|
||||
|
||||
res, err := dataG()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res.Obis31_4_0 != nil {
|
||||
return decorateTqEm(m, m.currents), nil
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *TqEm) CurrentPower() (float64, error) {
|
||||
res, err := m.dataG()
|
||||
return res.Obis1_4_0 - res.Obis2_4_0, err
|
||||
}
|
||||
|
||||
var _ api.MeterEnergy = (*TqEm)(nil)
|
||||
|
||||
func (m *TqEm) TotalEnergy() (float64, error) {
|
||||
res, err := m.dataG()
|
||||
return res.Obis1_8_0 / 1e3, err
|
||||
}
|
||||
|
||||
func (m *TqEm) currents() (float64, float64, float64, error) {
|
||||
res, err := m.dataG()
|
||||
if err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
return *res.Obis31_4_0, *res.Obis51_4_0, *res.Obis71_4_0, nil
|
||||
}
|
||||
35
meter/tq-em_decorators.go
Normal file
35
meter/tq-em_decorators.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package meter
|
||||
|
||||
// Code generated by github.com/evcc-io/evcc/cmd/tools/decorate.go. DO NOT EDIT.
|
||||
|
||||
import (
|
||||
"github.com/evcc-io/evcc/api"
|
||||
)
|
||||
|
||||
func decorateTqEm(base api.Meter, meterCurrent func() (float64, float64, float64, error)) api.Meter {
|
||||
switch {
|
||||
case meterCurrent == nil:
|
||||
return base
|
||||
|
||||
case meterCurrent != nil:
|
||||
return &struct {
|
||||
api.Meter
|
||||
api.MeterCurrent
|
||||
}{
|
||||
Meter: base,
|
||||
MeterCurrent: &decorateTqEmMeterCurrentImpl{
|
||||
meterCurrent: meterCurrent,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type decorateTqEmMeterCurrentImpl struct {
|
||||
meterCurrent func() (float64, float64, float64, error)
|
||||
}
|
||||
|
||||
func (impl *decorateTqEmMeterCurrentImpl) Currents() (float64, float64, float64, error) {
|
||||
return impl.meterCurrent()
|
||||
}
|
||||
19
templates/definition/meter/tq-em.yaml
Normal file
19
templates/definition/meter/tq-em.yaml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
template: tq-em
|
||||
products:
|
||||
- brand: TQ
|
||||
description:
|
||||
generic: Energy Manager EM2xx
|
||||
- brand: TQ
|
||||
description:
|
||||
generic: Energy Manager EM3xx
|
||||
params:
|
||||
- name: usage
|
||||
choice: ["grid"]
|
||||
- name: host
|
||||
- name: port
|
||||
default: 80
|
||||
- name: password
|
||||
render: |
|
||||
type: tq-em
|
||||
uri: http://{{ .host }}:{{ .port }}
|
||||
password: {{ .password }}
|
||||
12
templates/docs/meter/tq-em_0.yaml
Normal file
12
templates/docs/meter/tq-em_0.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
product:
|
||||
brand: TQ
|
||||
description: Energy Manager EM2xx
|
||||
render:
|
||||
- usage: grid
|
||||
default: |
|
||||
type: template
|
||||
template: tq-em
|
||||
usage: grid
|
||||
host: 192.0.2.2 # IP-Adresse oder Hostname
|
||||
port: 80 # Port # Optional
|
||||
password: # Passwort des Benutzerkontos (bei führenden Nullen bitte in einfache Hochkommata setzen) # Optional
|
||||
12
templates/docs/meter/tq-em_1.yaml
Normal file
12
templates/docs/meter/tq-em_1.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
product:
|
||||
brand: TQ
|
||||
description: Energy Manager EM3xx
|
||||
render:
|
||||
- usage: grid
|
||||
default: |
|
||||
type: template
|
||||
template: tq-em
|
||||
usage: grid
|
||||
host: 192.0.2.2 # IP-Adresse oder Hostname
|
||||
port: 80 # Port # Optional
|
||||
password: # Passwort des Benutzerkontos (bei führenden Nullen bitte in einfache Hochkommata setzen) # Optional
|
||||
|
|
@ -62,6 +62,7 @@
|
|||
"Sonnenbatterie",
|
||||
"Sungrow",
|
||||
"Tesla",
|
||||
"TQ",
|
||||
"VARTA",
|
||||
"Victron",
|
||||
"Volkszähler"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue