Skip to content

Commit

Permalink
Add exception handling
Browse files Browse the repository at this point in the history
  • Loading branch information
jcjones committed Jun 6, 2024
1 parent 7ee69af commit 881ea48
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 19 deletions.
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "py-watersmart"
authors = [
{name = "J.C. Jones", email = "[email protected]"},
]
version = "0.1.1"
version = "0.1.2"
readme = "README.md"
description = "Obtain water usage data from Watersmart.com"
classifiers = [
Expand All @@ -14,7 +14,8 @@ classifiers = [
keywords = ["water meter"]
dependencies = [
"aiohttp",
"aiohttp-client-cache[sqlite]"
"aiohttp-client-cache[sqlite]",
"async_timeout"
]
requires-python = ">=3.11"

Expand Down
32 changes: 22 additions & 10 deletions src/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
import asyncio
import argparse
import logging
from watersmart import WatersmartClient
from watersmart import (
WatersmartClient,
WatersmartClientAuthenticationError,
WatersmartClientCommunicationError,
WatersmartClientError,
)


PARSER = argparse.ArgumentParser(description=__doc__)
Expand All @@ -24,15 +29,22 @@ async def main():
log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
logging.basicConfig(level=args.log_level, format=log_format)
wc = WatersmartClient(args.url, args.email, args.password)
data = await wc.usage()
for datapoint in sorted(data, key=lambda x: x["read_datetime"]):
parts = [
f"{datapoint['local_datetime']}",
f"usage: {datapoint['gallons']:8}gal",
f"leak: {datapoint['leak_gallons']:8}gal",
f"flags: {datapoint['flags']}",
]
print(" | ".join(parts))
try:
data = await wc.usage()
for datapoint in sorted(data, key=lambda x: x["read_datetime"]):
parts = [
f"{datapoint['local_datetime']}",
f"usage: {datapoint['gallons']:8}gal",
f"leak: {datapoint['leak_gallons']:8}gal",
f"flags: {datapoint['flags']}",
]
print(" | ".join(parts))
except WatersmartClientAuthenticationError:
logging.exception("Login failure")
except WatersmartClientCommunicationError:
logging.exception("Communications error")
except WatersmartClientError:
logging.exception("Unknown error")


def start():
Expand Down
48 changes: 41 additions & 7 deletions src/watersmart/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
"""Main WaterSmart module"""

import aiohttp
import asyncio
import async_timeout
import logging
import socket
import time

from aiohttp_client_cache import CachedSession, SQLiteBackend
from importlib.metadata import version


class WatersmartClientError(Exception):
"""Exception to indicate a general API error."""


class WatersmartClientCommunicationError(WatersmartClientError):
"""Exception to indicate a communication error."""


class WatersmartClientAuthenticationError(WatersmartClientError):
"""Exception to indicate an authentication error."""


class WatersmartClient:
def __init__(self, url, email, password):
self._url = url
Expand All @@ -30,11 +46,14 @@ def __init__(self, url, email, password):
async def _login(self):
url = f"{self._url}/index.php/welcome/login?forceEmail=1"
login = {"token": "", "email": self._email, "password": self._password}
await self._session.post(url, data=login)
result = await self._session.post(url, data=login)
logging.debug(result)

async def _populate_data(self):
url = f"{self._url}/index.php/rest/v1/Chart/RealTimeChart"
chart_rsp = await self._session.get(url)
if chart_rsp.status != 200:
raise WatersmartClientAuthenticationError()
data = await chart_rsp.json()
self._data_series = data["data"]["series"]

Expand All @@ -49,16 +68,31 @@ def _amend_with_local_ts(cls, datapoint):

async def usage(self):
if not self._data_series:
logging.debug("Loading watersmart data")
await self._login()
await self._populate_data()
await self._close()
try:
async with async_timeout.timeout(10):
logging.debug("Loading watersmart data from %s", self._url)
await self._login()
await self._populate_data()
except WatersmartClientAuthenticationError as e:
raise e
except asyncio.TimeoutError as exception:
raise WatersmartClientCommunicationError(
"Timeout error fetching information",
) from exception
except (aiohttp.ClientError, socket.gaierror) as exception:
raise WatersmartClientCommunicationError(
"Error fetching information",
) from exception
except Exception as exception: # pylint: disable=broad-except
raise WatersmartClientError(
"Something really wrong happened!"
) from exception
finally:
await self._close()

result = []

for datapoint in self._data_series:
result.append(WatersmartClient._amend_with_local_ts(datapoint))

return result

async def _close(self):
Expand Down

0 comments on commit 881ea48

Please sign in to comment.