evcc-io/meter/shelly/connection.go

82 lines
1.9 KiB
Go

package shelly
import (
"errors"
"fmt"
"strings"
"time"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/request"
"github.com/evcc-io/evcc/util/transport"
)
type Generation interface {
CurrentPower() (float64, error)
Enabled() (bool, error)
Enable(bool) error
TotalEnergy() (float64, error)
}
type Phases interface {
Currents() (float64, float64, float64, error)
Voltages() (float64, float64, float64, error)
Powers() (float64, float64, float64, error)
}
// Connection is the Shelly connection
type Connection struct {
Generation
}
// 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.Split(resp.Type+resp.Model, "-")[0]
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{gen}
return conn, nil
}