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.
 
 
 
 

191 lines
3.6 KiB

package lifx
import (
"encoding/binary"
"fmt"
"git.aiterp.net/lucifer/new-server/internal/lerrors"
"log"
"net"
)
func createPacket() Packet {
p := make(Packet, 36)
p.Reset()
return p
}
type Packet []byte
func (p *Packet) Reset() {
p.ensureHeader()
copy(*p, blankHeader)
}
func (p *Packet) Size() int {
return int(binary.LittleEndian.Uint16((*p)[0:2]))
}
func (p *Packet) Protocol() int {
return int(binary.LittleEndian.Uint16((*p)[2:3]) & 0b00001111_11111111)
}
func (p *Packet) SetTagged(v bool) {
if v {
(*p)[3] |= uint8(0b00100000)
} else {
(*p)[3] &= ^uint8(0b00100000)
}
}
func (p *Packet) Source() uint32 {
return binary.LittleEndian.Uint32((*p)[4:8])
}
func (p *Packet) Target() net.HardwareAddr {
return net.HardwareAddr((*p)[8:14])
}
func (p *Packet) SetSource(v uint32) {
binary.LittleEndian.PutUint32((*p)[4:8], v)
}
func (p *Packet) CopyTargetFrom(other Packet) {
copy((*p)[8:14], other[8:14])
}
func (p *Packet) SetTarget(v string) error {
addr, err := net.ParseMAC(v)
if err != nil {
return err
}
if len(addr) != 6 {
return lerrors.ErrInvalidAddress
}
copy((*p)[8:], addr)
return nil
}
func (p *Packet) SetResRequired(v bool) {
if v {
(*p)[22] |= uint8(0b00000001)
} else {
(*p)[22] &= ^uint8(0b00000001)
}
}
func (p *Packet) SetAckRequired(v bool) {
if v {
(*p)[22] |= uint8(0b00000010)
} else {
(*p)[22] &= ^uint8(0b00000010)
}
}
func (p *Packet) Sequence() uint8 {
return (*p)[23]
}
func (p *Packet) SetSequence(v uint8) {
(*p)[23] = v
}
func (p *Packet) PacketType() uint16 {
return binary.LittleEndian.Uint16((*p)[32:34])
}
func (p *Packet) SetRawPayload(packetType uint16, data []byte) {
p.ensureSize(36 + len(data))
copy((*p)[36:], data)
binary.LittleEndian.PutUint16(*p, uint16(36+len(data)))
binary.LittleEndian.PutUint16((*p)[32:34], packetType)
}
func (p *Packet) Payload() (res Payload, err error) {
switch p.PacketType() {
case 2:
res = &GetService{}
case 3:
res = &StateService{}
err = res.Decode((*p)[36:])
case 14:
res = &GetHostFirmware{}
case 15:
res = &StateHostFirmware{}
err = res.Decode((*p)[36:])
case 32:
res = &GetVersion{}
case 33:
res = &StateVersion{}
err = res.Decode((*p)[36:])
case 45:
res = &Acknowledgement{}
case 101:
res = &GetColor{}
case 102:
res = &SetColor{}
err = res.Decode((*p)[36:])
case 107:
res = &LightState{}
err = res.Decode((*p)[36:])
case 117:
res = &SetLightPower{}
err = res.Decode((*p)[36:])
default:
err = lerrors.ErrUnrecognizedPacketType
}
if err != nil {
res = nil
}
if res != nil && res.PacketType() != p.PacketType() {
log.Println("BUG Incorrect packet type used in Packet.Payload")
log.Println("BUG Payload Type:", p.PacketType())
log.Println("BUG Panic Packet Type:", res.PacketType())
log.Println("BUG Panic Payload:", res.String())
}
return
}
func (p *Packet) SetPayload(payload Payload) {
ackRequired, resRequired := payload.Flags()
p.SetRawPayload(payload.PacketType(), payload.Encode())
p.SetAckRequired(ackRequired)
p.SetResRequired(resRequired)
}
func (p *Packet) ensureHeader() {
p.ensureSize(36)
}
func (p *Packet) ensureSize(size int) {
if len(*p) < size {
newData := make([]byte, size)
copy(newData, *p)
*p = newData
}
}
type Payload interface {
fmt.Stringer
Decode(data []byte) error
Encode() []byte
PacketType() uint16
Flags() (ackRequired bool, resRequired bool)
}
var blankHeader = []byte{
0x24, 0x00, 0x00, 0x14,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
}