Skip to content

Releases: bazwilliams/openhomedevice

2.7

Choose a tag to compare

@bazwilliams bazwilliams released this 12 Sep 16:49

What's Changed

Full Changelog: 2.6...2.7

2.6

2.6

Choose a tag to compare

@bazwilliams bazwilliams released this 10 Sep 22:23

Subscribe to device events instead of polling

A device can push its changes rather than being asked for them. Each key is
named for the method returning the same value, so is_muted in an event is
what is_muted() would have returned.

def on_event(changes):
    if changes.get("is_subscribed") is False:
        return          # subscription lost; treat the state as stale
    print(changes)      # {"volume": 42, "is_muted": False}

if device.events_enabled:
    await device.subscribe(on_event)
    ...
    await device.unsubscribe()

The first event after subscribing carries the device's whole state; after
that only what changed. Subscribing needs the Product, Transport and Info
services — events_enabled says whether this device has them, and
subscribe() raises OpenhomeDeviceError if it does not, so older firmware
and third party renderers should keep polling.

Subscriptions are renewed in the background. If one cannot be renewed the
rest are released and the callback is called a final time with
{"is_subscribed": False}; call subscribe() again to recover.

By default a notify server is started per device. Pass an event_handler to
the constructor to share one across several, in the same way a session can
already be shared.

Report what the device can do with what is playing

Rather than guessing from the source type, ask the device.

await device.can_pause()           # False on a turntable at a line input
await device.can_skip_next()       # True on a playlist
await device.can_skip_previous()

Each returns None on a device with no Transport service, meaning it cannot
say rather than the answer being no, so a caller can fall back to guessing
from the source type as before.

Fixes

sources() now reads <Visible>1</Visible> as well as <Visible>true</Visible>,
so it no longer returns an empty list on AURALiC devices. Diagnosed by
@droman42 in #26.

await device.sources()   # ['Playlist', 'Radio', 'Songcast', 'Spotify']

pins_enabled reports whether the Pins service resolved, rather than whether
the device advertises Pins version 1 specifically, so a later version of the
service no longer reads as having no pins at all.

What's Changed

Full Changelog: 2.5...2.6

2.5

2.5

Choose a tag to compare

@bazwilliams bazwilliams released this 03 Sep 14:09
373bb39

No breaking changes. Both additions below are opt-in: code written against
2.4.1 behaves identically on 2.5.

Pass in your own aiohttp session

Device now takes an optional session:

device = Device(location, session=session)
await device.init()

Previously every request opened and closed a ClientSession of its own, so
polling a device meant a new session and TCP connection each time. Supplying a
session reuses it and its connection pool. The session is never closed by this
library — whoever created it owns it. Omitting it keeps the 2.4.1 behaviour.

Errors now come from openhomedevice.exceptions

Callers previously had to import async_upnp_client to catch a failed request,
coupling their error handling to a transitive dependency. Every method that
talks to the device now raises one of:

Exception Meaning
OpenhomeError Base class for all of the below
OpenhomeConnectionError The device could not be reached
OpenhomeTimeoutError The device did not answer in time (subclass of the above)
OpenhomeDeviceError The device answered, but refused the request or replied unusably

The original exception is preserved as __cause__. Because OpenhomeTimeoutError
subclasses OpenhomeConnectionError, catching the latter covers a device that is
off or unreachable for any reason.

Separately, a bare except: in the DIDL-Lite parser is now narrowed to
ParseError and TypeError; as written it also swallowed KeyboardInterrupt
and SystemExit.

Packaging and CI

  • python_requires corrected from >=3.6 to >=3.10. async_upnp_client has
    required 3.10 for some time, so the old floor was untrue.
  • Tests now run on GitHub Actions for every push and pull request, across
    Python 3.10, 3.11, 3.12, 3.13 and 3.14.

What's Changed

Full Changelog: 2.4.1...2.5

2.4.1

Choose a tag to compare

@bazwilliams bazwilliams released this 03 Sep 09:28

What's Changed

  • Remove lxml dependency from setup.py by @droman42 in #25
  • Added songcast support so supported devices can join and leave and songcast group for multi room audio

New Contributors

Full Changelog: 2.3.1...2.4.1

2.3.1

Choose a tag to compare

@bazwilliams bazwilliams released this 09 Aug 16:40

Fixed test data

Full Changelog: 2.3...2.3.1

2.3

2.3

Choose a tag to compare

@bazwilliams bazwilliams released this 02 Jun 22:31

Fix escaping issue when parsing metadata

Full Changelog: 2.2...2.3

2.2

2.2

Choose a tag to compare

@bazwilliams bazwilliams released this 27 Jun 19:29

Added manufacturer, model name and friendly name to API

Full Changelog: 2.1...2.2

2.1

2.1

Choose a tag to compare

@bazwilliams bazwilliams released this 14 Jun 19:57
  • Fixed test suite
  • Add support for firmware updates

Full Changelog: 2.0.2...2.1

2.0.2

Choose a tag to compare

@bazwilliams bazwilliams released this 13 Apr 23:06
  • updated dependencies and fixed changed imports

Full Changelog: 2.0.1...2.0.2