Shelly: split api types (#20445)
This commit is contained in:
parent
61e79f39da
commit
92b8fdded5
9 changed files with 297 additions and 250 deletions
|
|
@ -15,12 +15,11 @@ import (
|
|||
// Connection is the Shelly connection
|
||||
type Connection struct {
|
||||
*request.Helper
|
||||
uri string
|
||||
channel int
|
||||
gen int // Shelly api generation
|
||||
devicetype string // Shelly device type
|
||||
app string // Shelly device app code
|
||||
profile string // Shelly device profile
|
||||
uri string
|
||||
channel int
|
||||
gen int // Shelly api generation
|
||||
model string // Shelly device type
|
||||
profile string // Shelly device profile
|
||||
}
|
||||
|
||||
// NewConnection creates a new Shelly device connection.
|
||||
|
|
@ -42,39 +41,41 @@ func NewConnection(uri, user, password string, channel int) (*Connection, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if (resp.Auth || resp.AuthEn) && (user == "" || password == "") {
|
||||
return nil, fmt.Errorf("%s (%s) missing user/password", resp.Model, resp.Mac)
|
||||
}
|
||||
|
||||
conn := &Connection{
|
||||
Helper: client,
|
||||
channel: channel,
|
||||
gen: resp.Gen,
|
||||
devicetype: resp.Type,
|
||||
app: resp.App,
|
||||
profile: resp.Profile,
|
||||
Helper: client,
|
||||
uri: util.DefaultScheme(uri, "http"),
|
||||
channel: channel,
|
||||
gen: resp.Gen,
|
||||
model: strings.Split(resp.Type+resp.Model, "-")[0],
|
||||
profile: resp.Profile,
|
||||
}
|
||||
|
||||
conn.Client.Transport = request.NewTripper(log, transport.Insecure())
|
||||
|
||||
if (resp.Auth || resp.AuthEn) && (user == "" || password == "") {
|
||||
return conn, fmt.Errorf("%s (%s) missing user/password", resp.Model, resp.Mac)
|
||||
// Set default profile to "monophase" if not provided
|
||||
if resp.Profile == "" {
|
||||
resp.Profile = "monophase"
|
||||
}
|
||||
|
||||
switch conn.gen {
|
||||
case 0, 1:
|
||||
// Shelly GEN 1 API
|
||||
// https://shelly-api-docs.shelly.cloud/gen1/#shelly-family-overview
|
||||
conn.uri = util.DefaultScheme(uri, "http")
|
||||
if user != "" {
|
||||
log.Redact(transport.BasicAuthHeader(user, password))
|
||||
conn.Client.Transport = transport.BasicAuth(user, password, conn.Client.Transport)
|
||||
}
|
||||
|
||||
case 2, 3:
|
||||
// Shelly GEN 2+ API
|
||||
// https://shelly-api-docs.shelly.cloud/gen2/
|
||||
conn.uri = fmt.Sprintf("%s/rpc", util.DefaultScheme(uri, "http"))
|
||||
conn.uri += "/rpc"
|
||||
if user != "" {
|
||||
conn.Client.Transport = digest.NewTransport(user, password, conn.Client.Transport)
|
||||
}
|
||||
|
||||
default:
|
||||
return conn, fmt.Errorf("%s (%s) unknown api generation (%d)", resp.Type, resp.Model, conn.gen)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ package shelly
|
|||
import "github.com/evcc-io/evcc/api"
|
||||
|
||||
type EnergyMeter struct {
|
||||
*Connection
|
||||
conn *Connection
|
||||
}
|
||||
|
||||
func NewEnergyMeter(conn *Connection) *EnergyMeter {
|
||||
res := &EnergyMeter{
|
||||
Connection: conn,
|
||||
conn: conn,
|
||||
}
|
||||
|
||||
return res
|
||||
|
|
@ -17,7 +17,7 @@ func NewEnergyMeter(conn *Connection) *EnergyMeter {
|
|||
// CurrentPower implements the api.Meter interface
|
||||
func (sh *EnergyMeter) CurrentPower() (float64, error) {
|
||||
var res Gen2StatusResponse
|
||||
if err := sh.Connection.execGen2Cmd("EM.GetStatus", false, &res); err != nil {
|
||||
if err := sh.conn.execGen2Cmd("EM.GetStatus", false, &res); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ func (sh *EnergyMeter) CurrentPower() (float64, error) {
|
|||
// TotalEnergy implements the api.Meter interface
|
||||
func (sh *EnergyMeter) TotalEnergy() (float64, error) {
|
||||
var res Gen2EmDataStatusResponse
|
||||
if err := sh.Connection.execGen2Cmd("EMData.GetStatus", false, &res); err != nil {
|
||||
if err := sh.conn.execGen2Cmd("EMData.GetStatus", false, &res); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ var _ api.PhaseCurrents = (*EnergyMeter)(nil)
|
|||
// Currents implements the api.PhaseCurrents interface
|
||||
func (sh *EnergyMeter) Currents() (float64, float64, float64, error) {
|
||||
var res Gen2StatusResponse
|
||||
if err := sh.Connection.execGen2Cmd("EM.GetStatus", false, &res); err != nil {
|
||||
if err := sh.conn.execGen2Cmd("EM.GetStatus", false, &res); err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ var _ api.PhaseVoltages = (*EnergyMeter)(nil)
|
|||
// Voltages implements the api.PhaseVoltages interface
|
||||
func (sh *EnergyMeter) Voltages() (float64, float64, float64, error) {
|
||||
var res Gen2StatusResponse
|
||||
if err := sh.Connection.execGen2Cmd("EM.GetStatus", false, &res); err != nil {
|
||||
if err := sh.conn.execGen2Cmd("EM.GetStatus", false, &res); err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ var _ api.PhasePowers = (*EnergyMeter)(nil)
|
|||
// Powers implements the api.PhasePowers interface
|
||||
func (sh *EnergyMeter) Powers() (float64, float64, float64, error) {
|
||||
var res Gen2StatusResponse
|
||||
if err := sh.Connection.execGen2Cmd("EM.GetStatus", false, &res); err != nil {
|
||||
if err := sh.conn.execGen2Cmd("EM.GetStatus", false, &res); err != nil {
|
||||
return 0, 0, 0, err
|
||||
}
|
||||
|
||||
|
|
|
|||
25
meter/shelly/gen1.go
Normal file
25
meter/shelly/gen1.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package shelly
|
||||
|
||||
// Gen1API endpoint reference: https://shelly-api-docs.shelly.cloud/gen1/#shelly-family-overview
|
||||
|
||||
type Gen1SwitchResponse struct {
|
||||
Ison bool
|
||||
}
|
||||
|
||||
type Gen1Status struct {
|
||||
Meters []struct {
|
||||
Power float64
|
||||
Current float64
|
||||
Voltage float64
|
||||
Total float64
|
||||
Total_Returned float64
|
||||
}
|
||||
// Shelly EM meter JSON response
|
||||
EMeters []struct {
|
||||
Power float64
|
||||
Current float64
|
||||
Voltage float64
|
||||
Total float64
|
||||
Total_Returned float64
|
||||
}
|
||||
}
|
||||
45
meter/shelly/gen1_test.go
Normal file
45
meter/shelly/gen1_test.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package shelly
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Test Gen1Status response
|
||||
func TestUnmarshalGen1Status(t *testing.T) {
|
||||
{
|
||||
// Shelly 1 PM channel 0 (1)
|
||||
var res Gen1Status
|
||||
|
||||
jsonstr := `{"wifi_sta":{"connected":true,"ssid":"XXXX-WLAN","ip":"192.168.178.XXX","rssi":-54},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"17:59","unixtime":1676134770,"serial":2437,"has_update":true,"mac":"84CCA8XXXXXXX","cfg_changed_cnt":1,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"overpower":false,"source":"http"}],"meters":[{"power":4711.12,"overpower":0.00,"is_valid":true,"timestamp":1676138370,"counters":[0.000, 0.000, 0.000],"total":6472513}],"inputs":[{"input":0,"event":"","event_cnt":0}],"temperature":16.79,"overtemperature":false,"tmp":{"tC":16.79,"tF":62.22, "is_valid":true},"temperature_status":"Normal","ext_sensors":{},"ext_temperature":{},"ext_humidity":{},"update":{"status":"pending","has_update":true,"new_version":"20221108-153925/v1.12.1-1PM-fix-g2821131","old_version":"20220209-094317/v1.11.8-g8c7bb8d"},"ram_total":50456,"ram_free":37056,"fs_size":233681,"fs_free":149094,"uptime":17284290}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, 107875.21666666666, gen1Energy("SHSW-PM", res.Meters[0].Total))
|
||||
assert.Equal(t, 4711.12, res.Meters[0].Power)
|
||||
}
|
||||
|
||||
{
|
||||
// Shelly 1 channel 0 (1)
|
||||
var res Gen1Status
|
||||
|
||||
jsonstr := `{"wifi_sta":{"connected":true,"ssid":"XXXX-WLAN","ip":"192.168.178.XXX","rssi":-57},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"19:25","unixtime":1676139913,"serial":959,"has_update":true,"mac":"E8DB8XXXXXX","cfg_changed_cnt":1,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"source":"timer"}],"meters":[{"power":81.5,"is_valid":true}],"inputs":[{"input":0,"event":"","event_cnt":0}],"ext_sensors":{},"ext_temperature":{},"ext_humidity":{},"update":{"status":"pending","has_update":true,"new_version":"20221027-091427/v1.12.1-ga9117d3","old_version":"20211109-124958/v1.11.7-g682a0db"},"ram_total":50880,"ram_free":38796,"fs_size":233681,"fs_free":151102,"uptime":20319391}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, 0.0, gen1Energy("SHSW-1", res.Meters[0].Total))
|
||||
assert.Equal(t, 81.5, res.Meters[0].Power)
|
||||
}
|
||||
|
||||
{
|
||||
// Shelly EM channel 0 (1)
|
||||
var res Gen1Status
|
||||
|
||||
jsonstr := `{"wifi_sta":{"connected":true,"ssid":"XXXX","ip":"192.168.178.XXX","rssi":-55},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"11:16","unixtime":1676110566,"serial":21580,"has_update":false,"mac":"C45BXXXXX","cfg_changed_cnt":0,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"overpower":false,"is_valid":true,"source":"input"}],"emeters":[{"power":-620.34,"reactive":714.48,"pf":-0.66,"voltage":235.68,"is_valid":true,"total":401472.9,"total_returned":653673.7},{"power":0.00,"reactive":0.00,"pf":0.00,"voltage":235.68,"is_valid":true,"total":173411.3,"total_returned":294.2}],"update":{"status":"idle","has_update":false,"new_version":"20221027-105518/v1.12.1-ga9117d3","old_version":"20221027-105518/v1.12.1-ga9117d3"},"ram_total":51072,"ram_free":35660,"fs_size":233681,"fs_free":156373,"uptime":2226140}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, 401472.9, gen1Energy("SHEM", res.EMeters[0].Total))
|
||||
assert.Equal(t, -620.34, res.EMeters[0].Power)
|
||||
}
|
||||
}
|
||||
82
meter/shelly/gen2.go
Normal file
82
meter/shelly/gen2.go
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package shelly
|
||||
|
||||
// Gen2API endpoint reference: https://shelly-api-docs.shelly.cloud/gen2/
|
||||
|
||||
type Gen2RpcPost struct {
|
||||
Id int `json:"id"`
|
||||
On bool `json:"on"`
|
||||
Src string `json:"src"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
type Gen2SwitchStatus struct {
|
||||
Output bool
|
||||
Apower float64
|
||||
Voltage float64
|
||||
Current float64
|
||||
Aenergy struct {
|
||||
Total float64
|
||||
}
|
||||
Ret_Aenergy struct {
|
||||
Total float64
|
||||
}
|
||||
}
|
||||
|
||||
type Gen2EMStatus struct {
|
||||
TotalActPower float64 `json:"total_act_power"`
|
||||
ACurrent float64 `json:"a_current"`
|
||||
BCurrent float64 `json:"b_current"`
|
||||
CCurrent float64 `json:"c_current"`
|
||||
AVoltage float64 `json:"a_voltage"`
|
||||
BVoltage float64 `json:"b_voltage"`
|
||||
CVoltage float64 `json:"c_voltage"`
|
||||
AActPower float64 `json:"a_act_power"`
|
||||
BActPower float64 `json:"b_act_power"`
|
||||
CActPower float64 `json:"c_act_power"`
|
||||
}
|
||||
|
||||
type Gen2EMData struct {
|
||||
TotalAct float64 `json:"total_act"`
|
||||
TotalActRet float64 `json:"total_act_ret"`
|
||||
}
|
||||
|
||||
type Gen2EM1Status struct {
|
||||
Current float64 `json:"current"`
|
||||
Voltage float64 `json:"voltage"`
|
||||
ActPower float64 `json:"act_power"`
|
||||
}
|
||||
|
||||
type Gen2EM1Data struct {
|
||||
TotalActEnergy float64 `json:"total_act_energy"`
|
||||
TotalActRetEnergy float64 `json:"total_act_ret_energy"`
|
||||
}
|
||||
|
||||
type Gen2StatusResponse struct {
|
||||
Switch0 Gen2SwitchStatus `json:"switch:0"`
|
||||
Switch1 Gen2SwitchStatus `json:"switch:1"`
|
||||
Switch2 Gen2SwitchStatus `json:"switch:2"`
|
||||
Pm0 Gen2SwitchStatus `json:"pm1:0"`
|
||||
Pm1 Gen2SwitchStatus `json:"pm2:1"`
|
||||
Pm2 Gen2SwitchStatus `json:"pm3:2"`
|
||||
// additional shelly Pro EM meter JSON response
|
||||
TotalPower float64 `json:"total_act_power"`
|
||||
CurrentA float64 `json:"a_current"`
|
||||
CurrentB float64 `json:"b_current"`
|
||||
CurrentC float64 `json:"c_current"`
|
||||
VoltageA float64 `json:"a_voltage"`
|
||||
VoltageB float64 `json:"b_voltage"`
|
||||
VoltageC float64 `json:"c_voltage"`
|
||||
PowerA float64 `json:"a_act_power"`
|
||||
PowerB float64 `json:"b_act_power"`
|
||||
PowerC float64 `json:"c_act_power"`
|
||||
Em0 Gen2EM1Status `json:"em1:0"`
|
||||
Em1 Gen2EM1Status `json:"em1:1"`
|
||||
Em2 Gen2EM1Status `json:"em1:2"`
|
||||
Em0Data Gen2EM1Data `json:"em1data:0"`
|
||||
Em1Data Gen2EM1Data `json:"em1data:1"`
|
||||
Em2Data Gen2EM1Data `json:"em1data:2"`
|
||||
}
|
||||
|
||||
type Gen2EmDataStatusResponse struct {
|
||||
TotalEnergy float64 `json:"total_act"`
|
||||
}
|
||||
80
meter/shelly/gen2_test.go
Normal file
80
meter/shelly/gen2_test.go
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package shelly
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Test Gen2+ status responses
|
||||
func TestUnmarshalGen2StatusResponse(t *testing.T) {
|
||||
{
|
||||
// Shelly Pro 1PM channel 0 (1)
|
||||
var res Gen2StatusResponse
|
||||
|
||||
jsonstr := `{"ble":{},"cloud":{"connected":true},"eth":{"ip":null},"input:0":{"id":0,"state":false},"input:1":{"id":1,"state":false},"mqtt":{"connected":false},"switch:0":{"id":0, "source":"HTTP", "output":false, "apower":47.11, "voltage":232.0, "current":0.000, "pf":0.00, "aenergy":{"total":5.125,"by_minute":[0.000,0.000,0.000],"minute_ts":1675718520},"temperature":{"tC":25.3, "tF":77.5}},"sys":{"mac":"30C6F78BB4D8","restart_required":false,"time":"22:22","unixtime":1675718522,"uptime":45070,"ram_size":234204,"ram_free":137716,"fs_size":524288,"fs_free":172032,"cfg_rev":13,"kvs_rev":1,"schedule_rev":0,"webhook_rev":0,"available_updates":{"beta":{"version":"0.13.0-beta3"}}},"wifi":{"sta_ip":"192.168.178.64","status":"got ip","ssid":"***","rssi":-62},"ws":{"connected":false}}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, 5.125, res.Switch0.Aenergy.Total)
|
||||
assert.Equal(t, 47.11, res.Switch0.Apower)
|
||||
}
|
||||
|
||||
{
|
||||
// Shelly PM Mini Gen3 channel 0 (1)
|
||||
var res Gen2StatusResponse
|
||||
|
||||
jsonstr := `{"ble":{},"cloud":{"connected":true},"mqtt":{"connected":false},"pm1:0":{"id":0, "voltage":239.9, "current":7.434, "apower":1780.1 ,"freq":50.1,"aenergy":{"total":3551.682,"by_minute":[15234.772,29611.247,29825.821],"minute_ts":1719917850},"ret_aenergy":{"total":0.000,"by_minute":[0.000,0.000,0.000],"minute_ts":1719917850}},"sys":{"mac":"84FCE638D818","restart_required":false,"time":"12:57","unixtime":1719917851,"uptime":62328,"ram_size":261744,"ram_free":151436,"fs_size":1048576,"fs_free":712704,"cfg_rev":10,"kvs_rev":1,"schedule_rev":0,"webhook_rev":0,"available_updates":{"stable":{"version":"1.3.3"}},"reset_reason":1},"wifi":{"sta_ip":"192.168.178.89","status":"got ip","ssid":"FritzBox 8 2.4","rssi":-62},"ws":{"connected":false}}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, 3551.682, res.Pm0.Aenergy.Total)
|
||||
assert.Equal(t, 1780.1, res.Pm0.Apower)
|
||||
}
|
||||
|
||||
{
|
||||
// Shelly Pro 3EM
|
||||
var res Gen2StatusResponse
|
||||
|
||||
jsonstr := `{"ble":{},"bthome":{"errors":["bluetooth_disabled"]},"cloud":{"connected":true},"em1:0":{"id":0,"current":3.705,"voltage":242.8,"act_power":598.9,"aprt_power":900.6,"pf":0.66,"freq":50.0,"calibration":"factory"},"em1:1":{"id":1,"current":0.194,"voltage":242.8,"act_power":0.0,"aprt_power":47.2,"pf":0.00,"freq":50.0,"calibration":"factory"},"em1:2":{"id":2,"current":0.027,"voltage":242.8,"act_power":0.0,"aprt_power":6.6,"pf":0.00,"freq":50.0,"calibration":"factory"},"em1data:0":{"id":0,"total_act_energy":3458.24,"total_act_ret_energy":1605.24},"em1data:1":{"id":1,"total_act_energy":2768.67,"total_act_ret_energy":25.49},"em1data:2":{"id":2,"total_act_energy":3.09,"total_act_ret_energy":0.71},"eth":{"ip":null},"modbus":{},"mqtt":{"connected":false},"sys":{"mac":"FCE8C0DBA850","restart_required":false,"time":"19:46","unixtime":1731404780,"uptime":563,"ram_size":247148,"ram_free":110596,"fs_size":524288,"fs_free":176128,"cfg_rev":21,"kvs_rev":0,"schedule_rev":3,"webhook_rev":1,"available_updates":{},"reset_reason":3},"temperature:0":{"id": 0,"tC":39.0, "tF":102.2},"wifi":{"sta_ip":"192.168.40.174","status":"got ip","ssid":"IoT","rssi":-67},"ws":{"connected":false}}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
// Channel 0 (1)
|
||||
assert.Equal(t, 598.9, res.Em0.ActPower)
|
||||
assert.Equal(t, 3.705, res.Em0.Current)
|
||||
assert.Equal(t, 242.8, res.Em0.Voltage)
|
||||
assert.Equal(t, 3458.24, res.Em0Data.TotalActEnergy)
|
||||
assert.Equal(t, 1605.24, res.Em0Data.TotalActRetEnergy)
|
||||
// Channel 1 (2)
|
||||
assert.Equal(t, 0.0, res.Em1.ActPower)
|
||||
assert.Equal(t, 0.194, res.Em1.Current)
|
||||
assert.Equal(t, 242.8, res.Em1.Voltage)
|
||||
assert.Equal(t, 2768.67, res.Em1Data.TotalActEnergy)
|
||||
assert.Equal(t, 25.49, res.Em1Data.TotalActRetEnergy)
|
||||
// Channel 2 (3)
|
||||
assert.Equal(t, 0.0, res.Em2.ActPower)
|
||||
assert.Equal(t, 0.027, res.Em2.Current)
|
||||
assert.Equal(t, 242.8, res.Em2.Voltage)
|
||||
assert.Equal(t, 3.09, res.Em2Data.TotalActEnergy)
|
||||
assert.Equal(t, 0.71, res.Em2Data.TotalActRetEnergy)
|
||||
}
|
||||
|
||||
{
|
||||
// Shelly Pro EM-50 channel 0 + 1
|
||||
var res Gen2StatusResponse
|
||||
|
||||
jsonstr := `{"ble":{},"bthome":{"errors":["bluetooth_disabled"]},"cloud":{"connected":true},"em1:0":{"id":0,"current":1.473,"voltage":226.9,"act_power":-332.2,"aprt_power":335.0,"pf":0.99,"freq":50.0,"calibration":"factory"},"em1:1":{"id":1,"current":0.428,"voltage":227.0,"act_power":-38.5,"aprt_power":97.4,"pf":0.38,"freq":50.0,"calibration":"factory"},"em1data:0":{"id":0,"total_act_energy":1264.15,"total_act_ret_energy":144792.28},"em1data:1":{"id":1,"total_act_energy":48002.83,"total_act_ret_energy":33241.59},"eth":{"ip":null},"modbus":{},"mqtt":{"connected":false},"switch:0":{"id":0, "source":"HTTP_in", "output":false,"temperature":{"tC":46.4, "tF":115.5}},"sys":{"mac":"08F9E0E8AF2C","restart_required":false,"time":"10:42","unixtime":1742809323,"uptime":3671372,"ram_size":249680,"ram_free":107492,"fs_size":524288,"fs_free":188416,"cfg_rev":12,"kvs_rev":0,"schedule_rev":1,"webhook_rev":0,"available_updates":{"beta":{"version":"1.5.1-beta2"}},"reset_reason":3},"wifi":{"sta_ip":"192.168.1.120","status":"got ip","ssid":"Spaetzlewerk","rssi":-61},"ws":{"connected":false}}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
// Channel 0 (1)
|
||||
assert.Equal(t, -332.2, res.Em0.ActPower)
|
||||
assert.Equal(t, 1.473, res.Em0.Current)
|
||||
assert.Equal(t, 226.9, res.Em0.Voltage)
|
||||
assert.Equal(t, 1264.15, res.Em0Data.TotalActEnergy)
|
||||
assert.Equal(t, 144792.28, res.Em0Data.TotalActRetEnergy)
|
||||
// Channel 1 (2)
|
||||
assert.Equal(t, -38.5, res.Em1.ActPower)
|
||||
assert.Equal(t, 0.428, res.Em1.Current)
|
||||
assert.Equal(t, 227.0, res.Em1.Voltage)
|
||||
assert.Equal(t, 48002.83, res.Em1Data.TotalActEnergy)
|
||||
assert.Equal(t, 33241.59, res.Em1Data.TotalActRetEnergy)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,12 +8,12 @@ import (
|
|||
)
|
||||
|
||||
type Switch struct {
|
||||
*Connection
|
||||
conn *Connection
|
||||
}
|
||||
|
||||
func NewSwitch(conn *Connection) *Switch {
|
||||
res := &Switch{
|
||||
Connection: conn,
|
||||
conn: conn,
|
||||
}
|
||||
|
||||
return res
|
||||
|
|
@ -23,31 +23,30 @@ func NewSwitch(conn *Connection) *Switch {
|
|||
func (sh *Switch) CurrentPower() (float64, error) {
|
||||
var power float64
|
||||
|
||||
d := sh.Connection
|
||||
switch d.gen {
|
||||
switch sh.conn.gen {
|
||||
case 0, 1:
|
||||
var res Gen1StatusResponse
|
||||
uri := fmt.Sprintf("%s/status", d.uri)
|
||||
if err := d.GetJSON(uri, &res); err != nil {
|
||||
var res Gen1Status
|
||||
uri := fmt.Sprintf("%s/status", sh.conn.uri)
|
||||
if err := sh.conn.GetJSON(uri, &res); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case d.channel < len(res.Meters):
|
||||
power = res.Meters[d.channel].Power
|
||||
case d.channel < len(res.EMeters):
|
||||
power = res.EMeters[d.channel].Power
|
||||
case sh.conn.channel < len(res.Meters):
|
||||
power = res.Meters[sh.conn.channel].Power
|
||||
case sh.conn.channel < len(res.EMeters):
|
||||
power = res.EMeters[sh.conn.channel].Power
|
||||
default:
|
||||
return 0, errors.New("invalid channel, missing power meter")
|
||||
}
|
||||
|
||||
default:
|
||||
var res Gen2StatusResponse
|
||||
if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil {
|
||||
if err := sh.conn.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch d.channel {
|
||||
switch sh.conn.channel {
|
||||
case 1:
|
||||
power = res.Switch1.Apower + res.Pm1.Apower + res.Em1.ActPower
|
||||
case 2:
|
||||
|
|
@ -63,17 +62,16 @@ func (sh *Switch) CurrentPower() (float64, error) {
|
|||
|
||||
// Enabled implements the api.Charger interface
|
||||
func (sh *Switch) Enabled() (bool, error) {
|
||||
d := sh.Connection
|
||||
switch d.gen {
|
||||
switch sh.conn.gen {
|
||||
case 0, 1:
|
||||
var res Gen1SwitchResponse
|
||||
uri := fmt.Sprintf("%s/relay/%d", d.uri, d.channel)
|
||||
err := d.GetJSON(uri, &res)
|
||||
uri := fmt.Sprintf("%s/relay/%d", sh.conn.uri, sh.conn.channel)
|
||||
err := sh.conn.GetJSON(uri, &res)
|
||||
return res.Ison, err
|
||||
|
||||
default:
|
||||
var res Gen2SwitchResponse
|
||||
err := d.execGen2Cmd("Switch.GetStatus", false, &res)
|
||||
var res Gen2SwitchStatus
|
||||
err := sh.conn.execGen2Cmd("Switch.GetStatus", false, &res)
|
||||
return res.Output, err
|
||||
}
|
||||
}
|
||||
|
|
@ -83,16 +81,15 @@ func (sh *Switch) Enable(enable bool) error {
|
|||
var err error
|
||||
onoff := map[bool]string{true: "on", false: "off"}
|
||||
|
||||
d := sh.Connection
|
||||
switch d.gen {
|
||||
switch sh.conn.gen {
|
||||
case 0, 1:
|
||||
var res Gen1SwitchResponse
|
||||
uri := fmt.Sprintf("%s/relay/%d?turn=%s", d.uri, d.channel, onoff[enable])
|
||||
err = d.GetJSON(uri, &res)
|
||||
uri := fmt.Sprintf("%s/relay/%d?turn=%s", sh.conn.uri, sh.conn.channel, onoff[enable])
|
||||
err = sh.conn.GetJSON(uri, &res)
|
||||
|
||||
default:
|
||||
var res Gen2SwitchResponse
|
||||
err = d.execGen2Cmd("Switch.Set", enable, &res)
|
||||
var res Gen2SwitchStatus
|
||||
err = sh.conn.execGen2Cmd("Switch.Set", enable, &res)
|
||||
}
|
||||
|
||||
return err
|
||||
|
|
@ -102,33 +99,32 @@ func (sh *Switch) Enable(enable bool) error {
|
|||
func (sh *Switch) TotalEnergy() (float64, error) {
|
||||
var energy float64
|
||||
|
||||
d := sh.Connection
|
||||
switch d.gen {
|
||||
switch sh.conn.gen {
|
||||
case 0, 1:
|
||||
var res Gen1StatusResponse
|
||||
uri := fmt.Sprintf("%s/status", d.uri)
|
||||
if err := d.GetJSON(uri, &res); err != nil {
|
||||
var res Gen1Status
|
||||
uri := fmt.Sprintf("%s/status", sh.conn.uri)
|
||||
if err := sh.conn.GetJSON(uri, &res); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case d.channel < len(res.Meters):
|
||||
energy = res.Meters[d.channel].Total
|
||||
case d.channel < len(res.EMeters):
|
||||
energy = res.EMeters[d.channel].Total
|
||||
case sh.conn.channel < len(res.Meters):
|
||||
energy = res.Meters[sh.conn.channel].Total
|
||||
case sh.conn.channel < len(res.EMeters):
|
||||
energy = res.EMeters[sh.conn.channel].Total
|
||||
default:
|
||||
return 0, errors.New("invalid channel, missing power meter")
|
||||
}
|
||||
|
||||
energy = gen1Energy(d.devicetype, energy)
|
||||
energy = gen1Energy(sh.conn.model, energy)
|
||||
|
||||
default:
|
||||
var res Gen2StatusResponse
|
||||
if err := d.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil {
|
||||
if err := sh.conn.execGen2Cmd("Shelly.GetStatus", false, &res); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch d.channel {
|
||||
switch sh.conn.channel {
|
||||
case 1:
|
||||
energy = res.Switch1.Aenergy.Total + res.Pm1.Aenergy.Total + res.Em1Data.TotalActEnergy - res.Em1Data.TotalActRetEnergy
|
||||
case 2:
|
||||
|
|
|
|||
|
|
@ -1,91 +1,15 @@
|
|||
package shelly
|
||||
|
||||
// Shelly api homepage
|
||||
// https://shelly-api-docs.shelly.cloud/#common-http-api
|
||||
// DeviceInfo is the common /shelly endpoint response
|
||||
// https://shelly-api-docs.shelly.cloud/gen1/#shelly
|
||||
// https://shelly-api-docs.shelly.cloud/gen2/ComponentsAndServices/Shelly#http-endpoint-shelly
|
||||
type DeviceInfo struct {
|
||||
Mac string `json:"mac"`
|
||||
Gen int `json:"gen"`
|
||||
Id string `json:"id"`
|
||||
Model string `json:"model"`
|
||||
Type string `json:"type"`
|
||||
Mac string `json:"mac"`
|
||||
App string `json:"app"`
|
||||
Auth bool `json:"auth"`
|
||||
AuthEn bool `json:"auth_en"`
|
||||
NumMeters int `json:"num_meters"`
|
||||
Profile string `json:"profile"`
|
||||
}
|
||||
|
||||
type Gen2RpcPost struct {
|
||||
Id int `json:"id"`
|
||||
On bool `json:"on"`
|
||||
Src string `json:"src"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
type Gen2SwitchResponse struct {
|
||||
Output bool `json:"output"`
|
||||
}
|
||||
|
||||
type Gen2Switch struct {
|
||||
Apower float64
|
||||
Aenergy struct {
|
||||
Total float64
|
||||
}
|
||||
}
|
||||
|
||||
type Gen2Em struct {
|
||||
Current float64 `json:"current"`
|
||||
Voltage float64 `json:"voltage"`
|
||||
ActPower float64 `json:"act_power"`
|
||||
}
|
||||
|
||||
type Gen2EmData struct {
|
||||
TotalActEnergy float64 `json:"total_act_energy"`
|
||||
TotalActRetEnergy float64 `json:"total_act_ret_energy"`
|
||||
}
|
||||
|
||||
type Gen2StatusResponse struct {
|
||||
Switch0 Gen2Switch `json:"switch:0"`
|
||||
Switch1 Gen2Switch `json:"switch:1"`
|
||||
Switch2 Gen2Switch `json:"switch:2"`
|
||||
Pm0 Gen2Switch `json:"pm1:0"`
|
||||
Pm1 Gen2Switch `json:"pm2:1"`
|
||||
Pm2 Gen2Switch `json:"pm3:2"`
|
||||
// additional shelly Pro EM meter JSON response
|
||||
TotalPower float64 `json:"total_act_power"`
|
||||
CurrentA float64 `json:"a_current"`
|
||||
CurrentB float64 `json:"b_current"`
|
||||
CurrentC float64 `json:"c_current"`
|
||||
VoltageA float64 `json:"a_voltage"`
|
||||
VoltageB float64 `json:"b_voltage"`
|
||||
VoltageC float64 `json:"c_voltage"`
|
||||
PowerA float64 `json:"a_act_power"`
|
||||
PowerB float64 `json:"b_act_power"`
|
||||
PowerC float64 `json:"c_act_power"`
|
||||
Em0 Gen2Em `json:"em1:0"`
|
||||
Em1 Gen2Em `json:"em1:1"`
|
||||
Em2 Gen2Em `json:"em1:2"`
|
||||
Em0Data Gen2EmData `json:"em1data:0"`
|
||||
Em1Data Gen2EmData `json:"em1data:1"`
|
||||
Em2Data Gen2EmData `json:"em1data:2"`
|
||||
}
|
||||
|
||||
type Gen2EmDataStatusResponse struct {
|
||||
TotalEnergy float64 `json:"total_act"`
|
||||
}
|
||||
|
||||
type Gen1SwitchResponse struct {
|
||||
Ison bool
|
||||
}
|
||||
|
||||
type Gen1StatusResponse struct {
|
||||
Meters []struct {
|
||||
Power float64
|
||||
Total float64
|
||||
}
|
||||
// Shelly EM meter JSON response
|
||||
EMeters []struct {
|
||||
Power float64
|
||||
Total float64
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,113 +8,6 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Test Gen1StatusResponse response
|
||||
func TestUnmarshalGen1StatusResponse(t *testing.T) {
|
||||
{
|
||||
// Shelly 1 PM channel 0 (1)
|
||||
var res Gen1StatusResponse
|
||||
|
||||
jsonstr := `{"wifi_sta":{"connected":true,"ssid":"XXXX-WLAN","ip":"192.168.178.XXX","rssi":-54},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"17:59","unixtime":1676134770,"serial":2437,"has_update":true,"mac":"84CCA8XXXXXXX","cfg_changed_cnt":1,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"overpower":false,"source":"http"}],"meters":[{"power":4711.12,"overpower":0.00,"is_valid":true,"timestamp":1676138370,"counters":[0.000, 0.000, 0.000],"total":6472513}],"inputs":[{"input":0,"event":"","event_cnt":0}],"temperature":16.79,"overtemperature":false,"tmp":{"tC":16.79,"tF":62.22, "is_valid":true},"temperature_status":"Normal","ext_sensors":{},"ext_temperature":{},"ext_humidity":{},"update":{"status":"pending","has_update":true,"new_version":"20221108-153925/v1.12.1-1PM-fix-g2821131","old_version":"20220209-094317/v1.11.8-g8c7bb8d"},"ram_total":50456,"ram_free":37056,"fs_size":233681,"fs_free":149094,"uptime":17284290}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, 107875.21666666666, gen1Energy("SHSW-PM", res.Meters[0].Total))
|
||||
assert.Equal(t, 4711.12, res.Meters[0].Power)
|
||||
}
|
||||
|
||||
{
|
||||
// Shelly 1 channel 0 (1)
|
||||
var res Gen1StatusResponse
|
||||
|
||||
jsonstr := `{"wifi_sta":{"connected":true,"ssid":"XXXX-WLAN","ip":"192.168.178.XXX","rssi":-57},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"19:25","unixtime":1676139913,"serial":959,"has_update":true,"mac":"E8DB8XXXXXX","cfg_changed_cnt":1,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"source":"timer"}],"meters":[{"power":81.5,"is_valid":true}],"inputs":[{"input":0,"event":"","event_cnt":0}],"ext_sensors":{},"ext_temperature":{},"ext_humidity":{},"update":{"status":"pending","has_update":true,"new_version":"20221027-091427/v1.12.1-ga9117d3","old_version":"20211109-124958/v1.11.7-g682a0db"},"ram_total":50880,"ram_free":38796,"fs_size":233681,"fs_free":151102,"uptime":20319391}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, 0.0, gen1Energy("SHSW-1", res.Meters[0].Total))
|
||||
assert.Equal(t, 81.5, res.Meters[0].Power)
|
||||
}
|
||||
|
||||
{
|
||||
// Shelly EM channel 0 (1)
|
||||
var res Gen1StatusResponse
|
||||
|
||||
jsonstr := `{"wifi_sta":{"connected":true,"ssid":"XXXX","ip":"192.168.178.XXX","rssi":-55},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"11:16","unixtime":1676110566,"serial":21580,"has_update":false,"mac":"C45BXXXXX","cfg_changed_cnt":0,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"overpower":false,"is_valid":true,"source":"input"}],"emeters":[{"power":-620.34,"reactive":714.48,"pf":-0.66,"voltage":235.68,"is_valid":true,"total":401472.9,"total_returned":653673.7},{"power":0.00,"reactive":0.00,"pf":0.00,"voltage":235.68,"is_valid":true,"total":173411.3,"total_returned":294.2}],"update":{"status":"idle","has_update":false,"new_version":"20221027-105518/v1.12.1-ga9117d3","old_version":"20221027-105518/v1.12.1-ga9117d3"},"ram_total":51072,"ram_free":35660,"fs_size":233681,"fs_free":156373,"uptime":2226140}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, 401472.9, gen1Energy("SHEM", res.EMeters[0].Total))
|
||||
assert.Equal(t, -620.34, res.EMeters[0].Power)
|
||||
}
|
||||
}
|
||||
|
||||
// Test Gen2+ status responses
|
||||
func TestUnmarshalGen2StatusResponse(t *testing.T) {
|
||||
{
|
||||
// Shelly Pro 1PM channel 0 (1)
|
||||
var res Gen2StatusResponse
|
||||
|
||||
jsonstr := `{"ble":{},"cloud":{"connected":true},"eth":{"ip":null},"input:0":{"id":0,"state":false},"input:1":{"id":1,"state":false},"mqtt":{"connected":false},"switch:0":{"id":0, "source":"HTTP", "output":false, "apower":47.11, "voltage":232.0, "current":0.000, "pf":0.00, "aenergy":{"total":5.125,"by_minute":[0.000,0.000,0.000],"minute_ts":1675718520},"temperature":{"tC":25.3, "tF":77.5}},"sys":{"mac":"30C6F78BB4D8","restart_required":false,"time":"22:22","unixtime":1675718522,"uptime":45070,"ram_size":234204,"ram_free":137716,"fs_size":524288,"fs_free":172032,"cfg_rev":13,"kvs_rev":1,"schedule_rev":0,"webhook_rev":0,"available_updates":{"beta":{"version":"0.13.0-beta3"}}},"wifi":{"sta_ip":"192.168.178.64","status":"got ip","ssid":"***","rssi":-62},"ws":{"connected":false}}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, 5.125, res.Switch0.Aenergy.Total)
|
||||
assert.Equal(t, 47.11, res.Switch0.Apower)
|
||||
}
|
||||
|
||||
{
|
||||
// Shelly PM Mini Gen3 channel 0 (1)
|
||||
var res Gen2StatusResponse
|
||||
|
||||
jsonstr := `{"ble":{},"cloud":{"connected":true},"mqtt":{"connected":false},"pm1:0":{"id":0, "voltage":239.9, "current":7.434, "apower":1780.1 ,"freq":50.1,"aenergy":{"total":3551.682,"by_minute":[15234.772,29611.247,29825.821],"minute_ts":1719917850},"ret_aenergy":{"total":0.000,"by_minute":[0.000,0.000,0.000],"minute_ts":1719917850}},"sys":{"mac":"84FCE638D818","restart_required":false,"time":"12:57","unixtime":1719917851,"uptime":62328,"ram_size":261744,"ram_free":151436,"fs_size":1048576,"fs_free":712704,"cfg_rev":10,"kvs_rev":1,"schedule_rev":0,"webhook_rev":0,"available_updates":{"stable":{"version":"1.3.3"}},"reset_reason":1},"wifi":{"sta_ip":"192.168.178.89","status":"got ip","ssid":"FritzBox 8 2.4","rssi":-62},"ws":{"connected":false}}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, 3551.682, res.Pm0.Aenergy.Total)
|
||||
assert.Equal(t, 1780.1, res.Pm0.Apower)
|
||||
}
|
||||
|
||||
{
|
||||
// Shelly Pro 3EM
|
||||
var res Gen2StatusResponse
|
||||
|
||||
jsonstr := `{"ble":{},"bthome":{"errors":["bluetooth_disabled"]},"cloud":{"connected":true},"em1:0":{"id":0,"current":3.705,"voltage":242.8,"act_power":598.9,"aprt_power":900.6,"pf":0.66,"freq":50.0,"calibration":"factory"},"em1:1":{"id":1,"current":0.194,"voltage":242.8,"act_power":0.0,"aprt_power":47.2,"pf":0.00,"freq":50.0,"calibration":"factory"},"em1:2":{"id":2,"current":0.027,"voltage":242.8,"act_power":0.0,"aprt_power":6.6,"pf":0.00,"freq":50.0,"calibration":"factory"},"em1data:0":{"id":0,"total_act_energy":3458.24,"total_act_ret_energy":1605.24},"em1data:1":{"id":1,"total_act_energy":2768.67,"total_act_ret_energy":25.49},"em1data:2":{"id":2,"total_act_energy":3.09,"total_act_ret_energy":0.71},"eth":{"ip":null},"modbus":{},"mqtt":{"connected":false},"sys":{"mac":"FCE8C0DBA850","restart_required":false,"time":"19:46","unixtime":1731404780,"uptime":563,"ram_size":247148,"ram_free":110596,"fs_size":524288,"fs_free":176128,"cfg_rev":21,"kvs_rev":0,"schedule_rev":3,"webhook_rev":1,"available_updates":{},"reset_reason":3},"temperature:0":{"id": 0,"tC":39.0, "tF":102.2},"wifi":{"sta_ip":"192.168.40.174","status":"got ip","ssid":"IoT","rssi":-67},"ws":{"connected":false}}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
// Channel 0 (1)
|
||||
assert.Equal(t, 598.9, res.Em0.ActPower)
|
||||
assert.Equal(t, 3.705, res.Em0.Current)
|
||||
assert.Equal(t, 242.8, res.Em0.Voltage)
|
||||
assert.Equal(t, 3458.24, res.Em0Data.TotalActEnergy)
|
||||
assert.Equal(t, 1605.24, res.Em0Data.TotalActRetEnergy)
|
||||
// Channel 1 (2)
|
||||
assert.Equal(t, 0.0, res.Em1.ActPower)
|
||||
assert.Equal(t, 0.194, res.Em1.Current)
|
||||
assert.Equal(t, 242.8, res.Em1.Voltage)
|
||||
assert.Equal(t, 2768.67, res.Em1Data.TotalActEnergy)
|
||||
assert.Equal(t, 25.49, res.Em1Data.TotalActRetEnergy)
|
||||
// Channel 2 (3)
|
||||
assert.Equal(t, 0.0, res.Em2.ActPower)
|
||||
assert.Equal(t, 0.027, res.Em2.Current)
|
||||
assert.Equal(t, 242.8, res.Em2.Voltage)
|
||||
assert.Equal(t, 3.09, res.Em2Data.TotalActEnergy)
|
||||
assert.Equal(t, 0.71, res.Em2Data.TotalActRetEnergy)
|
||||
}
|
||||
|
||||
{
|
||||
// Shelly Pro EM-50 channel 0 + 1
|
||||
var res Gen2StatusResponse
|
||||
|
||||
jsonstr := `{"ble":{},"bthome":{"errors":["bluetooth_disabled"]},"cloud":{"connected":true},"em1:0":{"id":0,"current":1.473,"voltage":226.9,"act_power":-332.2,"aprt_power":335.0,"pf":0.99,"freq":50.0,"calibration":"factory"},"em1:1":{"id":1,"current":0.428,"voltage":227.0,"act_power":-38.5,"aprt_power":97.4,"pf":0.38,"freq":50.0,"calibration":"factory"},"em1data:0":{"id":0,"total_act_energy":1264.15,"total_act_ret_energy":144792.28},"em1data:1":{"id":1,"total_act_energy":48002.83,"total_act_ret_energy":33241.59},"eth":{"ip":null},"modbus":{},"mqtt":{"connected":false},"switch:0":{"id":0, "source":"HTTP_in", "output":false,"temperature":{"tC":46.4, "tF":115.5}},"sys":{"mac":"08F9E0E8AF2C","restart_required":false,"time":"10:42","unixtime":1742809323,"uptime":3671372,"ram_size":249680,"ram_free":107492,"fs_size":524288,"fs_free":188416,"cfg_rev":12,"kvs_rev":0,"schedule_rev":1,"webhook_rev":0,"available_updates":{"beta":{"version":"1.5.1-beta2"}},"reset_reason":3},"wifi":{"sta_ip":"192.168.1.120","status":"got ip","ssid":"Spaetzlewerk","rssi":-61},"ws":{"connected":false}}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
// Channel 0 (1)
|
||||
assert.Equal(t, -332.2, res.Em0.ActPower)
|
||||
assert.Equal(t, 1.473, res.Em0.Current)
|
||||
assert.Equal(t, 226.9, res.Em0.Voltage)
|
||||
assert.Equal(t, 1264.15, res.Em0Data.TotalActEnergy)
|
||||
assert.Equal(t, 144792.28, res.Em0Data.TotalActRetEnergy)
|
||||
// Channel 1 (2)
|
||||
assert.Equal(t, -38.5, res.Em1.ActPower)
|
||||
assert.Equal(t, 0.428, res.Em1.Current)
|
||||
assert.Equal(t, 227.0, res.Em1.Voltage)
|
||||
assert.Equal(t, 48002.83, res.Em1Data.TotalActEnergy)
|
||||
assert.Equal(t, 33241.59, res.Em1Data.TotalActRetEnergy)
|
||||
}
|
||||
}
|
||||
|
||||
// Test Shelly device info
|
||||
func TestUnmarshalDeviceInfoResponse(t *testing.T) {
|
||||
{
|
||||
|
|
@ -124,7 +17,8 @@ func TestUnmarshalDeviceInfoResponse(t *testing.T) {
|
|||
jsonstr := `{"name":null,"id":"shellypro3em-fce8c0dba900","mac":"FCE8C0DBA900","slot":1,"model":"SPEM-003CEBEU","gen":2,"fw_id":"20241011-114455/1.4.4-g6d2a586","ver":"1.4.4","app":"Pro3EM","auth_en":false,"auth_domain":null,"profile":"monophase"}`
|
||||
require.NoError(t, json.Unmarshal([]byte(jsonstr), &res))
|
||||
|
||||
assert.Equal(t, "Pro3EM", res.App)
|
||||
assert.Equal(t, 2, res.Gen)
|
||||
assert.Equal(t, "SPEM-003CEBEU", res.Model)
|
||||
assert.Equal(t, "monophase", res.Profile)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue