diff --git a/README.md b/README.md index b0b9e89..becbcc2 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,51 @@ I didn't even test it yet so no idea if it works $ TELEGRAM_APITOKEN=YOUR_API_TOKEN ./alarm_bot --satel_addr=127.0.0.1 --satel_port=31337 --tg_chat_id=YOUR_CHAT_ID_FROM_BOTFATHER ``` +### Filtering events by change type + +It's possible to filter events by change type. Use the `--allowed-types=TYPE1,TYPE2,...` command line parameter to do that. If that parameter is not provided, then all change types are allowed, otherwise only the provided ones will be used for notifications. + +Supported types are: + - `zone-violation` + - `zone-tamper` + - `zone-alarm` + - `zone-tamper-alarm` + - `zone-alarm-memory` + - `zone-tamper-alarm-memory` + - `zone-bypass` + - `zone-no-violation-trouble` + - `zone-long-violation-trouble` + - `armed-partition-suppressed` + - `armed-partition` + - `partition-armed-mode-2` + - `partition-armed-mode-3` + - `partition-with-1st-code-enter` + - `partition-entry-time` + - `partition-exit-time-over-10s` + - `partition-exit-time-under-10s` + - `partition-temporary-blocked` + - `partition-blocked-guard-round` + - `partition-alarm` + - `partition-fire-alarm` + - `partition-alarm-memory` + - `partition-fire-alarm-memory` + - `output` + - `doors-opened` + - `doors-opened-long` + - `status-bit` + - `trouble-part-1` + - `trouble-part-2` + - `trouble-part-3` + - `trouble-part-4` + - `trouble-part-5` + - `trouble-memory-part-1` + - `trouble-memory-part-2` + - `trouble-memory-part-3` + - `trouble-memory-part-4` + - `trouble-memory-part-5` + - `partition-with-violated-zones` + - `zone-isolate` + ## Debugging Set the `OMIT_TG` environment variable to, well, omit sending anything over to Telegram and just see the logs instead. diff --git a/filters.go b/filters.go index 64ad41a..c5c749e 100644 --- a/filters.go +++ b/filters.go @@ -13,17 +13,27 @@ import ( func FilterByType(ev <-chan satel.Event, allowedTypes []satel.ChangeType) <-chan satel.Event { returnChan := make(chan satel.Event) - go func() { - for e := range ev { - for _, allowedType := range allowedTypes { - if allowedType == e.Type { - returnChan <- e - continue + if len(allowedTypes) == 0 { + // no allowed types == all types are allowed + go func() { + for e := range ev { + returnChan <- e + } + close(returnChan) + }() + } else { + go func() { + for e := range ev { + for _, allowedType := range allowedTypes { + if allowedType == e.Type { + returnChan <- e + continue + } } } - } - close(returnChan) - }() + close(returnChan) + }() + } return returnChan } diff --git a/filters_test.go b/filters_test.go index e712104..21fb4fc 100644 --- a/filters_test.go +++ b/filters_test.go @@ -39,6 +39,32 @@ func TestSatelEventTypeFiltering(t *testing.T) { assert.Contains(t, receivedEvents, satel.Event{Type: satel.PartitionFireAlarm, Index: 4, Value: true}) } +func TestSatelEventTypeFiltering_NoAllowedEventTypesMeansAllAreAllowed(t *testing.T) { + testEvents := make(chan satel.Event) + receivedEvents := make([]satel.Event, 0) + wg := sync.WaitGroup{} + + go func() { + wg.Add(1) + for e := range FilterByType(testEvents, []satel.ChangeType{}) { + receivedEvents = append(receivedEvents, e) + } + wg.Done() + }() + + for index, ct := range SUPPORTED_CHANGE_TYPES { + testEvents <- satel.Event{Type: ct, Index: index, Value: true} + } + + close(testEvents) + wg.Wait() + + assert.Len(t, receivedEvents, len(SUPPORTED_CHANGE_TYPES)) + for index, ct := range SUPPORTED_CHANGE_TYPES { + assert.Contains(t, receivedEvents, satel.Event{Type: ct, Index: index, Value: true}) + } +} + func TestSatelLastSeenFiltering(t *testing.T) { f, err := os.CreateTemp("", "TestSatelLastSeenFiltering") assert.NoError(t, err) diff --git a/main.go b/main.go index 974aea6..95ebcbf 100644 --- a/main.go +++ b/main.go @@ -68,10 +68,11 @@ func (self RealSleeper) Sleep(ch chan<- interface{}) { }() } -func getCmdLineParams(logger *log.Logger) (string, []int64) { +func getCmdLineParams(logger *log.Logger) (string, []int64, []satel.ChangeType) { satelApiAddr := flag.String("satel-addr", "", "Address that should be used to connect to the SATEL device") satelApiPort := flag.String("satel-port", "7094", "Port that should be used to connect to the SATEL device") chatIdRaw := flag.String("tg-chat-id", "", "Telegram Chat ID where to send updates. Use \",\" to specify multiple IDs.") + allowedTypesRaw := flag.String("allowed-types", "", "Satel change types that are allowed. All other types will be discarded. By default all are allowed. Use \",\" to specify multiple types.") flag.Parse() if len(*satelApiAddr) == 0 || len(*satelApiPort) == 0 || len(*chatIdRaw) == 0 { @@ -86,9 +87,18 @@ func getCmdLineParams(logger *log.Logger) (string, []int64) { } chatIds = append(chatIds, chatId) } + allowedTypesStrings := strings.Split(*allowedTypesRaw, ",") + var allowedTypes []satel.ChangeType + for _, allowedTypeStr := range allowedTypesStrings { + allowedType, err := StringToSatelChangeType(allowedTypeStr) + if err != nil { + logger.Fatalf("Error trying to understand an allowed type: %s.", err) + } + allowedTypes = append(allowedTypes, allowedType) + } satelAddr := fmt.Sprintf("%s:%s", *satelApiAddr, *satelApiPort) - return satelAddr, chatIds + return satelAddr, chatIds, allowedTypes } func makeSatel(satelAddr string) *satel.Satel { @@ -107,7 +117,7 @@ func main() { logger = log.New(os.Stderr, "Main", log.Lmicroseconds) ) - satelAddr, chatIds := getCmdLineParams(logger) + satelAddr, chatIds, allowedTypes := getCmdLineParams(logger) s := makeSatel(satelAddr) logger.Printf("Connected to Satel: %s", satelAddr) @@ -125,7 +135,9 @@ func main() { tgSenderWorker(tgEvents, &wg, sleeper, log.New(os.Stderr, "TgSender", log.Lmicroseconds)), tgSender, &wg, log.New(os.Stderr, "SendToTg", log.Lmicroseconds))) - for e := range FilterByLastSeen(s.Events, "hs_wro_last_seen.bin", log.New(os.Stderr, "FilterByLastSeen", log.Lmicroseconds)) { + for e := range FilterByType( + FilterByLastSeen(s.Events, "hs_wro_last_seen.bin", log.New(os.Stderr, "FilterByLastSeen", log.Lmicroseconds)), + allowedTypes) { logger.Print("Received change from SATEL: ", e) for _, chatId := range chatIds { sendTgMessage(tgEvents, SatelMsgContent{e}, chatId) diff --git a/satel_utils.go b/satel_utils.go new file mode 100644 index 0000000..9736b95 --- /dev/null +++ b/satel_utils.go @@ -0,0 +1,100 @@ +package main + +import ( + "errors" + "fmt" + + "github.com/probakowski/go-satel" +) + +var SUPPORTED_CHANGE_TYPES = [...]satel.ChangeType{ + satel.ZoneViolation, + satel.ZoneTamper, + satel.ZoneAlarm, + satel.ZoneTamperAlarm, + satel.ZoneAlarmMemory, + satel.ZoneTamperAlarmMemory, + satel.ZoneBypass, + satel.ZoneNoViolationTrouble, + satel.ZoneLongViolationTrouble, + satel.ArmedPartitionSuppressed, + satel.ArmedPartition, + satel.PartitionArmedInMode2, + satel.PartitionArmedInMode3, + satel.PartitionWith1stCodeEntered, + satel.PartitionEntryTime, + satel.PartitionExitTimeOver10s, + satel.PartitionExitTimeUnder10s, + satel.PartitionTemporaryBlocked, + satel.PartitionBlockedForGuardRound, + satel.PartitionAlarm, + satel.PartitionFireAlarm, + satel.PartitionAlarmMemory, + satel.PartitionFireAlarmMemory, + satel.Output, + satel.DoorOpened, + satel.DoorOpenedLong, + satel.StatusBit, + satel.TroublePart1, + satel.TroublePart2, + satel.TroublePart3, + satel.TroublePart4, + satel.TroublePart5, + satel.TroubleMemoryPart1, + satel.TroubleMemoryPart2, + satel.TroubleMemoryPart3, + satel.TroubleMemoryPart4, + satel.TroubleMemoryPart5, + satel.PartitionWithViolatedZones, + satel.ZoneIsolate, +} + +var SATEL_STRING_TO_CHANGE_TYPE = map[string]satel.ChangeType{ + "zone-violation": satel.ZoneViolation, + "zone-tamper": satel.ZoneTamper, + "zone-alarm": satel.ZoneAlarm, + "zone-tamper-alarm": satel.ZoneTamperAlarm, + "zone-alarm-memory": satel.ZoneAlarmMemory, + "zone-tamper-alarm-memory": satel.ZoneTamperAlarmMemory, + "zone-bypass": satel.ZoneBypass, + "zone-no-violation-trouble": satel.ZoneNoViolationTrouble, + "zone-long-violation-trouble": satel.ZoneLongViolationTrouble, + "armed-partition-suppressed": satel.ArmedPartitionSuppressed, + "armed-partition": satel.ArmedPartition, + "partition-armed-mode-2": satel.PartitionArmedInMode2, + "partition-armed-mode-3": satel.PartitionArmedInMode3, + "partition-with-1st-code-entered": satel.PartitionWith1stCodeEntered, + "partition-entry-time": satel.PartitionEntryTime, + "partition-exit-time-over-10s": satel.PartitionExitTimeOver10s, + "partition-exit-time-under-10s": satel.PartitionExitTimeUnder10s, + "partition-temporary-blocked": satel.PartitionTemporaryBlocked, + "partition-blocked-guard-round": satel.PartitionBlockedForGuardRound, + "partition-alarm": satel.PartitionAlarm, + "partition-fire-alarm": satel.PartitionFireAlarm, + "partition-alarm-memory": satel.PartitionAlarmMemory, + "partition-fire-alarm-memory": satel.PartitionFireAlarmMemory, + "output": satel.Output, + "doors-opened": satel.DoorOpened, + "doors-opened-long": satel.DoorOpenedLong, + "status-bit": satel.StatusBit, + "trouble-part-1": satel.TroublePart1, + "trouble-part-2": satel.TroublePart2, + "trouble-part-3": satel.TroublePart3, + "trouble-part-4": satel.TroublePart4, + "trouble-part-5": satel.TroublePart5, + "trouble-memory-part-1": satel.TroubleMemoryPart1, + "trouble-memory-part-2": satel.TroubleMemoryPart2, + "trouble-memory-part-3": satel.TroubleMemoryPart3, + "trouble-memory-part-4": satel.TroubleMemoryPart4, + "trouble-memory-part-5": satel.TroubleMemoryPart5, + "partition-with-violated-zones": satel.PartitionWithViolatedZones, + "zone-isolate": satel.ZoneIsolate, +} + +func StringToSatelChangeType(s string) (satel.ChangeType, error) { + ct, ok := SATEL_STRING_TO_CHANGE_TYPE[s] + if !ok { + return 0, errors.New(fmt.Sprint("Unknown change type: ", s)) + } + return ct, nil +} diff --git a/satel_utils_test.go b/satel_utils_test.go new file mode 100644 index 0000000..b72c5c9 --- /dev/null +++ b/satel_utils_test.go @@ -0,0 +1,22 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestStringToSatelChangeType(t *testing.T) { + for _, ct := range SUPPORTED_CHANGE_TYPES { + str := ct.String() + new_ct, err := StringToSatelChangeType(str) + assert.NoError(t, err) + assert.Equal(t, ct, new_ct) + assert.Equal(t, new_ct.String(), str) + } +} + +func TestStringToSatelChangeType_UnknownStringPanics(t *testing.T) { + _, err := StringToSatelChangeType("smród") + assert.Error(t, err, "") +}