No need to be Mac-Specific anymore. I now have a script that will get the files from your iPhone no matter what platform you run - Mac Windows Linux.
EDIT: Update (new screenshots in the replies)
All data is now categorized into Sleep, Activity, Heart, Nutrition, Respiration.
All data from a single session that takes multiple readings are grouped by session, not individually.
StethO results are now available for review.
I have to finish up putting sleep info into HealthKit.
I am not going to pursue the nutrition and activity importations, I've done enough.
Devices I do not own (Scanwatch, Aura, U-Scan, CGM) may likely not appear, but every effort to allow Scanwatch readings to work has been done (it should work). The app will import any readings taken from a Withings device that have not been previously imported into Apple Health.
Removal of private information from the project files - then:
Upload to GitHub & documentation.
If you have an M-series Mac, read my instructions at the end of this top post. If you like tinkering and can use Xcode, you can have a lot of fun with this.
If you do not have an M-series Mac, this will still work - but you will need a 3rd party utility to get it to work.
ORIGINAL POST:
In response to the recent HealthKit developments, I'm nearing completion on an application that will fix the issue. An interesting side effect is the application reveals a hell of a lot more than just potential HealthKit data, it reveals - EVERYTHING. But the primary purpose is extraction of Withings data for HealthKit import.
Your data is placed in an iCloud directory and all processing on it is done on-device (iPhone).
Your Metrics Categories show up in a drill-down menu, shown below:
Select an item, say BMR, and you get a list of entries:
Tap on one and you get the details of every measurement:
Same with BP and other measurements:
Sleep data too:
And sleep details:
Aside from reviewing your data, you can then select to import any compatible data into HealthKit.
The app does duplicate detection, so it will not duplicate any Withings entry that already exists to any entry by the app that already exists.
The app also will delete all entries it makes into HealthKit, so its actions are fully reversible.
This is a proof of concept. I still have to bring in the workout sessions.
So the next question is:
Where do I get this data?
Pretty much it is already on your phone. There are several ways of making it accessible. I'm developing in a Mac environment, but I wanted to make sure the input to the utility is platform-agnostic as much as possible.
For Android users, there is probably a similar mechanism to get to the data.
I will not be releasing this as an application on the app store - for obvious reasons. But the source code will be made available for other's research purposes.
I'll answer questions as they pop up.
In the meantime, you can prepare to save your personal data:
If you have a Mac, go to the App Store and download the Withings' application. Log in and view your data on your Mac as you do on your phone. Then stand by.
If the app gets pulled, or you do not have a Mac, you can still get the data off of your iPhone jumping through an additional hoop. But it is available.
You do not need to jailbreak your device, you do not need to use Withings API - all data in already present locally on your device. Nothing is sent out over the internet, only to and from HealthKit and iCloud.
// DISCLAIMER: This code is for educational and illustrative purposes only. // It is provided "as is" without any express or implied warranties. // Use at your own risk. The author assumes no liability for errors or damages.
Here's how to get and keep your data.
I've tried to make these two scripts as cross-platform compatible as I can, but it's only been tested on Mac. It's python 3, so if you know what that is, you should know what you are doing.
Main script, copy and paste into a file named "backup_iphone.py":
#!/usr/bin/env python3
"""
Cross-platform (Windows / macOS / Linux) iPhone backup + extract.
Runs idevicebackup2 (libimobiledevice) to take a local backup, then calls the
extractor from extract_withings.py (keep both files in the same folder).
Requires libimobiledevice tools on PATH:
macOS: brew install libimobiledevice
Linux: sudo apt install libimobiledevice-utils usbmuxd
Windows: Apple's USB driver (iTunes or Apple Devices app) plus the
libimobiledevice Windows binaries on PATH.
Encrypted backups also need: pip install iphone_backup_decrypt
Usage:
python backup_iphone.py [--backup-root DIR] [--out DIR] [--full] [--no-extract]
"""
import argparse
import shutil
import subprocess
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE))
def run(cmd, check=True, capture=False):
return subprocess.run(cmd, check=check, text=True, capture_output=capture)
def main() -> None:
ap = argparse.ArgumentParser(description=__doc__.split("\n")[1])
ap.add_argument("--backup-root", type=Path, default=Path.home() / "iphone_backups")
ap.add_argument("--out", type=Path, default=Path("withings_out"))
ap.add_argument("--full", action="store_true", help="force a full (non-incremental) backup")
ap.add_argument("--no-extract", action="store_true", help="only take the backup")
args = ap.parse_args()
for tool in ("idevice_id", "idevicepair", "idevicebackup2"):
if not shutil.which(tool):
sys.exit(f"Missing {tool} - install libimobiledevice and put it on PATH.")
ids = run(["idevice_id", "-l"], capture=True).stdout.split()
if not ids:
sys.exit("No iPhone found. Plug it in, unlock it, and tap Trust if prompted.")
udid = ids[0]
print(f"Device: {udid}")
# Pair (no-op if already paired). May require tapping Trust on the phone.
run(["idevicepair", "-u", udid, "pair"], check=False)
if run(["idevicepair", "-u", udid, "validate"], check=False).returncode != 0:
sys.exit("Pairing validation failed.")
args.backup_root.mkdir(parents=True, exist_ok=True)
print("Starting backup (keep the phone unlocked; this can take a while)...")
cmd = ["idevicebackup2", "-u", udid, "backup"]
if args.full:
cmd.append("--full")
cmd.append(str(args.backup_root))
# If the phone has backup encryption on, idevicebackup2 will need that password.
if run(cmd, check=False).returncode != 0:
sys.exit("Backup failed.")
backup_dir = args.backup_root / udid
print(f"Backup complete: {backup_dir}")
if args.no_extract:
return
from extract_withings import extract_encrypted, extract_unencrypted, is_encrypted
args.out.mkdir(parents=True, exist_ok=True)
if is_encrypted(backup_dir):
print("Backup is encrypted.")
extract_encrypted(backup_dir, args.out)
else:
print("Backup is not encrypted.")
print(f"Copied {extract_unencrypted(backup_dir, args.out)} files.")
print(f"Done. Output in: {args.out.resolve()}")
if __name__ == "__main__":
main()
Script #2, copy and paste into a file named "extract_withings.py", the filename must be this name, and in the same directory, as the first script calls this one:
#!/usr/bin/env python3
"""
Extract an app's files from a local iPhone backup (encrypted or unencrypted).
Usage:
python extract_withings.py "<backup folder>" [output folder]
Example (Mac):
python extract_withings.py \
"$HOME/Library/Application Support/MobileSync/Backup/<device-id>" ./withings_out
Encrypted backups need: pip install iphone_backup_decrypt
"""
import getpass
import plistlib
import shutil
import sqlite3
import sys
from pathlib import Path
BUNDLE_ID = "com.withings.wiScaleNG"
# Matches AppDomain-<id>, AppDomainGroup-group.<id>, AppDomainPlugin-<id>.*
DOMAIN_LIKE = f"%{BUNDLE_ID}%"
# Only SQLite files under a CoreData directory. The trailing % also catches the
# -wal and -shm sidecar files, which CoreData stores need to be complete.
PATH_LIKE = "%CoreData%.sqlite%"
def is_encrypted(backup: Path) -> bool:
with open(backup / "Manifest.plist", "rb") as f:
return bool(plistlib.load(f).get("IsEncrypted", False))
def extract_unencrypted(backup: Path, out: Path) -> int:
db = sqlite3.connect(f"file:{backup / 'Manifest.db'}?mode=ro", uri=True)
rows = db.execute(
"SELECT fileID, domain, relativePath FROM Files "
"WHERE domain LIKE ? AND relativePath LIKE ? AND flags = 1",
(DOMAIN_LIKE, PATH_LIKE),
).fetchall()
count = 0
for file_id, domain, rel in rows:
src = backup / file_id[:2] / file_id
if not src.exists():
continue # listed in manifest but not stored in this backup
dest = out / domain / rel
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dest)
count += 1
return count
def extract_encrypted(backup: Path, out: Path) -> None:
try:
from iphone_backup_decrypt import EncryptedBackup
except ImportError:
sys.exit("Encrypted backup: run pip install iphone_backup_decrypt first.")
passphrase = getpass.getpass("Backup password: ")
eb = EncryptedBackup(backup_directory=str(backup), passphrase=passphrase)
eb.test_decryption() # raises if the password is wrong
kwargs = dict(
relative_paths_like=PATH_LIKE,
domain_like=DOMAIN_LIKE,
output_folder=str(out),
preserve_folders=True,
)
try:
eb.extract_files(domain_subfolders=True, **kwargs)
except TypeError:
# Older library versions lack domain_subfolders
eb.extract_files(**kwargs)
def main() -> None:
if len(sys.argv) < 2:
sys.exit(__doc__)
backup = Path(sys.argv[1]).expanduser()
out = Path(sys.argv[2] if len(sys.argv) > 2 else "./withings_out").expanduser()
out.mkdir(parents=True, exist_ok=True)
if not (backup / "Manifest.plist").exists():
sys.exit(f"No Manifest.plist in {backup} - is this the device's backup folder?")
if is_encrypted(backup):
print("Backup is encrypted.")
extract_encrypted(backup, out)
else:
print("Backup is not encrypted.")
n = extract_unencrypted(backup, out)
print(f"Copied {n} files.")
print(f"Done. Output in: {out.resolve()}")
if __name__ == "__main__":
main()
Now that you have all that, here's how you use it:
Read the comments in the top of the backup_iphone.py script to ensure you have the necessary utilities/modules installed. The two things it needs are: libimobiledevice and iphone_backup_decrypt.
Connect your iPhone to your computer via USB cable. Tell your iPhone to trust the computer (if prompted)
Type your passcode to start the backup. This should be an encrypted backup, You do have your phone set to encrypt backups, don't you? and in the example above, it will place the backup in the folder "iphone_backups". Note that backups are incremental, and it does the entire phone on the first backup. So the first backup will take a while. Subsequent backups will take a lot less time as only changed files will be backed up.
After the backup is complete, the script will prompt you for your password - The password should be the one you set up on your phone when you told it to do encrypted backups.
Your files will be in the withings_out directory. Specifically: ./withings_out/AppDomain-com.withings.wiScaleNG/Library/Application Support/coredata
In there you will find a list of files:
The "XXXXXXXX" will be replaced with your Withings' account identifier. You will have additional sets of these files for each authorized user you share your devices with.
That's your data. All of it.
Write an app to read it all, or import data to Apple health, or Google's offering.
New to this sub. Just want to share the surprise I got from my wife's withings scale. Over the past 11 years, I have occasionally stepped on my wife's withings body+ scale, and it recorded my weight. Turns out all the weight had been stored in Withing's server. I was able to extra the weight via screen cap and reading it with AI:
The data has been highly useful in figuring out my "safe weight", when it's correlated with my liver enzyme values. I've recently been diagnosed with severe fatty liver and starting treatment with it.
I have to say, I've been happy with Withings as a company, especially how it supports its product and handles its data for the past decade. Hopefully it stays a similar company for the next decade, despite the pressures of investors. Fingers crossed.
Got the BF last week and the first couple of days the bodyfat % seemed to be right (around 15%) but the last 2 days it dropped down to 12% with no real weight change. 12% seems too low for me so not sure what happened.
I’m considering it. Aside from all this Apple Health malarkey:
- I can wear the Oura Ring in the sauna and in the ocean.
- I put all this food info into MyFitnessPal which syncs to Withings. Withings then give me no insights about my food whatsoever.
Another big one:
- IMO Withings work best if you go all in on their ecosystem. Makes it a bit hard to do so in Australia where their piss tracker is unavailable (the app relies on this for nutrition insights).