package mill import ( "bytes" "encoding/json" "fmt" lucifer3 "git.aiterp.net/lucifer3/server" "git.aiterp.net/lucifer3/server/device" "git.aiterp.net/lucifer3/server/events" "git.aiterp.net/lucifer3/server/internal/gentools" "log" "math" "net/http" "strings" "sync" "time" ) type WifiBridge struct { ID string IP string state *device.State mx sync.Mutex started bool bus *lucifer3.EventBus } func (w *WifiBridge) SetBus(bus *lucifer3.EventBus) { w.bus = bus } func (w *WifiBridge) SetState(id string, state device.State) bool { if !strings.HasPrefix(id, w.ID) { return false } return true } func (w *WifiBridge) Start() { if err := w.refresh(); err != nil { w.bus.RunEvent(deviceFailed(w.ID, err)) return } w.started = true go func() { defer func() { recover() log.Printf("Mill: %s stopped unexpectedly", w.ID) w.started = false }() for { time.Sleep(1 * time.Minute) if err := w.refresh(); err != nil { w.bus.RunEvent(deviceFailed(w.ID, err)) break } } }() } func (w *WifiBridge) IsStarted() bool { return w.started } func (w *WifiBridge) refresh() error { w.mx.Lock() defer w.mx.Unlock() temp, err := w.getTemperature() if err != nil { return err } if w.state != nil { w.state = &device.State{ Temperature: gentools.Ptr(temp), } w.bus.RunEvent(events.DeviceReady{ID: w.ID}) w.bus.RunEvent(events.HardwareMetadata{ID: w.ID, Icon: "heater"}) w.bus.RunEvent(events.HardwareState{ ID: w.ID, InternalName: "Mill heater @ " + w.IP, SupportFlags: device.SFlagTemperature, State: *w.state, }) } w.state.Temperature = gentools.Ptr(*w.state.Temperature - math.Mod(*w.state.Temperature, 0.5)) if math.Abs(temp-*w.state.Temperature) >= 0.1 { if err := w.writeTemperature(); err != nil { return err } } return nil } func (w *WifiBridge) getTemperature() (float64, error) { body, err := json.Marshal(getSetTemperatureBody{Type: "Normal"}) if err != nil { return 0.0, err } req, err := http.NewRequest( "GET", fmt.Sprintf("http://%s/set-temperature", w.IP), bytes.NewReader(body), ) if err != nil { return 0.0, err } res, err := http.DefaultClient.Do(req) if err != nil { return 0.0, err } var resBody getSetTemperatureResponse err = json.NewDecoder(res.Body).Decode(&resBody) if err != nil || resBody.Status != "ok" { return 0.0, fmt.Errorf("refresh failed %s (bad response from query)", w.ID) } return resBody.Value, nil } func (w *WifiBridge) writeTemperature() error { body, err := json.Marshal(postSetTemperatureBody{ Type: "Normal", Value: *w.state.Temperature, }) if err != nil { return err } req, err := http.NewRequest( "POST", fmt.Sprintf("http://%s/set-temperature", w.IP), bytes.NewReader(body), ) if err != nil { return err } res, err := http.DefaultClient.Do(req) if err != nil { return err } var resBody postSetTemperatureResponse err = json.NewDecoder(res.Body).Decode(&resBody) if err != nil || resBody.Status != "ok" { return fmt.Errorf("refresh failed %s (bad response from query)", w.ID) } w.bus.RunEvent(events.HardwareState{ ID: w.ID, InternalName: "Mill heater @ " + w.IP, SupportFlags: device.SFlagTemperature, State: *w.state, }) return nil } type getSetTemperatureBody struct { Type string `json:"type"` } type postSetTemperatureBody struct { Type string `json:"type"` Value float64 `json:"value"` } type getSetTemperatureResponse struct { Value float64 `json:"value"` Status string `json:"status"` } type postSetTemperatureResponse struct { Status string `json:"status"` }