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.

195 lines
4.9 KiB

3 years ago
  1. package main
  2. import (
  3. "bufio"
  4. "context"
  5. "encoding/json"
  6. "flag"
  7. "fmt"
  8. "git.aiterp.net/lucifer/new-server/app/config"
  9. "git.aiterp.net/lucifer/new-server/internal/color"
  10. "git.aiterp.net/lucifer/new-server/models"
  11. "log"
  12. "os"
  13. "sort"
  14. "strconv"
  15. "strings"
  16. "time"
  17. )
  18. var flagDriver = flag.String("driver", string(models.DTNanoLeaf), "The bridge driver to use")
  19. var flagAddress = flag.String("address", "127.0.0.1", "The bridge's address")
  20. var flagToken = flag.String("token", "", "The bridge's access token / api key / login")
  21. var flagPair = flag.Bool("pair", false, "Try to pair with the bridge.")
  22. var flagSearch = flag.Bool("search", false, "Search for devices first.")
  23. var flagSearchTimeout = flag.Duration("search-timeout", time.Second*3, "Timeout for device search.")
  24. func main() {
  25. flag.Parse()
  26. // Find drivers
  27. driver, err := config.DriverProvider().Provide(models.DriverKind(*flagDriver))
  28. if err != nil {
  29. log.Fatalln("Failed to find driver:", err)
  30. }
  31. // Find bridge
  32. bridges, err := driver.SearchBridge(context.Background(), *flagAddress, *flagToken, !*flagPair)
  33. if err != nil {
  34. log.Fatalln("Failed to search bridge:", err)
  35. }
  36. if len(bridges) == 0 {
  37. log.Fatalln("No bridges found")
  38. }
  39. bridge := bridges[0]
  40. if !*flagPair {
  41. bridge.Token = *flagToken
  42. } else {
  43. log.Println("New token:", bridge.Token)
  44. }
  45. ch := config.EventChannel
  46. go func() {
  47. err := driver.Run(context.Background(), bridge, ch)
  48. if err != nil {
  49. log.Fatalln("Run bridge stopped:", err)
  50. }
  51. }()
  52. time.Sleep(time.Second)
  53. // List devices
  54. var devices []models.Device
  55. if *flagSearch {
  56. devices, err = driver.SearchDevices(context.Background(), bridge, *flagSearchTimeout)
  57. if err != nil {
  58. log.Fatalln("Failed to search devices:", err)
  59. }
  60. } else {
  61. devices, err = driver.ListDevices(context.Background(), bridge)
  62. if err != nil {
  63. log.Fatalln("Failed to list devices:", err)
  64. }
  65. }
  66. idMap := make(map[string]int)
  67. nextId := len(devices) + 1
  68. for i := range devices {
  69. devices[i].ID = i + 1
  70. idMap[devices[i].InternalID] = i + 1
  71. }
  72. go func() {
  73. reader := bufio.NewReader(os.Stdin)
  74. _, _ = fmt.Fprintln(os.Stderr, "Format: [id1,id2,...] [on|off] [color] [intensity]")
  75. for _, device := range devices {
  76. _, _ = fmt.Fprintf(os.Stderr, "Device: %d - %s %+v\n", device.ID, device.InternalID, device.Capabilities)
  77. }
  78. _, _ = fmt.Fprintln(os.Stderr, "Format: [id1,id2,...] [on|off] [color]")
  79. for {
  80. text, _ := reader.ReadString('\n')
  81. text = strings.Trim(text, "\t  \r\n")
  82. if text == "search" {
  83. _, _ = driver.SearchDevices(context.Background(), bridge, time.Second)
  84. }
  85. if text == "list" || text == "search" || text == "json" {
  86. devices, err = driver.ListDevices(context.Background(), bridge)
  87. if err != nil {
  88. log.Fatalln("Failed to list devices:", err)
  89. }
  90. for i, device := range devices {
  91. if extId, ok := idMap[device.InternalID]; ok {
  92. devices[i].ID = extId
  93. } else {
  94. idMap[device.InternalID] = nextId
  95. devices[i].ID = nextId
  96. nextId += 1
  97. }
  98. if text != "json" {
  99. _, _ = fmt.Fprintf(os.Stderr, "Device: %d - %s %+v\n", device.ID, device.InternalID, device.Capabilities)
  100. }
  101. }
  102. }
  103. if text == "json" {
  104. j, _ := json.MarshalIndent(devices, "", " ")
  105. fmt.Println(string(j))
  106. }
  107. tokens := strings.Split(text, " ")
  108. if len(tokens) < 4 {
  109. continue
  110. }
  111. color, err := color.Parse(tokens[2])
  112. if err != nil {
  113. _, _ = fmt.Fprintln(os.Stderr, "Invalid color:", err)
  114. continue
  115. }
  116. intensity, _ := strconv.ParseFloat(tokens[3], 64)
  117. power := strings.ToLower(tokens[1]) == "on"
  118. idsStr := strings.Split(tokens[0], ",")
  119. ids := make([]int, 0, len(idsStr))
  120. for _, idStr := range idsStr {
  121. if idStr == "*" {
  122. ids = append(ids[:0], -1)
  123. break
  124. }
  125. id, err := strconv.Atoi(idStr)
  126. if err != nil {
  127. continue
  128. }
  129. ids = append(ids, id)
  130. }
  131. updatedDevices := devices[:0:0]
  132. for _, device := range devices {
  133. for _, id := range ids {
  134. if id == -1 || id == device.ID {
  135. if (color.IsKelvin() && device.HasCapability(models.DCColorKelvin)) || (color.IsHueSat() && device.HasCapability(models.DCColorHS)) {
  136. device.State.Color = color
  137. }
  138. if device.HasCapability(models.DCPower) {
  139. device.State.Power = power
  140. }
  141. if device.HasCapability(models.DCIntensity) {
  142. device.State.Intensity = intensity
  143. }
  144. updatedDevices = append(updatedDevices, device)
  145. }
  146. }
  147. }
  148. if len(updatedDevices) > 0 {
  149. err := driver.Publish(context.Background(), bridge, updatedDevices)
  150. if err != nil {
  151. log.Fatalln("Publish to bridge failed:", err)
  152. return
  153. }
  154. }
  155. }
  156. }()
  157. for event := range ch {
  158. _, _ = fmt.Fprintf(os.Stderr, "Event %s", event.Name)
  159. keys := make([]string, 0, 8)
  160. for key := range event.Payload {
  161. keys = append(keys, key)
  162. }
  163. sort.Strings(keys)
  164. for _, key := range keys {
  165. _, _ = fmt.Fprintf(os.Stderr, " %s=%#+v", key, event.Payload[key])
  166. }
  167. _, _ = fmt.Fprint(os.Stderr, "\n")
  168. }
  169. }