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.0.tar.gz (16.6 kB view details)

Uploaded Source

Built Distribution

pylovens-0.3.0-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for pylovens-0.3.0.tar.gz
Algorithm Hash digest
SHA256 a983db6e40678ac2a7d7cb839a803fb51dcdc4ef0f2e7d3611d5c9873a914348
MD5 2f39ac4bc2c6f25f5ef157d55b4ae3e7
BLAKE2b-256 da06b5282e3b8682fb7012d8c9962e0ad05495bcba30b352d3d1c481ccd10ce3

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for pylovens-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9ca2b72603ec17c9f240076e7b1a69f08e0086082838bfc5e6f14316bd63db92
MD5 591b9ca1e8016c291631b3b50a8645c0
BLAKE2b-256 d3468773212f3c4904657cdc1417e82801ae3e42e58f89e7bb12ca48be5f70a5

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