xair-api 2.3.2__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.
@@ -1,25 +1,24 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: xair-api
3
- Version: 2.3.2
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: onyx-and-iris
6
+ Author: Onyx and Iris
8
7
  Author-email: code@onyxandiris.online
9
- Requires-Python: >=3.10,<4.0
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
- Requires-Dist: python-osc (>=1.8.0,<2.0.0)
15
- Requires-Dist: tomli (>=2.0.1,<3.0.0) ; python_version < "3.11"
16
- Project-URL: Repository, https://github.com/onyx-and-iris/xair-api-python
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Dist: python-osc (>=1.9.3,<2.0.0)
16
+ Requires-Dist: tomli (>=2.0.1,<3.0) ; python_version < "3.11"
17
17
  Description-Content-Type: text/markdown
18
18
 
19
19
  [![PyPI version](https://badge.fury.io/py/xair-api.svg)](https://badge.fury.io/py/xair-api)
20
20
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/onyx-and-iris/xair-api-python/blob/dev/LICENSE)
21
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
22
- [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
21
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
23
22
  ![Tests Status](./tests/xair/MR18.svg?dummy=8484744)
24
23
 
25
24
  # Xair API
@@ -58,18 +57,18 @@ import xair_api
58
57
 
59
58
 
60
59
  def main():
61
- kind_id = "XR18"
62
- ip = "<ip address>"
60
+ kind_id = 'XR18'
61
+ ip = '<ip address>'
63
62
 
64
63
  with xair_api.connect(kind_id, ip=ip) as mixer:
65
- mixer.strip[8].config.name = "sm7b"
64
+ mixer.strip[8].config.name = 'sm7b'
66
65
  mixer.strip[8].mix.on = True
67
66
  print(
68
- f"strip 09 ({mixer.strip[8].config.name}) on has been set to {mixer.strip[8].mix.on}"
67
+ f'strip 09 ({mixer.strip[8].config.name}) on has been set to {mixer.strip[8].mix.on}'
69
68
  )
70
69
 
71
70
 
72
- if __name__ == "__main__":
71
+ if __name__ == '__main__':
73
72
  main()
74
73
  ```
75
74
 
@@ -132,6 +131,10 @@ A class representing auxreturn channel
132
131
 
133
132
  A class representing the main config settings
134
133
 
134
+ `mixer.headamp`
135
+
136
+ A class representing the channel preamps (phantom power/gain).
137
+
135
138
  ### `LR`
136
139
 
137
140
  Contains the subclasses:
@@ -162,6 +165,13 @@ Contains the subclasses:
162
165
  Contains the subclasses:
163
166
  (`Config`, `Preamp`, `EQ`, `Mix`, `Group`, `Send`)
164
167
 
168
+ ### `HeadAmp`
169
+
170
+ The following properties are available:
171
+
172
+ - `gain`: float, from -12.0 to 60.0
173
+ - `phantom`: bool
174
+
165
175
  ### `Subclasses`
166
176
 
167
177
  For each subclass the corresponding properties are available.
@@ -322,8 +332,8 @@ Send an OSC command directly to the mixer
322
332
  for example:
323
333
 
324
334
  ```python
325
- mixer.send("/ch/01/mix/on", 1)
326
- mixer.send("/bus/2/config/name", "somename")
335
+ mixer.send('/ch/01/mix/on', 1)
336
+ mixer.send('/bus/2/config/name', 'somename')
327
337
  ```
328
338
 
329
339
  Query the value of a command:
@@ -333,7 +343,7 @@ Query the value of a command:
333
343
  for example:
334
344
 
335
345
  ```python
336
- print(mixer.query("/ch/01/mix/on"))
346
+ print(mixer.query('/ch/01/mix/on'))
337
347
  ```
338
348
 
339
349
  ### Errors
@@ -1,7 +1,6 @@
1
1
  [![PyPI version](https://badge.fury.io/py/xair-api.svg)](https://badge.fury.io/py/xair-api)
2
2
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/onyx-and-iris/xair-api-python/blob/dev/LICENSE)
3
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
4
- [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
3
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
5
4
  ![Tests Status](./tests/xair/MR18.svg?dummy=8484744)
6
5
 
7
6
  # Xair API
@@ -40,18 +39,18 @@ import xair_api
40
39
 
41
40
 
42
41
  def main():
43
- kind_id = "XR18"
44
- ip = "<ip address>"
42
+ kind_id = 'XR18'
43
+ ip = '<ip address>'
45
44
 
46
45
  with xair_api.connect(kind_id, ip=ip) as mixer:
47
- mixer.strip[8].config.name = "sm7b"
46
+ mixer.strip[8].config.name = 'sm7b'
48
47
  mixer.strip[8].mix.on = True
49
48
  print(
50
- f"strip 09 ({mixer.strip[8].config.name}) on has been set to {mixer.strip[8].mix.on}"
49
+ f'strip 09 ({mixer.strip[8].config.name}) on has been set to {mixer.strip[8].mix.on}'
51
50
  )
52
51
 
53
52
 
54
- if __name__ == "__main__":
53
+ if __name__ == '__main__':
55
54
  main()
56
55
  ```
57
56
 
@@ -114,6 +113,10 @@ A class representing auxreturn channel
114
113
 
115
114
  A class representing the main config settings
116
115
 
116
+ `mixer.headamp`
117
+
118
+ A class representing the channel preamps (phantom power/gain).
119
+
117
120
  ### `LR`
118
121
 
119
122
  Contains the subclasses:
@@ -144,6 +147,13 @@ Contains the subclasses:
144
147
  Contains the subclasses:
145
148
  (`Config`, `Preamp`, `EQ`, `Mix`, `Group`, `Send`)
146
149
 
150
+ ### `HeadAmp`
151
+
152
+ The following properties are available:
153
+
154
+ - `gain`: float, from -12.0 to 60.0
155
+ - `phantom`: bool
156
+
147
157
  ### `Subclasses`
148
158
 
149
159
  For each subclass the corresponding properties are available.
@@ -304,8 +314,8 @@ Send an OSC command directly to the mixer
304
314
  for example:
305
315
 
306
316
  ```python
307
- mixer.send("/ch/01/mix/on", 1)
308
- mixer.send("/bus/2/config/name", "somename")
317
+ mixer.send('/ch/01/mix/on', 1)
318
+ mixer.send('/bus/2/config/name', 'somename')
309
319
  ```
310
320
 
311
321
  Query the value of a command:
@@ -315,7 +325,7 @@ Query the value of a command:
315
325
  for example:
316
326
 
317
327
  ```python
318
- print(mixer.query("/ch/01/mix/on"))
328
+ print(mixer.query('/ch/01/mix/on'))
319
329
  ```
320
330
 
321
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
+ ]
@@ -1,3 +1,3 @@
1
1
  from .xair import request_remote_obj as connect
2
2
 
3
- _ALL__ = ["connect"]
3
+ _ALL__ = ['connect']
@@ -1,4 +1,5 @@
1
1
  from .bus import Bus as IBus
2
+ from .headamp import HeadAmp as IHeadAmp
2
3
  from .lr import LR as ILR
3
4
  from .rtn import AuxRtn as IAuxRtn
4
5
  from .rtn import FxRtn as IFxRtn
@@ -7,34 +8,40 @@ from .rtn import FxRtn as IFxRtn
7
8
  class Bus(IBus):
8
9
  @property
9
10
  def address(self):
10
- return f"/bus/{str(self.index).zfill(2)}"
11
+ return f'/bus/{str(self.index).zfill(2)}'
11
12
 
12
13
 
13
14
  class AuxRtn(IAuxRtn):
14
15
  @property
15
16
  def address(self):
16
- return f"/auxin/{str(self.index).zfill(2)}"
17
+ return f'/auxin/{str(self.index).zfill(2)}'
17
18
 
18
19
 
19
20
  class FxRtn(IFxRtn):
20
21
  @property
21
22
  def address(self):
22
- return f"/fxrtn/{str(self.index).zfill(2)}"
23
+ return f'/fxrtn/{str(self.index).zfill(2)}'
23
24
 
24
25
 
25
26
  class MainStereo(ILR):
26
27
  @property
27
28
  def address(self) -> str:
28
- return "/main/st"
29
+ return '/main/st'
29
30
 
30
31
 
31
32
  class MainMono(ILR):
32
33
  @property
33
34
  def address(self) -> str:
34
- return "/main/m"
35
+ return '/main/m'
35
36
 
36
37
 
37
38
  class Matrix(ILR):
38
39
  @property
39
40
  def address(self) -> str:
40
- return f"/mtx/{str(self.index).zfill(2)}"
41
+ return f'/mtx/{str(self.index).zfill(2)}'
42
+
43
+
44
+ class HeadAmp(IHeadAmp):
45
+ @property
46
+ def address(self):
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"{self.address}/{param}")
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"{self.address}/{param}", val)
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"Bus{remote.kind}",
42
+ f'Bus{remote.kind}',
43
43
  (cls,),
44
44
  {
45
45
  **{
46
46
  _cls.__name__.lower(): type(
47
- f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
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
- "mute": mute_prop(),
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"/bus/{self.index}"
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"{self.address}/{param}")
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"{self.address}/{param}", val)
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("MuteGroup", (Config.MuteGroup, cls), {})
40
- MONITOR_cls = type("ConfigMonitor", (Config.Monitor, cls), {})
39
+ MUTEGROUP_cls = type('MuteGroup', (Config.MuteGroup, cls), {})
40
+ MONITOR_cls = type('ConfigMonitor', (Config.Monitor, cls), {})
41
41
  CONFIG_cls = type(
42
- f"Config{remote.kind}",
42
+ f'Config{remote.kind}',
43
43
  (cls, LINKS_cls),
44
44
  {
45
- "mute_group": tuple(MUTEGROUP_cls(remote, i) for i in range(4)),
46
- "monitor": MONITOR_cls(remote),
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 "/config"
53
+ return '/config'
54
54
 
55
55
  @property
56
56
  def amixenable(self) -> bool:
57
- return self.getter("mute")[0] == 1
57
+ return self.getter('mute')[0] == 1
58
58
 
59
59
  @amixenable.setter
60
60
  def amixenable(self, val: bool):
61
- self.setter("amixenable", 1 if val else 0)
61
+ self.setter('amixenable', 1 if val else 0)
62
62
 
63
63
  @property
64
64
  def amixlock(self) -> bool:
65
- return self.getter("amixlock")[0] == 1
65
+ return self.getter('amixlock')[0] == 1
66
66
 
67
67
  @amixlock.setter
68
68
  def amixlock(self, val: bool):
69
- self.setter("amixlock", 1 if val else 0)
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"{root}/mute"
79
+ return f'{root}/mute'
80
80
 
81
81
  @property
82
82
  def on(self) -> bool:
83
- return self.getter(f"{self.i}")[0] == 1
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"{self.i}", 1 if val else 0)
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"{root}/solo"
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("level")[0]
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("level", val)
103
+ self.setter('level', val)
104
104
 
105
105
  @property
106
106
  def source(self) -> int:
107
- return int(self.getter("source")[0])
107
+ return int(self.getter('source')[0])
108
108
 
109
109
  @source.setter
110
110
  def source(self, val: int):
111
- self.setter("source", val)
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("sourcetrim")[0]), 1)
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"sourcetrim got {val}, expected value in range -18.0 to 18.0"
121
+ f'sourcetrim got {val}, expected value in range -18.0 to 18.0'
122
122
  )
123
- self.setter("sourcetrim", util.lin_set(-18, 18, val))
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("chmode")[0] == 1
127
+ return self.getter('chmode')[0] == 1
128
128
 
129
129
  @chmode.setter
130
130
  def chmode(self, val: bool):
131
- self.setter("chmode", 1 if val else 0)
131
+ self.setter('chmode', 1 if val else 0)
132
132
 
133
133
  @property
134
134
  def busmode(self) -> bool:
135
- return self.getter("busmode")[0] == 1
135
+ return self.getter('busmode')[0] == 1
136
136
 
137
137
  @busmode.setter
138
138
  def busmode(self, val: bool):
139
- self.setter("busmode", 1 if val else 0)
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("dimatt")[0]))
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"dimgain got {val}, expected value in range -40 to 0"
149
+ f'dimgain got {val}, expected value in range -40 to 0'
150
150
  )
151
- self.setter("dimatt", util.lin_set(-40, 0, val))
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("dim")[0] == 1
155
+ return self.getter('dim')[0] == 1
156
156
 
157
157
  @dim.setter
158
158
  def dim(self, val: bool):
159
- self.setter("dim", 1 if val else 0)
159
+ self.setter('dim', 1 if val else 0)
160
160
 
161
161
  @property
162
162
  def mono(self) -> bool:
163
- return self.getter("mono")[0] == 1
163
+ return self.getter('mono')[0] == 1
164
164
 
165
165
  @mono.setter
166
166
  def mono(self, val: bool):
167
- self.setter("mono", 1 if val else 0)
167
+ self.setter('mono', 1 if val else 0)
168
168
 
169
169
  @property
170
170
  def mute(self) -> bool:
171
- return self.getter("mute")[0] == 1
171
+ return self.getter('mute')[0] == 1
172
172
 
173
173
  @mute.setter
174
174
  def mute(self, val: bool):
175
- self.setter("mute", 1 if val else 0)
175
+ self.setter('mute', 1 if val else 0)
176
176
 
177
177
  @property
178
178
  def dimfpl(self) -> bool:
179
- return self.getter("dimfpl")[0] == 1
179
+ return self.getter('dimfpl')[0] == 1
180
180
 
181
181
  @dimfpl.setter
182
182
  def dimfpl(self, val: bool):
183
- self.setter("dimfpl", 1 if val else 0)
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"Links{kind}",
189
+ f'Links{kind}',
190
190
  (),
191
191
  {
192
- "link_eq": bool_prop("linkcfg/eq"),
193
- "link_dyn": bool_prop("linkcfg/dyn"),
194
- "link_fader_mute": bool_prop("linkcfg/fdrmute"),
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"chlink{i}_{i+1}": bool_prop(f"chlink/{i}-{i+1}")
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"buslink{i}_{i+1}": bool_prop(f"buslink/{i}-{i+1}")
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
  },