From 41fe7195025c166f002924f40d1566f5ed6e0c93 Mon Sep 17 00:00:00 2001 From: Kevin - Stark 4N6 <48143894+stark4n6@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:10:21 -0500 Subject: [PATCH] Update health.py - Add activity location type - Add source devices --- scripts/artifacts/health.py | 85 +++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/scripts/artifacts/health.py b/scripts/artifacts/health.py index 1ccf688b..9f7cb5b7 100644 --- a/scripts/artifacts/health.py +++ b/scripts/artifacts/health.py @@ -178,17 +178,28 @@ "paths": ('*Health/healthdb_secure.sqlite*',), "output_types": "standard", "artifact_icon": 'moon' + }, + "healthSourceDevices": { + "name": "Health - Source Devices", + "description": "Parses Apple Health device info from the healthdb.sqlite database, including make/model/software information.", + "author": "@stark4n6", + "version": "0.1", + "date": "2025-03-03", + "requirements": "none", + "category": "Health", + "notes": "", + "paths": ('*Health/healthdb.sqlite*',), + "output_types": "standard", + "artifact_icon": 'smartphone' } } - import scripts.artifacts.artGlobals from packaging import version from scripts.builds_ids import OS_build, device_id from scripts.ilapfuncs import artifact_processor, open_sqlite_db_readonly, attach_sqlite_db_readonly, convert_ts_human_to_timezone_offset - @artifact_processor def healthWorkouts(files_found, report_folder, seeker, wrap_text, timezone_offset): data_list = [] @@ -359,6 +370,11 @@ def healthWorkouts(files_found, report_folder, seeker, wrap_text, timezone_offse CASE workout_activities.activity_type''' + activity_types + ''' ELSE "Unknown" || "-" || workout_activities.activity_type END AS 'Type', + CASE workout_activities.location_type + WHEN 2 THEN 'Indoor' + WHEN 3 THEN 'Outdoor' + ELSE workout_activities.location_type + END AS 'Location Type', strftime('%H:%M:%S', samples.end_date - samples.start_date, 'unixepoch') AS 'Total Time Duration', strftime('%H:%M:%S', workout_activities.duration, 'unixepoch') AS 'Duration', ''' + distance_and_goals + ''' @@ -414,28 +430,28 @@ def healthWorkouts(files_found, report_folder, seeker, wrap_text, timezone_offse for row in all_rows: start_timestamp = convert_ts_human_to_timezone_offset(row[0], timezone_offset) end_timestamp = convert_ts_human_to_timezone_offset(row[1], timezone_offset) - added_timestamp = convert_ts_human_to_timezone_offset(row[25], timezone_offset) - hardware = device_id.get(row[21], row[21]) + added_timestamp = convert_ts_human_to_timezone_offset(row[26], timezone_offset) + hardware = device_id.get(row[22], row[22]) os_family = '' - if 'Watch' in row[21]: + if 'Watch' in row[22]: os_family = 'watchOS ' - elif 'iPhone' in row[21]: + elif 'iPhone' in row[22]: os_family = 'iOS ' - software_version = f'{os_family}{row[23]}' + software_version = f'{os_family}{row[24]}' - if row[15]: - celcius_temp = round(((row[15] - 32) * (5 / 9)), 2) + if row[16]: + celcius_temp = round(((row[16] - 32) * (5 / 9)), 2) data_list.append( - (start_timestamp, end_timestamp, row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], - row[10], row[11], row[12], row[13], row[14], celcius_temp, row[15], row[16], row[17], row[18], - row[19], row[20], hardware, row[22], software_version, row[24], added_timestamp) + (start_timestamp, end_timestamp, str(row[2]).title(), row[3], row[4], row[5], row[6], row[7], row[8], row[9], + row[10], row[11], row[12], row[13], row[14], row[15], celcius_temp, row[16], row[17], row[18], row[19], + row[20], row[21], hardware, row[23], software_version, row[25], added_timestamp) ) data_headers = ( - ('Start Timestamp', 'datetime'), ('End Timestamp', 'datetime'), 'Type', 'Total Time Duration', 'Duration', 'Distance (in KM)', + ('Start Timestamp', 'datetime'), ('End Timestamp', 'datetime'), 'Activity Type', 'Location Type','Total Time Duration', 'Duration', 'Distance (in KM)', 'Distance (in Miles)', 'Goal Type', 'Goal', 'Total Active Energy (kcal)', 'Total Resting Energy (kcal)', 'Average METs', 'Min. Heart Rate (BPM)', 'Max. Heart Rate (BPM)', 'Average Heart Rate (BPM)', 'Temperature (°C)', 'Temperature (°F)', 'Humidity (%)', 'Latitude', 'Longitude', 'Min. ground elevation (in Meters)', 'Max. ground elevation (in Meters)', @@ -1060,8 +1076,49 @@ def healthWatchBySleepPeriod(files_found, report_folder, seeker, wrap_text, time (start_timestamp, end_timestamp, row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11])) data_headers = ( - ('Sleep Start Time'), ('Sleep End Time'), 'Time in Bed (HH:MM:SS)', + ('Sleep Start Time', 'datetime'), ('Sleep End Time', 'datetime'), 'Time in Bed (HH:MM:SS)', 'Time Asleep (HH:MM:SS)', 'Awake Duration (HH:MM:SS)', 'REM Duration (HH:MM:SS)', 'Core Duration (HH:MM:SS)', 'Deep Duration (HH:MM:SS)', 'Awake %', 'REM %', 'Core %', 'Deep %') return data_headers, data_list, healthdb_secure + +@artifact_processor +def healthSourceDevices(files_found, report_folder, seeker, wrap_text, timezone_offset): + data_list = [] + healthdb = '' + + for file_found in files_found: + if file_found.endswith('healthdb.sqlite'): + healthdb = file_found + break + + with open_sqlite_db_readonly(healthdb) as db: + cursor = db.cursor() + cursor.execute(''' + select + DATETIME(creation_date + 978307200, 'UNIXEPOCH') AS creation_date, + name, + manufacturer, + model, + hardware, + firmware, + software, + localIdentifier, + sync_provenance, + sync_identity + from source_devices + where name not like '__NONE__' and localIdentifier not like '__NONE__' + ''') + + all_rows = cursor.fetchall() + + for row in all_rows: + creation_date = convert_ts_human_to_timezone_offset(row[0], timezone_offset) + device = device_id.get(row[4], row[4]) + localID = '' + if str(row[7]).endswith('-tacl'): + localID = str(row[7])[:-5] + data_list.append((creation_date, row[1], row[2], row[3], device, row[5], row[6], localID, row[8], row[9])) + + data_headers = (('Creation Date', 'datetime'),'Device Name','Manufacturer','Model','Hardware','Firmware','Software','Local ID','Sync Provenance','Sync ID') + return data_headers, data_list, healthdb \ No newline at end of file