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 hashes)

Uploaded Source

Built Distribution

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

Uploaded Python 3

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