Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More types #5

Merged
merged 4 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
PULSE_ECO_USERNAME=
PULSE_ECO_PASSWORD=
PULSE_ECO_SKOPJE_USERNAME=
PULSE_ECO_SKOPJE_PASSWORD=
3 changes: 0 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ jobs:
run: hatch run lint

- name: Run tests
env:
PULSE_ECO_USERNAME: ${{ secrets.PULSE_ECO_USERNAME }}
PULSE_ECO_PASSWORD: ${{ secrets.PULSE_ECO_PASSWORD }}
run: hatch run cov

- name: Upload coverage reports to Codecov
Expand Down
34 changes: 34 additions & 0 deletions mkdocs/environment-variables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Environment variables

## Base URL format

Environment variable: `PULSE_ECO_BASE_URL_FORMAT`

The default base URL format is `https://{city_name}.pulse.eco/rest/{end_point}`.

## Authentication

Authentication is not required for fetching data. But if provided, it has to be valid for the city.

Credentials can also be provided as environment variables. To provide credentials for a city, use the following format:

```txt
PULSE_ECO_{city_name}_USERNAME
PULSE_ECO_{city_name}_PASSWORD
```

Example environmtent variables in priority order:

```txt
PULSE_ECO_SKOPJE_USERNAME
PULSE_ECO_SKOPJE_PASSWORD

PULSE_ECO_skopje_USERNAME
PULSE_ECO_skopje_PASSWORD

PULSE_ECO_USERNAME
PULSE_ECO_PASSWORD
```

Only use the generic `PULSE_ECO_USERNAME` and `PULSE_ECO_PASSWORD` environment variables
if your application requests data from a single city.
2 changes: 2 additions & 0 deletions mkdocs/example-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Initialize client

Authentication is not required for fetching data. But if provided, it has to be valid. Authentication is per city.

```python
from pulseeco import PulseEcoClient

Expand Down
69 changes: 56 additions & 13 deletions pulseeco/api/pulse_eco_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@

import requests

from pulseeco.constants import AVG_DATA_MAX_SPAN, DATA_RAW_MAX_SPAN, PULSE_ECO_BASE_URL
from pulseeco.constants import (
AVG_DATA_MAX_SPAN,
DATA_RAW_MAX_SPAN,
PULSE_ECO_BASE_URL_FORMAT,
PULSE_ECO_BASE_URL_FORMAT_ENV_KEY,
PULSE_ECO_CITY_PASSWORD_ENV_KEY_FORMAT,
PULSE_ECO_CITY_USERNAME_ENV_KEY_FORMAT,
PULSE_ECO_PASSWORD_ENV_KEY,
PULSE_ECO_USERNAME_ENV_KEY,
)
from pulseeco.utils import convert_datetime_to_str, split_datetime_span

from .base import PulseEcoAPIBase
Expand All @@ -16,14 +25,55 @@
import datetime


def get_auth_from_env(city_name: str) -> tuple[str, str] | None:
"""Get the auth tuple from the environment variables.

:param city_name: the city name
:return: a tuple of (email, password) or None
"""
city_upper_username_env_key = PULSE_ECO_CITY_USERNAME_ENV_KEY_FORMAT.format(
city_name=city_name.upper()
)
city_upper_password_env_key = PULSE_ECO_CITY_PASSWORD_ENV_KEY_FORMAT.format(
city_name=city_name.upper()
)

city_username_env_key = PULSE_ECO_CITY_USERNAME_ENV_KEY_FORMAT.format(
city_name=city_name
)
city_password_env_key = PULSE_ECO_CITY_PASSWORD_ENV_KEY_FORMAT.format(
city_name=city_name
)

for username_env_key in (
city_upper_username_env_key,
city_username_env_key,
PULSE_ECO_USERNAME_ENV_KEY,
):
if username_env_key in os.environ:
username = os.environ[username_env_key]
break
else:
return None

for password_env_key in (
city_upper_password_env_key,
city_password_env_key,
PULSE_ECO_PASSWORD_ENV_KEY,
):
if password_env_key in os.environ:
return username, os.environ[password_env_key]
return None

Check warning on line 66 in pulseeco/api/pulse_eco_api.py

View check run for this annotation

Codecov / codecov/patch

pulseeco/api/pulse_eco_api.py#L66

Added line #L66 was not covered by tests


class PulseEcoAPI(PulseEcoAPIBase):
"""Low level unsafe pulse.eco API wrapper."""

def __init__(
self,
city_name: str,
auth: tuple[str, str] | None = None,
base_url: str = PULSE_ECO_BASE_URL,
base_url: str = PULSE_ECO_BASE_URL_FORMAT,
session: requests.Session | None = None,
) -> None:
"""Initialize the pulse.eco API wrapper.
Expand All @@ -37,23 +87,16 @@
"""
self.city_name = city_name

if base_url is None and "PULSE_ECO_BASE_URL" in os.environ:
base_url = os.environ["PULSE_ECO_BASE_URL"]
if base_url is not None and PULSE_ECO_BASE_URL_FORMAT_ENV_KEY in os.environ:
base_url = os.environ[PULSE_ECO_BASE_URL_FORMAT_ENV_KEY]

if session is not None:
self._session = session
else:
self._session = requests.Session()

if (
auth is None
and "PULSE_ECO_USERNAME" in os.environ
and "PULSE_ECO_PASSWORD" in os.environ
):
auth = (
os.environ["PULSE_ECO_USERNAME"],
os.environ["PULSE_ECO_PASSWORD"],
)
if auth is None:
auth = get_auth_from_env(city_name=city_name)

if auth is not None:
self._session.auth = auth
Expand Down
4 changes: 2 additions & 2 deletions pulseeco/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import TYPE_CHECKING

from .api import PulseEcoAPI
from .constants import PULSE_ECO_BASE_URL
from .constants import PULSE_ECO_BASE_URL_FORMAT
from .models import DataValue, Overall, Sensor

if TYPE_CHECKING:
Expand All @@ -22,7 +22,7 @@ def __init__(
self,
city_name: str,
auth: tuple[str, str] | None = None,
base_url: str = PULSE_ECO_BASE_URL,
base_url: str = PULSE_ECO_BASE_URL_FORMAT,
session: requests.Session | None = None,
pulse_eco_api: PulseEcoAPIBase | None = None,
) -> None:
Expand Down
8 changes: 7 additions & 1 deletion pulseeco/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import datetime

PULSE_ECO_BASE_URL = "https://{city_name}.pulse.eco/rest/{end_point}"
PULSE_ECO_BASE_URL_FORMAT_ENV_KEY = "PULSE_ECO_BASE_URL_FORMAT"
PULSE_ECO_USERNAME_ENV_KEY = "PULSE_ECO_USERNAME"
PULSE_ECO_PASSWORD_ENV_KEY = "PULSE_ECO_PASSWORD" # noqa: S105
PULSE_ECO_CITY_USERNAME_ENV_KEY_FORMAT = "PULSE_ECO_{city_name}_USERNAME"
PULSE_ECO_CITY_PASSWORD_ENV_KEY_FORMAT = "PULSE_ECO_{city_name}_PASSWORD" # noqa: S105

PULSE_ECO_BASE_URL_FORMAT = "https://{city_name}.pulse.eco/rest/{end_point}"
DATA_RAW_MAX_SPAN = datetime.timedelta(days=7)
AVG_DATA_MAX_SPAN = datetime.timedelta(days=365)
12 changes: 12 additions & 0 deletions pulseeco/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ def __repr__(self) -> str:


class SensorType(StrEnum):
# unknown type
TYPE_NEG_1 = "-1"
# MOEPP measurement station
TYPE_0 = "0"
# SkopjePulse LoRaWAN based sensor, version 1
Expand All @@ -30,6 +32,10 @@ class SensorType(StrEnum):
TYPE_20003 = "20003"
# sensor.community crowdsourced device
TYPE_20004 = "20004"
# unknown type
TYPE_20005 = "20005"
# unknown type
TYPE_20006 = "20006"


class SensorStatus(StrEnum):
Expand All @@ -51,11 +57,17 @@ class SensorStatus(StrEnum):

class DataValueType(StrEnum):
NO2 = "no2"
NO2_PPB = "no2_ppb"
O3 = "o3"
SO2 = "so2"
CO = "co"
CO_PPB = "co_ppb"
NH3 = "nh3"
NH3_PPM = "nh3_ppm"
NH3_PPB = "nh3_ppb"
PM25 = "pm25"
PM10 = "pm10"
PM1 = "pm1"
TEMPERATURE = "temperature"
HUMIDITY = "humidity"
PRESSURE = "pressure"
Expand Down
6 changes: 6 additions & 0 deletions pulseeco/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,17 @@ class OverallValues(BaseModel):
model_config = ConfigDict(extra="allow")

no2: OverallValue = None
no2_ppb: OverallValue = None
o3: OverallValue = None
so2: OverallValue = None
co: OverallValue = None
co_ppb: OverallValue = None
nh3: OverallValue = None
nh3_ppm: OverallValue = None
nh3_ppb: OverallValue = None
pm25: OverallValue = None
pm10: OverallValue = None
pm1: OverallValue = None
temperature: OverallValue = None
humidity: OverallValue = None
pressure: OverallValue = None
Expand Down
Loading