Skip to content

Instantly share code, notes, and snippets.

@i39
Forked from chazcheadle/camera_mon.go
Created September 29, 2022 22:57
Show Gist options
  • Select an option

  • Save i39/72687d2b4c806e3ad223fa542f1a57ba to your computer and use it in GitHub Desktop.

Select an option

Save i39/72687d2b4c806e3ad223fa542f1a57ba to your computer and use it in GitHub Desktop.
Golang/MQTT service to monitor webcam snapshots and send status
package main
import (
"encoding/json"
"io"
"io/ioutil"
"os"
"sort"
"time"
log "github.com/Sirupsen/logrus"
MQTT "github.com/eclipse/paho.mqtt.golang"
)
type Cameras struct {
Cameras []Camera
}
type Camera struct {
CameraID string `json:"camera_id"`
CameraName string `json:"camera_name"`
ImageDir string `json:"image_dir"`
LastImage string `json:"last_image"`
Online bool `json:"online"`
StatusCode string `json:"status_code"`
Timestamp time.Time `json:"timestamp"`
ErrorCount int `json:"error_count"`
}
type Data struct {
Online bool
Timestamp time.Time
}
// Create struct to be used by our custom sort function.
type ByTime []os.FileInfo
// Len, Swap and Less functions for sorting files by ModTime.
func (f ByTime) Len() int { return len(f) }
func (f ByTime) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
func (f ByTime) Less(i, j int) bool { return f[i].ModTime().After(f[j].ModTime()) }
var (
image_dirs = []string{"/tmp/hab_camera-1", "/tmp/hab_camera-2"}
mqttBroker = "tcp://HOSTNAME:8883"
mqttClientId = "Camera-Monitor"
errorThreshold = 2
snapshotCount = 0
)
func copyFile(src, dst string) (err error) {
in, err := os.Open(src)
if err != nil {
return
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return
}
defer func() {
cerr := out.Close()
if err == nil {
err = cerr
}
}()
if _, err = io.Copy(out, in); err != nil {
return
}
err = out.Sync()
return
}
func main() {
cameras := Cameras{
Cameras: []Camera{{"hab_camera-1", "Exterior (South)", "/tmp/hab_camera-1", "", false, "", time.Now(), 0}, {"hab_camera-2", "Exterior IR (South)", "/tmp/hab_camera-2", "", false, "", time.Now(), 0}},
}
opts := MQTT.NewClientOptions()
opts.AddBroker(mqttBroker)
opts.SetClientID(mqttClientId)
c := MQTT.NewClient(opts)
if token := c.Connect(); token.Wait() && token.Error() != nil {
log.Warn("Could not connect to: ", mqttBroker)
}
for index, cam := range cameras.Cameras {
ticker := time.NewTicker(time.Millisecond * 3000)
go func(index int, cam Camera) {
for timestamp := range ticker.C {
files, err := ioutil.ReadDir(cam.ImageDir)
if err != nil {
log.Fatal(err)
}
// Sort files by time if there happens to be more than one.
if len(files) > 1 {
sort.Sort(ByTime(files))
}
if len(files) > 0 {
// Check if the last image has been updated.
if cameras.Cameras[index].LastImage == files[0].Name() {
// Allow a threshold for unchanged images due to camera software performance.
if cameras.Cameras[index].ErrorCount >= errorThreshold {
cameras.Cameras[index].Online = false
cameras.Cameras[index].StatusCode = "No new image found."
} else {
cameras.Cameras[index].ErrorCount++
}
} else {
cameras.Cameras[index].Online = true
cameras.Cameras[index].ErrorCount = 0
cameras.Cameras[index].StatusCode = ""
if snapshotCount == 0 || snapshotCount > 4 {
cerr := copyFile(cam.ImageDir+"/"+files[0].Name(), cam.ImageDir+"/out.jpg")
if cerr != nil {
log.Warn("File copy failed")
log.Warn(cerr)
}
snapshotCount = 0
}
snapshotCount++
}
cameras.Cameras[index].LastImage = files[0].Name()
cameras.Cameras[index].Timestamp = timestamp
}
// Build message data packet and json.Marshal
var data = &Data{}
data.Timestamp = timestamp
data.Online = cameras.Cameras[index].Online
messageData, _ := json.Marshal(data)
// Send MQTT message
token := c.Publish("test/cameras/"+cam.CameraID, 0, true, messageData)
token.Wait()
}
}(index, cam)
}
select {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment