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.
Files changed (74) hide show
  1. mx_remote/Interface.py +1656 -0
  2. mx_remote/Uid.py +137 -0
  3. mx_remote/__init__.py +12 -0
  4. mx_remote/api/BayConfig.py +53 -0
  5. mx_remote/api/__init__.py +8 -0
  6. mx_remote/const.py +20 -0
  7. mx_remote/main.py +117 -0
  8. mx_remote/proto/BayConfig.py +104 -0
  9. mx_remote/proto/Constants.py +382 -0
  10. mx_remote/proto/Data.py +142 -0
  11. mx_remote/proto/Factory.py +168 -0
  12. mx_remote/proto/FrameAmpDolbySettings.py +51 -0
  13. mx_remote/proto/FrameAmpZoneSettings.py +123 -0
  14. mx_remote/proto/FrameBase.py +80 -0
  15. mx_remote/proto/FrameBayConfig.py +47 -0
  16. mx_remote/proto/FrameBayConfigSecondary.py +43 -0
  17. mx_remote/proto/FrameBayHide.py +53 -0
  18. mx_remote/proto/FrameBayStatus.py +51 -0
  19. mx_remote/proto/FrameConnectStatus.py +38 -0
  20. mx_remote/proto/FrameDiscover.py +21 -0
  21. mx_remote/proto/FrameEDID.py +20 -0
  22. mx_remote/proto/FrameEDIDProfile.py +24 -0
  23. mx_remote/proto/FrameFilterStatus.py +39 -0
  24. mx_remote/proto/FrameFirmwareVersion.py +40 -0
  25. mx_remote/proto/FrameHeader.py +92 -0
  26. mx_remote/proto/FrameHello.py +77 -0
  27. mx_remote/proto/FrameLinks.py +45 -0
  28. mx_remote/proto/FrameMeshOperation.py +66 -0
  29. mx_remote/proto/FrameMirrorStatus.py +49 -0
  30. mx_remote/proto/FrameNetworkStatus.py +163 -0
  31. mx_remote/proto/FramePDUState.py +71 -0
  32. mx_remote/proto/FramePowerChange.py +38 -0
  33. mx_remote/proto/FrameRCAction.py +50 -0
  34. mx_remote/proto/FrameRCIr.py +17 -0
  35. mx_remote/proto/FrameRCKey.py +40 -0
  36. mx_remote/proto/FrameReboot.py +26 -0
  37. mx_remote/proto/FrameRoutingChange.py +66 -0
  38. mx_remote/proto/FrameSetName.py +27 -0
  39. mx_remote/proto/FrameSignalStatus.py +46 -0
  40. mx_remote/proto/FrameSignalStatusNew.py +285 -0
  41. mx_remote/proto/FrameSysTemperature.py +50 -0
  42. mx_remote/proto/FrameTXRCAction.py +58 -0
  43. mx_remote/proto/FrameTopology.py +36 -0
  44. mx_remote/proto/FrameV2IPDeviceConfiguration.py +86 -0
  45. mx_remote/proto/FrameV2IPLink.py +16 -0
  46. mx_remote/proto/FrameV2IPSetMaster.py +16 -0
  47. mx_remote/proto/FrameV2IPSourceSwitch.py +84 -0
  48. mx_remote/proto/FrameV2IPSources.py +43 -0
  49. mx_remote/proto/FrameV2IPStats.py +55 -0
  50. mx_remote/proto/FrameV2IPStreamDetails.py +46 -0
  51. mx_remote/proto/FrameVolume.py +62 -0
  52. mx_remote/proto/FrameVolumeDown.py +28 -0
  53. mx_remote/proto/FrameVolumeSet.py +81 -0
  54. mx_remote/proto/FrameVolumeUp.py +28 -0
  55. mx_remote/proto/LinkConfig.py +121 -0
  56. mx_remote/proto/PDUState.py +95 -0
  57. mx_remote/proto/Svd.py +69 -0
  58. mx_remote/proto/V2IPConfig.py +71 -0
  59. mx_remote/proto/V2IPStats.py +126 -0
  60. mx_remote/proto/__init__.py +8 -0
  61. mx_remote/proto/svd.csv +157 -0
  62. mx_remote/remote/Bay.py +923 -0
  63. mx_remote/remote/ConnectionAsync.py +132 -0
  64. mx_remote/remote/Device.py +530 -0
  65. mx_remote/remote/Link.py +187 -0
  66. mx_remote/remote/PDU.py +152 -0
  67. mx_remote/remote/Remote.py +300 -0
  68. mx_remote/remote/State.py +28 -0
  69. mx_remote/remote/V2IP.py +83 -0
  70. mx_remote-2.0.0.dist-info/METADATA +90 -0
  71. mx_remote-2.0.0.dist-info/RECORD +74 -0
  72. mx_remote-2.0.0.dist-info/WHEEL +4 -0
  73. mx_remote-2.0.0.dist-info/entry_points.txt +2 -0
  74. 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)