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.

273 lines
6.0 KiB

2 years ago
1 year ago
2 years ago
2 years ago
2 years ago
  1. package nanoleaf
  2. import (
  3. "encoding/binary"
  4. "git.aiterp.net/lucifer3/server/device"
  5. "git.aiterp.net/lucifer3/server/internal/color"
  6. "time"
  7. )
  8. type EffectInfo struct {
  9. EffectsList []string `json:"effectsList"`
  10. Select string `json:"select"`
  11. }
  12. type PanelLayout struct {
  13. GlobalOrientation GlobalOrientation `json:"globalOrientation"`
  14. Data PanelLayoutData `json:"layout"`
  15. }
  16. type GlobalOrientation struct {
  17. Value int `json:"value"`
  18. Max int `json:"max"`
  19. Min int `json:"min"`
  20. }
  21. type PanelLayoutData struct {
  22. NumPanels int `json:"numPanels"`
  23. SideLength int `json:"sideLength"`
  24. PositionData []PositionData `json:"positionData"`
  25. }
  26. type PositionData struct {
  27. PanelID uint16 `json:"panelId"`
  28. X int `json:"x"`
  29. Y int `json:"y"`
  30. O int `json:"o"`
  31. ShapeType int `json:"shapeType"`
  32. }
  33. type StateBool struct {
  34. Value bool `json:"value"`
  35. }
  36. type StateInt struct {
  37. Value int `json:"value"`
  38. Max int `json:"max"`
  39. Min int `json:"min"`
  40. }
  41. type State struct {
  42. Brightness StateInt `json:"brightness"`
  43. ColorMode string `json:"colorMode"`
  44. Ct StateInt `json:"ct"`
  45. Hue StateInt `json:"hue"`
  46. On StateBool `json:"on"`
  47. Sat StateInt `json:"sat"`
  48. }
  49. type Overview struct {
  50. Name string `json:"name"`
  51. SerialNumber string `json:"serialNo"`
  52. Manufacturer string `json:"manufacturer"`
  53. FirmwareVersion string `json:"firmwareVersion"`
  54. HardwareVersion string `json:"hardwareVersion"`
  55. Model string `json:"model"`
  56. Effects EffectInfo `json:"effects"`
  57. PanelLayout PanelLayout `json:"panelLayout"`
  58. State State `json:"state"`
  59. }
  60. type DeviceInfo struct {
  61. SerialNumber string `json:"serialNumber"`
  62. HardwareVersion string `json:"hardwareVersion"`
  63. FirmwareVersion string `json:"firmwareVersion"`
  64. BootloaderVersion string `json:"bootloaderVersion"`
  65. ModelNumber string `json:"modelNumber"`
  66. }
  67. type TokenResponse struct {
  68. Token string `json:"auth_token"`
  69. }
  70. type PanelUpdate []byte
  71. func (u *PanelUpdate) Add(message [8]byte) {
  72. if len(*u) < 2 {
  73. *u = make([]byte, 2, 10)
  74. }
  75. binary.BigEndian.PutUint16(*u, binary.BigEndian.Uint16(*u)+1)
  76. *u = append(*u, message[:]...)
  77. }
  78. func (u *PanelUpdate) Len() int {
  79. if len(*u) < 2 {
  80. return 0
  81. }
  82. return int(binary.BigEndian.Uint16(*u))
  83. }
  84. type PanelEventMessage []byte
  85. func (remote PanelEventMessage) Count() int {
  86. return int(binary.BigEndian.Uint16(remote[0:]))
  87. }
  88. func (remote PanelEventMessage) ValidateLength() bool {
  89. return len(remote) >= (2 + remote.Count()*5)
  90. }
  91. func (remote PanelEventMessage) PanelID(idx int) uint16 {
  92. return binary.BigEndian.Uint16(remote[2+(idx*5):])
  93. }
  94. func (remote PanelEventMessage) TouchType(idx int) int {
  95. value := int(remote[2+(idx*5)])
  96. return (value & 0b11100000) >> 5
  97. }
  98. func (remote PanelEventMessage) TouchStrength(idx int) int {
  99. value := int(remote[2+(idx*5)])
  100. return (value & 0b00011110) >> 1
  101. }
  102. func (remote PanelEventMessage) SwipedFromPanelID(idx int) uint16 {
  103. return binary.BigEndian.Uint16(remote[2+(idx*5)+3:])
  104. }
  105. var shapeTypeMap = map[int]string{
  106. 0: "Legacy Triangle",
  107. 1: "Rhythm",
  108. 2: "Square",
  109. 3: "Control Square Master",
  110. 4: "Control Square Passive",
  111. 7: "Hexagon",
  112. 8: "Triangle",
  113. 9: "Mini Triangle",
  114. 12: "Shapes Controller",
  115. 15: "Element Corner",
  116. }
  117. var shapeIconMap = map[int]string{
  118. 0: "shape_triangle",
  119. 1: "shape_triangle",
  120. 2: "shape_square",
  121. 3: "shape_square",
  122. 4: "shape_square",
  123. 7: "shape_hexagon",
  124. 8: "shape_triangle",
  125. 9: "shape_triangle",
  126. 12: "shape_hexagon",
  127. 15: "shape_hexagon_corner",
  128. }
  129. var shapeWidthMap = map[int]int{
  130. 0: 150,
  131. 1: -1,
  132. 2: 100,
  133. 3: 100,
  134. 4: 100,
  135. 7: 67,
  136. 8: 134,
  137. 9: 67,
  138. 12: -1,
  139. }
  140. var httpMessage = []byte(`{ "write": { "command": "display", "animType": "extControl", "extControlVersion": "v2" }}`)
  141. type panel struct {
  142. ID uint16
  143. FullID string
  144. On bool
  145. Intensity float64
  146. ColorRGBA [4]byte
  147. TransitionAt time.Time
  148. O int
  149. X int
  150. Y int
  151. ShapeType int
  152. Stale bool
  153. SlowUpdates int
  154. TicksUntilSlowUpdate int
  155. }
  156. func (p *panel) message() (message [8]byte) {
  157. transitionTime := p.TransitionAt.Sub(time.Now()).Round(time.Millisecond * 100)
  158. if transitionTime > maxTransitionTime {
  159. transitionTime = maxTransitionTime
  160. } else if transitionTime < 0 {
  161. transitionTime = 0
  162. }
  163. binary.BigEndian.PutUint16(message[0:], p.ID)
  164. copy(message[2:], p.ColorRGBA[:])
  165. binary.BigEndian.PutUint16(message[6:], uint16(transitionTime/(time.Millisecond*100)))
  166. return
  167. }
  168. func (p *panel) update(colorRGBA [4]byte, transitionAt time.Time) {
  169. if p.ColorRGBA != colorRGBA {
  170. p.ColorRGBA = colorRGBA
  171. p.Stale = true
  172. p.SlowUpdates = 3
  173. p.TicksUntilSlowUpdate = 10
  174. }
  175. p.TransitionAt = transitionAt
  176. }
  177. func (p *panel) apply(change device.State, transitionTime time.Time) {
  178. if change.Power != nil {
  179. p.On = *change.Power
  180. }
  181. if change.Intensity != nil {
  182. p.Intensity = *change.Intensity
  183. }
  184. if change.Color == nil {
  185. change.Color = &color.Color{RGB: &color.RGB{Red: 255, Green: 255, Blue: 255}}
  186. }
  187. if !p.On {
  188. newColor := [4]byte{0, 0, 0, 0}
  189. if newColor != p.ColorRGBA {
  190. p.update(newColor, transitionTime)
  191. }
  192. } else {
  193. rgbColor, ok := change.Color.ToRGB()
  194. if !ok {
  195. newColor := [4]byte{255, 255, 255, 255}
  196. if newColor != p.ColorRGBA {
  197. p.update(newColor, transitionTime)
  198. }
  199. }
  200. rgb := rgbColor.RGB.AtIntensity(p.Intensity)
  201. red := byte(rgb.Red * 255.0001)
  202. green := byte(rgb.Green * 255.0001)
  203. blue := byte(rgb.Blue * 255.0001)
  204. newColor := [4]byte{red, green, blue, 255}
  205. if newColor != p.ColorRGBA {
  206. p.update(newColor, transitionTime)
  207. }
  208. }
  209. }
  210. type panelUpdate []byte
  211. func (u *panelUpdate) Add(message [8]byte) {
  212. if len(*u) < 2 {
  213. *u = make([]byte, 2, 10)
  214. }
  215. binary.BigEndian.PutUint16(*u, binary.BigEndian.Uint16(*u)+1)
  216. *u = append(*u, message[:]...)
  217. }
  218. func (u *panelUpdate) Len() int {
  219. if len(*u) < 2 {
  220. return 0
  221. }
  222. return int(binary.BigEndian.Uint16(*u))
  223. }
  224. const maxTransitionTime = time.Minute * 109