true, CURLOPT_HTTPHEADER => $headers, CURLOPT_TIMEOUT => 8, CURLOPT_CONNECTTIMEOUT => 4, CURLOPT_SSL_VERIFYPEER => false, ]); if ($method === 'POST') { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); } $resp = curl_exec($ch); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($code >= 200 && $code < 300 && $resp) { return json_decode($resp, true); } return null; } $configured = !(HA_TOKEN === 'YOUR_HA_TOKEN_HERE' || strpos(HA_URL, '10.48.200.X') !== false); if (!$configured) { echo json_encode(['configured' => false, 'message' => 'HA token not configured.', 'entities' => []]); exit; } // Live service call (toggle device) — always direct to HA, never cached if ($method === 'POST' && $action === 'service') { $domain = $data['domain'] ?? ''; $service = $data['service'] ?? ''; $entity_id = $data['entity_id'] ?? ''; if ($domain && $service && $entity_id) { $result = haRequest("/services/{$domain}/{$service}", 'POST', ['entity_id' => $entity_id]); echo json_encode(['success' => true, 'result' => $result]); } else { echo json_encode(['error' => 'Missing domain/service/entity_id']); } exit; } // Serve entities from cache (populated by stats_cache.php cron, every 5 min) $cached = JarvisDB::query( 'SELECT data, UNIX_TIMESTAMP(updated_at) as updated_ts FROM api_cache WHERE cache_key=? LIMIT 1', ['ha_entities'] ); if ($cached && !empty($cached[0]['data'])) { $row = $cached[0]; $data_out = json_decode($row['data'], true); $data_out['cache_age_s'] = (int)(time() - (int)$row['updated_ts']); echo json_encode($data_out); } else { echo json_encode([ 'configured' => true, 'ha_version' => 'unknown', 'location' => 'Home', 'entity_count' => 0, 'entities' => [], 'cached_at' => null, 'cache_age_s' => -1, 'message' => 'Cache warming up — first update in under 5 minutes.', ]); }