#!/bin/sh # SecuBox MQTT Bridge RPC backend . /lib/functions.sh . /usr/share/libubox/jshn.sh . /usr/share/mqtt-bridge/usb-database.sh ZIGBEE_VENDOR="0424" ZIGBEE_PRODUCT="2134" ZIGBEE_PROFILE_ID="zigbee_usb2134" find_usb_tty() { local base="$1" local sub node tty for sub in "$base" "$base"/* "$base"/*/*; do [ -d "$sub/tty" ] || continue for node in "$sub"/tty/*; do [ -e "$node" ] || continue tty="$(basename "$node")" if [ -e "/dev/$tty" ]; then echo "/dev/$tty" return 0 fi done done return 1 } append_zigbee_profile() { local matched=0 local dev for dev in /sys/bus/usb/devices/*; do [ -f "$dev/idVendor" ] || continue [ -f "$dev/idProduct" ] || continue local vendor product vendor="$(cat "$dev/idVendor" 2>/dev/null)" product="$(cat "$dev/idProduct" 2>/dev/null)" [ "$vendor" = "$ZIGBEE_VENDOR" ] || continue [ "$product" = "$ZIGBEE_PRODUCT" ] || continue matched=1 local busnum devnum label port busnum="$(cat "$dev/busnum" 2>/dev/null)" devnum="$(cat "$dev/devnum" 2>/dev/null)" if [ -f "$dev/product" ]; then label="$(cat "$dev/product")" else label="SMSC USB2134B" fi port="$(find_usb_tty "$dev")" json_add_object json_add_string "id" "$ZIGBEE_PROFILE_ID" json_add_string "label" "$label Zigbee Bridge" json_add_string "vendor" "$vendor" json_add_string "product" "$product" [ -n "$busnum" ] && json_add_string "bus" "$busnum" [ -n "$devnum" ] && json_add_string "device" "$devnum" [ -n "$port" ] && json_add_string "port" "$port" json_add_boolean "detected" 1 json_add_string "notes" "Detected via USB bus $busnum device $devnum" json_close_object done if [ "$matched" -eq 0 ]; then json_add_object json_add_string "id" "$ZIGBEE_PROFILE_ID" json_add_string "label" "SMSC USB2134B Zigbee Bridge" json_add_string "vendor" "$ZIGBEE_VENDOR" json_add_string "product" "$ZIGBEE_PRODUCT" json_add_boolean "detected" 0 json_add_string "notes" "Connect the dongle (Bus 003 Device 002: ID 0424:2134) to auto-fill tty port" json_close_object fi } add_adapter_json() { local section="$1" local enabled vendor product title preset notes detected port bus device health last_seen config_get enabled "$section" enabled "1" config_get vendor "$section" vendor config_get product "$section" product config_get title "$section" title config_get preset "$section" preset config_get notes "$section" notes config_get detected "$section" detected config_get port "$section" port config_get bus "$section" bus config_get device "$section" device config_get health "$section" health config_get last_seen "$section" last_seen json_add_object json_add_string "id" "$section" [ -n "$title" ] && json_add_string "label" "$title" [ -n "$preset" ] && json_add_string "preset" "$preset" [ -n "$vendor" ] && json_add_string "vendor" "$vendor" [ -n "$product" ] && json_add_string "product" "$product" json_add_boolean "enabled" "${enabled:-0}" json_add_boolean "detected" "${detected:-0}" [ -n "$port" ] && json_add_string "port" "$port" [ -n "$bus" ] && json_add_string "bus" "$bus" [ -n "$device" ] && json_add_string "device" "$device" [ -n "$health" ] && json_add_string "health" "$health" [ -n "$last_seen" ] && json_add_string "last_seen" "$last_seen" [ -n "$notes" ] && json_add_string "notes" "$notes" json_close_object } append_configured_adapters() { json_add_array "adapters" config_load mqtt-bridge config_foreach add_adapter_json adapter json_close_array } add_template_json() { local section="$1" local device_type topic qos retain config_get device_type "$section" device_type config_get topic "$section" topic config_get qos "$section" qos config_get retain "$section" retain json_add_object json_add_string "id" "$section" [ -n "$device_type" ] && json_add_string "device_type" "$device_type" [ -n "$topic" ] && json_add_string "topic" "$topic" [ -n "$qos" ] && json_add_string "qos" "$qos" [ -n "$retain" ] && json_add_string "retain" "$retain" json_close_object } append_templates() { json_add_array "templates" config_load mqtt-bridge config_foreach add_template_json template json_close_array } add_rule_json() { local section="$1" local type adapter when action message topic config_get type "$section" type config_get adapter "$section" adapter config_get when "$section" when config_get action "$section" action config_get message "$section" message config_get topic "$section" topic json_add_object json_add_string "id" "$section" [ -n "$type" ] && json_add_string "type" "$type" [ -n "$adapter" ] && json_add_string "adapter" "$adapter" [ -n "$when" ] && json_add_string "when" "$when" [ -n "$action" ] && json_add_string "action" "$action" [ -n "$message" ] && json_add_string "message" "$message" [ -n "$topic" ] && json_add_string "topic" "$topic" json_close_object } append_rules() { json_add_array "rules" config_load mqtt-bridge config_foreach add_rule_json rule json_close_array } apply_adapter_settings() { local adapter_keys json_get_keys adapter_keys for adapter in $adapter_keys; do json_select "$adapter" || continue local enabled label port vendor product preset json_get_var enabled enabled json_get_var label label json_get_var port port json_get_var vendor vendor json_get_var product product json_get_var preset preset json_select .. [ -n "$adapter" ] || continue if ! uci -q get mqtt-bridge.adapter."$adapter" >/dev/null 2>&1; then uci set mqtt-bridge.adapter."$adapter"="adapter" fi [ -n "$enabled" ] && uci set mqtt-bridge.adapter."$adapter".enabled="$enabled" if [ -n "$label" ]; then uci set mqtt-bridge.adapter."$adapter".title="$label" else uci delete mqtt-bridge.adapter."$adapter".title >/dev/null 2>&1 fi if [ -n "$port" ]; then uci set mqtt-bridge.adapter."$adapter".port="$port" else uci delete mqtt-bridge.adapter."$adapter".port >/dev/null 2>&1 fi [ -n "$vendor" ] && uci set mqtt-bridge.adapter."$adapter".vendor="$vendor" [ -n "$product" ] && uci set mqtt-bridge.adapter."$adapter".product="$product" [ -n "$preset" ] && uci set mqtt-bridge.adapter."$adapter".preset="$preset" done } status() { json_init json_add_string "broker" "$(uci -q get mqtt-bridge.broker.host || echo 'localhost')" json_add_int "clients" "$(uci -q get mqtt-bridge.stats.clients || echo 0)" local adapters="$(ls /dev/ttyUSB* 2>/dev/null | wc -l)" json_add_int "adapters" "$adapters" json_add_int "messages_per_sec" "$(uci -q get mqtt-bridge.stats.mps || echo 0)" json_add_int "retained" "$(uci -q get mqtt-bridge.stats.retained || echo 0)" json_add_string "uptime" "$(uci -q get mqtt-bridge.stats.uptime || echo '0s')" json_add_string "last_event" "$(uci -q get mqtt-bridge.stats.last_event || echo '')" # Device statistics local total=0 online=0 while true; do local ser=$(uci -q get mqtt-bridge.device.@device[$total].serial) [ -z "$ser" ] && break local is_on=$(uci -q get mqtt-bridge.device.@device[$total].online || echo 0) [ "$is_on" = "1" ] && online=$((online + 1)) total=$((total + 1)) done json_add_object "device_stats" json_add_int "total" "$total" json_add_int "online" "$online" json_add_int "usb" "$adapters" json_close_object json_add_array "recent_payloads" local idx=0 while true; do local topic=$(uci -q get mqtt-bridge.payloads.@payload[$idx].topic) [ -z "$topic" ] && break json_add_object json_add_string "topic" "$topic" json_add_string "payload" "$(uci -q get mqtt-bridge.payloads.@payload[$idx].data)" json_add_string "timestamp" "$(uci -q get mqtt-bridge.payloads.@payload[$idx].time)" json_close_object idx=$((idx + 1)) done json_close_array json_add_object "settings" json_add_string "host" "$(uci -q get mqtt-bridge.broker.host || echo '127.0.0.1')" json_add_int "port" "$(uci -q get mqtt-bridge.broker.port || echo 1883)" json_add_string "username" "$(uci -q get mqtt-bridge.broker.username || echo '')" json_add_string "password" "" json_add_string "base_topic" "$(uci -q get mqtt-bridge.bridge.base_topic || echo 'secubox/+/state')" json_add_int "retention" "$(uci -q get mqtt-bridge.bridge.retention || echo 7)" json_close_object json_add_array "profiles" append_zigbee_profile json_close_array append_configured_adapters append_templates append_rules json_dump } list_devices() { json_init json_add_array "devices" local idx=0 while true; do local serial=$(uci -q get mqtt-bridge.device.@device[$idx].serial) [ -z "$serial" ] && break json_add_object json_add_string "serial" "$serial" json_add_string "name" "$(uci -q get mqtt-bridge.device.@device[$idx].name)" json_add_string "protocol" "$(uci -q get mqtt-bridge.device.@device[$idx].protocol || echo 'USB')" json_add_string "port" "$(uci -q get mqtt-bridge.device.@device[$idx].port || echo '-')" json_add_boolean "online" "$(uci -q get mqtt-bridge.device.@device[$idx].online || echo 0)" json_close_object idx=$((idx + 1)) done json_close_array json_dump } trigger_pairing() { json_init touch /tmp/mqtt-bridge-pairing json_add_boolean "success" 1 json_add_string "message" "pairing_window_open" json_dump } apply_settings() { read input json_load "$input" json_get_var host host json_get_var port port json_get_var username username json_get_var password password json_get_var base_topic base_topic json_get_var retention retention json_get_type adapters_type adapters if [ "$adapters_type" = "object" ]; then json_select adapters apply_adapter_settings json_select .. fi json_cleanup [ -n "$host" ] && uci set mqtt-bridge.broker.host="$host" [ -n "$port" ] && uci set mqtt-bridge.broker.port="$port" [ -n "$username" ] && uci set mqtt-bridge.broker.username="$username" [ -n "$password" ] && uci set mqtt-bridge.broker.password="$password" [ -n "$base_topic" ] && uci set mqtt-bridge.bridge.base_topic="$base_topic" [ -n "$retention" ] && uci set mqtt-bridge.bridge.retention="$retention" uci commit mqtt-bridge json_init json_add_boolean "success" 1 json_add_string "message" "settings_saved" json_dump } rescan_adapters() { /usr/sbin/mqtt-bridge --rescan >/dev/null 2>&1 & json_init json_add_boolean "success" 1 json_add_string "message" "rescan_triggered" json_dump } reset_adapter() { read input json_load "$input" json_get_var adapter adapter json_cleanup if [ -z "$adapter" ]; then json_init json_add_boolean "success" 0 json_add_string "error" "missing_adapter" json_dump return fi uci delete mqtt-bridge.adapter."$adapter".port >/dev/null 2>&1 uci delete mqtt-bridge.adapter."$adapter".bus >/dev/null 2>&1 uci delete mqtt-bridge.adapter."$adapter".device >/dev/null 2>&1 uci delete mqtt-bridge.adapter."$adapter".detected >/dev/null 2>&1 uci delete mqtt-bridge.adapter."$adapter".health >/dev/null 2>&1 uci delete mqtt-bridge.adapter."$adapter".last_seen >/dev/null 2>&1 uci commit mqtt-bridge json_init json_add_boolean "success" 1 json_add_string "message" "adapter_reset" json_dump } # === NEW USB DETECTION RPC METHODS === get_usb_devices() { json_init json_add_array "devices" for dev in /sys/bus/usb/devices/*; do [ -f "$dev/idVendor" ] || continue [ -f "$dev/idProduct" ] || continue local vendor="$(cat "$dev/idVendor" 2>/dev/null)" local product="$(cat "$dev/idProduct" 2>/dev/null)" local busnum="$(cat "$dev/busnum" 2>/dev/null)" local devnum="$(cat "$dev/devnum" 2>/dev/null)" local manufacturer="$(cat "$dev/manufacturer" 2>/dev/null || echo "Unknown")" local prod_name="$(cat "$dev/product" 2>/dev/null || echo "Unknown")" local adapter_type="$(detect_adapter_type "$vendor" "$product")" local device_name="$(get_device_name "$vendor" "$product")" local port="$(find_usb_tty "$dev")" json_add_object json_add_string "vid" "$vendor" json_add_string "pid" "$product" json_add_string "bus" "$busnum" json_add_string "device" "$devnum" json_add_string "manufacturer" "$manufacturer" json_add_string "product" "$prod_name" json_add_string "type" "$adapter_type" json_add_string "name" "$device_name" [ -n "$port" ] && json_add_string "port" "$port" json_close_object done json_close_array json_dump } detect_iot_adapters() { json_init detect_zigbee_adapters detect_zwave_adapters detect_modbus_adapters json_dump } get_serial_ports() { json_init list_serial_ports json_dump } get_adapter_info() { read input json_load "$input" json_get_var adapter_id adapter json_cleanup if [ -z "$adapter_id" ]; then json_init json_add_boolean "success" 0 json_add_string "error" "missing_adapter_id" json_dump return fi json_init # Get UCI configuration local enabled="$(uci -q get mqtt-bridge.adapter."$adapter_id".enabled || echo 0)" local type="$(uci -q get mqtt-bridge.adapter."$adapter_id".type || echo 'unknown')" local title="$(uci -q get mqtt-bridge.adapter."$adapter_id".title || echo 'Unknown Adapter')" local vendor="$(uci -q get mqtt-bridge.adapter."$adapter_id".vendor || echo '')" local product="$(uci -q get mqtt-bridge.adapter."$adapter_id".product || echo '')" local port="$(uci -q get mqtt-bridge.adapter."$adapter_id".port || echo '')" local detected="$(uci -q get mqtt-bridge.adapter."$adapter_id".detected || echo 0)" local health="$(uci -q get mqtt-bridge.adapter."$adapter_id".health || echo 'unknown')" json_add_string "id" "$adapter_id" json_add_boolean "enabled" "$enabled" json_add_string "type" "$type" json_add_string "title" "$title" json_add_string "vid" "$vendor" json_add_string "pid" "$product" json_add_string "port" "$port" json_add_boolean "detected" "$detected" json_add_string "health" "$health" # Add type-specific config case "$type" in zigbee) json_add_string "baud" "$(uci -q get mqtt-bridge.adapter."$adapter_id".baud || echo '115200')" json_add_string "channel" "$(uci -q get mqtt-bridge.adapter."$adapter_id".channel || echo '11')" json_add_string "pan_id" "$(uci -q get mqtt-bridge.adapter."$adapter_id".pan_id || echo '0x1A62')" ;; zwave) json_add_string "baud" "$(uci -q get mqtt-bridge.adapter."$adapter_id".baud || echo '115200')" ;; modbus) json_add_string "baud" "$(uci -q get mqtt-bridge.adapter."$adapter_id".baud || echo '9600')" json_add_string "parity" "$(uci -q get mqtt-bridge.adapter."$adapter_id".parity || echo 'N')" json_add_string "databits" "$(uci -q get mqtt-bridge.adapter."$adapter_id".databits || echo '8')" json_add_string "stopbits" "$(uci -q get mqtt-bridge.adapter."$adapter_id".stopbits || echo '1')" json_add_string "slave_id" "$(uci -q get mqtt-bridge.adapter."$adapter_id".slave_id || echo '1')" ;; esac # Check current USB presence if VID:PID known if [ -n "$vendor" ] && [ -n "$product" ]; then local found=0 for dev in /sys/bus/usb/devices/*; do [ -f "$dev/idVendor" ] || continue local v="$(cat "$dev/idVendor")" local p="$(cat "$dev/idProduct")" if [ "$v" = "$vendor" ] && [ "$p" = "$product" ]; then found=1 local current_port="$(find_usb_tty "$dev")" [ -n "$current_port" ] && json_add_string "current_port" "$current_port" break fi done json_add_boolean "usb_present" "$found" fi json_dump } test_connection() { read input json_load "$input" json_get_var port port json_cleanup if [ -z "$port" ]; then json_init json_add_boolean "success" 0 json_add_string "error" "missing_port" json_dump return fi json_init if test_serial_port "$port"; then json_add_boolean "success" 1 json_add_boolean "accessible" 1 json_add_string "message" "port_accessible" else json_add_boolean "success" 1 json_add_boolean "accessible" 0 json_add_string "message" "port_not_accessible" fi json_dump } configure_adapter() { read input json_load "$input" json_get_var adapter_id id json_get_var enabled enabled json_get_var type type json_get_var title title json_get_var vendor vid json_get_var product pid json_get_var port port json_cleanup if [ -z "$adapter_id" ]; then json_init json_add_boolean "success" 0 json_add_string "error" "missing_adapter_id" json_dump return fi # Create or update adapter section uci -q get mqtt-bridge.adapter."$adapter_id" >/dev/null 2>&1 || { uci set mqtt-bridge.adapter."$adapter_id"=adapter } [ -n "$enabled" ] && uci set mqtt-bridge.adapter."$adapter_id".enabled="$enabled" [ -n "$type" ] && uci set mqtt-bridge.adapter."$adapter_id".type="$type" [ -n "$title" ] && uci set mqtt-bridge.adapter."$adapter_id".title="$title" [ -n "$vendor" ] && uci set mqtt-bridge.adapter."$adapter_id".vendor="$vendor" [ -n "$product" ] && uci set mqtt-bridge.adapter."$adapter_id".product="$product" [ -n "$port" ] && uci set mqtt-bridge.adapter."$adapter_id".port="$port" # Mark as detected if port is present if [ -n "$port" ] && [ -c "$port" ]; then uci set mqtt-bridge.adapter."$adapter_id".detected=1 uci set mqtt-bridge.adapter."$adapter_id".health="online" else uci set mqtt-bridge.adapter."$adapter_id".detected=0 uci set mqtt-bridge.adapter."$adapter_id".health="unknown" fi uci commit mqtt-bridge json_init json_add_boolean "success" 1 json_add_string "message" "adapter_configured" json_add_string "id" "$adapter_id" json_dump } get_adapter_status() { json_init json_add_array "adapters" # Iterate through all adapter sections in UCI local idx=0 while uci -q get mqtt-bridge.@adapter[$idx] >/dev/null 2>&1; do local adapter_id="$(uci -q get mqtt-bridge.@adapter[$idx])" idx=$((idx + 1)) done # Get named adapter sections for adapter_id in $(uci -q show mqtt-bridge.adapter 2>/dev/null | grep '=adapter$' | cut -d. -f3 | cut -d= -f1); do local enabled="$(uci -q get mqtt-bridge.adapter."$adapter_id".enabled || echo 0)" local type="$(uci -q get mqtt-bridge.adapter."$adapter_id".type || echo 'unknown')" local title="$(uci -q get mqtt-bridge.adapter."$adapter_id".title || echo 'Unknown')" local vendor="$(uci -q get mqtt-bridge.adapter."$adapter_id".vendor || echo '')" local product="$(uci -q get mqtt-bridge.adapter."$adapter_id".product || echo '')" local port="$(uci -q get mqtt-bridge.adapter."$adapter_id".port || echo '')" # Real-time health check local health="unknown" local usb_present=0 local port_accessible=0 # Check USB presence if [ -n "$vendor" ] && [ -n "$product" ]; then for dev in /sys/bus/usb/devices/*; do [ -f "$dev/idVendor" ] || continue local v="$(cat "$dev/idVendor")" local p="$(cat "$dev/idProduct")" if [ "$v" = "$vendor" ] && [ "$p" = "$product" ]; then usb_present=1 local detected_port="$(find_usb_tty "$dev")" [ -n "$detected_port" ] && port="$detected_port" break fi done fi # Check port accessibility if [ -n "$port" ] && test_serial_port "$port"; then port_accessible=1 health="online" elif [ "$usb_present" = "1" ]; then health="error" elif [ -n "$port" ]; then health="missing" fi json_add_object json_add_string "id" "$adapter_id" json_add_boolean "enabled" "$enabled" json_add_string "type" "$type" json_add_string "title" "$title" json_add_string "port" "$port" json_add_string "health" "$health" json_add_boolean "usb_present" "$usb_present" json_add_boolean "port_accessible" "$port_accessible" json_close_object done json_close_array json_dump } case "$1" in list) cat <<'JSON' { "status": {}, "list_devices": {}, "trigger_pairing": {}, "apply_settings": {}, "rescan_adapters": {}, "reset_adapter": {}, "get_usb_devices": {}, "detect_iot_adapters": {}, "get_serial_ports": {}, "get_adapter_info": { "adapter": "string" }, "test_connection": { "port": "string" }, "configure_adapter": { "id": "string", "enabled": "boolean", "type": "string", "title": "string", "vid": "string", "pid": "string", "port": "string" }, "get_adapter_status": {} } JSON ;; call) case "$2" in status) status ;; list_devices) list_devices ;; trigger_pairing) trigger_pairing ;; apply_settings) apply_settings ;; rescan_adapters) rescan_adapters ;; reset_adapter) reset_adapter ;; get_usb_devices) get_usb_devices ;; detect_iot_adapters) detect_iot_adapters ;; get_serial_ports) get_serial_ports ;; get_adapter_info) get_adapter_info ;; test_connection) test_connection ;; configure_adapter) configure_adapter ;; get_adapter_status) get_adapter_status ;; *) json_init json_add_boolean "success" 0 json_add_string "error" "unknown_method" json_dump ;; esac ;; esac