mx-remote 9.0.0__tar.gz → 9.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 (271) hide show
  1. {mx_remote-9.0.0 → mx_remote-9.1.0}/PKG-INFO +1 -1
  2. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/api.md +17 -0
  3. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/oneip.md +25 -0
  4. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/Interface.py +52 -0
  5. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/Interface.pyi +15 -0
  6. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/Uid.py +11 -8
  7. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/const.py +1 -1
  8. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/Constants.py +19 -0
  9. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/Constants.pyi +11 -0
  10. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPDeviceConfiguration.py +66 -6
  11. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPDeviceConfiguration.pyi +8 -2
  12. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPSources.py +5 -6
  13. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/Bay.py +2 -1
  14. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/Device.py +51 -1
  15. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/Device.pyi +6 -2
  16. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/Remote.py +43 -16
  17. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/complete.py +12 -0
  18. mx_remote-9.1.0/tests/downmix.py +124 -0
  19. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/run.py +2 -0
  20. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/staterequest.py +64 -0
  21. mx_remote-9.1.0/tests/uid.py +50 -0
  22. {mx_remote-9.0.0 → mx_remote-9.1.0}/.gitignore +0 -0
  23. {mx_remote-9.0.0 → mx_remote-9.1.0}/AGENTS.md +0 -0
  24. {mx_remote-9.0.0 → mx_remote-9.1.0}/CLAUDE.md +0 -0
  25. {mx_remote-9.0.0 → mx_remote-9.1.0}/LICENSE +0 -0
  26. {mx_remote-9.0.0 → mx_remote-9.1.0}/README.md +0 -0
  27. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/README.md +0 -0
  28. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/_static/brand.css +0 -0
  29. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/_static/favicon.svg +0 -0
  30. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/_static/logo-dark.svg +0 -0
  31. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/_static/logo.svg +0 -0
  32. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/audio.md +0 -0
  33. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/callbacks.md +0 -0
  34. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/conf.py +0 -0
  35. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/configuration.md +0 -0
  36. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/devices-and-bays.md +0 -0
  37. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/diagnostics.md +0 -0
  38. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/index.md +0 -0
  39. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/multiviewer.md +0 -0
  40. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/requirements.txt +0 -0
  41. {mx_remote-9.0.0 → mx_remote-9.1.0}/docs/routing.md +0 -0
  42. {mx_remote-9.0.0 → mx_remote-9.1.0}/hatch_build.py +0 -0
  43. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/Uid.pyi +0 -0
  44. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/__init__.py +0 -0
  45. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/__init__.pyi +0 -0
  46. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/compat.py +0 -0
  47. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/compat.pyi +0 -0
  48. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/const.pyi +0 -0
  49. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/deprecated.py +0 -0
  50. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/deprecated.pyi +0 -0
  51. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/main.py +0 -0
  52. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/main.pyi +0 -0
  53. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/BayConfig.py +0 -0
  54. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/BayConfig.pyi +0 -0
  55. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/Data.py +0 -0
  56. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/Data.pyi +0 -0
  57. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/Factory.py +0 -0
  58. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/Factory.pyi +0 -0
  59. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameAmpDolbySettings.py +0 -0
  60. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameAmpDolbySettings.pyi +0 -0
  61. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameAmpZoneSettings.py +0 -0
  62. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameAmpZoneSettings.pyi +0 -0
  63. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameAudioClip.py +0 -0
  64. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameAudioClip.pyi +0 -0
  65. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameAudioSetRoute.py +0 -0
  66. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameAudioSetRoute.pyi +0 -0
  67. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBase.py +0 -0
  68. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBase.pyi +0 -0
  69. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBayConfig.py +0 -0
  70. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBayConfig.pyi +0 -0
  71. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBayConfigSecondary.py +0 -0
  72. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBayConfigSecondary.pyi +0 -0
  73. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBayContent.py +0 -0
  74. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBayContent.pyi +0 -0
  75. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBayHide.py +0 -0
  76. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBayHide.pyi +0 -0
  77. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBayStatus.py +0 -0
  78. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameBayStatus.pyi +0 -0
  79. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameConnectStatus.py +0 -0
  80. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameConnectStatus.pyi +0 -0
  81. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameDebug.py +0 -0
  82. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameDebug.pyi +0 -0
  83. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameDiscover.py +0 -0
  84. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameDiscover.pyi +0 -0
  85. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameEDID.py +0 -0
  86. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameEDID.pyi +0 -0
  87. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameEDIDProfile.py +0 -0
  88. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameEDIDProfile.pyi +0 -0
  89. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameFactoryReset.py +0 -0
  90. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameFactoryReset.pyi +0 -0
  91. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameFilterStatus.py +0 -0
  92. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameFilterStatus.pyi +0 -0
  93. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameFirmwareVersion.py +0 -0
  94. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameFirmwareVersion.pyi +0 -0
  95. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameHeader.py +0 -0
  96. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameHeader.pyi +0 -0
  97. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameHello.py +0 -0
  98. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameHello.pyi +0 -0
  99. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameLinks.py +0 -0
  100. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameLinks.pyi +0 -0
  101. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameMeshOperation.py +0 -0
  102. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameMeshOperation.pyi +0 -0
  103. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameMirrorStatus.py +0 -0
  104. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameMirrorStatus.pyi +0 -0
  105. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameMonitoringPulse.py +0 -0
  106. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameMonitoringPulse.pyi +0 -0
  107. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameNetworkStatus.py +0 -0
  108. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameNetworkStatus.pyi +0 -0
  109. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FramePing.py +0 -0
  110. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FramePing.pyi +0 -0
  111. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FramePowerChange.py +0 -0
  112. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FramePowerChange.pyi +0 -0
  113. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameRCAction.py +0 -0
  114. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameRCAction.pyi +0 -0
  115. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameRCIr.py +0 -0
  116. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameRCIr.pyi +0 -0
  117. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameRCKey.py +0 -0
  118. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameRCKey.pyi +0 -0
  119. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameRCSettings.py +0 -0
  120. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameRCSettings.pyi +0 -0
  121. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameReboot.py +0 -0
  122. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameReboot.pyi +0 -0
  123. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameRoutingChange.py +0 -0
  124. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameRoutingChange.pyi +0 -0
  125. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSetInstaller.py +0 -0
  126. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSetInstaller.pyi +0 -0
  127. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSetName.py +0 -0
  128. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSetName.pyi +0 -0
  129. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSetRoute.py +0 -0
  130. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSetRoute.pyi +0 -0
  131. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSetupStatus.py +0 -0
  132. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSetupStatus.pyi +0 -0
  133. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSignalStatus.py +0 -0
  134. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSignalStatus.pyi +0 -0
  135. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSignalStatusNew.py +0 -0
  136. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSignalStatusNew.pyi +0 -0
  137. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSysTemperature.py +0 -0
  138. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSysTemperature.pyi +0 -0
  139. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSystemStatus.py +0 -0
  140. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameSystemStatus.pyi +0 -0
  141. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTXRCAction.py +0 -0
  142. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTXRCAction.pyi +0 -0
  143. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTXRCKey.py +0 -0
  144. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTXRCKey.pyi +0 -0
  145. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTime.py +0 -0
  146. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTime.pyi +0 -0
  147. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTimeZone.py +0 -0
  148. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTimeZone.pyi +0 -0
  149. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTopology.py +0 -0
  150. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTopology.pyi +0 -0
  151. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTxIR.py +0 -0
  152. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameTxIR.pyi +0 -0
  153. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameUnitCommand.py +0 -0
  154. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameUnitCommand.pyi +0 -0
  155. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameUnitStatus.py +0 -0
  156. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameUnitStatus.pyi +0 -0
  157. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameUpgradeFPGA.py +0 -0
  158. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameUpgradeFPGA.pyi +0 -0
  159. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPAudio.py +0 -0
  160. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPAudio.pyi +0 -0
  161. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPBayMapping.py +0 -0
  162. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPBayMapping.pyi +0 -0
  163. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPDetectBays.py +0 -0
  164. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPDetectBays.pyi +0 -0
  165. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPLink.py +0 -0
  166. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPLink.pyi +0 -0
  167. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPManualSourceSwitch.py +0 -0
  168. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPManualSourceSwitch.pyi +0 -0
  169. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPMultiviewer.py +0 -0
  170. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPMultiviewer.pyi +0 -0
  171. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPOsdSync.py +0 -0
  172. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPOsdSync.pyi +0 -0
  173. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPPowerSave.py +0 -0
  174. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPPowerSave.pyi +0 -0
  175. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPPrivacy.py +0 -0
  176. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPPrivacy.pyi +0 -0
  177. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPSetMaster.py +0 -0
  178. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPSetMaster.pyi +0 -0
  179. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPSettingsAll.py +0 -0
  180. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPSettingsAll.pyi +0 -0
  181. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPSinkSdp.py +0 -0
  182. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPSinkSdp.pyi +0 -0
  183. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPSourceSwitch.py +0 -0
  184. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPSourceSwitch.pyi +0 -0
  185. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPSources.pyi +0 -0
  186. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPStats.py +0 -0
  187. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPStats.pyi +0 -0
  188. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPTestcard.py +0 -0
  189. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPTestcard.pyi +0 -0
  190. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPTiling.py +0 -0
  191. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPTiling.pyi +0 -0
  192. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPVideoWall.py +0 -0
  193. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameV2IPVideoWall.pyi +0 -0
  194. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameVolume.py +0 -0
  195. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameVolume.pyi +0 -0
  196. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameVolumeDown.py +0 -0
  197. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameVolumeDown.pyi +0 -0
  198. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameVolumeSet.py +0 -0
  199. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameVolumeSet.pyi +0 -0
  200. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameVolumeUp.py +0 -0
  201. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/FrameVolumeUp.pyi +0 -0
  202. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/LinkConfig.py +0 -0
  203. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/LinkConfig.pyi +0 -0
  204. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/Multiviewer.py +0 -0
  205. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/Multiviewer.pyi +0 -0
  206. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/Svd.py +0 -0
  207. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/Svd.pyi +0 -0
  208. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/V2IPConfig.py +0 -0
  209. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/V2IPConfig.pyi +0 -0
  210. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/V2IPStats.py +0 -0
  211. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/V2IPStats.pyi +0 -0
  212. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/__init__.py +0 -0
  213. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/__init__.pyi +0 -0
  214. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/proto/svd.csv +0 -0
  215. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/py.typed +0 -0
  216. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/Bay.pyi +0 -0
  217. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/ConnectionAsync.py +0 -0
  218. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/ConnectionAsync.pyi +0 -0
  219. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/Link.py +0 -0
  220. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/Link.pyi +0 -0
  221. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/OsdSync.py +0 -0
  222. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/OsdSync.pyi +0 -0
  223. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/Remote.pyi +0 -0
  224. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/State.py +0 -0
  225. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/State.pyi +0 -0
  226. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/V2IP.py +0 -0
  227. {mx_remote-9.0.0 → mx_remote-9.1.0}/mx_remote/remote/V2IP.pyi +0 -0
  228. {mx_remote-9.0.0 → mx_remote-9.1.0}/pyproject.toml +0 -0
  229. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/audio_ir.py +0 -0
  230. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/audiolock.py +0 -0
  231. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/baycontent.py +0 -0
  232. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/capture.py +0 -0
  233. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/deprecated.py +0 -0
  234. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/e2e.py +0 -0
  235. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/entry.py +0 -0
  236. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/handlers.py +0 -0
  237. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/handlers_cover.py +0 -0
  238. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/hello.py +0 -0
  239. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/idempotent.py +0 -0
  240. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/lengths.py +0 -0
  241. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/merge.py +0 -0
  242. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/mesh.py +0 -0
  243. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/meshbays.py +0 -0
  244. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/newops.py +0 -0
  245. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/offline.py +0 -0
  246. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/offsets.py +0 -0
  247. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/osdmessage.py +0 -0
  248. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/osdsync.py +0 -0
  249. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/ping.py +0 -0
  250. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/privacy.py +0 -0
  251. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/proto_stamp.py +0 -0
  252. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/protogate.py +0 -0
  253. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/rcsettings.py +0 -0
  254. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/roundtrip.py +0 -0
  255. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/routes.py +0 -0
  256. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/smoke.py +0 -0
  257. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/sources.py +0 -0
  258. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/stats.py +0 -0
  259. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/structs.py +0 -0
  260. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/txresult.py +0 -0
  261. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/unit.py +0 -0
  262. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/unknowns.py +0 -0
  263. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/v2ipptp.py +0 -0
  264. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/v2ipscaling.py +0 -0
  265. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/v2ipsdp.py +0 -0
  266. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/v2ipsettings.py +0 -0
  267. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/v2ipsubject.py +0 -0
  268. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/v2iptestcard.py +0 -0
  269. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/v2ipvlan.py +0 -0
  270. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/vdetails.py +0 -0
  271. {mx_remote-9.0.0 → mx_remote-9.1.0}/tests/wirefix.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mx_remote
3
- Version: 9.0.0
3
+ Version: 9.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://opdenkamp.github.io/mx-remote/
@@ -168,6 +168,23 @@ bit, and the range of the infrared profiles.
168
168
  .. autodata:: mx_remote.proto.Constants.ONEIP_VLAN_ID_MAX
169
169
  ```
170
170
 
171
+ ## OneIP stereo downmix
172
+
173
+ How a device's output mixes 5.1 and 7.1 PCM down to stereo.
174
+
175
+ ```{eval-rst}
176
+ .. autoclass:: mx_remote.OneIPDownmix
177
+ :members:
178
+
179
+ .. autoclass:: mx_remote.proto.Constants.OneIPDownmixMode
180
+ :members:
181
+
182
+ .. autoclass:: mx_remote.proto.Constants.OneIPDownmixLevel
183
+ :members:
184
+
185
+ .. autodata:: mx_remote.proto.Constants.ONEIP_DOWNMIX_LFE_MAX
186
+ ```
187
+
171
188
  ## OneIP PTP
172
189
 
173
190
  A device's PTP settings, which it reports and a controller writes, and the PTP
@@ -318,6 +318,31 @@ if ptp is not None:
318
318
  print(ptp.grandmaster) # its clock identity, None while there is none
319
319
  ```
320
320
 
321
+ ### Stereo downmix
322
+
323
+ A device whose output can mix 5.1 and 7.1 PCM down to stereo reports how,
324
+ once its video processor has answered for it: a mode, a level and the percent
325
+ the LFE channel is mixed in at, which a controller writes, and the state and
326
+ channel count, which only the device reports. A device that reports none has
327
+ no downmix to set, so a write to one is refused, as is a mode, level or LFE
328
+ percent out of range. The write sends the mode, level and LFE together; the
329
+ state and channels are never sent.
330
+
331
+ ```python
332
+ from mx_remote import OneIPDownmix
333
+ from mx_remote.proto.Constants import OneIPDownmixMode, OneIPDownmixLevel
334
+
335
+ downmix = device.oneip_downmix # None: no report yet, or no downmix
336
+ if downmix is not None:
337
+ print(downmix.mode, downmix.level, downmix.lfe) # LFE up to ONEIP_DOWNMIX_LFE_MAX
338
+ print(downmix.state, downmix.channels) # the device's own
339
+ await device.set_oneip_downmix(OneIPDownmix(mode=OneIPDownmixMode.AUTO,
340
+ level=OneIPDownmixLevel.NORMALISED, lfe=50))
341
+ ```
342
+
343
+ The downmix ends the settings block, behind a second valid word, so the write
344
+ runs on to 248 bytes and carries no other setting.
345
+
321
346
  ### VLAN
322
347
 
323
348
  A device that announces `DeviceFeature.VLAN` tags its uplink by a VLAN
@@ -2135,6 +2135,46 @@ class OneIPPtpState:
2135
2135
  return (f"{mode} domain {self.domain} port state {self.port_state} {lock}"
2136
2136
  f"{' followed' if self.followed else ''} clock {self.clock_identity.hex(':')} grandmaster {gm}")
2137
2137
 
2138
+ @dataclass(frozen=True)
2139
+ class OneIPDownmix:
2140
+ """
2141
+ How a OneIP output mixes 5.1 and 7.1 PCM down to stereo, and whether it
2142
+ does now.
2143
+
2144
+ A device reports it once its video processor has answered for it, so never
2145
+ one whose output cannot mix down.
2146
+ """
2147
+ mode: OneIPDownmixMode|int = OneIPDownmixMode.OFF
2148
+ """When the output mixes down; a raw int for a mode this library does not
2149
+ name."""
2150
+ level: OneIPDownmixLevel|int = OneIPDownmixLevel.NORMALISED
2151
+ """How loud the mix is; a raw int for a level this library does not name."""
2152
+ lfe: int = 0
2153
+ """The percent the LFE channel is mixed in at, at most
2154
+ ONEIP_DOWNMIX_LFE_MAX; 0 leaves it out."""
2155
+ state: int = 0
2156
+ """Why the stream is or is not mixed down, as the device's own code. Only
2157
+ the device reports it."""
2158
+ channels: int = 0
2159
+ """The stream's channels while it is mixed down, else 0. Only the device
2160
+ reports it."""
2161
+
2162
+ def is_valid(self) -> bool:
2163
+ """Whether the settings a write carries are in range."""
2164
+ return ((OneIPDownmixMode.OFF <= self.mode <= OneIPDownmixMode.ALWAYS)
2165
+ and (OneIPDownmixLevel.NORMALISED <= self.level <= OneIPDownmixLevel.UNSCALED)
2166
+ and (0 <= self.lfe <= ONEIP_DOWNMIX_LFE_MAX))
2167
+
2168
+ def written(self, write:'OneIPDownmix') -> 'OneIPDownmix':
2169
+ """The settings of write applied to this report, which keeps what only
2170
+ the device reports."""
2171
+ return replace(self, mode=write.mode, level=write.level, lfe=write.lfe)
2172
+
2173
+ def __str__(self) -> str:
2174
+ mode = self.mode.name.lower() if isinstance(self.mode, OneIPDownmixMode) else self.mode
2175
+ level = self.level.name.lower() if isinstance(self.level, OneIPDownmixLevel) else self.level
2176
+ return f"{mode} {level} lfe {self.lfe}% state {self.state} channels {self.channels}"
2177
+
2138
2178
  @dataclass(frozen=True)
2139
2179
  class OneIPVlan:
2140
2180
  """
@@ -3239,6 +3279,14 @@ class DeviceBase(ABC):
3239
3279
  async def confirm_unit_network(self) -> bool:
3240
3280
  '''keep the unit's pending IP configuration'''
3241
3281
 
3282
+ @property
3283
+ @abstractmethod
3284
+ def oneip_downmix(self) -> OneIPDownmix|None:
3285
+ '''How this OneIP device's output mixes down to stereo, as it last
3286
+ reported, None while it reports no downmix: until its video processor
3287
+ has answered for it, and always for one whose output cannot mix
3288
+ down.'''
3289
+
3242
3290
  @property
3243
3291
  @abstractmethod
3244
3292
  def oneip_ptp(self) -> OneIPPtpState|None:
@@ -3532,6 +3580,10 @@ class DeviceBase(ABC):
3532
3580
  async def set_oneip_ptp_messages(self, which:OneIPDeviceSetting, messages:OneIPPtpMessages) -> bool:
3533
3581
  '''set how the device paces its PTP messages, its SAP timings and its streams' TTL: the values of messages behind the bits of which'''
3534
3582
 
3583
+ @abstractmethod
3584
+ async def set_oneip_downmix(self, downmix:OneIPDownmix) -> bool:
3585
+ '''set how the device's output mixes down to stereo'''
3586
+
3535
3587
  @abstractmethod
3536
3588
  async def set_oneip_vlan(self, vlan:OneIPVlan) -> bool:
3537
3589
  '''change the device's VLAN configuration'''
@@ -757,6 +757,16 @@ class OneIPPtpState:
757
757
  @property
758
758
  def grandmaster(self) -> bytes | None: ...
759
759
 
760
+ @dataclass(frozen=True)
761
+ class OneIPDownmix:
762
+ mode: OneIPDownmixMode | int = ...
763
+ level: OneIPDownmixLevel | int = ...
764
+ lfe: int = ...
765
+ state: int = ...
766
+ channels: int = ...
767
+ def is_valid(self) -> bool: ...
768
+ def written(self, write: OneIPDownmix) -> OneIPDownmix: ...
769
+
760
770
  @dataclass(frozen=True)
761
771
  class OneIPVlan:
762
772
  flags: OneIPVlanFlag = ...
@@ -1183,6 +1193,9 @@ class DeviceBase(ABC):
1183
1193
  async def confirm_unit_network(self) -> bool: ...
1184
1194
  @property
1185
1195
  @abstractmethod
1196
+ def oneip_downmix(self) -> OneIPDownmix | None: ...
1197
+ @property
1198
+ @abstractmethod
1186
1199
  def oneip_ptp(self) -> OneIPPtpState | None: ...
1187
1200
  @property
1188
1201
  @abstractmethod
@@ -1321,6 +1334,8 @@ class DeviceBase(ABC):
1321
1334
  @abstractmethod
1322
1335
  async def set_oneip_ptp_messages(self, which: OneIPDeviceSetting, messages: OneIPPtpMessages) -> bool: ...
1323
1336
  @abstractmethod
1337
+ async def set_oneip_downmix(self, downmix: OneIPDownmix) -> bool: ...
1338
+ @abstractmethod
1324
1339
  async def set_oneip_vlan(self, vlan: OneIPVlan) -> bool: ...
1325
1340
  @abstractmethod
1326
1341
  async def set_audio_endpoint_locked(self, endpoint: int, locked: bool) -> bool: ...
@@ -14,6 +14,7 @@ class MxrDeviceUid:
14
14
 
15
15
  def __init__(self, value: object) -> None:
16
16
  self._value:bytes
17
+ self._str:str|None = None
17
18
 
18
19
  if value is None:
19
20
  self._value = bytes([ 0 for _ in range(16)])
@@ -44,11 +45,11 @@ class MxrDeviceUid:
44
45
 
45
46
  @property
46
47
  def value(self) -> str:
47
- ''' value as human readble string '''
48
- return ''.join('%02x'%i for i in reversed(self._value[0:4])) + '.' + \
49
- ''.join('%02x'%i for i in reversed(self._value[4:8])) + '.' + \
50
- ''.join('%02x'%i for i in reversed(self._value[8:12])) + '.' + \
51
- ''.join('%02x'%i for i in reversed(self._value[12:16]))
48
+ ''' value as human readable string, formatted once, as every hash and
49
+ comparison reads it '''
50
+ if (self._str is None):
51
+ self._str = '.'.join(self._value[i:i + 4][::-1].hex() for i in (0, 4, 8, 12))
52
+ return self._str
52
53
 
53
54
  @property
54
55
  def empty(self) -> bool:
@@ -70,12 +71,14 @@ class MxrDeviceUid:
70
71
  return str(self)
71
72
 
72
73
  def __eq__(self, value: object) -> bool:
73
- if isinstance(value, str) or isinstance(value, MxrDeviceUid):
74
- return str(self) == str(value)
74
+ if isinstance(value, MxrDeviceUid):
75
+ return self.value == value.value
76
+ if isinstance(value, str):
77
+ return self.value == value
75
78
  return False
76
79
 
77
80
  def __hash__(self) -> int:
78
- return hash(str(self))
81
+ return hash(self.value)
79
82
 
80
83
  class MxrBayUidOld:
81
84
  '''Legacy bay identifier using serial number and port name.'''
@@ -14,7 +14,7 @@ Default communication modes:
14
14
  """
15
15
  import os
16
16
 
17
- VERSION = '9.0.0'
17
+ VERSION = '9.1.0'
18
18
  __version__ = VERSION
19
19
 
20
20
  MX_BCAST_UDP_PORT = 8811
@@ -609,6 +609,25 @@ class OneIPPowerSaveLevel(IntEnum):
609
609
  OFF = 3
610
610
  '''Everything but the network powers down.'''
611
611
 
612
+ class OneIPDownmixMode(IntEnum):
613
+ '''When a OneIP output mixes 5.1 and 7.1 PCM down to stereo.'''
614
+ OFF = 0
615
+ '''Never.'''
616
+ AUTO = 1
617
+ '''When the display's EDID lists two PCM channels or fewer.'''
618
+ ALWAYS = 2
619
+ '''Always.'''
620
+
621
+ class OneIPDownmixLevel(IntEnum):
622
+ '''How loud a OneIP output's stereo downmix is.'''
623
+ NORMALISED = 0
624
+ '''Scaled so the mix never clips.'''
625
+ UNSCALED = 1
626
+ '''Unscaled, with dialogue at the level it has in stereo.'''
627
+
628
+ ONEIP_DOWNMIX_LFE_MAX = 100
629
+ """The largest percent a OneIP output mixes its LFE channel in at."""
630
+
612
631
  class UnitPowerSave(IntFlag):
613
632
  '''What a unit offers at one power save level.'''
614
633
  SUPPORTED = (1 << 0)
@@ -236,6 +236,17 @@ class OneIPPowerSaveLevel(IntEnum):
236
236
  LOW: int
237
237
  OFF: int
238
238
 
239
+ class OneIPDownmixMode(IntEnum):
240
+ OFF: int
241
+ AUTO: int
242
+ ALWAYS: int
243
+
244
+ class OneIPDownmixLevel(IntEnum):
245
+ NORMALISED: int
246
+ UNSCALED: int
247
+
248
+ ONEIP_DOWNMIX_LFE_MAX: int
249
+
239
250
  class UnitPowerSave(IntFlag):
240
251
  SUPPORTED: Incomplete
241
252
  INPUT_WAKE: Incomplete
@@ -14,9 +14,9 @@ from .FrameHeader import FrameHeader
14
14
  from ..Uid import MxrDeviceUid
15
15
  from ..Interface import (DeviceBase, DeviceRegistry, DeviceOneIPDetails, DeviceOneIPScalingSettings,
16
16
  DeviceOneIPSink, OneIPAudioFormat, OneIPDeviceSettings, OneIPDscpConfig,
17
- OneIPPowerSaveSchedule, OneIPPtpMessages, OneIPPtpState, OneIPStreamSource,
18
- OneIPVlan, _wire_enum)
19
- from .Constants import (ONEIP_DEVICE_SETTINGS_BLOCK, ONEIP_DEVICE_SETTINGS_PTP, OneIPPtpFlag, OneIPPtpLock, OneIPPtpMode, OneIPDeviceSetting, OneIPVideoProcessorFeature, OneIPVlanFlag, MXR_SCALING_FLAG_AUTO_SCALING,
17
+ OneIPDownmix, OneIPPowerSaveSchedule, OneIPPtpMessages, OneIPPtpState,
18
+ OneIPStreamSource, OneIPVlan, _wire_enum)
19
+ from .Constants import (ONEIP_DEVICE_SETTINGS_BLOCK, ONEIP_DEVICE_SETTINGS_PTP, OneIPDownmixLevel, OneIPDownmixMode, OneIPPtpFlag, OneIPPtpLock, OneIPPtpMode, OneIPDeviceSetting, OneIPVideoProcessorFeature, OneIPVlanFlag, MXR_SCALING_FLAG_AUTO_SCALING,
20
20
  MXR_SCALING_FLAG_MODE_VALID,
21
21
  MXR_SCALING_FLAG_OPTIONS_VALID, MXR_SCALING_FLAG_OPTIONS2_VALID,
22
22
  MXR_SCALING_OPTIONS2_SETTINGS, MxrSignalType, oneip_dscp_value,
@@ -48,13 +48,19 @@ from .V2IPConfig import V2IPStreamSourceImpl, parse_v2ip_av_source
48
48
  # 216..224 the settings' own block: the PTP announce, sync and delay
49
49
  # request intervals as i8, u8 announce timeout, u8 PTP DSCP,
50
50
  # u8 SAP expiry in minutes, u16 SAP interval in seconds, and
51
- # from a device with the stream TTL, u8 TTL and 7 reserved
51
+ # from a device with the stream TTL, u8 TTL, a reserved byte,
52
+ # u16 output rotation and u16 rotation step at 224..230, 2
53
+ # reserved
54
+ # 232..248 from a device with the downmix: u32 second valid word, then
55
+ # the downmix's u8 mode, level, LFE percent, state and channels
56
+ # at 236..241, 7 reserved
52
57
  #
53
58
  # That block is the last of its frame, so it is read by its length: shorter
54
59
  # than 8 carries none of its values, and 8, which every device without the TTL
55
60
  # sends, carries all but the TTL. A write grows it to 16 only when it carries
56
- # the TTL, so the frames other writes send are unchanged for units that
57
- # predate it.
61
+ # the TTL or the rotation, and to 32 only for the downmix, so the frames other
62
+ # writes send are unchanged for units that predate them. A block too short to
63
+ # hold the downmix's channels carries no bit of the second valid word.
58
64
  #
59
65
  # Each of those blocks was appended behind what came before it, leaving every
60
66
  # offset ahead of it where it was, so the length is what says whether one is
@@ -136,6 +142,14 @@ _STREAM_TTL_AT = 8
136
142
  _SINK_ROTATION_AT = 10
137
143
  _SINK_ROTATION_STEP_AT = 12
138
144
  _SETTINGS_BLOCK_LONG_SIZE = 16
145
+ _VALID_EXT_OFFSET = SETTINGS_BLOCK_OFFSET + _SETTINGS_BLOCK_LONG_SIZE
146
+ '''The second valid word of the settings, behind the block of their own.'''
147
+ _DOWNMIX_OFFSET = _VALID_EXT_OFFSET + 4
148
+ _DOWNMIX_SIZE = 5
149
+ _WITH_DOWNMIX_SIZE = SETTINGS_BLOCK_OFFSET + 32
150
+ '''The frame a downmix write sends: the settings block run on to 32 bytes.'''
151
+ _SETTING_EXT_DOWNMIX = 1 << 0
152
+ '''The bit of the second valid word that carries the downmix.'''
139
153
 
140
154
  def carries_settings_block(settings:OneIPDeviceSettings) -> bool:
141
155
  '''Whether settings carry a value only the block of their own can hold.'''
@@ -424,6 +438,28 @@ class FrameV2IPDeviceConfiguration(FrameBase):
424
438
  payload += own_settings_block(settings)
425
439
  return FrameBase.construct_base(target=target, mxr=mxr, opcode=_OPCODE, payload=bytes(payload))
426
440
 
441
+ @staticmethod
442
+ def construct_downmix(mxr:DeviceRegistry, target:Any, target_uid:MxrDeviceUid,
443
+ downmix:OneIPDownmix) -> FrameBase|None:
444
+ '''Build the 0x3C write that changes how a device's output mixes down to
445
+ stereo and nothing else.
446
+
447
+ 248 bytes: the frame construct_settings() builds, with a settings block
448
+ that carries no setting, zeroed to 232, then the second valid word
449
+ carrying the downmix and its mode, level and LFE percent at 236..239.
450
+ The state and channels at 239 and 240 are the device's to report, and
451
+ are left 0.
452
+ '''
453
+ frame = FrameV2IPDeviceConfiguration.construct_settings(
454
+ mxr=mxr, target=target, target_uid=target_uid, settings=OneIPDeviceSettings())
455
+ if (frame is None):
456
+ return None
457
+ payload = bytearray(frame.payload or b"")
458
+ payload += bytes(_WITH_DOWNMIX_SIZE - len(payload))
459
+ struct.pack_into('<IBBB', payload, _VALID_EXT_OFFSET, _SETTING_EXT_DOWNMIX,
460
+ int(downmix.mode), int(downmix.level), downmix.lfe)
461
+ return FrameBase.construct_base(target=target, mxr=mxr, opcode=_OPCODE, payload=bytes(payload))
462
+
427
463
  @staticmethod
428
464
  def construct_vlan(mxr:DeviceRegistry, target:Any, target_uid:MxrDeviceUid,
429
465
  vlan:OneIPVlan) -> FrameBase|None:
@@ -580,6 +616,28 @@ class FrameV2IPDeviceConfiguration(FrameBase):
580
616
  port_state=port_state, lock=_wire_enum(OneIPPtpLock, lock),
581
617
  clock_identity=clock, grandmaster_identity=grandmaster)
582
618
 
619
+ @property
620
+ def carries_downmix(self) -> bool:
621
+ '''Whether the frame is long enough to say whether it carries a downmix.'''
622
+ return (self.payload is not None) and (len(self.payload) >= (_DOWNMIX_OFFSET + _DOWNMIX_SIZE))
623
+
624
+ @cached_property
625
+ def downmix(self) -> OneIPDownmix|None:
626
+ '''The downmix the frame carries, None when it carries none: in a frame
627
+ too short to hold it, or without its bit of the second valid word.
628
+
629
+ From the subject itself, None reports that it has no downmix; from
630
+ another sender it is a write, of which only the mode, level and LFE
631
+ percent count.'''
632
+ if not self.carries_downmix:
633
+ return None
634
+ p = self.payload or b""
635
+ valid_ext, mode, level, lfe, state, channels = struct.unpack_from('<IBBBBB', p, _VALID_EXT_OFFSET)
636
+ if not (valid_ext & _SETTING_EXT_DOWNMIX):
637
+ return None
638
+ return OneIPDownmix(mode=_wire_enum(OneIPDownmixMode, mode), level=_wire_enum(OneIPDownmixLevel, level),
639
+ lfe=lfe, state=state, channels=channels)
640
+
583
641
  def process(self) -> None:
584
642
  '''Update the cache of the device this configuration names, not of its sender.'''
585
643
  if ((dev := self.subject_device) is None):
@@ -595,6 +653,8 @@ class FrameV2IPDeviceConfiguration(FrameBase):
595
653
  dev.on_mxr_update(vlan)
596
654
  if ((ptp := self.ptp) is not None):
597
655
  dev.on_mxr_update(ptp)
656
+ if self.carries_downmix:
657
+ dev.on_mxr_update(self)
598
658
 
599
659
  def __str__(self) -> str:
600
660
  sink_str = f" sink=[{self.sink}]" if (self.sink is not None) else ""
@@ -1,6 +1,6 @@
1
- from ..Interface import DeviceBase as DeviceBase, DeviceOneIPDetails as DeviceOneIPDetails, DeviceOneIPScalingSettings as DeviceOneIPScalingSettings, DeviceOneIPSink as DeviceOneIPSink, DeviceRegistry as DeviceRegistry, OneIPAudioFormat as OneIPAudioFormat, OneIPDeviceSettings as OneIPDeviceSettings, OneIPDscpConfig as OneIPDscpConfig, OneIPPowerSaveSchedule as OneIPPowerSaveSchedule, OneIPPtpMessages as OneIPPtpMessages, OneIPPtpState as OneIPPtpState, OneIPStreamSource as OneIPStreamSource, OneIPVlan as OneIPVlan
1
+ from ..Interface import DeviceBase as DeviceBase, DeviceOneIPDetails as DeviceOneIPDetails, DeviceOneIPScalingSettings as DeviceOneIPScalingSettings, DeviceOneIPSink as DeviceOneIPSink, DeviceRegistry as DeviceRegistry, OneIPAudioFormat as OneIPAudioFormat, OneIPDeviceSettings as OneIPDeviceSettings, OneIPDownmix as OneIPDownmix, OneIPDscpConfig as OneIPDscpConfig, OneIPPowerSaveSchedule as OneIPPowerSaveSchedule, OneIPPtpMessages as OneIPPtpMessages, OneIPPtpState as OneIPPtpState, OneIPStreamSource as OneIPStreamSource, OneIPVlan as OneIPVlan
2
2
  from ..Uid import MxrDeviceUid as MxrDeviceUid
3
- from .Constants import MXR_SCALING_FLAG_AUTO_SCALING as MXR_SCALING_FLAG_AUTO_SCALING, MXR_SCALING_FLAG_MODE_VALID as MXR_SCALING_FLAG_MODE_VALID, MXR_SCALING_FLAG_OPTIONS2_VALID as MXR_SCALING_FLAG_OPTIONS2_VALID, MXR_SCALING_FLAG_OPTIONS_VALID as MXR_SCALING_FLAG_OPTIONS_VALID, MXR_SCALING_OPTIONS2_SETTINGS as MXR_SCALING_OPTIONS2_SETTINGS, MxrSignalType as MxrSignalType, ONEIP_DEVICE_SETTINGS_BLOCK as ONEIP_DEVICE_SETTINGS_BLOCK, ONEIP_DEVICE_SETTINGS_PTP as ONEIP_DEVICE_SETTINGS_PTP, OneIPDeviceSetting as OneIPDeviceSetting, OneIPPtpFlag as OneIPPtpFlag, OneIPPtpLock as OneIPPtpLock, OneIPPtpMode as OneIPPtpMode, OneIPVideoProcessorFeature as OneIPVideoProcessorFeature, OneIPVlanFlag as OneIPVlanFlag, oneip_dscp_value as oneip_dscp_value, oneip_rate_valid as oneip_rate_valid
3
+ from .Constants import MXR_SCALING_FLAG_AUTO_SCALING as MXR_SCALING_FLAG_AUTO_SCALING, MXR_SCALING_FLAG_MODE_VALID as MXR_SCALING_FLAG_MODE_VALID, MXR_SCALING_FLAG_OPTIONS2_VALID as MXR_SCALING_FLAG_OPTIONS2_VALID, MXR_SCALING_FLAG_OPTIONS_VALID as MXR_SCALING_FLAG_OPTIONS_VALID, MXR_SCALING_OPTIONS2_SETTINGS as MXR_SCALING_OPTIONS2_SETTINGS, MxrSignalType as MxrSignalType, ONEIP_DEVICE_SETTINGS_BLOCK as ONEIP_DEVICE_SETTINGS_BLOCK, ONEIP_DEVICE_SETTINGS_PTP as ONEIP_DEVICE_SETTINGS_PTP, OneIPDeviceSetting as OneIPDeviceSetting, OneIPDownmixLevel as OneIPDownmixLevel, OneIPDownmixMode as OneIPDownmixMode, OneIPPtpFlag as OneIPPtpFlag, OneIPPtpLock as OneIPPtpLock, OneIPPtpMode as OneIPPtpMode, OneIPVideoProcessorFeature as OneIPVideoProcessorFeature, OneIPVlanFlag as OneIPVlanFlag, oneip_dscp_value as oneip_dscp_value, oneip_rate_valid as oneip_rate_valid
4
4
  from .FrameBase import FrameBase as FrameBase
5
5
  from .FrameHeader import FrameHeader as FrameHeader
6
6
  from .V2IPConfig import V2IPStreamSourceImpl as V2IPStreamSourceImpl, parse_v2ip_av_source as parse_v2ip_av_source
@@ -48,6 +48,8 @@ class FrameV2IPDeviceConfiguration(FrameBase):
48
48
  @staticmethod
49
49
  def construct_settings(mxr: DeviceRegistry, target: Any, target_uid: MxrDeviceUid, settings: OneIPDeviceSettings) -> FrameBase | None: ...
50
50
  @staticmethod
51
+ def construct_downmix(mxr: DeviceRegistry, target: Any, target_uid: MxrDeviceUid, downmix: OneIPDownmix) -> FrameBase | None: ...
52
+ @staticmethod
51
53
  def construct_vlan(mxr: DeviceRegistry, target: Any, target_uid: MxrDeviceUid, vlan: OneIPVlan) -> FrameBase | None: ...
52
54
  @property
53
55
  def target_uid(self) -> MxrDeviceUid | None: ...
@@ -67,4 +69,8 @@ class FrameV2IPDeviceConfiguration(FrameBase):
67
69
  def vlan(self) -> OneIPVlan | None: ...
68
70
  @cached_property
69
71
  def ptp(self) -> OneIPPtpState | None: ...
72
+ @property
73
+ def carries_downmix(self) -> bool: ...
74
+ @cached_property
75
+ def downmix(self) -> OneIPDownmix | None: ...
70
76
  def process(self) -> None: ...
@@ -26,9 +26,10 @@ class FrameV2IPSources(FrameBase):
26
26
  that list was when the frame was built. None when the payload is neither
27
27
  form this opcode carries.
28
28
 
29
- A list that fits one frame is sent as bare records from the first byte.
30
- A longer one is split into pages, each behind a header. That header is
31
- not a whole record, so the remainder separates the two forms - and a
29
+ A list that fits one frame is sent as bare records from the first byte,
30
+ and a unit that knows no source sends it with none. A longer one is
31
+ split into pages, each behind a header. That header is not a whole
32
+ record, so the remainder separates the two forms - and a
32
33
  payload that is neither is refused rather than read from byte zero,
33
34
  which would shift every record by the header's width and report a full
34
35
  set of plausible addresses belonging to no bay.
@@ -37,9 +38,7 @@ class FrameV2IPSources(FrameBase):
37
38
  rather than a promise about the set, so it says whether this frame is
38
39
  the whole list and nothing more.
39
40
  '''
40
- pl = self.payload
41
- if (pl is None):
42
- return None
41
+ pl = self.payload or b''
43
42
  rest = (len(pl) % _RECORD_SIZE)
44
43
  if (rest == 0):
45
44
  return (0, int(len(pl) / _RECORD_SIZE))
@@ -175,9 +175,10 @@ class Bay(BayBase):
175
175
  device = self.device
176
176
  if (getattr(device, 'local_bay')(self.is_input) is not self):
177
177
  return []
178
+ owner = device.remote_id
178
179
  return [bay for dev in device.registry.remotes.values() if (dev is not device)
179
180
  for bay in dev.bays.values()
180
- if isinstance(bay, Bay) and (bay.mesh_owner is self)]
181
+ if isinstance(bay, Bay) and (bay._v2ip_uid == owner) and (bay.mesh_owner is self)]
181
182
 
182
183
  def register_callback(self, callback:Callable[[BayBase],None]) -> None:
183
184
  '''register a callback, called when the device state changed'''
@@ -12,6 +12,7 @@ from ..Interface import (
12
12
  OneIPStreamSources,
13
13
  OneIPPtpMessages,
14
14
  OneIPPtpState,
15
+ OneIPDownmix,
15
16
  UnitNetConfig,
16
17
  UnitStatus,
17
18
  AmpDolbySettings,
@@ -81,7 +82,7 @@ import time
81
82
 
82
83
  from ..Interface import DeviceBase, BayBase, DeviceRegistry
83
84
  from ..proto.FrameBase import FrameBase
84
- from ..proto.Constants import (MXR_PROTOCOL_VERSION_SLOW_HELLO, OSD_MESSAGE_TEXT_MAX, OsdMessageFlag, OsdMessagePriority, BayFeaturesMask, DeviceFeature, MxrSignalType, OneIPDeviceSetting, OneIPVideoProcessorFeature, OneIPVlanFlag,
85
+ from ..proto.Constants import (MXR_PROTOCOL_VERSION_SLOW_HELLO, ONEIP_DOWNMIX_LFE_MAX, OSD_MESSAGE_TEXT_MAX, OsdMessageFlag, OsdMessagePriority, BayFeaturesMask, DeviceFeature, MxrSignalType, OneIPDeviceSetting, OneIPVideoProcessorFeature, OneIPVlanFlag,
85
86
  OneIPTestPattern, OneIPToneMode,
86
87
  MXR_SCALING_FLAG_AUTO_SCALING, MXR_SCALING_FLAG_MODE_VALID,
87
88
  MXR_SCALING_FLAG_OPTIONS_VALID, ONEIP_DEVICE_SETTING_SWITCHES,
@@ -173,6 +174,7 @@ class Device(DeviceBase):
173
174
  self._v2ip_settings:OneIPDeviceSettings|None = None
174
175
  self._v2ip_vlan:OneIPVlan|None = None
175
176
  self._v2ip_ptp:OneIPPtpState|None = None
177
+ self._v2ip_downmix:OneIPDownmix|None = None
176
178
  self._unit_status:UnitStatus|None = None
177
179
  self._v2ip_testcard:OneIPTestcard|None = None
178
180
  self._v2ip_sink_sdp:OneIPSinkSdp|None = None
@@ -453,6 +455,11 @@ class Device(DeviceBase):
453
455
  def oneip_ptp(self) -> OneIPPtpState|None:
454
456
  return self._v2ip_ptp
455
457
 
458
+ @property
459
+ @override
460
+ def oneip_downmix(self) -> OneIPDownmix|None:
461
+ return self._v2ip_downmix
462
+
456
463
  @property
457
464
  @override
458
465
  def unit_status(self) -> UnitStatus|None:
@@ -1050,6 +1057,8 @@ class Device(DeviceBase):
1050
1057
  if (self._v2ip_ptp != ptp):
1051
1058
  self._v2ip_ptp = ptp
1052
1059
  self.call_callbacks()
1060
+ elif isinstance(data, FrameV2IPDeviceConfiguration):
1061
+ self._on_oneip_downmix(data.downmix, reported=data.target_self)
1053
1062
  elif isinstance(data, OneIPTestcard):
1054
1063
  if (self._v2ip_testcard != data):
1055
1064
  self._v2ip_testcard = data
@@ -1606,6 +1615,47 @@ class Device(DeviceBase):
1606
1615
  messages = dataclasses.replace(messages, sink_rotation_step=own.sink_rotation_step)
1607
1616
  return self._send_v2ip_settings(OneIPDeviceSettings(valid=which, ptp_messages=messages))
1608
1617
 
1618
+ def _on_oneip_downmix(self, downmix:OneIPDownmix|None, reported:bool) -> None:
1619
+ '''Take the downmix a configuration carries as the device takes it: a
1620
+ report replaces the one held, None included, and another sender's write
1621
+ is applied only to a device that reports a downmix, and never to what
1622
+ only the device reports.'''
1623
+ if not reported:
1624
+ if (downmix is None) or (self._v2ip_downmix is None):
1625
+ return
1626
+ downmix = self._v2ip_downmix.written(downmix)
1627
+ if (self._v2ip_downmix != downmix):
1628
+ self._v2ip_downmix = downmix
1629
+ self.call_callbacks()
1630
+
1631
+ async def set_oneip_downmix(self, downmix:OneIPDownmix) -> bool:
1632
+ '''Set how this OneIP device's output mixes 5.1 and 7.1 PCM down to
1633
+ stereo: the mode, level and LFE percent of downmix, which the device
1634
+ applies together. Its state and channels are the device's to report,
1635
+ and are not sent.
1636
+
1637
+ Refused before anything is sent for a mode, level or LFE percent out of
1638
+ range, and unless the device reports a downmix, which one whose output
1639
+ cannot mix down never does. The new downmix is cached once the frame is
1640
+ sent, and the device's next report replaces it.
1641
+ '''
1642
+ why = None
1643
+ if not downmix.is_valid():
1644
+ why = f'no such mode or level, or an LFE above {ONEIP_DOWNMIX_LFE_MAX} percent'
1645
+ elif (self._v2ip_settings is None):
1646
+ why = 'it has not reported its settings'
1647
+ elif ((reported := self._v2ip_downmix) is None):
1648
+ why = 'its output does not mix down'
1649
+ if (why is not None):
1650
+ _LOGGER.warning(f"not changing the downmix of {self}: {why}")
1651
+ return False
1652
+ frame = FrameV2IPDeviceConfiguration.construct_downmix(
1653
+ mxr=self.registry, target=self, target_uid=self.remote_id, downmix=downmix)
1654
+ if (frame is None) or (self.registry.transmit(frame.frame) != len(frame.frame)):
1655
+ return False
1656
+ self._on_oneip_downmix(reported.written(downmix), reported=True)
1657
+ return True
1658
+
1609
1659
  async def set_oneip_vlan(self, vlan:OneIPVlan) -> bool:
1610
1660
  '''Change this OneIP device's VLAN configuration.
1611
1661
 
@@ -1,9 +1,9 @@
1
- from ..Interface import AmpDolbySettings as AmpDolbySettings, AudioChangeSource as AudioChangeSource, AudioEndpoint as AudioEndpoint, AudioEndpoints as AudioEndpoints, AudioLinks as AudioLinks, BayBase as BayBase, DeviceBase as DeviceBase, DeviceClock as DeviceClock, DeviceOneIPDetails as DeviceOneIPDetails, DeviceOneIPScalingSettings as DeviceOneIPScalingSettings, DeviceOneIPSink as DeviceOneIPSink, DeviceRegistry as DeviceRegistry, DeviceStatus as DeviceStatus, Multiviewer as Multiviewer, MxrCallbacks as MxrCallbacks, OneIPDeviceSettings as OneIPDeviceSettings, OneIPOutputMode as OneIPOutputMode, OneIPPowerSaveSchedule as OneIPPowerSaveSchedule, OneIPPrivacy as OneIPPrivacy, OneIPPtpMessages as OneIPPtpMessages, OneIPPtpState as OneIPPtpState, OneIPScalingSettings as OneIPScalingSettings, OneIPSinkSdp as OneIPSinkSdp, OneIPStreamSources as OneIPStreamSources, OneIPStreamSourcesList as OneIPStreamSourcesList, OneIPTestSync as OneIPTestSync, OneIPTestTone as OneIPTestTone, OneIPTestcard as OneIPTestcard, OneIPVlan as OneIPVlan, OsdMessage as OsdMessage, OsdSync as OsdSync, OsdSyncReport as OsdSyncReport, SystemTemperature as SystemTemperature, TimeZone as TimeZone, UnitNetConfig as UnitNetConfig, UnitStatus as UnitStatus
1
+ from ..Interface import AmpDolbySettings as AmpDolbySettings, AudioChangeSource as AudioChangeSource, AudioEndpoint as AudioEndpoint, AudioEndpoints as AudioEndpoints, AudioLinks as AudioLinks, BayBase as BayBase, DeviceBase as DeviceBase, DeviceClock as DeviceClock, DeviceOneIPDetails as DeviceOneIPDetails, DeviceOneIPScalingSettings as DeviceOneIPScalingSettings, DeviceOneIPSink as DeviceOneIPSink, DeviceRegistry as DeviceRegistry, DeviceStatus as DeviceStatus, Multiviewer as Multiviewer, MxrCallbacks as MxrCallbacks, OneIPDeviceSettings as OneIPDeviceSettings, OneIPDownmix as OneIPDownmix, OneIPOutputMode as OneIPOutputMode, OneIPPowerSaveSchedule as OneIPPowerSaveSchedule, OneIPPrivacy as OneIPPrivacy, OneIPPtpMessages as OneIPPtpMessages, OneIPPtpState as OneIPPtpState, OneIPScalingSettings as OneIPScalingSettings, OneIPSinkSdp as OneIPSinkSdp, OneIPStreamSources as OneIPStreamSources, OneIPStreamSourcesList as OneIPStreamSourcesList, OneIPTestSync as OneIPTestSync, OneIPTestTone as OneIPTestTone, OneIPTestcard as OneIPTestcard, OneIPVlan as OneIPVlan, OsdMessage as OsdMessage, OsdSync as OsdSync, OsdSyncReport as OsdSyncReport, SystemTemperature as SystemTemperature, TimeZone as TimeZone, UnitNetConfig as UnitNetConfig, UnitStatus as UnitStatus
2
2
  from ..Uid import MxrDeviceUid as MxrDeviceUid
3
3
  from ..compat import override as override
4
4
  from ..const import MXR_CONFIG_TIMEOUT as MXR_CONFIG_TIMEOUT
5
5
  from ..proto.BayConfig import BayConfig as BayConfig
6
- from ..proto.Constants import BayFeaturesMask as BayFeaturesMask, DeviceFeature as DeviceFeature, MXR_PROTOCOL_VERSION_SLOW_HELLO as MXR_PROTOCOL_VERSION_SLOW_HELLO, MXR_SCALING_FLAG_AUTO_SCALING as MXR_SCALING_FLAG_AUTO_SCALING, MXR_SCALING_FLAG_MODE_VALID as MXR_SCALING_FLAG_MODE_VALID, MXR_SCALING_FLAG_OPTIONS_VALID as MXR_SCALING_FLAG_OPTIONS_VALID, MxrSignalType as MxrSignalType, ONEIP_DEVICE_SETTINGS_BLOCK as ONEIP_DEVICE_SETTINGS_BLOCK, ONEIP_DEVICE_SETTING_SWITCHES as ONEIP_DEVICE_SETTING_SWITCHES, ONEIP_DSCP_MAX as ONEIP_DSCP_MAX, ONEIP_IR_PROFILE_MAX as ONEIP_IR_PROFILE_MAX, ONEIP_IR_PROFILE_NOT_SET as ONEIP_IR_PROFILE_NOT_SET, ONEIP_PTP_DOMAIN_MAX as ONEIP_PTP_DOMAIN_MAX, ONEIP_SDP_MAX as ONEIP_SDP_MAX, ONEIP_VLAN_PORT_SFP as ONEIP_VLAN_PORT_SFP, OSD_MESSAGE_TEXT_MAX as OSD_MESSAGE_TEXT_MAX, OneIPDeviceSetting as OneIPDeviceSetting, OneIPPowerSaveLevel as OneIPPowerSaveLevel, OneIPPtpFlag as OneIPPtpFlag, OneIPPtpMode as OneIPPtpMode, OneIPTestPattern as OneIPTestPattern, OneIPToneMode as OneIPToneMode, OneIPVideoProcessorFeature as OneIPVideoProcessorFeature, OneIPVlanFlag as OneIPVlanFlag, OsdMessageFlag as OsdMessageFlag, OsdMessagePriority as OsdMessagePriority, UnitCommandFlag as UnitCommandFlag, UnitCommandKind as UnitCommandKind
6
+ from ..proto.Constants import BayFeaturesMask as BayFeaturesMask, DeviceFeature as DeviceFeature, MXR_PROTOCOL_VERSION_SLOW_HELLO as MXR_PROTOCOL_VERSION_SLOW_HELLO, MXR_SCALING_FLAG_AUTO_SCALING as MXR_SCALING_FLAG_AUTO_SCALING, MXR_SCALING_FLAG_MODE_VALID as MXR_SCALING_FLAG_MODE_VALID, MXR_SCALING_FLAG_OPTIONS_VALID as MXR_SCALING_FLAG_OPTIONS_VALID, MxrSignalType as MxrSignalType, ONEIP_DEVICE_SETTINGS_BLOCK as ONEIP_DEVICE_SETTINGS_BLOCK, ONEIP_DEVICE_SETTING_SWITCHES as ONEIP_DEVICE_SETTING_SWITCHES, ONEIP_DOWNMIX_LFE_MAX as ONEIP_DOWNMIX_LFE_MAX, ONEIP_DSCP_MAX as ONEIP_DSCP_MAX, ONEIP_IR_PROFILE_MAX as ONEIP_IR_PROFILE_MAX, ONEIP_IR_PROFILE_NOT_SET as ONEIP_IR_PROFILE_NOT_SET, ONEIP_PTP_DOMAIN_MAX as ONEIP_PTP_DOMAIN_MAX, ONEIP_SDP_MAX as ONEIP_SDP_MAX, ONEIP_VLAN_PORT_SFP as ONEIP_VLAN_PORT_SFP, OSD_MESSAGE_TEXT_MAX as OSD_MESSAGE_TEXT_MAX, OneIPDeviceSetting as OneIPDeviceSetting, OneIPPowerSaveLevel as OneIPPowerSaveLevel, OneIPPtpFlag as OneIPPtpFlag, OneIPPtpMode as OneIPPtpMode, OneIPTestPattern as OneIPTestPattern, OneIPToneMode as OneIPToneMode, OneIPVideoProcessorFeature as OneIPVideoProcessorFeature, OneIPVlanFlag as OneIPVlanFlag, OsdMessageFlag as OsdMessageFlag, OsdMessagePriority as OsdMessagePriority, UnitCommandFlag as UnitCommandFlag, UnitCommandKind as UnitCommandKind
7
7
  from ..proto.FrameBase import FrameBase as FrameBase
8
8
  from ..proto.FrameFirmwareVersion import FirmwareType as FirmwareType, FirmwareVersion as FirmwareVersion
9
9
  from ..proto.FrameHello import FrameHello as FrameHello
@@ -111,6 +111,9 @@ class Device(DeviceBase):
111
111
  def oneip_ptp(self) -> OneIPPtpState | None: ...
112
112
  @property
113
113
  @override
114
+ def oneip_downmix(self) -> OneIPDownmix | None: ...
115
+ @property
116
+ @override
114
117
  def unit_status(self) -> UnitStatus | None: ...
115
118
  @property
116
119
  @override
@@ -269,6 +272,7 @@ class Device(DeviceBase):
269
272
  async def set_oneip_ptp_priority1(self, priority1: int) -> bool: ...
270
273
  async def set_oneip_power_save_level(self, level: OneIPPowerSaveLevel) -> bool: ...
271
274
  async def set_oneip_ptp_messages(self, which: OneIPDeviceSetting, messages: OneIPPtpMessages) -> bool: ...
275
+ async def set_oneip_downmix(self, downmix: OneIPDownmix) -> bool: ...
272
276
  async def set_oneip_vlan(self, vlan: OneIPVlan) -> bool: ...
273
277
  async def set_audio_endpoint_locked(self, endpoint: int, locked: bool) -> bool: ...
274
278
  async def request_oneip_testcard(self) -> bool: ...