Skip to main content

Python client for the Lovens API.

Project description

pylovens

Python client for the Lovens API.

GitHub Workflow Status PyPI PyPI - License PyPI - Downloads

With pylovens you can access the data that is available in the Lovens app for bikes with a GPS unit.

Usage

Using the client is as simple as:

from pylovens import LovensClient

client = LovensClient("your_username", "your_secret_password")

bikes = client.get_bikes()

Note: only authentication with email/password is supported.

Methods

The LovensClient exposes the following methods:

User

get_user - Get information on the user.

def get_user(self) -> dict
Returns

An extensive dictionary with details on the user, including the following keys and many more:

{
    "id": 1234,
    "name": "Your Name",
    "creation_date": datetime(2023, 4, 30, 23, 59, 59, tzinfo=ZoneInfo(key='Europe/Amsterdam'),
    "email": "your@mail.address",
    "timezone": "Europe/Amsterdam",
    ...
}

Bikes

get_bikes - Fetch all bikes accessible to your user.

def get_bikes(self) -> list[dict]
Returns

A list of dictionaries of the following form:

{
  'id': 456,
  'user_id': 1234,
  'user_name': 'Your Name',
  'active_state': 0,
  'name': 'Your Bikes Name',
  'last_location': { <most recent available result of the get_location method> },
  'battery_percentage': 50,
  'owning_user': { <result of get_user method> },
  'geofences': [ <result of get_geofences method> ],
  ...
}

get_bike - Fetch a bike by its ID.

def get_bike(self, bike_id: int) -> dict
Arguments
  • bike_id: The ID of the bike.
Returns

A dictionaries of the following form:

{
  'id': 456,
  'user_id': 1234,
  'user_name': 'Your Name',
  'active_state': 0,
  'name': 'Your Bikes Name',
  'last_location': { <most recent available result of the get_location method> },
  'battery_percentage': 50,
  'owning_user': { <result of get_user method> },
  'geofences': [ <result of get_geofences method> ],
  ...
}

get_state - Get the state of a bike.

def get_state(self, bike_id: int) -> dict[str]
Arguments
  • bike_id: The ID of the bike.
Returns

A dictionary of the following form:

{
  'powered_on': False,
  'ecu_locked': False,
  'erl_locked': False,
  'battery_percentage': 50,
  'charging': False,
  'last_full_charge': datetime(2023, 4, 1, 17, 10, 30, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
  'odometer': 300,
  'range': 30
}

get_health - Get bike health status.

def get_health(self, bike_id: int) -> list[dict]
Arguments
  • bike_id: The ID of the bike.
Returns

A list of four dictionaries:

[
  {
    'key': 'last_connection',
    'status': True,
    'value': datetime(2023, 4, 1, 17, 10, 30, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    'value_type': 'datetime'
  },
  {
    'key': 'last_gps',
    'status': False,
    'value': datetime(2023, 3, 31, 16, 51, 22, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    'value_type': 'datetime'
  },
  {
    'key': 'gps_battery',
    'status': True,
    'value': '75%',
    'value_type': 'string'
  },
  {
    'key': 'bike_system',
    'status': True,
    'value': 'true',
    'value_type': 'bool'
  }
]

Rides

iterate_rides - Iterate through the rides of a bike.

def iterate_rides(
        self, bike_id: int, newest_first: bool = True, batch_size: int = 50, _offset: int = 0
    ) -> Iterable[dict]
Arguments
  • bike_id: The ID of the bike.
  • newest_first: If True, fetch the most recent ride first. Defaults to True.
  • batch_size: The number of rides to fetch at once.
  • _offset: Used in pagination.
Returns

An iterable of dictionaries describing the rides. Each dictionary contains, among others, the following keys:

{
    "id": 123456,
    "start_date": datetime(2023, 4, 1, 17, 1, 0, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    "end_date": datetime(2023, 4, 1, 17, 6, 30, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    "calories": 14,
    "avg_speed": 21,
    "distance_traveled": 1234,
    "bike_id": 123,
    "user_id": 1234,
    "user": { <same as output of get_user()> },
    "creation_date": datetime(2023, 4, 1, 17, 10, 30, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    "active_time": 330,
    "timezone": "Europe/Amsterdam",
    ...
}

get_rides - Fetch a list of rides of a bike.

def get_rides(self, bike_id: int, newest_first: bool = True, n: int = 50) -> list[dict]

If you are interested in fetching all rides of a bike, or are not sure how many you need, consider using iterate_rides.

Arguments
  • bike_id: The ID of the bike.
  • newest_first: If True, fetch the most recent ride first. Defaults to True.
  • n: Number of rides to fetch. Defaults to 50.
Returns

An list of dictionaries describing the rides. Each dictionary contains, among others, the following keys:

{
    "id": 123456,
    "start_date": datetime(2023, 4, 1, 17, 1, 0, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    "end_date": datetime(2023, 4, 1, 17, 6, 30, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    "calories": 14,
    "avg_speed": 21,
    "distance_traveled": 1234,
    "bike_id": 123,
    "user_id": 1234,
    "user": { <same as output of get_user()> },
    "creation_date": datetime(2023, 4, 1, 17, 10, 30, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    "active_time": 330,
    "timezone": "Europe/Amsterdam",
    ...
}

get_ride - Fetch a ride by its ID.

def get_ride(self, ride_id: int) -> dict
Arguments
  • ride_id: The ID of the ride.
Returns

A dictionary describing the ride. It contains, among others, the following keys:

{
    "id": 123456,
    "start_date": datetime(2023, 4, 1, 17, 1, 0, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    "end_date": datetime(2023, 4, 1, 17, 6, 30, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    "calories": 14,
    "avg_speed": 21,
    "distance_traveled": 1234,
    "bike_id": 123,
    "user_id": 1234,
    "user": { <same as output of get_user()> },
    "creation_date": datetime(2023, 4, 1, 17, 10, 30, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    "active_time": 330,
    "timezone": "Europe/Amsterdam",
    ...
}

get_location - Get location history in a time range.

def get_location(
        self, bike_id: int, start_date: datetime | date, end_date: datetime | date
    ) -> list[dict, bool, datetime | int | float]

If start_date and/or end_date is a date object, they are interpreted as the start and end of the day respectively. Timezone-naive datetime objects are converted to the user's timezone (see get_user).

Arguments
  • bike_id: The ID of the bike.
  • start_date: Start date of a timespan.
  • end_date: End date of a timespan.
Returns

A list of dictionaries of the following form:

{
    "id": "123456b123",
    'lat': 52.379189,
    'lon': 4.899431,
    "date": datetime(2023, 4, 1, 17, 1, 0, tzinfo=ZoneInfo(key='Europe/Amsterdam')),
    "speed": 21,
    "battery_percentage": 0,  # Always 0 - never filled
    "bike_id": 123,
    "is_moving": True
}

Battery

get_battery_state - Get the state of the battery of a bike.

def get_battery_state(self, bike_id: int) -> dict[str]
Arguments
  • bike_id: The ID of the bike.
Returns

A dictionary of the following form:

{
  'current': -450,
  'battery_percentage': 69,
  'last_battery_update': datetime(2023, 4, 30, 0, 0, 0, tzinfo=ZoneInfo(key='Europe/Amsterdam'),
  'last_full_charge': datetime(2023, 4, 30, 0, 0, 0, tzinfo=ZoneInfo(key='Europe/Amsterdam'),
  'charging': False,
  'range': 30
}

get_battery_statistics - Get historical state of the battery of a bike.

def get_battery_statistics(
        self,
        bike_id: int,
        start_date: date | datetime | None = None,
        end_date: date | datetime | None = None,
    )

If start_date is not provided or None, it defaults to 24 hours ago. If end_date is not provided or None, it defaults to now.

If start_date and/or end_date is a date object, they are interpreted as the start and end of the day respectively. Timezone-naive datetime objects are converted to the user's timezone (see get_user).

Arguments
  • bike_id: The ID of the bike.
  • start_date: Optional start date or datetime.
  • end_date: Optional end date or datetime (inclusive).
Returns

A list of dictionaries of the following form, in 15-minute intervals:

  {
    'date': datetime(2023, 4, 30, 0, 0, 0, tzinfo=ZoneInfo(key='Europe/Amsterdam'),
    'battery_percentage': 69,
    'charging': False,
    'seconds_in_ride': 0,
    'seconds_charging': 0,
  }

Note that battery_percentage is None when the battery is removed.

Statistics

get_statistics - Get ride statistics for a bike.

def get_statistics(
        self,
        bike_id: int,
        start_date: date | datetime,
        end_date: date | datetime,
        type: str = "daily",
    ) -> list[dict]

If start_date and/or end_date is a date object, they are interpreted as the start and end of the day respectively. Timezone-naive datetime objects are converted to the user's timezone (see get_user).

Note that this endpoint expects the end_date, if it is a datetime, to be one second before the start of the next bin. E.g. daily statistics for the month of January 2023 can be obtained by calling get_statistics(123, start_date=datetime(2023, 1, 1, 0, 0), end_date=datetime(2023, 1, 31, 23, 59, 59)). Passing datetime(2023, 2, 1, 0, 0) as end_date instead will return in an extra 0-second bin at the end.

Arguments
  • bike_id: The ID of the bike.
  • start_date: Start date or datetime.
  • end_date: End date or datetime (inclusive).
  • type: Aggregation level. One of "hourly", "daily" or "monthly". Defaults to "daily".
Returns

A list of dictionaries of the following form:

{
  'from': datetime(2023, 4, 30, 0, 0, 0, tzinfo=ZoneInfo(key='Europe/Amsterdam'),
  'till': datetime(2023, 4, 30, 23, 59, 59, tzinfo=ZoneInfo(key='Europe/Amsterdam'),
  'co2': 2584,
  'calories': 156,
  'avg_speed': 18,
  'distance_traveled': 10379,
  'avg_power_distribution': 83,
  'shift_advice': 0,
  'top_speed': 28,
  'elevation_up': 238,
  'elevation_down': 232
}

Geofences

get_geofences - Fetch all geofences associated to a bike.

def get_geofences(self, bike_id: int) -> list[dict[str, datetime | dict[str, float] | int | str]]
Arguments
  • bike_id: The ID of the bike.
Returns

A list of dictionaries of the following form:

{
  'id': 456,
  'bike_id': 123,
  'user_id': 1234,
  'name': 'Amsterdam City Center',
  'center': {'lat': 52.379189, 'lon': 4.899431},
  'radius': 200,
  'active_state': 0,
  'creation_date': datetime(2023, 4, 1, 17, 10, 30, tzinfo=ZoneInfo(key='Europe/Amsterdam'))
}

get_geofence - Get a single geofence by its ID.

def get_geofence(self, geofence_id: int) -> dict[str, datetime | dict[str, float] | int | str]
Arguments
  • geofence_id: The ID of the geofence.
Returns

A dictionary of the following form:

{
  'id': 456,
  'bike_id': 123,
  'user_id': 1234,
  'name': 'Amsterdam City Center',
  'center': {'lat': 52.379189, 'lon': 4.899431},
  'radius': 200,
  'active_state': 0,
  'creation_date': datetime(2023, 4, 1, 17, 10, 30, tzinfo=ZoneInfo(key='Europe/Amsterdam'))
}

get_geofence_stats - Get statistics of a geofence.

def get_geofence_stats(
        self,
        geofence_id: int,
        start_date: datetime | date | None = None,
        end_date: datetime | date | None = None,
    ) -> dict[str, int]
Arguments
  • geofence_id: The ID of the geofence.
  • start_date: Start date of a timespan. Optional, must be provided if end_date is provided.
  • end_date: End date of a timespan. Optional, must be provided if start_date is provided.
Returns

A dictionary of the following form:

{
  'entries_all_time': 1,
  'entries_in_timespan': 0  # Only if start_date and end_date provided.
}

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

pylovens-0.3.1.tar.gz (17.3 kB view details)

Uploaded Source

Built Distribution

pylovens-0.3.1-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

File details

Details for the file pylovens-0.3.1.tar.gz.

File metadata

  • Download URL: pylovens-0.3.1.tar.gz
  • Upload date:
  • Size: 17.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.4

File hashes

Hashes for pylovens-0.3.1.tar.gz
Algorithm Hash digest
SHA256 11de316154d2bfb2a23d831ea4a70e2c846b1c371337a3f0d697ab688964553a
MD5 46ea15eb3d83aadd7457cfed254ef060
BLAKE2b-256 4df2126c284e682b45e9b533f31b69e278adbbab43c5a587cdc30c2b2624fe89

See more details on using hashes here.

File details

Details for the file pylovens-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: pylovens-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 11.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.4

File hashes

Hashes for pylovens-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b74f2785af7e00de0a3c06b481c7c28b1b45df08c4d16bf0d20252a5ed6ee7dd
MD5 b960e1d4e35322fc4c58f3ee1a1a13f3
BLAKE2b-256 556369265aff4cdddb7d9546f59b9845e46e1eb2b57a7453178d2799ca00aa59

See more details on using hashes here.

Supported by

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