Skip to content
Open
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
6 changes: 6 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,12 @@ func (c *Config) Load() *Config {
if camConfig.GetBool("rawTcp") {
camera.BrokenHttp = true
}
if camConfig.GetBool("publishImages") {
camera.PublishImages = true
}
if camConfig.GetBool("sendXML") {
camera.SendXML = true
}
if myConfig.Debug {
fmt.Printf("Added Hikvision camera:\n"+
" name: %s \n"+
Expand Down
4 changes: 4 additions & 0 deletions docs/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ hikvision:
username: admin
password: admin1234
rawTcp: false
publishImages: true
myDoorbell:
address: 192.168.1.13
https: false
username: admin
password: admin666
# USE RAW TCP IF HTTP STREAMING DOES NOT WORK
rawTcp: true
publishImages: false
# SEND XML from device in message rather than just eventDescription
sendXML: true

hisilicon:
enabled: true
Expand Down
12 changes: 9 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,18 @@ func main() {
}
}

messageHandler := func(cameraName string, eventType string, extra string) {
messageHandler := func(cameraName string, eventType string, extra interface{}) {
if config.Mqtt.Enabled {
mqttBus.SendMessage(config.Mqtt.TopicRoot+"/"+cameraName+"/"+eventType, extra)
}
if config.Webhooks.Enabled {
webhookBus.SendMessage(cameraName, eventType, extra)
if config.Webhooks.Enabled {
switch v := extra.(type) {
case string:
webhookBus.SendMessage(cameraName, eventType, v)
default:
webhookBus.SendMessage(cameraName, eventType, "")
}

}
}

Expand Down
4 changes: 2 additions & 2 deletions servers/dahua/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Server struct {
Debug bool
WaitGroup *sync.WaitGroup
Cameras *[]DhCamera
MessageHandler func(cameraName string, eventType string, extra string)
MessageHandler func(cameraName string, eventType string, extra interface{})
}

type DhEvent struct {
Expand Down Expand Up @@ -254,7 +254,7 @@ func (server *Server) Start() {

if server.MessageHandler == nil {
fmt.Println("DAHUA: Message handler is not set for Dahua cams - that's probably not what you want")
server.MessageHandler = func(cameraName string, eventType string, extra string) {
server.MessageHandler = func(cameraName string, eventType string, extra interface{}) {
fmt.Printf("DAHUA: Lost alarm: %s - %s: %s\n", cameraName, eventType, extra)
}
}
Expand Down
4 changes: 2 additions & 2 deletions servers/ftp/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type Server struct {
AllowFiles bool
RootPath string
Password string
MessageHandler func(cameraName string, eventType string, extra string)
MessageHandler func(cameraName string, eventType string, extra interface{})
}

type Event struct {
Expand All @@ -25,7 +25,7 @@ type Event struct {
func (serv *Server) Start() {
if serv.MessageHandler == nil {
fmt.Println("FTP: Message handler is not set for FTP server - that's probably not what you want")
serv.MessageHandler = func(cameraName string, eventType string, extra string) {
serv.MessageHandler = func(cameraName string, eventType string, extra interface{}) {
fmt.Printf("FTP: Lost alarm: %s - %s: %s\n", cameraName, eventType, extra)
}
}
Expand Down
127 changes: 107 additions & 20 deletions servers/hikvision/httpEventReader.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"github.com/icholy/digest"
"io"
"strings"
"bytes"
"log"
"mime"
"mime/multipart"
Expand All @@ -17,6 +19,57 @@ type HttpEventReader struct {
client *http.Client
}

func BoundaryFilter(r io.Reader, boundary string) io.Reader{
b := make([]byte, len(boundary)+2)
copy(b, "--")
copy(b[2:], boundary)
return &boundaryFilter{r, b, true}
}

type boundaryFilter struct {
r io.Reader
boundary []byte
hasEndBoundary bool
}


// This Hikvision camera has a bug in that it sends an extra boundary at the end of
// its linedetect alarm, after it has sent the image. This code detects that it has
// already sent the boundary and removes the boundary from the next buffer
func(t *boundaryFilter) Read(p []byte) (n int, err error) {
buf := make([]byte, len(p))
n, err = t.r.Read(buf)
if n > 0 {
i := 0
//fmt.Println(string(buf[:n]))
//fmt.Println("------------------------------------------------")
for {
pos1 := bytes.Index(buf[i:], t.boundary)
if pos1 == 0 && t.hasEndBoundary {
copy(buf[i:], buf[i+len(t.boundary)+2:])
n -= len(t.boundary) + 2
//fmt.Println("remove boundary from start, we sent it at the end of last buffer")
t.hasEndBoundary = false
continue
} else if pos1 >= 0 {
pos2 := bytes.Index(buf[i+pos1+len(t.boundary):], t.boundary)
//fmt.Printf("Found boundary at %d %d\n", pos1, pos2)
if pos2 >= 0 {
pos2 += i + pos1 + len(t.boundary)
//fmt.Printf("i:%d pos1:%d pos2: %d len:%d n-pos2-len:%d\n", i, pos1, pos2, len(t.boundary), n - pos2 - len(t.boundary))
t.hasEndBoundary = n - pos2 - len(t.boundary) <= 2
i = pos2
continue
}
}
break
}
copy(p, buf[:n])
}
return n, err
}


func (eventReader *HttpEventReader) ReadEvents(camera *HikCamera, channel chan<- HikEvent, callback func()) {
if eventReader.client == nil {
eventReader.client = &http.Client{}
Expand Down Expand Up @@ -59,56 +112,90 @@ func (eventReader *HttpEventReader) ReadEvents(camera *HikCamera, channel chan<-
callback()
return
}
multipartBoundary := params["boundary"]
multipartBoundary := params["boundary"]

xmlEvent := XmlEvent{}

// READ PART BY PART
multipartReader := multipart.NewReader(response.Body, multipartBoundary)
multipartReader := multipart.NewReader(BoundaryFilter(response.Body, multipartBoundary), multipartBoundary)
for {
part, err := multipartReader.NextPart()
if err == io.EOF {
break
}
if err != nil {
if strings.Contains(err.Error(),"connection reset by peer") {
break
}
fmt.Println(err)
continue
}
contentType := part.Header.Get("Content-Type")
contentLength, _ := strconv.Atoi(part.Header.Get("Content-Length"))
body := make([]byte, contentLength)
_, err = part.Read(body)
if err != nil {
fmt.Println(err)
pos := 0
n := 0
for pos < contentLength {
n, err = part.Read(body[pos:])
pos += n
if err != nil {
break
}
}
if pos + 2 < contentLength {
continue
}

err = xml.Unmarshal(body, &xmlEvent)
if err != nil {
fmt.Println(err)
if strings.Contains(contentType, "image/jpeg") {
if camera.PublishImages {
_, params, err := mime.ParseMediaType(part.Header.Get("Content-Disposition"))
if err == nil {
filename, ok := params["filename"]
if ok {
if eventReader.Debug {
fmt.Println("HIK: Sending an image: " + filename)
}
event := HikEvent{Camera: camera}
event.Type = "image/" + filename
event.Message = body
channel <- event
}
}
}
continue
}

// FILL IN THE CAMERA INTO FRESHLY-UNMARSHALLED EVENT
xmlEvent.Camera = camera
if strings.Contains(contentType, "application/xml") {
err = xml.Unmarshal(body, &xmlEvent)
if err != nil {
fmt.Println(err)
continue
}

if eventReader.Debug {
log.Printf("%s event: %s (%s - %d)", xmlEvent.Camera.Name, xmlEvent.Type, xmlEvent.State, xmlEvent.Id)
}
// FILL IN THE CAMERA INTO FRESHLY-UNMARSHALLED EVENT
xmlEvent.Camera = camera

switch xmlEvent.State {
case "active":
if !xmlEvent.Active {
if eventReader.Debug {
log.Printf("%s event: %s (%s - %d)", xmlEvent.Camera.Name, xmlEvent.Type, xmlEvent.State, xmlEvent.Id)
}

switch xmlEvent.State {
case "active":
if eventReader.Debug {
fmt.Println("HIK: SENDING CAMERA EVENT!")
}
event := HikEvent{Camera: camera}
event.Type = xmlEvent.Type
event.Message = xmlEvent.Description
if camera.SendXML {
event.Message = string(body)
} else {
event.Message = xmlEvent.Description
}
channel <- event
xmlEvent.Active = true
case "inactive":
xmlEvent.Active = false
}
xmlEvent.Active = true
case "inactive":
xmlEvent.Active = false
}
}
}
22 changes: 12 additions & 10 deletions servers/hikvision/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,28 @@ const (
)

type HikCamera struct {
Name string `json:"name"`
Url string `json:"url"`
Username string `json:"username"`
Password string `json:"password"`
EventReader HikEventReader
BrokenHttp bool
AuthMethod HttpAuthMethod
Name string `json:"name"`
Url string `json:"url"`
Username string `json:"username"`
Password string `json:"password"`
EventReader HikEventReader
BrokenHttp bool
AuthMethod HttpAuthMethod
PublishImages bool
SendXML bool
}

type HikEvent struct {
Type string
Message string
Message interface{}
Camera *HikCamera
}

type Server struct {
Debug bool
WaitGroup *sync.WaitGroup
Cameras *[]HikCamera
MessageHandler func(cameraName string, eventType string, extra string)
MessageHandler func(cameraName string, eventType string, extra interface{})
}

type XmlEvent struct {
Expand Down Expand Up @@ -153,7 +155,7 @@ func (server *Server) Start() {

if server.MessageHandler == nil {
fmt.Println("HIK: Message handler is not set for Hikvision cams - that's probably not what you want")
server.MessageHandler = func(cameraName string, eventType string, extra string) {
server.MessageHandler = func(cameraName string, eventType string, extra interface{}) {
fmt.Printf("HIK: Lost alarm: %s - %s: %s\n", cameraName, eventType, extra)
}
}
Expand Down
4 changes: 2 additions & 2 deletions servers/hisilicon/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type Server struct {
Debug bool
WaitGroup *sync.WaitGroup
Port string
MessageHandler func(cameraName string, eventType string, extra string)
MessageHandler func(cameraName string, eventType string, extra interface{})
}

func (server *Server) handleTcpConnection(conn net.Conn) {
Expand Down Expand Up @@ -102,7 +102,7 @@ func (server *Server) Start() {
}
if server.MessageHandler == nil {
fmt.Println("HISI: Message handler is not set for HiSilicon cams - that's probably not what you want")
server.MessageHandler = func(cameraName string, eventType string, extra string) {
server.MessageHandler = func(cameraName string, eventType string, extra interface{}) {
fmt.Printf("HISI: Lost alarm: %s - %s: %s\n", cameraName, eventType, extra)
}
}
Expand Down