| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Modern, async Python client for the open‑source FMD (Find My Device) server. It handles authentication, key management, encrypted data decryption, location/picture retrieval, and common device commands with safe, validated helpers.
pip install fmd_apiimport asyncio, json
from fmd_api import FmdClient
async def main():
# Recommended: async context manager auto-closes session
async with await FmdClient.create("https://fmd.example.com", "alice", "secret", drop_password=True) as client:
# Request a fresh GPS fix and wait a bit on your side
await client.request_location("gps")
# Fetch most recent locations and decrypt the latest
blobs = await client.get_locations(num_to_get=1)
# decrypt_data_blob() returns raw bytes — decode then parse JSON for clarity
decrypted = client.decrypt_data_blob(blobs[0])
loc = json.loads(decrypted.decode("utf-8"))
print(loc["lat"], loc["lon"], loc.get("accuracy"))
# Take a picture (validated helper)
await client.take_picture("front")
asyncio.run(main())HTTPS is strongly recommended for all connections to FMD server. HTTP is permitted for local development or trusted private networks, but should not be used in production. If you need to connect to a server with a self-signed certificate, you have two options:
Examples:
import ssl
from fmd_api import FmdClient
# 1) Custom CA bundle / pinned cert (recommended)
ctx = ssl.create_default_context()
ctx.load_verify_locations(cafile="/path/to/your/ca.pem")
# Via constructor
client = FmdClient("https://fmd.example.com", ssl=ctx)
# Or via factory
# async with await FmdClient.create("https://fmd.example.com", "user", "pass", ssl=ctx) as client:
# 2) Disable verification (development only)
insecure_client = FmdClient("https://fmd.example.com", ssl=False)Notes:
Warning
Passing ssl=False disables TLS certificate validation and should only be used in development. For production, use a custom ssl.SSLContext that trusts your CA/certificate or pin the server certificate. Using http:// URLs sends credentials and data in plaintext — only use HTTP on trusted local networks or for development purposes.
If you're using a self-signed certificate and want to pin to that exact cert, load the server's PEM (or DER) directly into an SSLContext. This ensures only that certificate (or its CA) is trusted.
import ssl
from fmd_api import FmdClient
# Export your server's certificate to PEM (e.g., server-cert.pem)
ctx = ssl.create_default_context()
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.check_hostname = True # keep hostname verification when possible
ctx.load_verify_locations(cafile="/path/to/server-cert.pem")
client = FmdClient("https://fmd.example.com", ssl=ctx)
# async with await FmdClient.create("https://fmd.example.com", "user", "pass", ssl=ctx) as client:Tips:
FmdClient (primary API)
Note: Device statistics functionality (get_device_stats()) has been temporarily removed and will be restored when the FMD server supports it (see fmd-server#74).
Device helper (per‑device convenience)
IMPORTANT (breaking change in v2.0.5): legacy compatibility wrappers were removed. The following legacy methods were removed from the Device API: fetch_pictures, get_pictures, download_photo, get_picture, take_front_photo, and take_rear_photo. Update your code to use get_picture_blobs(), decode_picture(), take_front_picture() and take_rear_picture() instead.
import asyncio
from fmd_api import FmdClient, Device
async def main():
client = await FmdClient.create("https://fmd.example.com", "alice", "secret")
device = Device(client, "alice")
# Optional message is sanitized (quotes/newlines removed, whitespace collapsed)
await device.lock(message="Lost phone. Please call +1-555-555-1234")
await client.close()
asyncio.run(main())Use get_picture_blobs() to fetch the raw server responses (strings or dicts). If you want a strongly-typed list of picture metadata objects (where the server provides metadata as JSON objects), use get_picture_metadata(), which filters for dict entries and returns only those.
from fmd_api import FmdClient, Device
async def inspect_metadata():
client = await FmdClient.create("https://fmd.example.com", "alice", "secret")
device = Device(client, "alice")
# Raw values may be strings (base64 blobs) or dicts (metadata). Keep raw when you need
# to decode or handle both forms yourself.
raw = await device.get_picture_blobs(10)
# If you want only metadata entries returned by the server, use get_picture_metadata().
# This returns a list of dict-like metadata objects (e.g. id/date/filename) and filters
# out any raw string blobs.
metadata = await device.get_picture_metadata(10)
for m in metadata:
print(m.get("id"), m.get("date"))
await client.close()
asyncio.run(inspect_metadata())Runnable scripts under tests/functional/:
Put credentials in tests/utils/credentials.txt (copy from credentials.txt.example).
Located in tests/unit/:
Run with pytest:
pip install -e ".[dev]"
pytest tests/unit/You can onboard once with a raw password, optionally discard it immediately using drop_password=True, export authentication artifacts, and later resume without storing the raw secret:
client = await FmdClient.create(url, fmd_id, password, drop_password=True)
artifacts = await client.export_auth_artifacts()
# Persist `artifacts` securely (contains hash, token, private key)
# Later / after restart
client2 = await FmdClient.from_auth_artifacts(artifacts)
locations = await client2.get_locations(1)On a 401, the client will transparently reauthenticate using the stored Argon2id password_hash if available. When drop_password=True, the raw password is never retained after initial onboarding.
This client targets the FMD ecosystem:
MIT © 2025 Devin Slick
| Back | FazBrowse Home | New Git URL |