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.
417 lines
8.3 KiB
417 lines
8.3 KiB
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
|
|
}
|