Merge findmydevice data from both HA profiles

This commit is contained in:
seo
2026-06-17 20:07:34 +09:00
parent d9c8aef1dd
commit 3dfb506474
2 changed files with 115 additions and 17 deletions
+110 -16
View File
@@ -26,7 +26,7 @@ function findmydevice_mobile_command(string $message, array $data = []): bool
{
$config = custom_config();
$refresh = $config['findmydevice_location_refresh'] ?? [];
$profile = (string)($refresh['profile'] ?? 'seoul');
$profiles = (array)($refresh['profiles'] ?? [$refresh['profile'] ?? 'seoul']);
$service = (string)($refresh['notify_service'] ?? '');
if ($service === '') {
@@ -38,7 +38,12 @@ function findmydevice_mobile_command(string $message, array $data = []): bool
$payload['data'] = $data;
}
return custom_ha_service($profile, 'notify', $service, $payload, 5);
$ok = false;
foreach (array_values(array_unique(array_filter(array_map('strval', $profiles)))) as $profile) {
$ok = custom_ha_service($profile, 'notify', $service, $payload, 5) || $ok;
}
return $ok;
}
function findmydevice_location_refresh(string $name): array
@@ -100,14 +105,81 @@ if (isset($_GET['action']) && $_GET['action'] === 'webhook') {
$config = custom_config();
$entities = $config['entities'];
$allStates = custom_ha_request('seoul', 'GET', '/states') ?? [];
$stateMap = [];
foreach ($allStates as $stateRow) {
if (isset($stateRow['entity_id'])) {
$stateMap[$stateRow['entity_id']] = $stateRow;
}
$findmydeviceHaProfiles = ['seoul', 'main'];
function isMeaningfulHaValue($state): bool
{
return !($state === null || $state === '' || $state === 'unknown' || $state === 'unavailable' || $state === '<not connected>' || $state === '<not available>' || $state === 'none');
}
function haRowTimestamp(array $row): int
{
$time = $row['last_updated'] ?? $row['last_changed'] ?? null;
return $time ? (strtotime((string)$time) ?: 0) : 0;
}
function mergeHaAttributes(array $primary, array $fallback): array
{
$attrs = is_array($primary['attributes'] ?? null) ? $primary['attributes'] : [];
$fallbackAttrs = is_array($fallback['attributes'] ?? null) ? $fallback['attributes'] : [];
foreach ($fallbackAttrs as $key => $value) {
if (!array_key_exists($key, $attrs) || !isMeaningfulHaValue($attrs[$key])) {
$attrs[$key] = $value;
}
}
return $attrs;
}
function mergeHaStateRow(?array $current, array $candidate): array
{
if ($current === null) {
return $candidate;
}
$currentMeaningful = isMeaningfulHaValue($current['state'] ?? null);
$candidateMeaningful = isMeaningfulHaValue($candidate['state'] ?? null);
if ($candidateMeaningful && !$currentMeaningful) {
$candidate['attributes'] = mergeHaAttributes($candidate, $current);
return $candidate;
}
if (!$candidateMeaningful && $currentMeaningful) {
$current['attributes'] = mergeHaAttributes($current, $candidate);
return $current;
}
if (haRowTimestamp($candidate) > haRowTimestamp($current)) {
$candidate['attributes'] = mergeHaAttributes($candidate, $current);
return $candidate;
}
$current['attributes'] = mergeHaAttributes($current, $candidate);
return $current;
}
function loadMergedStateMap(array $profiles): array
{
$stateMap = [];
foreach ($profiles as $profile) {
$allStates = custom_ha_request((string)$profile, 'GET', '/states') ?? [];
foreach ($allStates as $stateRow) {
if (!isset($stateRow['entity_id']) || !is_array($stateRow)) {
continue;
}
$entityId = (string)$stateRow['entity_id'];
$stateRow['_source_profile'] = (string)$profile;
$stateMap[$entityId] = mergeHaStateRow($stateMap[$entityId] ?? null, $stateRow);
}
}
return $stateMap;
}
$stateMap = loadMergedStateMap($findmydeviceHaProfiles);
// 엔드포인트 URL
$deviceUrl = $entities['device'];
$geoUrl = $entities['geo'];
@@ -265,20 +337,41 @@ $ascendedData = getData($fascendedUrl);
$descendedData = getData($fdescendedUrl);
$stepsData = getData($stepsUrl);
$distanceData = getData($distanceUrl);
$historyData = $includeHistory ? (custom_ha_request('seoul', 'GET', $historyEndpoint, null, 0) ?? []) : [];
$historySets = [];
if ($includeHistory) {
foreach ($findmydeviceHaProfiles as $profile) {
$historySets[(string)$profile] = custom_ha_request((string)$profile, 'GET', $historyEndpoint, null, 0) ?? [];
}
}
$triggerData = getData($triggerUrl);
// 경로 기록
$path = [];
if (isset($historyData[0])) {
foreach ($historyData[0] as $item) {
$lat = $item['attributes']['latitude'] ?? null;
$lng = $item['attributes']['longitude'] ?? null;
$time = $item['last_changed'] ?? null;
if ($lat && $lng) {
$path[] = ['lat' => $lat, 'lng' => $lng, 'time' => $time];
$historySourceCounts = [];
if ($historySets) {
$pathByKey = [];
foreach ($historySets as $profile => $historyData) {
$historySourceCounts[$profile] = isset($historyData[0]) && is_array($historyData[0]) ? count($historyData[0]) : 0;
if (!isset($historyData[0]) || !is_array($historyData[0])) {
continue;
}
foreach ($historyData[0] as $item) {
$lat = $item['attributes']['latitude'] ?? null;
$lng = $item['attributes']['longitude'] ?? null;
$time = $item['last_changed'] ?? null;
if ($lat && $lng && $time) {
$timeKey = (string)(strtotime((string)$time) ?: $time);
$key = $timeKey . ':' . round((float)$lat, 5) . ':' . round((float)$lng, 5);
if (!isset($pathByKey[$key])) {
$pathByKey[$key] = ['lat' => $lat, 'lng' => $lng, 'time' => $time, 'source' => $profile];
}
}
}
}
$path = array_values($pathByKey);
usort($path, static function (array $a, array $b): int {
return (strtotime((string)$a['time']) ?: 0) <=> (strtotime((string)$b['time']) ?: 0);
});
}
function pathDistanceM(array $a, array $b): float
@@ -695,5 +788,6 @@ echo json_encode([
'history' => $includeHistory ? $path : null,
'historyRawCount' => $includeHistory ? $historyRawCount : null,
'historyRenderCount' => $includeHistory ? count($path) : null,
'historySourceCounts' => $includeHistory ? $historySourceCounts : null,
], JSON_UNESCAPED_UNICODE);
?>