Skip to content
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
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
uv run make -C docs html

tests:
name: Tests - Python ${{ matrix.python-version}} on ${{ matrix.os }}${{ fromJSON('[" (listen)", ""]')[matrix.extras == ''] }}
name: Tests - Python ${{ matrix.python-version}} on ${{ matrix.os }}
needs: linting
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -83,7 +83,6 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
uv-version: ${{ env.UV_VERSION }}
uv-install-options: ${{ matrix.extras == true && '--extra listen' || '' }}
- name: Run tests
run: >
uv run pytest tests/
Expand Down
17 changes: 7 additions & 10 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,6 @@ Installation
$ pip install \
git+https://github.com/python-ring-doorbell/python-ring-doorbell@master

Event Listener
++++++++++++++

If you want the ring api to listen for push events from ring.com for dings and motion you
will need to install with the `listen` extra::

$ pip install ring_doorbell[listen]

The api will then start listening for push events after you have first called `update_dings()`
or `update_data()` but only if there is a running `asyncio <https://docs.python.org/3/library/asyncio.html>`_ event loop (which there will be if using the CLI)

Using the CLI
-------------
Expand Down Expand Up @@ -180,7 +170,14 @@ For the deprecated sync example see `test_sync.py <https://github.com/python-rin
if __name__ == "__main__":
asyncio.run(main())

Event Listener
++++++++++++++

.. code-block:: python

event_listener = RingEventListener(ring, credentials, credentials_updated_callback)
event_listener.add_notification_callback(_event_handler(ring).on_event)
await event_listener.start()

Listing devices linked to your account
++++++++++++++++++++++++++++++++++++++
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ dependencies = [
"aiofiles>=23",
"typing-extensions>=4.12.2,<5.0",
"async-timeout>=3.0.0",
"websockets>=11.0.1"
"websockets>=11.0.1",
"firebase-messaging>=0.4.0",
]

keywords = [
Expand Down Expand Up @@ -48,7 +49,6 @@ ring-doorbell = "ring_doorbell.cli:cli"

[project.optional-dependencies]
docs = ["sphinx<7.2.6", "sphinx-rtd-theme~=1.3", "myst-parser"]
listen = ["firebase-messaging~=0.4"]

[build-system]
requires = ["hatchling"]
Expand Down
3 changes: 3 additions & 0 deletions ring_doorbell/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
)
from ring_doorbell.generic import RingGeneric
from ring_doorbell.group import RingLightGroup
from ring_doorbell.listen import RingEventListener, RingEventListenerConfig
from ring_doorbell.other import RingOther
from ring_doorbell.ring import Ring, RingDevices
from ring_doorbell.stickup_cam import RingStickUpCam
Expand All @@ -33,6 +34,8 @@
"RingDoorBell",
"RingOther",
"RingEvent",
"RingEventListener",
"RingEventListenerConfig",
"RingError",
"AuthenticationError",
"Requires2FAError",
Expand Down
11 changes: 3 additions & 8 deletions ring_doorbell/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
USER_AGENT,
DOORBELL_EXISTING_TYPE,
)
from ring_doorbell.listen import can_listen


def _header() -> None:
Expand Down Expand Up @@ -286,9 +285,9 @@ async def cli(ctx, username, password, debug, user_agent):
log_level = logging.DEBUG if debug else logging.INFO
logger = logging.getLogger(PACKAGE_NAME)
logger.setLevel(log_level)
if can_listen:
logger = logging.getLogger("firebase_messaging")
logger.setLevel(log_level)

logger = logging.getLogger("firebase_messaging")
logger.setLevel(log_level)

no_update_commands = ["listen"]
no_update = ctx.invoked_subcommand in no_update_commands
Expand Down Expand Up @@ -873,10 +872,6 @@ async def listen(
show_credentials,
) -> None:
"""Listen to push notification like the ones sent to your phone."""
if not can_listen:
echo("Ring is not configured for listening to notifications!")
echo("pip install ring_doorbell[listen]")
return

from ring_doorbell.listen import ( # pylint:disable=import-outside-toplevel
RingEventListener,
Expand Down
11 changes: 5 additions & 6 deletions ring_doorbell/listen/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
"""Package for listener modules."""

try:
from .eventlistener import RingEventListener
from .listenerconfig import RingEventListenerConfig
from .eventlistener import RingEventListener
from .listenerconfig import RingEventListenerConfig

can_listen = True
except ImportError: # pragma: no cover
can_listen = False # pylint:disable=invalid-name
# can_listen used to be checkable to see if the optional listen extra installed.
# Now installed as default.
can_listen = True

__all__ = [
"RingEventListener",
Expand Down
3 changes: 1 addition & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from aioresponses import CallbackResult, aioresponses
from ring_doorbell import Auth, Ring
from ring_doorbell.const import USER_AGENT
from ring_doorbell.listen import can_listen


# The kwargs below are useful for request assertions
Expand Down Expand Up @@ -130,7 +129,7 @@ def load_alert_v2(

@pytest.fixture(autouse=True)
def _listen_mock(mocker, request) -> None:
if not can_listen or "nolistenmock" in request.keywords:
if "nolistenmock" in request.keywords:
return

mocker.patch(
Expand Down
7 changes: 0 additions & 7 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
videos,
)
from ring_doorbell.const import GCM_TOKEN_FILE
from ring_doorbell.listen import can_listen

from tests.conftest import (
load_alert_v1,
Expand Down Expand Up @@ -207,9 +206,6 @@ async def test_motion_detection(ring, aioresponses_mock, devices_fixture):
assert expected in res.output


@pytest.mark.skipif(
can_listen is False, reason="requires the extra [listen] to be installed"
)
@pytest.mark.nolistenmock
async def test_listen_store_credentials(mocker, auth):
runner = CliRunner()
Expand Down Expand Up @@ -247,9 +243,6 @@ async def test_listen_store_credentials(mocker, auth):
assert firebase_messaging.FcmPushClient.start.call_count == 2


@pytest.mark.skipif(
can_listen is False, reason="requires the extra [listen] to be installed"
)
async def test_listen_event_handler(mocker, auth):
from ring_doorbell.listen import RingEventListener

Expand Down
10 changes: 1 addition & 9 deletions tests/test_listen.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,10 @@
from freezegun.api import FrozenDateTimeFactory
from ring_doorbell import Ring
from ring_doorbell.exceptions import RingError
from ring_doorbell.listen import can_listen
from ring_doorbell.listen import RingEventListener

from tests.conftest import load_alert_v1, load_alert_v2, load_fixture

# test_module.py
pytestmark = pytest.mark.skipif(
can_listen is False, reason=("requires the extra [listen] to be installed")
)

if can_listen:
from ring_doorbell.listen import RingEventListener


async def test_listen(auth, mocker):
import firebase_messaging
Expand Down
32 changes: 15 additions & 17 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.