Skip to main content

Asyncio UPnP Client library for Python/asyncio.

Written initially for use in Home Assistant to drive DLNA DMR-capable devices, but useful for other projects as well.

Status

https://github.com/StevenLooman/async_upnp_client/workflows/Build/badge.svg https://img.shields.io/pypi/v/async_upnp_client.svg https://img.shields.io/pypi/format/async_upnp_client.svg https://img.shields.io/pypi/pyversions/async_upnp_client.svg https://img.shields.io/pypi/l/async_upnp_client.svg

General set up

The UPnP Device Architecture document contains several sections describing different parts of the UPnP standard. These chapters/sections can mostly be mapped to the following modules:

  • Chapter 1 Discovery
    • Section 1.1 SSDP: async_upnp_client.ssdp

    • Section 1.2 Advertisement: async_upnp_client.advertisement provides basic functionality to receive advertisements.

    • Section 1.3 Search: async_upnp_client.search provides basic functionality to do search requests and gather the responses.

    • async_upnp_client.ssdp_client contains the SsdpListener which combines advertisements and search to get the known devices and provides callbacks on changes. It is meant as something which runs continuously to provide useful information about the SSDP-active devices.

  • Chapter 2 Description / Chapter 3 Control
    • async_upnp_client.client_factory/async_upnp_client.client provide a series of classes to get information about the device/services using the ‘description’, and interact with these devices.

    • async_upnp_client.server provides a series of classes to set up a UPnP server, including SSDP discovery/advertisements.

  • Chapter 4 Eventing
    • async_upnp_client.event_handler provides functionality to handle events received from the device.

There are several ‘profiles’ which a device can implement to provide a standard interface to talk to. Some of these profiles are added to this library. The following profiles are currently available:

  • Internet Gateway Device (IGD)
    • async_upnp_client.profiles.igd

  • Digital Living Network Alliance (DLNA)
    • async_upnp_client.profiles.dlna

  • Printers
    • async_upnp_client.profiles.printer

For examples on how to use async_upnp_client, see examples/ .

Note that this library is most likely does not fully implement all functionality from the UPnP Device Architecture document and/or contains errors/bugs/mis-interpretations.

Contributing

See CONTRIBUTING.rst.

Development

Development is done on the development branch.

pre-commit is used to run several checks before committing. You can install pre-commit and the git-hook by doing:

$ pip install pre-commit
$ pre-commit --install

The Open Connectivity Foundation provides a bundle with all UPnP Specifications.

Changes

Changes are recorded using Towncier. Once a new release is created, towncrier is used to create the file CHANGES.rst.

To create a new change run:

$ towncrier create <pr-number>.<change type>

A change type can be one of:

  • feature: Signifying a new feature.

  • bugfix: Signifying a bug fix.

  • doc: Signifying a documentation improvement.

  • removal: Signifying a deprecation or removal of public API.

  • misc: A ticket has been closed, but it is not of interest to users.

A new file is then created in the changes directory. Add a short description of the change to that file.

Releasing

Steps for releasing:

  • Switch to development: git checkout development

  • Do a pull: git pull

  • Run towncrier: towncrier build --version <version>

  • Commit towncrier results: git commit -m "Towncrier"

  • Run bump2version (note that this creates a new commit + tag): bump2version --tag major/minor/patch

  • Push to github: git push && git push --tags

Profiling

To do profiling it is recommended to install pytest-profiling. Then run a test with profiling enabled, and write the results to a graph:

# Run tests with profiling and svg-output enabled. This will generate prof/*.prof files, and a svg file.
$ pytest --profile-svg -k test_case_insensitive_dict_profile
...

# Open generated SVG file.
$ xdg-open prof/combined.svg

Alternatively, you can generate a profiling data file, use pyprof2calltree to convert the data and open kcachegrind. For example:

# Run tests with profiling enabled, this will generate prof/*.prof files.
$ pytest --profile -k test_case_insensitive_dict_profile
...

$ pyprof2calltree -i prof/combined.prof -k
launching kcachegrind

upnp-client

A command line interface is provided via the upnp-client script. This script can be used to:

  • call an action

  • subscribe to services and listen for events

  • show UPnP traffic (–debug-traffic) from and to the device

  • show pretty printed JSON (–pprint) for human readability

  • search for devices

  • listen for advertisements

The output of the script is a single line of JSON for each action-call or subscription-event. See the programs help for more information.

An example of calling an action:

$ upnp-client --pprint call-action http://192.168.178.10:49152/description.xml RC/GetVolume InstanceID=0 Channel=Master
{
    "timestamp": 1531482271.5603056,
    "service_id": "urn:upnp-org:serviceId:RenderingControl",
    "service_type": "urn:schemas-upnp-org:service:RenderingControl:1",
    "action": "GetVolume",
    "in_parameters": {
        "InstanceID": 0,
        "Channel": "Master"
    },
    "out_parameters": {
        "CurrentVolume": 70
    }
}

An example of subscribing to all services, note that the program stays running until you stop it (ctrl-c):

$ upnp-client --pprint subscribe http://192.168.178.10:49152/description.xml \*
{
    "timestamp": 1531482518.3663802,
    "service_id": "urn:upnp-org:serviceId:RenderingControl",
    "service_type": "urn:schemas-upnp-org:service:RenderingControl:1",
    "state_variables": {
        "LastChange": "<Event xmlns=\"urn:schemas-upnp-org:metadata-1-0/AVT_RCS\">\n<InstanceID val=\"0\">\n<Mute channel=\"Master\" val=\"0\"/>\n<Volume channel=\"Master\" val=\"70\"/>\n</InstanceID>\n</Event>\n"
    }
}
{
    "timestamp": 1531482518.366804,
    "service_id": "urn:upnp-org:serviceId:RenderingControl",
    "service_type": "urn:schemas-upnp-org:service:RenderingControl:1",
    "state_variables": {
        "Mute": false,
        "Volume": 70
    }
}
...

You can subscribe to list of services by providing these names or abbreviated names, such as:

$ upnp-client --pprint subscribe http://192.168.178.10:49152/description.xml RC AVTransport

An example of searching for devices:

$ upnp-client --pprint search
{
    "Cache-Control": "max-age=3600",
    "Date": "Sat, 27 Oct 2018 10:43:42 GMT",
    "EXT": "",
    "Location": "http://192.168.178.1:49152/description.xml",
    "OPT": "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01",
    "01-NLS": "906ad736-cfc4-11e8-9c22-8bb67c653324",
    "Server": "Linux/4.14.26+, UPnP/1.0, Portable SDK for UPnP devices/1.6.20.jfd5",
    "X-User-Agent": "redsonic",
    "ST": "upnp:rootdevice",
    "USN": "uuid:e3a17dd5-9d85-3131-3c34-b827eb498d72::upnp:rootdevice",
    "_timestamp": "2018-10-27 12:43:09.125408",
    "_host": "192.168.178.1",
    "_port": 49152
    "_udn": "uuid:e3a17dd5-9d85-3131-3c34-b827eb498d72",
    "_source": "search"
}

An example of listening for advertisements, note that the program stays running until you stop it (ctrl-c):

$ upnp-client --pprint advertisements
{
    "Host": "239.255.255.250:1900",
    "Cache-Control": "max-age=30",
    "Location": "http://192.168.178.1:1900/WFADevice.xml",
    "NTS": "ssdp:alive",
    "Server": "POSIX, UPnP/1.0 UPnP Stack/2013.4.3.0",
    "NT": "urn:schemas-wifialliance-org:device:WFADevice:1",
    "USN": "uuid:99cb221c-1f15-c620-dc29-395f415623c6::urn:schemas-wifialliance-org:device:WFADevice:1",
    "_timestamp": "2018-12-23 11:22:47.154293",
    "_host": "192.168.178.1",
    "_port": 1900
    "_udn": "uuid:99cb221c-1f15-c620-dc29-395f415623c6",
    "_source": "advertisement"
}

IPv6 support

IPv6 is supported for the UPnP client functionality as well as the SSDP functionality. Please do note that multicast over IPv6 does require a scope_id/interface ID. The scope_id is used to specify which interface should be used.

There are several ways to get the scope_id. Via Python this can be done via the ifaddr library. From the (Linux) command line the scope_id can be found via the ip command:

$ ip address
...
6: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:15:5d:38:97:cf brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.2/24 brd 192.168.1.255 scope global eth0
        valid_lft forever preferred_lft forever
    inet6 fe80::215:5dff:fe38:97cf/64 scope link
        valid_lft forever preferred_lft forever

In this case, the interface index is 6 (start of the line) and thus the scope_id is 6.

Or on Windows using the ipconfig command:

C:\> ipconfig /all
...
Ethernet adapter Ethernet:
    ...
    Link-local IPv6 Address . . . . . : fe80::e530:c739:24d7:c8c7%8(Preferred)
...

The scope_id is 8 in this example, as shown after the % character at the end of the IPv6 address.

Or on macOS using the ifconfig command:

% ifconfig
...
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
      options=50b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV,CHANNEL_IO>
      ether 38:c9:86:30:fe:be
      inet6 fe80::215:5dff:fe38:97cf%en0 prefixlen 64 secured scopeid 0x4
...

The scope_id is 4 in this example, as shown by scopeid 0x4. Note that this is a hexadecimal value.

All functionality regarding SSDP uses AddressTupleVXType to specify addresses.

For consistency, the AiohttpNotifyServer also uses AddressTupleVXType to specify the source (the address and port the notify server listens on.)

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

async_upnp_client-0.48.0.tar.gz (115.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

async_upnp_client-0.48.0-py3-none-any.whl (106.0 kB view details)

Uploaded Python 3

File details

Details for the file async_upnp_client-0.48.0.tar.gz.

File metadata

  • Download URL: async_upnp_client-0.48.0.tar.gz
  • Upload date:
  • Size: 115.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for async_upnp_client-0.48.0.tar.gz
Algorithm Hash digest
SHA256 64e6b0be515bc8ccd0fb03216b01900b3ee9e4f438423baed5a4d0c519d6a1c4
MD5 50be27635ced11fa7fd23b44806efba9
BLAKE2b-256 23d92bec0e80ac465610f185fcac49e585b9be7290dcf510fb5d2f8187f1edbf

See more details on using hashes here.

Provenance

The following attestation bundles were made for async_upnp_client-0.48.0.tar.gz:

Publisher: ci-cd.yml on StevenLooman/async_upnp_client

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file async_upnp_client-0.48.0-py3-none-any.whl.

File metadata

File hashes

Hashes for async_upnp_client-0.48.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f7672a1f9f8af0792eded6ac490d2b51569c0d844f261372b257540bf3bf0783
MD5 03006ac2101e73dc8749e2764533e28d
BLAKE2b-256 8c9f30d3e6073769b6137f8af517baec43dd9e900a655710b4bf192737802427

See more details on using hashes here.

Provenance

The following attestation bundles were made for async_upnp_client-0.48.0-py3-none-any.whl:

Publisher: ci-cd.yml on StevenLooman/async_upnp_client

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.48.0 This release

2 files

0.47.1

2 files

0.47.0

2 files

0.46.2

2 files

0.46.1

2 files

0.46.0

2 files

0.45.0

2 files

0.44.0

2 files

0.43.0

2 files

0.42.0

2 files

0.41.0

2 files

0.40.0

1 file

0.39.0

1 file

0.38.3

1 file

0.38.2

1 file

0.38.1

1 file

0.38.0

1 file

0.36.2

1 file

0.36.1

1 file

0.36.0

1 file

0.35.1

1 file

0.35.0

1 file

0.34.1

1 file

0.34.0

1 file

0.33.2

1 file

0.33.1

1 file

0.33.0

1 file

0.32.3

1 file

0.32.2

1 file

0.32.1

1 file

0.32.0

1 file

0.31.2

1 file

0.31.1

1 file

0.31.0

1 file

0.30.1

1 file

0.30.0

1 file

0.29.0

1 file

0.28.0

1 file

0.27.0

1 file

0.26.0

1 file

0.25.0

1 file

0.24.0

1 file

0.23.5

1 file

0.23.4

1 file

0.23.3

1 file

0.23.2

1 file

0.23.1

1 file

0.23.0

1 file

0.22.12

1 file

0.22.11

1 file

0.22.10

1 file

0.22.9

1 file

0.22.8

1 file

0.22.7

1 file

0.22.6

1 file

0.22.5

1 file

0.22.4

1 file

0.22.3

1 file

0.22.2

1 file

0.22.1

1 file

0.22.0

1 file

0.21.3

1 file

0.21.2

1 file

0.21.1

1 file

0.21.0

1 file

0.20.0

1 file

0.19.2

1 file

0.19.1

1 file

0.19.0

1 file

0.18.0

1 file

0.17.0

1 file

0.16.2

1 file

0.16.1

1 file

0.16.0

1 file

0.15.0

1 file

0.14.15

2 files

0.14.14

2 files

0.14.13

2 files

0.14.12

2 files

0.14.11

2 files

0.14.10

2 files

0.14.9

2 files

0.14.8

2 files

0.14.7

2 files

0.14.6

2 files

0.14.5

2 files

0.14.4

2 files

0.14.3

2 files

0.14.2

2 files

0.14.1

2 files

0.14.0

2 files

0.13.8

2 files

0.13.7

2 files

0.13.6

2 files

0.13.5

2 files

0.13.4

2 files

0.13.3

2 files

0.13.2

2 files

0.13.1

2 files

0.13.0

2 files

0.12.7

2 files

0.12.6

2 files

0.12.5

2 files

0.12.4

2 files

0.12.3

2 files

0.12.2

2 files

0.12.1

2 files

0.12.0

2 files

0.11.1

2 files

0.11.0

2 files

0.10.1

2 files

0.10.0

2 files

0.9.1

1 file

0.9.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page