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.

271 lines
5.9 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. }
  116. var shapeIconMap = map[int]string{
  117. 0: "shape_triangle",
  118. 1: "shape_triangle",
  119. 2: "shape_square",
  120. 3: "shape_square",
  121. 4: "shape_square",
  122. 7: "shape_hexagon",
  123. 8: "shape_triangle",
  124. 9: "shape_triangle",
  125. 12: "shape_hexagon",
  126. }
  127. var shapeWidthMap = map[int]int{
  128. 0: 150,
  129. 1: -1,
  130. 2: 100,
  131. 3: 100,
  132. 4: 100,
  133. 7: 67,
  134. 8: 134,
  135. 9: 67,
  136. 12: -1,
  137. }
  138. var httpMessage = []byte(`{ "write": { "command": "display", "animType": "extControl", "extControlVersion": "v2" }}`)
  139. type panel struct {
  140. ID uint16
  141. FullID string
  142. On bool
  143. Intensity float64
  144. ColorRGBA [4]byte
  145. TransitionAt time.Time
  146. O int
  147. X int
  148. Y int
  149. ShapeType int
  150. Stale bool
  151. SlowUpdates int
  152. TicksUntilSlowUpdate int
  153. }
  154. func (p *panel) message() (message [8]byte) {
  155. transitionTime := p.TransitionAt.Sub(time.Now()).Round(time.Millisecond * 100)
  156. if transitionTime > maxTransitionTime {
  157. transitionTime = maxTransitionTime
  158. } else if transitionTime < 0 {
  159. transitionTime = 0
  160. }
  161. binary.BigEndian.PutUint16(message[0:], p.ID)
  162. copy(message[2:], p.ColorRGBA[:])
  163. binary.BigEndian.PutUint16(message[6:], uint16(transitionTime/(time.Millisecond*100)))
  164. return
  165. }
  166. func (p *panel) update(colorRGBA [4]byte, transitionAt time.Time) {
  167. if p.ColorRGBA != colorRGBA {
  168. p.ColorRGBA = colorRGBA
  169. p.Stale = true
  170. p.SlowUpdates = 3
  171. p.TicksUntilSlowUpdate = 10
  172. }
  173. p.TransitionAt = transitionAt
  174. }
  175. func (p *panel) apply(change device.State, transitionTime time.Time) {
  176. if change.Power != nil {
  177. p.On = *change.Power
  178. }
  179. if change.Intensity != nil {
  180. p.Intensity = *change.Intensity
  181. }
  182. if change.Color == nil {
  183. change.Color = &color.Color{RGB: &color.RGB{Red: 255, Green: 255, Blue: 255}}
  184. }
  185. if !p.On {
  186. newColor := [4]byte{0, 0, 0, 0}
  187. if newColor != p.ColorRGBA {
  188. p.update(newColor, transitionTime)
  189. }
  190. } else {
  191. rgbColor, ok := change.Color.ToRGB()
  192. if !ok {
  193. newColor := [4]byte{255, 255, 255, 255}
  194. if newColor != p.ColorRGBA {
  195. p.update(newColor, transitionTime)
  196. }
  197. }
  198. rgb := rgbColor.RGB.AtIntensity(p.Intensity)
  199. red := byte(rgb.Red * 255.0001)
  200. green := byte(rgb.Green * 255.0001)
  201. blue := byte(rgb.Blue * 255.0001)
  202. newColor := [4]byte{red, green, blue, 255}
  203. if newColor != p.ColorRGBA {
  204. p.update(newColor, transitionTime)
  205. }
  206. }
  207. }
  208. type panelUpdate []byte
  209. func (u *panelUpdate) Add(message [8]byte) {
  210. if len(*u) < 2 {
  211. *u = make([]byte, 2, 10)
  212. }
  213. binary.BigEndian.PutUint16(*u, binary.BigEndian.Uint16(*u)+1)
  214. *u = append(*u, message[:]...)
  215. }
  216. func (u *panelUpdate) Len() int {
  217. if len(*u) < 2 {
  218. return 0
  219. }
  220. return int(binary.BigEndian.Uint16(*u))
  221. }
  222. const maxTransitionTime = time.Minute * 109