Skip to content

Commit

Permalink
feat: support powerstrips like HS300
Browse files Browse the repository at this point in the history
fix #8
  • Loading branch information
fffonion committed Dec 7, 2020
1 parent 9199b34 commit dd56d46
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 38 deletions.
62 changes: 43 additions & 19 deletions exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,40 +77,64 @@ func (k *Exporter) Describe(ch chan<- *prometheus.Desc) {
}

func (k *Exporter) Collect(ch chan<- prometheus.Metric) {
s := k.client.SystemService()
s := k.client.SystemService(nil)
r, err := s.GetSysInfo()

if err != nil {
ch <- prometheus.MustNewConstMetric(k.metricsUp, prometheus.GaugeValue,
0)
return
}
alias := r.Alias

ch <- prometheus.MustNewConstMetric(k.metricsRelayState, prometheus.GaugeValue,
float64(r.RelayState), alias)
float64(r.RelayState), r.Alias)
ch <- prometheus.MustNewConstMetric(k.metricsOnTime, prometheus.CounterValue,
float64(r.OnTime), alias)
float64(r.OnTime), r.Alias)
ch <- prometheus.MustNewConstMetric(k.metricsRssi, prometheus.GaugeValue,
float64(r.RSSI), alias)
float64(r.RSSI), r.Alias)

aliases := map[string]string{}
emeterContexts := []*kasa.KasaRequestContext{
nil, // a nil context, represent the single plug or the parent strip
}
// iterrate over every child plug in a power strip
for _, children := range r.Children {
aliases[children.ID] = children.Alias
emeterContexts = append(emeterContexts, &kasa.KasaRequestContext{
ChildIDs: []string{children.ID},
})

ch <- prometheus.MustNewConstMetric(k.metricsRelayState, prometheus.GaugeValue,
float64(children.State), children.Alias)
}

if s.EmeterSupported(r) {
m := k.client.EmeterService()
r, err := m.GetRealtime()
for _, ctx := range emeterContexts {
m := k.client.EmeterService(ctx)
re, err := m.GetRealtime()

if err != nil {
ch <- prometheus.MustNewConstMetric(k.metricsUp, prometheus.GaugeValue,
0)
return
alias := r.Alias
if ctx != nil {
alias = aliases[ctx.ChildIDs[0]]
}

// TODO: only set the child up to 0 on error
if err != nil {
ch <- prometheus.MustNewConstMetric(k.metricsUp, prometheus.GaugeValue,
0)
return
}

ch <- prometheus.MustNewConstMetric(k.metricsCurrent, prometheus.GaugeValue,
float64(re.Current), alias)
ch <- prometheus.MustNewConstMetric(k.metricsVoltage, prometheus.GaugeValue,
float64(re.Voltage), alias)
ch <- prometheus.MustNewConstMetric(k.metricsPowerLoad, prometheus.GaugeValue,
float64(re.Power), alias)
ch <- prometheus.MustNewConstMetric(k.metricsPowerTotal, prometheus.CounterValue,
float64(re.Total), alias)
}
ch <- prometheus.MustNewConstMetric(k.metricsCurrent, prometheus.GaugeValue,
float64(r.Current), alias)
ch <- prometheus.MustNewConstMetric(k.metricsVoltage, prometheus.GaugeValue,
float64(r.Voltage), alias)
ch <- prometheus.MustNewConstMetric(k.metricsPowerLoad, prometheus.GaugeValue,
float64(r.Power), alias)
ch <- prometheus.MustNewConstMetric(k.metricsPowerTotal, prometheus.CounterValue,
float64(r.Total), alias)

}

ch <- prometheus.MustNewConstMetric(k.metricsUp, prometheus.GaugeValue,
Expand Down
5 changes: 3 additions & 2 deletions kasa/emeter.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package kasa

type KasaClientEmeterService struct {
c *KasaClient
c *KasaClient
ctx *KasaRequestContext
}

type GetRealtimeRequest struct {
Expand Down Expand Up @@ -34,7 +35,7 @@ func (s *KasaClientEmeterService) GetRealtime() (*GetRealtimeResponse, error) {
response := GetRealtimeResponse{
TotalWh: -1,
}
err := s.c.RPC("emeter", "get_realtime", GetRealtimeRequest{}, &response)
err := s.c.RPC("emeter", "get_realtime", s.ctx, GetRealtimeRequest{}, &response)

response.Normalize()

Expand Down
25 changes: 18 additions & 7 deletions kasa/kasa.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ type KasaClientConfig struct {
Host string
}

type KasaRequestContext struct {
ChildIDs []string `json:"child_ids"`
}

type RPCResponse struct {
ErrCode int `json:"err_code"`
}
Expand Down Expand Up @@ -129,14 +133,19 @@ func (c *KasaClient) Request(payload interface{}) ([]byte, error) {
return buf, nil
}

func (c *KasaClient) RPC(service string, cmd string, payload interface{}, out interface{}) error {
payload = map[string]interface{}{
func (c *KasaClient) RPC(service string, cmd string,
ctx *KasaRequestContext, payload interface{}, out interface{}) error {

body := map[string]interface{}{
service: map[string]interface{}{
cmd: payload,
},
}
if ctx != nil {
body["context"] = ctx
}

response, err := c.Request(payload)
response, err := c.Request(body)
if err != nil {
return err
}
Expand All @@ -163,14 +172,16 @@ func (c *KasaClient) RPC(service string, cmd string, payload interface{}, out in

}

func (c *KasaClient) SystemService() *KasaClientSystemService {
func (c *KasaClient) SystemService(ctx *KasaRequestContext) *KasaClientSystemService {
return &KasaClientSystemService{
c: c,
c: c,
ctx: ctx,
}
}

func (c *KasaClient) EmeterService() *KasaClientEmeterService {
func (c *KasaClient) EmeterService(ctx *KasaRequestContext) *KasaClientEmeterService {
return &KasaClientEmeterService{
c: c,
c: c,
ctx: ctx,
}
}
28 changes: 18 additions & 10 deletions kasa/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,34 @@ package kasa
import "strings"

type KasaClientSystemService struct {
c *KasaClient
c *KasaClient
ctx *KasaRequestContext
}

type GetSysInfoRequest struct {
}

type SysInfoChildren struct {
ID string `mapstructure:"id"`
State int `mapstructure:"state"`
Alias string `mapstructure:"alias"`
}

type GetSysInfoResponse struct {
MAC string `mapstructure:"mac"`
Model string `mapstructure:"model"`
Alias string `mapstructure:"alias"`
Feature string `mapstructure:"feature"`
RelayState int `mapstructure:"relay_state"`
RSSI int `mapstructure:"rssi"`
LEDOff int `mapstructure:"led_off"`
OnTime int `mapstructure:"on_time"`
MAC string `mapstructure:"mac"`
Model string `mapstructure:"model"`
Alias string `mapstructure:"alias"`
Feature string `mapstructure:"feature"`
RelayState int `mapstructure:"relay_state"`
RSSI int `mapstructure:"rssi"`
LEDOff int `mapstructure:"led_off"`
OnTime int `mapstructure:"on_time"`
Children []SysInfoChildren `mapstructure:"children"`
}

func (s *KasaClientSystemService) GetSysInfo() (*GetSysInfoResponse, error) {
var response GetSysInfoResponse
err := s.c.RPC("system", "get_sysinfo", GetSysInfoRequest{}, &response)
err := s.c.RPC("system", "get_sysinfo", s.ctx, GetSysInfoRequest{}, &response)

return &response, err
}
Expand Down

0 comments on commit dd56d46

Please sign in to comment.