Skip to main content

Python versions GitHub Actions Workflow Status PyPI version

aioartnet is a pure python asyncio connector for the royalty-free Art-Net protocol, which is a transport to transmit and recieve the DMX-512 lighting control protocol over Ethernet (UDP). The protocol is the modern standard for interconnecting complex lighting fixtures directly, and for branching out individual universes for a specific local area to control classic DMX-512 XLR-cable interlinked fixtures. Both open and propriety lighting control systems can emit Art-Net directly.

This library aims to be simple and robust, and can both input data into Art-Net, as well as output it from "artnet" to your user code. It builds a dynamic model of the network's Art-Net nodes, their ports and the universe(s) of DMX-512 that are being controlled. Fully type hinted to comply with PEP-561. No non-core dependancies, with a test suite that runs on Python 3.9 to 3.12.

It can also be used passively to build the network model without joining as an Art-Net Node, by listening to the broadcasts from other Art-Net devices. However it is not usually possible to monitor the DMX values passively.

We also have a propriety iOS/Xcode component available for commercial licensing that uses the same event driven API as this code, built on top of Apple's Networking framework NWProtocolUDP. This will be a significantly better starting point for new designs than libartnet for most projects.

Install & Demo

Use pip to install the package from pypi. Running aioartnet.main will show other nodes and universes discovered:

$ pip install aioartnet
$ python -m aioartnet.main
INFO:aioartnet:preferred interfaces: [(1, 'wlp4s0'), (10, 'br-ee82b9af434e'), (10, 'docker0'), (10, 'lo')]
INFO:aioartnet:using interface wlp4s0 with ip 192.168.1.205 broadcast ip 192.168.1.255
status:
  ArtNetNode<aioartnet,192.168.1.205:6454>                     [Port<Input,DMX,0:0:1>, Port<Output,DMX,0:0:5>]
  ArtNetNode<ODE Mk3,192.168.1.238:6454>                       [Port<Output,DMX,0:0:0>, Port<Output,DMX,0:0:1>]
  ArtNetNode<DMX Monitor,192.168.1.222:6454>                   []
 0:0:1 pubs:[ArtNetNode<aioartnet,192.168.1.205:6454>] subs:[ArtNetNode<ODE Mk3,192.168.1.238:6454>]
 0:0:5 pubs:[] subs:[ArtNetNode<aioartnet,192.168.1.205:6454>]
 0:0:0 pubs:[] subs:[ArtNetNode<ODE Mk3,192.168.1.238:6454>]

There is a built in command-line lighting console for quick Art-Net testing, which maps channels 1:1 to a single universe:

$ python -m aioartnet.console --universe 0:0:0
INFO:aioartnet:preferred interfaces: [(1, 'wlp4s0'), (10, 'br-ee82b9af434e'), (10, 'docker0'), (10, 'lo'), (10, 'lxdbr0')]
INFO:aioartnet:using interface wlp4s0 with ip 192.168.1.204 broadcast ip 192.168.1.255, listening on 0.0.0.0 our mac 4485009be628
> live on
> ch 1 at f
> record cue 1
> ch 2 at f
> record cue 2
> go
activated ActiveCue(cue=Cue(name='', fade_in=0, hold=1, fade_out=0, channels=[ChannelIntensity(channel=0, intensity=255)]), since=1750845334.3249245, state=<FadeState.FADE_IN: 2>)
finished ActiveCue(cue=Cue(name='', fade_in=0, hold=1, fade_out=0, channels=[ChannelIntensity(channel=0, intensity=255)]), since=1750845334.3249245, state=<FadeState.OFF: 1>)
> go
activated ActiveCue(cue=Cue(name='', fade_in=0, hold=1, fade_out=0, channels=[ChannelIntensity(channel=1, intensity=255)]), since=1750845336.5779428, state=<FadeState.FADE_IN: 2>)
finished ActiveCue(cue=Cue(name='', fade_in=0, hold=1, fade_out=0, channels=[ChannelIntensity(channel=1, intensity=255)]), since=1750845336.5779428, state=<FadeState.OFF: 1>)
> [ctl-C]

Getting Started - Code

  1. Import the library and create an ArtNetClient
    from aioartnet import ArtNetClient
    client = ArtNetClient()
  1. Configure the desired input/output ports on the client using set_port_config(), each call returns ArtNetUniverse objects. Use these to optionally set the initial published value of universes set as ArtNet inputs:
    u1 = client.set_port_config("0:0:1", is_input=True)
    u5 = client.set_port_config("0:0:5", is_output=True)
    u1.set_dmx(bytes(list(range(128))*4))
  1. Await the co-routine client.connect() from an asyncio asynchronous context, e.g:
    async def main() -> None:
        client = ArtNetClient()
        u1 = client.set_port_config("0:0:1", is_input=True)
        u5 = client.set_port_config("0:0:5", is_output=True)
        u1.set_dmx(bytes(list(range(128))*4))
        await client.connect()

    if __name__ == "__main__":
        logging.basicConfig(level=logging.INFO)
        asyncio.run(main())
        asyncio.get_event_loop().run_forever()
  1. Once the client is connected, you can add/configure further ports, read the universes of DMX data with universe.get_dmx() and for input ports, change the published values with universe.set_dmx(bytes).

  2. The return value from client.connect() is an ayncio.DatagramTransport. If you call close() on this transport, the periodic task is also cancelled.

Networking

There are three ways to configure the networking:

Automatically

Creating an ArtNetClient with no arguments will perform discovery during connect() by iterating the interfaces available, checking if they have an IPV4 address (ie. AF_INET family) and prefering certain properties. Specifically we prefer 2.x.x.x subnets with the 255.0.0.0 subnet mask, then configured Ethernet (interface name enp*), then configured WiFi interfaces (wlp*), followed by any other remaining AF_INET interfaces. The priority ordering is set by aioartnet.PREFERED_INTERFACES_ORDER which can be monkey-patched if required.

Manually by specifying an interface

Passing an interface name to the constructor ArtNetClient(interface="en0"), or setting client.interface="en0" before calling connect() will force use of a specific interface, skipping the interface selection logic. The interface is then queried to determine the ip, broadcast and listening address to use using ioctl calls.

Manually by specifying unicast and broadcast IPs

If the two required IP addresses (ours, broadcast) are set manually on the client no discovery is performed. This will be the most portable method as we effectively supply the arguments directly to asyncio.

client = ArtNetClient()
client.unicast_ip = '192.168.1.15'
client.broadcast_ip = '192.168.1.255'
await client.connect()

Features

Message Recieve Transmit
Node and universe discovery :heavy_check_mark: :heavy_check_mark:
15-bit port addresses :heavy_check_mark: :heavy_check_mark:
>4 ports (bindIndex) on same IP :heavy_check_mark: :heavy_check_mark:
merge-mode (LTP/HTP) in reciever - -
RDM commands to enumerate fixtures - -
Timecode - -
Multi-universe sync message - -
local node reconfigure by API :heavy_check_mark: :heavy_check_mark:
remote reconfigure (by ArtAddress etc) - -
Fixture firmware upgrade messages Not planned Not planned

Implemented Messages

Message Recieve Transmit
ArtPoll :heavy_check_mark: :heavy_check_mark:
ArtPollReply :heavy_check_mark: :heavy_check_mark:
ArtDMX :heavy_check_mark: :heavy_check_mark:
ArtIpProg / ArtIpProgReply - -
ArtAddress - -
ArtDataRequest / ArtDataReply - -
ArtDiagData - -
ArtTimeCode - -
ArtCommand - -
ArtTrigger - -
ArtSync - -
RDM ArtTodRequest / ArtTodData / ArtTodControl / ArtRdm / ArtRdmSub - -

Art-Net

This application aims to be fully compatible with Art-Net devices. We have tested it with:

Art-Net logo Art-Net™ Designed by and Copyright Artistic Licence Engineering Ltd.

Metadata

Release files for aioartnet 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aioartnet 0.2.1
File Size Uploaded
aioartnet-0.2.1.tar.gz 30.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aioartnet 0.2.1
File Interpreter ABI Platform
aioartnet-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 53.6 kB

Release files / aioartnet-0.2.1.tar.gz

Download URL aioartnet-0.2.1.tar.gz
Size 30.9 kB
Tags Source
SHA-256 checksum
How to use checksums
d6a91dfcd2a77c8ca3513c8ade525f66f8347f191e16ba276ac4824c45058b7f
BLAKE2b-256 checksum
How to use checksums
3534b6d0fc42fb631be5d34d91041744fb6c6e02deb7c08f3e4c0ff0e57e315f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 29, 2025.

Transparency log

Release files / aioartnet-0.2.1-py3-none-any.whl

Download URL aioartnet-0.2.1-py3-none-any.whl
Size 22.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4d1ac51da67751d6a89f2b550e15756b76e51602a76cc2f564a5f8c9f730fae1
BLAKE2b-256 checksum
How to use checksums
2ba4c5cebc1e3a495eb660fbaf0d16109c3a57637fb92c3ceb0f3ce10b2d1d6c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 29, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page