You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

191 lines
3.6 KiB

2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
2 weeks ago
  1. package mill
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. lucifer3 "git.aiterp.net/lucifer3/server"
  7. "git.aiterp.net/lucifer3/server/device"
  8. "git.aiterp.net/lucifer3/server/events"
  9. "git.aiterp.net/lucifer3/server/internal/gentools"
  10. "log"
  11. "math"
  12. "net/http"
  13. "strings"
  14. "sync"
  15. "time"
  16. )
  17. type WifiBridge struct {
  18. ID string
  19. IP string
  20. state *device.State
  21. desiredState device.State
  22. mx sync.Mutex
  23. started bool
  24. bus *lucifer3.EventBus
  25. }
  26. func (w *WifiBridge) SetBus(bus *lucifer3.EventBus) {
  27. w.bus = bus
  28. }
  29. func (w *WifiBridge) SetState(id string, state device.State) bool {
  30. if !strings.HasPrefix(id, w.ID) {
  31. return false
  32. }
  33. w.desiredState = state
  34. return true
  35. }
  36. func (w *WifiBridge) Start() {
  37. if err := w.refresh(); err != nil {
  38. w.bus.RunEvent(deviceFailed(w.ID, err))
  39. return
  40. }
  41. w.started = true
  42. go func() {
  43. defer func() {
  44. recover()
  45. log.Printf("Mill: %s stopped unexpectedly", w.ID)
  46. w.started = false
  47. }()
  48. for {
  49. time.Sleep(1 * time.Minute)
  50. if err := w.refresh(); err != nil {
  51. w.bus.RunEvent(deviceFailed(w.ID, err))
  52. break
  53. }
  54. }
  55. }()
  56. }
  57. func (w *WifiBridge) IsStarted() bool {
  58. return w.started
  59. }
  60. func (w *WifiBridge) refresh() error {
  61. w.mx.Lock()
  62. defer w.mx.Unlock()
  63. temp, err := w.getTemperature()
  64. if err != nil {
  65. return err
  66. }
  67. if w.state == nil {
  68. w.state = &device.State{
  69. Temperature: gentools.Ptr(temp),
  70. }
  71. w.bus.RunEvent(events.DeviceReady{ID: w.ID})
  72. w.bus.RunEvent(events.HardwareMetadata{ID: w.ID, Icon: "heater"})
  73. w.bus.RunEvent(events.HardwareState{
  74. ID: w.ID,
  75. InternalName: "Mill heater @ " + w.IP,
  76. SupportFlags: device.SFlagTemperature,
  77. State: *w.state,
  78. })
  79. }
  80. if w.desiredState.Temperature != nil && math.Abs(temp-*w.desiredState.Temperature) >= 0.1 {
  81. if err := w.writeTemperature(); err != nil {
  82. return err
  83. }
  84. }
  85. return nil
  86. }
  87. func (w *WifiBridge) getTemperature() (float64, error) {
  88. body, err := json.Marshal(getSetTemperatureBody{Type: "Normal"})
  89. if err != nil {
  90. return 0.0, err
  91. }
  92. req, err := http.NewRequest(
  93. "GET",
  94. fmt.Sprintf("http://%s/set-temperature", w.IP),
  95. bytes.NewReader(body),
  96. )
  97. if err != nil {
  98. return 0.0, err
  99. }
  100. res, err := http.DefaultClient.Do(req)
  101. if err != nil {
  102. return 0.0, err
  103. }
  104. var resBody getSetTemperatureResponse
  105. err = json.NewDecoder(res.Body).Decode(&resBody)
  106. if err != nil || resBody.Status != "ok" {
  107. return 0.0, fmt.Errorf("refresh failed %s (bad response from query)", w.ID)
  108. }
  109. return resBody.Value, nil
  110. }
  111. func (w *WifiBridge) writeTemperature() error {
  112. body, err := json.Marshal(postSetTemperatureBody{
  113. Type: "Normal",
  114. Value: *w.state.Temperature,
  115. })
  116. if err != nil {
  117. return err
  118. }
  119. req, err := http.NewRequest(
  120. "POST",
  121. fmt.Sprintf("http://%s/set-temperature", w.IP),
  122. bytes.NewReader(body),
  123. )
  124. if err != nil {
  125. return err
  126. }
  127. res, err := http.DefaultClient.Do(req)
  128. if err != nil {
  129. return err
  130. }
  131. var resBody postSetTemperatureResponse
  132. err = json.NewDecoder(res.Body).Decode(&resBody)
  133. if err != nil || resBody.Status != "ok" {
  134. return fmt.Errorf("refresh failed %s (bad response from query)", w.ID)
  135. }
  136. w.bus.RunEvent(events.HardwareState{
  137. ID: w.ID,
  138. InternalName: "Mill heater @ " + w.IP,
  139. SupportFlags: device.SFlagTemperature,
  140. State: *w.state,
  141. })
  142. return nil
  143. }
  144. type getSetTemperatureBody struct {
  145. Type string `json:"type"`
  146. }
  147. type postSetTemperatureBody struct {
  148. Type string `json:"type"`
  149. Value float64 `json:"value"`
  150. }
  151. type getSetTemperatureResponse struct {
  152. Value float64 `json:"value"`
  153. Status string `json:"status"`
  154. }
  155. type postSetTemperatureResponse struct {
  156. Status string `json:"status"`
  157. }