Skip to content
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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10)
project(LightNVR VERSION 0.23.1 LANGUAGES C CXX)
project(LightNVR VERSION 0.23.2 LANGUAGES C CXX)

# Set C/C++ standards
set(CMAKE_C_STANDARD 11)
Expand Down
13 changes: 13 additions & 0 deletions config/lightnvr.ini
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,19 @@ qos = 1
; Retain detection messages on the broker (default: false)
retain = false

; Home Assistant MQTT auto-discovery
; When enabled, cameras and sensors automatically appear in Home Assistant
; Requires MQTT broker shared with Home Assistant
ha_discovery = false

; Discovery topic prefix (default: homeassistant)
; Must match Home Assistant's MQTT discovery prefix setting
ha_discovery_prefix = homeassistant

; Snapshot publish interval in seconds (default: 30, 0=disabled)
; Publishes JPEG camera snapshots via MQTT for HA camera entities
ha_snapshot_interval = 30

[onvif]
; ONVIF camera discovery settings
; Enable automatic discovery of ONVIF cameras on the network
Expand Down
5 changes: 5 additions & 0 deletions include/core/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ typedef struct {
int mqtt_keepalive; // MQTT keepalive interval in seconds (default: 60)
int mqtt_qos; // MQTT QoS level 0, 1, or 2 (default: 1)
bool mqtt_retain; // Retain detection messages (default: false)

// Home Assistant MQTT auto-discovery settings
bool mqtt_ha_discovery; // Enable HA MQTT auto-discovery (default: false)
char mqtt_ha_discovery_prefix[128]; // HA discovery topic prefix (default: "homeassistant")
int mqtt_ha_snapshot_interval; // Snapshot publish interval in seconds (default: 30, 0=disabled)
} config_t;

/**
Expand Down
66 changes: 65 additions & 1 deletion include/core/mqtt_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,58 @@ int mqtt_publish_detection(const char *stream_name, const detection_result_t *re

/**
* Publish a raw message to a custom topic
*
*
* @param topic Full topic path (topic_prefix is NOT automatically prepended)
* @param payload Message payload (null-terminated string)
* @param retain Whether to set the retain flag
* @return 0 on success, -1 on failure
*/
int mqtt_publish_raw(const char *topic, const char *payload, bool retain);

/**
* Publish binary data to a topic (e.g., JPEG snapshots)
*
* @param topic Full topic path
* @param data Binary data buffer
* @param len Length of data in bytes
* @param retain Whether to set the retain flag
* @return 0 on success, -1 on failure
*/
int mqtt_publish_binary(const char *topic, const void *data, size_t len, bool retain);

/**
* Publish Home Assistant MQTT discovery messages for all configured streams.
* Publishes camera, binary_sensor (motion), and sensor (object counts) entities.
* Called on connect and when stream configuration changes.
*
* @return 0 on success, -1 on failure
*/
int mqtt_publish_ha_discovery(void);

/**
* Update the motion state for a camera stream.
* Publishes ON to the motion topic when detection occurs.
* After a timeout with no new detections, publishes OFF.
*
* @param stream_name Name of the stream
* @param result Detection results (NULL to force OFF)
*/
void mqtt_set_motion_state(const char *stream_name, const detection_result_t *result);

/**
* Start Home Assistant services (snapshot timer, motion timeout checker).
* Should be called after MQTT is connected and HA discovery is published.
*
* @return 0 on success, -1 on failure
*/
int mqtt_start_ha_services(void);

/**
* Stop Home Assistant services.
* Should be called before MQTT cleanup.
*/
void mqtt_stop_ha_services(void);

/**
* Disconnect from the MQTT broker gracefully
*/
Expand All @@ -66,6 +110,16 @@ void mqtt_disconnect(void);
*/
void mqtt_cleanup(void);

/**
* Reinitialize MQTT client with current configuration.
* Performs cleanup → init → connect → HA discovery/services.
* Used for hot-reload when settings change from the web UI.
*
* @param config Pointer to the (updated) application configuration
* @return 0 on success, -1 on failure
*/
int mqtt_reinit(const config_t *config);

#else /* ENABLE_MQTT not defined */

/* Stub implementations when MQTT is disabled */
Expand All @@ -78,8 +132,18 @@ static inline int mqtt_publish_detection(const char *stream_name, const detectio
static inline int mqtt_publish_raw(const char *topic, const char *payload, bool retain) {
(void)topic; (void)payload; (void)retain; return 0;
}
static inline int mqtt_publish_binary(const char *topic, const void *data, size_t len, bool retain) {
(void)topic; (void)data; (void)len; (void)retain; return 0;
}
static inline int mqtt_publish_ha_discovery(void) { return 0; }
static inline void mqtt_set_motion_state(const char *stream_name, const detection_result_t *result) {
(void)stream_name; (void)result;
}
static inline int mqtt_start_ha_services(void) { return 0; }
static inline void mqtt_stop_ha_services(void) {}
static inline void mqtt_disconnect(void) {}
static inline void mqtt_cleanup(void) {}
static inline int mqtt_reinit(const config_t *config) { (void)config; return 0; }

#endif /* ENABLE_MQTT */

Expand Down
4 changes: 2 additions & 2 deletions include/core/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

#define LIGHTNVR_VERSION_MAJOR 0
#define LIGHTNVR_VERSION_MINOR 23
#define LIGHTNVR_VERSION_PATCH 1
#define LIGHTNVR_VERSION_STRING "0.23.1"
#define LIGHTNVR_VERSION_PATCH 2
#define LIGHTNVR_VERSION_STRING "0.23.2"

#define LIGHTNVR_BUILD_DATE ""
#define LIGHTNVR_GIT_COMMIT ""
Expand Down
42 changes: 42 additions & 0 deletions src/core/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,11 @@ void load_default_config(config_t *config) {
config->mqtt_keepalive = 60; // 60 seconds keepalive
config->mqtt_qos = 1; // QoS 1 (at least once)
config->mqtt_retain = false; // Don't retain messages by default

// Home Assistant MQTT auto-discovery settings
config->mqtt_ha_discovery = false; // Disabled by default
snprintf(config->mqtt_ha_discovery_prefix, sizeof(config->mqtt_ha_discovery_prefix), "homeassistant");
config->mqtt_ha_snapshot_interval = 30; // 30 seconds default
}

// Create directory if it doesn't exist
Expand Down Expand Up @@ -810,6 +815,19 @@ static int config_ini_handler(void* user, const char* section, const char* name,
}
} else if (strcmp(name, "retain") == 0) {
config->mqtt_retain = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
} else if (strcmp(name, "ha_discovery") == 0 || strcmp(name, "ha_discovery_enabled") == 0) {
config->mqtt_ha_discovery = (strcmp(value, "true") == 0 || strcmp(value, "1") == 0);
} else if (strcmp(name, "ha_discovery_prefix") == 0) {
strncpy(config->mqtt_ha_discovery_prefix, value, sizeof(config->mqtt_ha_discovery_prefix) - 1);
config->mqtt_ha_discovery_prefix[sizeof(config->mqtt_ha_discovery_prefix) - 1] = '\0';
} else if (strcmp(name, "ha_snapshot_interval") == 0) {
config->mqtt_ha_snapshot_interval = atoi(value);
if (config->mqtt_ha_snapshot_interval < 0) {
config->mqtt_ha_snapshot_interval = 0; // 0 = disabled
}
if (config->mqtt_ha_snapshot_interval > 300) {
config->mqtt_ha_snapshot_interval = 300; // Maximum 5 minutes
}
}
}

Expand Down Expand Up @@ -1336,6 +1354,30 @@ int save_config(const config_t *config, const char *path) {
fprintf(file, "turn_password = %s\n", config->turn_password);
}

// Write MQTT settings
fprintf(file, "\n[mqtt]\n");
fprintf(file, "enabled = %s\n", config->mqtt_enabled ? "true" : "false");
if (config->mqtt_broker_host[0] != '\0') {
fprintf(file, "broker_host = %s\n", config->mqtt_broker_host);
}
fprintf(file, "broker_port = %d\n", config->mqtt_broker_port);
if (config->mqtt_username[0] != '\0') {
fprintf(file, "username = %s\n", config->mqtt_username);
}
if (config->mqtt_password[0] != '\0') {
fprintf(file, "password = %s\n", config->mqtt_password);
}
fprintf(file, "client_id = %s\n", config->mqtt_client_id);
fprintf(file, "topic_prefix = %s\n", config->mqtt_topic_prefix);
fprintf(file, "tls_enabled = %s\n", config->mqtt_tls_enabled ? "true" : "false");
fprintf(file, "keepalive = %d\n", config->mqtt_keepalive);
fprintf(file, "qos = %d\n", config->mqtt_qos);
fprintf(file, "retain = %s\n", config->mqtt_retain ? "true" : "false");
fprintf(file, "; Home Assistant MQTT auto-discovery\n");
fprintf(file, "ha_discovery = %s\n", config->mqtt_ha_discovery ? "true" : "false");
fprintf(file, "ha_discovery_prefix = %s\n", config->mqtt_ha_discovery_prefix);
fprintf(file, "ha_snapshot_interval = %d\n", config->mqtt_ha_snapshot_interval);

// Write ONVIF settings
fprintf(file, "\n[onvif]\n");
fprintf(file, "discovery_enabled = %s\n", config->onvif_discovery_enabled ? "true" : "false");
Expand Down
6 changes: 6 additions & 0 deletions src/core/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,12 @@ int main(int argc, char *argv[]) {
log_warn("Failed to connect to MQTT broker, will retry automatically");
} else {
log_info("Connected to MQTT broker");

// Publish Home Assistant discovery and start HA services if enabled
if (config.mqtt_ha_discovery) {
mqtt_publish_ha_discovery();
mqtt_start_ha_services();
}
}
}
}
Expand Down
Loading
Loading