package lifx import ( "encoding/binary" "fmt" "git.aiterp.net/lucifer/new-server/internal/lerrors" "math" "time" ) type GetService struct{} func (p *GetService) Decode([]byte) error { return nil } func (p *GetService) Encode() []byte { return []byte{} } func (p *GetService) PacketType() uint16 { return 2 } func (p *GetService) Flags() (ackRequired bool, resRequired bool) { return false, true } func (p *GetService) String() string { return "GetService()" } type GetVersion struct{} func (p *GetVersion) Decode([]byte) error { return nil } func (p *GetVersion) Encode() []byte { return []byte{} } func (p *GetVersion) PacketType() uint16 { return 32 } func (p *GetVersion) Flags() (ackRequired bool, resRequired bool) { return false, true } func (p *GetVersion) String() string { return "GetVersion()" } type GetHostFirmware struct{} func (p *GetHostFirmware) Decode([]byte) error { return nil } func (p *GetHostFirmware) Encode() []byte { return []byte{} } func (p *GetHostFirmware) PacketType() uint16 { return 14 } func (p *GetHostFirmware) Flags() (ackRequired bool, resRequired bool) { return false, true } func (p *GetHostFirmware) String() string { return "GetHostFirmware()" } type StateHostFirmware struct { BuildTime time.Time Major uint16 Minor uint16 } func (p *StateHostFirmware) Decode(data []byte) error { if len(data) < 20 { return lerrors.ErrPayloadTooShort } ts := int64(binary.LittleEndian.Uint64(data[0:8])) p.BuildTime = time.Unix(ts/1000000000, ts%1000000000) p.Major = binary.LittleEndian.Uint16(data[16:18]) p.Minor = binary.LittleEndian.Uint16(data[18:20]) return nil } func (p *StateHostFirmware) Encode() []byte { var data [20]byte binary.LittleEndian.PutUint64(data[0:8], uint64(p.BuildTime.UnixNano())) binary.LittleEndian.PutUint16(data[16:18], p.Major) binary.LittleEndian.PutUint16(data[18:20], p.Minor) return data[:] } func (p *StateHostFirmware) PacketType() uint16 { return 15 } func (p *StateHostFirmware) Flags() (ackRequired bool, resRequired bool) { return false, false } func (p *StateHostFirmware) String() string { return fmt.Sprintf("StateHostFirmware(major=%d, minor=%d, build=%s)", p.Major, p.Minor, p.BuildTime.Format(time.RFC3339)) } type StateVersion struct { Vendor uint32 Product uint32 } func (p *StateVersion) Decode(data []byte) error { if len(data) < 8 { return lerrors.ErrPayloadTooShort } p.Vendor = binary.LittleEndian.Uint32(data[0:4]) p.Product = binary.LittleEndian.Uint32(data[4:8]) return nil } func (p *StateVersion) Encode() []byte { var data [12]byte binary.LittleEndian.PutUint32(data[0:4], p.Vendor) binary.LittleEndian.PutUint32(data[4:8], p.Product) return data[:] } func (p *StateVersion) PacketType() uint16 { return 33 } func (p *StateVersion) Flags() (ackRequired bool, resRequired bool) { return false, false } func (p *StateVersion) String() string { return fmt.Sprintf("StateVersion(vendor=%d, product=%d)", p.Vendor, p.Product) } type GetColor struct{} func (p *GetColor) Decode([]byte) error { return nil } func (p *GetColor) Encode() []byte { return []byte{} } func (p *GetColor) PacketType() uint16 { return 101 } func (p *GetColor) Flags() (ackRequired bool, resRequired bool) { return false, true } func (p *GetColor) String() string { return "GetColor()" } type Acknowledgement struct{} func (p *Acknowledgement) Decode([]byte) error { return nil } func (p *Acknowledgement) Encode() []byte { return []byte{} } func (p *Acknowledgement) PacketType() uint16 { return 45 } func (p *Acknowledgement) Flags() (ackRequired bool, resRequired bool) { return false, true } func (p *Acknowledgement) String() string { return "Acknowledgement()" } type SetColor struct { Hue float64 Sat float64 Bri float64 Kelvin int TransitionTime time.Duration } func (p *SetColor) Decode(data []byte) error { if len(data) < 13 { return lerrors.ErrPayloadTooShort } hue := binary.LittleEndian.Uint16(data[1:3]) sat := binary.LittleEndian.Uint16(data[3:5]) bri := binary.LittleEndian.Uint16(data[5:7]) kelvin := binary.LittleEndian.Uint16(data[7:9]) transitionMs := binary.LittleEndian.Uint32(data[9:13]) p.Hue = float64(hue) / (65536 / 360) p.Sat = float64(sat) / 65535 p.Bri = float64(bri) / 65535 p.Kelvin = int(kelvin) p.TransitionTime = time.Duration(transitionMs) * time.Millisecond return nil } func (p *SetColor) Encode() []byte { var data [13]byte data[0] = 0x00 // reserved binary.LittleEndian.PutUint16(data[1:3], uint16(math.Mod(p.Hue, 360)*(65536/360))) binary.LittleEndian.PutUint16(data[3:5], uint16(p.Sat*65535)) binary.LittleEndian.PutUint16(data[5:7], uint16(p.Bri*65535)) binary.LittleEndian.PutUint16(data[7:9], uint16(p.Kelvin)) binary.LittleEndian.PutUint32(data[9:13], uint32(p.TransitionTime.Milliseconds())) return data[:] } func (p *SetColor) PacketType() uint16 { return 102 } func (p *SetColor) Flags() (ackRequired bool, resRequired bool) { return true, false } func (p *SetColor) String() string { return fmt.Sprintf("SetColor(hsvk={%f, %f, %f, %d}, ttime=%s)", p.Hue, p.Sat, p.Bri, p.Kelvin, p.TransitionTime, ) } type SetLightPower struct { On bool TransitionTime time.Duration } func (p *SetLightPower) Decode(data []byte) error { if len(data) < 6 { return lerrors.ErrPayloadTooShort } level := binary.LittleEndian.Uint16(data[0:2]) transitionMs := binary.LittleEndian.Uint32(data[2:6]) p.On = level > 0 p.TransitionTime = time.Duration(transitionMs) * time.Millisecond return nil } func (p *SetLightPower) Encode() []byte { var data [6]byte if p.On { data[0], data[1] = 0xff, 0xff } else { data[0], data[1] = 0x00, 0x00 } binary.LittleEndian.PutUint32(data[2:6], uint32(p.TransitionTime.Milliseconds())) return data[:] } func (p *SetLightPower) PacketType() uint16 { return 117 } func (p *SetLightPower) Flags() (ackRequired bool, resRequired bool) { return true, false } func (p *SetLightPower) String() string { return fmt.Sprintf("SetLightPower(on=%t, ttime=%s)", p.On, p.TransitionTime, ) } type StateService struct { Service int Port int } func (p *StateService) String() string { return fmt.Sprintf("StateService(service=%d, port=%d)", p.Service, p.Port) } func (p *StateService) Decode(data []byte) error { if len(data) < 5 { return lerrors.ErrPayloadTooShort } p.Service = int(data[0]) p.Port = int(binary.LittleEndian.Uint32(data[1:5])) return nil } func (p *StateService) Encode() []byte { var data [5]byte data[0] = uint8(p.Service) binary.LittleEndian.PutUint32(data[1:5], uint32(p.Port)) return data[:] } func (p *StateService) PacketType() uint16 { return 3 } func (p *StateService) Flags() (ackRequired bool, resRequired bool) { return false, false } type LightState struct { Hue float64 Sat float64 Bri float64 Kelvin int On bool Label string } func (p *LightState) String() string { return fmt.Sprintf("LightState(hsvk=(%f, %f, %f, %d), on=%t, label=%+v)", p.Hue, p.Sat, p.Bri, p.Kelvin, p.On, p.Label, ) } func (p *LightState) Decode(data []byte) error { if len(data) < 52 { return lerrors.ErrPayloadTooShort } hue := binary.LittleEndian.Uint16(data[0:2]) sat := binary.LittleEndian.Uint16(data[2:4]) bri := binary.LittleEndian.Uint16(data[4:6]) kelvin := binary.LittleEndian.Uint16(data[6:8]) power := binary.LittleEndian.Uint16(data[10:12]) p.Hue = float64(hue) / (65536 / 360) p.Sat = float64(sat) / 65535 p.Bri = float64(bri) / 65535 p.Kelvin = int(kelvin) p.On = power > 32767 p.Label = "" for i, v := range data[14:46] { if v == 0 { p.Label = string(data[14 : 14+i]) break } } return nil } func (p *LightState) Encode() []byte { var data [52]byte power := uint16(0) if p.On { power = 0xffff } binary.LittleEndian.PutUint16(data[0:2], uint16(math.Mod(p.Hue, 360)*(65536/360))) binary.LittleEndian.PutUint16(data[2:4], uint16(p.Sat*65535)) binary.LittleEndian.PutUint16(data[4:6], uint16(p.Bri*65535)) binary.LittleEndian.PutUint16(data[6:8], uint16(p.Kelvin)) binary.LittleEndian.PutUint16(data[10:12], power) label := p.Label labelBytes := []byte(label) for len(labelBytes) > 31 { label = label[:len(label)-1] labelBytes = []byte(label) } copy(data[14:], labelBytes) return data[:] } func (p *LightState) PacketType() uint16 { return 107 } func (p *LightState) Flags() (ackRequired bool, resRequired bool) { return false, false }