Use shared auth and proxy Home Assistant webhooks

This commit is contained in:
seo
2026-06-12 15:10:20 +09:00
parent a2a89998b6
commit 72aedf47e5
3 changed files with 128 additions and 121 deletions
+54 -87
View File
@@ -1,11 +1,6 @@
<?php
// Apache 환경 차단
if (isset($_SERVER['SERVER_SOFTWARE']) && stripos($_SERVER['SERVER_SOFTWARE'], 'Apache') !== false) {
http_response_code(403);
header('Content-Type: application/json');
echo json_encode(['error' => 'Access forbidden: Apache environment is not allowed']);
exit;
}
require_once __DIR__ . '/../../common/auth.php';
require_once __DIR__ . '/../../common/ha.php';
// POST 전용 검사
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
@@ -15,69 +10,47 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
exit;
}
// JSON 요청 검사
$content_type = $_SERVER['CONTENT_TYPE'] ?? '';
if (stripos($content_type, 'application/json') === false) {
http_response_code(415); // Unsupported Media Type
header('Content-Type: application/json');
echo json_encode(['error' => 'Unsupported Content-Type. application/json required']);
exit;
}
function get_authorization_header() {
if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
return trim($_SERVER['HTTP_AUTHORIZATION']);
} elseif (function_exists('apache_request_headers')) {
foreach (apache_request_headers() as $key => $value) {
if (strcasecmp($key, 'Authorization') === 0) {
return trim($value);
}
}
}
return null;
}
$auth_header = get_authorization_header();
if (!$auth_header || !preg_match('/^Bearer\s+(\S+)$/', $auth_header, $matches)) {
http_response_code(401);
header('Content-Type: application/json');
echo json_encode(['error' => 'Unauthorized: Missing or invalid Authorization header']);
exit;
}
$allowed_password = 'XBv9z1IGqPUdtVf7DABFA16H0E154LJS';
$password = $matches[1];
if ($password !== $allowed_password) {
http_response_code(401);
header('Content-Type: application/json');
echo json_encode(['error' => 'Unauthorized: Invalid token']);
exit;
}
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Expires: 0');
$baseUrl = "https://ha.seoul.chaegeon.com/api/states";
$headers = [
"Authorization: Bearer REDACTED_HA_TOKEN",
"Content-Type: application/json"
];
custom_require_auth();
$requestData = custom_read_request_data();
if (isset($_GET['action']) && $_GET['action'] === 'webhook') {
$name = (string)($requestData['name'] ?? '');
$allowed = [
'auto_on' => 'find_auto_on',
'auto_off' => 'find_auto_off',
'auto_tick' => 'find_auto_tick',
'force_update' => 'find_force_update',
];
if (!isset($allowed[$name])) {
custom_json(['result' => 'invalid_webhook'], 400);
}
$ok = custom_ha_webhook($allowed[$name]);
custom_json(['result' => $ok ? 'ok' : 'fail'], $ok ? 200 : 502);
}
$config = custom_config();
$entities = $config['entities'];
// 엔드포인트 URL
$deviceUrl = "$baseUrl/device_tracker.seocaegeonyi_z_fold7";
$geoUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_geocoded_location";
$batteryStateUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_battery_state";
$batteryLevelUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_battery_level";
$watchBatteryStateUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_watch_battery_state";
$watchBatteryLevelUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_watch_battery_level";
$activityUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_detected_activity";
$connectionUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_network_type";
$fascendedUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_daily_floors";
$fdescendedUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_daily_floors";
$stepsUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_daily_steps";
$distanceUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_daily_distance";
$triggerUrl = "$baseUrl/sensor.seocaegeonyi_z_fold7_last_update_trigger";
$deviceUrl = $entities['device'];
$geoUrl = $entities['geo'];
$batteryStateUrl = $entities['battery_state'];
$batteryLevelUrl = $entities['battery_level'];
$watchBatteryStateUrl = $entities['watch_battery_state'];
$watchBatteryLevelUrl = $entities['watch_battery_level'];
$activityUrl = $entities['activity'];
$connectionUrl = $entities['connection'];
$fascendedUrl = $entities['daily_floors'];
$fdescendedUrl = $entities['daily_floors'];
$stepsUrl = $entities['daily_steps'];
$distanceUrl = $entities['daily_distance'];
$triggerUrl = $entities['trigger'];
// GET 파라미터 읽기
$startParam = $_GET['startTime'] ?? null;
@@ -97,34 +70,28 @@ if ($startParam && $endParam) {
}
// API URL 생성
$historyUrl = "https://ha.seoul.chaegeon.com/api/history/period/{$startTime}?filter_entity_id=device_tracker.seocaegeonyi_z_fold7&end_time={$endTime}";
$historyEndpoint = "/history/period/{$startTime}?filter_entity_id=" . rawurlencode($entities['device']) . "&end_time={$endTime}";
// 공통 요청 함수
function getData($url, $headers) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
function getData($entityId) {
return custom_ha_state('seoul', $entityId) ?? [];
}
// 데이터 불러오기
$deviceData = getData($deviceUrl, $headers);
$geoData = getData($geoUrl, $headers);
$batteryStateData = getData($batteryStateUrl, $headers);
$batteryLevelData = getData($batteryLevelUrl, $headers);
$watchBatteryStateData = getData($watchBatteryStateUrl, $headers);
$watchBatteryLevelData = getData($watchBatteryLevelUrl, $headers);
$activityData = getData($activityUrl, $headers);
$connectionData = getData($connectionUrl, $headers);
$ascendedData = getData($fascendedUrl, $headers);
$descendedData = getData($fdescendedUrl, $headers);
$stepsData = getData($stepsUrl, $headers);
$distanceData = getData($distanceUrl, $headers);
$historyData = getData($historyUrl, $headers);
$triggerData = getData($triggerUrl, $headers);
$deviceData = getData($deviceUrl);
$geoData = getData($geoUrl);
$batteryStateData = getData($batteryStateUrl);
$batteryLevelData = getData($batteryLevelUrl);
$watchBatteryStateData = getData($watchBatteryStateUrl);
$watchBatteryLevelData = getData($watchBatteryLevelUrl);
$activityData = getData($activityUrl);
$connectionData = getData($connectionUrl);
$ascendedData = getData($fascendedUrl);
$descendedData = getData($fdescendedUrl);
$stepsData = getData($stepsUrl);
$distanceData = getData($distanceUrl);
$historyData = custom_ha_request('seoul', 'GET', $historyEndpoint) ?? [];
$triggerData = getData($triggerUrl);
// 경로 기록
$path = [];