Files
2025-03-27 13:32:30 +01:00

188 lines
4.7 KiB
Go

package main
import (
"encoding/json"
"flag"
"fmt"
log "github.com/sirupsen/logrus"
"io"
"net/http"
"strings"
"sync"
"time"
)
var (
shellyFlag = flag.String("s", "", "list shelly ips, comma seperated")
debugFlag = flag.Bool("d", false, "enable debug mode")
wg sync.WaitGroup
)
const (
SleepMinutes = 5
)
type ShellyStatus struct {
WifiSta struct {
Connected bool `json:"connected"`
Ssid string `json:"ssid"`
Ip string `json:"ip"`
Rssi int `json:"rssi"`
} `json:"wifi_sta"`
Cloud struct {
Enabled bool `json:"enabled"`
Connected bool `json:"connected"`
} `json:"cloud"`
Mqtt struct {
Connected bool `json:"connected"`
} `json:"mqtt"`
Time string `json:"time"`
UnixTime int `json:"unixtime"`
Serial int `json:"serial"`
HasUpdate bool `json:"has_update"`
Mac string `json:"mac"`
CfgChangedCnt int `json:"cfg_changed_cnt"`
ActionsStats struct {
Skipped int `json:"skipped"`
} `json:"actions_stats"`
Thermostats []struct {
Pos float64 `json:"pos"`
TargetT struct {
Enabled bool `json:"enabled"`
Value float64 `json:"value"`
ValueOp float64 `json:"value_op"`
Units string `json:"units"`
} `json:"target_t"`
Tmp struct {
Value float64 `json:"value"`
Units string `json:"units"`
IsValid bool `json:"is_valid"`
} `json:"tmp"`
Schedule bool `json:"schedule"`
ScheduleProfile int `json:"schedule_profile"`
BoostMinutes int `json:"boost_minutes"`
WindowOpen bool `json:"window_open"`
} `json:"thermostats"`
Calibrated bool `json:"calibrated"`
Bat struct {
Value int `json:"value"`
Voltage float64 `json:"voltage"`
} `json:"bat"`
Charger bool `json:"charger"`
Update struct {
Status string `json:"status"`
HasUpdate bool `json:"has_update"`
NewVersion string `json:"new_version"`
OldVersion string `json:"old_version"`
BetaVersion interface{} `json:"beta_version"`
} `json:"update"`
RamTotal int `json:"ram_total"`
RamFree int `json:"ram_free"`
FsSize int `json:"fs_size"`
FsFree int `json:"fs_free"`
Uptime int `json:"uptime"`
FwInfo struct {
Device string `json:"device"`
Fw string `json:"fw"`
} `json:"fw_info"`
PsMode int `json:"ps_mode"`
DbgFlags int `json:"dbg_flags"`
}
type ShellyRebootResponse struct {
Ok bool `json:"ok"`
}
func main() {
flag.Parse()
if *debugFlag {
log.SetLevel(log.DebugLevel)
}
if *shellyFlag == "" {
log.Fatal("no shelly ips specified")
}
wg := new(sync.WaitGroup)
for _, ip := range strings.Split(*shellyFlag, ",") {
wg.Add(1)
go func() {
err := checkShelly(ip)
if err != nil {
log.Fatalf("shelly worker %s failed: %v", ip, err)
}
}()
}
wg.Wait()
}
func checkShelly(ip string) error {
defer wg.Done()
log.Infof("shelly worker %s started", ip)
for {
resp, err := http.Get(fmt.Sprintf("http://%s/status", ip))
if err != nil {
log.Warningf("shelly %s status check failed: %v", ip, err)
time.Sleep(time.Duration(SleepMinutes) * time.Minute)
continue
}
if resp.StatusCode != 200 {
log.Warningf("shelly %s status check failed: %v", ip, resp.Status)
time.Sleep(time.Duration(SleepMinutes) * time.Minute)
continue
}
respBin, err := io.ReadAll(resp.Body)
if err != nil {
log.Warningf("shelly %s response cannot be read: %v", ip, err)
time.Sleep(time.Duration(SleepMinutes) * time.Minute)
continue
}
shellyStatus := new(ShellyStatus)
if err := json.Unmarshal(respBin, shellyStatus); err != nil {
log.Warningf("shelly %s response cannot be parsed: %v", ip, err)
time.Sleep(time.Duration(SleepMinutes) * time.Minute)
continue
}
if !shellyStatus.Calibrated {
log.Infof("shelly %s calibrated: %v", ip, shellyStatus.Calibrated)
rebootResp, err := http.Get(fmt.Sprintf("http://%s/reboot", ip))
if err != nil {
log.Warningf("shelly %s reboot request failed: %v", ip, err)
time.Sleep(time.Duration(SleepMinutes) * time.Minute)
continue
}
if rebootResp.StatusCode != 200 {
log.Warningf("shelly %s reboot request failed: %v", ip, resp.Status)
time.Sleep(time.Duration(SleepMinutes) * time.Minute)
continue
}
rebootBin, err := io.ReadAll(rebootResp.Body)
if err != nil {
log.Warningf("shelly %s reboot response cannot be read: %v", ip, err)
time.Sleep(time.Duration(SleepMinutes) * time.Minute)
continue
}
shellyReboot := new(ShellyRebootResponse)
if err := json.Unmarshal(rebootBin, shellyReboot); err != nil {
log.Warningf("shelly %s reboot response cannot be parsed: %v", ip, err)
time.Sleep(time.Duration(SleepMinutes) * time.Minute)
continue
}
log.Infof("shelly %s reboot response: %v", ip, shellyReboot.Ok)
}
time.Sleep(time.Minute * 5)
}
}