evcc-io/meter/shelly/connection.go

93 lines
2.1 KiB
Go

package shelly
import (
"errors"
"fmt"
"strings"
"time"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/transport"
)
type Generation interface {
Enabled() (bool, error)
Enable(bool) error
api.Meter
api.MeterEnergy
api.MeterReturnEnergy
IsThreePhase() bool
IsReversed() bool
HasReturnEnergy() bool
}
type Phases interface {
api.PhaseCurrents
api.PhaseVoltages
api.PhasePowers
}
// Connection is the Shelly connection
type Connection struct {
Generation
gen int
}
// SignedPower reports whether the device returns directional (signed) power.
func (c *Connection) SignedPower() bool {
return c.gen >= 3
}
// NewConnection creates a new Shelly device connection.
func NewConnection(uri, user, password string, channel int, cache time.Duration) (*Connection, error) {
if uri == "" {
return nil, errors.New("missing uri")
}
for _, suffix := range []string{"/", "/rcp", "/shelly"} {
uri = strings.TrimSuffix(uri, suffix)
}
uri = util.DefaultScheme(uri, "http")
log := util.NewLogger("shelly")
client := request.NewHelper(log)
// Shelly Gen1 and Gen2 families expose the /shelly endpoint
var resp DeviceInfo
if err := client.GetJSON(fmt.Sprintf("%s/shelly", uri), &resp); err != nil {
return nil, err
}
if (resp.Auth || resp.AuthEn) && (user == "" || password == "") {
return nil, fmt.Errorf("%s (%s) missing user/password", resp.Model, resp.Mac)
}
model, _, _ := strings.Cut(resp.Type+resp.Model, "-")
client.Transport = request.NewTripper(log, transport.Insecure())
var gen Generation
if resp.Gen < 2 {
// Shelly GEN 1 API
// https://shelly-api-docs.shelly.cloud/gen1/#shelly-family-overview
if user != "" {
log.Redact(transport.BasicAuthHeader(user, password))
}
gen = newGen1(client, uri, model, channel, user, password, cache)
} else {
// Shelly GEN 2+ API
// https://shelly-api-docs.shelly.cloud/gen2/
var err error
gen, err = newGen2(client, uri, model, channel, user, password, cache)
if err != nil {
return nil, err
}
}
conn := &Connection{Generation: gen, gen: resp.Gen}
return conn, nil
}