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,51 @@
|
|
|
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 .FrameBase import FrameBase
|
|
9
|
+
from .FrameHeader import FrameHeader
|
|
10
|
+
from ..Interface import DeviceBase, AmpDolbySettings
|
|
11
|
+
from ..Uid import MxrDeviceUid
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
_LOGGER = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
class FrameAmpDolbySettings(FrameBase):
|
|
17
|
+
def __init__(self, header:FrameHeader):
|
|
18
|
+
super().__init__(header)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def target_device(self) -> DeviceBase:
|
|
22
|
+
if self.target_uid.empty:
|
|
23
|
+
return self.mxr.get_by_uid(self.header.remote_id)
|
|
24
|
+
return self.mxr.get_by_uid(self.target_uid)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def target_uid(self) -> MxrDeviceUid:
|
|
28
|
+
return MxrDeviceUid(self.payload[0:16])
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def dolby_mode(self) -> int:
|
|
32
|
+
return self.payload[16]
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def pcm_upmix(self) -> bool:
|
|
36
|
+
return (self.payload[17] != 0)
|
|
37
|
+
|
|
38
|
+
def as_settings(self) -> AmpDolbySettings:
|
|
39
|
+
settings = AmpDolbySettings()
|
|
40
|
+
settings.mode = self.dolby_mode
|
|
41
|
+
settings.pcm_upmix = self.pcm_upmix
|
|
42
|
+
return settings
|
|
43
|
+
|
|
44
|
+
def process(self):
|
|
45
|
+
# update the local cache
|
|
46
|
+
device = self.target_device
|
|
47
|
+
if device is not None:
|
|
48
|
+
device.dolby_settings = self.as_settings()
|
|
49
|
+
|
|
50
|
+
def __str__(self) -> str:
|
|
51
|
+
return f"amp dolby settings {self.target_device}: mode={self.dolby_mode} upmix={self.pcm_upmix}"
|
|
@@ -0,0 +1,123 @@
|
|
|
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 .FrameBase import FrameBase
|
|
9
|
+
from .FrameHeader import FrameHeader
|
|
10
|
+
from ..Interface import DeviceBase, BayBase, AmpZoneSettings
|
|
11
|
+
from ..Uid import MxrDeviceUid
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
_LOGGER = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
class FrameAmpZoneSettings(FrameBase):
|
|
17
|
+
def __init__(self, header:FrameHeader):
|
|
18
|
+
super().__init__(header)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def target_device(self) -> DeviceBase:
|
|
22
|
+
if self.target_uid.empty:
|
|
23
|
+
return self.mxr.get_by_uid(self.header.remote_id)
|
|
24
|
+
return self.mxr.get_by_uid(self.target_uid)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def target_uid(self) -> MxrDeviceUid:
|
|
28
|
+
return MxrDeviceUid(self.payload[0:16])
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def zone(self) -> int:
|
|
32
|
+
return int.from_bytes(self.payload[16:17], "little")
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def bay(self) -> BayBase|None:
|
|
36
|
+
target_device = self.target_device
|
|
37
|
+
if target_device is None:
|
|
38
|
+
_LOGGER.warning(f"amp zone settings no target uid = {self.target_uid}")
|
|
39
|
+
return None
|
|
40
|
+
return target_device.get_by_portnum(self.zone)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def gain_left(self) -> int:
|
|
44
|
+
return self.payload[18]
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
def gain_right(self) -> int:
|
|
48
|
+
return self.payload[19]
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def volume_min(self) -> int:
|
|
52
|
+
return self.payload[20]
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def volume_max(self) -> int:
|
|
56
|
+
return self.payload[21]
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def delay_left(self) -> int:
|
|
60
|
+
return int.from_bytes(self.payload[22:26], "little")
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def delay_right(self) -> int:
|
|
64
|
+
return int.from_bytes(self.payload[26:30], "little")
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def bass(self) -> int:
|
|
68
|
+
return self.payload[30]
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def treble(self) -> int:
|
|
72
|
+
return self.payload[31]
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def bridged(self) -> int:
|
|
76
|
+
return self.payload[32]
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def power_mode(self) -> int:
|
|
80
|
+
return self.payload[33]
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def power_level(self) -> int:
|
|
84
|
+
return self.payload[34]
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def power_timeout(self) -> int:
|
|
88
|
+
return int.from_bytes(self.payload[35:39], "little")
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def eq_left(self) -> list[int]:
|
|
92
|
+
return [int(x) for x in self.payload[39:44]]
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def eq_right(self) -> list[int]:
|
|
96
|
+
return [int(x) for x in self.payload[44:49]]
|
|
97
|
+
|
|
98
|
+
def as_settings(self) -> AmpZoneSettings:
|
|
99
|
+
settings = AmpZoneSettings()
|
|
100
|
+
settings.gain_left = self.gain_left
|
|
101
|
+
settings.gain_right = self.gain_right
|
|
102
|
+
settings.volume_min = self.volume_min
|
|
103
|
+
settings.volume_max = self.volume_max
|
|
104
|
+
settings.delay_left = self.delay_left
|
|
105
|
+
settings.delay_right = self.delay_right
|
|
106
|
+
settings.bass = self.bass
|
|
107
|
+
settings.treble = self.treble
|
|
108
|
+
settings.bridged = self.bridged
|
|
109
|
+
settings.power_mode = self.power_mode
|
|
110
|
+
settings.power_level = self.power_level
|
|
111
|
+
settings.power_timeout = self.power_timeout
|
|
112
|
+
settings.eq_left = self.eq_left
|
|
113
|
+
settings.eq_right = self.eq_right
|
|
114
|
+
return settings
|
|
115
|
+
|
|
116
|
+
def process(self):
|
|
117
|
+
# update the local cache
|
|
118
|
+
bay = self.bay
|
|
119
|
+
if bay is not None:
|
|
120
|
+
bay.amp_settings = self.as_settings()
|
|
121
|
+
|
|
122
|
+
def __str__(self) -> str:
|
|
123
|
+
return f"amp zone settings {self.bay} size {len(self.payload)}: volume_range={self.volume_min}-{self.volume_max} bridged={self.bridged} power={self.power_mode} level={self.power_level} timeout={self.power_timeout} delay={self.delay_left}/{self.delay_right} gain={self.gain_left}/{self.gain_right} bass={self.bass} treble={self.treble} eq_left={self.eq_left} eq_right={self.eq_right}"
|
|
@@ -0,0 +1,80 @@
|
|
|
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 .Constants import MXR_PROTOCOL_VERSION
|
|
9
|
+
from .FrameHeader import FrameHeader
|
|
10
|
+
from ..Interface import DeviceBase, DeviceRegistry
|
|
11
|
+
from ..Uid import MxrDeviceUid
|
|
12
|
+
|
|
13
|
+
def append_payload_str(payload:list[int], value:str, sz:int) -> list[int]:
|
|
14
|
+
value = value[0:16]
|
|
15
|
+
return payload + list(value.encode('ascii')) + [ 0 for _ in range(sz - len(value))]
|
|
16
|
+
class FrameBase:
|
|
17
|
+
''' Base class for decoded mx_remote frames '''
|
|
18
|
+
def __init__(self, header:FrameHeader):
|
|
19
|
+
assert(isinstance(header, FrameHeader))
|
|
20
|
+
self.header = header
|
|
21
|
+
|
|
22
|
+
def construct_frame(mxr:DeviceRegistry, opcode:int, protocol:int=MXR_PROTOCOL_VERSION, payload:bytes=bytes([]), size:int|None=None) -> 'FrameBase':
|
|
23
|
+
rv = FrameBase(FrameHeader.construct(mxr=mxr, opcode=opcode, protocol=protocol))
|
|
24
|
+
if (size is not None):
|
|
25
|
+
if len(payload) > size:
|
|
26
|
+
payload = payload[:size]
|
|
27
|
+
elif len(payload) < size:
|
|
28
|
+
payload += bytes([0 for _ in range(size - len(rv))])
|
|
29
|
+
rv.payload = payload
|
|
30
|
+
return rv
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def mxr(self) -> DeviceRegistry:
|
|
34
|
+
# remote instance
|
|
35
|
+
return self.header.mxr
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def address(self) -> str:
|
|
39
|
+
# address that sent this frame
|
|
40
|
+
(addr, _) = self.header.addr
|
|
41
|
+
return addr
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def protocol(self) -> int:
|
|
45
|
+
# frame protocol version
|
|
46
|
+
return self.header.protocol
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def remote_id(self) -> MxrDeviceUid:
|
|
50
|
+
# unique id of the device that sent this frame
|
|
51
|
+
return self.header.remote_id
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def remote_device(self) -> DeviceBase:
|
|
55
|
+
# device instance for the device that sent this frame
|
|
56
|
+
return self.mxr.get_by_uid(self.remote_id)
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def payload(self) -> bytes:
|
|
60
|
+
# frame payload bytes
|
|
61
|
+
return self.header.payload
|
|
62
|
+
|
|
63
|
+
@payload.setter
|
|
64
|
+
def payload(self, val:bytes) -> None:
|
|
65
|
+
self.header.payload = val
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def frame(self) -> bytes:
|
|
69
|
+
return self.header.data
|
|
70
|
+
|
|
71
|
+
def process(self) -> None:
|
|
72
|
+
# update the local cache with the new data that was received in this frame
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
def __len__(self) -> int:
|
|
76
|
+
# number of payload bytes
|
|
77
|
+
return self.header.payload_len
|
|
78
|
+
|
|
79
|
+
def __str__(self) -> str:
|
|
80
|
+
return f"generic frame opcode {self.header.opcode:X}"
|
|
@@ -0,0 +1,47 @@
|
|
|
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 .BayConfig import BayConfig
|
|
9
|
+
from .FrameBase import FrameBase
|
|
10
|
+
from .FrameHeader import FrameHeader
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
_LOGGER = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class FrameBayConfig(FrameBase):
|
|
16
|
+
''' Bay configuration and information for all bays that are available on a remote device '''
|
|
17
|
+
def __init__(self, header:FrameHeader):
|
|
18
|
+
super().__init__(header)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def nb_bays(self) -> int:
|
|
22
|
+
# total number of bay descriptors in this frame
|
|
23
|
+
return len(self) / 61
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def bays(self) -> list[BayConfig]:
|
|
27
|
+
# get a list of bay configurations defined in this frame
|
|
28
|
+
rv = []
|
|
29
|
+
baynum = 0
|
|
30
|
+
while baynum < self.nb_bays:
|
|
31
|
+
bay = BayConfig(self.payload[(baynum*61):((baynum+1)*61)])
|
|
32
|
+
rv.append(bay)
|
|
33
|
+
baynum += 1
|
|
34
|
+
return rv
|
|
35
|
+
|
|
36
|
+
def process(self) -> None:
|
|
37
|
+
# register or update all bays in the local cache
|
|
38
|
+
dev = self.remote_device
|
|
39
|
+
if dev is None:
|
|
40
|
+
_LOGGER.debug("not processing bay config - hello not received")
|
|
41
|
+
return
|
|
42
|
+
for bayconfig in self.bays:
|
|
43
|
+
_LOGGER.debug(f"process {bayconfig}")
|
|
44
|
+
dev.on_mxr_bay_config(bayconfig)
|
|
45
|
+
|
|
46
|
+
def __str__(self) -> str:
|
|
47
|
+
return f"{self.remote_device} bay config: {len(self.bays)} bays"
|
|
@@ -0,0 +1,43 @@
|
|
|
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 .BayConfig import BayConfig
|
|
9
|
+
from .FrameBase import FrameBase
|
|
10
|
+
from .FrameHeader import FrameHeader
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
class FrameBayConfigSecondary(FrameBase):
|
|
14
|
+
''' Bay configuration and information for all bays that are available on a remote device '''
|
|
15
|
+
def __init__(self, header:FrameHeader):
|
|
16
|
+
super().__init__(header)
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def nb_bays(self) -> int:
|
|
20
|
+
# total number of bay descriptors in this frame
|
|
21
|
+
return len(self) / 61
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def bays(self) -> list[BayConfig]:
|
|
25
|
+
# get a list of bay configurations defined in this frame
|
|
26
|
+
rv = []
|
|
27
|
+
baynum = 0
|
|
28
|
+
while baynum < self.nb_bays:
|
|
29
|
+
bay = BayConfig(self.payload[(baynum*61):((baynum+1)*61)])
|
|
30
|
+
rv.append(bay)
|
|
31
|
+
baynum += 1
|
|
32
|
+
return rv
|
|
33
|
+
|
|
34
|
+
def process(self) -> None:
|
|
35
|
+
# register or update bay in the local cache
|
|
36
|
+
dev = self.remote_device
|
|
37
|
+
if dev is None:
|
|
38
|
+
logging.debug("not processing secondary bay config - hello not received")
|
|
39
|
+
return
|
|
40
|
+
dev.on_mxr_bay_config(BayConfig(self.payload))
|
|
41
|
+
|
|
42
|
+
def __str__(self) -> str:
|
|
43
|
+
return f"{self.remote_device} secondary bay config: {len(self.bays)} bays"
|
|
@@ -0,0 +1,53 @@
|
|
|
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 .Constants import BayStatusMask
|
|
9
|
+
from .FrameBase import FrameBase
|
|
10
|
+
from .FrameHeader import FrameHeader
|
|
11
|
+
from ..Interface import BayBase, DeviceRegistry, DeviceBase, MxrDeviceUid
|
|
12
|
+
import logging
|
|
13
|
+
import struct
|
|
14
|
+
|
|
15
|
+
_LOGGER = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class FrameBayHide(FrameBase):
|
|
18
|
+
def __init__(self, header:FrameHeader):
|
|
19
|
+
super().__init__(header)
|
|
20
|
+
|
|
21
|
+
def construct(mxr:DeviceRegistry, target:BayBase, hidden:bool) -> FrameBase:
|
|
22
|
+
payload = target.device.remote_id.byte_value + \
|
|
23
|
+
bytes([(target.port >> 0) & 0xFF, (target.port >> 8) & 0xFF]) + \
|
|
24
|
+
bytes([0 for _ in range(6)]) + \
|
|
25
|
+
bytes([1 if hidden else 0]) + \
|
|
26
|
+
bytes([0 for _ in range(7)])
|
|
27
|
+
return FrameBase.construct_frame(mxr=mxr, opcode=0x27, payload=payload)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def target_uid(self) -> DeviceBase:
|
|
31
|
+
return MxrDeviceUid(self.payload[0:16])
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def target(self) -> DeviceBase:
|
|
35
|
+
return self.remote_device.registry.get_by_uid(self.target_uid)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def bay(self) -> BayBase:
|
|
39
|
+
target = self.target
|
|
40
|
+
if target is None:
|
|
41
|
+
return None
|
|
42
|
+
portnum = ((self.payload[17] << 8) | self.payload[18])
|
|
43
|
+
return target.get_by_portnum(portnum)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def hidden(self) -> bool:
|
|
47
|
+
return (self.payload[24] == 1)
|
|
48
|
+
|
|
49
|
+
def process(self) -> None:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
def __str__(self) -> str:
|
|
53
|
+
return f"bay hide {self.bay} hidden={self.hidden}"
|
|
@@ -0,0 +1,51 @@
|
|
|
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 .Constants import BayStatusMask
|
|
9
|
+
from .FrameBase import FrameBase
|
|
10
|
+
from .FrameHeader import FrameHeader
|
|
11
|
+
from ..Interface import BayBase
|
|
12
|
+
import logging
|
|
13
|
+
import struct
|
|
14
|
+
|
|
15
|
+
_LOGGER = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class FrameBayStatus(FrameBase):
|
|
18
|
+
def __init__(self, header:FrameHeader):
|
|
19
|
+
super().__init__(header)
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def bay(self) -> BayBase:
|
|
23
|
+
portnum = ((self.payload[1] << 8) | self.payload[0])
|
|
24
|
+
dev = self.remote_device
|
|
25
|
+
if dev is None:
|
|
26
|
+
return None
|
|
27
|
+
return dev.get_by_portnum(portnum)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def signal_type(self) -> str:
|
|
31
|
+
return self.payload[2:18].split(b'\0',1)[0].decode('ascii')
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def status(self) -> BayStatusMask:
|
|
35
|
+
return BayStatusMask(struct.unpack('<L', self.payload[20:24])[0])
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def features(self) -> int:
|
|
39
|
+
return struct.unpack('<L', self.payload[24:28])[0]
|
|
40
|
+
|
|
41
|
+
def process(self) -> None:
|
|
42
|
+
if self.bay is None:
|
|
43
|
+
_LOGGER.warning("bay not registered yet")
|
|
44
|
+
else:
|
|
45
|
+
self.bay.features_mask = self.features
|
|
46
|
+
self.bay.on_mxr_bay_status(self.status)
|
|
47
|
+
if not self.status.signal_detected or not self.bay.device.is_v2ip:
|
|
48
|
+
self.bay.signal_type = self.signal_type
|
|
49
|
+
|
|
50
|
+
def __str__(self) -> str:
|
|
51
|
+
return f"bay status {self.bay} signal '{self.signal_type}' status {self.status} features {self.features}"
|
|
@@ -0,0 +1,38 @@
|
|
|
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 .FrameBase import FrameBase
|
|
9
|
+
from .FrameHeader import FrameHeader
|
|
10
|
+
from ..Interface import BayBase
|
|
11
|
+
|
|
12
|
+
class FrameConnectStatus(FrameBase):
|
|
13
|
+
''' Device was connected or disconnected. For sources, this means that an input signal was detected '''
|
|
14
|
+
def __init__(self, header:FrameHeader):
|
|
15
|
+
super().__init__(header)
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def bay(self) -> BayBase:
|
|
19
|
+
# bay that changed
|
|
20
|
+
portnum = self.payload[0]
|
|
21
|
+
dev = self.remote_device
|
|
22
|
+
if dev is None:
|
|
23
|
+
return None
|
|
24
|
+
return dev.get_by_portnum(portnum)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def connected(self) -> bool:
|
|
28
|
+
# new connected / signal detect status
|
|
29
|
+
return (self.payload[1] == 1)
|
|
30
|
+
|
|
31
|
+
def process(self) -> None:
|
|
32
|
+
# update the cached connected status for this bay
|
|
33
|
+
bay = self.bay
|
|
34
|
+
if bay is not None:
|
|
35
|
+
bay.connected = self.connected
|
|
36
|
+
|
|
37
|
+
def __str__(self) -> str:
|
|
38
|
+
return "connect status {}: {}".format(str(self.bay), str(self.connected))
|
|
@@ -0,0 +1,21 @@
|
|
|
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 .FrameBase import FrameBase
|
|
9
|
+
from .FrameHeader import FrameHeader
|
|
10
|
+
from ..Interface import DeviceRegistry
|
|
11
|
+
|
|
12
|
+
class FrameDiscover(FrameBase):
|
|
13
|
+
''' Discovery, ask all devices on the network to send their info '''
|
|
14
|
+
def __init__(self, header:FrameHeader):
|
|
15
|
+
super().__init__(header)
|
|
16
|
+
|
|
17
|
+
def construct(mxr:DeviceRegistry) -> FrameBase:
|
|
18
|
+
return FrameBase.construct_frame(mxr=mxr, opcode=1, protocol=1)
|
|
19
|
+
|
|
20
|
+
def __str__(self) -> str:
|
|
21
|
+
return "discover devices"
|
|
@@ -0,0 +1,20 @@
|
|
|
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 .FrameBase import FrameBase
|
|
9
|
+
from .FrameHeader import FrameHeader
|
|
10
|
+
|
|
11
|
+
class FrameEDID(FrameBase):
|
|
12
|
+
def __init__(self, header:FrameHeader):
|
|
13
|
+
super().__init__(header)
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def port(self):
|
|
17
|
+
return "Output" if self.payload[0] == 1 else "Input"
|
|
18
|
+
|
|
19
|
+
def __str__(self) -> str:
|
|
20
|
+
return f"EDID data {self.port}"
|
|
@@ -0,0 +1,24 @@
|
|
|
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 .FrameBase import FrameBase
|
|
9
|
+
from .FrameHeader import FrameHeader
|
|
10
|
+
from ..Interface import DeviceRegistry, DeviceBase, EdidProfile
|
|
11
|
+
|
|
12
|
+
class FrameEDIDProfile(FrameBase):
|
|
13
|
+
''' Change an EDID profile '''
|
|
14
|
+
def __init__(self, header:FrameHeader):
|
|
15
|
+
super().__init__(header)
|
|
16
|
+
|
|
17
|
+
def construct(mxr:DeviceRegistry, target:DeviceBase, profile:EdidProfile) -> FrameBase:
|
|
18
|
+
payload = target.remote_id.byte_value + \
|
|
19
|
+
bytes([(profile.value >> 0) & 0xFF, (profile.value >> 8) & 0xFF]) + \
|
|
20
|
+
bytes([0 for _ in range(6)])
|
|
21
|
+
return FrameBase.construct_frame(mxr=mxr, opcode=0x34, payload=payload)
|
|
22
|
+
|
|
23
|
+
def __str__(self) -> str:
|
|
24
|
+
return f"EDID profile change"
|
|
@@ -0,0 +1,39 @@
|
|
|
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 .FrameBase import FrameBase
|
|
9
|
+
from .FrameHeader import FrameHeader
|
|
10
|
+
from ..Uid import MxrDeviceUid
|
|
11
|
+
|
|
12
|
+
class FrameFilterStatus(FrameBase):
|
|
13
|
+
def __init__(self, header:FrameHeader):
|
|
14
|
+
super().__init__(header)
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def filtered(self) -> list[MxrDeviceUid]:
|
|
18
|
+
filtered = []
|
|
19
|
+
data = self.payload[16:]
|
|
20
|
+
while len(data) >= 16:
|
|
21
|
+
filtered.append(MxrDeviceUid(data[0:16]))
|
|
22
|
+
data = data[16:]
|
|
23
|
+
return filtered
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def target_uid(self) -> MxrDeviceUid:
|
|
27
|
+
return MxrDeviceUid(self.payload[0:16])
|
|
28
|
+
|
|
29
|
+
def process(self) -> None:
|
|
30
|
+
dev = self.remote_device
|
|
31
|
+
if dev is None:
|
|
32
|
+
return
|
|
33
|
+
if len (dev.outputs) < 1:
|
|
34
|
+
return
|
|
35
|
+
first_out = dev.outputs[list(dev.outputs.keys())[0]]
|
|
36
|
+
first_out.filtered = self.filtered
|
|
37
|
+
|
|
38
|
+
def __str__(self) -> str:
|
|
39
|
+
return f"bay filter status: {len(self.filtered)} sources filtered"
|
|
@@ -0,0 +1,40 @@
|
|
|
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 .FrameBase import FrameBase
|
|
9
|
+
from .FrameHeader import FrameHeader
|
|
10
|
+
import struct
|
|
11
|
+
|
|
12
|
+
class FrameFirmwareVersion(FrameBase):
|
|
13
|
+
def __init__(self, header:FrameHeader):
|
|
14
|
+
super().__init__(header)
|
|
15
|
+
self._type = self.payload[0]
|
|
16
|
+
self._hash = struct.unpack('<L', self.payload[4:8])[0]
|
|
17
|
+
self._timestamp = struct.unpack('<L', self.payload[8:12])[0]
|
|
18
|
+
self._name = self.payload[12:].split(b'\0',1)[0].decode('ascii')
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def fw_type(self):
|
|
22
|
+
return self._type
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def name(self):
|
|
26
|
+
return self._name
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def hash(self):
|
|
30
|
+
return self._hash
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def timestamp(self):
|
|
34
|
+
return self._timestamp
|
|
35
|
+
|
|
36
|
+
def __str__(self) -> str:
|
|
37
|
+
return f"firmware version: type {self.fw_type}: '{self.name}' hash: {hex(self.hash)} timestamp: {self.timestamp}"
|
|
38
|
+
|
|
39
|
+
def __repr__(self) -> str:
|
|
40
|
+
return str(self)
|