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.

268 lines
5.7 KiB

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