Homewizard: add cache (#9158)

This commit is contained in:
Markus Thierolf 2023-07-30 14:24:24 +02:00 • committed by GitHub
parent 4cc88a0983
commit 0c9dbe0008
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 52 deletions

View file

@ -2,13 +2,11 @@ package charger
import (
"errors"
"fmt"
"net/http"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/meter/homewizard"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
)
// HomeWizard project homepage
@ -26,22 +24,25 @@ func init() {
// NewHomeWizardFromConfig creates a HomeWizard charger from generic config
func NewHomeWizardFromConfig(other map[string]interface{}) (api.Charger, error) {
var cc = struct {
cc := struct {
embed `mapstructure:",squash"`
URI string
StandbyPower float64
}{}
Cache time.Duration
}{
Cache: time.Second,
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewHomeWizard(cc.embed, cc.URI, cc.StandbyPower)
return NewHomeWizard(cc.embed, cc.URI, cc.StandbyPower, cc.Cache)
}
// NewHomeWizard creates HomeWizard charger
func NewHomeWizard(embed embed, uri string, standbypower float64) (*HomeWizard, error) {
conn, err := homewizard.NewConnection(uri)
func NewHomeWizard(embed embed, uri string, standbypower float64, cache time.Duration) (*HomeWizard, error) {
conn, err := homewizard.NewConnection(uri, cache)
if err != nil {
return nil, err
}
@ -52,7 +53,7 @@ func NewHomeWizard(embed embed, uri string, standbypower float64) (*HomeWizard,
// Check compatible product type
if c.conn.ProductType != "HWE-SKT" {
return nil, errors.New("not supported product type: " + c.conn.ProductType)
return nil, errors.New("unsupported product type: " + c.conn.ProductType)
}
c.switchSocket = NewSwitchSocket(&embed, c.Enabled, c.conn.CurrentPower, standbypower)
@ -62,34 +63,12 @@ func NewHomeWizard(embed embed, uri string, standbypower float64) (*HomeWizard,
// Enabled implements the api.Charger interface
func (c *HomeWizard) Enabled() (bool, error) {
var res homewizard.StateResponse
err := c.conn.GetJSON(fmt.Sprintf("%s/data", c.conn.URI), &res)
return res.PowerOn, err
return c.conn.Enabled()
}
// Enable implements the api.Charger interface
func (c *HomeWizard) Enable(enable bool) error {
var res homewizard.StateResponse
data := map[string]interface{}{
"power_on": enable,
}
req, err := request.New(http.MethodPut, fmt.Sprintf("%s/state", c.conn.URI), request.MarshalJSON(data), request.JSONEncoding)
if err != nil {
return err
}
if err := c.conn.DoJSON(req, &res); err != nil {
return err
}
switch {
case enable && !res.PowerOn:
return errors.New("switchOn failed")
case !enable && res.PowerOn:
return errors.New("switchOff failed")
default:
return nil
}
return c.conn.Enable(enable)
}
var _ api.MeterEnergy = (*HomeWizard)(nil)

View file

@ -1,6 +1,8 @@
package meter
import (
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/meter/homewizard"
"github.com/evcc-io/evcc/util"
@ -19,19 +21,22 @@ func init() {
// NewHomeWizardFromConfig creates a HomeWizard meter from generic config
func NewHomeWizardFromConfig(other map[string]interface{}) (api.Meter, error) {
cc := struct {
URI string
}{}
URI string
Cache time.Duration
}{
Cache: time.Second,
}
if err := util.DecodeOther(other, &cc); err != nil {
return nil, err
}
return NewHomeWizard(cc.URI)
return NewHomeWizard(cc.URI, cc.Cache)
}
// NewHomeWizard creates HomeWizard meter
func NewHomeWizard(uri string) (*HomeWizard, error) {
conn, err := homewizard.NewConnection(uri)
func NewHomeWizard(uri string, cache time.Duration) (*HomeWizard, error) {
conn, err := homewizard.NewConnection(uri, cache)
if err != nil {
return nil, err
}
@ -40,7 +45,7 @@ func NewHomeWizard(uri string) (*HomeWizard, error) {
conn: conn,
}
return c, err
return c, nil
}
var _ api.Meter = (*HomeWizard)(nil)

View file

@ -3,8 +3,11 @@ package homewizard
import (
"errors"
"fmt"
"net/http"
"strings"
"time"
"github.com/evcc-io/evcc/provider"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/transport"
@ -13,12 +16,14 @@ import (
// Connection is the homewizard connection
type Connection struct {
*request.Helper
URI string
uri string
ProductType string
dataCache provider.Cacheable[DataResponse]
stateCache provider.Cacheable[StateResponse]
}
// NewConnection creates a homewizard connection
func NewConnection(uri string) (*Connection, error) {
func NewConnection(uri string, cache time.Duration) (*Connection, error) {
if uri == "" {
return nil, errors.New("missing uri")
}
@ -26,36 +31,82 @@ func NewConnection(uri string) (*Connection, error) {
log := util.NewLogger("homewizard")
c := &Connection{
Helper: request.NewHelper(log),
URI: fmt.Sprintf("%s/api", util.DefaultScheme(strings.TrimRight(uri, "/"), "http")),
uri: fmt.Sprintf("%s/api", util.DefaultScheme(strings.TrimRight(uri, "/"), "http")),
}
c.Client.Transport = request.NewTripper(log, transport.Insecure())
// Check and set API version + product type
// check and set API version + product type
var res ApiResponse
if err := c.GetJSON(c.URI, &res); err != nil {
if err := c.GetJSON(c.uri, &res); err != nil {
return c, err
}
if res.ApiVersion != "v1" {
return nil, errors.New("not supported api version: " + res.ApiVersion)
return nil, errors.New("unsupported api version: " + res.ApiVersion)
}
c.URI = c.URI + "/" + res.ApiVersion
c.uri = c.uri + "/" + res.ApiVersion
c.ProductType = res.ProductType
c.dataCache = provider.ResettableCached(func() (DataResponse, error) {
var res DataResponse
err := c.GetJSON(fmt.Sprintf("%s/data", c.uri), &res)
return res, err
}, cache)
c.stateCache = provider.ResettableCached(func() (StateResponse, error) {
var res StateResponse
err := c.GetJSON(fmt.Sprintf("%s/state", c.uri), &res)
return res, err
}, cache)
return c, nil
}
// Enable implements the api.Charger interface
func (c *Connection) Enable(enable bool) error {
var res StateResponse
data := map[string]interface{}{
"power_on": enable,
}
req, err := request.New(http.MethodPut, fmt.Sprintf("%s/state", c.uri), request.MarshalJSON(data), request.JSONEncoding)
if err != nil {
return err
}
if err := c.DoJSON(req, &res); err != nil {
return err
}
if err == nil {
c.stateCache.Reset()
c.dataCache.Reset()
}
switch {
case enable && !res.PowerOn:
return errors.New("switchOn failed")
case !enable && res.PowerOn:
return errors.New("switchOff failed")
default:
return nil
}
}
// Enabled reads the homewizard switch state true=on/false=off
func (c *Connection) Enabled() (bool, error) {
res, err := c.stateCache.Get()
return res.PowerOn, err
}
// CurrentPower implements the api.Meter interface
func (d *Connection) CurrentPower() (float64, error) {
var res DataResponse
err := d.GetJSON(fmt.Sprintf("%s/data", d.URI), &res)
func (c *Connection) CurrentPower() (float64, error) {
res, err := c.dataCache.Get()
return res.ActivePowerW, err
}
// TotalEnergy implements the api.MeterEnergy interface
func (d *Connection) TotalEnergy() (float64, error) {
var res DataResponse
err := d.GetJSON(fmt.Sprintf("%s/data", d.URI), &res)
func (c *Connection) TotalEnergy() (float64, error) {
res, err := c.dataCache.Get()
return res.TotalPowerImportT1kWh + res.TotalPowerImportT2kWh + res.TotalPowerImportT3kWh + res.TotalPowerImportT4kWh, err
}