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

Adds ability to pass exception handlers to websocket #52

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5c02a36
test: remove a lot of unnecessary checks
BeatsuDev Sep 7, 2024
c90297b
test: add timeouts for realtime tests
BeatsuDev Sep 7, 2024
7a0de65
Added on_exception argument to start_live_feed()
BeatsuDev Feb 8, 2023
98b302b
Removed backoff from home.py
BeatsuDev Feb 11, 2023
c507a31
Removed retry interval (this is set automatically with backoff)
BeatsuDev Feb 11, 2023
5625057
Removed ping interval and ping timeout
BeatsuDev Feb 11, 2023
79c7d53
Set default retries value to 5
BeatsuDev Feb 11, 2023
50eea94
Removed while loop
BeatsuDev Feb 11, 2023
2660216
Added on_connect and on_query exception handler parameters
BeatsuDev Feb 11, 2023
2d2ea81
Added a section about error handling to the README
BeatsuDev Feb 12, 2023
e0dd44f
Implemented starts of on_connection_error handler
BeatsuDev Feb 12, 2023
dd968d6
Added connection_retries and query_retries
BeatsuDev Feb 23, 2023
9e92023
Changed asyncio.sleep to time.sleep
BeatsuDev Feb 23, 2023
c5968a2
Moved 100 value into min function
BeatsuDev Feb 23, 2023
b7d88d8
Added a running property to home and set it to False when exit condit…
BeatsuDev Feb 23, 2023
607ddac
Added logging and removed throwing exception if no error handler is d…
BeatsuDev Feb 23, 2023
ba0cd41
Show the exception type in logger warning
BeatsuDev Feb 23, 2023
8642069
Reset the query retry counter
BeatsuDev Feb 23, 2023
41c9ca6
Added space after exception name
BeatsuDev Feb 23, 2023
ee1761b
Fixed logger to output when QUERY will retry
BeatsuDev Feb 23, 2023
10bdf3e
Retry connection if query fails
BeatsuDev Feb 23, 2023
d6fdd0e
Make error messages how correct retries made
BeatsuDev Feb 23, 2023
494b622
Added KeyboardInterrupt catch
BeatsuDev Feb 23, 2023
9e653a7
Added break statement to keyboardinterrupt catch
BeatsuDev Feb 23, 2023
48d0aee
test: remove a lot of unnecessary checks
BeatsuDev Sep 5, 2024
e19842f
chore: fix format errors
BeatsuDev Sep 5, 2024
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
60 changes: 54 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
> Note that this is NOT [pyTibber](https://github.com/Danielhiversen/pyTibber) which is the official python wrapper endorsed by Tibber themselves.

tibber.py is an unofficial python wrapper package for communication with the [Tibber API](https://developer.tibber.com/).
This package aims to cover all functionalities of the Tibber API in the most beginner-friendly modern Pythonic way. You can read all the capabilites of the API and explore it
This package aims to cover all functionalities of the Tibber API in the most beginner-friendly modern Pythonic way. You can read all the capabilites of the API and explore it
with [Tibbers' API explorer](https://developer.tibber.com/explorer). For documentation on how to use tibber.py head over to https://tibberpy.readthedocs.io/en/latest/.

Every field of the API types should be found in the corresponding `tibber.type` (e.g. the `size: Int` field of `Home`
Expand All @@ -21,21 +21,24 @@ docs (located on the right side of the Tibber API explorer).
[![Pytest Python 3.7 / 3.11](https://github.com/BeatsuDev/tibber.py/actions/workflows/pytests.yml/badge.svg)](https://github.com/BeatsuDev/tibber.py/actions/workflows/pytests.yml)
![Publish to PyPi status](https://github.com/BeatsuDev/tibber.py/actions/workflows/publish-to-pypi.yml/badge.svg)



Do you want to ask a question, report an issue, or even showcase your project that uses tibber.py? 🤩<br>Find out where to post by [checking out this overview](https://github.com/BeatsuDev/tibber.py/discussions/46).


## Installation

### Install via pip

```
python -m pip install tibber.py
```

### Requirements

tibber.py depends on `gql`, `gql[aiohttp]`, `gql[websockets]` and `graphql-core`. tibber.py supports Python versions 3.7 and up!

## Examples

### Getting basic account data

```python
import tibber

Expand All @@ -56,6 +59,7 @@ print(account.name)
```

### Getting basic home data

```python
import tibber

Expand All @@ -75,6 +79,7 @@ print(home.main_fuse_size) # 25
```

### Reading historical data

```python
import tibber

Expand All @@ -99,8 +104,10 @@ for hour in hour_data:
```

### Reading live measurements

Note how you can register multiple callbacks for the same event. These will be run
in asynchronously (at the same time)!

```python
import tibber

Expand All @@ -115,11 +122,52 @@ async def show_current_power(data):
@home.event("live_measurement")
async def show_accumulated_cost(data):
print(f"{data.accumulated_cost} {data.currency}")

def when_to_stop(data):
return data.power < 1500

# Start the live feed. This runs until data.power is less than 1500.
# If a user agent was not defined earlier, this will be required here
home.start_live_feed(user_agent = "UserAgent/0.0.1", exit_condition = when_to_stop)
home.start_live_feed(user_agent = "UserAgent/0.0.1", exit_condition = when_to_stop)
```

### Handling errors in a websocket connection

```python
import tibber

from tibber.exceptions import ConnectionErrorList
from tibber.exceptions import QueryErrorList
from tibber.exceptions import MalformedQueryException

account = tibber.Account(tibber.DEMO_TOKEN)
home = account.homes[0]

@home.event("live_measurement")
async def show_current_power(data):
print(data.power)


# Define error handlers:
def connection_error_handler(errors: ConnectionErrorList, additional_data: dict):
for error in errors:
print(error)

# Returning true will make tibber.py continue retrying the connection
# to the websocket until max retries has been reached.
# Returning False will exit the real time data feed regardless of
# how many connection attempts has already been done.
return True

def query_error_handler(errors: QueryErrorList, additional_data: dict):
if isinstance(error, MalformedQueryException):
print(data.query) # Prints the query that failed
return False # Exit the real time data feed when a MalformedQueryException happens
return True

home.start_live_feed(
user_agent = "UserAgent/0.0.1",
on_connection_error = connection_error_handler # Runs when the websocket fails to connect
on_query_error = query_error_handler # Run when the subscription query returns an error
)
```
27 changes: 2 additions & 25 deletions tests/realtime/test_live_measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@ def test_starting_live_feed_with_no_listeners_shows_warning(caplog):
home.start_live_feed(f"tibber.py-tests/{__version__}", exit_condition = lambda data: True)
assert "The event that was broadcasted has no listeners / callbacks! Nothing was run." in caplog.text


def test_retrieving_live_measurements():
account = tibber.Account(tibber.DEMO_TOKEN)
home = account.homes[0]

global callback_was_run
callback_was_run = False

@home.event("live_measurement")
async def callback(data):
global callback_was_run
Expand All @@ -37,31 +39,6 @@ async def callback(data):
timestamp = timestamp.replace(tzinfo=None)
now = datetime.now().replace(tzinfo=None)
assert timestamp > now - timedelta(seconds=30)
assert data.power > 0
assert data.last_meter_consumption > 0
assert data.accumulated_consumption > 0
assert isinstance(data.accumulated_production, (int, float))
assert data.accumulated_consumption_last_hour > 0
assert isinstance(data.accumulated_production_last_hour, (int, float))
assert data.accumulated_cost > 0
assert isinstance(data.accumulated_reward, (int, float))
assert data.currency == "SEK"
assert data.min_power >= 0
assert data.max_power > 0
assert data.average_power > 0
assert isinstance(data.power_production, (int, float))
assert isinstance(data.power_reactive, (int, float))
assert data.power_production_reactive > 0
assert isinstance(data.min_power_production, (int, float))
assert isinstance(data.max_power_production, (int, float))
assert data.last_meter_production > 0
assert data.power_factor > 0
assert data.voltage_phase_1 > 0
assert data.voltage_phase_2 > 0
assert data.voltage_phase_3 > 0
assert data.currentL1 > 0
assert data.currentL2 > 0
assert data.currentL3 > 0

# Return immediately after the first callback
home.start_live_feed(f"tibber.py-tests/{__version__}", exit_condition = lambda data: True)
Expand Down
Loading
Loading