mx-remote 9.1.0__tar.gz → 9.2.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.1.0 → mx_remote-9.2.0}/PKG-INFO +1 -1
  2. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/oneip.md +22 -13
  3. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/Interface.py +33 -15
  4. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/Interface.pyi +4 -0
  5. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/const.py +1 -1
  6. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/Constants.py +5 -1
  7. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/Device.py +22 -8
  8. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/Device.pyi +1 -0
  9. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/Remote.py +6 -0
  10. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/Remote.pyi +1 -0
  11. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/v2ipsettings.py +41 -1
  12. {mx_remote-9.1.0 → mx_remote-9.2.0}/.gitignore +0 -0
  13. {mx_remote-9.1.0 → mx_remote-9.2.0}/AGENTS.md +0 -0
  14. {mx_remote-9.1.0 → mx_remote-9.2.0}/CLAUDE.md +0 -0
  15. {mx_remote-9.1.0 → mx_remote-9.2.0}/LICENSE +0 -0
  16. {mx_remote-9.1.0 → mx_remote-9.2.0}/README.md +0 -0
  17. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/README.md +0 -0
  18. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/_static/brand.css +0 -0
  19. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/_static/favicon.svg +0 -0
  20. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/_static/logo-dark.svg +0 -0
  21. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/_static/logo.svg +0 -0
  22. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/api.md +0 -0
  23. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/audio.md +0 -0
  24. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/callbacks.md +0 -0
  25. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/conf.py +0 -0
  26. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/configuration.md +0 -0
  27. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/devices-and-bays.md +0 -0
  28. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/diagnostics.md +0 -0
  29. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/index.md +0 -0
  30. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/multiviewer.md +0 -0
  31. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/requirements.txt +0 -0
  32. {mx_remote-9.1.0 → mx_remote-9.2.0}/docs/routing.md +0 -0
  33. {mx_remote-9.1.0 → mx_remote-9.2.0}/hatch_build.py +0 -0
  34. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/Uid.py +0 -0
  35. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/Uid.pyi +0 -0
  36. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/__init__.py +0 -0
  37. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/__init__.pyi +0 -0
  38. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/compat.py +0 -0
  39. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/compat.pyi +0 -0
  40. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/const.pyi +0 -0
  41. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/deprecated.py +0 -0
  42. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/deprecated.pyi +0 -0
  43. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/main.py +0 -0
  44. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/main.pyi +0 -0
  45. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/BayConfig.py +0 -0
  46. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/BayConfig.pyi +0 -0
  47. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/Constants.pyi +0 -0
  48. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/Data.py +0 -0
  49. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/Data.pyi +0 -0
  50. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/Factory.py +0 -0
  51. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/Factory.pyi +0 -0
  52. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameAmpDolbySettings.py +0 -0
  53. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameAmpDolbySettings.pyi +0 -0
  54. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameAmpZoneSettings.py +0 -0
  55. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameAmpZoneSettings.pyi +0 -0
  56. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameAudioClip.py +0 -0
  57. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameAudioClip.pyi +0 -0
  58. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameAudioSetRoute.py +0 -0
  59. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameAudioSetRoute.pyi +0 -0
  60. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBase.py +0 -0
  61. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBase.pyi +0 -0
  62. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBayConfig.py +0 -0
  63. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBayConfig.pyi +0 -0
  64. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBayConfigSecondary.py +0 -0
  65. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBayConfigSecondary.pyi +0 -0
  66. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBayContent.py +0 -0
  67. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBayContent.pyi +0 -0
  68. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBayHide.py +0 -0
  69. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBayHide.pyi +0 -0
  70. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBayStatus.py +0 -0
  71. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameBayStatus.pyi +0 -0
  72. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameConnectStatus.py +0 -0
  73. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameConnectStatus.pyi +0 -0
  74. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameDebug.py +0 -0
  75. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameDebug.pyi +0 -0
  76. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameDiscover.py +0 -0
  77. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameDiscover.pyi +0 -0
  78. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameEDID.py +0 -0
  79. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameEDID.pyi +0 -0
  80. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameEDIDProfile.py +0 -0
  81. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameEDIDProfile.pyi +0 -0
  82. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameFactoryReset.py +0 -0
  83. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameFactoryReset.pyi +0 -0
  84. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameFilterStatus.py +0 -0
  85. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameFilterStatus.pyi +0 -0
  86. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameFirmwareVersion.py +0 -0
  87. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameFirmwareVersion.pyi +0 -0
  88. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameHeader.py +0 -0
  89. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameHeader.pyi +0 -0
  90. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameHello.py +0 -0
  91. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameHello.pyi +0 -0
  92. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameLinks.py +0 -0
  93. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameLinks.pyi +0 -0
  94. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameMeshOperation.py +0 -0
  95. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameMeshOperation.pyi +0 -0
  96. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameMirrorStatus.py +0 -0
  97. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameMirrorStatus.pyi +0 -0
  98. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameMonitoringPulse.py +0 -0
  99. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameMonitoringPulse.pyi +0 -0
  100. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameNetworkStatus.py +0 -0
  101. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameNetworkStatus.pyi +0 -0
  102. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FramePing.py +0 -0
  103. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FramePing.pyi +0 -0
  104. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FramePowerChange.py +0 -0
  105. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FramePowerChange.pyi +0 -0
  106. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameRCAction.py +0 -0
  107. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameRCAction.pyi +0 -0
  108. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameRCIr.py +0 -0
  109. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameRCIr.pyi +0 -0
  110. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameRCKey.py +0 -0
  111. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameRCKey.pyi +0 -0
  112. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameRCSettings.py +0 -0
  113. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameRCSettings.pyi +0 -0
  114. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameReboot.py +0 -0
  115. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameReboot.pyi +0 -0
  116. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameRoutingChange.py +0 -0
  117. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameRoutingChange.pyi +0 -0
  118. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSetInstaller.py +0 -0
  119. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSetInstaller.pyi +0 -0
  120. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSetName.py +0 -0
  121. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSetName.pyi +0 -0
  122. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSetRoute.py +0 -0
  123. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSetRoute.pyi +0 -0
  124. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSetupStatus.py +0 -0
  125. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSetupStatus.pyi +0 -0
  126. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSignalStatus.py +0 -0
  127. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSignalStatus.pyi +0 -0
  128. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSignalStatusNew.py +0 -0
  129. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSignalStatusNew.pyi +0 -0
  130. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSysTemperature.py +0 -0
  131. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSysTemperature.pyi +0 -0
  132. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSystemStatus.py +0 -0
  133. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameSystemStatus.pyi +0 -0
  134. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTXRCAction.py +0 -0
  135. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTXRCAction.pyi +0 -0
  136. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTXRCKey.py +0 -0
  137. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTXRCKey.pyi +0 -0
  138. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTime.py +0 -0
  139. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTime.pyi +0 -0
  140. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTimeZone.py +0 -0
  141. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTimeZone.pyi +0 -0
  142. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTopology.py +0 -0
  143. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTopology.pyi +0 -0
  144. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTxIR.py +0 -0
  145. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameTxIR.pyi +0 -0
  146. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameUnitCommand.py +0 -0
  147. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameUnitCommand.pyi +0 -0
  148. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameUnitStatus.py +0 -0
  149. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameUnitStatus.pyi +0 -0
  150. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameUpgradeFPGA.py +0 -0
  151. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameUpgradeFPGA.pyi +0 -0
  152. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPAudio.py +0 -0
  153. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPAudio.pyi +0 -0
  154. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPBayMapping.py +0 -0
  155. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPBayMapping.pyi +0 -0
  156. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPDetectBays.py +0 -0
  157. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPDetectBays.pyi +0 -0
  158. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPDeviceConfiguration.py +0 -0
  159. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPDeviceConfiguration.pyi +0 -0
  160. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPLink.py +0 -0
  161. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPLink.pyi +0 -0
  162. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPManualSourceSwitch.py +0 -0
  163. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPManualSourceSwitch.pyi +0 -0
  164. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPMultiviewer.py +0 -0
  165. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPMultiviewer.pyi +0 -0
  166. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPOsdSync.py +0 -0
  167. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPOsdSync.pyi +0 -0
  168. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPPowerSave.py +0 -0
  169. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPPowerSave.pyi +0 -0
  170. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPPrivacy.py +0 -0
  171. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPPrivacy.pyi +0 -0
  172. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPSetMaster.py +0 -0
  173. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPSetMaster.pyi +0 -0
  174. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPSettingsAll.py +0 -0
  175. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPSettingsAll.pyi +0 -0
  176. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPSinkSdp.py +0 -0
  177. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPSinkSdp.pyi +0 -0
  178. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPSourceSwitch.py +0 -0
  179. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPSourceSwitch.pyi +0 -0
  180. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPSources.py +0 -0
  181. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPSources.pyi +0 -0
  182. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPStats.py +0 -0
  183. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPStats.pyi +0 -0
  184. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPTestcard.py +0 -0
  185. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPTestcard.pyi +0 -0
  186. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPTiling.py +0 -0
  187. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPTiling.pyi +0 -0
  188. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPVideoWall.py +0 -0
  189. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameV2IPVideoWall.pyi +0 -0
  190. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameVolume.py +0 -0
  191. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameVolume.pyi +0 -0
  192. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameVolumeDown.py +0 -0
  193. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameVolumeDown.pyi +0 -0
  194. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameVolumeSet.py +0 -0
  195. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameVolumeSet.pyi +0 -0
  196. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameVolumeUp.py +0 -0
  197. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/FrameVolumeUp.pyi +0 -0
  198. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/LinkConfig.py +0 -0
  199. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/LinkConfig.pyi +0 -0
  200. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/Multiviewer.py +0 -0
  201. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/Multiviewer.pyi +0 -0
  202. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/Svd.py +0 -0
  203. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/Svd.pyi +0 -0
  204. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/V2IPConfig.py +0 -0
  205. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/V2IPConfig.pyi +0 -0
  206. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/V2IPStats.py +0 -0
  207. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/V2IPStats.pyi +0 -0
  208. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/__init__.py +0 -0
  209. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/__init__.pyi +0 -0
  210. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/proto/svd.csv +0 -0
  211. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/py.typed +0 -0
  212. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/Bay.py +0 -0
  213. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/Bay.pyi +0 -0
  214. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/ConnectionAsync.py +0 -0
  215. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/ConnectionAsync.pyi +0 -0
  216. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/Link.py +0 -0
  217. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/Link.pyi +0 -0
  218. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/OsdSync.py +0 -0
  219. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/OsdSync.pyi +0 -0
  220. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/State.py +0 -0
  221. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/State.pyi +0 -0
  222. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/V2IP.py +0 -0
  223. {mx_remote-9.1.0 → mx_remote-9.2.0}/mx_remote/remote/V2IP.pyi +0 -0
  224. {mx_remote-9.1.0 → mx_remote-9.2.0}/pyproject.toml +0 -0
  225. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/audio_ir.py +0 -0
  226. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/audiolock.py +0 -0
  227. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/baycontent.py +0 -0
  228. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/capture.py +0 -0
  229. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/complete.py +0 -0
  230. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/deprecated.py +0 -0
  231. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/downmix.py +0 -0
  232. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/e2e.py +0 -0
  233. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/entry.py +0 -0
  234. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/handlers.py +0 -0
  235. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/handlers_cover.py +0 -0
  236. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/hello.py +0 -0
  237. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/idempotent.py +0 -0
  238. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/lengths.py +0 -0
  239. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/merge.py +0 -0
  240. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/mesh.py +0 -0
  241. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/meshbays.py +0 -0
  242. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/newops.py +0 -0
  243. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/offline.py +0 -0
  244. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/offsets.py +0 -0
  245. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/osdmessage.py +0 -0
  246. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/osdsync.py +0 -0
  247. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/ping.py +0 -0
  248. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/privacy.py +0 -0
  249. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/proto_stamp.py +0 -0
  250. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/protogate.py +0 -0
  251. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/rcsettings.py +0 -0
  252. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/roundtrip.py +0 -0
  253. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/routes.py +0 -0
  254. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/run.py +0 -0
  255. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/smoke.py +0 -0
  256. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/sources.py +0 -0
  257. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/staterequest.py +0 -0
  258. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/stats.py +0 -0
  259. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/structs.py +0 -0
  260. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/txresult.py +0 -0
  261. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/uid.py +0 -0
  262. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/unit.py +0 -0
  263. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/unknowns.py +0 -0
  264. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/v2ipptp.py +0 -0
  265. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/v2ipscaling.py +0 -0
  266. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/v2ipsdp.py +0 -0
  267. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/v2ipsubject.py +0 -0
  268. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/v2iptestcard.py +0 -0
  269. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/v2ipvlan.py +0 -0
  270. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/vdetails.py +0 -0
  271. {mx_remote-9.1.0 → mx_remote-9.2.0}/tests/wirefix.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mx_remote
3
- Version: 9.1.0
3
+ Version: 9.2.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/
@@ -225,27 +225,36 @@ goes out at 176 bytes: a receiver with the schedule ignores the settings of a
225
225
  shorter frame, and one whose settings end at the idle minutes takes it all the
226
226
  same.
227
227
 
228
- How far a device powers down is its power save level: reduced, low or off, each
229
- powering more down than the one before. Its unit status says which levels it
230
- supports and what wakes it at each.
228
+ Power save is switched on or off. Switched on, a device powers down by itself
229
+ to its power save level: reduced, low or off, each powering more down than the
230
+ one before. The device picks the level itself, the one that saves the most
231
+ while its input and display can still wake it, and reports it. Its unit status
232
+ says which levels it supports and what wakes it at each.
231
233
 
232
234
  ```python
233
235
  from mx_remote import OneIPPowerSaveLevel, UnitPowerSave
234
236
 
235
- print(settings.power_save_level)
237
+ print(settings.get(OneIPDeviceSetting.POWER_SAVE_DISABLED)) # False: switched on
238
+ print(settings.power_save_level) # the level it picked
236
239
  print(device.unit_status.power_save_level(OneIPPowerSaveLevel.LOW)) # e.g. SUPPORTED|INPUT_WAKE
237
240
 
238
- await device.set_oneip_power_save_level(OneIPPowerSaveLevel.LOW)
241
+ await device.set_oneip_power_save(True)
242
+ await mx.set_all_oneip_power_save(False) # every OneIP device of the mesh
239
243
  ```
240
244
 
241
- A level this library does not name is refused before it is sent, and not cached
242
- from another controller's write.
243
-
244
- `OneIPDeviceSetting.POWER_SAVE_DISABLED` is a switch that keeps a device from
245
- powering down by itself; a power save request still powers it down. A device
246
- that takes a power save level switches it off, so a level write, this client's
247
- or another sender's, clears it unless the same frame states it. Firmware
248
- without the bit never reports it, and the switch is refused there.
245
+ Switched off, `OneIPDeviceSetting.POWER_SAVE_DISABLED` is on: the device does
246
+ not power down by itself, and a power save request still powers it down.
247
+ Switching on is written as a mesh controller writes it, with that switch clear
248
+ and the power save level at 0, so a device whose firmware still takes the level
249
+ as its setting returns to its own default.
250
+
251
+ `set_oneip_power_save_level()` writes a level, which switches power save on. A
252
+ current device ignores the level; one whose firmware predates picking it takes
253
+ it. A written level is never cached, from this client or another controller:
254
+ `power_save_level` is what the device reports. A level write without the switch
255
+ only clears `POWER_SAVE_DISABLED` on a device that reports it. A level this
256
+ library does not name is refused before it is sent. Firmware without the switch
257
+ never reports it, and switching is refused there.
249
258
 
250
259
  `OneIPDeviceSetting.CLOUD` is a switch for whether a device connects to the
251
260
  cloud service. A device that does not report it connects regardless, and the
@@ -2712,6 +2712,16 @@ class OneIPDeviceSettings:
2712
2712
  self._power_save_level = int(power_save_level)
2713
2713
  self._ptp_messages = ptp_messages if (ptp_messages is not None) else OneIPPtpMessages()
2714
2714
 
2715
+ @classmethod
2716
+ def power_save_switch(cls, enabled:bool) -> 'OneIPDeviceSettings':
2717
+ """The settings that switch a device's power save on or off. On is both
2718
+ POWER_SAVE_DISABLED, clear, and POWER_SAVE_LEVEL, at 0."""
2719
+ disabled = OneIPDeviceSetting.POWER_SAVE_DISABLED
2720
+ if enabled:
2721
+ return cls(valid=(disabled | OneIPDeviceSetting.POWER_SAVE_LEVEL),
2722
+ power_save_level=OneIPPowerSaveLevel.NONE)
2723
+ return cls(valid=disabled, flags=disabled)
2724
+
2715
2725
  @property
2716
2726
  def valid(self) -> OneIPDeviceSetting:
2717
2727
  """The settings reported so far."""
@@ -2791,8 +2801,9 @@ class OneIPDeviceSettings:
2791
2801
 
2792
2802
  @property
2793
2803
  def power_save_level(self) -> OneIPPowerSaveLevel|int|None:
2794
- """The level the device powers down to in power save, a raw int for one
2795
- this library does not name, None while it is not reported."""
2804
+ """The level the device powers down to in power save, which it picks
2805
+ itself, a raw int for one this library does not name, None while it is
2806
+ not reported."""
2796
2807
  if (OneIPDeviceSetting.POWER_SAVE_LEVEL not in self._valid):
2797
2808
  return None
2798
2809
  return _wire_enum(OneIPPowerSaveLevel, self._power_save_level)
@@ -2812,18 +2823,14 @@ class OneIPDeviceSettings:
2812
2823
 
2813
2824
  Each bit in this block's ``valid`` replaces its own setting and leaves
2814
2825
  the others alone, so a write about one setting does not clear what was
2815
- known about the rest. The exception is POWER_SAVE_DISABLED: a device
2816
- takes a power save level as switching it off.
2826
+ known about the rest.
2817
2827
  """
2818
2828
  if (previous is None):
2819
2829
  previous = OneIPDeviceSettings()
2820
2830
  valid = self._valid
2821
- flags = (previous._flags & ~valid) | (self._flags & valid)
2822
- if (OneIPDeviceSetting.POWER_SAVE_LEVEL in valid) and (OneIPDeviceSetting.POWER_SAVE_DISABLED not in valid):
2823
- flags &= ~OneIPDeviceSetting.POWER_SAVE_DISABLED
2824
2831
  return OneIPDeviceSettings(
2825
2832
  valid=(previous._valid | valid),
2826
- flags=flags,
2833
+ flags=((previous._flags & ~valid) | (self._flags & valid)),
2827
2834
  ir_profiles=(self._ir_profiles if (OneIPDeviceSetting.IR_PROFILES in valid)
2828
2835
  else previous._ir_profiles),
2829
2836
  ir_profile=(self._ir_profile if (OneIPDeviceSetting.IR_PROFILE in valid)
@@ -2850,9 +2857,11 @@ class OneIPDeviceSettings:
2850
2857
  write about it.
2851
2858
 
2852
2859
  A device applies a setting only if it has it, a profile, PTP mode, PTP
2853
- domain, power save level or PTP DSCP only within its range, a stream
2854
- TTL only when it is not 0, a rotation only by its own step, and never
2855
- what only it can report about itself. The step stays the device's.
2860
+ domain or PTP DSCP only within its range, a stream TTL only when it is
2861
+ not 0, a rotation only by its own step, and never what only it can
2862
+ report about itself. The step stays the device's. It picks its power
2863
+ save level itself, and takes a level it is written, without
2864
+ POWER_SAVE_DISABLED, as switching power save on.
2856
2865
 
2857
2866
  reported may be only the bits the device reported, which says nothing
2858
2867
  of its step, so a rotation other than 0 is then left out.
@@ -2871,15 +2880,20 @@ class OneIPDeviceSettings:
2871
2880
  valid &= ~OneIPDeviceSetting.PTP_MODE
2872
2881
  if (self._ptp_domain > ONEIP_PTP_DOMAIN_MAX):
2873
2882
  valid &= ~OneIPDeviceSetting.PTP_DOMAIN
2874
- if (self._power_save_level > OneIPPowerSaveLevel.OFF):
2875
- valid &= ~OneIPDeviceSetting.POWER_SAVE_LEVEL
2876
2883
  if (self._ptp_messages.dscp > ONEIP_DSCP_MAX):
2877
2884
  valid &= ~OneIPDeviceSetting.PTP_DSCP
2878
2885
  if (self._ptp_messages.stream_ttl == 0):
2879
2886
  valid &= ~OneIPDeviceSetting.STREAM_TTL
2880
2887
  if not own.takes_sink_rotation(self._ptp_messages.sink_rotation):
2881
2888
  valid &= ~OneIPDeviceSetting.SINK_ROTATION
2882
- return OneIPDeviceSettings(valid=valid, flags=self._flags, ir_profiles=self._ir_profiles,
2889
+ flags = self._flags
2890
+ if (OneIPDeviceSetting.POWER_SAVE_LEVEL in valid):
2891
+ valid &= ~OneIPDeviceSetting.POWER_SAVE_LEVEL
2892
+ if (OneIPDeviceSetting.POWER_SAVE_DISABLED not in valid) \
2893
+ and (OneIPDeviceSetting.POWER_SAVE_DISABLED in reported._valid):
2894
+ valid |= OneIPDeviceSetting.POWER_SAVE_DISABLED
2895
+ flags &= ~OneIPDeviceSetting.POWER_SAVE_DISABLED
2896
+ return OneIPDeviceSettings(valid=valid, flags=flags, ir_profiles=self._ir_profiles,
2883
2897
  ir_profile=self._ir_profile, ir_profile_sink=self._ir_profile_sink,
2884
2898
  auto_power_save=self._auto_power_save, power_save=self._power_save,
2885
2899
  ptp_mode=self._ptp_mode, ptp_domain=self._ptp_domain,
@@ -3572,9 +3586,13 @@ class DeviceBase(ABC):
3572
3586
  async def set_oneip_ptp_priority1(self, priority1:int) -> bool:
3573
3587
  '''set the priority1 the device announces as a PTP grandmaster'''
3574
3588
 
3589
+ @abstractmethod
3590
+ async def set_oneip_power_save(self, enabled:bool) -> bool:
3591
+ '''switch power save on, so the device powers down by itself to the level it picks, or off'''
3592
+
3575
3593
  @abstractmethod
3576
3594
  async def set_oneip_power_save_level(self, level:OneIPPowerSaveLevel) -> bool:
3577
- '''set the level the device powers down to in power save'''
3595
+ '''write the level the device powers down to in power save, which switches power save on'''
3578
3596
 
3579
3597
  @abstractmethod
3580
3598
  async def set_oneip_ptp_messages(self, which:OneIPDeviceSetting, messages:OneIPPtpMessages) -> bool:
@@ -962,6 +962,8 @@ class OneIPTestcard:
962
962
 
963
963
  class OneIPDeviceSettings:
964
964
  def __init__(self, valid: OneIPDeviceSetting = ..., flags: OneIPDeviceSetting = ..., ir_profiles: int = 0, ir_profile: int = 0, ir_profile_sink: int = 0, auto_power_save: int = 0, power_save: OneIPPowerSaveSchedule | None = None, ptp_mode: OneIPPtpMode | int = ..., ptp_domain: int = 0, ptp_priority1: int = 0, power_save_level: OneIPPowerSaveLevel | int = ..., ptp_messages: OneIPPtpMessages | None = None) -> None: ...
965
+ @classmethod
966
+ def power_save_switch(cls, enabled: bool) -> OneIPDeviceSettings: ...
965
967
  @property
966
968
  def valid(self) -> OneIPDeviceSetting: ...
967
969
  @property
@@ -1330,6 +1332,8 @@ class DeviceBase(ABC):
1330
1332
  @abstractmethod
1331
1333
  async def set_oneip_ptp_priority1(self, priority1: int) -> bool: ...
1332
1334
  @abstractmethod
1335
+ async def set_oneip_power_save(self, enabled: bool) -> bool: ...
1336
+ @abstractmethod
1333
1337
  async def set_oneip_power_save_level(self, level: OneIPPowerSaveLevel) -> bool: ...
1334
1338
  @abstractmethod
1335
1339
  async def set_oneip_ptp_messages(self, which: OneIPDeviceSetting, messages: OneIPPtpMessages) -> bool: ...
@@ -14,7 +14,7 @@ Default communication modes:
14
14
  """
15
15
  import os
16
16
 
17
- VERSION = '9.1.0'
17
+ VERSION = '9.2.0'
18
18
  __version__ = VERSION
19
19
 
20
20
  MX_BCAST_UDP_PORT = 8811
@@ -349,7 +349,11 @@ class OneIPDeviceSetting(IntFlag):
349
349
  AES67 = (1 << 17)
350
350
  '''Sends the source's audio as an AES67 flow, where the source allows.'''
351
351
  POWER_SAVE_LEVEL = (1 << 18)
352
- '''The power save level the device powers down to.'''
352
+ '''The level the device powers down to in power save. A device picks it
353
+ itself: the level that saves the most while its input and display can
354
+ still wake it. Written without POWER_SAVE_DISABLED, it switches power save
355
+ on, and the level written is ignored, except by a device whose firmware
356
+ predates picking it, which takes the level as its setting.'''
353
357
  PTP_ANNOUNCE = (1 << 19)
354
358
  '''The interval between the device's PTP announce messages.'''
355
359
  PTP_SYNC = (1 << 20)
@@ -76,7 +76,6 @@ from typing import Any, Callable
76
76
  from ..compat import override
77
77
  from datetime import datetime
78
78
  from enum import Enum
79
- import dataclasses
80
79
  import logging
81
80
  import time
82
81
 
@@ -1572,12 +1571,28 @@ class Device(DeviceBase):
1572
1571
  return self._send_v2ip_settings(OneIPDeviceSettings(
1573
1572
  valid=OneIPDeviceSetting.PTP_PRIORITY1, ptp_priority1=priority1))
1574
1573
 
1575
- async def set_oneip_power_save_level(self, level:OneIPPowerSaveLevel) -> bool:
1576
- '''Set the level this OneIP device powers down to in power save.
1574
+ async def set_oneip_power_save(self, enabled:bool) -> bool:
1575
+ '''Switch this OneIP device's power save on, so it powers down by itself
1576
+ to the level it picks, or off, so it powers down only when asked.
1577
1577
 
1578
- A level this library does not name is refused, since a device ignores it.
1579
- One the unit does not offer is one its unit_status reports without
1580
- UnitPowerSave.SUPPORTED. The terms of set_oneip_setting() apply.
1578
+ On is written as a mesh controller writes it: both POWER_SAVE_DISABLED,
1579
+ clear, and POWER_SAVE_LEVEL, at 0, so a device whose firmware takes the
1580
+ level as its setting returns to its own default. The terms of
1581
+ set_oneip_setting() apply.
1582
+ '''
1583
+ return self._send_v2ip_settings(OneIPDeviceSettings.power_save_switch(enabled))
1584
+
1585
+ async def set_oneip_power_save_level(self, level:OneIPPowerSaveLevel) -> bool:
1586
+ '''Write the level this OneIP device powers down to in power save, which
1587
+ switches power save on.
1588
+
1589
+ A device picks its level itself and ignores the one written, except one
1590
+ whose firmware predates picking it, which takes the level as its
1591
+ setting. A level that unit does not offer is one its unit_status
1592
+ reports without UnitPowerSave.SUPPORTED. A level this library does not
1593
+ name is refused. The level is not cached: oneip_settings reads what the
1594
+ device reports. set_oneip_power_save() switches power save on or off
1595
+ without naming a level. The terms of set_oneip_setting() apply.
1581
1596
  '''
1582
1597
  if not (OneIPPowerSaveLevel.NONE <= level <= OneIPPowerSaveLevel.OFF):
1583
1598
  _LOGGER.warning(f"not setting power save level {level} on {self}: no such level")
@@ -1612,7 +1627,6 @@ class Device(DeviceBase):
1612
1627
  _LOGGER.warning(f"not turning the output of {self} by {messages.sink_rotation} degrees: "
1613
1628
  f"it turns by steps of {own.sink_rotation_step}")
1614
1629
  return False
1615
- messages = dataclasses.replace(messages, sink_rotation_step=own.sink_rotation_step)
1616
1630
  return self._send_v2ip_settings(OneIPDeviceSettings(valid=which, ptp_messages=messages))
1617
1631
 
1618
1632
  def _on_oneip_downmix(self, downmix:OneIPDownmix|None, reported:bool) -> None:
@@ -1932,7 +1946,7 @@ class Device(DeviceBase):
1932
1946
  return False
1933
1947
  if self.registry.transmit(frame.frame) != len(frame.frame):
1934
1948
  return False
1935
- self._merge_v2ip_settings(settings)
1949
+ self._merge_v2ip_settings(settings.as_applied_to(reported))
1936
1950
  return True
1937
1951
 
1938
1952
  @property
@@ -270,6 +270,7 @@ class Device(DeviceBase):
270
270
  async def set_oneip_ptp_mode(self, mode: OneIPPtpMode) -> bool: ...
271
271
  async def set_oneip_ptp_domain(self, domain: int) -> bool: ...
272
272
  async def set_oneip_ptp_priority1(self, priority1: int) -> bool: ...
273
+ async def set_oneip_power_save(self, enabled: bool) -> bool: ...
273
274
  async def set_oneip_power_save_level(self, level: OneIPPowerSaveLevel) -> bool: ...
274
275
  async def set_oneip_ptp_messages(self, which: OneIPDeviceSetting, messages: OneIPPtpMessages) -> bool: ...
275
276
  async def set_oneip_downmix(self, downmix: OneIPDownmix) -> bool: ...
@@ -561,6 +561,12 @@ class Remote(DeviceRegistry, ConnectionCallbacks):
561
561
  return False
562
562
  return self._send_to_all(FrameV2IPSettingsAll.construct(mxr=self, settings=settings))
563
563
 
564
+ async def set_all_oneip_power_save(self, enabled:bool) -> bool:
565
+ '''Switch power save on or off on every OneIP device of the mesh with
566
+ one frame, written as Device.set_oneip_power_save() writes it.
567
+ Otherwise as set_all_oneip_device_settings().'''
568
+ return await self.set_all_oneip_device_settings(OneIPDeviceSettings.power_save_switch(enabled))
569
+
564
570
  async def request_all_oneip_privacy(self) -> bool:
565
571
  '''Ask every OneIP source unit of the mesh how its streams are
566
572
  encrypted, as Device.request_oneip_privacy() asks one. A unit below
@@ -73,6 +73,7 @@ class Remote(DeviceRegistry, ConnectionCallbacks):
73
73
  def transmit(self, data: bytes) -> int: ...
74
74
  def tx_discover(self) -> int: ...
75
75
  async def set_all_oneip_device_settings(self, settings: OneIPDeviceSettings) -> bool: ...
76
+ async def set_all_oneip_power_save(self, enabled: bool) -> bool: ...
76
77
  async def request_all_oneip_privacy(self) -> bool: ...
77
78
  async def set_mesh_time_zone(self, zone: str, rule: str) -> bool: ...
78
79
  async def clear_mesh_time_zone(self) -> bool: ...
@@ -373,7 +373,8 @@ assert call(level.set_oneip_power_save_level(mx_remote.OneIPPowerSaveLevel.OFF))
373
373
  block = sent[-1][24 + 128:]
374
374
  assert struct.unpack('<I', block[:4])[0] == int(S.POWER_SAVE_LEVEL)
375
375
  assert block[44:48] == bytes([0, 0, 0, 3]), block[44:48].hex()
376
- assert level.oneip_settings.power_save_level is mx_remote.OneIPPowerSaveLevel.OFF
376
+ assert level.oneip_settings.power_save_level is mx_remote.OneIPPowerSaveLevel.LOW, 'a written level was cached'
377
+ assert level.oneip_settings.get(S.POWER_SAVE_DISABLED) is None, 'a switch the device does not have'
377
378
  assert call(level.set_oneip_setting(S.AES67, False)) is True, 'AES67 is not switched'
378
379
  assert level.oneip_settings.get(S.AES67) is False
379
380
  refused(level.set_oneip_power_save_level(4), 'a power save level with no name')
@@ -397,6 +398,35 @@ assert struct.unpack('<II', sent[-1][24 + 128:24 + 136]) == (DIS, DIS)
397
398
  refused(ptp.set_oneip_setting(S.POWER_SAVE_DISABLED, True), 'a unit that never reported the bit')
398
399
  print('ps disabled : a switch, and a level switches it off')
399
400
 
401
+ # Power save is switched on as a mesh controller switches it, with both bits
402
+ # and a level of 0, and off with the switch alone. A device picks its level
403
+ # itself, so a written level, ours or another sender's, is never cached, and
404
+ # only switches power save on.
405
+ BOTH = int(S.POWER_SAVE_LEVEL) | DIS
406
+ rx(uid(0x22), 0x3C, cfg_level(uid(0x22), LEVEL | DIS, 2, flags=DIS))
407
+ cached = lambda: (level.oneip_settings.get(S.POWER_SAVE_DISABLED), level.oneip_settings.power_save_level)
408
+ LOW = mx_remote.OneIPPowerSaveLevel.LOW
409
+ assert call(level.set_oneip_power_save(True)) is True
410
+ block = sent[-1][24 + 128:]
411
+ assert struct.unpack('<II', block[:8]) == (BOTH, 0), block[:8].hex()
412
+ assert block[47] == 0, 'the level'
413
+ assert cached() == (False, LOW), cached()
414
+ assert call(level.set_oneip_power_save(False)) is True
415
+ block = sent[-1][24 + 128:]
416
+ assert struct.unpack('<II', block[:8]) == (DIS, DIS), block[:8].hex()
417
+ assert cached() == (True, LOW), cached()
418
+ assert call(level.set_oneip_power_save_level(mx_remote.OneIPPowerSaveLevel.OFF)) is True
419
+ assert cached() == (False, LOW), "our level"
420
+ # Another sender's level: a flag outside valid says nothing, and one behind its
421
+ # own bit decides.
422
+ assert call(level.set_oneip_power_save(False)) is True
423
+ rx(uid(0x04), 0x3C, cfg_level(uid(0x22), int(S.POWER_SAVE_LEVEL), 3, flags=DIS))
424
+ assert cached() == (False, LOW), "another sender's level"
425
+ rx(uid(0x04), 0x3C, cfg_level(uid(0x22), BOTH, 3, flags=DIS))
426
+ assert cached() == (True, LOW), 'and its switch'
427
+ refused(ptp.set_oneip_power_save(True), 'a unit that never reported the bits')
428
+ print('ps switch : both bits and level 0 on, the switch alone off; the level is the device\'s')
429
+
400
430
  # The cloud connection is a switch too. A unit without the bit connects
401
431
  # regardless, and never reports it, so the switch is refused there.
402
432
  CLOUD = int(S.CLOUD)
@@ -540,6 +570,16 @@ write = mx_remote.OneIPDeviceSettings(valid=S.STATUS_LED | S.SINK_ROTATION,
540
570
  assert write.as_applied_to(S.STATUS_LED | S.SINK_ROTATION).valid == S.STATUS_LED
541
571
  print('rotation : a u16 at 10, by the unit\'s own step, which a write leaves 0')
542
572
 
573
+ # The mesh-wide power save switch writes what the per-device one does, in the
574
+ # frame every unit of the mesh applies.
575
+ for enabled, valid, flags in ((True, BOTH, 0), (False, DIS, DIS)):
576
+ assert call(mx.set_all_oneip_power_save(enabled)) is True, enabled
577
+ p = sent[-1][24:]
578
+ assert len(p) == 48, (enabled, len(p))
579
+ assert struct.unpack('<II', p[:8]) == (valid, flags), (enabled, p[:8].hex())
580
+ assert p[47] == 0, enabled
581
+ print('ps all : the mesh-wide switch is the per-device one')
582
+
543
583
  # What the builder writes, the decoder reads back as the same settings.
544
584
  assert call(target.set_oneip_ir_profile(7)) is True
545
585
  rx(uid(0x10), 0x3C, sent[-1][24:])
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes