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.

188 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", o.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. }
  85. func (w *WifiBridge) getTemperature() (float64, error) {
  86. body, err := json.Marshal(getSetTemperatureBody{Type: "Normal"})
  87. if err != nil {
  88. return 0.0, err
  89. }
  90. req, err := http.NewRequest(
  91. "GET",
  92. "http://%s/set-temperature",
  93. bytes.NewReader(body),
  94. )
  95. if err != nil {
  96. return 0.0, err
  97. }
  98. res, err := http.DefaultClient.Do(req)
  99. if err != nil {
  100. return 0.0, err
  101. }
  102. var resBody getSetTemperatureResponse
  103. err = json.NewDecoder(res.Body).Decode(&resBody)
  104. if err != nil || resBody.Status != "ok" {
  105. return 0.0, fmt.Errorf("refresh failed %s (bad response from query)", w.ID)
  106. }
  107. return resBody.Value, nil
  108. }
  109. func (w *WifiBridge) writeTemperature() error {
  110. body, err := json.Marshal(postSetTemperatureBody{
  111. Type: "Normal",
  112. Value: *w.state.Temperature,
  113. })
  114. if err != nil {
  115. return err
  116. }
  117. req, err := http.NewRequest(
  118. "POST",
  119. "http://%s/set-temperature",
  120. bytes.NewReader(body),
  121. )
  122. if err != nil {
  123. return err
  124. }
  125. res, err := http.DefaultClient.Do(req)
  126. if err != nil {
  127. return err
  128. }
  129. var resBody postSetTemperatureResponse
  130. err = json.NewDecoder(res.Body).Decode(&resBody)
  131. if err != nil || resBody.Status != "ok" {
  132. return fmt.Errorf("refresh failed %s (bad response from query)", w.ID)
  133. }
  134. w.bus.RunEvent(events.HardwareState{
  135. ID: w.ID,
  136. InternalName: "Mill heater @ " + w.IP,
  137. SupportFlags: device.SFlagTemperature,
  138. State: *w.state,
  139. })
  140. return nil
  141. }
  142. type getSetTemperatureBody struct {
  143. Type string `json:"type"`
  144. }
  145. type postSetTemperatureBody struct {
  146. Type string `json:"type"`
  147. Value float64 `json:"value"`
  148. }
  149. type getSetTemperatureResponse struct {
  150. Value float64 `json:"value"`
  151. Status string `json:"status"`
  152. }
  153. type postSetTemperatureResponse struct {
  154. Status string `json:"status"`
  155. }