Skip to content

Commit

Permalink
#5 Add cli lib (#6)
Browse files Browse the repository at this point in the history
* adds library for processing cli arguments

* go mod tidy
  • Loading branch information
isaric authored Dec 12, 2022
1 parent b28fc25 commit 26f7957
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 78 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 4,8 @@ go 1.19

require (
github.com/isaric/go-posix-time v1.1.2
github.com/slack-go/slack v0.11.0
github.com/jessevdk/go-flags v1.4.0
github.com/slack-go/slack v0.11.4
github.com/use-go/onvif v0.0.1
gopkg.in/xmlpath.v2 v2.0.0-20150820204837-860cbeca3ebc
)
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 24,17 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/ ClbZu8SPxiqlu12wZP/3sWm
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55 EU/adAjf1fMHhE=
github.com/isaric/go-posix-time v1.1.2 h1:4FGcjMGR7XKFbiE64jpbUevHBXqMVCXykE/zGbVrnVI=
github.com/isaric/go-posix-time v1.1.2/go.mod h1:obM8z4QYGl9iONV vFO3N/u2iUC/oVv8Zf6r4UBxn7s=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/leodido/go-urn v1.2.0/go.mod h1: 8 nEpDfqqsY g338gtMEUOtuK 4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/slack-go/slack v0.11.0 h1:sBBjQz8LY 6eeWhGJNZpRm5jvLRNnWBFZ/cAq58a6k=
github.com/slack-go/slack v0.11.0/go.mod h1:hlGi5oXA Gt yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/slack-go/slack v0.11.4 h1:ojSa7KlPm3PqY2AomX4VTxEsK5eci5JaxCjlzGV5zoM=
github.com/slack-go/slack v0.11.4/go.mod h1:hlGi5oXA Gt yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF rwdDfMAkV7OtwuqBVzrE8GR6GFx wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn 9EI=
Expand Down
92 changes: 37 additions & 55 deletions motion-poll.go
Original file line number Diff line number Diff line change
@@ -1,31 1,22 @@
package main

import (
"fmt"
"github.com/jessevdk/go-flags"
"github.com/slack-go/slack"
"github.com/use-go/onvif"
"github.com/use-go/onvif/event"
"gopkg.in/xmlpath.v2"
"io/ioutil"

"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"time"
)

/**
Script for polling an ONVIF camera and getting motion events - specifically designed for Hisseu cameras
CLI args:
0 - url and port, separated by semicolon
1 - username
2 - password
3 - camera name (or location)
4 - slack hook url
5 - (optional) cooldown time after motion event detected
6 - (optional) slack bot token
7 - (optional) slack channel id
8 - (optional) json message template for sprintf ex. ./motion-poll "${<file.json}"
Script for polling an ONVIF camera and getting motion events - specifically designed for Hisseu cameras
*/

const ssErrorTemplate = "Error while getting snapshot %s\n"
Expand All @@ -43,85 34,64 @@ xmlns:tt="http://www.onvif.org/ver10/schema">
`

func main() {
// get and validate number of cli args
args := os.Args[1:]
if len(args) < 5 {
fmt.Println("Not enough arguments given. There must be at least 5! Exiting!")
var opts options
_, err := flags.ParseArgs(&opts, os.Args)

if err != nil {
println("Invalid command line arguments! Exiting!")
return
}

// make initial pull point subscription
cam, _ := onvif.NewDevice(args[0])
cam.Authenticate(args[1], args[2])
cam, _ := onvif.NewDevice(opts.Host)
cam.Authenticate(opts.Username, opts.Password)
res := &event.CreatePullPointSubscription{SubscriptionPolicy: event.SubscriptionPolicy{ChangedOnly: true},
InitialTerminationTime: event.AbsoluteOrRelativeTimeType{
Duration: "PT300S",
}}
_, err := cam.CallMethod(res)
_, err = cam.CallMethod(res)
if err != nil {
fmt.Printf("Aborting due to err when subscribing %s", err)
return
}

// get Slack message from template - use default if cli arg is not given
camName := args[3]
var msgT, botToken, channelID string
if len(args) > 7 {
botToken = args[6]
channelID = args[7]
}
if len(args) > 8 {
msgT = args[8]
} else {
msgT = "Motion detected at %s"
}

//get cooldown time from args, default 10 seconds
cooldown := 10
if len(args) > 5 {
convInt, err := strconv.Atoi(args[5])
if err == nil {
cooldown = convInt
} else {
fmt.Printf("Could not parse cooldown time %s with error %s Defaulting to: %d\n", args[5], err, cooldown)
}
}

r, err := http.Post(fmt.Sprintf("http://%s", args[0]), "application/soap xml", strings.NewReader(fmt.Sprintf(soap, "000")))
// retrieve the snapshot url
r, err := http.Post(fmt.Sprintf("http://%s", opts.Host), "application/soap xml", strings.NewReader(fmt.Sprintf(soap, "000")))
ssUrl := ""
path := xmlpath.MustCompile("//*/Uri")
if err == nil {
data, _ := ioutil.ReadAll(r.Body)
data, _ := io.ReadAll(r.Body)
root, _ := xmlpath.Parse(strings.NewReader(string(data)))
if err == nil {
ssUrl, _ = path.String(root)
}
}
fmt.Printf("Snapshot url is %s\n", ssUrl)
slackClient := slack.New(botToken)

slackClient := slack.New(opts.SlackBotToken)

// continue polling for motion events. if motion is detected, send Slack notification
for true {
r2, _ := cam.CallMethod(event.PullMessages{})
bodyBytes, _ := ioutil.ReadAll(r2.Body)
bodyBytes, _ := io.ReadAll(r2.Body)
bodyS := string(bodyBytes)
if strings.Contains(bodyS, "<tt:SimpleItem Name=\"IsMotion\" Value=\"true\" />") {
msg := fmt.Sprintf(msgT, camName)
err = slack.PostWebhook(args[4], &slack.WebhookMessage{Text: msg})
msg := fmt.Sprintf(opts.MessageTemplate, opts.CameraName)
err = slack.PostWebhook(opts.SlackHook, &slack.WebhookMessage{Text: msg})
if err != nil {
fmt.Printf("there was an error while posting the slack notification %s", err)
}
if ssUrl != "" && botToken != "" && channelID != "" {
getSnapshot(ssUrl, channelID, *slackClient)
if ssUrl != "" && opts.SlackBotToken != "token" && opts.SlackChannelID != "" {
getAndUploadSnapshot(ssUrl, opts.SlackChannelID, *slackClient)
}
time.Sleep(time.Duration(cooldown) * time.Second)
time.Sleep(time.Duration(opts.CooldownTimer) * time.Second)
}
time.Sleep(1 * time.Second)
}

}

func getSnapshot(url, channelID string, slackClient slack.Client) {
func getAndUploadSnapshot(url, channelID string, slackClient slack.Client) {
r, e := http.Get(url)
if e != nil {
fmt.Printf(ssErrorTemplate, e)
Expand All @@ -144,3 114,15 @@ func getSnapshot(url, channelID string, slackClient slack.Client) {
fmt.Printf("error while posting snapshot %s", err)
}
}

type options struct {
Username string `short:"u" long:"user" description:"The username for authenticating to the ONVIF device" required:"true"`
Password string `short:"p" long:"password" description:"The password for authenticating to the ONVIF device" required:"true"`
Host string `short:"h" long:"host" description:"The address of the ONVIF device and its port separated by semicolon" required:"true"`
CameraName string `short:"n" long:"name" description:"The name or location of the ONVIF device that will appear in all notifications" required:"true"`
SlackHook string `short:"s" long:"slack-hook" description:"The address of the slack hook to which notifications will be posted" required:"true"`
CooldownTimer int `short:"c" long:"cooldown" description:"The integer value of the number of seconds after an event has occurred before polling resumes" required:"false" default:"10"`
SlackChannelID string `short:"cid" long:"channel-id" description:"The ID of the slack channel where snapshots will be posted if provided" required:"false"`
SlackBotToken string `short:"bt" long:"bot-token" description:"The token for the slack bot that will upload a snapshot if provided" required:"false" default:"token"`
MessageTemplate string `short:"mt" long:"message-template" description:"The message template in JSON format to use for notifications instead of the default one" required:"false" default:"Motion detected at %s"`
}
36 changes: 16 additions & 20 deletions set-time.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 3,32 @@ package main
import (
"fmt"
"github.com/isaric/go-posix-time/pkg/p_time"
"github.com/jessevdk/go-flags"
"github.com/use-go/onvif"
"github.com/use-go/onvif/device"
"github.com/use-go/onvif/xsd"
onvif2 "github.com/use-go/onvif/xsd/onvif"
"os"
"strconv"
"time"
)

/*
*
Script for setting the date and time on an ONVIF camera- specifically designed for Hisseu cameras
CLI args:
0 - url and port, separated by semicolon
1 - username
2 - password
*/
func main() {
// get and validate number of cli args
args := os.Args[1:]
if len(args) < 3 {
fmt.Println("Not enough arguments given. There must be at least 3! Exiting!")
var opts timeOptions
_, err := flags.ParseArgs(&opts, os.Args)

if err != nil {
println("Invalid command line arguments! Exiting!")
return
}

// create device and authenticate
cam, _ := onvif.NewDevice(args[0])
cam.Authenticate(args[1], args[2])

var interval = 30
if len(args) > 4 {
convInt, err := strconv.Atoi(args[4])
if err == nil {
interval = convInt
}
}
cam, _ := onvif.NewDevice(opts.Host)
cam.Authenticate(opts.Username, opts.Password)

// repeat call after interval passes
for true {
Expand All @@ -50,7 39,7 @@ func main() {
fmt.Printf("Could not set time. %s Exiting!\n", err)
return
}
time.Sleep(time.Duration(interval) * time.Minute)
time.Sleep(time.Duration(opts.Interval) * time.Minute)
}

}
Expand All @@ -76,3 65,10 @@ func getOnvifDateTime(ct time.Time) device.SetSystemDateAndTime {
Day xsd.Int
}{Year: xsd.Int(ct.Year()), Month: xsd.Int(ct.Month()), Day: xsd.Int(ct.Day())})})}
}

type timeOptions struct {
Username string `short:"u" long:"user" description:"The username for authenticating to the ONVIF device" required:"true"`
Password string `short:"p" long:"password" description:"The password for authenticating to the ONVIF device" required:"true"`
Host string `short:"h" long:"host" description:"The address of the ONVIF device and its port separated by semicolon" required:"true"`
Interval int `short:"i" long:"interval" description:"The integer representing the number of minutes to pass between polls" required:"false" default:"10"`
}

0 comments on commit 26f7957

Please sign in to comment.