mx-remote 5.0.0__tar.gz → 5.1.0__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.
Files changed (204) hide show
  1. {mx_remote-5.0.0 → mx_remote-5.1.0}/CLAUDE.md +13 -0
  2. {mx_remote-5.0.0 → mx_remote-5.1.0}/PKG-INFO +1 -1
  3. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/Interface.py +114 -2
  4. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/Interface.pyi +29 -1
  5. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/const.py +1 -1
  6. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameAmpZoneSettings.py +1 -1
  7. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameBase.py +33 -2
  8. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameBase.pyi +4 -1
  9. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameBayHide.py +1 -1
  10. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameEDIDProfile.py +1 -1
  11. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameMeshOperation.py +1 -1
  12. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameRCAction.py +1 -1
  13. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameReboot.py +1 -1
  14. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSetName.py +1 -1
  15. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSignalStatusNew.py +10 -19
  16. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSignalStatusNew.pyi +2 -8
  17. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameTXRCAction.py +1 -1
  18. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameTXRCKey.py +1 -1
  19. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPAudio.py +3 -3
  20. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPManualSourceSwitch.py +1 -1
  21. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPMultiviewer.py +15 -15
  22. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPSourceSwitch.py +1 -1
  23. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameVolumeSet.py +1 -1
  24. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/Bay.py +28 -1
  25. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/Bay.pyi +5 -2
  26. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/Device.py +9 -0
  27. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/Device.pyi +3 -0
  28. mx_remote-5.1.0/tests/protogate.py +85 -0
  29. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/run.py +2 -0
  30. mx_remote-5.1.0/tests/txresult.py +198 -0
  31. mx_remote-5.1.0/tests/vdetails.py +86 -0
  32. mx_remote-5.0.0/tests/txresult.py +0 -105
  33. {mx_remote-5.0.0 → mx_remote-5.1.0}/.gitignore +0 -0
  34. {mx_remote-5.0.0 → mx_remote-5.1.0}/LICENSE +0 -0
  35. {mx_remote-5.0.0 → mx_remote-5.1.0}/README.md +0 -0
  36. {mx_remote-5.0.0 → mx_remote-5.1.0}/hatch_build.py +0 -0
  37. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/Uid.py +0 -0
  38. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/Uid.pyi +0 -0
  39. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/__init__.py +0 -0
  40. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/__init__.pyi +0 -0
  41. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/const.pyi +0 -0
  42. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/main.py +0 -0
  43. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/main.pyi +0 -0
  44. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/BayConfig.py +0 -0
  45. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/BayConfig.pyi +0 -0
  46. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/Constants.py +0 -0
  47. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/Constants.pyi +0 -0
  48. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/Data.py +0 -0
  49. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/Data.pyi +0 -0
  50. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/Factory.py +0 -0
  51. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/Factory.pyi +0 -0
  52. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameAmpDolbySettings.py +0 -0
  53. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameAmpDolbySettings.pyi +0 -0
  54. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameAmpZoneSettings.pyi +0 -0
  55. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameAudioClip.py +0 -0
  56. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameAudioClip.pyi +0 -0
  57. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameAudioSetRoute.py +0 -0
  58. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameAudioSetRoute.pyi +0 -0
  59. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameBayConfig.py +0 -0
  60. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameBayConfig.pyi +0 -0
  61. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameBayConfigSecondary.py +0 -0
  62. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameBayConfigSecondary.pyi +0 -0
  63. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameBayHide.pyi +0 -0
  64. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameBayStatus.py +0 -0
  65. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameBayStatus.pyi +0 -0
  66. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameConnectStatus.py +0 -0
  67. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameConnectStatus.pyi +0 -0
  68. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameDebug.py +0 -0
  69. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameDebug.pyi +0 -0
  70. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameDiscover.py +0 -0
  71. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameDiscover.pyi +0 -0
  72. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameEDID.py +0 -0
  73. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameEDID.pyi +0 -0
  74. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameEDIDProfile.pyi +0 -0
  75. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameFactoryReset.py +0 -0
  76. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameFactoryReset.pyi +0 -0
  77. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameFilterStatus.py +0 -0
  78. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameFilterStatus.pyi +0 -0
  79. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameFirmwareVersion.py +0 -0
  80. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameFirmwareVersion.pyi +0 -0
  81. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameHeader.py +0 -0
  82. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameHeader.pyi +0 -0
  83. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameHello.py +0 -0
  84. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameHello.pyi +0 -0
  85. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameLinks.py +0 -0
  86. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameLinks.pyi +0 -0
  87. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameMeshOperation.pyi +0 -0
  88. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameMirrorStatus.py +0 -0
  89. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameMirrorStatus.pyi +0 -0
  90. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameMonitoringPulse.py +0 -0
  91. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameMonitoringPulse.pyi +0 -0
  92. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameNetworkStatus.py +0 -0
  93. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameNetworkStatus.pyi +0 -0
  94. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FramePDUState.py +0 -0
  95. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FramePDUState.pyi +0 -0
  96. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FramePowerChange.py +0 -0
  97. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FramePowerChange.pyi +0 -0
  98. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameRCAction.pyi +0 -0
  99. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameRCIr.py +0 -0
  100. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameRCIr.pyi +0 -0
  101. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameRCKey.py +0 -0
  102. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameRCKey.pyi +0 -0
  103. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameRCSettings.py +0 -0
  104. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameRCSettings.pyi +0 -0
  105. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameReboot.pyi +0 -0
  106. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameRoutingChange.py +0 -0
  107. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameRoutingChange.pyi +0 -0
  108. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSetInstaller.py +0 -0
  109. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSetInstaller.pyi +0 -0
  110. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSetName.pyi +0 -0
  111. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSetRoute.py +0 -0
  112. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSetRoute.pyi +0 -0
  113. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSetupStatus.py +0 -0
  114. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSetupStatus.pyi +0 -0
  115. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSignalStatus.py +0 -0
  116. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSignalStatus.pyi +0 -0
  117. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSysTemperature.py +0 -0
  118. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSysTemperature.pyi +0 -0
  119. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSystemStatus.py +0 -0
  120. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameSystemStatus.pyi +0 -0
  121. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameTXRCAction.pyi +0 -0
  122. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameTXRCKey.pyi +0 -0
  123. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameTopology.py +0 -0
  124. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameTopology.pyi +0 -0
  125. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameTxIR.py +0 -0
  126. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameTxIR.pyi +0 -0
  127. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameUpgradeFPGA.py +0 -0
  128. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameUpgradeFPGA.pyi +0 -0
  129. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPAudio.pyi +0 -0
  130. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPBayMapping.py +0 -0
  131. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPBayMapping.pyi +0 -0
  132. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPDetectBays.py +0 -0
  133. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPDetectBays.pyi +0 -0
  134. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPDeviceConfiguration.py +0 -0
  135. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPDeviceConfiguration.pyi +0 -0
  136. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPLink.py +0 -0
  137. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPLink.pyi +0 -0
  138. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPManualSourceSwitch.pyi +0 -0
  139. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPMultiviewer.pyi +0 -0
  140. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPPowerSave.py +0 -0
  141. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPPowerSave.pyi +0 -0
  142. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPSetMaster.py +0 -0
  143. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPSetMaster.pyi +0 -0
  144. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPSourceSwitch.pyi +0 -0
  145. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPSources.py +0 -0
  146. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPSources.pyi +0 -0
  147. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPStats.py +0 -0
  148. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPStats.pyi +0 -0
  149. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPStreamDetails.py +0 -0
  150. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPStreamDetails.pyi +0 -0
  151. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPTiling.py +0 -0
  152. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPTiling.pyi +0 -0
  153. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPVideoWall.py +0 -0
  154. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameV2IPVideoWall.pyi +0 -0
  155. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameVolume.py +0 -0
  156. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameVolume.pyi +0 -0
  157. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameVolumeDown.py +0 -0
  158. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameVolumeDown.pyi +0 -0
  159. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameVolumeSet.pyi +0 -0
  160. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameVolumeUp.py +0 -0
  161. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/FrameVolumeUp.pyi +0 -0
  162. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/LinkConfig.py +0 -0
  163. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/LinkConfig.pyi +0 -0
  164. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/Multiviewer.py +0 -0
  165. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/Multiviewer.pyi +0 -0
  166. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/PDUState.py +0 -0
  167. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/PDUState.pyi +0 -0
  168. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/Svd.py +0 -0
  169. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/Svd.pyi +0 -0
  170. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/V2IPConfig.py +0 -0
  171. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/V2IPConfig.pyi +0 -0
  172. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/V2IPStats.py +0 -0
  173. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/V2IPStats.pyi +0 -0
  174. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/__init__.py +0 -0
  175. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/__init__.pyi +0 -0
  176. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/proto/svd.csv +0 -0
  177. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/py.typed +0 -0
  178. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/ConnectionAsync.py +0 -0
  179. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/ConnectionAsync.pyi +0 -0
  180. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/Link.py +0 -0
  181. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/Link.pyi +0 -0
  182. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/P8PDU.py +0 -0
  183. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/P8PDU.pyi +0 -0
  184. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/Remote.py +0 -0
  185. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/Remote.pyi +0 -0
  186. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/State.py +0 -0
  187. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/State.pyi +0 -0
  188. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/V2IP.py +0 -0
  189. {mx_remote-5.0.0 → mx_remote-5.1.0}/mx_remote/remote/V2IP.pyi +0 -0
  190. {mx_remote-5.0.0 → mx_remote-5.1.0}/pyproject.toml +0 -0
  191. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/audio_ir.py +0 -0
  192. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/capture.py +0 -0
  193. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/e2e.py +0 -0
  194. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/handlers.py +0 -0
  195. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/merge.py +0 -0
  196. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/newops.py +0 -0
  197. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/offsets.py +0 -0
  198. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/proto_stamp.py +0 -0
  199. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/rcsettings.py +0 -0
  200. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/smoke.py +0 -0
  201. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/sources.py +0 -0
  202. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/stats.py +0 -0
  203. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/structs.py +0 -0
  204. {mx_remote-5.0.0 → mx_remote-5.1.0}/tests/unknowns.py +0 -0
@@ -52,6 +52,19 @@ replaced. Its expected values come from firmware behaviour rather than from this
52
52
  decoder, which is the property a synthetic fixture cannot have — assert against
53
53
  something external, or a fixture only proves the decoder agrees with itself.
54
54
 
55
+ Two things a test of a *command* must do, both of which look like coverage
56
+ when they are missing:
57
+
58
+ - **Assert both directions.** A command that returns False for an unrelated
59
+ reason satisfies a failure-only assertion on its own, so the success case is
60
+ what gives the failure case meaning.
61
+ - **Pick an input that can reach the send.** Every multiviewer getter reads
62
+ `UNKNOWN` until a config frame arrives, and each setter returns True without
63
+ transmitting when the value already matches — so a table built from the first
64
+ enum member, which is `UNKNOWN`, never transmits at all.
65
+
66
+ `tests/txresult.py` is the worked example of both.
67
+
55
68
  Validate wire changes against live hardware as well, by running `mxr` or
56
69
  replaying a capture file with `mxr -i`.
57
70
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mx_remote
3
- Version: 5.0.0
3
+ Version: 5.1.0
4
4
  Summary: Python 3 library for interfacing with MX Remote compatible devices
5
5
  Project-URL: Homepage, https://github.com/opdenkamp/mx-remote/
6
6
  Project-URL: Documentation, https://github.com/opdenkamp/mx-remote/
@@ -173,10 +173,96 @@ class ConnectStatus(IntEnum):
173
173
  def __repr__(self) -> str:
174
174
  return str(self)
175
175
 
176
+ class VideoColourSpace(IntEnum):
177
+ '''Video colour space encoding format.'''
178
+ RGB = 0
179
+ YUV444 = 1
180
+ YUV422 = 2
181
+ YUV420 = 3
182
+
183
+ def __str__(self) -> str:
184
+ if self.value == 0:
185
+ return 'RGB'
186
+ if self.value == 1:
187
+ return '4:4:4'
188
+ if self.value == 2:
189
+ return '4:2:2'
190
+ if self.value == 3:
191
+ return '4:2:0'
192
+ return 'unknown'
193
+
194
+ class VideoSignalDetails:
195
+ """
196
+ Video format of a bay: CTA-861 svd, colour space, bit depth, frame rate kind.
197
+
198
+ ``from_report`` says where it came from, because the two sources do not carry
199
+ the same currency. A signal status report is the live format and arrives on a
200
+ signal change. The bay config snapshot is a fallback for a bay no report has
201
+ been seen for: it refreshes on the bay config broadcast cycle, so it can lag a
202
+ change by a broadcast interval.
203
+
204
+ ``bpp`` is a bit depth in both cases, not a wire index. None means no format
205
+ is set; 0 means one is set whose depth is not known, and those are different
206
+ answers.
207
+ """
208
+ def __init__(self, svd:int|None, colour:VideoColourSpace|None, bpp:int|None,
209
+ non_int:bool|None, from_report:bool) -> None:
210
+ self._svd = svd
211
+ self._colour = colour
212
+ self._bpp = bpp
213
+ self._non_int = non_int
214
+ self._from_report = from_report
215
+
216
+ @property
217
+ def svd(self) -> int|None:
218
+ '''CTA-861 short video descriptor, or None when no format is set.'''
219
+ return self._svd
220
+
221
+ @property
222
+ def colour(self) -> VideoColourSpace|None:
223
+ '''Colour space, or None when no format is set or the value is unknown to this build.'''
224
+ return self._colour
225
+
226
+ @property
227
+ def bpp(self) -> int|None:
228
+ '''Bit depth, 0 when a format is set whose depth is not known.'''
229
+ return self._bpp
230
+
231
+ @property
232
+ def non_int(self) -> bool|None:
233
+ '''True for a non-integer (1000/1001) frame rate.'''
234
+ return self._non_int
235
+
236
+ @property
237
+ def from_report(self) -> bool:
238
+ '''True when this came from a signal status report rather than the bay config snapshot.'''
239
+ return self._from_report
240
+
241
+ def __eq__(self, other:Any) -> bool:
242
+ if not isinstance(other, VideoSignalDetails):
243
+ return NotImplemented
244
+ return (self._svd == other._svd) and (self._colour == other._colour) \
245
+ and (self._bpp == other._bpp) and (self._non_int == other._non_int)
246
+
247
+ def __hash__(self) -> int:
248
+ return hash((self._svd, self._colour, self._bpp, self._non_int))
249
+
250
+ def __str__(self) -> str:
251
+ if self._svd is None:
252
+ return 'no format'
253
+ src = 'report' if self._from_report else 'bay config'
254
+ bpp = f', {self._bpp}bpp' if self._bpp else ''
255
+ return f'svd {self._svd}, {self._colour}{bpp} ({src})'
256
+
257
+ def __repr__(self) -> str:
258
+ return str(self)
259
+
176
260
  class SignalStatus:
177
- def __init__(self, detected:bool, description:str|None=None) -> None:
261
+ def __init__(self, detected:bool, description:str|None=None,
262
+ details:VideoSignalDetails|None=None) -> None:
178
263
  self._detected = detected
179
264
  self._description = description
265
+ self._details = details
180
266
 
181
267
  @property
182
268
  def detected(self) -> bool:
@@ -186,6 +272,11 @@ class SignalStatus:
186
272
  def description(self) -> str|None:
187
273
  return self._description
188
274
 
275
+ @property
276
+ def details(self) -> VideoSignalDetails|None:
277
+ '''Structured video format, or None when the report carried no valid stream.'''
278
+ return self._details
279
+
189
280
  class AmpDolbySettings:
190
281
  """
191
282
  Dolby Digital settings for an amplifier
@@ -984,6 +1075,15 @@ class BayBase(ABC):
984
1075
  def signal_type(self) -> str:
985
1076
  '''Audio or video signal type'''
986
1077
 
1078
+ @property
1079
+ @abstractmethod
1080
+ def video_details(self) -> VideoSignalDetails|None:
1081
+ '''Current video format, or None if neither source has carried one.
1082
+
1083
+ Prefers the signal status report, which arrives on a signal change, and
1084
+ falls back to the bay config snapshot for a bay no report has been seen
1085
+ for. Read from_report to tell which one this is.'''
1086
+
987
1087
  @property
988
1088
  @abstractmethod
989
1089
  def hpd_detected(self) -> bool:
@@ -1795,7 +1895,19 @@ class DeviceBase(ABC):
1795
1895
  @property
1796
1896
  @abstractmethod
1797
1897
  def protocol(self) -> int:
1798
- pass
1898
+ """Highest protocol version this device advertises, or 0 if none has been seen."""
1899
+
1900
+ @abstractmethod
1901
+ def supports_opcode(self, opcode:int) -> bool:
1902
+ """True when this device can receive opcode.
1903
+
1904
+ Each opcode has a minimum protocol version, and a device whose cap is
1905
+ lower drops the frame without answering - there is no NAK at any layer,
1906
+ so an ungated send looks like success. A device that has advertised no
1907
+ version yet returns True: refusing on that would break every send made
1908
+ before the first hello arrives, and the firmware itself only compares
1909
+ against a version it has.
1910
+ """
1799
1911
 
1800
1912
  @property
1801
1913
  @abstractmethod
@@ -48,12 +48,35 @@ class ConnectStatus(IntEnum):
48
48
  CONNECTED: int
49
49
  DISCONNECTED: int
50
50
 
51
+ class VideoColourSpace(IntEnum):
52
+ RGB: int
53
+ YUV444: int
54
+ YUV422: int
55
+ YUV420: int
56
+
57
+ class VideoSignalDetails:
58
+ def __init__(self, svd: int | None, colour: VideoColourSpace | None, bpp: int | None, non_int: bool | None, from_report: bool) -> None: ...
59
+ @property
60
+ def svd(self) -> int | None: ...
61
+ @property
62
+ def colour(self) -> VideoColourSpace | None: ...
63
+ @property
64
+ def bpp(self) -> int | None: ...
65
+ @property
66
+ def non_int(self) -> bool | None: ...
67
+ @property
68
+ def from_report(self) -> bool: ...
69
+ def __eq__(self, other: Any) -> bool: ...
70
+ def __hash__(self) -> int: ...
71
+
51
72
  class SignalStatus:
52
- def __init__(self, detected: bool, description: str | None = None) -> None: ...
73
+ def __init__(self, detected: bool, description: str | None = None, details: VideoSignalDetails | None = None) -> None: ...
53
74
  @property
54
75
  def detected(self) -> bool: ...
55
76
  @property
56
77
  def description(self) -> str | None: ...
78
+ @property
79
+ def details(self) -> VideoSignalDetails | None: ...
57
80
 
58
81
  class AmpDolbySettings:
59
82
  mode: int
@@ -420,6 +443,9 @@ class BayBase(ABC):
420
443
  def signal_type(self) -> str: ...
421
444
  @property
422
445
  @abstractmethod
446
+ def video_details(self) -> VideoSignalDetails | None: ...
447
+ @property
448
+ @abstractmethod
423
449
  def hpd_detected(self) -> bool: ...
424
450
  @property
425
451
  @abstractmethod
@@ -796,6 +822,8 @@ class DeviceBase(ABC):
796
822
  @property
797
823
  @abstractmethod
798
824
  def protocol(self) -> int: ...
825
+ @abstractmethod
826
+ def supports_opcode(self, opcode: int) -> bool: ...
799
827
  @property
800
828
  @abstractmethod
801
829
  def is_mesh_master(self) -> bool: ...
@@ -14,7 +14,7 @@ Default communication modes:
14
14
  """
15
15
  import os
16
16
 
17
- VERSION = '5.0.0'
17
+ VERSION = '5.1.0'
18
18
  __version__ = VERSION
19
19
 
20
20
  MX_BCAST_UDP_PORT = 8811
@@ -65,7 +65,7 @@ class FrameAmpZoneSettings(FrameBase):
65
65
  for r in range(5):
66
66
  payload.append(settings.eq_right[r] & 0xFF)
67
67
  payload += bytes([0, 0]) # padding
68
- return FrameBase.construct_base(mxr=mxr, opcode=0x3D, protocol=0x1C, payload=payload)
68
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x3D, protocol=0x1C, payload=payload)
69
69
 
70
70
  @property
71
71
  def target_device(self) -> DeviceBase|None:
@@ -7,6 +7,8 @@
7
7
 
8
8
  '''Base class and utilities for decoded MX Remote protocol frames.'''
9
9
 
10
+ from typing import Any
11
+ import logging
10
12
  from functools import cached_property
11
13
  import time
12
14
  from .Constants import MXR_OPCODE_VERSIONS, MXR_PROTOCOL_VERSION
@@ -14,6 +16,8 @@ from .FrameHeader import FrameHeader
14
16
  from ..Interface import DeviceBase, DeviceRegistry, BayBase
15
17
  from ..Uid import MxrDeviceUid
16
18
 
19
+ _LOGGER = logging.getLogger(__name__)
20
+
17
21
  def append_payload_str(payload:list[int], value:str, sz:int) -> list[int]:
18
22
  '''Append a fixed-size ASCII string to a payload byte list, zero-padded to sz.
19
23
 
@@ -46,14 +50,19 @@ class FrameBase:
46
50
  return MXR_OPCODE_VERSIONS.get(opcode, MXR_PROTOCOL_VERSION)
47
51
 
48
52
  @staticmethod
49
- def construct_base(mxr:DeviceRegistry, opcode:int, protocol:int|None=None, payload:bytes=bytes([]), size:int|None=None) -> 'FrameBase|None':
53
+ def construct_base(mxr:DeviceRegistry, opcode:int, protocol:int|None=None, payload:bytes=bytes([]), size:int|None=None, target:Any=None) -> 'FrameBase|None':
50
54
  '''Construct a new frame for transmission with the given opcode and payload.
51
55
 
52
56
  Leave protocol unset unless the payload really does need a newer
53
57
  receiver than the opcode itself does; it then defaults to the opcode's
54
- own minimum from MXR_OPCODE_VERSIONS.'''
58
+ own minimum from MXR_OPCODE_VERSIONS.
59
+
60
+ Pass target for anything addressed at one device, so a frame it cannot
61
+ receive is refused here rather than sent into silence.'''
55
62
  if (protocol is None):
56
63
  protocol = FrameBase.opcode_protocol(opcode)
64
+ if not FrameBase.target_supports(mxr=mxr, target=target, opcode=opcode):
65
+ return None
57
66
  header = FrameHeader.construct(mxr=mxr, opcode=opcode, protocol=protocol)
58
67
  if (header is None):
59
68
  return None
@@ -66,6 +75,28 @@ class FrameBase:
66
75
  rv.payload = payload
67
76
  return rv
68
77
 
78
+ @staticmethod
79
+ def target_supports(mxr:DeviceRegistry, target:Any, opcode:int) -> bool:
80
+ '''Whether a frame addressed at target can be received by it.
81
+
82
+ Accepts what the constructFrame helpers are given: a device, a bay, a
83
+ uid, or None for a broadcast, which has no single target and is always
84
+ allowed.
85
+ '''
86
+ if (target is None):
87
+ return True
88
+ device = getattr(target, 'device', None) or (target if hasattr(target, 'supports_opcode') else None)
89
+ if (device is None):
90
+ device = mxr.get_by_uid(target) if isinstance(target, (str, bytes, MxrDeviceUid)) else None
91
+ if (device is None) or not hasattr(device, 'supports_opcode'):
92
+ return True
93
+ if device.supports_opcode(opcode):
94
+ return True
95
+ _LOGGER.warning(
96
+ f"not sending opcode {opcode:02X} to {device}: it advertises protocol "
97
+ f"{device.protocol:02X} and the opcode needs {FrameBase.opcode_protocol(opcode):02X}")
98
+ return False
99
+
69
100
  @property
70
101
  def mxr(self) -> DeviceRegistry:
71
102
  '''Remote instance.'''
@@ -4,6 +4,7 @@ from .Constants import MXR_OPCODE_VERSIONS as MXR_OPCODE_VERSIONS, MXR_PROTOCOL_
4
4
  from .FrameHeader import FrameHeader as FrameHeader
5
5
  from _typeshed import Incomplete
6
6
  from functools import cached_property
7
+ from typing import Any
7
8
 
8
9
  def append_payload_str(payload: list[int], value: str, sz: int) -> list[int]: ...
9
10
 
@@ -14,7 +15,9 @@ class FrameBase:
14
15
  @staticmethod
15
16
  def opcode_protocol(opcode: int) -> int: ...
16
17
  @staticmethod
17
- def construct_base(mxr: DeviceRegistry, opcode: int, protocol: int | None = None, payload: bytes = ..., size: int | None = None) -> FrameBase | None: ...
18
+ def construct_base(mxr: DeviceRegistry, opcode: int, protocol: int | None = None, payload: bytes = ..., size: int | None = None, target: Any = None) -> FrameBase | None: ...
19
+ @staticmethod
20
+ def target_supports(mxr: DeviceRegistry, target: Any, opcode: int) -> bool: ...
18
21
  @property
19
22
  def mxr(self) -> DeviceRegistry: ...
20
23
  @property
@@ -22,7 +22,7 @@ class FrameBayHide(FrameBase):
22
22
  bytes([(target.port >> 0) & 0xFF, (target.port >> 8) & 0xFF]) + \
23
23
  bytes([1 if hidden else 0]) + \
24
24
  bytes([0 for _ in range(5)])
25
- return FrameBase.construct_base(mxr=mxr, opcode=0x27, payload=payload)
25
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x27, payload=payload)
26
26
 
27
27
  @cached_property
28
28
  def target(self) -> DeviceBase|None:
@@ -17,7 +17,7 @@ class FrameEDIDProfile(FrameBase):
17
17
  payload = target.remote_id.byte_value + \
18
18
  bytes([(profile.value >> 0) & 0xFF, (profile.value >> 8) & 0xFF]) + \
19
19
  bytes([0 for _ in range(6)])
20
- return FrameBase.construct_base(mxr=mxr, opcode=0x34, payload=payload)
20
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x34, payload=payload)
21
21
 
22
22
  def __str__(self) -> str:
23
23
  return f"EDID profile change"
@@ -55,7 +55,7 @@ class FrameMeshOperation(FrameBase):
55
55
  payload += option.remote_id.byte_value
56
56
  else:
57
57
  payload += bytes([0 for _ in range(16)])
58
- return FrameBase.construct_base(mxr=mxr, opcode=0x3B, payload=payload)
58
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x3B, payload=payload)
59
59
 
60
60
  @cached_property
61
61
  def operation(self) -> MeshOperation|None:
@@ -21,7 +21,7 @@ class FrameRCAction(FrameBase):
21
21
  '''Build an RC action frame for transmission.'''
22
22
  payload = target.device.remote_id.byte_value
23
23
  payload += bytes([(target.port & 0xFF), ((target.port >> 8) & 0xFF), (int(action.value) & 0xFF), ((int(action.value) >> 8) & 0xFF)])
24
- return FrameBase.construct_base(mxr=mxr, opcode=0x0E, payload=payload)
24
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x0E, payload=payload)
25
25
 
26
26
  @cached_property
27
27
  def bay(self) -> BayBase|None:
@@ -15,7 +15,7 @@ class FrameReboot(FrameBase):
15
15
  @staticmethod
16
16
  def construct(mxr:DeviceRegistry, target:DeviceBase) -> FrameBase|None:
17
17
  '''Build a reboot frame for transmission to the target device.'''
18
- return FrameBase.construct_base(mxr=mxr, opcode=0x28, payload=target.remote_id.byte_value)
18
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x28, payload=target.remote_id.byte_value)
19
19
 
20
20
  def __str__(self) -> str:
21
21
  return "reboot"
@@ -20,7 +20,7 @@ class FrameSetName(FrameBase):
20
20
  payload = target.device.remote_id.byte_value + \
21
21
  bytes([(target.port >> 0) & 0xFF, (target.port >> 8) & 0xFF]) + \
22
22
  name_bytes
23
- return FrameBase.construct_base(mxr=mxr, opcode=0x22, protocol=0x11, payload=payload)
23
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x22, protocol=0x11, payload=payload)
24
24
 
25
25
  def __str__(self) -> str:
26
26
  return f"Name change"
@@ -9,29 +9,12 @@
9
9
  from enum import IntEnum
10
10
  from functools import cached_property
11
11
  from .FrameBase import FrameBase
12
- from ..Interface import BayBase, SignalStatus
12
+ from ..Interface import VideoColourSpace, VideoSignalDetails, BayBase, SignalStatus
13
13
  from ..Uid import MxrDeviceUid
14
14
  import struct
15
15
  from .Svd import SvdMap, Svd
16
16
  from .Constants import BayStatusMask, MxrSignalType, decode_enum
17
17
 
18
- class VideoColourSpace(IntEnum):
19
- '''Video colour space encoding format.'''
20
- RGB = 0
21
- YUV444 = 1
22
- YUV422 = 2
23
- YUV420 = 3
24
-
25
- def __str__(self) -> str:
26
- if self.value == 0:
27
- return 'RGB'
28
- if self.value == 1:
29
- return '4:4:4'
30
- if self.value == 2:
31
- return '4:2:2'
32
- if self.value == 3:
33
- return '4:2:0'
34
- return 'unknown'
35
18
 
36
19
  class AvDetailsSupportFlags:
37
20
  '''Bitmask indicating which AV detail fields are present in the frame.'''
@@ -318,9 +301,17 @@ class FrameSignalStatusNew(FrameBase):
318
301
  if self.stream_flags.video_hdr:
319
302
  signal_type += ' HDR'
320
303
  signal_type += f' / {self.frame_rate}Hz'
304
+ details = VideoSignalDetails(
305
+ svd=self.video.svd.id,
306
+ colour=self.video.colour_space,
307
+ bpp=self.video.colour_depth,
308
+ non_int=(self.stream_flags is not None) and self.stream_flags.video_non_int_clock,
309
+ from_report=True)
321
310
  else:
322
311
  signal_type = 'No Signal'
323
- bay.on_mxr_update(SignalStatus(detected=self.stream_valid, description=signal_type))
312
+ details = None
313
+ bay.on_mxr_update(SignalStatus(detected=self.stream_valid, description=signal_type,
314
+ details=details))
324
315
 
325
316
  def __str__(self) -> str:
326
317
  if (self.payload is None):
@@ -1,18 +1,12 @@
1
- from ..Interface import BayBase as BayBase, SignalStatus as SignalStatus
1
+ from ..Interface import BayBase as BayBase, SignalStatus as SignalStatus, VideoColourSpace as VideoColourSpace, VideoSignalDetails as VideoSignalDetails
2
2
  from ..Uid import MxrDeviceUid as MxrDeviceUid
3
3
  from .Constants import BayStatusMask as BayStatusMask, MxrSignalType as MxrSignalType, decode_enum as decode_enum
4
4
  from .FrameBase import FrameBase as FrameBase
5
5
  from .Svd import Svd as Svd, SvdMap as SvdMap
6
6
  from _typeshed import Incomplete
7
- from enum import IntEnum
7
+ from enum import IntEnum as IntEnum
8
8
  from functools import cached_property
9
9
 
10
- class VideoColourSpace(IntEnum):
11
- RGB: int
12
- YUV444: int
13
- YUV422: int
14
- YUV420: int
15
-
16
10
  class AvDetailsSupportFlags:
17
11
  data: Incomplete
18
12
  def __init__(self, data: int) -> None: ...
@@ -30,7 +30,7 @@ class FrameTXRCAction(FrameBase):
30
30
  '''Build an RC action frame for transmission to the target bay.'''
31
31
  payload = target.device.remote_id.byte_value
32
32
  payload += bytes([(target.port & 0xFF), ((target.port >> 8) & 0xFF), (int(action.value) & 0xFF), ((int(action.value) >> 8) & 0xFF)])
33
- return FrameBase.construct_base(mxr=mxr, opcode=0x0E, payload=payload)
33
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x0E, payload=payload)
34
34
 
35
35
  @cached_property
36
36
  def target_device(self) -> DeviceBase|None:
@@ -30,7 +30,7 @@ class FrameTXRCKey(FrameBase):
30
30
  payload = target.device.remote_id.byte_value
31
31
  payload += bytes([(target.port & 0xFF), ((target.port >> 8) & 0xFF),
32
32
  (int(key.value) & 0xFF), ((int(key.value) >> 8) & 0xFF)])
33
- return FrameBase.construct_base(mxr=mxr, opcode=0x0C, payload=payload)
33
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x0C, payload=payload)
34
34
 
35
35
  @cached_property
36
36
  def target_uid(self) -> MxrDeviceUid|None:
@@ -732,17 +732,17 @@ class FrameV2IPAudio(FrameBase):
732
732
  @staticmethod
733
733
  def construct_mute(mxr:DeviceRegistry, target:MxrDeviceUid, endpoint_id:int, mute:bool) -> FrameBase|None:
734
734
  body = _audio_cmd_header(AudioCommandOpcode.MUTE, target) + _pack_audio_param(endpoint_id, 1 if mute else 0)
735
- return FrameBase.construct_base(mxr=mxr, opcode=0x43, payload=body)
735
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x43, payload=body)
736
736
 
737
737
  @staticmethod
738
738
  def construct_trigger(mxr:DeviceRegistry, target:MxrDeviceUid, endpoint_id:int, trigger:bool) -> FrameBase|None:
739
739
  body = _audio_cmd_header(AudioCommandOpcode.TRIGGER, target) + _pack_audio_param(endpoint_id, 1 if trigger else 0)
740
- return FrameBase.construct_base(mxr=mxr, opcode=0x43, payload=body)
740
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x43, payload=body)
741
741
 
742
742
  @staticmethod
743
743
  def construct_volume(mxr:DeviceRegistry, target:MxrDeviceUid, endpoint_id:int, volume:int) -> FrameBase|None:
744
744
  body = _audio_cmd_header(AudioCommandOpcode.VOLUME, target) + _pack_audio_param(endpoint_id, volume & 0xFFFFFFFF)
745
- return FrameBase.construct_base(mxr=mxr, opcode=0x43, payload=body)
745
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x43, payload=body)
746
746
 
747
747
  @cached_property
748
748
  def opcode(self) -> AudioCommandOpcode:
@@ -50,7 +50,7 @@ class FrameV2IPManualSourceSwitch(FrameBase):
50
50
  payload += bytes([port & 0xFF, (port >> 8) & 0xFF, 0, 0])
51
51
  if audio_fmt is not None:
52
52
  payload += audio_fmt.value
53
- return FrameBase.construct_base(mxr=mxr, opcode=_OPCODE, payload=bytes(payload))
53
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=_OPCODE, payload=bytes(payload))
54
54
 
55
55
  @cached_property
56
56
  def target_uid(self) -> MxrDeviceUid|None:
@@ -241,7 +241,7 @@ class FrameV2IPMultiviewer(FrameBase):
241
241
  payload.append(MultiviewerOpcode.VIEW_MODE.value)
242
242
  payload += bytes([0 for _ in range(7)])
243
243
  payload.append(view_mode.value)
244
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
244
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
245
245
 
246
246
  @staticmethod
247
247
  def construct_set_video_source(mxr:DeviceRegistry, target:DeviceBase, screen:int, source:MultiviewerSource) -> FrameBase|None:
@@ -252,7 +252,7 @@ class FrameV2IPMultiviewer(FrameBase):
252
252
  payload += bytes([0 for _ in range(7)])
253
253
  payload.append(screen)
254
254
  payload.append(source.value)
255
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
255
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
256
256
 
257
257
  @staticmethod
258
258
  def construct_set_audio_source(mxr:DeviceRegistry, target:DeviceBase, source:MultiviewerSource) -> FrameBase|None:
@@ -262,7 +262,7 @@ class FrameV2IPMultiviewer(FrameBase):
262
262
  payload.append(MultiviewerOpcode.AUDIO_SOURCE.value)
263
263
  payload += bytes([0 for _ in range(7)])
264
264
  payload.append(source.value - 1)
265
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
265
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
266
266
 
267
267
  @staticmethod
268
268
  def construct_set_audio_volume(mxr:DeviceRegistry, target:DeviceBase, volume:int, muted:bool) -> FrameBase|None:
@@ -273,7 +273,7 @@ class FrameV2IPMultiviewer(FrameBase):
273
273
  payload += bytes([0 for _ in range(7)])
274
274
  payload.append(volume)
275
275
  payload.append(1 if muted else 0)
276
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
276
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
277
277
 
278
278
  @staticmethod
279
279
  def construct_set_edid_template(mxr:DeviceRegistry, target:DeviceBase, edid:MultiviewerEDIDTemplate) -> FrameBase|None:
@@ -283,7 +283,7 @@ class FrameV2IPMultiviewer(FrameBase):
283
283
  payload.append(MultiviewerOpcode.EDID_TEMPLATE.value)
284
284
  payload += bytes([0 for _ in range(7)])
285
285
  payload.append(edid.value)
286
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
286
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
287
287
 
288
288
  @staticmethod
289
289
  def construct_set_remote_control(mxr:DeviceRegistry, target:DeviceBase, source:MultiviewerSource) -> FrameBase|None:
@@ -293,7 +293,7 @@ class FrameV2IPMultiviewer(FrameBase):
293
293
  payload.append(MultiviewerOpcode.ROUTE_RC.value)
294
294
  payload += bytes([0 for _ in range(7)])
295
295
  payload.append(source.value - 1)
296
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
296
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
297
297
 
298
298
  @staticmethod
299
299
  def construct_set_pip_size(mxr:DeviceRegistry, target:DeviceBase, size:MultiviewerPipSize) -> FrameBase|None:
@@ -303,7 +303,7 @@ class FrameV2IPMultiviewer(FrameBase):
303
303
  payload.append(MultiviewerOpcode.PIP_SIZE.value)
304
304
  payload += bytes([0 for _ in range(7)])
305
305
  payload.append(size.value)
306
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
306
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
307
307
 
308
308
  @staticmethod
309
309
  def construct_set_pip_position(mxr:DeviceRegistry, target:DeviceBase, position:MultiviewerPipPosition) -> FrameBase|None:
@@ -313,7 +313,7 @@ class FrameV2IPMultiviewer(FrameBase):
313
313
  payload.append(MultiviewerOpcode.PIP_POSITION.value)
314
314
  payload += bytes([0 for _ in range(7)])
315
315
  payload.append(position.value)
316
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
316
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
317
317
 
318
318
  @staticmethod
319
319
  def construct_set_screen_aspect(mxr:DeviceRegistry, target:DeviceBase, aspect:MultiviewerAspectRatio) -> FrameBase|None:
@@ -323,7 +323,7 @@ class FrameV2IPMultiviewer(FrameBase):
323
323
  payload.append(MultiviewerOpcode.ASPECT.value)
324
324
  payload += bytes([0 for _ in range(7)])
325
325
  payload.append(aspect.value)
326
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
326
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
327
327
 
328
328
  @staticmethod
329
329
  def construct_set_auto_switch(mxr:DeviceRegistry, target:DeviceBase, enable:bool) -> FrameBase|None:
@@ -333,7 +333,7 @@ class FrameV2IPMultiviewer(FrameBase):
333
333
  payload.append(MultiviewerOpcode.AUTO_SWITCH.value)
334
334
  payload += bytes([0 for _ in range(7)])
335
335
  payload.append(1 if enable else 0)
336
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
336
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
337
337
 
338
338
  @staticmethod
339
339
  def construct_set_output_mode(mxr:DeviceRegistry, target:DeviceBase, mode:MultiviewerOutputMode) -> FrameBase|None:
@@ -343,7 +343,7 @@ class FrameV2IPMultiviewer(FrameBase):
343
343
  payload.append(MultiviewerOpcode.OUTPUT_MODE.value)
344
344
  payload += bytes([0 for _ in range(7)])
345
345
  payload.append(mode.value)
346
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
346
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
347
347
 
348
348
  @staticmethod
349
349
  def construct_set_output_itc_mode(mxr:DeviceRegistry, target:DeviceBase, mode:MultiviewerITCMode) -> FrameBase|None:
@@ -353,7 +353,7 @@ class FrameV2IPMultiviewer(FrameBase):
353
353
  payload.append(MultiviewerOpcode.OUTPUT_ITC_MODE.value)
354
354
  payload += bytes([0 for _ in range(7)])
355
355
  payload.append(mode.value)
356
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
356
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
357
357
 
358
358
  @staticmethod
359
359
  def construct_set_hdcp_mode(mxr:DeviceRegistry, target:DeviceBase, mode:MultiviewerHDCPMode) -> FrameBase|None:
@@ -363,7 +363,7 @@ class FrameV2IPMultiviewer(FrameBase):
363
363
  payload.append(MultiviewerOpcode.HDCP_MODE.value)
364
364
  payload += bytes([0 for _ in range(7)])
365
365
  payload.append(mode.value)
366
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
366
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
367
367
 
368
368
  @staticmethod
369
369
  def construct_set_connected_source(mxr:DeviceRegistry, target:DeviceBase, input:int, source:MxrDeviceUid|None) -> FrameBase|None:
@@ -378,7 +378,7 @@ class FrameV2IPMultiviewer(FrameBase):
378
378
  payload += source.byte_value
379
379
  payload.append(input)
380
380
  payload += bytes([0 for _ in range(7)])
381
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
381
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
382
382
 
383
383
  @staticmethod
384
384
  def construct_auto_route(mxr:DeviceRegistry, target:DeviceBase) -> FrameBase|None:
@@ -387,7 +387,7 @@ class FrameV2IPMultiviewer(FrameBase):
387
387
  payload += target.remote_id.byte_value
388
388
  payload.append(MultiviewerOpcode.AUTO_ROUTE.value)
389
389
  payload += bytes([0 for _ in range(7)])
390
- return FrameBase.construct_base(mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
390
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=0x42, protocol=0x20, payload=payload)
391
391
 
392
392
  @staticmethod
393
393
  def construct_status(