Support Galaxy sensor fields and remove debug file

This commit is contained in:
seo
2026-06-12 15:18:13 +09:00
parent 72aedf47e5
commit 4559301fa6
4 changed files with 294 additions and 233 deletions
+3 -3
View File
@@ -1,13 +1,14 @@
# Find My Device
Home Assistant에 기록된 Fold7 위치, 배터리, 활동 상태, 이동 경로를 지도에 표시하는 개인용 나의 찾기 페이지입니다.
Home Assistant에 기록된 Galaxy 기기의 위치, 배터리, 활동 상태, 이동 경로를 지도에 표시하는 개인용 나의 찾기 페이지입니다.
## 기능
- 현재 위치 지도 표시
- GPS 정확도 원 표시
- 최근 24시간 및 사용자 지정 기간 이동 경로 표시
- 배터리, 워치 배터리, 활동 상태, 걸음 수, 이동 거리 표시
- 배터리, 충전 상태, 배터리 건강, 온도, 전력, 활동 상태, 걸음 수, 이동 거리 표시
- Wi-Fi, 모바일 데이터, 전화 상태, 화면 활성, Doze, 절전, 잠금 상태 표시
- 자동 위치 갱신 웹훅 제어
- 강제 업데이트 요청
- 한국어/영어 전환
@@ -15,7 +16,6 @@ Home Assistant에 기록된 Fold7 위치, 배터리, 활동 상태, 이동 경
## 구조
- `debug.html`: 페이지 삽입용 HTML 조각
- `php/api.php`: Home Assistant 상태/히스토리 조회 API
- `js/main.js`: 지도 렌더링, 상태 갱신, 히스토리 표시
- `css/style.css`: iOS 설정 화면 스타일
-175
View File
@@ -1,175 +0,0 @@
<meta charset="UTF-8">
<link href="/custom/findmydevice/css/style.css" rel="stylesheet" />
<div id="mySettingContainer" style="user-select: none;">
<div id="settingsHeader">
<a id="blogBackLink"></a>
<span class="headerTitle"></span>
<div id="langToggle">
<img src="/custom/findmydevice/img/language-empty.svg" alt="Language Toggle">
</div>
</div>
<div id="toggleContent" style="padding-bottom:80px;">
<div id="mapContainer">
<div id="map" style="width:100%; height:100%;"></div>
</div>
<div class="settingTitle">
<span id="titleSettings"></span>
</div>
<div class="settingSection">
<div class="settingRow" id="wakeLockRow">
<span id="labelKeepScreenOn"></span>
<label class="iosSwitch">
<input type="checkbox" id="wakeLockSwitch">
<span class="slider"></span>
</label>
</div>
</div>
<div class="settingTitle">
<span id="titleHistory"></span>
</div>
<div class="settingSection">
<div class="settingRow">
<span id="labelStartTime"></span>
<input type="datetime-local" id="rangeStart" class="dth">
</div>
<div class="settingRow">
<span id="labelEndTime"></span>
<input type="datetime-local" id="rangeEnd" class="dth">
</div>
<div class="settingRow locateRow" id="HistoryResetBtn">
<span style="color:#007AFF; cursor:default;" id="labelHistoryReset"></span>
</div>
</div>
<div class="settingTitle">
<span id="titleLocation"></span>
</div>
<div class="settingSection">
<div class="settingRow">
<span id="labelCurrentPosition"></span>
<span id="currentPosition"></span>
</div>
<div class="settingRow">
<span id="labelGpsAccuracy"></span>
<span id="accuracyValue"></span>
</div>
<div class="settingRow" id="autoLocateBtn">
<span id="labelCurrentAutoPosition"></span>
<label class="iosSwitch">
<input type="checkbox" id="autoPositionSwitch">
<span class="slider"></span>
</label>
</div>
<div class="settingRow locateRow" id="locateBtn">
<span style="color:#007AFF; cursor:default;" id="labelMoveToCurrent"></span>
</div>
</div>
<div class="settingTitle">
<span id="titleUpdate"></span>
</div>
<div class="settingSection">
<div class="settingRow">
<span id="labelLastUpdate"></span>
<span id="lastUpdateTime"></span>
</div>
<div class="settingRow">
<span id="labelUpdateTrigger"></span>
<span id="updateTrigger"></span>
</div>
<div class="settingRow" id="autoUpdateBtn">
<span id="labelAutoUpdate"></span>
<label class="iosSwitch">
<input type="checkbox" id="autoUpdateSwitch">
<span class="slider"></span>
</label>
</div>
<div class="settingRow locateRow" id="updateBtn">
<span style="color:#007AFF; cursor:default;" id="labelForceUpdate"></span>
</div>
</div>
<div class="settingTitle">
<span id="titleBattery"></span>
</div>
<div class="settingSection">
<div class="settingRow">
<span id="labelBatteryState"></span>
<span id="batteryStatus"></span>
</div>
<div class="settingRow">
<span id="labelBatteryLevel"></span>
<span id="Battery"></span>
</div>
<div class="settingRow">
<span id="labelWatchBatteryState"></span>
<span id="WbatteryStatus"></span>
</div>
<div class="settingRow">
<span id="labelWatchBatteryLevel"></span>
<span id="WBattery"></span>
</div>
</div>
<div class="settingTitle">
<span id="titleActivity"></span>
</div>
<div class="settingSection">
<div class="settingRow">
<span id="labelCurrentActivity"></span>
<span id="nowActivity"></span>
</div>
<div class="settingRow" id="existnowActivity">
<span id="labelActivityAccuracy"></span>
<span id="nowactivityAccuracy"></span>
</div>
<div class="settingRow">
<span id="labelDistance"></span>
<span id="todayDistance"></span>
</div>
<div class="settingRow">
<span id="labelSteps"></span>
<span id="todaySteps"></span>
</div>
<div class="settingRow">
<span id="labelAscended"></span>
<span id="todayAscended"></span>
</div>
<div class="settingRow">
<span id="labelDescended"></span>
<span id="todayDescended"></span>
</div>
</div>
<div class="settingTitle">
<span id="titleEtc"></span>
</div>
<div class="settingSection">
<div class="settingRow">
<span id="labelConnection"></span>
<span id="connectionType"></span>
</div>
</div>
</div>
</div>
<div id="ios-pwa-tooltip">
<span class="close-btn" id="close-tooltip"></span>
<span
style="width:18px;height:18px;display:inline-block;vertical-align:middle;margin-right:5px;transform:translateZ(0);will-change:transform;">
<svg height="18" viewbox="0 0 50 50" width="18" xmlns="http://www.w3.org/2000/svg">
<path d="M30.3 13.7L25 8.4l-5.3 5.3-1.4-1.4L25 5.6l6.7 6.7z"></path>
<path d="M24 7h2v21h-2z"></path>
<path
d="M35 40H15c-1.7 0-3-1.3-3-3V19c0-1.7 1.3-3 3-3h7v2h-7c-.6 0-1 .4-1 1v18c0 .6.4 1 1 1h20c.6 0 1-.4 1-1V19c0-.6-.4-1-1-1h-7v-2h7c1.7 0 3 1.3 3 3v18c0 1.7-1.3 3-3 3z">
</path>
</svg>
</span>
</div>
<script src="https://oapi.map.naver.com/openapi/v3/maps.js?ncpKeyId=g3btu2qfeb"></script>
<script src="/custom/findmydevice/js/main.js"></script>
+186 -45
View File
@@ -15,21 +15,39 @@ const deviceLang = (navigator.language || navigator.userLanguage || "en").starts
autoCurrentUpdate: "현재 위치 자동 이동",
autoUpdate: "자동 업데이트",
forceUpdate: "업데이트 하기",
battery: "배터리",
batteryState: "배터리 상태",
batteryLevel: "배터리 잔량",
watchbatteryState: "워치 배터리 상태",
watchbatteryLevel: "워치 배터리 잔량",
activity: "활동",
currentActivity: "현재 활동",
activityAccuracy: "활동 정확도",
battery: "배터리",
batteryState: "배터리 상태",
batteryLevel: "배터리 잔량",
watchbatteryState: "워치 배터리 상태",
watchbatteryLevel: "워치 배터리 잔량",
isCharging: "충전 여부",
chargerType: "충전 방식",
batteryHealth: "배터리 건강",
batteryTemperature: "배터리 온도",
batteryPower: "배터리 전력",
remainingChargeTime: "충전 남은 시간",
batteryCycleCount: "배터리 사이클",
activity: "활동",
currentActivity: "현재 활동",
activityAccuracy: "활동 정확도",
distance: "도보로 움직인 거리",
steps: "걸음 수",
ascended: "오른 계단",
descended: "내려간 계단",
etc: "기타",
connection: "연결 방법",
editPage: "페이지 수정",
etc: "기타",
connection: "연결 방법",
wifiConnection: "Wi-Fi",
wifiIp: "Wi-Fi IP",
wifiLinkSpeed: "Wi-Fi 속도",
wifiFrequency: "Wi-Fi 주파수",
wifiSignalStrength: "Wi-Fi 신호",
mobileData: "모바일 데이터",
phoneState: "전화 상태",
interactive: "화면 활성",
dozeMode: "Doze 모드",
powerSave: "절전 모드",
deviceLocked: "잠금 상태",
editPage: "페이지 수정",
pwa: "눌러 홈 화면에 추가해 바로 보기",
history: "기록",
startTime: "시작",
@@ -50,21 +68,39 @@ const deviceLang = (navigator.language || navigator.userLanguage || "en").starts
autoCurrentUpdate: "Current Location Auto Update",
autoUpdate: "Auto Update",
forceUpdate: "Force Update",
battery: "Battery",
batteryState: "Battery State",
batteryLevel: "Battery Level",
watchbatteryState: "Watch Battery State",
watchbatteryLevel: "Watch Battery Level",
activity: "Activity",
battery: "Battery",
batteryState: "Battery State",
batteryLevel: "Battery Level",
watchbatteryState: "Watch Battery State",
watchbatteryLevel: "Watch Battery Level",
isCharging: "Charging",
chargerType: "Charger Type",
batteryHealth: "Battery Health",
batteryTemperature: "Battery Temp",
batteryPower: "Battery Power",
remainingChargeTime: "Remaining Charge",
batteryCycleCount: "Battery Cycles",
activity: "Activity",
currentActivity: "Current Activity",
activityAccuracy: "Activity Confidence",
distance: "Distance walked",
steps: "Steps",
ascended: "Floors Ascended",
descended: "Floors Descended",
etc: "Others",
connection: "Network Type",
editPage: "Edit Page",
etc: "Others",
connection: "Network Type",
wifiConnection: "Wi-Fi",
wifiIp: "Wi-Fi IP",
wifiLinkSpeed: "Wi-Fi Speed",
wifiFrequency: "Wi-Fi Frequency",
wifiSignalStrength: "Wi-Fi Signal",
mobileData: "Mobile Data",
phoneState: "Phone State",
interactive: "Interactive",
dozeMode: "Doze Mode",
powerSave: "Power Save",
deviceLocked: "Device Locked",
editPage: "Edit Page",
pwa: "Add to Home Screen for quick access!",
history: "History",
startTime: "Start",
@@ -538,29 +574,133 @@ function setCookieHours(name, value, hours) {
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; expires=${d.toUTCString()}; path=/; SameSite=Lax`;
}
function updateDOM(data) {
const textMap = {
currentPosition: data.address,
function updateDOM(data) {
const textMap = {
currentPosition: data.address,
lastUpdateTime: data.updated ? getRelativeTime(data.updated) : "-",
nowActivity: data.activity,
connectionType: data.connection,
updateTrigger: data.trigger,
batteryStatus: data.state,
Battery: data.level > 0 ? `${data.level}%` : "-",
WbatteryStatus: data.wstate,
WBattery: data.wlevel > 0 ? `${data.wlevel}%` : "-",
todaySteps: data.steps > 0 ? `${formatNumber(data.steps)}${langCode === "ko" ? "걸음" : "steps"}` : "-",
batteryStatus: data.state,
Battery: data.level > 0 ? `${data.level}%` : "-",
WbatteryStatus: data.wstate,
WBattery: data.wlevel > 0 ? `${data.wlevel}%` : "-",
todaySteps: data.steps > 0 ? `${formatNumber(data.steps)}${langCode === "ko" ? "걸음" : "steps"}` : "-",
todayAscended: data.ascended > 0 ? `${formatNumber(data.ascended)}${langCode === "ko" ? "층" : "floors"}` : "-",
todayDescended: data.descended > 0 ? `${formatNumber(data.descended)}${langCode === "ko" ? "층" : "floors"}` : "-",
todayDistance: data.distance > 0 ? `${formatNumber(data.distance)}m` : "-",
accuracyValue: data.accuracy > 0 ? `${formatNumber(data.accuracy)}m` : "-"
};
for (const [id, value] of Object.entries(textMap)) {
const el = document.getElementById(id);
if (el) el.textContent = value || "-";
}
}
for (const [id, value] of Object.entries(textMap)) {
const el = document.getElementById(id);
if (el) el.textContent = value || "-";
}
toggleOptionalRow('WbatteryStatus', data.wstate);
toggleOptionalRow('WBattery', data.wlevel);
updateGalaxyRows(data.galaxy || {});
}
function updateGalaxyRows(galaxy) {
const t = translations[langCode] || translations.ko;
const rows = [
['isCharging', t.isCharging, galaxy.isCharging],
['chargerType', t.chargerType, galaxy.chargerType],
['batteryHealth', t.batteryHealth, galaxy.batteryHealth],
['batteryTemperature', t.batteryTemperature, formatWithUnit(galaxy.batteryTemperature, '°C')],
['batteryPower', t.batteryPower, formatWithUnit(galaxy.batteryPower, 'W')],
['remainingChargeTime', t.remainingChargeTime, formatDuration(galaxy.remainingChargeTime)],
['batteryCycleCount', t.batteryCycleCount, galaxy.batteryCycleCount],
['wifiConnection', t.wifiConnection, galaxy.wifiConnection],
['wifiIp', t.wifiIp, galaxy.wifiIp],
['wifiLinkSpeed', t.wifiLinkSpeed, formatWithUnit(galaxy.wifiLinkSpeed, 'Mbps')],
['wifiFrequency', t.wifiFrequency, formatWithUnit(galaxy.wifiFrequency, 'MHz')],
['wifiSignalStrength', t.wifiSignalStrength, formatWithUnit(galaxy.wifiSignalStrength, 'dBm')],
['mobileData', t.mobileData, galaxy.mobileData],
['phoneState', t.phoneState, galaxy.phoneState],
['interactive', t.interactive, galaxy.interactive],
['dozeMode', t.dozeMode, galaxy.dozeMode],
['powerSave', t.powerSave, galaxy.powerSave],
['deviceLocked', t.deviceLocked, galaxy.deviceLocked],
];
rows.forEach(([key, label, value]) => {
const targetSection = ['isCharging', 'chargerType', 'batteryHealth', 'batteryTemperature', 'batteryPower', 'remainingChargeTime', 'batteryCycleCount'].includes(key)
? findSectionByTitle(langCode === 'ko' ? '배터리' : 'Battery')
: findSectionByTitle(langCode === 'ko' ? '기타' : 'Others');
upsertOptionalRow(targetSection, `galaxy_${key}`, label, value);
});
}
function findSectionByTitle(titleText) {
const titles = document.querySelectorAll('.settingTitle');
for (const title of titles) {
if (title.textContent.trim() === titleText) {
const section = title.nextElementSibling;
if (section && section.classList.contains('settingSection')) return section;
}
}
return null;
}
function upsertOptionalRow(section, id, label, value) {
if (!section) return;
const displayValue = normalizeDisplayValue(value);
let row = document.getElementById(id);
if (!displayValue) {
if (row) row.remove();
return;
}
if (!row) {
row = document.createElement('div');
row.className = 'settingRow';
row.id = id;
row.innerHTML = `<span></span><span></span>`;
section.appendChild(row);
}
row.children[0].textContent = label;
row.children[1].textContent = displayValue;
}
function toggleOptionalRow(valueId, value) {
const el = document.getElementById(valueId);
const row = el?.closest('.settingRow');
if (!row) return;
row.style.display = normalizeDisplayValue(value) ? '' : 'none';
}
function normalizeDisplayValue(value) {
if (value === null || typeof value === 'undefined' || value === '' || value === '-' || value === 'unknown' || value === 'unavailable') {
return '';
}
return String(value);
}
function formatWithUnit(value, unit) {
const normalized = normalizeDisplayValue(value);
if (!normalized) return '';
return /[a-zA-Z%°]/.test(normalized) ? normalized : `${normalized}${unit}`;
}
function formatDuration(value) {
const normalized = normalizeDisplayValue(value);
if (!normalized) return '';
const seconds = Number(normalized);
if (!Number.isFinite(seconds)) return normalized;
if (seconds <= 0) return '0s';
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = Math.floor(seconds % 60);
return [
h ? `${h}h` : '',
m ? `${m}m` : '',
s && !h ? `${s}s` : '',
].filter(Boolean).join(' ');
}
function updateActivitySection(data) {
const validActivities = ["Stationary", "Walking", "Running", "In Vehicle", "Cycling", "가만히 있음", "걷는 중", "달리는 중", "차량 이동 중", "자전거 이동 중"];
@@ -1006,18 +1146,19 @@ function debounce(fn, delay = 400) {
};
}
function resetDisplayValues() {
const ids = [
'currentPosition', 'accuracyValue', 'lastUpdateTime', 'updateTrigger',
'batteryStatus', 'Battery', 'WbatteryStatus', 'WBattery',
'nowActivity', 'nowactivityAccuracy', 'todayDistance',
'todaySteps', 'todayAscended', 'todayDescended', 'connectionType'
];
ids.forEach(id => {
const el = document.getElementById(id);
if (el) el.textContent = '-';
});
}
function resetDisplayValues() {
const ids = [
'currentPosition', 'accuracyValue', 'lastUpdateTime', 'updateTrigger',
'batteryStatus', 'Battery', 'WbatteryStatus', 'WBattery',
'nowActivity', 'nowactivityAccuracy', 'todayDistance',
'todaySteps', 'todayAscended', 'todayDescended', 'connectionType'
];
ids.forEach(id => {
const el = document.getElementById(id);
if (el) el.textContent = '-';
});
document.querySelectorAll('[id^="galaxy_"]').forEach(row => row.remove());
}
function triggerForceUpdate() {
callDeviceWebhook('force_update');
+105 -10
View File
@@ -42,8 +42,6 @@ $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'];
@@ -77,13 +75,39 @@ function getData($entityId) {
return custom_ha_state('seoul', $entityId) ?? [];
}
function getEntityData($entities, $key) {
if (empty($entities[$key])) {
return [];
}
return getData($entities[$key]);
}
function stateValue($data) {
$state = $data['state'] ?? null;
if ($state === null || $state === 'unknown' || $state === 'unavailable' || $state === '<not connected>' || $state === '<not available>') {
return null;
}
return $state;
}
function boolText($state, $lang) {
if ($state === null) return null;
if ($lang === 'ko') {
return $state === 'on' ? '켜짐' : ($state === 'off' ? '꺼짐' : $state);
}
return $state === 'on' ? 'On' : ($state === 'off' ? 'Off' : $state);
}
function mappedText($state, $lang, $map) {
if ($state === null) return null;
return $map[$lang][$state] ?? $map[$lang][strtolower((string)$state)] ?? $state;
}
// 데이터 불러오기
$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);
@@ -92,6 +116,27 @@ $stepsData = getData($stepsUrl);
$distanceData = getData($distanceUrl);
$historyData = custom_ha_request('seoul', 'GET', $historyEndpoint) ?? [];
$triggerData = getData($triggerUrl);
$galaxyData = [
'isCharging' => getEntityData($entities, 'is_charging'),
'chargerType' => getEntityData($entities, 'charger_type'),
'batteryHealth' => getEntityData($entities, 'battery_health'),
'batteryTemperature' => getEntityData($entities, 'battery_temperature'),
'batteryPower' => getEntityData($entities, 'battery_power'),
'remainingChargeTime' => getEntityData($entities, 'remaining_charge_time'),
'batteryCycleCount' => getEntityData($entities, 'battery_cycle_count'),
'wifiConnection' => getEntityData($entities, 'wifi_connection'),
'wifiBssid' => getEntityData($entities, 'wifi_bssid'),
'wifiIp' => getEntityData($entities, 'wifi_ip'),
'wifiLinkSpeed' => getEntityData($entities, 'wifi_link_speed'),
'wifiFrequency' => getEntityData($entities, 'wifi_frequency'),
'wifiSignalStrength' => getEntityData($entities, 'wifi_signal_strength'),
'mobileData' => getEntityData($entities, 'mobile_data'),
'phoneState' => getEntityData($entities, 'phone_state'),
'interactive' => getEntityData($entities, 'interactive'),
'dozeMode' => getEntityData($entities, 'doze_mode'),
'powerSave' => getEntityData($entities, 'power_save'),
'deviceLocked' => getEntityData($entities, 'device_locked'),
];
// 경로 기록
$path = [];
@@ -160,18 +205,43 @@ $connectionMap = [
$batteryStateMap = [
'ko' => [
'Charging' => '충전 중',
'Discharging' => '방전 중',
'Not Charging' => '충전중이 아님',
'Full' => '가득참',
'charging' => '충전 중',
'discharging' => '방전 중',
'not_charging' => '충전중이 아님',
'full' => '가득참',
'Unknown' => '-',
],
'en' => [
'Charging' => 'Charging',
'Discharging' => 'Discharging',
'Not Charging' => 'Not Charging',
'Full' => 'Full',
'charging' => 'Charging',
'discharging' => 'Discharging',
'not_charging' => 'Not Charging',
'full' => 'Full',
'Unknown' => '-',
]
];
$androidValueMap = [
'chargerType' => [
'ko' => ['none' => '없음', 'wireless' => '무선', 'ac' => '유선', 'usb' => 'USB'],
'en' => ['none' => 'None', 'wireless' => 'Wireless', 'ac' => 'AC', 'usb' => 'USB'],
],
'batteryHealth' => [
'ko' => ['good' => '양호', 'overheat' => '과열', 'dead' => '불량', 'over_voltage' => '과전압', 'cold' => '저온'],
'en' => ['good' => 'Good', 'overheat' => 'Overheat', 'dead' => 'Dead', 'over_voltage' => 'Over voltage', 'cold' => 'Cold'],
],
'phoneState' => [
'ko' => ['idle' => '대기', 'ringing' => '수신 중', 'offhook' => '통화 중'],
'en' => ['idle' => 'Idle', 'ringing' => 'Ringing', 'offhook' => 'Off hook'],
],
];
$triggerMap = [
'ko' => [
"Launch" => "앱 실행",
@@ -202,10 +272,9 @@ $triggerMap = [
];
$activityState = $activityData['state'] ?? null;
$aaccuracyState = $activityData['attributes']['Confidence'] ?? null;
$aaccuracyState = $activityData['attributes']['Confidence'] ?? $activityData['attributes']['confidence'] ?? null;
$connectionState = $connectionData['state'] ?? null;
$batteryState = $batteryStateData['state'] ?? null;
$watchBatteryState = $watchBatteryStateData['state'] ?? null;
$address = null;
@@ -224,6 +293,34 @@ if (($deviceData['state'] ?? null) !== 'not_home') {
$address = trim($locality . ' ' . ($geoData['attributes']['Name'] ?? ''));
}
$galaxy = [
'isCharging' => boolText(stateValue($galaxyData['isCharging']), $lang),
'chargerType' => mappedText(stateValue($galaxyData['chargerType']), $lang, $androidValueMap['chargerType']),
'batteryHealth' => mappedText(stateValue($galaxyData['batteryHealth']), $lang, $androidValueMap['batteryHealth']),
'batteryTemperature' => stateValue($galaxyData['batteryTemperature']),
'batteryPower' => stateValue($galaxyData['batteryPower']),
'remainingChargeTime' => stateValue($galaxyData['remainingChargeTime']),
'batteryCycleCount' => stateValue($galaxyData['batteryCycleCount']),
'wifiConnection' => stateValue($galaxyData['wifiConnection']),
'wifiBssid' => stateValue($galaxyData['wifiBssid']),
'wifiIp' => stateValue($galaxyData['wifiIp']),
'wifiLinkSpeed' => stateValue($galaxyData['wifiLinkSpeed']),
'wifiFrequency' => stateValue($galaxyData['wifiFrequency']),
'wifiSignalStrength' => stateValue($galaxyData['wifiSignalStrength']),
'mobileData' => boolText(stateValue($galaxyData['mobileData']), $lang),
'phoneState' => mappedText(stateValue($galaxyData['phoneState']), $lang, $androidValueMap['phoneState']),
'interactive' => boolText(stateValue($galaxyData['interactive']), $lang),
'dozeMode' => boolText(stateValue($galaxyData['dozeMode']), $lang),
'powerSave' => boolText(stateValue($galaxyData['powerSave']), $lang),
'deviceLocked' => boolText(stateValue($galaxyData['deviceLocked']), $lang),
];
if (!$galaxy['wifiConnection']) {
foreach (['wifiBssid', 'wifiIp', 'wifiLinkSpeed', 'wifiFrequency', 'wifiSignalStrength'] as $wifiKey) {
$galaxy[$wifiKey] = null;
}
}
// 해시 요청만 들어온 경우
if (isset($_GET['hashonly']) && $_GET['hashonly'] == '1') {
$raw = json_encode([
@@ -235,8 +332,6 @@ if (isset($_GET['hashonly']) && $_GET['hashonly'] == '1') {
'address' => $address,
'state' => $batteryStateMap[$lang][$batteryState] ?? $batteryState ?? null,
'level' => $batteryLevelData['state'] ?? null,
'wstate' => $batteryStateMap[$lang][$watchBatteryState] ?? $watchBatteryState ?? null,
'wlevel' => $watchBatteryLevelData['state'] ?? null,
'ascended' => $ascendedData['state'] ?? null,
'descended' => $descendedData['state'] ?? null,
'steps' => $stepsData['state'] ?? null,
@@ -244,6 +339,7 @@ if (isset($_GET['hashonly']) && $_GET['hashonly'] == '1') {
'activity' => $activityMap[$lang][$activityState] ?? $activityState ?? null,
'aaccuracy' => $aaccuracyMap[$lang][$aaccuracyState] ?? $aaccuracyState ?? null,
'connection' => $connectionMap[$lang][$connectionState] ?? $connectionState ?? null,
'galaxy' => $galaxy,
]);
echo json_encode(['hash' => hash('sha256', $raw)]);
@@ -259,8 +355,6 @@ echo json_encode([
'address' => $address,
'state' => $batteryStateMap[$lang][$batteryState] ?? $batteryState ?? null,
'level' => $batteryLevelData['state'] ?? null,
'wstate' => $batteryStateMap[$lang][$watchBatteryState] ?? $watchBatteryState ?? null,
'wlevel' => $watchBatteryLevelData['state'] ?? null,
'ascended' => $ascendedData['state'] ?? null,
'descended' => $descendedData['state'] ?? null,
'steps' => $stepsData['state'] ?? null,
@@ -268,6 +362,7 @@ echo json_encode([
'activity' => $activityMap[$lang][$activityState] ?? $activityState ?? null,
'aaccuracy' => $aaccuracyMap[$lang][$aaccuracyState] ?? $aaccuracyState ?? null,
'connection' => $connectionMap[$lang][$connectionState] ?? $connectionState ?? null,
'galaxy' => $galaxy,
'history' => $path ?? null,
], JSON_UNESCAPED_UNICODE);
?>