mirror of
https://github.com/myronblair/jarvis
synced 2026-06-30 17:50:23 -05:00
21e0b81a98
- Added to skipDomains: media_player - Added to skipKeywords: konnected, energy/power/voltage/current, camera controls (infrared, email, FTP, push, siren, hub ringtone, manual record), system noise (CEC scanner, ESPHome builder, Echo DND) - Auto-refresh every 30s when HA tab is active
140 lines
4.9 KiB
PHP
140 lines
4.9 KiB
PHP
<?php
|
|
// Home Assistant endpoint — entities served from api_cache (refreshed every 5 min by cron)
|
|
// Live service calls (turn on/off) still go direct to HA.
|
|
|
|
function haRequest(string $path, string $method = 'GET', array $payload = []): ?array {
|
|
if (HA_TOKEN === 'YOUR_HA_TOKEN_HERE' || strpos(HA_URL, '10.48.200.X') !== false) {
|
|
return null;
|
|
}
|
|
$ch = curl_init(HA_URL . '/api' . $path);
|
|
$headers = [
|
|
'Authorization: Bearer ' . HA_TOKEN,
|
|
'Content-Type: application/json',
|
|
];
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_RETURNTRANSFER => 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;
|
|
}
|
|
|
|
// Scene list
|
|
if ($method === 'GET' && $action === 'scenes') {
|
|
$states = haRequest('/states') ?? [];
|
|
$scenes = [];
|
|
foreach ($states as $s) {
|
|
if (str_starts_with($s['entity_id'] ?? '', 'scene.')) {
|
|
$scenes[] = [
|
|
'entity_id' => $s['entity_id'],
|
|
'name' => $s['attributes']['friendly_name'] ?? $s['entity_id'],
|
|
];
|
|
}
|
|
}
|
|
echo json_encode(['scenes' => $scenes]);
|
|
exit;
|
|
}
|
|
|
|
// Scene activate
|
|
if ($method === 'POST' && $action === 'scene_activate') {
|
|
$entity_id = $data['entity_id'] ?? '';
|
|
if ($entity_id && str_starts_with($entity_id, 'scene.')) {
|
|
$result = haRequest('/services/scene/turn_on', 'POST', ['entity_id' => $entity_id]);
|
|
echo json_encode(['success' => true, 'entity_id' => $entity_id]);
|
|
} else {
|
|
echo json_encode(['error' => 'Invalid scene entity_id']);
|
|
}
|
|
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 ha_entities table (real-time agent push data)
|
|
$skipDomains = ['sensor','binary_sensor','button','update','select','number',
|
|
'device_tracker','event','image','person','zone','tts','conversation',
|
|
'assist_satellite','input_button','media_player'];
|
|
$skipKeywords = [
|
|
// HACS / system toggles
|
|
'pre_release','get_hacs','matter_server','zerotier','mariadb',
|
|
'spotify_connect','file_editor','ssh_web','uptime_kuma','adguard_',
|
|
'folding_home','music_assistant','mealie','mosquitto','social_to',
|
|
'assist_microphone','cec_scanner','esphome_device_builder',
|
|
// Camera controls
|
|
'_record','_ftp_','_push_','_hub_ringtone','_siren_on',
|
|
'_email_on','_manual_record','_infrared_','motion_detection',
|
|
'front_yard_record','down_hill_record','camera1_record',
|
|
'back_yard_record','nvr_',
|
|
// Echo / smart display noise
|
|
'do_not_disturb',
|
|
// Konnected security panel switches
|
|
'konnected',
|
|
// Energy / power monitoring (sensors, not controls)
|
|
'_energy','_power','_voltage','_current','_consumption',
|
|
'electricity_maps',
|
|
];
|
|
|
|
$rows = JarvisDB::query(
|
|
"SELECT entity_id, entity_name, domain, state, UNIX_TIMESTAMP(updated_at) as updated_ts
|
|
FROM ha_entities
|
|
WHERE state NOT IN ('unavailable','unknown')
|
|
ORDER BY domain, entity_name"
|
|
) ?? [];
|
|
|
|
$grouped = [];
|
|
$latestTs = 0;
|
|
foreach ($rows as $e) {
|
|
$dom = $e['domain'];
|
|
if (in_array($dom, $skipDomains)) continue;
|
|
$skip = false;
|
|
if ($dom === 'switch') {
|
|
foreach ($skipKeywords as $kw) {
|
|
if (strpos($e['entity_id'], $kw) !== false) { $skip = true; break; }
|
|
}
|
|
}
|
|
if ($skip) continue;
|
|
if ((int)$e['updated_ts'] > $latestTs) $latestTs = (int)$e['updated_ts'];
|
|
$grouped[$dom][] = [
|
|
'entity_id' => $e['entity_id'],
|
|
'name' => $e['entity_name'],
|
|
'state' => $e['state'],
|
|
];
|
|
}
|
|
|
|
echo json_encode([
|
|
'configured' => true,
|
|
'entities' => $grouped,
|
|
'cache_age_s' => $latestTs > 0 ? (int)(time() - $latestTs) : -1,
|
|
'cached_at' => $latestTs > 0 ? date('c', $latestTs) : null,
|
|
]);
|