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.

190 lines
3.5 KiB

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