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

RYM Pro full functional client #1

Open
wants to merge 1 commit into
base: migration
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
__pycache__/
\.idea/
*.pyc

build/
Expand Down
36 changes: 36 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
repos:
- repo: https://github.com/asottile/pyupgrade
rev: v3.2.2
hooks:
- id: pyupgrade
args: [ --py310 ]

- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black
args:
- --safe
- --quiet
files: ^((script|tests)/.+)?[^/]+\.py$

- repo: https://github.com/codespell-project/codespell
rev: v2.2.2
hooks:
- id: codespell
args:
- --skip="./.*,*.csv,*.json"
- --quiet-level=2
exclude_types: [csv, json]
exclude: ^tests/fixtures/

- repo: https://github.com/pycqa/flake8
rev: 5.0.4
hooks:
- id: flake8
files: ^(script|tests)/.+\.py$

- repo: https://github.com/PyCQA/isort
rev: 5.10.1
hooks:
- id: isort
4 changes: 4 additions & 0 deletions Contributors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Contributors
- [Maor] (https://github.com/maorcc)
- [On Freund] (https://github.com/OnFreund)
- [Elad Bar] (https://github.com/elad-bar)
78 changes: 68 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# pyrympro

A python library to communitcate with [Read Your Meter Pro](https://rym-pro.com/).
A python library to communicate with [Read Your Meter Pro](https://rym-pro.com/).

## Installation

Expand All @@ -13,15 +13,73 @@ Python 3.7 and above are supported.

## How to use

### Initialize

**With predefined client session**

```python
from pyrympro import RymPro
rym = RymPro()
# you can also pass in your own session
import aiohttp
from pyrympro.rympro import RymPro

session = aiohttp.ClientSession()
rym = RymPro(session)
```

**Let client generate session**
```python
from pyrympro.rympro import RymPro

rym = RymPro()
```

### Initialize client and login
```python
from pyrympro.rympro import RymPro

rym = RymPro()
await rym.initialize("<username>", "<password>")
```

### Get the latest details
```python
from pyrympro.rympro import RymPro

rym = RymPro()
await rym.initialize("<username>", "<password>")

await rym.update()

print(f"profile: {rym.profile}")
print(f"meters: {rym.meters}")
print(f"customer_service: {rym.customer_service}")
print(f"settings: {rym.settings}")
```

### Set alerts for leaks in all channels
```python
from pyrympro.helpers.enums import MediaTypes, AlertTypes
from pyrympro.rympro import RymPro

rym = RymPro()
await rym.initialize("<username>", "<password>")

await rym.update()

print(f"settings: {rym.settings}")

await rym.set_alert_settings(AlertTypes.LEAK, MediaTypes.ALL, True)

print(f"settings: {rym.settings}")
```

**Channels**

- None (MediaTypes.NONE)
- Email (MediaTypes.EMAIL)
- SMS (MediaTypes.SMS)
- All (MediaTypes.ALL)

# device_id can be anything you choose
await rym.login("<email>", "<password>", "<device_id>")
info = await rym.account_info()
meter_reads = await rym.last_read()
...
```
**Alert Types**
- Daily exception (AlertTypes.DAILY_EXCEPTION)
- Leak (AlertTypes.LEAK)
- Consumption identified will away or vacation (AlertTypes.CONSUMPTION_WHILE_AWAY)
Empty file added __init__.py
Empty file.
1 change: 0 additions & 1 deletion pyrympro/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
from .rympro import CannotConnectError, OperationError, UnauthorizedError, RymPro
14 changes: 0 additions & 14 deletions pyrympro/const.py

This file was deleted.

Empty file added pyrympro/helpers/__init__.py
Empty file.
137 changes: 137 additions & 0 deletions pyrympro/helpers/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
BASE_URL_OLD = "https://api.city-mind.com"
BASE_URL_NEW = "https://api-ctm.city-mind.com"

CONSUMER_URL_OLD = f"{BASE_URL_OLD}/consumer"
CONSUMER_URL_NEW = f"{BASE_URL_NEW}/consumer"

CONSUMPTION_URL = f"{BASE_URL_NEW}/consumption"

API_HEADER_TOKEN = "x-access-token"

API_URL_OLD = "https://api.city-mind.com"
API_URL_NEW = "https://api-ctm.city-mind.com"

CITY_MIND_WEBSITE = "https://rym-pro.com"

ENDPOINT_PARAMETER_METER_ID = "meter_id"
ENDPOINT_PARAMETER_YESTERDAY = "yesterday"
ENDPOINT_PARAMETER_TODAY = "today"
ENDPOINT_PARAMETER_LAST_DAY_MONTH = "last_day_month"
ENDPOINT_PARAMETER_MUNICIPALITY_ID = "municipality_id"
ENDPOINT_PARAMETER_CURRENT_MONTH = "current_month"
ENDPOINT_PARAMETER_ALERT_TYPE = "alert_type"

ENDPOINT_CONSUMER_OLD = f"{API_URL_OLD}/consumer"
ENDPOINT_CONSUMER_NEW = f"{API_URL_NEW}/consumer"

ENDPOINT_CONSUMPTION_NEW = f"{API_URL_NEW}/consumption"

ENDPOINT_MUNICIPALS_OLD = f"{API_URL_OLD}/municipals"
ENDPOINT_MUNICIPALS_NEW = f"{API_URL_NEW}/municipality"

ENDPOINT_LOGIN = f"{ENDPOINT_CONSUMER_OLD}/login"

ENDPOINT_ME = f"{ENDPOINT_CONSUMER_OLD}/me"
ENDPOINT_METERS = f"{ENDPOINT_CONSUMER_NEW}/meters"
ENDPOINT_UNITS = f"{ENDPOINT_MUNICIPALS_OLD}/h1/measurmentunits"
ENDPOINT_CUSTOMER_SERVICE = f"{ENDPOINT_MUNICIPALS_OLD}/municipalCustomerService"

ENDPOINT_ALERTS_FOR_SETTINGS = f"{ENDPOINT_CONSUMER_NEW}/alertsForSettings"

ENDPOINT_LAST_READ = f"{ENDPOINT_CONSUMPTION_NEW}/last-read"
ENDPOINT_CONSUMPTION_DAILY = f"{ENDPOINT_CONSUMPTION_NEW}/daily/lastbillingCycle/{{meter_id}}/{{yesterday}}/{{today}}"
ENDPOINT_CONSUMPTION_MONTHLY = f"{ENDPOINT_CONSUMPTION_NEW}/monthly/{{meter_id}}/{{current_month}}/{{last_day_month}}"
ENDPOINT_VACATIONS = f"{ENDPOINT_CONSUMER_OLD}/vacations"
ENDPOINT_MY_ALERTS = f"{ENDPOINT_CONSUMER_NEW}/myalerts"
ENDPOINT_MY_ALERTS_SETTINGS = f"{ENDPOINT_CONSUMER_OLD}/myalerts/settings"
ENDPOINT_MY_MESSAGES = f"{ENDPOINT_MUNICIPALS_NEW}/{{municipality_id}}/messages"
ENDPOINT_MY_MESSAGE_SUBJECTS = f"{ENDPOINT_MY_MESSAGES}/message-subjects"
ENDPOINT_CONSUMPTION_LOW_RATE_LIMIT = f"{ENDPOINT_CONSUMPTION_NEW}/Low-Rate-Limit"
ENDPOINT_CONSUMPTION_FORECAST = f"{ENDPOINT_CONSUMPTION_NEW}/forecast/{{meter_id}}"
ENDPOINT_MY_ALERTS_SETTINGS_UPDATE = f"{ENDPOINT_MY_ALERTS}/settings/{{alert_type}}"

API_DATA_TOKEN = "token"
API_DATA_ERROR_CODE = "code"
API_DATA_ERROR_REASON = "error"

API_DATA_SECTION_ME = "me"
API_DATA_SECTION_METERS = "meters"
# API_DATA_SECTION_UNITS = "units"
API_DATA_SECTION_CUSTOMER_SERVICE = "customer-service"
# API_DATA_SECTION_MY_MESSAGE_SUBJECTS = "my-message-subjects"
# API_DATA_SECTION_ALERTS_FOR_SETTINGS = "alerts-for-settings"
API_DATA_SECTION_LAST_READ = "last-read"
# API_DATA_SECTION_CONSUMPTION_LOW_RATE_LIMIT = "consumption-low-rate-limit"
API_DATA_SECTION_VACATIONS = "vacations"
API_DATA_SECTION_MY_ALERTS = "my-alerts"
API_DATA_SECTION_MY_MESSAGES = "my-messages"
API_DATA_SECTION_CONSUMPTION_DAILY = "consumption-daily"
API_DATA_SECTION_CONSUMPTION_MONTHLY = "consumption-monthly"
API_DATA_SECTION_CONSUMPTION_FORECAST = "consumption-forecast"
API_DATA_SECTION_SETTINGS = "settings"

CUSTOMER_SERVICE_PHONE_NUMBER = "phoneNumber"
CUSTOMER_SERVICE_DESCRIPTION = "description"
CUSTOMER_SERVICE_PHONE_MUNICIPAL_ID = "municipalID"
CUSTOMER_SERVICE_EMAIL = "Email"

METER_COUNT = "meterCount"
METER_SERIAL_NUMBER = "meterSn"
METER_FULL_ADDRESS = "fullAddress"

LAST_READ_METER_COUNT = METER_COUNT
LAST_READ_METER_ID = "meterId"
LAST_READ_VALUE = "read"

ME_FIRST_NAME = "firstName"
ME_LAST_NAME = "lastName"
ME_ACCOUNT_NUMBER = "accountNumber"
ME_PHONE_NUMBER_SECTION = "phoneNumber"
ME_COUNTRY_CODE = "countryCode"
ME_PHONE_NUMBER = "phoneNumber"
ME_ADDITIONAL_PHONE_NUMBER = "AdditionalPhoneNumber"
ME_MUNICIPAL_ID = "municipalId"

CONSUMPTION_METER_COUNT = METER_COUNT
CONSUMPTION_VALUE = "cons"
CONSUMPTION_DATE = "consDate"
CONSUMPTION_ESTIMATION_TYPE = "estimationType"
CONSUMPTION_METER_STATUS_DESC = "meterStatusDesc"
CONSUMPTION_COMMON_DATA = "commonCons"

CONSUMPTION_FORECAST_ESTIMATED_CONSUMPTION = "estimatedConsumption"

SETTINGS_ALERT_TYPE_ID = "alertTypeId"
SETTINGS_MEDIA_TYPE_ID = "mediaTypeId"

ENDPOINT_DATA_INITIALIZE = {
API_DATA_SECTION_ME: ENDPOINT_ME,
API_DATA_SECTION_METERS: ENDPOINT_METERS,
API_DATA_SECTION_CUSTOMER_SERVICE: ENDPOINT_CUSTOMER_SERVICE,
}

ENDPOINT_DATA_UPDATE = {
# API_DATA_SECTION_VACATIONS: ENDPOINT_VACATIONS,
# API_DATA_SECTION_MY_ALERTS: ENDPOINT_MY_ALERTS,
# API_DATA_SECTION_MY_MESSAGES: ENDPOINT_MY_MESSAGES,
API_DATA_SECTION_SETTINGS: ENDPOINT_MY_ALERTS_SETTINGS
}

ENDPOINT_DATA_UPDATE_PER_METER = {
API_DATA_SECTION_LAST_READ: ENDPOINT_LAST_READ,
API_DATA_SECTION_CONSUMPTION_DAILY: ENDPOINT_CONSUMPTION_DAILY,
API_DATA_SECTION_CONSUMPTION_MONTHLY: ENDPOINT_CONSUMPTION_MONTHLY,
API_DATA_SECTION_CONSUMPTION_FORECAST: ENDPOINT_CONSUMPTION_FORECAST,
}

ENDPOINT_DATA_RELOAD = {API_DATA_SECTION_SETTINGS: ENDPOINT_MY_ALERTS_SETTINGS}


LOGIN_EMAIL = "email"
LOGIN_PASSWORD = "pw"
LOGIN_DEVICE_ID = "deviceId"

DEFAULT_DEVICE_ID = "test"

FORMAT_DATE_ISO = "%Y-%m-%d"
FORMAT_DATE_YEAR_MONTH = "%Y-%m"
14 changes: 14 additions & 0 deletions pyrympro/helpers/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from enum import Enum, IntEnum


class MediaTypes(Enum):
NONE = "0"
EMAIL = "3"
SMS = "1"
ALL = "4"


class AlertTypes(IntEnum):
DAILY_EXCEPTION = 12
LEAK = 23
CONSUMPTION_WHILE_AWAY = 1001
10 changes: 10 additions & 0 deletions pyrympro/helpers/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CannotConnectError(Exception):
"""Exception to indicate an error in connection."""


class UnauthorizedError(Exception):
"""Exception to indicate an error in authorization."""


class OperationError(Exception):
"""Exception to indicate an error in operation."""
16 changes: 16 additions & 0 deletions pyrympro/models/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import json


class BaseObject:
_data: dict | None

def __init__(self, data: dict):
self._data = data

def as_dict(self) -> dict:
return self._data

def __repr__(self):
json_data = json.dumps(self.as_dict(), indent=4)

return json_data
54 changes: 54 additions & 0 deletions pyrympro/models/consumption.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from datetime import datetime

from ..helpers.const import *
from .base import BaseObject


class Consumption(BaseObject):
def __init__(self, data: dict):
super().__init__(data)

@property
def meter_count(self) -> int:
return self._data.get(CONSUMPTION_METER_COUNT)

@property
def date(self) -> datetime:
date_iso = self._data.get(CONSUMPTION_DATE)
date = datetime.fromisoformat(date_iso)

return date

@property
def consumption(self) -> str:
return self._data.get(CONSUMPTION_VALUE)

@property
def estimation_type(self) -> str:
return self._data.get(CONSUMPTION_ESTIMATION_TYPE)

@property
def common_consumption(self) -> str:
return self._data.get(CONSUMPTION_COMMON_DATA)

@property
def status_description(self) -> str:
return self._data.get(CONSUMPTION_METER_STATUS_DESC)

@staticmethod
def load(data: dict):
consumption = Consumption(data)

return consumption

def as_dict(self) -> dict:
data = {
"meter_count": self.meter_count,
"date": self.date.isoformat(),
"consumption": self.consumption,
"estimation_type": self.estimation_type,
"common_consumption": self.common_consumption,
"status_description": self.status_description,
}

return data
Loading