Skip to main content

An async Python client library for the LoJack API, designed for Home Assistant integrations.

Project description

lojack_api

An async Python client library for the Spireon LoJack API, designed for Home Assistant integrations.

Tests codecov PyPI - Downloads

Features

  • Async-first design - Built with asyncio and aiohttp for non-blocking I/O
  • No httpx dependency - Uses aiohttp to avoid version conflicts with Home Assistant
  • Spireon LoJack API - Full support for the Spireon identity and services APIs
  • Session management - Automatic token refresh and session resumption support
  • Type hints - Full typing support with py.typed marker
  • Clean device abstractions - Device and Vehicle wrappers with convenient methods

Installation

# From the repository
pip install .

# With development dependencies
pip install .[dev]

Quick Start

Basic Usage

import asyncio
from lojack_api import LoJackClient

async def main():
    # Create and authenticate (uses default Spireon URLs)
    async with await LoJackClient.create(
        "your_username",
        "your_password"
    ) as client:
        # List all devices/vehicles
        devices = await client.list_devices()

        for device in devices:
            print(f"Device: {device.name} ({device.id})")

            # Get current location
            location = await device.get_location()
            if location:
                print(f"  Location: {location.latitude}, {location.longitude}")

asyncio.run(main())

Session Resumption (for Home Assistant)

For Home Assistant integrations, you can persist authentication across restarts:

from lojack_api import LoJackClient, AuthArtifacts

# First time - login and save auth
async def initial_login(username, password):
    client = await LoJackClient.create(username, password)
    auth_data = client.export_auth().to_dict()
    # Save auth_data to Home Assistant storage
    await client.close()
    return auth_data

# Later - resume without re-entering password
async def resume_session(auth_data, username=None, password=None):
    auth = AuthArtifacts.from_dict(auth_data)
    # Pass credentials for auto-refresh if token expires
    client = await LoJackClient.from_auth(auth, username=username, password=password)
    return client

Using External aiohttp Session

For Home Assistant integrations, pass the shared session:

from aiohttp import ClientSession
from lojack_api import LoJackClient

async def setup(hass_session: ClientSession, username, password):
    client = await LoJackClient.create(
        username,
        password,
        session=hass_session  # Won't be closed when client closes
    )
    return client

Working with Vehicles

Vehicles have additional properties and commands:

from lojack_api import Vehicle

async def vehicle_example(client):
    devices = await client.list_devices()

    for device in devices:
        if isinstance(device, Vehicle):
            print(f"Vehicle: {device.name}")
            print(f"  VIN: {device.vin}")
            print(f"  Make: {device.make} {device.model} ({device.year})")

            # Vehicle-specific commands
            await device.start_engine()
            await device.honk_horn()
            await device.flash_lights()

Device Commands

# All devices support these commands
await device.lock(message="Please return this device")
await device.unlock()
await device.ring(duration=30)
await device.request_location_update()

# Get location history
async for location in device.get_history(limit=100):
    print(f"{location.timestamp}: {location.latitude}, {location.longitude}")

API Reference

LoJackClient

The main entry point for the API.

# Factory methods (using default Spireon URLs)
client = await LoJackClient.create(username, password)
client = await LoJackClient.from_auth(auth_artifacts)

# With custom URLs
client = await LoJackClient.create(
    username,
    password,
    identity_url="https://identity.spireon.com",
    services_url="https://services.spireon.com/v0/rest"
)

# Properties
client.is_authenticated  # bool
client.user_id           # Optional[str]

# Methods
devices = await client.list_devices()           # List[Device | Vehicle]
device = await client.get_device(device_id)     # Device | Vehicle
locations = await client.get_locations(device_id, limit=10)
success = await client.send_command(device_id, "locate")
auth = client.export_auth()                     # AuthArtifacts
await client.close()

Device

Wrapper for tracked devices.

# Properties
device.id            # str
device.name          # Optional[str]
device.info          # DeviceInfo
device.last_seen     # Optional[datetime]
device.cached_location  # Optional[Location]

# Methods
await device.refresh(force=True)
location = await device.get_location(force=False)
async for loc in device.get_history(limit=100):
    ...
await device.lock(message="...", passcode="...")
await device.unlock()
await device.ring(duration=30)
await device.request_location_update()
await device.send_command("custom_command")

Vehicle (extends Device)

Additional properties and methods for vehicles.

# Properties
vehicle.vin           # Optional[str]
vehicle.make          # Optional[str]
vehicle.model         # Optional[str]
vehicle.year          # Optional[int]
vehicle.license_plate # Optional[str]
vehicle.odometer      # Optional[float]

# Methods
await vehicle.start_engine()
await vehicle.stop_engine()
await vehicle.honk_horn()
await vehicle.flash_lights()

Data Models

from lojack_api import Location, DeviceInfo, VehicleInfo

# Location - Core fields
location.latitude   # Optional[float]
location.longitude  # Optional[float]
location.timestamp  # Optional[datetime]
location.accuracy   # Optional[float] - GPS accuracy in METERS (for HA gps_accuracy)
location.speed      # Optional[float]
location.heading    # Optional[float]
location.address    # Optional[str]

# Note on accuracy: The API may return HDOP values or quality strings.
# These are automatically converted to meters for Home Assistant compatibility:
# - HDOP values (1-15) are multiplied by 5 to get approximate meters
# - Quality strings ("GOOD", "POOR", etc.) are mapped to reasonable meter values
# - Values > 15 are assumed to already be in meters

# Location - Extended telemetry (from events)
location.odometer        # Optional[float] - Vehicle odometer reading
location.battery_voltage # Optional[float] - Battery voltage
location.engine_hours    # Optional[float] - Engine hours
location.distance_driven # Optional[float] - Total distance driven
location.signal_strength # Optional[float] - Signal strength (0.0 to 1.0)
location.gps_fix_quality # Optional[str] - GPS quality (e.g., "GOOD", "POOR")
location.event_type      # Optional[str] - Event type (e.g., "SLEEP_ENTER")
location.event_id        # Optional[str] - Unique event identifier

# Location - Raw data
location.raw        # Dict[str, Any] - Original API response

Exceptions

from lojack_api import (
    LoJackError,           # Base exception
    AuthenticationError,   # 401 errors, invalid credentials
    AuthorizationError,    # 403 errors, permission denied
    ApiError,              # Other API errors (has status_code)
    ConnectionError,       # Network connectivity issues
    TimeoutError,          # Request timeouts
    DeviceNotFoundError,   # Device not found (has device_id)
    CommandError,          # Command failed (has command, device_id)
    InvalidParameterError, # Invalid parameter (has parameter, value)
)

Spireon API Details

The library uses the Spireon LoJack API:

  • Identity Service: https://identity.spireon.com - For authentication
  • Services API: https://services.spireon.com/v0/rest - For device/asset management

Authentication uses HTTP Basic Auth with the following headers:

  • X-Nspire-Apptoken - Application token
  • X-Nspire-Correlationid - Unique request ID
  • X-Nspire-Usertoken - User token (after authentication)

Development

# Install dev dependencies
pip install .[dev]

# Run tests
pytest

# Run tests with coverage
pytest --cov=lojack_api

# Type checking
mypy lojack_api

# Linting
# Preferred: ruff for quick fixes
ruff check .

# Use flake8 for strict style checks (reports shown in CI)
# Match ruff's line length setting
flake8 lojack_api/ tests/ --count --show-source --statistics --max-line-length=100

License

MIT License - see LICENSE for details.

Contributing

Contributions are welcome! This library is designed to be vendored into Home Assistant integrations to avoid dependency conflicts.

Credits

This library was inspired by the original lojack-clients package and uses the Spireon LoJack API endpoints.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

lojack_api-0.6.3.tar.gz (30.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

lojack_api-0.6.3-py3-none-any.whl (23.4 kB view details)

Uploaded Python 3

File details

Details for the file lojack_api-0.6.3.tar.gz.

File metadata

  • Download URL: lojack_api-0.6.3.tar.gz
  • Upload date:
  • Size: 30.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for lojack_api-0.6.3.tar.gz
Algorithm Hash digest
SHA256 5530b887b56f5962c2295820a7b388fa7b0acae431bc9236357047ddca0461e2
MD5 ad8553b2598a50b3862002754b814418
BLAKE2b-256 5ddd24976305a844cda75e0d38ea8b7daf765f960b5a9a5f2726942469452dff

See more details on using hashes here.

Provenance

The following attestation bundles were made for lojack_api-0.6.3.tar.gz:

Publisher: publish.yml on devinslick/lojack_api

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lojack_api-0.6.3-py3-none-any.whl.

File metadata

  • Download URL: lojack_api-0.6.3-py3-none-any.whl
  • Upload date:
  • Size: 23.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for lojack_api-0.6.3-py3-none-any.whl
Algorithm Hash digest
SHA256 379e7bdec0cf8a970b277d0e02d3060c04b9c6f6e4e4a777f78aa7cf343092b2
MD5 e1a6aeaaabd5e5e7666dd2e7dcff65f7
BLAKE2b-256 6d3074d5b53f27f28e62db264bfc140ec9e24d93bed3dbdd5ab4115a5e99eefa

See more details on using hashes here.

Provenance

The following attestation bundles were made for lojack_api-0.6.3-py3-none-any.whl:

Publisher: publish.yml on devinslick/lojack_api

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page