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.

333 lines
7.3 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. var scene *models.Scene
  186. if body.SceneName != "" {
  187. scene, err = config.SceneRepository().FindName(ctxOf(c), body.SceneName)
  188. if err != nil {
  189. return nil, err
  190. }
  191. if body.DurationMS < 0 {
  192. body.DurationMS = 0
  193. }
  194. body.StartTime = time.Now()
  195. body.SceneID = scene.ID
  196. } else {
  197. scene, err = config.SceneRepository().Find(ctxOf(c), body.SceneID)
  198. if err != nil {
  199. return nil, err
  200. }
  201. if body.DurationMS < 0 {
  202. body.DurationMS = 0
  203. }
  204. body.StartTime = time.Now()
  205. body.SceneName = scene.Name
  206. }
  207. pushMode := c.Query("push") == "true"
  208. for i := range devices {
  209. if pushMode {
  210. devices[i].SceneAssignments = append(devices[i].SceneAssignments, body)
  211. } else {
  212. devices[i].SceneAssignments = []models.DeviceSceneAssignment{body}
  213. }
  214. }
  215. config.PublishChannel <- devices
  216. err = config.DeviceRepository().SaveMany(ctxOf(c), 0, devices)
  217. if err != nil {
  218. return nil, err
  219. }
  220. return withSceneState(devices), nil
  221. }))
  222. r.DELETE("/:fetch/scene", handler(func(c *gin.Context) (interface{}, error) {
  223. devices, err := fetchDevices(ctxOf(c), c.Param("fetch"))
  224. if err != nil {
  225. return nil, err
  226. }
  227. if len(devices) == 0 {
  228. return []models.Device{}, nil
  229. }
  230. for i := range devices {
  231. devices[i].SceneAssignments = []models.DeviceSceneAssignment{}
  232. }
  233. config.PublishChannel <- devices
  234. err = config.DeviceRepository().SaveMany(ctxOf(c), 0, devices)
  235. if err != nil {
  236. return nil, err
  237. }
  238. return withSceneState(devices), nil
  239. }))
  240. r.DELETE("/:fetch", handler(func(c *gin.Context) (interface{}, error) {
  241. id, err := strconv.Atoi(c.Param("fetch"))
  242. if err != nil {
  243. return nil, err
  244. }
  245. device, err := config.DeviceRepository().Find(ctxOf(c), id)
  246. if err != nil {
  247. return nil, err
  248. }
  249. if c.Query("forgetmenot") != "true" {
  250. bridge, err := config.BridgeRepository().Find(ctxOf(c), device.BridgeID)
  251. if err != nil {
  252. return nil, err
  253. }
  254. driver, err := config.DriverProvider().Provide(bridge.Driver)
  255. if err != nil {
  256. return nil, err
  257. }
  258. forgettableDriver, ok := driver.(models.ForgettableDriver)
  259. if !ok {
  260. return nil, models.ErrCannotForget
  261. }
  262. err = forgettableDriver.ForgetDevice(ctxOf(c), bridge, *device)
  263. if err != nil {
  264. return nil, err
  265. }
  266. }
  267. err = config.DeviceRepository().Delete(ctxOf(c), device)
  268. if err != nil {
  269. return nil, err
  270. }
  271. return device, nil
  272. }))
  273. }
  274. func withSceneState(devices []models.Device) []models.Device {
  275. res := make([]models.Device, 0, len(devices))
  276. for _, device := range devices {
  277. device.SceneState = publisher.Global().SceneState(device.ID)
  278. res = append(res, device)
  279. }
  280. return res
  281. }