Compare commits

...

6 Commits

Author SHA1 Message Date
caaf2689ff upd docstring 2024-02-16 12:53:34 +00:00
7e7aa1b4de 2.3.2 section added to CHANGELOG
patch bump
2024-02-16 12:50:54 +00:00
2dc096e306 connect_timeout kwarg added to README
Errors section added to README
2024-02-16 12:37:15 +00:00
ed397e57aa XAirRemoteConnectionTimeoutError added to errors 2024-02-16 12:26:24 +00:00
718ecbd982 timeout decorator func added to util 2024-02-16 12:26:00 +00:00
69cabb3db0 add configurable kwarg connect_timeout 2024-02-16 12:25:41 +00:00
6 changed files with 78 additions and 14 deletions

View File

@ -11,6 +11,14 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
- [ ] - [ ]
## [2.3.2] - 2024-02-16
### Added
- Configurable kwarg `connect_timeout` added. Defaults to 2 seconds.
- New error class `XAirRemoteConnectionTimeoutError`. Raised if a connection validation times out.
- timeout kwarg + Errors section added to README.
## [2.3.1] - 2024-02-15 ## [2.3.1] - 2024-02-15
### Changed ### Changed

View File

@ -55,7 +55,7 @@ if __name__ == "__main__":
main() main()
``` ```
#### `xair_api.connect(kind_id, ip=ip, delay=delay)` #### `xair_api.connect(kind_id, ip=ip, delay=0.02, connect_timeout=2)`
Currently the following devices are supported: Currently the following devices are supported:
@ -72,6 +72,7 @@ The following keyword arguments may be passed:
- `port`: mixer port, defaults to 10023 for x32 and 10024 for xair - `port`: mixer port, defaults to 10023 for x32 and 10024 for xair
- `delay`: a delay between each command (applies to the getters). Defaults to 20ms. - `delay`: a delay between each command (applies to the getters). Defaults to 20ms.
- a note about delay, stability may rely on network connection. For wired connections the delay can be safely reduced. - a note about delay, stability may rely on network connection. For wired connections the delay can be safely reduced.
- `connect_timeout`: amount of time to wait for a validated connection. Defaults to 2s.
## API ## API
@ -317,6 +318,14 @@ for example:
print(mixer.query("/ch/01/mix/on")) print(mixer.query("/ch/01/mix/on"))
``` ```
### Errors
- `errors.XAirRemoteError`: Base error class for XAIR Remote.
- `errors.XAirRemoteConnectionTimeoutError`:Exception raised when a connection attempt times out.
- The following attributes are available:
- `ip`: IP of the mixer.
- `port`: Port of the mixer.
### `Tests` ### `Tests`
Unplug any expensive equipment before running tests. Unplug any expensive equipment before running tests.

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "xair-api" name = "xair-api"
version = "2.3.1" version = "2.3.2"
description = "Remote control Behringer X-Air | Midas MR mixers through OSC" description = "Remote control Behringer X-Air | Midas MR mixers through OSC"
authors = ["onyx-and-iris <code@onyxandiris.online>"] authors = ["onyx-and-iris <code@onyxandiris.online>"]
license = "MIT" license = "MIT"

View File

@ -1,2 +1,14 @@
class XAirRemoteError(Exception): class XAirRemoteError(Exception):
"""Base error class for XAIR Remote.""" """Base error class for XAIR Remote."""
class XAirRemoteConnectionTimeoutError(XAirRemoteError):
"""Exception raised when a connection attempt times out"""
def __init__(self, ip, port):
self.ip = ip
self.port = port
super().__init__(
f"Timeout attempting to connect to mixer at {self.ip}:{self.port}"
)

View File

@ -1,6 +1,35 @@
import functools import functools
import time
from math import exp, log from math import exp, log
from .errors import XAirRemoteConnectionTimeoutError
def timeout(func):
"""
Times out the validate_connection function once time elapsed exceeds remote.connect_timeout.
"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
remote, *_ = args
err = None
start = time.time()
while time.time() < start + remote.connect_timeout:
try:
func(*args, **kwargs)
remote.logger.debug(f"login time: {round(time.time() - start, 2)}")
err = None
break
except XAirRemoteConnectionTimeoutError as e:
err = e
continue
if err:
raise err
return wrapper
def lin_get(min, max, val): def lin_get(min, max, val):
return min + (max - min) * val return min + (max - min) * val

View File

@ -14,11 +14,11 @@ from pythonosc.dispatcher import Dispatcher
from pythonosc.osc_message_builder import OscMessageBuilder from pythonosc.osc_message_builder import OscMessageBuilder
from pythonosc.osc_server import BlockingOSCUDPServer from pythonosc.osc_server import BlockingOSCUDPServer
from . import adapter, kinds from . import adapter, kinds, util
from .bus import Bus from .bus import Bus
from .config import Config from .config import Config
from .dca import DCA from .dca import DCA
from .errors import XAirRemoteError from .errors import XAirRemoteConnectionTimeoutError, XAirRemoteError
from .fx import FX, FXSend from .fx import FX, FXSend
from .kinds import KindMap from .kinds import KindMap
from .lr import LR from .lr import LR
@ -47,8 +47,6 @@ class OSCClientServer(BlockingOSCUDPServer):
class XAirRemote(abc.ABC): class XAirRemote(abc.ABC):
"""Handles the communication with the mixer via the OSC protocol""" """Handles the communication with the mixer via the OSC protocol"""
_CONNECT_TIMEOUT = 0.5
_info_response = [] _info_response = []
def __init__(self, **kwargs): def __init__(self, **kwargs):
@ -57,6 +55,7 @@ class XAirRemote(abc.ABC):
self.xair_ip = kwargs["ip"] or self._ip_from_toml() self.xair_ip = kwargs["ip"] or self._ip_from_toml()
self.xair_port = kwargs["port"] self.xair_port = kwargs["port"]
self._delay = kwargs["delay"] self._delay = kwargs["delay"]
self.connect_timeout = kwargs["connect_timeout"]
self.logger = logger.getChild(self.__class__.__name__) self.logger = logger.getChild(self.__class__.__name__)
if not self.xair_ip: if not self.xair_ip:
raise XAirRemoteError("No valid ip detected") raise XAirRemoteError("No valid ip detected")
@ -74,13 +73,10 @@ class XAirRemote(abc.ABC):
conn = tomllib.load(f) conn = tomllib.load(f)
return conn["connection"].get("ip") return conn["connection"].get("ip")
@util.timeout
def validate_connection(self): def validate_connection(self):
self.send("/xinfo") if not self.query("/xinfo"):
time.sleep(self._CONNECT_TIMEOUT) raise XAirRemoteConnectionTimeoutError(self.xair_ip, self.xair_port)
if not self.info_response:
raise XAirRemoteError(
"Failed to setup OSC connection to mixer. Please check for correct ip address."
)
self.logger.info( self.logger.info(
f"Successfully connected to {self.info_response[2]} at {self.info_response[0]}." f"Successfully connected to {self.info_response[2]} at {self.info_response[0]}."
) )
@ -117,7 +113,12 @@ def _make_remote(kind: KindMap) -> XAirRemote:
""" """
def init_x32(self, *args, **kwargs): def init_x32(self, *args, **kwargs):
defaultkwargs = {"ip": None, "port": 10023, "delay": 0.02} defaultkwargs = {
"ip": None,
"port": 10023,
"delay": 0.02,
"connect_timeout": 2,
}
kwargs = defaultkwargs | kwargs kwargs = defaultkwargs | kwargs
XAirRemote.__init__(self, *args, **kwargs) XAirRemote.__init__(self, *args, **kwargs)
self.kind = kind self.kind = kind
@ -135,7 +136,12 @@ def _make_remote(kind: KindMap) -> XAirRemote:
self.config = Config.make(self) self.config = Config.make(self)
def init_xair(self, *args, **kwargs): def init_xair(self, *args, **kwargs):
defaultkwargs = {"ip": None, "port": 10024, "delay": 0.02} defaultkwargs = {
"ip": None,
"port": 10024,
"delay": 0.02,
"connect_timeout": 2,
}
kwargs = defaultkwargs | kwargs kwargs = defaultkwargs | kwargs
XAirRemote.__init__(self, *args, **kwargs) XAirRemote.__init__(self, *args, **kwargs)
self.kind = kind self.kind = kind