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.

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