go-satel/satel.go

206 wiersze
4.1 KiB
Go

package satel
import (
"bufio"
"errors"
"net"
"sync"
"time"
)
type BasicEventElement struct {
Type ChangeType
Index int
Value bool
}
type Event struct {
BasicEvents []BasicEventElement
}
type Config struct {
EventsQueueSize int
LongCommands bool
PoolingInterval time.Duration
}
type Satel struct {
conn net.Conn
mu sync.Mutex
cmdSize int
cmdChan chan int
rawEvents chan []byte
commandQueue chan func()
Events chan Event
}
func New(conn net.Conn) *Satel {
return NewConfig(conn, Config{})
}
func callCommandAndRetrieveResult(cmd byte, s *Satel) func() {
return func() {
err := s.sendCmd(cmd)
if err != nil {
return
}
s.readRawEvents()
}
}
func NewConfig(conn net.Conn, config Config) *Satel {
s := &Satel{
conn: conn,
cmdChan: make(chan int),
rawEvents: make(chan []byte, config.EventsQueueSize),
commandQueue: make(chan func(), config.EventsQueueSize),
Events: make(chan Event, config.EventsQueueSize),
}
if config.LongCommands {
s.cmdSize = 32
} else {
s.cmdSize = 16
}
go s.read()
err := s.sendCmd(0x7F, 0x01, 0x04, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
if err != nil {
close(s.Events)
return s
}
go func() {
for {
s.commandQueue <- callCommandAndRetrieveResult(SatelCommand_armedPartitionsReally, s)
s.commandQueue <- callCommandAndRetrieveResult(SatelCommand_partitionsAlarm, s)
time.Sleep(config.PoolingInterval)
}
}()
go func() {
for f := range s.commandQueue {
f()
}
}()
return s
}
func (s *Satel) ArmPartition(code string, mode, index int) error {
data := make([]byte, 4)
data[index/8] = 1 << (index % 8)
bytes := prepareCommand(code, byte(0x80+mode), data...)
return s.sendCmd(bytes...)
}
func (s *Satel) ForceArmPartition(code string, mode, index int) error {
data := make([]byte, 4)
data[index/8] = 1 << (index % 8)
bytes := prepareCommand(code, byte(0xA0+mode), data...)
return s.sendCmd(bytes...)
}
func (s *Satel) DisarmPartition(code string, index int) error {
data := make([]byte, 4)
data[index/8] = 1 << (index % 8)
bytes := prepareCommand(code, byte(0x84), data...)
return s.sendCmd(bytes...)
}
func (s *Satel) SetOutput(code string, index int, value bool) error {
cmd := byte(0x89)
if value {
cmd = 0x88
}
data := make([]byte, s.cmdSize)
data[index/8] = 1 << (index % 8)
bytes := prepareCommand(code, cmd, data...)
return s.sendCmd(bytes...)
}
func prepareCommand(code string, cmd byte, data ...byte) []byte {
bytes := append([]byte{cmd}, transformCode(code)...)
return append(bytes, data...)
}
func (s *Satel) Close() error {
return s.conn.Close()
}
type command struct {
prev [32]byte
initialized bool
}
func (s *Satel) readRawEvents() {
var bytes = <-s.rawEvents
cmd := bytes[0]
bytes = bytes[1 : len(bytes)-2]
if cmd == Satel_Result {
return
}
basicElements := make([]BasicEventElement, 0)
for i, bb := range bytes {
for j := 0; j < 8; j++ {
index := byte(1 << j)
basicElements = append(basicElements, BasicEventElement{
Type: ChangeType(cmd),
Index: i*8 + j,
Value: bb&index != 0,
})
}
}
s.Events <- Event{basicElements}
}
func (s *Satel) read() {
scanner := bufio.NewScanner(s.conn)
scanner.Split(scan)
for ok := scanner.Scan(); ok; ok = scanner.Scan() {
bytes := scanner.Bytes()
s.cmdRes()
s.rawEvents <- bytes
}
close(s.Events)
_ = s.conn.Close()
}
func (s *Satel) cmdRes() {
select {
case s.cmdChan <- 0:
default:
}
}
func (s *Satel) sendCmd(data ...byte) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
if s.conn == nil {
return errors.New("no connection")
}
_, err = s.conn.Write(frame(data...))
if err == nil {
select {
case <-s.cmdChan:
case <-time.After(3 * time.Second):
}
}
return
}
func transformCode(code string) []byte {
bytes := make([]byte, 8)
for i := 0; i < 16; i++ {
if i < len(code) {
digit := code[i]
if i%2 == 0 {
bytes[i/2] = (digit - '0') << 4
} else {
bytes[i/2] |= digit - '0'
}
} else if i%2 == 0 {
bytes[i/2] = 0xFF
} else if i == len(code) {
bytes[i/2] |= 0x0F
}
}
return bytes
}