xair-api 2.4.0__tar.gz → 2.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xair_api-2.4.0 → xair_api-2.4.1}/PKG-INFO +14 -16
- {xair_api-2.4.0 → xair_api-2.4.1}/README.md +8 -8
- xair_api-2.4.1/pyproject.toml +139 -0
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/__init__.py +1 -1
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/adapter.py +7 -7
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/bus.py +6 -6
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/config.py +44 -44
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/dca.py +9 -9
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/errors.py +1 -1
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/fx.py +9 -9
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/headamp.py +7 -7
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/kinds.py +13 -15
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/lr.py +6 -6
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/meta.py +2 -2
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/rtn.py +12 -12
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/shared.py +168 -168
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/strip.py +7 -7
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/util.py +1 -1
- {xair_api-2.4.0 → xair_api-2.4.1}/xair_api/xair.py +24 -24
- xair_api-2.4.0/pyproject.toml +0 -135
- {xair_api-2.4.0 → xair_api-2.4.1}/LICENSE +0 -0
|
@@ -1,21 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: xair-api
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Summary: Remote control Behringer X-Air | Midas MR mixers through OSC
|
|
5
|
-
Home-page: https://github.com/onyx-and-iris/xair-api-python
|
|
6
5
|
License: MIT
|
|
7
|
-
Author:
|
|
6
|
+
Author: Onyx and Iris
|
|
8
7
|
Author-email: code@onyxandiris.online
|
|
9
|
-
Requires-Python:
|
|
8
|
+
Requires-Python: <4.0,>=3.10
|
|
10
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
10
|
Classifier: Programming Language :: Python :: 3
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
Requires-Dist: python-osc (>=1.
|
|
17
|
-
Requires-Dist: tomli (>=2.0.1,<3.0
|
|
18
|
-
Project-URL: Repository, https://github.com/onyx-and-iris/xair-api-python
|
|
15
|
+
Requires-Dist: python-osc (>=1.9.3,<2.0.0)
|
|
16
|
+
Requires-Dist: tomli (>=2.0.1,<3.0) ; python_version < "3.11"
|
|
19
17
|
Description-Content-Type: text/markdown
|
|
20
18
|
|
|
21
19
|
[](https://badge.fury.io/py/xair-api)
|
|
@@ -59,18 +57,18 @@ import xair_api
|
|
|
59
57
|
|
|
60
58
|
|
|
61
59
|
def main():
|
|
62
|
-
kind_id =
|
|
63
|
-
ip =
|
|
60
|
+
kind_id = 'XR18'
|
|
61
|
+
ip = '<ip address>'
|
|
64
62
|
|
|
65
63
|
with xair_api.connect(kind_id, ip=ip) as mixer:
|
|
66
|
-
mixer.strip[8].config.name =
|
|
64
|
+
mixer.strip[8].config.name = 'sm7b'
|
|
67
65
|
mixer.strip[8].mix.on = True
|
|
68
66
|
print(
|
|
69
|
-
f
|
|
67
|
+
f'strip 09 ({mixer.strip[8].config.name}) on has been set to {mixer.strip[8].mix.on}'
|
|
70
68
|
)
|
|
71
69
|
|
|
72
70
|
|
|
73
|
-
if __name__ ==
|
|
71
|
+
if __name__ == '__main__':
|
|
74
72
|
main()
|
|
75
73
|
```
|
|
76
74
|
|
|
@@ -334,8 +332,8 @@ Send an OSC command directly to the mixer
|
|
|
334
332
|
for example:
|
|
335
333
|
|
|
336
334
|
```python
|
|
337
|
-
mixer.send(
|
|
338
|
-
mixer.send(
|
|
335
|
+
mixer.send('/ch/01/mix/on', 1)
|
|
336
|
+
mixer.send('/bus/2/config/name', 'somename')
|
|
339
337
|
```
|
|
340
338
|
|
|
341
339
|
Query the value of a command:
|
|
@@ -345,7 +343,7 @@ Query the value of a command:
|
|
|
345
343
|
for example:
|
|
346
344
|
|
|
347
345
|
```python
|
|
348
|
-
print(mixer.query(
|
|
346
|
+
print(mixer.query('/ch/01/mix/on'))
|
|
349
347
|
```
|
|
350
348
|
|
|
351
349
|
### Errors
|
|
@@ -39,18 +39,18 @@ import xair_api
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def main():
|
|
42
|
-
kind_id =
|
|
43
|
-
ip =
|
|
42
|
+
kind_id = 'XR18'
|
|
43
|
+
ip = '<ip address>'
|
|
44
44
|
|
|
45
45
|
with xair_api.connect(kind_id, ip=ip) as mixer:
|
|
46
|
-
mixer.strip[8].config.name =
|
|
46
|
+
mixer.strip[8].config.name = 'sm7b'
|
|
47
47
|
mixer.strip[8].mix.on = True
|
|
48
48
|
print(
|
|
49
|
-
f
|
|
49
|
+
f'strip 09 ({mixer.strip[8].config.name}) on has been set to {mixer.strip[8].mix.on}'
|
|
50
50
|
)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
if __name__ ==
|
|
53
|
+
if __name__ == '__main__':
|
|
54
54
|
main()
|
|
55
55
|
```
|
|
56
56
|
|
|
@@ -314,8 +314,8 @@ Send an OSC command directly to the mixer
|
|
|
314
314
|
for example:
|
|
315
315
|
|
|
316
316
|
```python
|
|
317
|
-
mixer.send(
|
|
318
|
-
mixer.send(
|
|
317
|
+
mixer.send('/ch/01/mix/on', 1)
|
|
318
|
+
mixer.send('/bus/2/config/name', 'somename')
|
|
319
319
|
```
|
|
320
320
|
|
|
321
321
|
Query the value of a command:
|
|
@@ -325,7 +325,7 @@ Query the value of a command:
|
|
|
325
325
|
for example:
|
|
326
326
|
|
|
327
327
|
```python
|
|
328
|
-
print(mixer.query(
|
|
328
|
+
print(mixer.query('/ch/01/mix/on'))
|
|
329
329
|
```
|
|
330
330
|
|
|
331
331
|
### Errors
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "xair-api"
|
|
3
|
+
version = "2.4.1"
|
|
4
|
+
description = "Remote control Behringer X-Air | Midas MR mixers through OSC"
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Onyx and Iris",email = "code@onyxandiris.online"}
|
|
7
|
+
]
|
|
8
|
+
license = {text = "MIT"}
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = "<4.0,>=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"python-osc (>=1.9.3,<2.0.0)",
|
|
13
|
+
"tomli (>=2.0.1,<3.0) ; python_version < '3.11'"
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[tool.poetry.requires-plugins]
|
|
17
|
+
poethepoet = "^0.32.1"
|
|
18
|
+
|
|
19
|
+
[tool.poetry.group.dev.dependencies]
|
|
20
|
+
pytest = "^8.3.4"
|
|
21
|
+
pytest-randomly = "^3.16.0"
|
|
22
|
+
ruff = "^0.8.6"
|
|
23
|
+
tox = "^4.23.2"
|
|
24
|
+
virtualenv-pyenv = "^0.5.0"
|
|
25
|
+
|
|
26
|
+
[build-system]
|
|
27
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
28
|
+
build-backend = "poetry.core.masonry.api"
|
|
29
|
+
|
|
30
|
+
[tool.poe.tasks]
|
|
31
|
+
obs.script = "scripts:ex_obs"
|
|
32
|
+
sends.script = "scripts:ex_sends"
|
|
33
|
+
headamp.script = "scripts:ex_headamp"
|
|
34
|
+
xair.script = "scripts:test_xair"
|
|
35
|
+
x32.script = "scripts:test_x32"
|
|
36
|
+
all.script = "scripts:test_all"
|
|
37
|
+
|
|
38
|
+
[tool.tox]
|
|
39
|
+
legacy_tox_ini = """
|
|
40
|
+
[tox]
|
|
41
|
+
envlist = py310,py311,py312
|
|
42
|
+
|
|
43
|
+
[testenv]
|
|
44
|
+
setenv = VIRTUALENV_DISCOVERY=pyenv
|
|
45
|
+
allowlist_externals = poetry
|
|
46
|
+
commands =
|
|
47
|
+
poetry install -v
|
|
48
|
+
poetry run pytest tests/
|
|
49
|
+
|
|
50
|
+
[testenv:obs]
|
|
51
|
+
setenv = VIRTUALENV_DISCOVERY=pyenv
|
|
52
|
+
allowlist_externals = poetry
|
|
53
|
+
deps = obsws-python
|
|
54
|
+
commands =
|
|
55
|
+
poetry install -v --without dev
|
|
56
|
+
poetry run python examples/xair_obs/
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
[tool.ruff]
|
|
60
|
+
exclude = [
|
|
61
|
+
".bzr",
|
|
62
|
+
".direnv",
|
|
63
|
+
".eggs",
|
|
64
|
+
".git",
|
|
65
|
+
".git-rewrite",
|
|
66
|
+
".hg",
|
|
67
|
+
".mypy_cache",
|
|
68
|
+
".nox",
|
|
69
|
+
".pants.d",
|
|
70
|
+
".pytype",
|
|
71
|
+
".ruff_cache",
|
|
72
|
+
".svn",
|
|
73
|
+
".tox",
|
|
74
|
+
".venv",
|
|
75
|
+
"__pypackages__",
|
|
76
|
+
"_build",
|
|
77
|
+
"buck-out",
|
|
78
|
+
"build",
|
|
79
|
+
"dist",
|
|
80
|
+
"node_modules",
|
|
81
|
+
"venv",
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
# Same as Black.
|
|
85
|
+
line-length = 88
|
|
86
|
+
indent-width = 4
|
|
87
|
+
|
|
88
|
+
# Assume Python 3.10
|
|
89
|
+
target-version = "py310"
|
|
90
|
+
|
|
91
|
+
[tool.ruff.lint]
|
|
92
|
+
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
|
93
|
+
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
|
94
|
+
# McCabe complexity (`C901`) by default.
|
|
95
|
+
select = ["E4", "E7", "E9", "F"]
|
|
96
|
+
ignore = []
|
|
97
|
+
|
|
98
|
+
# Allow fix for all enabled rules (when `--fix`) is provided.
|
|
99
|
+
fixable = ["ALL"]
|
|
100
|
+
unfixable = []
|
|
101
|
+
|
|
102
|
+
# Allow unused variables when underscore-prefixed.
|
|
103
|
+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
104
|
+
|
|
105
|
+
[tool.ruff.format]
|
|
106
|
+
# Unlike Black, use single quotes for strings.
|
|
107
|
+
quote-style = "single"
|
|
108
|
+
|
|
109
|
+
# Like Black, indent with spaces, rather than tabs.
|
|
110
|
+
indent-style = "space"
|
|
111
|
+
|
|
112
|
+
# Like Black, respect magic trailing commas.
|
|
113
|
+
skip-magic-trailing-comma = false
|
|
114
|
+
|
|
115
|
+
# Like Black, automatically detect the appropriate line ending.
|
|
116
|
+
line-ending = "auto"
|
|
117
|
+
|
|
118
|
+
# Enable auto-formatting of code examples in docstrings. Markdown,
|
|
119
|
+
# reStructuredText code/literal blocks and doctests are all supported.
|
|
120
|
+
#
|
|
121
|
+
# This is currently disabled by default, but it is planned for this
|
|
122
|
+
# to be opt-out in the future.
|
|
123
|
+
docstring-code-format = false
|
|
124
|
+
|
|
125
|
+
# Set the line length limit used when formatting code snippets in
|
|
126
|
+
# docstrings.
|
|
127
|
+
#
|
|
128
|
+
# This only has an effect when the `docstring-code-format` setting is
|
|
129
|
+
# enabled.
|
|
130
|
+
docstring-code-line-length = "dynamic"
|
|
131
|
+
|
|
132
|
+
[tool.ruff.lint.mccabe]
|
|
133
|
+
max-complexity = 10
|
|
134
|
+
|
|
135
|
+
[tool.ruff.lint.per-file-ignores]
|
|
136
|
+
"__init__.py" = [
|
|
137
|
+
"E402",
|
|
138
|
+
"F401",
|
|
139
|
+
]
|
|
@@ -8,40 +8,40 @@ from .rtn import FxRtn as IFxRtn
|
|
|
8
8
|
class Bus(IBus):
|
|
9
9
|
@property
|
|
10
10
|
def address(self):
|
|
11
|
-
return f
|
|
11
|
+
return f'/bus/{str(self.index).zfill(2)}'
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class AuxRtn(IAuxRtn):
|
|
15
15
|
@property
|
|
16
16
|
def address(self):
|
|
17
|
-
return f
|
|
17
|
+
return f'/auxin/{str(self.index).zfill(2)}'
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class FxRtn(IFxRtn):
|
|
21
21
|
@property
|
|
22
22
|
def address(self):
|
|
23
|
-
return f
|
|
23
|
+
return f'/fxrtn/{str(self.index).zfill(2)}'
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class MainStereo(ILR):
|
|
27
27
|
@property
|
|
28
28
|
def address(self) -> str:
|
|
29
|
-
return
|
|
29
|
+
return '/main/st'
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
class MainMono(ILR):
|
|
33
33
|
@property
|
|
34
34
|
def address(self) -> str:
|
|
35
|
-
return
|
|
35
|
+
return '/main/m'
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class Matrix(ILR):
|
|
39
39
|
@property
|
|
40
40
|
def address(self) -> str:
|
|
41
|
-
return f
|
|
41
|
+
return f'/mtx/{str(self.index).zfill(2)}'
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class HeadAmp(IHeadAmp):
|
|
45
45
|
@property
|
|
46
46
|
def address(self):
|
|
47
|
-
return f
|
|
47
|
+
return f'/headamp/{str(self.index).zfill(3)}'
|
|
@@ -16,10 +16,10 @@ class IBus(abc.ABC):
|
|
|
16
16
|
self.logger = logger.getChild(self.__class__.__name__)
|
|
17
17
|
|
|
18
18
|
def getter(self, param: str):
|
|
19
|
-
return self._remote.query(f
|
|
19
|
+
return self._remote.query(f'{self.address}/{param}')
|
|
20
20
|
|
|
21
21
|
def setter(self, param: str, val: int):
|
|
22
|
-
self._remote.send(f
|
|
22
|
+
self._remote.send(f'{self.address}/{param}', val)
|
|
23
23
|
|
|
24
24
|
@abc.abstractmethod
|
|
25
25
|
def address(self):
|
|
@@ -39,12 +39,12 @@ class Bus(IBus):
|
|
|
39
39
|
Returns a Bus class of a kind.
|
|
40
40
|
"""
|
|
41
41
|
BUS_cls = type(
|
|
42
|
-
f
|
|
42
|
+
f'Bus{remote.kind}',
|
|
43
43
|
(cls,),
|
|
44
44
|
{
|
|
45
45
|
**{
|
|
46
46
|
_cls.__name__.lower(): type(
|
|
47
|
-
f
|
|
47
|
+
f'{_cls.__name__}{remote.kind}', (_cls, cls), {}
|
|
48
48
|
)(remote, index)
|
|
49
49
|
for _cls in (
|
|
50
50
|
Config,
|
|
@@ -56,11 +56,11 @@ class Bus(IBus):
|
|
|
56
56
|
Group,
|
|
57
57
|
)
|
|
58
58
|
},
|
|
59
|
-
|
|
59
|
+
'mute': mute_prop(),
|
|
60
60
|
},
|
|
61
61
|
)
|
|
62
62
|
return BUS_cls(remote, index)
|
|
63
63
|
|
|
64
64
|
@property
|
|
65
65
|
def address(self) -> str:
|
|
66
|
-
return f
|
|
66
|
+
return f'/bus/{self.index}'
|
|
@@ -15,10 +15,10 @@ class IConfig(abc.ABC):
|
|
|
15
15
|
self.logger = logger.getChild(self.__class__.__name__)
|
|
16
16
|
|
|
17
17
|
def getter(self, param: str):
|
|
18
|
-
return self._remote.query(f
|
|
18
|
+
return self._remote.query(f'{self.address}/{param}')
|
|
19
19
|
|
|
20
20
|
def setter(self, param: str, val: int):
|
|
21
|
-
self._remote.send(f
|
|
21
|
+
self._remote.send(f'{self.address}/{param}', val)
|
|
22
22
|
|
|
23
23
|
@abc.abstractmethod
|
|
24
24
|
def address(self):
|
|
@@ -36,37 +36,37 @@ class Config(IConfig):
|
|
|
36
36
|
Returns a Config class of a kind.
|
|
37
37
|
"""
|
|
38
38
|
LINKS_cls = _make_links_mixins[remote.kind.id_]
|
|
39
|
-
MUTEGROUP_cls = type(
|
|
40
|
-
MONITOR_cls = type(
|
|
39
|
+
MUTEGROUP_cls = type('MuteGroup', (Config.MuteGroup, cls), {})
|
|
40
|
+
MONITOR_cls = type('ConfigMonitor', (Config.Monitor, cls), {})
|
|
41
41
|
CONFIG_cls = type(
|
|
42
|
-
f
|
|
42
|
+
f'Config{remote.kind}',
|
|
43
43
|
(cls, LINKS_cls),
|
|
44
44
|
{
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
'mute_group': tuple(MUTEGROUP_cls(remote, i) for i in range(4)),
|
|
46
|
+
'monitor': MONITOR_cls(remote),
|
|
47
47
|
},
|
|
48
48
|
)
|
|
49
49
|
return CONFIG_cls(remote)
|
|
50
50
|
|
|
51
51
|
@property
|
|
52
52
|
def address(self) -> str:
|
|
53
|
-
return
|
|
53
|
+
return '/config'
|
|
54
54
|
|
|
55
55
|
@property
|
|
56
56
|
def amixenable(self) -> bool:
|
|
57
|
-
return self.getter(
|
|
57
|
+
return self.getter('mute')[0] == 1
|
|
58
58
|
|
|
59
59
|
@amixenable.setter
|
|
60
60
|
def amixenable(self, val: bool):
|
|
61
|
-
self.setter(
|
|
61
|
+
self.setter('amixenable', 1 if val else 0)
|
|
62
62
|
|
|
63
63
|
@property
|
|
64
64
|
def amixlock(self) -> bool:
|
|
65
|
-
return self.getter(
|
|
65
|
+
return self.getter('amixlock')[0] == 1
|
|
66
66
|
|
|
67
67
|
@amixlock.setter
|
|
68
68
|
def amixlock(self, val: bool):
|
|
69
|
-
self.setter(
|
|
69
|
+
self.setter('amixlock', 1 if val else 0)
|
|
70
70
|
|
|
71
71
|
class MuteGroup:
|
|
72
72
|
def __init__(self, remote, i):
|
|
@@ -76,128 +76,128 @@ class Config(IConfig):
|
|
|
76
76
|
@property
|
|
77
77
|
def address(self) -> str:
|
|
78
78
|
root = super(Config.MuteGroup, self).address
|
|
79
|
-
return f
|
|
79
|
+
return f'{root}/mute'
|
|
80
80
|
|
|
81
81
|
@property
|
|
82
82
|
def on(self) -> bool:
|
|
83
|
-
return self.getter(f
|
|
83
|
+
return self.getter(f'{self.i}')[0] == 1
|
|
84
84
|
|
|
85
85
|
@on.setter
|
|
86
86
|
def on(self, val: bool):
|
|
87
|
-
self.setter(f
|
|
87
|
+
self.setter(f'{self.i}', 1 if val else 0)
|
|
88
88
|
|
|
89
89
|
class Monitor:
|
|
90
90
|
@property
|
|
91
91
|
def address(self) -> str:
|
|
92
92
|
root = super(Config.Monitor, self).address
|
|
93
|
-
return f
|
|
93
|
+
return f'{root}/solo'
|
|
94
94
|
|
|
95
95
|
@property
|
|
96
96
|
@util.db_from
|
|
97
97
|
def level(self) -> float:
|
|
98
|
-
return self.getter(
|
|
98
|
+
return self.getter('level')[0]
|
|
99
99
|
|
|
100
100
|
@level.setter
|
|
101
101
|
@util.db_to
|
|
102
102
|
def level(self, val: float):
|
|
103
|
-
self.setter(
|
|
103
|
+
self.setter('level', val)
|
|
104
104
|
|
|
105
105
|
@property
|
|
106
106
|
def source(self) -> int:
|
|
107
|
-
return int(self.getter(
|
|
107
|
+
return int(self.getter('source')[0])
|
|
108
108
|
|
|
109
109
|
@source.setter
|
|
110
110
|
def source(self, val: int):
|
|
111
|
-
self.setter(
|
|
111
|
+
self.setter('source', val)
|
|
112
112
|
|
|
113
113
|
@property
|
|
114
114
|
def sourcetrim(self) -> float:
|
|
115
|
-
return round(util.lin_get(-18, 18, self.getter(
|
|
115
|
+
return round(util.lin_get(-18, 18, self.getter('sourcetrim')[0]), 1)
|
|
116
116
|
|
|
117
117
|
@sourcetrim.setter
|
|
118
118
|
def sourcetrim(self, val: float):
|
|
119
119
|
if not -18 <= val <= 18:
|
|
120
120
|
self.logger.warning(
|
|
121
|
-
f
|
|
121
|
+
f'sourcetrim got {val}, expected value in range -18.0 to 18.0'
|
|
122
122
|
)
|
|
123
|
-
self.setter(
|
|
123
|
+
self.setter('sourcetrim', util.lin_set(-18, 18, val))
|
|
124
124
|
|
|
125
125
|
@property
|
|
126
126
|
def chmode(self) -> bool:
|
|
127
|
-
return self.getter(
|
|
127
|
+
return self.getter('chmode')[0] == 1
|
|
128
128
|
|
|
129
129
|
@chmode.setter
|
|
130
130
|
def chmode(self, val: bool):
|
|
131
|
-
self.setter(
|
|
131
|
+
self.setter('chmode', 1 if val else 0)
|
|
132
132
|
|
|
133
133
|
@property
|
|
134
134
|
def busmode(self) -> bool:
|
|
135
|
-
return self.getter(
|
|
135
|
+
return self.getter('busmode')[0] == 1
|
|
136
136
|
|
|
137
137
|
@busmode.setter
|
|
138
138
|
def busmode(self, val: bool):
|
|
139
|
-
self.setter(
|
|
139
|
+
self.setter('busmode', 1 if val else 0)
|
|
140
140
|
|
|
141
141
|
@property
|
|
142
142
|
def dimgain(self) -> int:
|
|
143
|
-
return int(util.lin_get(-40, 0, self.getter(
|
|
143
|
+
return int(util.lin_get(-40, 0, self.getter('dimatt')[0]))
|
|
144
144
|
|
|
145
145
|
@dimgain.setter
|
|
146
146
|
def dimgain(self, val: int):
|
|
147
147
|
if not -40 <= val <= 0:
|
|
148
148
|
self.logger.warning(
|
|
149
|
-
f
|
|
149
|
+
f'dimgain got {val}, expected value in range -40 to 0'
|
|
150
150
|
)
|
|
151
|
-
self.setter(
|
|
151
|
+
self.setter('dimatt', util.lin_set(-40, 0, val))
|
|
152
152
|
|
|
153
153
|
@property
|
|
154
154
|
def dim(self) -> bool:
|
|
155
|
-
return self.getter(
|
|
155
|
+
return self.getter('dim')[0] == 1
|
|
156
156
|
|
|
157
157
|
@dim.setter
|
|
158
158
|
def dim(self, val: bool):
|
|
159
|
-
self.setter(
|
|
159
|
+
self.setter('dim', 1 if val else 0)
|
|
160
160
|
|
|
161
161
|
@property
|
|
162
162
|
def mono(self) -> bool:
|
|
163
|
-
return self.getter(
|
|
163
|
+
return self.getter('mono')[0] == 1
|
|
164
164
|
|
|
165
165
|
@mono.setter
|
|
166
166
|
def mono(self, val: bool):
|
|
167
|
-
self.setter(
|
|
167
|
+
self.setter('mono', 1 if val else 0)
|
|
168
168
|
|
|
169
169
|
@property
|
|
170
170
|
def mute(self) -> bool:
|
|
171
|
-
return self.getter(
|
|
171
|
+
return self.getter('mute')[0] == 1
|
|
172
172
|
|
|
173
173
|
@mute.setter
|
|
174
174
|
def mute(self, val: bool):
|
|
175
|
-
self.setter(
|
|
175
|
+
self.setter('mute', 1 if val else 0)
|
|
176
176
|
|
|
177
177
|
@property
|
|
178
178
|
def dimfpl(self) -> bool:
|
|
179
|
-
return self.getter(
|
|
179
|
+
return self.getter('dimfpl')[0] == 1
|
|
180
180
|
|
|
181
181
|
@dimfpl.setter
|
|
182
182
|
def dimfpl(self, val: bool):
|
|
183
|
-
self.setter(
|
|
183
|
+
self.setter('dimfpl', 1 if val else 0)
|
|
184
184
|
|
|
185
185
|
|
|
186
186
|
def _make_links_mixin(kind):
|
|
187
187
|
"""Creates a links mixin"""
|
|
188
188
|
return type(
|
|
189
|
-
f
|
|
189
|
+
f'Links{kind}',
|
|
190
190
|
(),
|
|
191
191
|
{
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
'link_eq': bool_prop('linkcfg/eq'),
|
|
193
|
+
'link_dyn': bool_prop('linkcfg/dyn'),
|
|
194
|
+
'link_fader_mute': bool_prop('linkcfg/fdrmute'),
|
|
195
195
|
**{
|
|
196
|
-
f
|
|
196
|
+
f'chlink{i}_{i+1}': bool_prop(f'chlink/{i}-{i+1}')
|
|
197
197
|
for i in range(1, kind.num_strip, 2)
|
|
198
198
|
},
|
|
199
199
|
**{
|
|
200
|
-
f
|
|
200
|
+
f'buslink{i}_{i+1}': bool_prop(f'buslink/{i}-{i+1}')
|
|
201
201
|
for i in range(1, kind.num_bus, 2)
|
|
202
202
|
},
|
|
203
203
|
},
|
|
@@ -13,10 +13,10 @@ class IDCA(abc.ABC):
|
|
|
13
13
|
self.logger = logger.getChild(self.__class__.__name__)
|
|
14
14
|
|
|
15
15
|
def getter(self, param: str) -> tuple:
|
|
16
|
-
return self._remote.query(f
|
|
16
|
+
return self._remote.query(f'{self.address}/{param}')
|
|
17
17
|
|
|
18
18
|
def setter(self, param: str, val: int):
|
|
19
|
-
self._remote.send(f
|
|
19
|
+
self._remote.send(f'{self.address}/{param}', val)
|
|
20
20
|
|
|
21
21
|
@abc.abstractmethod
|
|
22
22
|
def address(self):
|
|
@@ -28,15 +28,15 @@ class DCA(IDCA):
|
|
|
28
28
|
|
|
29
29
|
@property
|
|
30
30
|
def address(self) -> str:
|
|
31
|
-
return f
|
|
31
|
+
return f'/dca/{self.index}'
|
|
32
32
|
|
|
33
33
|
@property
|
|
34
34
|
def on(self) -> bool:
|
|
35
|
-
return self.getter(
|
|
35
|
+
return self.getter('on')[0] == 1
|
|
36
36
|
|
|
37
37
|
@on.setter
|
|
38
38
|
def on(self, val: bool):
|
|
39
|
-
self.setter(
|
|
39
|
+
self.setter('on', 1 if val else 0)
|
|
40
40
|
|
|
41
41
|
@property
|
|
42
42
|
def mute(self) -> bool:
|
|
@@ -48,16 +48,16 @@ class DCA(IDCA):
|
|
|
48
48
|
|
|
49
49
|
@property
|
|
50
50
|
def name(self) -> str:
|
|
51
|
-
return self.getter(
|
|
51
|
+
return self.getter('config/name')[0]
|
|
52
52
|
|
|
53
53
|
@name.setter
|
|
54
54
|
def name(self, val: str):
|
|
55
|
-
self.setter(
|
|
55
|
+
self.setter('config/name', val)
|
|
56
56
|
|
|
57
57
|
@property
|
|
58
58
|
def color(self) -> int:
|
|
59
|
-
return self.getter(
|
|
59
|
+
return self.getter('config/color')[0]
|
|
60
60
|
|
|
61
61
|
@color.setter
|
|
62
62
|
def color(self, val: int):
|
|
63
|
-
self.setter(
|
|
63
|
+
self.setter('config/color', val)
|