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

package nanoleaf
import (
"encoding/binary"
"git.aiterp.net/lucifer3/server/device"
"git.aiterp.net/lucifer3/server/internal/color"
"time"
)
type EffectInfo struct {
EffectsList []string `json:"effectsList"`
Select string `json:"select"`
}
type PanelLayout struct {
GlobalOrientation GlobalOrientation `json:"globalOrientation"`
Data PanelLayoutData `json:"layout"`
}
type GlobalOrientation struct {
Value int `json:"value"`
Max int `json:"max"`
Min int `json:"min"`
}
type PanelLayoutData struct {
NumPanels int `json:"numPanels"`
SideLength int `json:"sideLength"`
PositionData []PositionData `json:"positionData"`
}
type PositionData struct {
PanelID uint16 `json:"panelId"`
X int `json:"x"`
Y int `json:"y"`
O int `json:"o"`
ShapeType int `json:"shapeType"`
}
type StateBool struct {
Value bool `json:"value"`
}
type StateInt struct {
Value int `json:"value"`
Max int `json:"max"`
Min int `json:"min"`
}
type State struct {
Brightness StateInt `json:"brightness"`
ColorMode string `json:"colorMode"`
Ct StateInt `json:"ct"`
Hue StateInt `json:"hue"`
On StateBool `json:"on"`
Sat StateInt `json:"sat"`
}
type Overview struct {
Name string `json:"name"`
SerialNumber string `json:"serialNo"`
Manufacturer string `json:"manufacturer"`
FirmwareVersion string `json:"firmwareVersion"`
HardwareVersion string `json:"hardwareVersion"`
Model string `json:"model"`
Effects EffectInfo `json:"effects"`
PanelLayout PanelLayout `json:"panelLayout"`
State State `json:"state"`
}
type DeviceInfo struct {
SerialNumber string `json:"serialNumber"`
HardwareVersion string `json:"hardwareVersion"`
FirmwareVersion string `json:"firmwareVersion"`
BootloaderVersion string `json:"bootloaderVersion"`
ModelNumber string `json:"modelNumber"`
}
type TokenResponse struct {
Token string `json:"auth_token"`
}
type PanelUpdate []byte
func (u *PanelUpdate) Add(message [8]byte) {
if len(*u) < 2 {
*u = make([]byte, 2, 10)
}
binary.BigEndian.PutUint16(*u, binary.BigEndian.Uint16(*u)+1)
*u = append(*u, message[:]...)
}
func (u *PanelUpdate) Len() int {
if len(*u) < 2 {
return 0
}
return int(binary.BigEndian.Uint16(*u))
}
type PanelEventMessage []byte
func (remote PanelEventMessage) Count() int {
return int(binary.BigEndian.Uint16(remote[0:]))
}
func (remote PanelEventMessage) ValidateLength() bool {
return len(remote) >= (2 + remote.Count()*5)
}
func (remote PanelEventMessage) PanelID(idx int) uint16 {
return binary.BigEndian.Uint16(remote[2+(idx*5):])
}
func (remote PanelEventMessage) TouchType(idx int) int {
value := int(remote[2+(idx*5)])
return (value & 0b11100000) >> 5
}
func (remote PanelEventMessage) TouchStrength(idx int) int {
value := int(remote[2+(idx*5)])
return (value & 0b00011110) >> 1
}
func (remote PanelEventMessage) SwipedFromPanelID(idx int) uint16 {
return binary.BigEndian.Uint16(remote[2+(idx*5)+3:])
}
var shapeTypeMap = map[int]string{
0: "Legacy Triangle",
1: "Rhythm",
2: "Square",
3: "Control Square Master",
4: "Control Square Passive",
7: "Hexagon",
8: "Triangle",
9: "Mini Triangle",
12: "Shapes Controller",
}
var shapeIconMap = map[int]string{
0: "triangle",
1: "triangle",
2: "square",
3: "square",
4: "square",
7: "hexagon",
8: "triangle",
9: "triangle",
12: "hexagon",
}
var shapeWidthMap = map[int]int{
0: 150,
1: -1,
2: 100,
3: 100,
4: 100,
7: 67,
8: 134,
9: 67,
12: -1,
}
var httpMessage = []byte(`{ "write": { "command": "display", "animType": "extControl", "extControlVersion": "v2" }}`)
type panel struct {
ID uint16
FullID string
On bool
Intensity float64
ColorRGBA [4]byte
TransitionAt time.Time
O int
X int
Y int
ShapeType int
Stale bool
SlowUpdates int
TicksUntilSlowUpdate int
}
func (p *panel) message() (message [8]byte) {
transitionTime := p.TransitionAt.Sub(time.Now()).Round(time.Millisecond * 100)
if transitionTime > maxTransitionTime {
transitionTime = maxTransitionTime
} else if transitionTime < 0 {
transitionTime = 0
}
binary.BigEndian.PutUint16(message[0:], p.ID)
copy(message[2:], p.ColorRGBA[:])
binary.BigEndian.PutUint16(message[6:], uint16(transitionTime/(time.Millisecond*100)))
return
}
func (p *panel) update(colorRGBA [4]byte, transitionAt time.Time) {
if p.ColorRGBA != colorRGBA {
p.ColorRGBA = colorRGBA
p.Stale = true
p.SlowUpdates = 3
p.TicksUntilSlowUpdate = 10
}
p.TransitionAt = transitionAt
}
func (p *panel) apply(change device.State, transitionTime time.Time) {
if change.Power != nil {
p.On = *change.Power
}
if change.Intensity != nil {
p.Intensity = *change.Intensity
}
if change.Color == nil {
change.Color = &color.Color{RGB: &color.RGB{Red: 255, Green: 255, Blue: 255}}
}
if !p.On {
newColor := [4]byte{0, 0, 0, 0}
if newColor != p.ColorRGBA {
p.update(newColor, transitionTime)
}
} else {
rgbColor, ok := change.Color.ToRGB()
if !ok {
newColor := [4]byte{255, 255, 255, 255}
if newColor != p.ColorRGBA {
p.update(newColor, transitionTime)
}
}
rgb := rgbColor.RGB.AtIntensity(p.Intensity)
red := byte(rgb.Red * 255.0001)
green := byte(rgb.Green * 255.0001)
blue := byte(rgb.Blue * 255.0001)
newColor := [4]byte{red, green, blue, 255}
if newColor != p.ColorRGBA {
p.update(newColor, transitionTime)
}
}
}
type panelUpdate []byte
func (u *panelUpdate) Add(message [8]byte) {
if len(*u) < 2 {
*u = make([]byte, 2, 10)
}
binary.BigEndian.PutUint16(*u, binary.BigEndian.Uint16(*u)+1)
*u = append(*u, message[:]...)
}
func (u *panelUpdate) Len() int {
if len(*u) < 2 {
return 0
}
return int(binary.BigEndian.Uint16(*u))
}
const maxTransitionTime = time.Minute * 109