pylovens
Python client for the Lovens API.
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 toTrue.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 ifend_dateis provided.end_date: End date of a timespan. Optional, must be provided ifstart_dateis provided.
Returns
A dictionary of the following form:
{
'entries_all_time': 1,
'entries_in_timespan': 0 # Only if start_date and end_date provided.
}
Metadata
Release files for pylovens 0.3.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pylovens-0.3.1.tar.gz | 17.3 kB | Without provenance | Details for pylovens-0.3.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pylovens-0.3.1-py3-none-any.whl | Python 3 | none | any | Details for pylovens-0.3.1-py3-none-any.whl |
Total release size: 29.1 kB
Release files / pylovens-0.3.1.tar.gz
| Download URL | pylovens-0.3.1.tar.gz |
|---|---|
| Size | 17.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
11de316154d2bfb2a23d831ea4a70e2c846b1c371337a3f0d697ab688964553a
|
|
BLAKE2b-256 checksum How to use checksums |
4df2126c284e682b45e9b533f31b69e278adbbab43c5a587cdc30c2b2624fe89
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.2 CPython/3.11.4
|
Release files / pylovens-0.3.1-py3-none-any.whl
| Download URL | pylovens-0.3.1-py3-none-any.whl |
|---|---|
| Size | 11.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b74f2785af7e00de0a3c06b481c7c28b1b45df08c4d16bf0d20252a5ed6ee7dd
|
|
BLAKE2b-256 checksum How to use checksums |
556369265aff4cdddb7d9546f59b9845e46e1eb2b57a7453178d2799ca00aa59
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/4.0.2 CPython/3.11.4
|
Security for pylovens 0.3.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.