From fc947e43252ee368c2be0ae983976ac0b8a3f909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Rudowicz?= Date: Sun, 3 Mar 2024 22:42:13 +0100 Subject: [PATCH] Get device name from SATEL --- get_name.go | 45 ++++++++++++++++++ get_name_test.go | 16 +++++++ go.mod | 5 +- go.sum | 32 +++++++++++++ satel.go | 116 +++++++++++++++++++++++++++++++++-------------- 5 files changed, 179 insertions(+), 35 deletions(-) create mode 100644 get_name.go create mode 100644 get_name_test.go diff --git a/get_name.go b/get_name.go new file mode 100644 index 0000000..ad13e94 --- /dev/null +++ b/get_name.go @@ -0,0 +1,45 @@ +package satel + +import ( + "strings" + + "golang.org/x/text/encoding/charmap" +) + +type DeviceType byte + +const ( + DeviceType_Partition DeviceType = 0 + DeviceType_Zone DeviceType = 1 + DeviceType_User DeviceType = 2 + DeviceType_ExpanderOrLCD DeviceType = 3 + DeviceType_Output DeviceType = 4 + DeviceType_ZoneWithPartitionAssignment DeviceType = 5 + DeviceType_Timer DeviceType = 6 + DeviceType_Telephone DeviceType = 7 + DeviceType_Object DeviceType = 15 + DeviceType_PartitionWithObjectAssignment DeviceType = 16 + DeviceType_OutputWithDurationTime DeviceType = 17 + DeviceType_PartitionWithObjectAssignmentAndOptions DeviceType = 18 +) + +type NameEvent struct { + DevType DeviceType + DevNumber byte + DevTypeFunction byte + DevName string +} + +func makeNameEvent(bytes []byte) NameEvent { + cp1250dec := charmap.Windows1250.NewDecoder() + name, err := cp1250dec.String(string(bytes[3:19])) + if err != nil { + name = "CP1250 DECODE ERROR" + } + return NameEvent{ + DevType: DeviceType(bytes[0]), + DevNumber: bytes[1], + DevTypeFunction: bytes[2], + DevName: strings.TrimSpace(name), + } +} diff --git a/get_name_test.go b/get_name_test.go new file mode 100644 index 0000000..f961f07 --- /dev/null +++ b/get_name_test.go @@ -0,0 +1,16 @@ +package satel + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestMakeNameEvent(t *testing.T) { + assert := assert.New(t) + assert.Equal(NameEvent{ + DevType: DeviceType(1), + DevNumber: 2, + DevTypeFunction: 3, + DevName: "0123456789ABCDEF", + }, makeNameEvent([]byte{01, 02, 03, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 0xFF})) +} diff --git a/go.mod b/go.mod index dbfa039..65ff88a 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module github.com/probakowski/go-satel go 1.16 -require github.com/stretchr/testify v1.7.0 +require ( + github.com/stretchr/testify v1.7.0 + golang.org/x/text v0.14.0 +) diff --git a/go.sum b/go.sum index acb88a4..aca142f 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,38 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/satel.go b/satel.go index dfb7603..da9f7e3 100644 --- a/satel.go +++ b/satel.go @@ -3,6 +3,7 @@ package satel import ( "bufio" "errors" + "fmt" "net" "sync" "time" @@ -20,11 +21,13 @@ type Config struct { } type Satel struct { - conn net.Conn - mu sync.Mutex - cmdSize int - cmdChan chan int - Events chan Event + 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 { @@ -33,9 +36,11 @@ func New(conn net.Conn) *Satel { func NewConfig(conn net.Conn, config Config) *Satel { s := &Satel{ - conn: conn, - cmdChan: make(chan int), - Events: make(chan Event, config.EventsQueueSize), + 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 @@ -50,13 +55,21 @@ func NewConfig(conn net.Conn, config Config) *Satel { } go func() { for { - err = s.sendCmd(0x0A) - if err != nil { - return + s.commandQueue <- func() { + err = s.sendCmd(0x0A) + if err != nil { + return + } + s.readRawEvents() } time.Sleep(5 * time.Second) } }() + go func() { + for f := range s.commandQueue { + f() + } + }() return s } @@ -92,6 +105,44 @@ func (s *Satel) SetOutput(code string, index int, value bool) error { return s.sendCmd(bytes...) } +func (s *Satel) GetName(devType DeviceType, devIndex byte) NameEvent { + resultChan := make(chan NameEvent) + + s.commandQueue <- func() { + err := s.sendCmd(0xEE, byte(devType), devIndex) + if err != nil { + resultChan <- NameEvent{ + DevType: DeviceType(devType), + DevNumber: devIndex, + DevTypeFunction: 0, + DevName: fmt.Sprint("ERROR RETRIEVING NAME ", devType, devIndex), + } + } + + var bytes = <-s.rawEvents + cmd := bytes[0] + bytes = bytes[1 : len(bytes)-2] + if cmd == 0xEF { + resultChan <- NameEvent{ + DevType: DeviceType(devType), + DevNumber: devIndex, + DevTypeFunction: 0, + DevName: fmt.Sprint("NO NAME ", devType, devIndex), + } + } else if cmd == 0xEE { + resultChan <- makeNameEvent(bytes) + } else { + resultChan <- NameEvent{ + DevType: DeviceType(devType), + DevNumber: devIndex, + DevTypeFunction: 0, + DevName: fmt.Sprint("ERROR GETTING NAME ", devType, devIndex), + } + } + } + return <-resultChan +} + func prepareCommand(code string, cmd byte, data ...byte) []byte { bytes := append([]byte{cmd}, transformCode(code)...) return append(bytes, data...) @@ -106,36 +157,33 @@ type command struct { initialized bool } +func (s *Satel) readRawEvents() { + var bytes = <-s.rawEvents + cmd := bytes[0] + bytes = bytes[1 : len(bytes)-2] + if cmd == 0xEF { + return + } + for i, bb := range bytes { + for j := 0; j < 8; j++ { + index := byte(1 << j) + s.Events <- Event{ + Type: ChangeType(cmd), + Index: i*8 + j, + Value: bb&index != 0, + } + } + } +} + func (s *Satel) read() { scanner := bufio.NewScanner(s.conn) scanner.Split(scan) - commands := make(map[byte]command) for ok := scanner.Scan(); ok; ok = scanner.Scan() { bytes := scanner.Bytes() - cmd := bytes[0] - bytes = bytes[1 : len(bytes)-2] s.cmdRes() - if cmd == 0xEF { - continue - } - c := commands[cmd] - for i, bb := range bytes { - change := bb ^ c.prev[i] - for j := 0; j < 8; j++ { - index := byte(1 << j) - if !c.initialized || change&index != 0 { - s.Events <- Event{ - Type: ChangeType(cmd), - Index: i*8 + j, - Value: bb&index != 0, - } - } - } - c.prev[i] = bytes[i] - } - c.initialized = true - commands[cmd] = c + s.rawEvents <- bytes } close(s.Events) _ = s.conn.Close()