mx-remote 2.0.0__py3-none-any.whl
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.
- mx_remote/Interface.py +1656 -0
- mx_remote/Uid.py +137 -0
- mx_remote/__init__.py +12 -0
- mx_remote/api/BayConfig.py +53 -0
- mx_remote/api/__init__.py +8 -0
- mx_remote/const.py +20 -0
- mx_remote/main.py +117 -0
- mx_remote/proto/BayConfig.py +104 -0
- mx_remote/proto/Constants.py +382 -0
- mx_remote/proto/Data.py +142 -0
- mx_remote/proto/Factory.py +168 -0
- mx_remote/proto/FrameAmpDolbySettings.py +51 -0
- mx_remote/proto/FrameAmpZoneSettings.py +123 -0
- mx_remote/proto/FrameBase.py +80 -0
- mx_remote/proto/FrameBayConfig.py +47 -0
- mx_remote/proto/FrameBayConfigSecondary.py +43 -0
- mx_remote/proto/FrameBayHide.py +53 -0
- mx_remote/proto/FrameBayStatus.py +51 -0
- mx_remote/proto/FrameConnectStatus.py +38 -0
- mx_remote/proto/FrameDiscover.py +21 -0
- mx_remote/proto/FrameEDID.py +20 -0
- mx_remote/proto/FrameEDIDProfile.py +24 -0
- mx_remote/proto/FrameFilterStatus.py +39 -0
- mx_remote/proto/FrameFirmwareVersion.py +40 -0
- mx_remote/proto/FrameHeader.py +92 -0
- mx_remote/proto/FrameHello.py +77 -0
- mx_remote/proto/FrameLinks.py +45 -0
- mx_remote/proto/FrameMeshOperation.py +66 -0
- mx_remote/proto/FrameMirrorStatus.py +49 -0
- mx_remote/proto/FrameNetworkStatus.py +163 -0
- mx_remote/proto/FramePDUState.py +71 -0
- mx_remote/proto/FramePowerChange.py +38 -0
- mx_remote/proto/FrameRCAction.py +50 -0
- mx_remote/proto/FrameRCIr.py +17 -0
- mx_remote/proto/FrameRCKey.py +40 -0
- mx_remote/proto/FrameReboot.py +26 -0
- mx_remote/proto/FrameRoutingChange.py +66 -0
- mx_remote/proto/FrameSetName.py +27 -0
- mx_remote/proto/FrameSignalStatus.py +46 -0
- mx_remote/proto/FrameSignalStatusNew.py +285 -0
- mx_remote/proto/FrameSysTemperature.py +50 -0
- mx_remote/proto/FrameTXRCAction.py +58 -0
- mx_remote/proto/FrameTopology.py +36 -0
- mx_remote/proto/FrameV2IPDeviceConfiguration.py +86 -0
- mx_remote/proto/FrameV2IPLink.py +16 -0
- mx_remote/proto/FrameV2IPSetMaster.py +16 -0
- mx_remote/proto/FrameV2IPSourceSwitch.py +84 -0
- mx_remote/proto/FrameV2IPSources.py +43 -0
- mx_remote/proto/FrameV2IPStats.py +55 -0
- mx_remote/proto/FrameV2IPStreamDetails.py +46 -0
- mx_remote/proto/FrameVolume.py +62 -0
- mx_remote/proto/FrameVolumeDown.py +28 -0
- mx_remote/proto/FrameVolumeSet.py +81 -0
- mx_remote/proto/FrameVolumeUp.py +28 -0
- mx_remote/proto/LinkConfig.py +121 -0
- mx_remote/proto/PDUState.py +95 -0
- mx_remote/proto/Svd.py +69 -0
- mx_remote/proto/V2IPConfig.py +71 -0
- mx_remote/proto/V2IPStats.py +126 -0
- mx_remote/proto/__init__.py +8 -0
- mx_remote/proto/svd.csv +157 -0
- mx_remote/remote/Bay.py +923 -0
- mx_remote/remote/ConnectionAsync.py +132 -0
- mx_remote/remote/Device.py +530 -0
- mx_remote/remote/Link.py +187 -0
- mx_remote/remote/PDU.py +152 -0
- mx_remote/remote/Remote.py +300 -0
- mx_remote/remote/State.py +28 -0
- mx_remote/remote/V2IP.py +83 -0
- mx_remote-2.0.0.dist-info/METADATA +90 -0
- mx_remote-2.0.0.dist-info/RECORD +74 -0
- mx_remote-2.0.0.dist-info/WHEEL +4 -0
- mx_remote-2.0.0.dist-info/entry_points.txt +2 -0
- mx_remote-2.0.0.dist-info/licenses/LICENSE +11 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
##################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp.eu) ##
|
|
5
|
+
## copyright (c) 2024 Op den Kamp IT Solutions ##
|
|
6
|
+
##################################################
|
|
7
|
+
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
MXR_PROTOCOL_VERSION = 20
|
|
12
|
+
|
|
13
|
+
MXR_DEVICE_FEATURE_IR_RX = (1 << 0)
|
|
14
|
+
MXR_DEVICE_FEATURE_IR_TX = (1 << 1)
|
|
15
|
+
MXR_DEVICE_FEATURE_CEC = (1 << 2)
|
|
16
|
+
MXR_DEVICE_FEATURE_V2IP_SOURCE = (1 << 3)
|
|
17
|
+
MXR_DEVICE_FEATURE_V2IP_SINK = (1 << 4)
|
|
18
|
+
MXR_DEVICE_FEATURE_VIDEO_ROUTING = (1 << 5)
|
|
19
|
+
MXR_DEVICE_FEATURE_AUDIO_ROUTING = (1 << 6)
|
|
20
|
+
MXR_DEVICE_FEATURE_VOLUME_CONTROL = (1 << 7)
|
|
21
|
+
MXR_DEVICE_FEATURE_AUDIO_RETURN = (1 << 8)
|
|
22
|
+
MXR_DEVICE_FEATURE_REMOTE_CONTROL = (1 << 9)
|
|
23
|
+
MXR_DEVICE_FEATURE_SETUP_COMPLETED = (1 << 10)
|
|
24
|
+
MXR_DEVICE_FEATURE_MESH_MASTER = (1 << 11)
|
|
25
|
+
MXR_DEVICE_FEATURE_STATUS_NOTIFY = (1 << 12)
|
|
26
|
+
MXR_DEVICE_FEATURE_STATUS_WARNING = (1 << 13)
|
|
27
|
+
MXR_DEVICE_FEATURE_STATUS_ERROR = (1 << 14)
|
|
28
|
+
MXR_DEVICE_FEATURE_STATUS_REBOOTING = (1 << 15)
|
|
29
|
+
MXR_DEVICE_FEATURE_MESH_MEMBER = (1 << 16)
|
|
30
|
+
MXR_DEVICE_FEATURE_AUDIO_AMPLIFIER = (1 << 17)
|
|
31
|
+
MXR_DEVICE_FEATURE_BOOTING = (1 << 18)
|
|
32
|
+
MXR_DEVICE_FEATURE_MANAGER = (1 << 19)
|
|
33
|
+
MXR_DEVICE_FEATURE_BOOT_BIT = (1 << 31)
|
|
34
|
+
|
|
35
|
+
MX_BAY_FEATURE_HDMI_OUT = (1 << 0)
|
|
36
|
+
MX_BAY_FEATURE_HDMI_IN = (1 << 1)
|
|
37
|
+
MX_BAY_FEATURE_AUDIO_DIG_OUT = (1 << 2)
|
|
38
|
+
MX_BAY_FEATURE_AUDIO_DIG_IN = (1 << 3)
|
|
39
|
+
MX_BAY_FEATURE_AUDIO_ANA_OUT = (1 << 4)
|
|
40
|
+
MX_BAY_FEATURE_AUDIO_ANA_IN = (1 << 5)
|
|
41
|
+
MX_BAY_FEATURE_IR_IN = (1 << 6)
|
|
42
|
+
MX_BAY_FEATURE_IR_OUT = (1 << 7)
|
|
43
|
+
MX_BAY_FEATURE_AUDIO_AMP_OUT = (1 << 8)
|
|
44
|
+
MX_BAY_FEATURE_RC_OUT = (1 << 9)
|
|
45
|
+
MX_BAY_FEATURE_RC_IN = (1 << 10)
|
|
46
|
+
MX_BAY_FEATURE_DOLBY = (1 << 11)
|
|
47
|
+
MX_BAY_FEATURE_AUTO_OFF = (1 << 12)
|
|
48
|
+
MX_BAY_FEATURE_V2IP_SOURCE_REMOTE = (1 << 13)
|
|
49
|
+
MX_BAY_FEATURE_V2IP_SINK_REMOTE = (1 << 14)
|
|
50
|
+
MX_BAY_FEATURE_V2IP_SOURCE_LOCAL = (1 << 15)
|
|
51
|
+
MX_BAY_FEATURE_V2IP_SINK_LOCAL = (1 << 16)
|
|
52
|
+
MX_BAY_FEATURE_DOLBY_IN_POS = 24
|
|
53
|
+
|
|
54
|
+
MX_LINK_FEATURE_NONE = 0
|
|
55
|
+
MX_LINK_FEATURE_VIDEO_HDMI = (1 << 0)
|
|
56
|
+
MX_LINK_FEATURE_AUDIO_OPTICAL = (1 << 1)
|
|
57
|
+
MX_LINK_FEATURE_AUDIO_ANALOG = (1 << 2)
|
|
58
|
+
MX_LINK_FEATURE_IR = (1 << 3)
|
|
59
|
+
MX_LINK_FEATURE_RC = (1 << 4)
|
|
60
|
+
|
|
61
|
+
class RCAction(Enum):
|
|
62
|
+
ACTION_POWER_TOGGLE = 0
|
|
63
|
+
ACTION_POWER_ON = 1
|
|
64
|
+
ACTION_POWER_OFF = 2
|
|
65
|
+
ACTION_VOLUME_DOWN = 3
|
|
66
|
+
ACTION_VOLUME_UP = 4
|
|
67
|
+
ACTION_VOLUME_MUTE = 5
|
|
68
|
+
|
|
69
|
+
class RCKey(Enum):
|
|
70
|
+
KEY_0 = 0
|
|
71
|
+
KEY_1 = 1
|
|
72
|
+
KEY_2 = 2
|
|
73
|
+
KEY_3 = 3
|
|
74
|
+
KEY_4 = 4
|
|
75
|
+
KEY_5 = 5
|
|
76
|
+
KEY_6 = 6
|
|
77
|
+
KEY_7 = 7
|
|
78
|
+
KEY_8 = 8
|
|
79
|
+
KEY_9 = 9
|
|
80
|
+
KEY_SELECT = 10
|
|
81
|
+
KEY_BACK = 11
|
|
82
|
+
KEY_UP = 12
|
|
83
|
+
KEY_DOWN = 13
|
|
84
|
+
KEY_LEFT = 14
|
|
85
|
+
KEY_RIGHT = 15
|
|
86
|
+
KEY_MENU = 16
|
|
87
|
+
KEY_CONTENT_MENU = 17
|
|
88
|
+
KEY_CHANNEL_UP = 18
|
|
89
|
+
KEY_CHANNEL_DOWN = 19
|
|
90
|
+
KEY_PLAY = 20
|
|
91
|
+
KEY_PAUSE = 21
|
|
92
|
+
KEY_STOP = 22
|
|
93
|
+
KEY_RECORD = 23
|
|
94
|
+
KEY_FAST_FORWARD = 24
|
|
95
|
+
KEY_REWIND = 25
|
|
96
|
+
KEY_RED = 26
|
|
97
|
+
KEY_GREEN = 27
|
|
98
|
+
KEY_YELLOW = 28
|
|
99
|
+
KEY_BLUE = 29
|
|
100
|
+
KEY_HELP = 30
|
|
101
|
+
KEY_INFORMATION = 31
|
|
102
|
+
KEY_TEXT = 32
|
|
103
|
+
KEY_GUIDE = 33
|
|
104
|
+
KEY_VIDEO_ON_DEMAND = 34
|
|
105
|
+
KEY_PREVIOUS_CHANNEL = 80
|
|
106
|
+
KEY_3D_MODE = 81
|
|
107
|
+
KEY_SUBTITLE = 82
|
|
108
|
+
KEY_SOUND_SELECT = 83
|
|
109
|
+
KEY_INPUT_SELECT = 84
|
|
110
|
+
KEY_EJECT = 85
|
|
111
|
+
KEY_NEXT_CHAPTER = 86
|
|
112
|
+
KEY_PREVIOUS_CHAPTER = 87
|
|
113
|
+
KEY_CUSTOM_CEC = 1280
|
|
114
|
+
KEY_INTERACTIVE = 128
|
|
115
|
+
KEY_SEARCH = 129
|
|
116
|
+
KEY_SKY = 130
|
|
117
|
+
KEY_CUSTOM_SKY = 2048
|
|
118
|
+
|
|
119
|
+
class BayStatusMask:
|
|
120
|
+
FAULT = (1 << 0)
|
|
121
|
+
HIDDEN = (1 << 1)
|
|
122
|
+
POWERED = (1 << 2)
|
|
123
|
+
SIGNAL_DETECTED = (1 << 3)
|
|
124
|
+
HPD_DETECTED = (1 << 4)
|
|
125
|
+
SIGNAL_SCRAMBLED =(1 << 5)
|
|
126
|
+
HDBT_CONNECTED = (1 << 6)
|
|
127
|
+
CEC_DETECTED = (1 << 7)
|
|
128
|
+
POWERED_ON = (1 << 8)
|
|
129
|
+
POWERED_OFF = (1 << 9)
|
|
130
|
+
AUDIO_ARC_HDMI = (1 << 10)
|
|
131
|
+
AUDIO_ARC_OPTICAL = (1 << 11)
|
|
132
|
+
AUDIO_ARC_ANALOG = (1 << 12)
|
|
133
|
+
OFFLINE = (1 << 13)
|
|
134
|
+
DECODER_DISABLED = (1 << 14)
|
|
135
|
+
ENCODER_DISABLED = (1 << 15)
|
|
136
|
+
|
|
137
|
+
def __init__(self, mask:int):
|
|
138
|
+
self._mask = mask
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def fault(self) -> bool:
|
|
142
|
+
# bay is faulty
|
|
143
|
+
return (self._mask & self.FAULT) != 0
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def hidden(self) -> bool:
|
|
147
|
+
# bay is hidden
|
|
148
|
+
return (self._mask & self.HIDDEN) != 0
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def powered(self) -> bool:
|
|
152
|
+
# PoE powered on
|
|
153
|
+
return (self._mask & self.POWERED) != 0
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def signal_detected(self) -> bool:
|
|
157
|
+
# signal detected
|
|
158
|
+
return (self._mask & self.SIGNAL_DETECTED) != 0
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def hpd_detected(self) -> bool:
|
|
162
|
+
# hotplug detected
|
|
163
|
+
return (self._mask & self.HPD_DETECTED) != 0
|
|
164
|
+
|
|
165
|
+
@property
|
|
166
|
+
def hdbt_connected(self) -> bool:
|
|
167
|
+
# HDBaseT connected
|
|
168
|
+
return (self._mask & self.HDBT_CONNECTED) != 0
|
|
169
|
+
|
|
170
|
+
@property
|
|
171
|
+
def cec_detected(self) -> bool:
|
|
172
|
+
# CEC capable device connected to this bay
|
|
173
|
+
return (self._mask & self.CEC_DETECTED) != 0
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def powered_on(self) -> bool:
|
|
177
|
+
# device is powered on
|
|
178
|
+
return (self._mask & self.POWERED_ON) != 0
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def powered_off(self) -> bool:
|
|
182
|
+
# device is powered off
|
|
183
|
+
return (self._mask & self.POWERED_OFF) != 0
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def audio_arc_hdmi(self) -> bool:
|
|
187
|
+
# HDMI ARC active
|
|
188
|
+
return (self._mask & self.AUDIO_ARC_HDMI) != 0
|
|
189
|
+
|
|
190
|
+
@property
|
|
191
|
+
def audio_arc_optical(self) -> bool:
|
|
192
|
+
# optical ARC active
|
|
193
|
+
return (self._mask & self.AUDIO_ARC_OPTICAL) != 0
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def audio_arc_analog(self) -> bool:
|
|
197
|
+
# analog ARC active
|
|
198
|
+
return (self._mask & self.AUDIO_ARC_ANALOG) != 0
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def offline(self) -> bool:
|
|
202
|
+
# v2ip unit offline
|
|
203
|
+
return (self._mask & self.OFFLINE) != 0
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def decoder_disabled(self) -> bool:
|
|
207
|
+
# v2ip decoder disabled
|
|
208
|
+
return (self._mask & self.DECODER_DISABLED) != 0
|
|
209
|
+
|
|
210
|
+
@property
|
|
211
|
+
def encoder_disabled(self) -> bool:
|
|
212
|
+
# v2ip decoder disabled
|
|
213
|
+
return (self._mask & self.ENCODER_DISABLED) != 0
|
|
214
|
+
|
|
215
|
+
def __str__(self) -> str:
|
|
216
|
+
rv = ""
|
|
217
|
+
if self.fault:
|
|
218
|
+
rv += " [fault]"
|
|
219
|
+
if self.hidden:
|
|
220
|
+
rv += " [hidden]"
|
|
221
|
+
if self.powered:
|
|
222
|
+
rv += " [powered]"
|
|
223
|
+
if self.signal_detected:
|
|
224
|
+
rv += " [signal]"
|
|
225
|
+
if self.hpd_detected:
|
|
226
|
+
rv += " [hpd]"
|
|
227
|
+
if self.hdbt_connected:
|
|
228
|
+
rv += " [hdbt]"
|
|
229
|
+
if self.cec_detected:
|
|
230
|
+
rv += " [cec]"
|
|
231
|
+
if self.powered_on:
|
|
232
|
+
rv += " [powered on]"
|
|
233
|
+
if self.powered_off:
|
|
234
|
+
rv += " [powered off]"
|
|
235
|
+
if self.audio_arc_hdmi:
|
|
236
|
+
rv += " [hdmi arc]"
|
|
237
|
+
if self.audio_arc_optical:
|
|
238
|
+
rv += " [optical arc]"
|
|
239
|
+
if self.audio_arc_analog:
|
|
240
|
+
rv += " [analog arc]"
|
|
241
|
+
if self.offline:
|
|
242
|
+
rv += " [offline]"
|
|
243
|
+
if self.decoder_disabled:
|
|
244
|
+
rv += " [decoder disabled]"
|
|
245
|
+
if self.encoder_disabled:
|
|
246
|
+
rv += " [encoder disabled]"
|
|
247
|
+
if len(rv) != 0:
|
|
248
|
+
return rv[1:]
|
|
249
|
+
return "[none]"
|
|
250
|
+
|
|
251
|
+
def __eq__(self, mask:Any) -> bool:
|
|
252
|
+
if isinstance(mask, int):
|
|
253
|
+
return self._mask == mask
|
|
254
|
+
if isinstance(mask, BayStatusMask):
|
|
255
|
+
return self._mask == mask._mask
|
|
256
|
+
return False
|
|
257
|
+
|
|
258
|
+
def __ne__(self, mask:Any) -> bool:
|
|
259
|
+
return not (self == mask)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class RCType(Enum):
|
|
263
|
+
IR = 0
|
|
264
|
+
CEC = 1
|
|
265
|
+
SKY_UK = 2
|
|
266
|
+
TIVO = 3
|
|
267
|
+
KODI = 4
|
|
268
|
+
DISH = 5
|
|
269
|
+
DIRECTV = 6
|
|
270
|
+
MX_REMOTE = 7
|
|
271
|
+
|
|
272
|
+
def __str__(self) -> str:
|
|
273
|
+
if self.value == RCType.IR.value:
|
|
274
|
+
return "IR"
|
|
275
|
+
if self.value == RCType.CEC.value:
|
|
276
|
+
return "CEC"
|
|
277
|
+
if self.value == RCType.SKY_UK.value:
|
|
278
|
+
return "Sky"
|
|
279
|
+
if self.value == RCType.TIVO.value:
|
|
280
|
+
return "TiVo"
|
|
281
|
+
if self.value == RCType.KODI.value:
|
|
282
|
+
return "Kodi"
|
|
283
|
+
if self.value == RCType.DISH.value:
|
|
284
|
+
return "Dish"
|
|
285
|
+
if self.value == RCType.DIRECTV.value:
|
|
286
|
+
return "DirecTV"
|
|
287
|
+
if self.value == RCType.MX_REMOTE.value:
|
|
288
|
+
return "MX-Remote"
|
|
289
|
+
return "Unknown"
|
|
290
|
+
|
|
291
|
+
def values() -> dict[int, str]:
|
|
292
|
+
rv:dict[int, str] = {}
|
|
293
|
+
for val in range(8):
|
|
294
|
+
rv[val] = str(RCType(val))
|
|
295
|
+
return rv
|
|
296
|
+
|
|
297
|
+
class EdidProfile(Enum):
|
|
298
|
+
def __str__(self) -> str:
|
|
299
|
+
if self.value == EdidProfile.TEMPLATE_1080P_STEREO.value:
|
|
300
|
+
return '1080p stereo'
|
|
301
|
+
if self.value == EdidProfile.FIXED.value:
|
|
302
|
+
return 'fixed'
|
|
303
|
+
if self.value == EdidProfile.TEMPLATE_4K.value:
|
|
304
|
+
return '4K'
|
|
305
|
+
if self.value == EdidProfile.TEMPLATE_1080P_5_1.value:
|
|
306
|
+
return '1080p 5.1'
|
|
307
|
+
if self.value == EdidProfile.TEMPLATE_720P.value:
|
|
308
|
+
return '720p'
|
|
309
|
+
if self.value == EdidProfile.TEMPLATE_1080P_7_1.value:
|
|
310
|
+
return '1080p 7.1'
|
|
311
|
+
if self.value == EdidProfile.TEMPLATE_4K_HDR_STEREO.value:
|
|
312
|
+
return '4K HDR Stereo'
|
|
313
|
+
if self.value == EdidProfile.TEMPLATE_4K_HDR_7_1.value:
|
|
314
|
+
return '4K HDR 7.1'
|
|
315
|
+
if self.value == EdidProfile.TEMPLATE_4K_HDR_AVR_ONLY.value:
|
|
316
|
+
return '4K HDR AVR'
|
|
317
|
+
if self.value == EdidProfile.LOWEST_COMMON_DENOMINATOR.value:
|
|
318
|
+
return 'lowest common denominator'
|
|
319
|
+
if self.value == EdidProfile.LOWEST_COMMON_DENOMINATOR_ALL.value:
|
|
320
|
+
return 'lowest common denominator (all sinks)'
|
|
321
|
+
if self.value == EdidProfile.TEMPLATE_4K_HDR_ATMOS.value:
|
|
322
|
+
return '4K HDR Dolby Atmos'
|
|
323
|
+
if (self.value >= EdidProfile.SINK_1.value) and (self.value <= EdidProfile.SINK_32.value):
|
|
324
|
+
return f'copy from sink #{self.value - EdidProfile.SINK_1.value + 1}'
|
|
325
|
+
return "unknown"
|
|
326
|
+
|
|
327
|
+
TEMPLATE_1080P_STEREO = 0
|
|
328
|
+
FIXED = 1
|
|
329
|
+
TEMPLATE_4K = 2
|
|
330
|
+
TEMPLATE_1080P_5_1 = 3
|
|
331
|
+
TEMPLATE_720P = 4
|
|
332
|
+
TEMPLATE_1080P_7_1 = 5
|
|
333
|
+
TEMPLATE_4K_7_1 = 6
|
|
334
|
+
TEMPLATE_4K_HDR_STEREO = 7
|
|
335
|
+
TEMPLATE_4K_HDR_7_1 = 8
|
|
336
|
+
TEMPLATE_4K_HDR_AVR_ONLY = 9
|
|
337
|
+
LOWEST_COMMON_DENOMINATOR = 10
|
|
338
|
+
LOWEST_COMMON_DENOMINATOR_ALL = 11
|
|
339
|
+
TEMPLATE_4K_HDR_ATMOS = 12
|
|
340
|
+
SINK_1 = 101
|
|
341
|
+
SINK_2 = 102
|
|
342
|
+
SINK_3 = 103
|
|
343
|
+
SINK_4 = 104
|
|
344
|
+
SINK_5 = 105
|
|
345
|
+
SINK_6 = 106
|
|
346
|
+
SINK_7 = 107
|
|
347
|
+
SINK_8 = 108
|
|
348
|
+
SINK_9 = 109
|
|
349
|
+
SINK_10 = 110
|
|
350
|
+
SINK_11 = 111
|
|
351
|
+
SINK_12 = 112
|
|
352
|
+
SINK_13 = 113
|
|
353
|
+
SINK_14 = 114
|
|
354
|
+
SINK_15= 115
|
|
355
|
+
SINK_16 = 116
|
|
356
|
+
SINK_17 = 117
|
|
357
|
+
SINK_18 = 118
|
|
358
|
+
SINK_19 = 119
|
|
359
|
+
SINK_20 = 120
|
|
360
|
+
SINK_21 = 121
|
|
361
|
+
SINK_22 = 122
|
|
362
|
+
SINK_23 = 123
|
|
363
|
+
SINK_24 = 124
|
|
364
|
+
SINK_25 = 125
|
|
365
|
+
SINK_26 = 126
|
|
366
|
+
SINK_27 = 127
|
|
367
|
+
SINK_28 = 128
|
|
368
|
+
SINK_29 = 129
|
|
369
|
+
SINK_30 = 130
|
|
370
|
+
SINK_31 = 131
|
|
371
|
+
SINK_32 = 132
|
|
372
|
+
UNKNOWN = 0xFFF
|
|
373
|
+
|
|
374
|
+
def values(nb_sinks:int|None=None) -> dict[int, str]:
|
|
375
|
+
rv:dict[int, str] = {}
|
|
376
|
+
for val in range(13):
|
|
377
|
+
rv[val] = str(EdidProfile(val))
|
|
378
|
+
if nb_sinks is None:
|
|
379
|
+
nb_sinks = 32
|
|
380
|
+
for val in range(101, 101 + nb_sinks):
|
|
381
|
+
rv[val] = str(EdidProfile(val))
|
|
382
|
+
return rv
|
mx_remote/proto/Data.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
##################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp.eu) ##
|
|
5
|
+
## copyright (c) 2024 Op den Kamp IT Solutions ##
|
|
6
|
+
##################################################
|
|
7
|
+
|
|
8
|
+
class MuteStatus:
|
|
9
|
+
''' mute left/right status '''
|
|
10
|
+
def __init__(self, val:int):
|
|
11
|
+
self._val = val
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def value(self) -> int:
|
|
15
|
+
return self._val
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def left(self) -> bool:
|
|
19
|
+
# left channel muted
|
|
20
|
+
return ((self._val & (1 << 0)) != 0)
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def right(self) -> bool:
|
|
24
|
+
# right channel muted
|
|
25
|
+
return ((self._val & (1 << 1)) != 0)
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def muted(self) -> bool:
|
|
29
|
+
# left or right channel muted (or both)
|
|
30
|
+
return (self._val != 0)
|
|
31
|
+
|
|
32
|
+
def __str__(self) -> str:
|
|
33
|
+
return f"left:{self.left} right:{self.right}"
|
|
34
|
+
|
|
35
|
+
class VolumeMuteStatus:
|
|
36
|
+
''' volume and mute status '''
|
|
37
|
+
def __init__(self, volume_left:int, volume_right:int, muted_left:bool=None, muted_right:bool=None):
|
|
38
|
+
self._volume_left = volume_left
|
|
39
|
+
self._volume_right = volume_right
|
|
40
|
+
self._muted_left = muted_left
|
|
41
|
+
self._muted_right = muted_right
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def value(self) -> bytes:
|
|
45
|
+
return bytes(bytearray([
|
|
46
|
+
self.volume_left,
|
|
47
|
+
self.volume_right,
|
|
48
|
+
self.muted_value,
|
|
49
|
+
]))
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def muted_value(self) -> int:
|
|
53
|
+
if not self.muted:
|
|
54
|
+
return 0
|
|
55
|
+
if self.muted_left and self.muted_right:
|
|
56
|
+
return 3
|
|
57
|
+
if self.muted_left:
|
|
58
|
+
return 1
|
|
59
|
+
return 2
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def volume(self) -> int:
|
|
63
|
+
# combined left/right volume %
|
|
64
|
+
vr = self._volume_right
|
|
65
|
+
if vr is None:
|
|
66
|
+
return self._volume_left if self._volume_left is not None else 0
|
|
67
|
+
return int((vr + self._volume_left) / 2.0)
|
|
68
|
+
|
|
69
|
+
@volume.setter
|
|
70
|
+
def volume(self, volume:int) -> None:
|
|
71
|
+
self.volume_left = volume
|
|
72
|
+
if self._volume_right is not None:
|
|
73
|
+
self.volume_right = volume
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def volume_left(self) -> int:
|
|
77
|
+
# left channel volume
|
|
78
|
+
return self._volume_left if (self._volume_left is not None) else self.volume
|
|
79
|
+
|
|
80
|
+
@volume_left.setter
|
|
81
|
+
def volume_left(self, volume:int) -> None:
|
|
82
|
+
self._volume_left = volume
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def volume_right(self) -> int:
|
|
86
|
+
# right channel volume
|
|
87
|
+
return self._volume_right if (self._volume_right is not None) else self.volume
|
|
88
|
+
|
|
89
|
+
@volume_right.setter
|
|
90
|
+
def volume_right(self, volume:int) -> None:
|
|
91
|
+
self._volume_right = volume
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def muted(self) -> bool:
|
|
95
|
+
# combined mute left/right status
|
|
96
|
+
if (self._muted_left is None) and (self._muted_right is None):
|
|
97
|
+
return None
|
|
98
|
+
return ((self._muted_left is not None) and self._muted_left) or \
|
|
99
|
+
((self._muted_right is not None) and self._muted_right)
|
|
100
|
+
|
|
101
|
+
@muted.setter
|
|
102
|
+
def muted(self, muted:bool) -> None:
|
|
103
|
+
self.muted_left = muted
|
|
104
|
+
if self._muted_right is not None:
|
|
105
|
+
self.muted_right = muted
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def muted_left(self) -> bool:
|
|
109
|
+
# left channel muted
|
|
110
|
+
return self._muted_left if (self._muted_left is not None) else self.muted
|
|
111
|
+
|
|
112
|
+
@muted_left.setter
|
|
113
|
+
def muted_left(self, muted:bool) -> None:
|
|
114
|
+
self._muted_left = muted
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def muted_right(self) -> bool:
|
|
118
|
+
# right channel muted
|
|
119
|
+
return self._muted_right if (self._muted_right is not None) else self.muted
|
|
120
|
+
|
|
121
|
+
@muted_right.setter
|
|
122
|
+
def muted_right(self, muted:bool) -> None:
|
|
123
|
+
self._muted_right = muted
|
|
124
|
+
|
|
125
|
+
def update(self, other:'VolumeMuteStatus') -> bool:
|
|
126
|
+
changed = False
|
|
127
|
+
if other._volume_left is not None:
|
|
128
|
+
changed = changed or (self._volume_left is None) or (self._volume_left != other._volume_left)
|
|
129
|
+
self._volume_left = other._volume_left
|
|
130
|
+
if other._volume_right is not None:
|
|
131
|
+
changed = changed or (self._volume_right is None) or (self._volume_right != other._volume_right)
|
|
132
|
+
self._volume_right = other._volume_right
|
|
133
|
+
if other._muted_left is not None:
|
|
134
|
+
changed = changed or (self._muted_left is None) or (self._muted_left != other._muted_left)
|
|
135
|
+
self._muted_left = other._muted_left
|
|
136
|
+
if other._muted_right is not None:
|
|
137
|
+
changed = changed or (self._muted_right is None) or (self._muted_right != other._muted_right)
|
|
138
|
+
self._muted_right = other._muted_right
|
|
139
|
+
return changed
|
|
140
|
+
|
|
141
|
+
def __str__(self) -> str:
|
|
142
|
+
return f"volume left:{self.volume_left} right:{self.volume_right} muted:{self.muted}"
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
##################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp.eu) ##
|
|
5
|
+
## copyright (c) 2024 Op den Kamp IT Solutions ##
|
|
6
|
+
##################################################
|
|
7
|
+
|
|
8
|
+
''' Methods for creating and processing frames '''
|
|
9
|
+
from .FrameBase import FrameBase
|
|
10
|
+
from .FrameHeader import FrameHeader
|
|
11
|
+
from ..Interface import DeviceRegistry
|
|
12
|
+
import logging
|
|
13
|
+
import traceback
|
|
14
|
+
|
|
15
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
16
|
+
|
|
17
|
+
def create_mxr_frame(uid:bytes, opcode:int, payload:bytes=None) -> bytes:
|
|
18
|
+
# create a new mx_remote frame for transmission
|
|
19
|
+
pkt = [80, 56, 1, 0 ]
|
|
20
|
+
pkt.extend(uid)
|
|
21
|
+
pkt.extend([(opcode & 0xFF), ((opcode >> 8) & 0xFF)])
|
|
22
|
+
if payload is None or len(payload) == 0:
|
|
23
|
+
pkt.extend([0, 0])
|
|
24
|
+
else:
|
|
25
|
+
l = len(payload)
|
|
26
|
+
pkt.extend([(l & 0xFF), ((l >> 8) & 0xFF)])
|
|
27
|
+
pkt.extend([payload])
|
|
28
|
+
return bytes(pkt)
|
|
29
|
+
|
|
30
|
+
def process_mxr_frame(mxr:DeviceRegistry, data:bytes, addr:tuple[str,int]) -> FrameBase:
|
|
31
|
+
# decode a (received) mx_remote frame
|
|
32
|
+
from .FrameHeader import FrameHeader
|
|
33
|
+
hdr = FrameHeader(mxr, data, addr)
|
|
34
|
+
if hdr is not None:
|
|
35
|
+
try:
|
|
36
|
+
# valid frame
|
|
37
|
+
if hdr.remote_id == mxr.uid:
|
|
38
|
+
logging.debug("ignore frame sent by myself")
|
|
39
|
+
return None
|
|
40
|
+
# not one of my own, process the incoming frame
|
|
41
|
+
return _mxr_frame_factory(hdr)
|
|
42
|
+
except Exception:
|
|
43
|
+
print(f"failed to process frame: {traceback.format_exc()}")
|
|
44
|
+
raise
|
|
45
|
+
logging.warning("frame header missing")
|
|
46
|
+
|
|
47
|
+
def _mxr_frame_factory(hdr:FrameHeader) -> FrameBase:
|
|
48
|
+
# create a new frame from a decoded mx_remote header
|
|
49
|
+
if hdr.opcode == 0x00:
|
|
50
|
+
from .FrameHello import FrameHello
|
|
51
|
+
return FrameHello(hdr)
|
|
52
|
+
if hdr.opcode == 0x01:
|
|
53
|
+
from .FrameDiscover import FrameDiscover
|
|
54
|
+
return FrameDiscover(hdr)
|
|
55
|
+
if hdr.opcode == 0x02:
|
|
56
|
+
from .FrameBayConfig import FrameBayConfig
|
|
57
|
+
return FrameBayConfig(hdr)
|
|
58
|
+
if hdr.opcode == 0x03:
|
|
59
|
+
from .FrameLinks import FrameLinks
|
|
60
|
+
return FrameLinks(hdr)
|
|
61
|
+
if hdr.opcode == 0x04:
|
|
62
|
+
from .FrameConnectStatus import FrameConnectStatus
|
|
63
|
+
return FrameConnectStatus(hdr)
|
|
64
|
+
if hdr.opcode == 0x05:
|
|
65
|
+
from .FramePowerChange import FramePowerChange
|
|
66
|
+
return FramePowerChange(hdr)
|
|
67
|
+
if hdr.opcode == 0x06:
|
|
68
|
+
from .FrameSignalStatus import FrameSignalStatus
|
|
69
|
+
return FrameSignalStatus(hdr)
|
|
70
|
+
if hdr.opcode == 0x07:
|
|
71
|
+
from .FrameEDID import FrameEDID
|
|
72
|
+
return FrameEDID(hdr)
|
|
73
|
+
if hdr.opcode == 0x08:
|
|
74
|
+
from .FrameRoutingChange import FrameRoutingChange
|
|
75
|
+
return FrameRoutingChange(hdr)
|
|
76
|
+
if hdr.opcode == 0x0A:
|
|
77
|
+
from .FrameRCIr import FrameRCIr
|
|
78
|
+
return FrameRCIr(hdr)
|
|
79
|
+
if hdr.opcode == 0x0B:
|
|
80
|
+
from .FrameRCKey import FrameRCKey
|
|
81
|
+
return FrameRCKey(hdr)
|
|
82
|
+
if hdr.opcode == 0x0D:
|
|
83
|
+
from .FrameRCAction import FrameRCAction
|
|
84
|
+
return FrameRCAction(hdr)
|
|
85
|
+
if hdr.opcode == 0x0F:
|
|
86
|
+
from .FrameVolumeUp import FrameVolumeUp
|
|
87
|
+
return FrameVolumeUp(hdr)
|
|
88
|
+
if hdr.opcode == 0x10:
|
|
89
|
+
from .FrameVolumeDown import FrameVolumeDown
|
|
90
|
+
return FrameVolumeDown(hdr)
|
|
91
|
+
if hdr.opcode == 0x12:
|
|
92
|
+
from .FrameVolume import FrameVolume
|
|
93
|
+
return FrameVolume(hdr)
|
|
94
|
+
if hdr.opcode == 0x14:
|
|
95
|
+
from .FrameVolumeSet import FrameVolumeSet
|
|
96
|
+
return FrameVolumeSet(hdr)
|
|
97
|
+
if hdr.opcode == 0x15:
|
|
98
|
+
from .FrameSysTemperature import FrameSysTemperature
|
|
99
|
+
return FrameSysTemperature(hdr)
|
|
100
|
+
if hdr.opcode == 0x1F:
|
|
101
|
+
from .FrameV2IPSourceSwitch import FrameV2IPSourceSwitch
|
|
102
|
+
return FrameV2IPSourceSwitch(hdr)
|
|
103
|
+
if hdr.opcode == 0x16:
|
|
104
|
+
from .FramePDUState import FramePDUState
|
|
105
|
+
return FramePDUState(hdr)
|
|
106
|
+
if hdr.opcode == 0x20:
|
|
107
|
+
from .FrameV2IPLink import FrameV2IPLinkStatus
|
|
108
|
+
return FrameV2IPLinkStatus(hdr)
|
|
109
|
+
if hdr.opcode == 0x22:
|
|
110
|
+
from .FrameSetName import FrameSetName
|
|
111
|
+
return FrameSetName(hdr)
|
|
112
|
+
if hdr.opcode == 0x23:
|
|
113
|
+
from .FrameBayConfigSecondary import FrameBayConfigSecondary
|
|
114
|
+
return FrameBayConfigSecondary(hdr)
|
|
115
|
+
if hdr.opcode == 0x26:
|
|
116
|
+
from .FrameV2IPSources import FrameV2IPSources
|
|
117
|
+
return FrameV2IPSources(hdr)
|
|
118
|
+
if hdr.opcode == 0x27:
|
|
119
|
+
from .FrameBayHide import FrameBayHide
|
|
120
|
+
return FrameBayHide(hdr)
|
|
121
|
+
if hdr.opcode == 0x28:
|
|
122
|
+
from .FrameReboot import FrameReboot
|
|
123
|
+
return FrameReboot(hdr)
|
|
124
|
+
if hdr.opcode == 0x29:
|
|
125
|
+
from .FrameNetworkStatus import FrameNetworkStatus
|
|
126
|
+
return FrameNetworkStatus(hdr)
|
|
127
|
+
if hdr.opcode == 0x2A:
|
|
128
|
+
from .FrameFirmwareVersion import FrameFirmwareVersion
|
|
129
|
+
return FrameFirmwareVersion(hdr)
|
|
130
|
+
if hdr.opcode == 0x30:
|
|
131
|
+
from .FrameTopology import FrameTopology
|
|
132
|
+
return FrameTopology(hdr)
|
|
133
|
+
if hdr.opcode == 0x31:
|
|
134
|
+
from .FrameSignalStatusNew import FrameSignalStatusNew
|
|
135
|
+
return FrameSignalStatusNew(hdr)
|
|
136
|
+
if hdr.opcode == 0x32:
|
|
137
|
+
from .FrameMirrorStatus import FrameMirrorStatus
|
|
138
|
+
return FrameMirrorStatus(hdr)
|
|
139
|
+
if hdr.opcode == 0x34:
|
|
140
|
+
from .FrameEDIDProfile import FrameEDIDProfile
|
|
141
|
+
return FrameEDIDProfile(hdr)
|
|
142
|
+
if hdr.opcode == 0x36:
|
|
143
|
+
from .FrameV2IPSetMaster import FrameV2IPSetMaster
|
|
144
|
+
return FrameV2IPSetMaster(hdr)
|
|
145
|
+
if hdr.opcode == 0x38:
|
|
146
|
+
from .FrameFilterStatus import FrameFilterStatus
|
|
147
|
+
return FrameFilterStatus(hdr)
|
|
148
|
+
if hdr.opcode == 0x39:
|
|
149
|
+
from .FrameBayStatus import FrameBayStatus
|
|
150
|
+
return FrameBayStatus(hdr)
|
|
151
|
+
if hdr.opcode == 0x3B:
|
|
152
|
+
from .FrameMeshOperation import FrameMeshOperation
|
|
153
|
+
return FrameMeshOperation(hdr)
|
|
154
|
+
if hdr.opcode == 0x3C:
|
|
155
|
+
from .FrameV2IPDeviceConfiguration import FrameV2IPDeviceConfiguration
|
|
156
|
+
return FrameV2IPDeviceConfiguration(hdr)
|
|
157
|
+
if hdr.opcode == 0x3D:
|
|
158
|
+
from .FrameAmpZoneSettings import FrameAmpZoneSettings
|
|
159
|
+
return FrameAmpZoneSettings(hdr)
|
|
160
|
+
if hdr.opcode == 0x3E:
|
|
161
|
+
from .FrameAmpDolbySettings import FrameAmpDolbySettings
|
|
162
|
+
return FrameAmpDolbySettings(hdr)
|
|
163
|
+
if hdr.opcode == 0x3F:
|
|
164
|
+
from .FrameV2IPStats import FrameV2IPStats
|
|
165
|
+
return FrameV2IPStats(hdr)
|
|
166
|
+
logging.warning(f"opcode {hdr.opcode:02X} is not processed")
|
|
167
|
+
return None
|
|
168
|
+
|