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", 15: "Element Corner", } var shapeIconMap = map[int]string{ 0: "shape_triangle", 1: "shape_triangle", 2: "shape_square", 3: "shape_square", 4: "shape_square", 7: "shape_hexagon", 8: "shape_triangle", 9: "shape_triangle", 12: "shape_hexagon", 15: "shape_hexagon_corner", } 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