Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pushover support #35

Merged
merged 2 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@

[![Static Badge](https://img.shields.io/badge/Project-Documentation-blue)](https://frigate-notify.0x2142.com) [![GitHub Repo stars](https://img.shields.io/github/stars/0x2142/frigate-notify)]() [![GitHub release (with filter)](https://img.shields.io/github/v/release/0x2142/frigate-notify)](https://github.com/0x2142/frigate-notify/releases) [![Static Badge](https://img.shields.io/badge/Docker-latest-blue)](https://github.com/0x2142/frigate-notify/pkgs/container/frigate-notify)


</div>

## About

This project is designed to generate notifications based on [Frigate](https://github.com/blakeblackshear/frigate) NVR events.
This project is designed to generate event notifications from a standalone [Frigate](https://github.com/blakeblackshear/frigate) NVR instance.

Currently Frigate only supports notifications through Home Assistant, which I'm not using right now. So I set out to build a simple notification app that would work with a standalone Frigate server.

Expand All @@ -24,6 +23,7 @@ Currently Frigate only supports notifications through Home Assistant, which I'm
- Gotify
- SMTP
- Telegram
- Pushover

**Other**
- Aliveness monitor via HTTP GET (for use with tools like [HealthChecks](https://github.com/healthchecks/healthchecks) or [Uptime Kuma](https://github.com/louislam/uptime-kuma))
Expand Down
48 changes: 48 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Frigate struct {
type WebAPI struct {
Enabled bool `fig:"enabled" default:false`
Interval int `fig:"interval" default:30`
TestMode bool `fig:"testmode" default:false`
}

type MQTT struct {
Expand All @@ -49,6 +50,7 @@ type Alerts struct {
Gotify Gotify `fig:"gotify"`
SMTP SMTP `fig:"smtp"`
Telegram Telegram `fig:"telegram"`
Pushover Pushover `fig:"pushover"`
}

type General struct {
Expand Down Expand Up @@ -89,6 +91,17 @@ type Telegram struct {
Token string `fig:"token" default:""`
}

type Pushover struct {
Enabled bool `fig:"enabled" default:false`
Token string `fig:"token" default:""`
Userkey string `fig:"userkey" default:""`
Devices string `fig:"devices" default:""`
Priority int `fig:"priority" default:0`
Retry int `fig:"retry" default:0`
Expire int `fig:"expire" default:0`
TTL int `fig:"ttl" default:0`
}

type Monitor struct {
Enabled bool `fig:"enabled" default:false`
URL string `fig:"url" default:""`
Expand Down Expand Up @@ -132,6 +145,15 @@ func validateConfig() {
configErrors = append(configErrors, "Please configure only one polling method: Frigate Web API or MQTT")
}

// Warn on test mode being enabled
if ConfigData.Frigate.WebAPI.Enabled && ConfigData.Frigate.WebAPI.TestMode {
log.Print("~~~~~~~~~~~~~~~~~~~")
log.Print("WARNING: Test Mode is enabled.")
log.Print("This is intended for development only & will only query Frigate for the last event.")
log.Print("Do not enable this in production! App will not accurately check for events.")
log.Print("~~~~~~~~~~~~~~~~~~~")
}

// Check if Frigate server URL contains protocol, assume HTTP if not specified
if !strings.Contains(ConfigData.Frigate.Server, "http://") && !strings.Contains(ConfigData.Frigate.Server, "https://") {
log.Println("No protocol specified on Frigate Server. Assuming http://. If this is incorrect, please adjust the config file.")
Expand Down Expand Up @@ -229,6 +251,30 @@ func validateConfig() {
configErrors = append(configErrors, "No Telegram bot token specified!")
}
}
if ConfigData.Alerts.Pushover.Enabled {
log.Print("Pushover alerting enabled.")
if ConfigData.Alerts.Pushover.Token == "" {
configErrors = append(configErrors, "No Pushover API token specified!")
}
if ConfigData.Alerts.Pushover.Userkey == "" {
configErrors = append(configErrors, "No Pushover user key specified!")
}
if ConfigData.Alerts.Pushover.Priority < -2 || ConfigData.Alerts.Pushover.Priority > 2 {
configErrors = append(configErrors, "Pushover priority must be between -2 and 2!")
}
// Priority 2 is emergency, needs a retry interval & expiration set
if ConfigData.Alerts.Pushover.Priority == 2 {
if ConfigData.Alerts.Pushover.Retry == 0 || ConfigData.Alerts.Pushover.Expire == 0 {
configErrors = append(configErrors, "Pushover retry interval & expiration must be set with priority 2!")
}
if ConfigData.Alerts.Pushover.Retry < 30 {
configErrors = append(configErrors, "Pushover retry cannot be less than 30 seconds!")
}
}
if ConfigData.Alerts.Pushover.TTL < 0 {
configErrors = append(configErrors, "Pushover TTL cannot be negative!")
}
}

// Validate monitoring config
if ConfigData.Monitor.Enabled {
Expand All @@ -242,10 +288,12 @@ func validateConfig() {
}

if len(configErrors) > 0 {
fmt.Println()
log.Println("Config validation failed:")
for _, msg := range configErrors {
log.Println(" -", msg)
}
fmt.Println()
log.Fatal("Please fix config errors before restarting app.")
} else {
log.Println("Config file validated!")
Expand Down
4 changes: 4 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [v0.2.5](https://github.com/0x2142/frigate-notify/releases/tag/v0.2.5) - TBD

- Added support for alerts via [Pushover](https://frigate-notify.0x2142.com/config/#pushover)

## [v0.2.4](https://github.com/0x2142/frigate-notify/releases/tag/v0.2.4) - Mar 28 2024

- Added support for alerts via [Telegram](https://frigate-notify.0x2142.com/config/#telegram)
Expand Down
52 changes: 52 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,44 @@ alerts:
token: 987654321:ABCDEFGHIJKLMNOP
```

### Pushover

- **enabled** (Optional - Default: `false`)
- Set to `true` to enable alerting via Pushover
- **token** (Required)
- Pushover application API token
- Required if this alerting method is enabled
- **userkey** (Required)
- Recipient user or group key from Pushover dashboard
- Required if this alerting method is enabled
- **devices** (Optional)
- Optionally specify list of devices to send notifications to
- If left empty, all devices will receive the notification
- **priority** (Optional)
- Optionally set message priority
- Valid priorities are -2, -1, 0, 1, 2
- **retry** (Optional)
- Message retry in seconds until message is acknowledged
- If `priority` is set to 2, this is required
- Minimum value is 30 seconds
- **expire** (Optional)
- Expiration timer for message retry
- If `priority` is set to 2, this is required
- **ttl** (Optional)
- Optionally set lifetime of message, in seconds
- If set, message notifications are deleted from devices after this time

```yaml title="Config File Snippet"
pushover:
enabled: true
token: aaaaaaaaaaaaaaaaaaaaaa
userkey: bbbbbbbbbbbbbbbbbbbbbb
devices: device1,device2
priority: 0
retry:
expire:
ttl:
```

## Monitor

Expand Down Expand Up @@ -327,6 +365,20 @@ alerts:
password:
recipient:

telegram:
enabled: false
chatid:
token:

pushover:
enabled: false
token:
userkey:
devices:
priority:
retry:
expire:
ttl:

monitor:
enabled: false
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Currently Frigate only supports notifications through Home Assistant, which I'm
- Gotify
- SMTP
- Telegram
- Pushover

**Other**

Expand Down
12 changes: 9 additions & 3 deletions events/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@ var LastEventTime float64 = float64(time.Now().Unix())

// CheckForEvents queries for all detection events since last alert time
func CheckForEvents() {
params := "?include_thumbnails=0&after=" + strconv.FormatFloat(LastEventTime, 'f', 6, 64)
// For testing, pull 1 event immediately
//params := "?include_thumbnails=0&limit=1"
var params string
if config.ConfigData.Frigate.WebAPI.TestMode {
// For testing, pull 1 event immediately
params = "?include_thumbnails=0&limit=1"
} else {
// Check for any events after last query time
params = "?include_thumbnails=0&after=" + strconv.FormatFloat(LastEventTime, 'f', 6, 64)
}

url := config.ConfigData.Frigate.Server + eventsURI + params
log.Println("Checking for new events...")

Expand Down
19 changes: 19 additions & 0 deletions example-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,25 @@ alerts:
# Bot API token
token:

# Pushover Config
pushover:
# Set to true to enable alerting via Pushover
enabled: false
# Pushover API token for this application
token:
# User or Group key for recipients
userkey:
# Optional list of target devices by name, separated by comma
devices:
# Optional message priority, default is 0
priority:
# If priority is 2, retry & expiration must be set
# Values in seconds. Retry must be 30 or higher
retry:
expire:
# Optional message lifetime
ttl:


## App Monitoring
# Sends HTTP GET to provided URL for aliveness checks
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (
github.com/disgoorg/json v1.1.0 // indirect
github.com/disgoorg/snowflake/v2 v2.0.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/gregdel/pushover v1.3.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47 h1:k4Tw0nt6lwr
github.com/gomarkdown/markdown v0.0.0-20231222211730-1d6d20845b47/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gregdel/pushover v1.3.0 h1:CewbxqsThoN/1imgwkDKFkRkltaQMoyBV0K9IquQLtw=
github.com/gregdel/pushover v1.3.0/go.mod h1:EcaO66Nn1StkpEm1iKtBTV3d2A16SoMsVER1PthX7to=
github.com/kkyr/fig v0.4.0 h1:4D/g72a8ij1fgRypuIbEoqIT7ukf2URVBtE777/gkbc=
github.com/kkyr/fig v0.4.0/go.mod h1:U4Rq/5eUNJ8o5UvOEc9DiXtNf41srOLn2r/BfCyuc58=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
Expand Down
3 changes: 3 additions & 0 deletions notifier/alerts.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,7 @@ func SendAlert(message, snapshotURL string, snapshot io.Reader) {
if config.ConfigData.Alerts.Telegram.Enabled {
SendTelegramMessage(message, bytes.NewReader(snap))
}
if config.ConfigData.Alerts.Pushover.Enabled {
SendPushoverMessage(message, bytes.NewReader(snap))
}
}
61 changes: 61 additions & 0 deletions notifier/pushover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package notifier

import (
"fmt"
"io"
"log"
"strings"
"time"

"github.com/0x2142/frigate-notify/config"
"github.com/gomarkdown/markdown"
"github.com/gregdel/pushover"
)

// SendPushoverMessage sends alert message through Pushover service
func SendPushoverMessage(message string, snapshot io.Reader) {
push := pushover.New(config.ConfigData.Alerts.Pushover.Token)
recipient := pushover.NewRecipient(config.ConfigData.Alerts.Pushover.Userkey)

// Convert message to HTML & strip newline characters
htmlMessage := string(markdown.ToHTML([]byte(message), nil, nil))
htmlMessage = strings.Replace(htmlMessage, "\n", "", -1)

// Create new message
notif := &pushover.Message{
Message: htmlMessage,
Title: config.ConfigData.Alerts.General.Title,
Priority: config.ConfigData.Alerts.Pushover.Priority,
HTML: true,
TTL: time.Duration(config.ConfigData.Alerts.Pushover.TTL) * time.Second,
}

// If emergency priority, set retry / expiration
if notif.Priority == 2 {
notif.Retry = time.Duration(config.ConfigData.Alerts.Pushover.Retry) * time.Second
notif.Expire = time.Duration(config.ConfigData.Alerts.Pushover.Expire) * time.Second
fmt.Print(notif.Retry, notif.Expire)
}

// Add target devices if specified
if config.ConfigData.Alerts.Pushover.Devices != "" {
devices := strings.ReplaceAll(config.ConfigData.Alerts.Pushover.Devices, " ", "")
notif.DeviceName = devices
}

// Send notification
if snapshot != nil {
notif.AddAttachment(snapshot)
if _, err := push.SendMessage(notif, recipient); err != nil {
log.Print("Error sending Pushover notification:", err)
return
}
} else {
if _, err := push.SendMessage(notif, recipient); err != nil {
log.Print("Error sending Pushover notification:", err)
return
}
}

log.Println("Pushover alert sent")
}
2 changes: 1 addition & 1 deletion notifier/telegram.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/gomarkdown/markdown"
)

// SendTelegramMessage pushes alert message to Discord via webhook
// SendTelegramMessage sends alert through Telegram to individual users
func SendTelegramMessage(message string, snapshot io.Reader) {
bot, err := tgbotapi.NewBotAPI(config.ConfigData.Alerts.Telegram.Token)
if err != nil {
Expand Down
Loading