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.

319 lines
7.0 KiB

  1. package api
  2. import (
  3. "context"
  4. "git.aiterp.net/lucifer/new-server/app/config"
  5. "git.aiterp.net/lucifer/new-server/app/services/publisher"
  6. "git.aiterp.net/lucifer/new-server/models"
  7. "github.com/gin-gonic/gin"
  8. "log"
  9. "strconv"
  10. "time"
  11. )
  12. func fetchDevices(ctx context.Context, fetchStr string) ([]models.Device, error) {
  13. kind, value := models.ParseFetchString(fetchStr)
  14. return config.DeviceRepository().FetchByReference(ctx, kind, value)
  15. }
  16. func Devices(r gin.IRoutes) {
  17. r.GET("", handler(func(c *gin.Context) (interface{}, error) {
  18. devices, err := config.DeviceRepository().FetchByReference(ctxOf(c), models.RKAll, "")
  19. if err != nil {
  20. return nil, err
  21. }
  22. return withSceneState(devices), nil
  23. }))
  24. r.GET("/:fetch", handler(func(c *gin.Context) (interface{}, error) {
  25. devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
  26. if err != nil {
  27. return nil, err
  28. }
  29. return withSceneState(devices), nil
  30. }))
  31. r.PUT("", handler(func(c *gin.Context) (interface{}, error) {
  32. var body []struct {
  33. Fetch string `json:"fetch"`
  34. SetState models.NewDeviceState `json:"setState"`
  35. }
  36. err := parseBody(c, &body)
  37. if err != nil {
  38. return nil, err
  39. }
  40. set := make(map[int]bool)
  41. changed := make([]models.Device, 0, 64)
  42. for _, job := range body {
  43. devices, err := fetchDevices(ctxOf(c), job.Fetch)
  44. if err != nil {
  45. return nil, err
  46. }
  47. if len(devices) == 0 {
  48. return []models.Device{}, nil
  49. }
  50. for i := range devices {
  51. if set[devices[i].ID] {
  52. continue
  53. }
  54. err := devices[i].SetState(job.SetState)
  55. if err != nil {
  56. return nil, err
  57. }
  58. set[devices[i].ID] = true
  59. changed = append(changed, devices[i])
  60. }
  61. }
  62. config.PublishChannel <- changed
  63. go func() {
  64. for _, device := range changed {
  65. err := config.DeviceRepository().Save(context.Background(), &device, models.SMState)
  66. if err != nil {
  67. log.Println("Failed to save device for state:", err)
  68. continue
  69. }
  70. }
  71. }()
  72. return withSceneState(changed), nil
  73. }))
  74. r.PUT("/:fetch", handler(func(c *gin.Context) (interface{}, error) {
  75. update := models.DeviceUpdate{}
  76. err := parseBody(c, &update)
  77. if err != nil {
  78. return nil, err
  79. }
  80. devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
  81. if err != nil {
  82. return nil, err
  83. }
  84. if len(devices) == 0 {
  85. return []models.Device{}, nil
  86. }
  87. for i := range devices {
  88. devices[i].ApplyUpdate(update)
  89. err := config.DeviceRepository().Save(context.Background(), &devices[i], models.SMProperties)
  90. if err != nil {
  91. log.Println("Failed to save device for state:", err)
  92. continue
  93. }
  94. }
  95. return withSceneState(devices), nil
  96. }))
  97. r.PUT("/:fetch/state", handler(func(c *gin.Context) (interface{}, error) {
  98. state := models.NewDeviceState{}
  99. err := parseBody(c, &state)
  100. if err != nil {
  101. return nil, err
  102. }
  103. devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
  104. if err != nil {
  105. return nil, err
  106. }
  107. if len(devices) == 0 {
  108. return []models.Device{}, nil
  109. }
  110. for i := range devices {
  111. err := devices[i].SetState(state)
  112. if err != nil {
  113. return nil, err
  114. }
  115. }
  116. config.PublishChannel <- devices
  117. err = config.DeviceRepository().SaveMany(ctxOf(c), models.SMState, devices)
  118. if err != nil {
  119. log.Println("Failed to save devices states")
  120. }
  121. return withSceneState(devices), nil
  122. }))
  123. r.PUT("/:fetch/tags", handler(func(c *gin.Context) (interface{}, error) {
  124. var body struct {
  125. Add []string `json:"add"`
  126. Remove []string `json:"remove"`
  127. }
  128. err := parseBody(c, &body)
  129. if err != nil {
  130. return nil, err
  131. }
  132. devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
  133. if err != nil {
  134. return nil, err
  135. }
  136. if len(devices) == 0 {
  137. return []models.Device{}, nil
  138. }
  139. for i := range devices {
  140. device := &devices[i]
  141. for _, tag := range body.Add {
  142. found := false
  143. for _, tag2 := range device.Tags {
  144. if tag == tag2 {
  145. found = true
  146. break
  147. }
  148. }
  149. if !found {
  150. device.Tags = append(device.Tags, tag)
  151. }
  152. }
  153. for _, tag := range body.Remove {
  154. index := -1
  155. for i, tag2 := range device.Tags {
  156. if tag == tag2 {
  157. index = i
  158. }
  159. }
  160. if index == -1 {
  161. continue
  162. }
  163. device.Tags = append(device.Tags[:index], device.Tags[index+1:]...)
  164. }
  165. err = config.DeviceRepository().Save(ctxOf(c), device, models.SMTags)
  166. if err != nil {
  167. return nil, err
  168. }
  169. }
  170. return withSceneState(devices), nil
  171. }))
  172. r.PUT("/:fetch/scene", handler(func(c *gin.Context) (interface{}, error) {
  173. var body models.DeviceSceneAssignment
  174. err := parseBody(c, &body)
  175. if err != nil {
  176. return nil, err
  177. }
  178. devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
  179. if err != nil {
  180. return nil, err
  181. }
  182. if len(devices) == 0 {
  183. return []models.Device{}, nil
  184. }
  185. _, err = config.SceneRepository().Find(ctxOf(c), body.SceneID)
  186. if err != nil {
  187. return nil, err
  188. }
  189. if body.DurationMS < 0 {
  190. body.DurationMS = 0
  191. }
  192. body.StartTime = time.Now()
  193. pushMode := c.Query("push") == "true"
  194. for i := range devices {
  195. if pushMode {
  196. devices[i].SceneAssignments = append(devices[i].SceneAssignments, body)
  197. } else {
  198. devices[i].SceneAssignments = []models.DeviceSceneAssignment{body}
  199. }
  200. }
  201. config.PublishChannel <- devices
  202. err = config.DeviceRepository().SaveMany(ctxOf(c), 0, devices)
  203. if err != nil {
  204. return nil, err
  205. }
  206. return withSceneState(devices), nil
  207. }))
  208. r.DELETE("/:fetch/scene", handler(func(c *gin.Context) (interface{}, error) {
  209. devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
  210. if err != nil {
  211. return nil, err
  212. }
  213. if len(devices) == 0 {
  214. return []models.Device{}, nil
  215. }
  216. for i := range devices {
  217. devices[i].SceneAssignments = []models.DeviceSceneAssignment{}
  218. }
  219. config.PublishChannel <- devices
  220. err = config.DeviceRepository().SaveMany(ctxOf(c), 0, devices)
  221. if err != nil {
  222. return nil, err
  223. }
  224. return withSceneState(devices), nil
  225. }))
  226. r.DELETE("/:fetch", handler(func(c *gin.Context) (interface{}, error) {
  227. id, err := strconv.Atoi(c.Param("fetch"))
  228. if err != nil {
  229. return nil, err
  230. }
  231. device, err := config.DeviceRepository().Find(ctxOf(c), id)
  232. if err != nil {
  233. return nil, err
  234. }
  235. if c.Query("forgetmenot") != "true" {
  236. bridge, err := config.BridgeRepository().Find(ctxOf(c), device.BridgeID)
  237. if err != nil {
  238. return nil, err
  239. }
  240. driver, err := config.DriverProvider().Provide(bridge.Driver)
  241. if err != nil {
  242. return nil, err
  243. }
  244. forgettableDriver, ok := driver.(models.ForgettableDriver)
  245. if !ok {
  246. return nil, models.ErrCannotForget
  247. }
  248. err = forgettableDriver.ForgetDevice(ctxOf(c), bridge, *device)
  249. if err != nil {
  250. return nil, err
  251. }
  252. }
  253. err = config.DeviceRepository().Delete(ctxOf(c), device)
  254. if err != nil {
  255. return nil, err
  256. }
  257. return device, nil
  258. }))
  259. }
  260. func withSceneState(devices []models.Device) []models.Device {
  261. res := make([]models.Device, 0, len(devices))
  262. for _, device := range devices {
  263. device.SceneState = publisher.Global().SceneState(device.ID)
  264. res = append(res, device)
  265. }
  266. return res
  267. }