feat: remote commands, systemd units, process observability, broker auth split
- Command intake (reboot/shutdown) on infoscreen/{uuid}/commands with ack lifecycle
- MQTT_USER/MQTT_PASSWORD_BROKER split from identity vars; configure_mqtt_security() updated
- infoscreen-simclient.service: Type=notify, WatchdogSec=60, Restart=on-failure
- infoscreen-notify-failure@.service + script: retained MQTT alert when systemd gives up (Gap 3)
- _sd_notify() watchdog keepalive in simclient main loop (Gap 1)
- broker_connection block in health payload: reconnect_count, last_disconnect_at (Gap 2)
- COMMAND_MOCK_REBOOT_IMMEDIATE_COMPLETE canary flag with safety guard
- SERVER_TEAM_ACTIONS.md: server-side integration action items
- Docs: README, CHANGELOG, src/README, copilot-instructions updated
- 43 tests passing
This commit is contained in:
@@ -36,6 +36,31 @@ fi
|
||||
|
||||
BROKER="${MQTT_BROKER:-localhost}"
|
||||
PORT="${MQTT_PORT:-1883}"
|
||||
MQTT_USERNAME="${MQTT_USERNAME:-}"
|
||||
MQTT_PASSWORD="${MQTT_PASSWORD:-}"
|
||||
MQTT_TLS_ENABLED="${MQTT_TLS_ENABLED:-0}"
|
||||
MQTT_TLS_CA_CERT="${MQTT_TLS_CA_CERT:-}"
|
||||
MQTT_TLS_CERT="${MQTT_TLS_CERT:-}"
|
||||
MQTT_TLS_KEY="${MQTT_TLS_KEY:-}"
|
||||
MQTT_TLS_INSECURE="${MQTT_TLS_INSECURE:-0}"
|
||||
|
||||
MQTT_AUTH_ARGS=()
|
||||
MQTT_TLS_ARGS=()
|
||||
|
||||
if [[ -n "$MQTT_USERNAME" ]]; then
|
||||
MQTT_AUTH_ARGS+=( -u "$MQTT_USERNAME" )
|
||||
fi
|
||||
if [[ -n "$MQTT_PASSWORD" ]]; then
|
||||
MQTT_AUTH_ARGS+=( -P "$MQTT_PASSWORD" )
|
||||
fi
|
||||
if [[ "$MQTT_TLS_ENABLED" == "1" || "$MQTT_TLS_ENABLED" == "true" || "$MQTT_TLS_ENABLED" == "yes" ]]; then
|
||||
[[ -n "$MQTT_TLS_CA_CERT" ]] && MQTT_TLS_ARGS+=( --cafile "$MQTT_TLS_CA_CERT" )
|
||||
[[ -n "$MQTT_TLS_CERT" ]] && MQTT_TLS_ARGS+=( --cert "$MQTT_TLS_CERT" )
|
||||
[[ -n "$MQTT_TLS_KEY" ]] && MQTT_TLS_ARGS+=( --key "$MQTT_TLS_KEY" )
|
||||
if [[ "$MQTT_TLS_INSECURE" == "1" || "$MQTT_TLS_INSECURE" == "true" || "$MQTT_TLS_INSECURE" == "yes" ]]; then
|
||||
MQTT_TLS_ARGS+=( --insecure )
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Read runtime IDs ─────────────────────────────────────────────────────────
|
||||
GROUP_ID_FILE="$PROJECT_ROOT/src/config/last_group_id.txt"
|
||||
@@ -107,7 +132,7 @@ EOF
|
||||
echo -e "${YELLOW}Publishing to: $topic${NC}"
|
||||
echo "$payload" | python3 -m json.tool 2>/dev/null || echo "$payload"
|
||||
echo ""
|
||||
mosquitto_pub -h "$BROKER" -p "$PORT" -t "$topic" -q 1 --retain -m "$payload"
|
||||
mosquitto_pub -h "$BROKER" -p "$PORT" "${MQTT_AUTH_ARGS[@]}" "${MQTT_TLS_ARGS[@]}" -t "$topic" -q 1 --retain -m "$payload"
|
||||
echo -e "${GREEN}Published (retained, QoS 1)${NC}"
|
||||
echo "intent_id: $intent_id"
|
||||
}
|
||||
@@ -119,7 +144,7 @@ clear_intent() {
|
||||
echo -e "${RED}No group_id found.${NC}"
|
||||
return 1
|
||||
fi
|
||||
mosquitto_pub -h "$BROKER" -p "$PORT" -t "$topic" -q 1 --retain --null-message
|
||||
mosquitto_pub -h "$BROKER" -p "$PORT" "${MQTT_AUTH_ARGS[@]}" "${MQTT_TLS_ARGS[@]}" -t "$topic" -q 1 --retain --null-message
|
||||
echo -e "${GREEN}Retained intent cleared from broker${NC}"
|
||||
}
|
||||
|
||||
@@ -167,7 +192,7 @@ subscribe_power_state() {
|
||||
echo -e "${YELLOW}Subscribing to: $topic${NC}"
|
||||
echo "(Ctrl-C to stop)"
|
||||
echo ""
|
||||
mosquitto_sub -h "$BROKER" -p "$PORT" -t "$topic" | \
|
||||
mosquitto_sub -h "$BROKER" -p "$PORT" "${MQTT_AUTH_ARGS[@]}" "${MQTT_TLS_ARGS[@]}" -t "$topic" | \
|
||||
python3 -c "
|
||||
import sys, json
|
||||
for line in sys.stdin:
|
||||
@@ -216,7 +241,7 @@ while true; do
|
||||
echo -e "${RED}No group_id.${NC}"
|
||||
else
|
||||
TOPIC="$(group_topic)"
|
||||
mosquitto_pub -h "$BROKER" -p "$PORT" -t "$TOPIC" -q 1 --retain \
|
||||
mosquitto_pub -h "$BROKER" -p "$PORT" "${MQTT_AUTH_ARGS[@]}" "${MQTT_TLS_ARGS[@]}" -t "$TOPIC" -q 1 --retain \
|
||||
-m '{"schema_version":"1.0","desired_state":"on"}'
|
||||
echo -e "${YELLOW}⚠ Malformed intent published - client must reject with 'missing required field' in log${NC}"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user