From 8bbe43b8c4536a54d845ce962e4a611d1e5b4b17 Mon Sep 17 00:00:00 2001 From: seo Date: Wed, 17 Jun 2026 19:20:28 +0900 Subject: [PATCH] Return full findmydevice history paths --- README.md | 6 ++++-- js/main.js | 1 - php/api.php | 50 +++++--------------------------------------------- 3 files changed, 9 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 0d682ec..a9a5627 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,9 @@ Home Assistant에 기록된 Galaxy Z Fold7의 위치, 배터리, 활동 상태, ## 히스토리와 지도 -실시간 화면은 현재 위치와 상태를 조회하고, 기본 기간 입력값에 해당하는 최근 하루 이동 기록도 지도에 함께 표시합니다. 기간 입력을 변경하면 해당 범위의 이동 기록을 `history=1` 옵션으로 다시 요청합니다. 서버는 Home Assistant 위치 기록을 5m 미만 미세 이동 기준으로 정리한 뒤 최대 800개 지점으로 샘플링합니다. +실시간 화면은 현재 위치와 상태를 조회하고, 기본 기간 입력값에 해당하는 최근 하루 이동 기록도 지도에 함께 표시합니다. 기간 입력을 변경하면 해당 범위의 이동 기록을 `history=1` 옵션으로 다시 요청합니다. 서버는 기간이 길어도 Home Assistant 히스토리 API를 실행하고, 반환된 위치 기록을 샘플링하거나 최대 개수로 자르지 않고 응답합니다. -Naver Map에는 전체 경로선을 먼저 그리고 시작/종료 마커와 최대 180개의 중간 포인트를 표시합니다. 기록 조회 시 지도는 경로 전체가 보이도록 자동으로 범위를 맞추며, 종료 지점 정보창을 먼저 열어 최근 위치를 바로 확인할 수 있게 합니다. +Naver Map에는 응답받은 전체 경로선을 먼저 그리고 시작/종료 마커와 중간 포인트를 표시합니다. 기록 조회 시 지도는 경로 전체가 보이도록 자동으로 범위를 맞추며, 종료 지점 정보창을 먼저 열어 최근 위치를 바로 확인할 수 있게 합니다. 지도 기능은 NAVER 지도 API v3의 기본 컨트롤, 지도 유형, `TrafficLayer`, `StreetLayer`, `Polyline`, `PointingIcon`, `Circle`, `fitBounds`를 활용합니다. 지도 아래 도구 패널에서 일반/위성 지도 전환, 교통 레이어, 거리뷰 가능 구역 레이어, 경로 전체 보기, 경로 재생, GPS 튐 보정, Naver 지도 열기를 제어합니다. @@ -68,4 +68,6 @@ Android Companion App의 실제 상태값은 API에서 한국어/영어 표시 ## 운영 메모 +긴 기간 히스토리 조회는 Home Assistant recorder 데이터량과 웹서버/프록시 timeout 영향을 받을 수 있습니다. findmydevice API는 자체 포인트 수 제한과 curl timeout을 걸지 않으므로, 가능한 범위에서는 Home Assistant가 반환한 전체 결과를 화면에 출력합니다. + 이 프로젝트는 위치, 주소, 배터리, 활동 기록을 포함하므로 private 저장소와 개인 접근 환경에서만 운영합니다. diff --git a/js/main.js b/js/main.js index 59d6dcd..dfbe440 100644 --- a/js/main.js +++ b/js/main.js @@ -2188,7 +2188,6 @@ function fetchDeviceData(includeLang = true, range = null) { if (e) params.set('endTime', e); if (hasHistoryRange) { params.set('history', '1'); - params.set('maxPoints', '800'); } const url = `https://chaegeon.com/custom/findmydevice/php/api.php?${params.toString()}`; diff --git a/php/api.php b/php/api.php index b99ad9c..f25d7eb 100644 --- a/php/api.php +++ b/php/api.php @@ -14,6 +14,10 @@ header('Content-Type: application/json; charset=utf-8'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Expires: 0'); +if (function_exists('set_time_limit')) { + @set_time_limit(0); +} + custom_require_auth(); $requestData = custom_read_request_data(); @@ -77,7 +81,6 @@ if ($startParam && $endParam) { // API URL 생성 $historyEndpoint = "/history/period/{$startTime}?filter_entity_id=" . rawurlencode($entities['device']) . "&end_time={$endTime}"; $includeHistory = isset($_GET['history']) && $_GET['history'] === '1' && !isset($_GET['hashonly']); -$historyMaxPoints = max(20, min(1200, (int)($_GET['maxPoints'] ?? 800))); // 공통 요청 함수 function getData($entityId) { @@ -202,7 +205,7 @@ $ascendedData = getData($fascendedUrl); $descendedData = getData($fdescendedUrl); $stepsData = getData($stepsUrl); $distanceData = getData($distanceUrl); -$historyData = $includeHistory ? (custom_ha_request('seoul', 'GET', $historyEndpoint) ?? []) : []; +$historyData = $includeHistory ? (custom_ha_request('seoul', 'GET', $historyEndpoint, null, 0) ?? []) : []; $triggerData = getData($triggerUrl); // 경로 기록 @@ -218,49 +221,6 @@ if (isset($historyData[0])) { } } -function path_distance_m(array $a, array $b): float -{ - $earth = 6371000; - $lat1 = deg2rad((float)$a['lat']); - $lat2 = deg2rad((float)$b['lat']); - $dLat = $lat2 - $lat1; - $dLng = deg2rad((float)$b['lng'] - (float)$a['lng']); - $h = sin($dLat / 2) ** 2 + cos($lat1) * cos($lat2) * sin($dLng / 2) ** 2; - return 2 * $earth * asin(min(1, sqrt($h))); -} - -function simplify_path(array $path, int $maxPoints): array -{ - $count = count($path); - if ($count <= $maxPoints) { - return $path; - } - - $filtered = []; - $last = null; - foreach ($path as $point) { - if ($last === null || path_distance_m($last, $point) >= 5) { - $filtered[] = $point; - $last = $point; - } - } - - if (count($filtered) <= $maxPoints) { - return $filtered; - } - - $sampled = []; - $lastIndex = count($filtered) - 1; - for ($i = 0; $i < $maxPoints; $i++) { - $idx = (int)round($i * $lastIndex / max(1, $maxPoints - 1)); - $sampled[] = $filtered[$idx]; - } - - return $sampled; -} - -$path = simplify_path($path, $historyMaxPoints); - $lang = $_GET['lang'] ?? 'en'; $activityMap = [