mirror of
https://github.com/Warky-Devs/nvr-notify-api.git
synced 2025-05-18 23:27:32 +00:00
Added /hikvision/alarm
This commit is contained in:
parent
9f02e73dc3
commit
b81d6afecb
@ -3,12 +3,14 @@ package main
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -22,6 +24,9 @@ type Config struct {
|
|||||||
TelegramEnabled bool `json:"telegram_enabled"`
|
TelegramEnabled bool `json:"telegram_enabled"`
|
||||||
TelegramToken string `json:"telegram_token"`
|
TelegramToken string `json:"telegram_token"`
|
||||||
TelegramChatID string `json:"telegram_chat_id"`
|
TelegramChatID string `json:"telegram_chat_id"`
|
||||||
|
HikEnabled bool `json:"hik_enabled"`
|
||||||
|
HikUsername string `json:"hik_username"`
|
||||||
|
HikPassword string `json:"hik_password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// VivotekEvent represents the event data structure from Vivotek NVR
|
// VivotekEvent represents the event data structure from Vivotek NVR
|
||||||
@ -34,6 +39,34 @@ type VivotekEvent struct {
|
|||||||
// Add more fields as needed based on Vivotek's event structure
|
// Add more fields as needed based on Vivotek's event structure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HikVisionEvent represents the alarm data structure from HIKVision
|
||||||
|
type HikVisionEvent struct {
|
||||||
|
EventType string `json:"eventType"`
|
||||||
|
EventTime time.Time `json:"eventTime"`
|
||||||
|
DeviceID string `json:"deviceId"`
|
||||||
|
ChannelID string `json:"channelId"`
|
||||||
|
EventDetails map[string]interface{} `json:"eventDetails"`
|
||||||
|
// Raw XML data for debugging/logging
|
||||||
|
RawXML string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HIKVisionAlarm represents the XML structure of a HIKVision alarm event
|
||||||
|
type HIKVisionAlarm struct {
|
||||||
|
XMLName xml.Name `xml:"EventNotificationAlert"`
|
||||||
|
IPAddress string `xml:"ipAddress"`
|
||||||
|
PortNo int `xml:"portNo"`
|
||||||
|
ProtocolType string `xml:"protocolType"`
|
||||||
|
MacAddress string `xml:"macAddress"`
|
||||||
|
ChannelID int `xml:"channelID"`
|
||||||
|
DateTime string `xml:"dateTime"`
|
||||||
|
ActivePostCount int `xml:"activePostCount"`
|
||||||
|
EventType string `xml:"eventType"`
|
||||||
|
EventState string `xml:"eventState"`
|
||||||
|
EventDescription string `xml:"eventDescription"`
|
||||||
|
// Optional fields that may be present in some events
|
||||||
|
DetectionRegionID int `xml:"detectionRegionID,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// GlobalState maintains the application state
|
// GlobalState maintains the application state
|
||||||
type GlobalState struct {
|
type GlobalState struct {
|
||||||
Config Config
|
Config Config
|
||||||
@ -42,13 +75,14 @@ type GlobalState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var state GlobalState
|
var state GlobalState
|
||||||
|
var startTime time.Time
|
||||||
|
|
||||||
// initConfig loads configuration from a JSON file
|
// initConfig loads configuration from a JSON file
|
||||||
func initConfig() error {
|
func initConfig() error {
|
||||||
// Default configuration
|
// Default configuration
|
||||||
state.Config = Config{
|
state.Config = Config{
|
||||||
ServerPort: "8080",
|
ServerPort: "8080",
|
||||||
LogFile: "vivotek_events.log",
|
LogFile: "nvr_events.log",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to load from config file if it exists
|
// Try to load from config file if it exists
|
||||||
@ -74,9 +108,7 @@ func initConfig() error {
|
|||||||
logOutput = file
|
logOutput = file
|
||||||
}
|
}
|
||||||
|
|
||||||
state.Logger = log.New(logOutput, "VIVOTEK-API: ", log.LstdFlags)
|
state.Logger = log.New(logOutput, "NVR-API: ", log.LstdFlags)
|
||||||
|
|
||||||
fmt.Printf("Config loaded, handing off logs to %s...\n", state.Config.LogFile)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +123,7 @@ func basicAuth(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
|
|
||||||
username, password, ok := r.BasicAuth()
|
username, password, ok := r.BasicAuth()
|
||||||
if !ok || username != state.Config.AuthUsername || password != state.Config.AuthPassword {
|
if !ok || username != state.Config.AuthUsername || password != state.Config.AuthPassword {
|
||||||
w.Header().Set("WWW-Authenticate", `Basic realm="Vivotek NVR API"`)
|
w.Header().Set("WWW-Authenticate", `Basic realm="NVR API"`)
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
w.Write([]byte("Unauthorized"))
|
w.Write([]byte("Unauthorized"))
|
||||||
return
|
return
|
||||||
@ -145,22 +177,187 @@ func handleEvent(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(response)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
// processEvent handles different event types
|
// handleHikVisionAlarm processes alarm events from HIKVision devices
|
||||||
func processEvent(event *VivotekEvent) {
|
func handleHikVisionAlarm(w http.ResponseWriter, r *http.Request) {
|
||||||
switch event.EventType {
|
if r.Method != http.MethodPost && r.Method != http.MethodGet {
|
||||||
case "MotionDetection":
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||||
handleMotionEvent(event)
|
w.Write([]byte("Only POST and GET methods are supported"))
|
||||||
case "VideoLoss":
|
return
|
||||||
handleVideoLossEvent(event)
|
|
||||||
case "DeviceConnection":
|
|
||||||
handleConnectionEvent(event)
|
|
||||||
default:
|
|
||||||
state.Logger.Printf("Unhandled event type: %s", event.EventType)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward to notification URL if configured
|
// Check for specific HIK authentication if enabled
|
||||||
if state.Config.NotifyURL != "" {
|
if state.Config.HikEnabled && state.Config.HikUsername != "" {
|
||||||
forwardEvent(event)
|
username, password, ok := r.BasicAuth()
|
||||||
|
if !ok || username != state.Config.HikUsername || password != state.Config.HikPassword {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
w.Write([]byte("Unauthorized for HIKVision integration"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the request body
|
||||||
|
body, err := io.ReadAll(r.Body)
|
||||||
|
if err != nil {
|
||||||
|
state.Logger.Printf("Error reading HIKVision request body: %v", err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the XML alarm data
|
||||||
|
var hikAlarm HIKVisionAlarm
|
||||||
|
err = xml.Unmarshal(body, &hikAlarm)
|
||||||
|
if err != nil {
|
||||||
|
state.Logger.Printf("Error parsing HIKVision XML: %v", err)
|
||||||
|
state.Logger.Printf("Raw payload: %s", string(body))
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to our standard event format
|
||||||
|
event := convertHikVisionAlarm(hikAlarm, string(body))
|
||||||
|
|
||||||
|
// Log the event
|
||||||
|
state.EventCount++
|
||||||
|
state.Logger.Printf("Received HIKVision alarm #%d: Type=%s, Device=%s, Channel=%s",
|
||||||
|
state.EventCount, event.EventType, event.DeviceID, event.ChannelID)
|
||||||
|
|
||||||
|
// Process the event based on type
|
||||||
|
processEvent(&event)
|
||||||
|
|
||||||
|
// Respond with success
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
response := map[string]interface{}{
|
||||||
|
"status": "success",
|
||||||
|
"message": "HIKVision alarm processed successfully",
|
||||||
|
"eventId": state.EventCount,
|
||||||
|
}
|
||||||
|
|
||||||
|
// HIKVision may expect XML response, but most implementations work fine with JSON
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertHikVisionAlarm converts HIKVision alarm format to our standard event format
|
||||||
|
func convertHikVisionAlarm(hikAlarm HIKVisionAlarm, rawXML string) HikVisionEvent {
|
||||||
|
// Parse the datetime from HIKVision format
|
||||||
|
eventTime, err := time.Parse("2006-01-02T15:04:05-07:00", hikAlarm.DateTime)
|
||||||
|
if err != nil {
|
||||||
|
// If standard format fails, try alternative formats
|
||||||
|
eventTime, err = time.Parse("2006-01-02T15:04:05Z", hikAlarm.DateTime)
|
||||||
|
if err != nil {
|
||||||
|
// If all parsing fails, use current time
|
||||||
|
eventTime = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map HIKVision event types to standardized types
|
||||||
|
eventType := mapHikEventType(hikAlarm.EventType)
|
||||||
|
|
||||||
|
// Create device ID from IP if available
|
||||||
|
deviceID := fmt.Sprintf("HIK_%s", hikAlarm.IPAddress)
|
||||||
|
if hikAlarm.MacAddress != "" {
|
||||||
|
deviceID = fmt.Sprintf("HIK_%s", strings.ReplaceAll(hikAlarm.MacAddress, ":", ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create channel ID
|
||||||
|
channelID := fmt.Sprintf("Channel%d", hikAlarm.ChannelID)
|
||||||
|
|
||||||
|
// Create event details map
|
||||||
|
eventDetails := map[string]interface{}{
|
||||||
|
"source": "HIKVision",
|
||||||
|
"ipAddress": hikAlarm.IPAddress,
|
||||||
|
"description": hikAlarm.EventDescription,
|
||||||
|
"state": hikAlarm.EventState,
|
||||||
|
"macAddress": hikAlarm.MacAddress,
|
||||||
|
"originalType": hikAlarm.EventType,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add optional fields if present
|
||||||
|
if hikAlarm.DetectionRegionID > 0 {
|
||||||
|
eventDetails["regionId"] = hikAlarm.DetectionRegionID
|
||||||
|
}
|
||||||
|
|
||||||
|
return HikVisionEvent{
|
||||||
|
EventType: eventType,
|
||||||
|
EventTime: eventTime,
|
||||||
|
DeviceID: deviceID,
|
||||||
|
ChannelID: channelID,
|
||||||
|
EventDetails: eventDetails,
|
||||||
|
RawXML: rawXML,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapHikEventType converts HIKVision event types to our standardized types
|
||||||
|
func mapHikEventType(hikType string) string {
|
||||||
|
// Map HIKVision event types to standardized types
|
||||||
|
// HIKVision has many event types, this is a simplified mapping
|
||||||
|
hikType = strings.ToLower(hikType)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.Contains(hikType, "motion"):
|
||||||
|
return "MotionDetection"
|
||||||
|
case strings.Contains(hikType, "videoloss"):
|
||||||
|
return "VideoLoss"
|
||||||
|
case strings.Contains(hikType, "tamper") || strings.Contains(hikType, "shelteralarm"):
|
||||||
|
return "TamperDetection"
|
||||||
|
case strings.Contains(hikType, "disk"):
|
||||||
|
return "StorageFailure"
|
||||||
|
case strings.Contains(hikType, "line") || strings.Contains(hikType, "crossing"):
|
||||||
|
return "LineCrossing"
|
||||||
|
case strings.Contains(hikType, "intrusion"):
|
||||||
|
return "IntrusionDetection"
|
||||||
|
case strings.Contains(hikType, "face"):
|
||||||
|
return "FaceDetection"
|
||||||
|
case strings.Contains(hikType, "io") || strings.Contains(hikType, "alarm"):
|
||||||
|
return "IOAlarm"
|
||||||
|
case strings.Contains(hikType, "connection"):
|
||||||
|
return "DeviceConnection"
|
||||||
|
default:
|
||||||
|
return "UnknownEvent_" + hikType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// processEvent handles different event types
|
||||||
|
func processEvent(event interface{}) {
|
||||||
|
// Process based on event type
|
||||||
|
switch e := event.(type) {
|
||||||
|
case *VivotekEvent:
|
||||||
|
switch e.EventType {
|
||||||
|
case "MotionDetection":
|
||||||
|
handleMotionEvent(e)
|
||||||
|
case "VideoLoss":
|
||||||
|
handleVideoLossEvent(e)
|
||||||
|
case "DeviceConnection":
|
||||||
|
handleConnectionEvent(e)
|
||||||
|
default:
|
||||||
|
state.Logger.Printf("Unhandled Vivotek event type: %s", e.EventType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward to notification URL if configured
|
||||||
|
if state.Config.NotifyURL != "" {
|
||||||
|
forwardEvent(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
case *HikVisionEvent:
|
||||||
|
switch e.EventType {
|
||||||
|
case "MotionDetection":
|
||||||
|
handleHikMotionEvent(e)
|
||||||
|
case "VideoLoss":
|
||||||
|
handleHikVideoLossEvent(e)
|
||||||
|
case "LineCrossing", "IntrusionDetection":
|
||||||
|
handleHikSmartEvent(e)
|
||||||
|
case "IOAlarm":
|
||||||
|
handleHikIOAlarmEvent(e)
|
||||||
|
case "DeviceConnection":
|
||||||
|
handleHikConnectionEvent(e)
|
||||||
|
default:
|
||||||
|
state.Logger.Printf("Unhandled HIKVision event type: %s", e.EventType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward to notification URL if configured
|
||||||
|
if state.Config.NotifyURL != "" {
|
||||||
|
forwardHikEvent(e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send to Telegram if enabled
|
// Send to Telegram if enabled
|
||||||
@ -187,6 +384,37 @@ func handleConnectionEvent(event *VivotekEvent) {
|
|||||||
// Add custom processing for connection events
|
// Add custom processing for connection events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleHikMotionEvent processes HIKVision motion detection events
|
||||||
|
func handleHikMotionEvent(event *HikVisionEvent) {
|
||||||
|
state.Logger.Printf("HIKVision motion detected on device %s, channel %s", event.DeviceID, event.ChannelID)
|
||||||
|
// Add custom processing for HIKVision motion events
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleHikVideoLossEvent processes HIKVision video loss events
|
||||||
|
func handleHikVideoLossEvent(event *HikVisionEvent) {
|
||||||
|
state.Logger.Printf("HIKVision video lost on device %s, channel %s", event.DeviceID, event.ChannelID)
|
||||||
|
// Add custom processing for HIKVision video loss events
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleHikSmartEvent processes HIKVision smart events (line crossing, intrusion)
|
||||||
|
func handleHikSmartEvent(event *HikVisionEvent) {
|
||||||
|
state.Logger.Printf("HIKVision smart event %s on device %s, channel %s",
|
||||||
|
event.EventType, event.DeviceID, event.ChannelID)
|
||||||
|
// Add custom processing for HIKVision smart events
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleHikIOAlarmEvent processes HIKVision IO alarm events
|
||||||
|
func handleHikIOAlarmEvent(event *HikVisionEvent) {
|
||||||
|
state.Logger.Printf("HIKVision IO alarm on device %s, channel %s", event.DeviceID, event.ChannelID)
|
||||||
|
// Add custom processing for HIKVision IO events
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleHikConnectionEvent processes HIKVision device connection events
|
||||||
|
func handleHikConnectionEvent(event *HikVisionEvent) {
|
||||||
|
state.Logger.Printf("HIKVision connection event for device %s", event.DeviceID)
|
||||||
|
// Add custom processing for HIKVision connection events
|
||||||
|
}
|
||||||
|
|
||||||
// forwardEvent sends the event to a configured notification URL
|
// forwardEvent sends the event to a configured notification URL
|
||||||
func forwardEvent(event *VivotekEvent) {
|
func forwardEvent(event *VivotekEvent) {
|
||||||
eventJSON, err := json.Marshal(event)
|
eventJSON, err := json.Marshal(event)
|
||||||
@ -207,9 +435,29 @@ func forwardEvent(event *VivotekEvent) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// forwardHikEvent forwards HIKVision events to notification URL
|
||||||
|
func forwardHikEvent(event *HikVisionEvent) {
|
||||||
|
eventJSON, err := json.Marshal(event)
|
||||||
|
if err != nil {
|
||||||
|
state.Logger.Printf("Error serializing HIKVision event for forwarding: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Post(state.Config.NotifyURL, "application/json", bytes.NewBuffer(eventJSON))
|
||||||
|
if err != nil {
|
||||||
|
state.Logger.Printf("Error forwarding HIKVision event: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
state.Logger.Printf("Error response from notification URL for HIKVision event: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sendTelegramNotification sends event information to a Telegram chat/bot
|
// sendTelegramNotification sends event information to a Telegram chat/bot
|
||||||
func sendTelegramNotification(event *VivotekEvent) {
|
func sendTelegramNotification(event interface{}) {
|
||||||
// Format the message
|
// Format the message based on event type
|
||||||
message := formatTelegramMessage(event)
|
message := formatTelegramMessage(event)
|
||||||
|
|
||||||
// Construct the Telegram Bot API URL
|
// Construct the Telegram Bot API URL
|
||||||
@ -234,48 +482,111 @@ func sendTelegramNotification(event *VivotekEvent) {
|
|||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
state.Logger.Printf("Telegram API error: status=%d, response=%s", resp.StatusCode, string(body))
|
state.Logger.Printf("Telegram API error: status=%d, response=%s", resp.StatusCode, string(body))
|
||||||
} else {
|
} else {
|
||||||
state.Logger.Printf("Telegram notification sent successfully for event type %s", event.EventType)
|
// Log success based on event type
|
||||||
|
switch e := event.(type) {
|
||||||
|
case *VivotekEvent:
|
||||||
|
state.Logger.Printf("Telegram notification sent successfully for Vivotek event type %s", e.EventType)
|
||||||
|
case *HikVisionEvent:
|
||||||
|
state.Logger.Printf("Telegram notification sent successfully for HIKVision event type %s", e.EventType)
|
||||||
|
default:
|
||||||
|
state.Logger.Printf("Telegram notification sent successfully for unknown event type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// formatTelegramMessage creates a human-readable message for Telegram
|
// formatTelegramMessage creates a human-readable message for Telegram
|
||||||
func formatTelegramMessage(event *VivotekEvent) string {
|
func formatTelegramMessage(event interface{}) string {
|
||||||
// Basic message with event details
|
var message string
|
||||||
message := fmt.Sprintf("<b>🚨 Vivotek NVR Alert</b>\n\n"+
|
|
||||||
"<b>Event:</b> %s\n"+
|
|
||||||
"<b>Time:</b> %s\n"+
|
|
||||||
"<b>Device:</b> %s\n"+
|
|
||||||
"<b>Channel:</b> %s\n",
|
|
||||||
event.EventType,
|
|
||||||
event.EventTime.Format("2006-01-02 15:04:05"),
|
|
||||||
event.DeviceID,
|
|
||||||
event.ChannelID)
|
|
||||||
|
|
||||||
// Add custom message based on event type
|
switch e := event.(type) {
|
||||||
switch event.EventType {
|
case *VivotekEvent:
|
||||||
case "MotionDetection":
|
// Basic message with event details
|
||||||
message += "📹 <b>Motion detected!</b>"
|
message = fmt.Sprintf("<b>🚨 NVR Alert</b>\n\n"+
|
||||||
|
"<b>Event:</b> %s\n"+
|
||||||
|
"<b>Time:</b> %s\n"+
|
||||||
|
"<b>Device:</b> %s\n"+
|
||||||
|
"<b>Channel:</b> %s\n",
|
||||||
|
e.EventType,
|
||||||
|
e.EventTime.Format("2006-01-02 15:04:05"),
|
||||||
|
e.DeviceID,
|
||||||
|
e.ChannelID)
|
||||||
|
|
||||||
// Add zone info if available
|
// Add custom message based on event type
|
||||||
if zone, ok := event.EventDetails["zoneId"].(string); ok {
|
switch e.EventType {
|
||||||
message += fmt.Sprintf(" (Zone: %s)", zone)
|
case "MotionDetection":
|
||||||
|
message += "📹 <b>Motion detected!</b>"
|
||||||
|
|
||||||
|
// Add zone info if available
|
||||||
|
if zone, ok := e.EventDetails["zoneId"].(string); ok {
|
||||||
|
message += fmt.Sprintf(" (Zone: %s)", zone)
|
||||||
|
}
|
||||||
|
|
||||||
|
case "VideoLoss":
|
||||||
|
message += "⚠️ <b>Video signal lost!</b> Please check camera connection."
|
||||||
|
|
||||||
|
case "DeviceConnection":
|
||||||
|
if status, ok := e.EventDetails["status"].(string); ok && status == "disconnected" {
|
||||||
|
message += "❌ <b>Device disconnected!</b> Network issue possible."
|
||||||
|
} else {
|
||||||
|
message += "✅ <b>Device connected</b> and operating normally."
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Add any available details for unknown event types
|
||||||
|
detailsJSON, _ := json.Marshal(e.EventDetails)
|
||||||
|
if len(detailsJSON) > 0 {
|
||||||
|
message += fmt.Sprintf("\n<pre>%s</pre>", string(detailsJSON))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case "VideoLoss":
|
case *HikVisionEvent:
|
||||||
message += "⚠️ <b>Video signal lost!</b> Please check camera connection."
|
// HIKVision specific formatting
|
||||||
|
message = fmt.Sprintf("<b>🔔 HIKVision Alarm</b>\n\n"+
|
||||||
|
"<b>Event:</b> %s\n"+
|
||||||
|
"<b>Time:</b> %s\n"+
|
||||||
|
"<b>Device:</b> %s\n"+
|
||||||
|
"<b>Channel:</b> %s\n",
|
||||||
|
e.EventType,
|
||||||
|
e.EventTime.Format("2006-01-02 15:04:05"),
|
||||||
|
e.DeviceID,
|
||||||
|
e.ChannelID)
|
||||||
|
|
||||||
case "DeviceConnection":
|
// Add description if available
|
||||||
if status, ok := event.EventDetails["status"].(string); ok && status == "disconnected" {
|
if desc, ok := e.EventDetails["description"].(string); ok && desc != "" {
|
||||||
message += "❌ <b>Device disconnected!</b> Network issue possible."
|
message += fmt.Sprintf("<b>Description:</b> %s\n", desc)
|
||||||
} else {
|
|
||||||
message += "✅ <b>Device connected</b> and operating normally."
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
// Add custom message based on event type
|
||||||
// Add any available details for unknown event types
|
switch e.EventType {
|
||||||
detailsJSON, _ := json.Marshal(event.EventDetails)
|
case "MotionDetection":
|
||||||
if len(detailsJSON) > 0 {
|
message += "📹 <b>Motion detected!</b>"
|
||||||
message += fmt.Sprintf("\n<pre>%s</pre>", string(detailsJSON))
|
|
||||||
|
case "LineCrossing":
|
||||||
|
message += "🚷 <b>Line crossing detected!</b>"
|
||||||
|
|
||||||
|
case "IntrusionDetection":
|
||||||
|
message += "🚨 <b>Intrusion detected!</b>"
|
||||||
|
|
||||||
|
case "FaceDetection":
|
||||||
|
message += "👤 <b>Face detected!</b>"
|
||||||
|
|
||||||
|
case "IOAlarm":
|
||||||
|
message += "🔌 <b>I/O Alarm triggered!</b>"
|
||||||
|
|
||||||
|
case "TamperDetection":
|
||||||
|
message += "⚠️ <b>Camera tampering detected!</b>"
|
||||||
|
|
||||||
|
case "VideoLoss":
|
||||||
|
message += "⚠️ <b>Video signal lost!</b>"
|
||||||
|
|
||||||
|
case "StorageFailure":
|
||||||
|
message += "💾 <b>Storage failure!</b> Check NVR hard drive."
|
||||||
|
|
||||||
|
default:
|
||||||
|
// For unknown events, include available details
|
||||||
|
if state, ok := e.EventDetails["state"].(string); ok {
|
||||||
|
message += fmt.Sprintf("\n<b>State:</b> %s", state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,12 +605,9 @@ func healthCheck(w http.ResponseWriter, r *http.Request) {
|
|||||||
json.NewEncoder(w).Encode(response)
|
json.NewEncoder(w).Encode(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
var startTime time.Time
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
fmt.Print("Starting NVR API...\n")
|
fmt.Print("Starting NVR API...\n")
|
||||||
|
|
||||||
// Initialize configuration
|
// Initialize configuration
|
||||||
if err := initConfig(); err != nil {
|
if err := initConfig(); err != nil {
|
||||||
log.Fatalf("Failed to initialize configuration: %v", err)
|
log.Fatalf("Failed to initialize configuration: %v", err)
|
||||||
@ -310,6 +618,9 @@ func main() {
|
|||||||
http.HandleFunc("/event", basicAuth(handleEvent))
|
http.HandleFunc("/event", basicAuth(handleEvent))
|
||||||
http.HandleFunc("/events", basicAuth(handleEvent)) // Alternative endpoint
|
http.HandleFunc("/events", basicAuth(handleEvent)) // Alternative endpoint
|
||||||
|
|
||||||
|
// Add HIKVision alarm server endpoint
|
||||||
|
http.HandleFunc("/hikvision/alarm/hikvision/alarm", basicAuth(handleHikVisionAlarm))
|
||||||
|
|
||||||
// Start the HTTP server
|
// Start the HTTP server
|
||||||
serverAddr := fmt.Sprintf(":%s", state.Config.ServerPort)
|
serverAddr := fmt.Sprintf(":%s", state.Config.ServerPort)
|
||||||
state.Logger.Printf("Starting NVR Event Handler API on %s", serverAddr)
|
state.Logger.Printf("Starting NVR Event Handler API on %s", serverAddr)
|
||||||
|
Loading…
Reference in New Issue
Block a user