@@ -288,8 +288,8 @@ collect_network() {
288288# bit 1 freq_capped_now bit 17 freq_capped_ever
289289# bit 2 throttled_now bit 18 throttled_ever
290290# bit 3 soft_temp_limit_now bit 19 soft_temp_limit_ever
291- # Sets pi_health_* globals. Returns 0 on success, 1 if vcgencmd absent or
292- # its output unparseable.
291+ # Sets the eight pi_*_now / pi_*_ever globals. Returns 0 on success, 1
292+ # if vcgencmd is absent or its output is unparseable.
293293collect_pi_throttle () {
294294 command -v vcgencmd > /dev/null 2>&1 || return 1
295295 local raw value
@@ -308,8 +308,10 @@ collect_pi_throttle() {
308308 pi_soft_temp_limit_ever=$(( (n >> 19 ) & 1 ))
309309}
310310
311- # timedatectl show -p NTPSynchronized --value -> "yes" / "no" / ""
312- collect_pi_ntp_sync () {
311+ # timedatectl show -p NTPSynchronized --value -> "yes" / "no" / "".
312+ # Universal host signal — every systemd Linux has this, not just Pis. Sits
313+ # at `system.ntp_synchronized` on the wire, alongside uptime and CPU.
314+ collect_ntp_sync () {
313315 command -v timedatectl > /dev/null 2>&1 || return 1
314316 local raw
315317 raw=" $( timeout 3s timedatectl show -p NTPSynchronized --value 2> /dev/null) " || return 1
@@ -460,15 +462,15 @@ build_service_json() {
460462 | with_entries(select(.value != null and .value != ""))'
461463}
462464
463- # Build the pi_health block as JSON, or print "null" if neither sub-probe
464- # produced data. Sub-probes are independent — a missing/broken
465- # `timedatectl` doesn't suppress vcgencmd throttle data and vice versa.
466- build_pi_health_json () {
467- local throttle_json= ' null ' ntp_json= ' null '
465+ # Build the flat pi_throttle block as JSON, or print "null" if vcgencmd
466+ # is absent or its output failed to parse. Pi-only — absent on every
467+ # non-Pi feeder. NTP-sync lives in `system.ntp_synchronized` now (it is
468+ # universal-host, not Pi-specific) and is plumbed separately.
469+ build_pi_throttle_json () {
468470 local pi_undervoltage_now=0 pi_freq_capped_now=0 pi_throttled_now=0 pi_soft_temp_limit_now=0
469471 local pi_undervoltage_ever=0 pi_freq_capped_ever=0 pi_throttled_ever=0 pi_soft_temp_limit_ever=0
470472 if command -v vcgencmd > /dev/null 2>&1 && collect_pi_throttle; then
471- throttle_json= " $( jq -nc \
473+ jq -nc \
472474 --argjson uv_now " $pi_undervoltage_now " \
473475 --argjson fc_now " $pi_freq_capped_now " \
474476 --argjson th_now " $pi_throttled_now " \
@@ -484,22 +486,10 @@ build_pi_health_json() {
484486 undervoltage_ever: ($uv_ever == 1),
485487 freq_capped_ever: ($fc_ever == 1),
486488 throttled_ever: ($th_ever == 1),
487- soft_temp_limit_ever: ($st_ever == 1)}' ) "
488- fi
489- if command -v timedatectl > /dev/null 2>&1 ; then
490- local ntp
491- if ntp=" $( collect_pi_ntp_sync) " ; then
492- ntp_json=" $ntp "
493- fi
494- fi
495- if [[ " $throttle_json " == ' null' && " $ntp_json " == ' null' ]]; then
496- printf ' null'
489+ soft_temp_limit_ever: ($st_ever == 1)}'
497490 return
498491 fi
499- jq -nc \
500- --argjson throttle " $throttle_json " \
501- --argjson ntp " $ntp_json " \
502- ' {throttle: $throttle, ntp_synchronized: $ntp}'
492+ printf ' null'
503493}
504494
505495# nullable_num VALUE — echoes the value if non-empty, otherwise "null".
@@ -689,8 +679,13 @@ main() {
689679 svc_978=" $( build_service_json airplanes-978 " $( root_path /run/airplanes-978/state) " ) "
690680 fi
691681
692- local pi_health_json
693- pi_health_json=" $( build_pi_health_json) "
682+ local pi_throttle_json ntp_sync_json
683+ pi_throttle_json=" $( build_pi_throttle_json) "
684+ # Universal host-clock signal — emits "true"/"false" or null on
685+ # any non-systemd host (no `timedatectl`). Wire path:
686+ # `system.ntp_synchronized`. Server stamps `system.clock_skew_seconds`
687+ # separately at ingest.
688+ ntp_sync_json=" $( collect_ntp_sync 2> /dev/null || printf ' null' ) "
694689
695690 local feed_scripts_version os_pretty_name os_id os_version_id kernel architecture image_release
696691 feed_scripts_version=" $( get_feed_scripts_version || true) "
@@ -730,7 +725,8 @@ main() {
730725 --argjson svc_readsb " $svc_readsb " \
731726 --argjson svc_dump978 " $svc_dump978 " \
732727 --argjson svc_978 " $svc_978 " \
733- --argjson pi_health " $pi_health_json " \
728+ --argjson pi_throttle " $pi_throttle_json " \
729+ --argjson ntp_synchronized " $ntp_sync_json " \
734730 --arg feed_scripts " ${feed_scripts_version:- } " \
735731 --arg os_pretty_name " ${os_pretty_name:- } " \
736732 --arg os_id " ${os_id:- } " \
@@ -758,7 +754,8 @@ main() {
758754 disk: {
759755 used_percent: $disk_used_pct,
760756 total_bytes: $disk_total_bytes
761- }
757+ },
758+ ntp_synchronized: $ntp_synchronized
762759 },
763760 network: {
764761 connection_type: $net_connection_type,
@@ -774,7 +771,7 @@ main() {
774771 architecture: $architecture,
775772 image_release: $image_release
776773 },
777- pi_health : $pi_health
774+ pi_throttle : $pi_throttle
778775 }
779776 | def _prune:
780777 if type == "object" then
0 commit comments