asyncio native asterisk client for python
Project description
ARI Client Library
A Python client library for Asterisk REST Interface (ARI) that provides an object-oriented approach to managing channels, bridges, and events.
Architecture
The library follows a clean architecture pattern with separation of concerns:
- AriClient: Main client class that handles WebSocket connections and event dispatching
- AriClientController: Separate controller class that handles all HTTP API operations
- Model Objects: Bridge, Channel, and Event objects that encapsulate state and provide methods for actions
Key Design Principles
- All actions are performed via Bridge, Channel, and Event objects - This ensures that operations are context-aware and type-safe
- Controller is separate from client - The controller handles HTTP operations, while the client manages WebSocket connections
- Objects are self-contained - Each Bridge, Channel, and Event object has its own controller reference for performing actions
Installation
pip install -r requirements.txt
# or using uv
uv sync
Quick Start
import asyncio
from ari_client import AriClient, StasisStartEvent, StasisEndEvent
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Create client
client = AriClient(
host="localhost",
port=8088,
ari_user="asterisk",
ari_password="asterisk",
tls_enabled=False
)
# Define event handlers
@client.on_stasis_start
async def on_stasis_start(event: StasisStartEvent):
logger.info(f"Channel entered Stasis: {event.channel.id}")
# Answer the channel using the channel object
await event.channel.answer()
# Create a bridge using the controller
bridge = await client.ari.create_bridge(type="mixing")
# Add channel to bridge using the bridge object
await bridge.add_channel(event.channel.id)
# Create external media using the controller
external_media = await client.ari.create_external_media(
external_host="192.168.1.100:10000",
format="ulaw"
)
# Add external media to bridge
await bridge.add_channel(external_media.id)
await external_media.answer()
@client.on_stasis_end
async def on_stasis_end(event: StasisEndEvent):
logger.info(f"Channel left Stasis: {event.channel.id}")
# Main function
async def main():
await client.connect(app="myapp", subscribe_to_all=True)
# Originate a call
channel = await client.ari.originate(
endpoint="PJSIP/1001",
timeout=30
)
logger.info(f"Originated channel: {channel.id}")
# Keep running
try:
await asyncio.sleep(3600)
except KeyboardInterrupt:
logger.info("Shutting down...")
finally:
await client.disconnect()
if __name__ == "__main__":
asyncio.run(main())
Core Concepts
Event Objects
Event objects (StasisStartEvent, StasisEndEvent) are received when channels enter or leave your Stasis application. They contain channel information and can be used to access the channel object for performing actions.
Note: To create bridges, external media, or originate calls, use the controller via client.ari rather than event methods.
Channel Objects
Channel objects represent Asterisk channels and provide methods for channel operations:
channel.answer()- Answer the channelchannel.stop()- Hang up the channel
Bridge Objects
Bridge objects represent Asterisk bridges and provide methods for bridge operations:
bridge.add_channel(channel_id)- Add a channel to the bridgebridge.stop()- Destroy the bridge
API Reference
AriClient
Main client class for connecting to Asterisk ARI.
Constructor
AriClient(
host: str,
port: int,
ari_user: str,
ari_password: str,
tls_enabled: bool = False
)
Methods
async connect(app: str, subscribe_to_all: bool = False)- Connect to Asterisk and start listening for eventson_stasis_start(handler)- Register handler for StasisStart events (can be used as decorator)on_stasis_end(handler)- Register handler for StasisEnd events (can be used as decorator)ari- Get the ari controller instance for performing actions outside event handlersasync disconnect()- Disconnect from Asterisk
Event Handlers
Event handlers can be registered using decorators or method calls:
# As decorator
@client.on_stasis_start
async def handler(event: StasisStartEvent):
pass
# As method call
async def handler(event: StasisStartEvent):
pass
client.on_stasis_start(handler)
AriClientController
Controller class that handles all HTTP API operations. Typically accessed via client.ari or through event/channel/bridge objects.
Methods
async answer_channel(channel_id: str)- Answer a channelasync stop_channel(channel_id: str)- Hang up a channelasync create_bridge(type: str, bridge_id: Optional[str] = None, name: Optional[str] = None) -> Bridge- Create a bridgeasync bridge_add_channel(bridge_id: str, channel_id: str)- Add channel to bridgeasync stop_bridge(bridge_id: str)- Destroy a bridgeasync create_external_media(...) -> Channel- Create external media channelasync originate(...) -> Channel- Originate a new channel
Event
Base event class for all ARI events.
StasisStartEvent
Event received when a channel enters your Stasis application.
Properties
type: EventType- Event type (STASIS_START)timestamp: datetime- Event timestampargs: List[str]- Arguments passed to the Stasis applicationchannel: Channel- The channel that entered Stasisasterisk_id: str- Asterisk instance IDapplication: str- Application name
StasisEndEvent
Event received when a channel leaves your Stasis application.
Properties
type: EventType- Event type (STASIS_END)timestamp: datetime- Event timestampchannel: Channel- The channel that left Stasisapplication: str- Application name
Channel
Represents an Asterisk channel.
Properties
id: str- Channel unique identifiername: str- Channel namestate: str- Channel statecaller: CallerID- Caller informationconnected: CallerID- Connected party informationcreationtime: datetime- Channel creation timestamp
Methods
async answer()- Answer the channelasync stop()- Hang up the channeladd_controller(controller: AriClientController)- Add controller for performing actions
Bridge
Represents an Asterisk bridge.
Properties
id: str- Bridge unique identifierbridge_type: BridgeType- Type of bridge (MIXING, HOLDING)name: str- Bridge namechannels: List[str]- List of channel IDs in the bridgevideo_mode: Optional[VideoMode]- Video mode if applicable
Methods
async add_channel(channel_id: str)- Add a channel to the bridgeasync stop()- Destroy the bridge
Best Practices
- Always use event/channel/bridge objects for actions - This ensures proper context and type safety
- Handle exceptions in event handlers - The library automatically logs exceptions, but you should handle them appropriately
- Use the controller for operations - Access the controller via
client.arito create bridges, external media, or originate calls - Store bridge/channel references - If you need to reference bridges or channels later, store them in a dictionary or similar structure
Example: Call Bridging
bridge_map: dict[str, Bridge] = {}
@client.on_stasis_start
async def on_stasis_start(event: StasisStartEvent):
# Skip external media channels
if event.channel.name.startswith("UnicastRTP"):
return
# Answer the incoming channel
await event.channel.answer()
# Create a mixing bridge using the controller
bridge = await client.ari.create_bridge(type="mixing,proxy_media")
# Add the channel to the bridge
await bridge.add_channel(event.channel.id)
# Create external media for streaming using the controller
external_media = await client.ari.create_external_media(
external_host="192.168.1.100:10000",
format="ulaw"
)
# Add external media to bridge and answer it
await bridge.add_channel(external_media.id)
await external_media.answer()
# Store bridge reference for cleanup
bridge_map[event.channel.id] = bridge
@client.on_stasis_end
async def on_stasis_end(event: StasisEndEvent):
# Clean up bridge when channel leaves
bridge = bridge_map.pop(event.channel.id, None)
if bridge:
await bridge.stop()
Error Handling
The library includes automatic error handling:
- Event handler exceptions are automatically logged and don't crash the event listener
- HTTP API errors raise exceptions with descriptive messages
- WebSocket connection errors are logged and re-raised
Always wrap your operations in try-except blocks when appropriate:
@client.on_stasis_start
async def on_stasis_start(event: StasisStartEvent):
try:
await event.channel.answer()
except Exception as e:
logger.error(f"Failed to answer channel: {e}")
License
[Your License Here]
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ari_client-0.1.0.tar.gz.
File metadata
- Download URL: ari_client-0.1.0.tar.gz
- Upload date:
- Size: 14.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
04467ea916bb354bf1d1268392c9ed4aef75e71ead8435c9f00f4fad4db9b547
|
|
| MD5 |
70c643107e19f49c1b0c62d5464bc8d7
|
|
| BLAKE2b-256 |
ad17b8bea101700ed6e3cbf82eb744fbf7182e15b0e4aa41a66b2ea95b603a41
|
File details
Details for the file ari_client-0.1.0-py3-none-any.whl.
File metadata
- Download URL: ari_client-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.9.13 {"installer":{"name":"uv","version":"0.9.13"},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d5a31b9c6a6ee82366266f9b67707da4a61aa2c8300d9b72e6ea38563b460c4
|
|
| MD5 |
e1361e8b6f9b362a128a58ba9a17be18
|
|
| BLAKE2b-256 |
b3db07759fe442729466145ea1d78a668b5dc50f0742fad19d0d1a9926e01c3b
|