Fix 8 issues from code review

- ha-poller: replace recursive main() retry with while loop (stack overflow fix)
- ha-poller: advance last_push on empty HA response (log spam fix)
- ha-poller: use datetime.now(timezone.utc) instead of deprecated utcnow()
- ping-probe: always call update_status() unconditionally so offline devices register as offline
- agent.php: heartbeat reads status from payload instead of hardcoding 'online'
- phone-probe: delegate JSON building to python3 (bash concatenation injection fix)
- netscan + phone-probe: read registration key from /etc/jarvis-agent/reg-key
- admin/index.php: sync ha_list skipDomains with ha.php (14 missing domains added)
- facts_collector: self-check JARVIS via 127.0.0.1 instead of Cloudflare hairpin
This commit is contained in:
2026-06-29 20:58:22 -05:00
parent c1275d47a6
commit 90e4ded7c9
7 changed files with 43 additions and 20 deletions
+3 -4
View File
@@ -175,7 +175,7 @@ def fetch_ha_states(cfg: dict) -> list:
dt = datetime.fromisoformat(lc.replace("Z", "+00:00")) dt = datetime.fromisoformat(lc.replace("Z", "+00:00"))
lc = dt.astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") lc = dt.astimezone(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
except Exception: except Exception:
lc = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S") lc = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S")
entities.append({ entities.append({
"entity_id": entity_id, "entity_id": entity_id,
@@ -194,13 +194,11 @@ def main():
heartbeat_every = int(cfg.get("heartbeat_every", 10)) heartbeat_every = int(cfg.get("heartbeat_every", 10))
api_key = state.get("api_key", "") api_key = state.get("api_key", "")
if not api_key: while not api_key:
api_key = register(cfg, state) api_key = register(cfg, state)
if not api_key: if not api_key:
log("Could not register. Retrying in 60s...") log("Could not register. Retrying in 60s...")
time.sleep(60) time.sleep(60)
main()
return
headers = {"X-Agent-Key": api_key} headers = {"X-Agent-Key": api_key}
last_push = 0 last_push = 0
@@ -229,6 +227,7 @@ def main():
last_push = now last_push = now
else: else:
log("No HA entities fetched (HA down or token invalid?)") log("No HA entities fetched (HA down or token invalid?)")
last_push = now
time.sleep(heartbeat_every) time.sleep(heartbeat_every)
+5 -1
View File
@@ -4,7 +4,11 @@
JARVIS_URL="http://10.48.200.211" JARVIS_URL="http://10.48.200.211"
JARVIS_HOST="jarvis.orbishosting.com" JARVIS_HOST="jarvis.orbishosting.com"
REG_KEY="f846a9aaf7ce9a61742c63c87c4186052a71d2a580c65518" REG_KEY=$(cat /etc/jarvis-agent/reg-key 2>/dev/null)
if [ -z "$REG_KEY" ]; then
echo "$(date): ERROR: /etc/jarvis-agent/reg-key not found" >&2
exit 1
fi
SUBNET="10.48.200.0/24" SUBNET="10.48.200.0/24"
TMPFILE=$(mktemp) TMPFILE=$(mktemp)
+28 -10
View File
@@ -5,7 +5,11 @@
JARVIS_URL="http://10.48.200.211" JARVIS_URL="http://10.48.200.211"
JARVIS_HOST="jarvis.orbishosting.com" JARVIS_HOST="jarvis.orbishosting.com"
REG_KEY="f846a9aaf7ce9a61742c63c87c4186052a71d2a580c65518" REG_KEY=$(cat /etc/jarvis-agent/reg-key 2>/dev/null)
if [ -z "$REG_KEY" ]; then
echo "$(date): ERROR: /etc/jarvis-agent/reg-key not found" >&2
exit 1
fi
FUSION_HOST="134.209.72.226" FUSION_HOST="134.209.72.226"
# IP|alias|extension(none=skip SIP check)|mac # IP|alias|extension(none=skip SIP check)|mac
@@ -21,19 +25,17 @@ PHONES=(
REG_OUTPUT=$(ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -o BatchMode=yes \ REG_OUTPUT=$(ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -o BatchMode=yes \
root@$FUSION_HOST "fs_cli -x 'show registrations'" 2>/dev/null || echo "") root@$FUSION_HOST "fs_cli -x 'show registrations'" 2>/dev/null || echo "")
DEVICES="[" # Collect results as TSV, delegate JSON building to python3 to avoid injection
FIRST=1 RESULTS=""
for PHONE in "${PHONES[@]}"; do for PHONE in "${PHONES[@]}"; do
IFS='|' read -r IP ALIAS EXT MAC <<< "$PHONE" IFS='|' read -r IP ALIAS EXT MAC <<< "$PHONE"
# Ping probe
if ping -c 1 -W 2 "$IP" > /dev/null 2>&1; then if ping -c 1 -W 2 "$IP" > /dev/null 2>&1; then
STATUS="online" STATUS="online"
else else
STATUS="offline" STATUS="offline"
fi fi
# SIP check — skip for external phones (ext=none)
if [ "$EXT" = "none" ]; then if [ "$EXT" = "none" ]; then
SIP="external" SIP="external"
elif [ -n "$REG_OUTPUT" ] && echo "$REG_OUTPUT" | grep -q "^${EXT},"; then elif [ -n "$REG_OUTPUT" ] && echo "$REG_OUTPUT" | grep -q "^${EXT},"; then
@@ -42,15 +44,31 @@ for PHONE in "${PHONES[@]}"; do
SIP="unregistered" SIP="unregistered"
fi fi
[ $FIRST -eq 0 ] && DEVICES+="," RESULTS="${RESULTS}${IP}\t${ALIAS}\t${MAC}\t${STATUS}\t${SIP}\t${EXT}\n"
DEVICES+="{\"ip\":\"$IP\",\"alias\":\"$ALIAS\",\"mac\":\"$MAC\",\"vendor\":\"Yealink\",\"status\":\"$STATUS\",\"sip_status\":\"$SIP\",\"extension\":\"$EXT\"}"
FIRST=0
done done
DEVICES+="]"
JSON=$(printf "%b" "$RESULTS" | python3 -c "
import sys, json
devices = []
for line in sys.stdin:
line = line.rstrip('\n')
if not line:
continue
parts = line.split('\t')
if len(parts) < 6:
continue
ip, alias, mac, status, sip, ext = parts[:6]
devices.append({
'ip': ip, 'alias': alias, 'mac': mac,
'vendor': 'Yealink', 'status': status,
'sip_status': sip, 'extension': ext,
})
print(json.dumps({'devices': devices}))
")
curl -sk --max-time 10 \ curl -sk --max-time 10 \
-X POST "$JARVIS_URL/api/netscan" \ -X POST "$JARVIS_URL/api/netscan" \
-H "Host: $JARVIS_HOST" \ -H "Host: $JARVIS_HOST" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-H "X-Registration-Key: $REG_KEY" \ -H "X-Registration-Key: $REG_KEY" \
-d "{\"devices\":$DEVICES}" > /dev/null 2>&1 -d "$JSON" > /dev/null 2>&1
+1 -2
View File
@@ -93,8 +93,7 @@ def main():
status = "online" if alive else "offline" status = "online" if alive else "offline"
print(f"{agent_id} ({ip}): {status}", flush=True) print(f"{agent_id} ({ip}): {status}", flush=True)
heartbeat(agent_id, api_key, alive) heartbeat(agent_id, api_key, alive)
if alive: update_status(agent_id, api_key, status)
update_status(agent_id, api_key, status)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+2 -1
View File
@@ -111,7 +111,8 @@ switch ($agentAction) {
// ── HEARTBEAT ──────────────────────────────────────────────────────────── // ── HEARTBEAT ────────────────────────────────────────────────────────────
case 'heartbeat': case 'heartbeat':
update_agent_seen($agent['agent_id'], 'online', trim($data['version'] ?? '') ?: null); $hbStatus = in_array($data['status'] ?? '', ['online','offline']) ? $data['status'] : 'online';
update_agent_seen($agent['agent_id'], $hbStatus, trim($data['version'] ?? '') ?: null);
// Return any pending commands for this agent // Return any pending commands for this agent
$commands = JarvisDB::query( $commands = JarvisDB::query(
+1 -1
View File
@@ -181,7 +181,7 @@ function collect_all(): array {
$results['sites'] = 'skipped (fresh)'; $results['sites'] = 'skipped (fresh)';
} else try { } else try {
$sites = [ $sites = [
"jarvis" => "https://jarvis.orbishosting.com", "jarvis" => "http://127.0.0.1",
'tomsjavajive' => 'https://tomsjavajive.com', 'tomsjavajive' => 'https://tomsjavajive.com',
'epictravelexp'=> 'https://epictravelexpeditions.com', 'epictravelexp'=> 'https://epictravelexpeditions.com',
'parkersling' => 'https://parkerslingshotrentals.com', 'parkersling' => 'https://parkerslingshotrentals.com',
+3 -1
View File
@@ -242,7 +242,9 @@ if ($action) {
$search = strtolower(trim($_GET['search'] ?? '')); $search = strtolower(trim($_GET['search'] ?? ''));
$skipDomains = ['sensor','binary_sensor','button','update','select','number', $skipDomains = ['sensor','binary_sensor','button','update','select','number',
'device_tracker','event','image','person','zone','tts','conversation', 'device_tracker','event','image','person','zone','tts','conversation',
'assist_satellite','input_button']; 'assist_satellite','input_button','media_player','scene','water_heater',
'alarm_control_panel','automation','script','calendar','notify',
'weather','camera','siren','remote','todo','lawn_mower'];
$skipKeywords = ['pre_release','_record','_ftp_','_push_','_hub_ringtone', $skipKeywords = ['pre_release','_record','_ftp_','_push_','_hub_ringtone',
'_siren_on','_email_on','_manual_record','_infrared_', '_siren_on','_email_on','_manual_record','_infrared_',
'do_not_disturb','matter_server','zerotier','mariadb', 'do_not_disturb','matter_server','zerotier','mariadb',