mx-remote 9.4.0__tar.gz → 11.0.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.
- {mx_remote-9.4.0 → mx_remote-11.0.0}/PKG-INFO +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/api.md +33 -2
- mx_remote-11.0.0/docs/audio.md +98 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/callbacks.md +2 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/configuration.md +40 -10
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/devices-and-bays.md +33 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/diagnostics.md +11 -5
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/oneip.md +147 -4
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/Interface.py +439 -27
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/Interface.pyi +164 -10
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/const.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/main.py +6 -2
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/Constants.py +78 -19
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/Constants.pyi +21 -7
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBayConfig.py +2 -0
- mx_remote-11.0.0/mx_remote/proto/FrameConnectStatus.py +62 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameConnectStatus.pyi +3 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameFactoryReset.py +7 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameFactoryReset.pyi +3 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameFilterStatus.py +8 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameFilterStatus.pyi +3 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameHello.py +71 -15
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameHello.pyi +13 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameMeshOperation.py +39 -36
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameMeshOperation.pyi +8 -13
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameMirrorStatus.py +8 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameMirrorStatus.pyi +3 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameNetworkStatus.py +12 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameNetworkStatus.pyi +7 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FramePing.py +3 -2
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameRCSettings.py +17 -2
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameRCSettings.pyi +4 -2
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameReboot.py +8 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameReboot.pyi +2 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSetInstaller.py +7 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSetInstaller.pyi +3 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSetupStatus.py +6 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSetupStatus.pyi +3 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSignalStatusNew.py +21 -11
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSignalStatusNew.pyi +2 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTopology.py +6 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameUpgradeFPGA.py +9 -3
- mx_remote-11.0.0/mx_remote/proto/FrameUpgradeFPGA.pyi +6 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPAudio.py +56 -36
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPAudio.pyi +13 -9
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPDeviceConfiguration.py +95 -9
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPDeviceConfiguration.pyi +12 -2
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPPowerSave.py +22 -2
- mx_remote-11.0.0/mx_remote/proto/FrameV2IPPowerSave.pyi +17 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPSourceSwitch.py +10 -3
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPSourceSwitch.pyi +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPSources.py +41 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPSources.pyi +5 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPTiling.py +9 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPTiling.pyi +3 -1
- mx_remote-11.0.0/mx_remote/remote/AudioRoute.py +87 -0
- mx_remote-11.0.0/mx_remote/remote/AudioRoute.pyi +3 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/Bay.py +45 -13
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/Bay.pyi +8 -3
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/ConnectionAsync.py +117 -63
- mx_remote-11.0.0/mx_remote/remote/ConnectionAsync.pyi +38 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/Device.py +629 -35
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/Device.pyi +76 -5
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/Remote.py +505 -57
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/Remote.pyi +59 -10
- mx_remote-11.0.0/mx_remote/remote/Tree.py +103 -0
- mx_remote-11.0.0/mx_remote/remote/Tree.pyi +14 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/audiolock.py +1 -1
- mx_remote-11.0.0/tests/audioroute.py +469 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/complete.py +1 -1
- mx_remote-11.0.0/tests/connect.py +85 -0
- mx_remote-11.0.0/tests/controllerset.py +241 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/deprecated.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/downmix.py +26 -8
- mx_remote-11.0.0/tests/endpoints.py +279 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/entry.py +2 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/hello.py +7 -6
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/lengths.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/mesh.py +3 -3
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/meshbays.py +1 -1
- mx_remote-11.0.0/tests/meshcontrol.py +396 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/offline.py +1 -1
- mx_remote-11.0.0/tests/oneiproute.py +179 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/osdmessage.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/osdsync.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/ping.py +11 -5
- mx_remote-11.0.0/tests/preview.py +91 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/privacy.py +23 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/protogate.py +1 -1
- mx_remote-11.0.0/tests/registry.py +528 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/roundtrip.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/routes.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/run.py +8 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/smoke.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/staterequest.py +5 -4
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/thirdparty.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/txresult.py +13 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/unit.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/v2ipscaling.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/v2ipsdp.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/v2ipsettings.py +2 -2
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/v2ipsubject.py +5 -6
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/v2iptestcard.py +12 -8
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/v2ipvlan.py +1 -1
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/vdetails.py +39 -3
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/wirefix.py +22 -1
- mx_remote-9.4.0/docs/audio.md +0 -56
- mx_remote-9.4.0/mx_remote/proto/FrameConnectStatus.py +0 -35
- mx_remote-9.4.0/mx_remote/proto/FrameUpgradeFPGA.pyi +0 -3
- mx_remote-9.4.0/mx_remote/proto/FrameV2IPPowerSave.pyi +0 -12
- mx_remote-9.4.0/mx_remote/remote/ConnectionAsync.pyi +0 -27
- {mx_remote-9.4.0 → mx_remote-11.0.0}/.gitignore +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/AGENTS.md +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/CLAUDE.md +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/LICENSE +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/README.md +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/README.md +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/_static/brand.css +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/_static/favicon.svg +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/_static/logo-dark.svg +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/_static/logo.svg +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/conf.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/index.md +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/multiviewer.md +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/requirements.txt +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/docs/routing.md +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/hatch_build.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/Uid.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/Uid.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/__init__.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/__init__.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/compat.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/compat.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/const.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/deprecated.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/deprecated.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/main.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/BayConfig.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/BayConfig.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/Data.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/Data.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/Factory.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/Factory.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameAmpDolbySettings.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameAmpDolbySettings.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameAmpZoneSettings.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameAmpZoneSettings.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameAudioClip.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameAudioClip.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameAudioSetRoute.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameAudioSetRoute.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBase.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBase.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBayConfig.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBayConfigSecondary.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBayConfigSecondary.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBayContent.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBayContent.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBayHide.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBayHide.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBayStatus.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameBayStatus.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameDebug.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameDebug.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameDiscover.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameDiscover.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameEDID.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameEDID.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameEDIDProfile.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameEDIDProfile.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameFirmwareVersion.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameFirmwareVersion.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameHeader.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameHeader.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameLinks.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameLinks.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameMonitoringPulse.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameMonitoringPulse.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FramePing.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FramePowerChange.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FramePowerChange.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameRCAction.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameRCAction.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameRCIr.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameRCIr.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameRCKey.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameRCKey.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameRoutingChange.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameRoutingChange.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSetName.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSetName.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSetRoute.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSetRoute.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSignalStatus.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSignalStatus.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSysTemperature.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSysTemperature.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSystemStatus.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameSystemStatus.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTXRCAction.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTXRCAction.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTXRCKey.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTXRCKey.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameThirdParty.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameThirdParty.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTime.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTime.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTimeZone.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTimeZone.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTopology.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTxIR.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameTxIR.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameUnitCommand.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameUnitCommand.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameUnitStatus.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameUnitStatus.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPBayMapping.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPBayMapping.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPDetectBays.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPDetectBays.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPLink.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPLink.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPManualSourceSwitch.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPManualSourceSwitch.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPMultiviewer.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPMultiviewer.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPOsdSync.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPOsdSync.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPPrivacy.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPPrivacy.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPSetMaster.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPSetMaster.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPSettingsAll.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPSettingsAll.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPSinkSdp.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPSinkSdp.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPStats.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPStats.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPTestcard.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPTestcard.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPVideoWall.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameV2IPVideoWall.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameVolume.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameVolume.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameVolumeDown.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameVolumeDown.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameVolumeSet.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameVolumeSet.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameVolumeUp.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/FrameVolumeUp.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/LinkConfig.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/LinkConfig.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/Multiviewer.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/Multiviewer.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/Svd.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/Svd.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/V2IPConfig.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/V2IPConfig.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/V2IPStats.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/V2IPStats.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/__init__.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/__init__.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/proto/svd.csv +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/py.typed +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/Link.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/Link.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/OsdSync.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/OsdSync.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/State.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/State.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/V2IP.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/mx_remote/remote/V2IP.pyi +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/pyproject.toml +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/audio_ir.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/baycontent.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/capture.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/e2e.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/handlers.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/handlers_cover.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/idempotent.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/merge.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/newops.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/offsets.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/proto_stamp.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/rcsettings.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/sources.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/stats.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/structs.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/uid.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/unknowns.py +0 -0
- {mx_remote-9.4.0 → mx_remote-11.0.0}/tests/v2ipptp.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mx_remote
|
|
3
|
-
Version:
|
|
3
|
+
Version: 11.0.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/
|
|
@@ -13,8 +13,10 @@ the directive source as prose and the build still succeeds.
|
|
|
13
13
|
|
|
14
14
|
## Client
|
|
15
15
|
|
|
16
|
-
`Remote` owns the
|
|
17
|
-
keeps both in step with the mesh.
|
|
16
|
+
`Remote` owns the two endpoints, the device registry and the background probe
|
|
17
|
+
that keeps both in step with the mesh. `reboot_all()`, `reboot_mesh()`,
|
|
18
|
+
`factory_reset_all()`, `request_all_oneip_power_save()` and
|
|
19
|
+
`upgrade_all_video_processors()` act on every unit at once.
|
|
18
20
|
|
|
19
21
|
```{eval-rst}
|
|
20
22
|
.. automodule:: mx_remote.remote.Remote
|
|
@@ -185,6 +187,14 @@ How a device's output mixes 5.1 and 7.1 PCM down to stereo.
|
|
|
185
187
|
.. autodata:: mx_remote.proto.Constants.ONEIP_DOWNMIX_LFE_MAX
|
|
186
188
|
```
|
|
187
189
|
|
|
190
|
+
## OneIP locale
|
|
191
|
+
|
|
192
|
+
The locale a device runs, by its number.
|
|
193
|
+
|
|
194
|
+
```{eval-rst}
|
|
195
|
+
.. autodata:: mx_remote.proto.Constants.ONEIP_LOCALE_UNKNOWN
|
|
196
|
+
```
|
|
197
|
+
|
|
188
198
|
## OneIP PTP
|
|
189
199
|
|
|
190
200
|
A device's PTP settings, which it reports and a controller writes, and the PTP
|
|
@@ -357,6 +367,27 @@ The time zone and clock a mesh controller announces.
|
|
|
357
367
|
:members:
|
|
358
368
|
```
|
|
359
369
|
|
|
370
|
+
## Mesh control
|
|
371
|
+
|
|
372
|
+
The operations a mesh controller sends, and what a controller announces.
|
|
373
|
+
|
|
374
|
+
```{eval-rst}
|
|
375
|
+
.. autoclass:: mx_remote.MeshOperationMessage
|
|
376
|
+
:members:
|
|
377
|
+
|
|
378
|
+
.. autoclass:: mx_remote.MeshControllerReport
|
|
379
|
+
:members:
|
|
380
|
+
|
|
381
|
+
.. autoclass:: mx_remote.proto.Constants.MeshOperation
|
|
382
|
+
:members:
|
|
383
|
+
|
|
384
|
+
.. autodata:: mx_remote.proto.Constants.ONEIP_SOURCE_FILTER_MAX
|
|
385
|
+
|
|
386
|
+
.. autodata:: mx_remote.proto.Constants.MXR_RC_STATUS_MAX
|
|
387
|
+
|
|
388
|
+
.. autodata:: mx_remote.proto.FrameV2IPSources.ONEIP_SOURCE_LIST_MAX
|
|
389
|
+
```
|
|
390
|
+
|
|
360
391
|
## Names from before OneIP
|
|
361
392
|
|
|
362
393
|
Releases up to 5.11 used the firmware's internal names in the public API: V2IP
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Audio
|
|
2
|
+
|
|
3
|
+
Volume, mute and remote-control passthrough.
|
|
4
|
+
|
|
5
|
+
## Volume Control
|
|
6
|
+
|
|
7
|
+
```python
|
|
8
|
+
bay.volume_up()
|
|
9
|
+
bay.volume_down()
|
|
10
|
+
bay.volume_set(volume=50) # set to 50%
|
|
11
|
+
bay.volume_set(volume=50, muted=False)
|
|
12
|
+
bay.mute_set(mute=True)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Audio endpoints
|
|
16
|
+
|
|
17
|
+
A OneIP device reports its audio endpoints, each with its features and a status
|
|
18
|
+
word on the same bits: `FEATURE_MUTE` while it is muted, `FEATURE_TRIGGER`
|
|
19
|
+
while its trigger is active, and `FEATURE_AUDIO_LOCK` while its audio source is
|
|
20
|
+
locked.
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
ep = device.audio_endpoint_by_id(1)
|
|
24
|
+
print(ep.features, ep.status, ep.audio_locked)
|
|
25
|
+
|
|
26
|
+
# keep the endpoint's audio source when the video route changes
|
|
27
|
+
await device.set_audio_endpoint_locked(1, True)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Only an endpoint whose features include `FEATURE_AUDIO_LOCK` is written, since
|
|
31
|
+
the device ignores the rest. Once the frame is away `audio_locked` reads the new
|
|
32
|
+
lock, until the device reports its endpoints again. A report whose only change
|
|
33
|
+
is a status, the routing or the device's own address fires the device's
|
|
34
|
+
callbacks.
|
|
35
|
+
|
|
36
|
+
`device.audio_endpoints.address` is the device's own address and HTTP port, as
|
|
37
|
+
its report gives them. `device.audio_reported` is when its last report was
|
|
38
|
+
taken, as `time.time()` read it.
|
|
39
|
+
|
|
40
|
+
### What a command holds
|
|
41
|
+
|
|
42
|
+
For 500 ms after an audio command to a device, its features and links reports
|
|
43
|
+
are ignored, so one it sent before it took the command cannot undo what the
|
|
44
|
+
command set. The report after that replaces it.
|
|
45
|
+
|
|
46
|
+
## Audio routes
|
|
47
|
+
|
|
48
|
+
A route can run from an endpoint on one unit to an endpoint on another, such as
|
|
49
|
+
an encoder's HDMI input to a decoder's HDMI output. Every unit owning an
|
|
50
|
+
endpoint on the way switches its own hops, and each finds the path itself from
|
|
51
|
+
the two units' endpoint tables. `audio_route()` finds the same path and returns
|
|
52
|
+
it hop by hop from the source, or `None` when there is none:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
hops = mx.audio_route(decoder.remote_id, 3, encoder.remote_id, 2)
|
|
56
|
+
for hop in hops: # AudioHop(sink_device, sink, source_device, source)
|
|
57
|
+
print(hop)
|
|
58
|
+
|
|
59
|
+
await mx.select_audio_endpoint_input(decoder.remote_id, 3, encoder.remote_id, 2)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`select_audio_endpoint_input()` sends the request to every unit on the path,
|
|
63
|
+
from the sink's end, and each unit's own hops read routed once its frame is
|
|
64
|
+
away. It is refused, sending nothing, when no reported path leads from the
|
|
65
|
+
source to the sink, and when the path reaches a unit on released firmware up to
|
|
66
|
+
4.4.2 from another unit, since such a unit routes only between its own
|
|
67
|
+
endpoints. An output bay's `select_audio_source(source, endpoint=...)` goes the
|
|
68
|
+
same way.
|
|
69
|
+
|
|
70
|
+
The path is searched back from the sink, within the sink's unit, depth first.
|
|
71
|
+
An endpoint takes the source directly when it is routed from the source's
|
|
72
|
+
endpoint id, or when it receives a OneIP stream and the source sends one.
|
|
73
|
+
Otherwise the endpoints it is routed from are tried in ascending id, a later
|
|
74
|
+
one kept only when its path is strictly shorter. Failing that, the source
|
|
75
|
+
unit's first sending endpoint feeds the sink unit's first receiving one.
|
|
76
|
+
|
|
77
|
+
## Remote Control
|
|
78
|
+
|
|
79
|
+
Send remote control key presses and actions:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
from mx_remote import RCKey, RCAction
|
|
83
|
+
|
|
84
|
+
# send a key press
|
|
85
|
+
await bay.send_key(RCKey.KEY_SELECT)
|
|
86
|
+
await bay.send_key(RCKey.KEY_UP)
|
|
87
|
+
|
|
88
|
+
# send a remote control action
|
|
89
|
+
await bay.tx_action(RCAction.ACTION_POWER_ON)
|
|
90
|
+
await bay.tx_action(RCAction.ACTION_POWER_OFF)
|
|
91
|
+
await bay.tx_action(RCAction.ACTION_POWER_TOGGLE)
|
|
92
|
+
await bay.tx_action(RCAction.ACTION_VOLUME_UP)
|
|
93
|
+
await bay.tx_action(RCAction.ACTION_VOLUME_DOWN)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
[Documentation index](README.md) | [Project README](https://github.com/opdenkamp/mx-remote#readme)
|
|
@@ -41,6 +41,7 @@ Available callback methods:
|
|
|
41
41
|
| `on_device_config_changed` | device configuration updated |
|
|
42
42
|
| `on_device_config_complete` | all device configuration received |
|
|
43
43
|
| `on_device_online_status_changed` | device went online/offline |
|
|
44
|
+
| `on_device_unregistered` | device forgotten after three offline windows of silence |
|
|
44
45
|
| `on_device_temperature_changed` | temperature readings changed |
|
|
45
46
|
| `on_bay_registered` | new bay discovered |
|
|
46
47
|
| `on_video_source_changed` | video routing changed |
|
|
@@ -68,6 +69,7 @@ Available callback methods:
|
|
|
68
69
|
| `on_amp_zone_settings_changed` | amplifier zone settings changed |
|
|
69
70
|
| `on_amp_dolby_settings_changed` | amplifier Dolby settings changed |
|
|
70
71
|
| `on_third_party_message` | another application sent a message under its vendor id |
|
|
72
|
+
| `on_mesh_operation` | a device sent a mesh operation other than a membership or controller report |
|
|
71
73
|
|
|
72
74
|
You can also register per-device and per-bay callbacks:
|
|
73
75
|
|
|
@@ -6,28 +6,46 @@ Opening a connection and keeping it in step with the mesh.
|
|
|
6
6
|
|
|
7
7
|
### Remote
|
|
8
8
|
|
|
9
|
-
`Remote` is the main entry point. It manages the UDP connection
|
|
9
|
+
`Remote` is the main entry point. It manages the UDP connection, handles device discovery, and maintains a registry of all discovered devices.
|
|
10
10
|
|
|
11
11
|
```python
|
|
12
|
-
#
|
|
12
|
+
# both endpoints a unit has: the group 224.8.8.8:8812 and the subnet broadcast on 8811
|
|
13
13
|
mx = mx_remote.Remote()
|
|
14
14
|
|
|
15
|
-
# use broadcast instead
|
|
16
|
-
mx = mx_remote.Remote(broadcast=True)
|
|
17
|
-
|
|
18
15
|
# bind to a specific network interface
|
|
19
16
|
mx = mx_remote.Remote(local_ip="192.168.1.100")
|
|
20
17
|
|
|
21
|
-
#
|
|
22
|
-
mx = mx_remote.Remote(target_ip="
|
|
18
|
+
# tests and development only: one endpoint on a scratch address in place of both
|
|
19
|
+
mx = mx_remote.Remote(target_ip="239.255.77.90", port=18890)
|
|
23
20
|
|
|
24
21
|
# offline mode for processing capture files
|
|
25
22
|
mx = mx_remote.Remote(open_connection=False)
|
|
26
23
|
|
|
27
24
|
# separate identity, to run concurrent clients from one account
|
|
28
25
|
mx = mx_remote.Remote(uid_path="/var/lib/myapp/.mxr-uid")
|
|
26
|
+
|
|
27
|
+
# a mesh device that can take the controller role, under its own serial and version
|
|
28
|
+
mx = mx_remote.Remote(mesh_device=True, serial="P9SN66660600", version="1.2.3")
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
Each frame leaves by the endpoints a unit picks for it:
|
|
32
|
+
|
|
33
|
+
- a frame for every unit at once goes to both: a discover, reboot or factory
|
|
34
|
+
reset of every unit, the monitoring pulse, bay detection and the video
|
|
35
|
+
processor upgrade;
|
|
36
|
+
- the hello goes to both when it is the first since the sockets opened, or the
|
|
37
|
+
last one there went 30 s ago or more;
|
|
38
|
+
- a frame meant for one device alone, such as a ping or a remote-control
|
|
39
|
+
action, goes by the endpoint that device is heard by;
|
|
40
|
+
- every other frame goes to the group, and to the broadcast address as well
|
|
41
|
+
while some peer has not been heard by multicast for two minutes or reports
|
|
42
|
+
that its multicast fails.
|
|
43
|
+
|
|
44
|
+
A peer heard only by broadcast in that time has this client announce
|
|
45
|
+
`DeviceFeature.STATUS_MCAST_FAULT` itself, at once. A datagram arriving by both
|
|
46
|
+
endpoints within 100 ms is acted on once. `broadcast=` is deprecated and
|
|
47
|
+
ignored.
|
|
48
|
+
|
|
31
49
|
The uid at `uid_path` (default `~/.mxr-uid`) is this client's identity on the mesh:
|
|
32
50
|
addressing is by uid in the payload, and a frame carrying our own uid is dropped as
|
|
33
51
|
an echo. Two clients sharing a uid are one peer to every device and each silently
|
|
@@ -37,6 +55,13 @@ discards everything the other sends, so give concurrent clients separate paths.
|
|
|
37
55
|
|
|
38
56
|
A `DeviceBase` represents a physical device on the network (a Pulse-Eight neo matrix, OneIP unit, or ProAmp8 amplifier). Devices are automatically registered when they respond to discovery requests.
|
|
39
57
|
|
|
58
|
+
The registry is kept as a unit keeps its own. Every frame from a live device
|
|
59
|
+
keeps it alive, and only a hello brings back one gone silent. A device silent
|
|
60
|
+
for three times the window that takes it offline is forgotten:
|
|
61
|
+
`on_device_unregistered` fires, and a hello from it later registers it as a new
|
|
62
|
+
device. `mx.device_tree` lists every device and this client as a unit lists
|
|
63
|
+
them: depth first, each under the device whose topology report names it.
|
|
64
|
+
|
|
40
65
|
```python
|
|
41
66
|
# look up a device by serial number or unique ID
|
|
42
67
|
device = mx.get_by_serial("AB1234")
|
|
@@ -49,7 +74,14 @@ device.model_name # model (e.g. "neo:8", "OneIP Transmitter")
|
|
|
49
74
|
device.address # IP address
|
|
50
75
|
device.version # firmware version
|
|
51
76
|
device.online # True if responding
|
|
77
|
+
device.bays_online # online, and its bays have arrived since it was heard,
|
|
78
|
+
# rebooted or went offline, as a unit judges it
|
|
79
|
+
device.multicast # heard by multicast in the last two minutes; None on one endpoint
|
|
52
80
|
device.status # DeviceStatus enum (ONLINE, OFFLINE, REBOOTING, BOOTING, INACTIVE)
|
|
81
|
+
device.raw_name # name, version and serial as the bytes announced,
|
|
82
|
+
device.raw_version # never substituted or decoded
|
|
83
|
+
device.raw_serial
|
|
84
|
+
device.igmp_querier # the IGMP querier its last uplink report names, or None
|
|
53
85
|
device.features # DeviceFeatures bitmask
|
|
54
86
|
device.config_initialised # False if the device broadcasts config blocks built from
|
|
55
87
|
# uninitialised memory; scaling, bay 0 addresses and the
|
|
@@ -97,6 +129,7 @@ bay.hidden # hidden from UI
|
|
|
97
129
|
bay.online # device is online
|
|
98
130
|
bay.features # BayFeaturesMask
|
|
99
131
|
bay.status # DeviceStatus enum
|
|
132
|
+
bay.status_word # the whole BayStatusMask as last sent, or None
|
|
100
133
|
|
|
101
134
|
# video/audio routing (output bays)
|
|
102
135
|
bay.video_source # currently selected video source bay
|
|
@@ -119,10 +152,7 @@ Update connection settings at runtime:
|
|
|
119
152
|
|
|
120
153
|
```python
|
|
121
154
|
await mx.update_config(
|
|
122
|
-
target_ip="10.8.8.255",
|
|
123
|
-
port=8811,
|
|
124
155
|
local_ip="192.168.1.100",
|
|
125
|
-
broadcast=True,
|
|
126
156
|
callbacks=MyCallbacks(),
|
|
127
157
|
name="My Application",
|
|
128
158
|
)
|
|
@@ -20,14 +20,20 @@ result = await device.get_api("system/status")
|
|
|
20
20
|
|
|
21
21
|
A device on protocol 0x2A pings a peer it suspects is gone and takes it
|
|
22
22
|
offline when nothing answers within about 1.5s. The client answers a ping
|
|
23
|
-
addressed to it with a hello at once, from
|
|
23
|
+
addressed to it with a hello at once, from a device it has heard, as a unit
|
|
24
|
+
does.
|
|
24
25
|
|
|
25
26
|
## Bay Naming
|
|
26
27
|
|
|
27
28
|
```python
|
|
28
29
|
await bay.set_name("Living Room TV")
|
|
30
|
+
await bay.set_name("") # back to the default name the unit picks
|
|
29
31
|
```
|
|
30
32
|
|
|
33
|
+
An empty name asks the unit to reset the bay to its default. The unit picks
|
|
34
|
+
that name itself, so the bay keeps its current name until the unit reports the
|
|
35
|
+
new one in its next bay list.
|
|
36
|
+
|
|
31
37
|
## Bay Visibility
|
|
32
38
|
|
|
33
39
|
Hide or show bays:
|
|
@@ -65,6 +71,32 @@ cleans it, so it reads as the unit's own API shows it. The content outlives the
|
|
|
65
71
|
signal, so check `signal_detected` before showing it. `on_bay_content_changed`
|
|
66
72
|
reports a change, and an empty `content` is a source showing nothing.
|
|
67
73
|
|
|
74
|
+
## Signal and Connected Device
|
|
75
|
+
|
|
76
|
+
`signal_type` is a description written for display. `video_details` holds the
|
|
77
|
+
parts it is written from, so a caller can name the signal its own way:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
d = bay.video_details
|
|
81
|
+
if d is not None and d.from_report:
|
|
82
|
+
print(d.width, d.height, d.colour, d.bpp, d.interlaced, d.hdr)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
A signal status report carries all of them for a valid stream, with `svd` 0 and
|
|
86
|
+
the measured size for a stream whose mode the CTA table lacks. The bay config
|
|
87
|
+
snapshot, which `video_details` falls back to, carries no size or flags, and
|
|
88
|
+
leaves them None.
|
|
89
|
+
|
|
90
|
+
A unit reports the Pulse-Eight device connected to a bay, such as an amplifier
|
|
91
|
+
on a matrix output, in the long form of its connect frame:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
if (p8 := bay.p8_device) is not None:
|
|
95
|
+
print(p8.model, p8.serial, p8.version, p8.ip)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
It is None after a short or a disconnecting connect frame.
|
|
99
|
+
|
|
68
100
|
## Mesh Bays
|
|
69
101
|
|
|
70
102
|
A OneIP unit lists the inputs and outputs of the other units in its mesh as
|
|
@@ -14,14 +14,21 @@ for port_id, port_status in device.network_status.items():
|
|
|
14
14
|
print(f" IP: {port_status.ip}")
|
|
15
15
|
if port_status.mac_address:
|
|
16
16
|
print(f" MAC: {port_status.mac_address}")
|
|
17
|
+
if port_status.uplink:
|
|
18
|
+
print(" the device's uplink")
|
|
19
|
+
if port_status.video_link:
|
|
20
|
+
print(" carries video")
|
|
17
21
|
```
|
|
18
22
|
|
|
23
|
+
`uplink` and `video_link` are read from protocol 0x22; an older report marks
|
|
24
|
+
neither.
|
|
25
|
+
|
|
19
26
|
## CLI Application
|
|
20
27
|
|
|
21
28
|
The `mxr` console application is installed with the package. It discovers devices and logs all received frames in human-readable form.
|
|
22
29
|
|
|
23
30
|
```
|
|
24
|
-
usage: mxr [-h] [-i INPUT] [-f FILTER] [-o OUTPUT] [-l LOCAL_IP]
|
|
31
|
+
usage: mxr [-h] [-i INPUT] [-f FILTER] [-o OUTPUT] [-l LOCAL_IP]
|
|
25
32
|
|
|
26
33
|
MX Remote Manager / Debugger
|
|
27
34
|
|
|
@@ -31,9 +38,11 @@ options:
|
|
|
31
38
|
-f FILTER only log frames from this ip address
|
|
32
39
|
-o OUTPUT write output to a file
|
|
33
40
|
-l LOCAL_IP local ip address of the network interface to use
|
|
34
|
-
-b use broadcast mode instead of multicast
|
|
35
41
|
```
|
|
36
42
|
|
|
43
|
+
`mxr` always runs both endpoints a unit has, the multicast group and the subnet
|
|
44
|
+
broadcast. `-b` is still accepted, and ignored.
|
|
45
|
+
|
|
37
46
|
### Examples
|
|
38
47
|
|
|
39
48
|
```bash
|
|
@@ -43,9 +52,6 @@ mxr
|
|
|
43
52
|
# bind to a specific network interface
|
|
44
53
|
mxr -l 192.168.1.100
|
|
45
54
|
|
|
46
|
-
# use broadcast mode
|
|
47
|
-
mxr -b
|
|
48
|
-
|
|
49
55
|
# log output to a file
|
|
50
56
|
mxr -o /path/to/output.txt
|
|
51
57
|
|
|
@@ -32,6 +32,12 @@ if device.oneip_details:
|
|
|
32
32
|
if details.dscp and details.dscp.complete:
|
|
33
33
|
print(f"DSCP: {details.dscp}") # video / audio / anc, 0..63
|
|
34
34
|
|
|
35
|
+
# mark a source's three streams; a unit takes a marking only for all three at
|
|
36
|
+
# once, so a partial one or one above ONEIP_DSCP_MAX is refused before sending,
|
|
37
|
+
# as is a device that announces no source. Read back from oneip_details.dscp
|
|
38
|
+
# until the device reports its own.
|
|
39
|
+
await device.set_oneip_dscp(mx_remote.OneIPDscpConfig(video=46, audio=34, anc=26))
|
|
40
|
+
|
|
35
41
|
# streaming statistics
|
|
36
42
|
# The subscription lapses 60s after the request and there is no free-running
|
|
37
43
|
# mode, so call this again inside the minute to keep the 1Hz reports coming.
|
|
@@ -125,6 +131,11 @@ await device.set_oneip_output_mode(mode)
|
|
|
125
131
|
await device.set_oneip_auto_scaling(True) # back on, if that is what you want
|
|
126
132
|
|
|
127
133
|
await device.clear_oneip_output_mode() # the only way to say "no mode"
|
|
134
|
+
|
|
135
|
+
# skip the check that the display lists 4:2:0; one validity bit covers this and
|
|
136
|
+
# match-source, so the write restates match-source as the sink last reported it
|
|
137
|
+
print(scaling.skip_420, scaling.match_source)
|
|
138
|
+
await device.set_oneip_skip_420(True)
|
|
128
139
|
```
|
|
129
140
|
|
|
130
141
|
**Set the mode with automatic scaling off.** A sink scaling automatically
|
|
@@ -240,8 +251,16 @@ print(device.unit_status.power_save_level(OneIPPowerSaveLevel.LOW)) # e.g. SUPP
|
|
|
240
251
|
|
|
241
252
|
await device.set_oneip_power_save(True)
|
|
242
253
|
await mx.set_all_oneip_power_save(False) # every OneIP device of the mesh
|
|
254
|
+
|
|
255
|
+
# ask for the state now, as a mesh controller does, rather than the setting
|
|
256
|
+
await device.request_oneip_power_save(True) # 0x41 naming the unit
|
|
257
|
+
await mx.request_all_oneip_power_save(False) # 0x41 with the flag alone
|
|
243
258
|
```
|
|
244
259
|
|
|
260
|
+
A power save request reaches a unit only from its mesh controller or a
|
|
261
|
+
management application, and one that cannot power down ignores it. A unit tells
|
|
262
|
+
the two forms apart by their exact length.
|
|
263
|
+
|
|
245
264
|
Switched off, `OneIPDeviceSetting.POWER_SAVE_DISABLED` is on: the device does
|
|
246
265
|
not power down by itself, and a power save request still powers it down.
|
|
247
266
|
Switching on is written as a mesh controller writes it, with that switch clear
|
|
@@ -335,7 +354,10 @@ the LFE channel is mixed in at, which a controller writes, and the state and
|
|
|
335
354
|
channel count, which only the device reports. A device that reports none has
|
|
336
355
|
no downmix to set, so a write to one is refused, as is a mode, level or LFE
|
|
337
356
|
percent out of range. The write sends the mode, level and LFE together; the
|
|
338
|
-
|
|
357
|
+
status, whether the stream is mixed down now, is the device's own and never
|
|
358
|
+
sent. After this client's write it is None until the device reports how it
|
|
359
|
+
mixes the new settings; another controller's write keeps the status the device
|
|
360
|
+
reported.
|
|
339
361
|
|
|
340
362
|
```python
|
|
341
363
|
from mx_remote import OneIPDownmix
|
|
@@ -344,7 +366,8 @@ from mx_remote.proto.Constants import OneIPDownmixMode, OneIPDownmixLevel
|
|
|
344
366
|
downmix = device.oneip_downmix # None: no report yet, or no downmix
|
|
345
367
|
if downmix is not None:
|
|
346
368
|
print(downmix.mode, downmix.level, downmix.lfe) # LFE up to ONEIP_DOWNMIX_LFE_MAX
|
|
347
|
-
|
|
369
|
+
if downmix.status is not None: # the device's own
|
|
370
|
+
print(downmix.status.state, downmix.status.channels)
|
|
348
371
|
await device.set_oneip_downmix(OneIPDownmix(mode=OneIPDownmixMode.AUTO,
|
|
349
372
|
level=OneIPDownmixLevel.NORMALISED, lfe=50))
|
|
350
373
|
```
|
|
@@ -352,6 +375,40 @@ if downmix is not None:
|
|
|
352
375
|
The downmix ends the settings block, behind a second valid word, so the write
|
|
353
376
|
runs on to 248 bytes and carries no other setting.
|
|
354
377
|
|
|
378
|
+
### Locale
|
|
379
|
+
|
|
380
|
+
A device whose firmware has locales reports the one it runs by its number, its
|
|
381
|
+
position in the list the device's `/system/locale` names, behind the downmix.
|
|
382
|
+
`ONEIP_LOCALE_UNKNOWN` is a stored locale the firmware does not know.
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
print(device.oneip_locale) # None: no report yet, or no locales
|
|
386
|
+
await device.set_oneip_locale(1)
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
A device stores a locale it knows and ignores one it does not, so the write goes
|
|
390
|
+
also to a device that has not reported its settings yet, and the locale is
|
|
391
|
+
cached once sent until the device's next report. Refused before sending:
|
|
392
|
+
`ONEIP_LOCALE_UNKNOWN`, a number that is not a byte, and a device whose settings
|
|
393
|
+
arrived without a locale, as its firmware has none. Another controller's write
|
|
394
|
+
reaches only a device that reports a locale.
|
|
395
|
+
|
|
396
|
+
### Live preview
|
|
397
|
+
|
|
398
|
+
A device whose firmware sends a live preview names the multicast group it sends
|
|
399
|
+
it to at the end of its settings block. The preview is RFC 2435 RTP/JPEG on
|
|
400
|
+
UDP port `ONEIP_PREVIEW_PORT` (5880).
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
from mx_remote.proto.Constants import ONEIP_PREVIEW_PORT
|
|
404
|
+
|
|
405
|
+
print(device.oneip_preview) # '239.7.8.9', or None: no report, or no preview
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Only the device's own report names it; another sender's configuration about the
|
|
409
|
+
device leaves it alone. A group of 0, or a block too short to hold one, is
|
|
410
|
+
firmware that sends no preview.
|
|
411
|
+
|
|
355
412
|
### VLAN
|
|
356
413
|
|
|
357
414
|
A device that announces `DeviceFeature.VLAN` tags its uplink by a VLAN
|
|
@@ -368,12 +425,18 @@ print(vlan.device, vlan.port, vlan.pinned_uplink_port, vlan.active_uplink_port)
|
|
|
368
425
|
print(vlan.is_pending, vlan.revert_s)
|
|
369
426
|
|
|
370
427
|
await device.set_oneip_vlan(OneIPVlan(device=10, port=(0, 20, 0), uplink=2))
|
|
428
|
+
if device.oneip_vlan.is_pending:
|
|
429
|
+
await device.confirm_oneip_vlan() # keep it, as the mesh controller does
|
|
371
430
|
```
|
|
372
431
|
|
|
373
432
|
Only the device knows what it runs, so `oneip_vlan` is read only from the device
|
|
374
433
|
describing itself, and a write is not cached. The device applies a change at
|
|
375
434
|
once and reverts it after `revert_s` seconds unless the mesh controller, hearing
|
|
376
|
-
it report the change as pending, confirms it.
|
|
435
|
+
it report the change as pending, confirms it. `confirm_oneip_vlan()` sends that
|
|
436
|
+
confirmation: the pending configuration echoed with its trunk bit and CONFIRM. A
|
|
437
|
+
device repeats its report every 3 s while the change is pending, so a
|
|
438
|
+
confirmation lost or sent twice is answered by the next report. Refused unless
|
|
439
|
+
the device announces the VLAN feature and last reported a pending change.
|
|
377
440
|
|
|
378
441
|
Only the ids, the uplink and the trunk bit are written. Refused before sending:
|
|
379
442
|
a device without the VLAN feature or that has not reported its configuration, an
|
|
@@ -384,7 +447,7 @@ frame it already understood.
|
|
|
384
447
|
|
|
385
448
|
### Test pattern, tone and lip-sync
|
|
386
449
|
|
|
387
|
-
A sink whose video processor reports `OneIPVideoProcessorFeature.
|
|
450
|
+
A sink whose video processor reports `OneIPVideoProcessorFeature.RTOS` draws a
|
|
388
451
|
test card on its output: a pattern, a test tone, and a lip-sync flash that marks
|
|
389
452
|
a frame and beeps a set number of sample periods after it.
|
|
390
453
|
|
|
@@ -509,6 +572,11 @@ await mx.request_all_oneip_privacy() # every source unit of the mesh
|
|
|
509
572
|
print(source.oneip_privacy) # None until the unit has reported
|
|
510
573
|
```
|
|
511
574
|
|
|
575
|
+
The video processor picks a new key generator every boot, so a hello showing
|
|
576
|
+
the unit may have restarted - it was offline, flipped its reboot toggle or is
|
|
577
|
+
about to reboot - drops the report, and `oneip_privacy` reads None until the
|
|
578
|
+
unit reports again.
|
|
579
|
+
|
|
512
580
|
A request to a unit that is not a source, or that is below protocol 0x30, is
|
|
513
581
|
refused before it is sent.
|
|
514
582
|
|
|
@@ -557,6 +625,7 @@ from mx_remote import UnitNetConfig
|
|
|
557
625
|
|
|
558
626
|
await device.clear_unit_status()
|
|
559
627
|
await device.upgrade_unit_video_processor(force=False)
|
|
628
|
+
await mx.upgrade_all_video_processors() # every unit, flashing only a differing image
|
|
560
629
|
await device.set_unit_network(UnitNetConfig(
|
|
561
630
|
ip='10.1.2.3', netmask='255.255.255.0', gateway='10.1.2.254', dns1='10.1.2.254'))
|
|
562
631
|
await device.confirm_unit_network() # keep it, once the unit reports it
|
|
@@ -595,6 +664,80 @@ the source's next configuration report carries the addresses it ends up with, in
|
|
|
595
664
|
`oneip_details`. A device that is not a source, or announces less than protocol
|
|
596
665
|
0x2B, would ignore the operation, so it is refused before anything is sent.
|
|
597
666
|
|
|
667
|
+
### Taking the controller role
|
|
668
|
+
|
|
669
|
+
A client can stand in for a unit as mesh controller. It announces itself as a
|
|
670
|
+
mesh device rather than a management application, so a mesh takes it as a
|
|
671
|
+
member, and under a serial and version of its own so the device list tells it
|
|
672
|
+
apart from the unit it replaces:
|
|
673
|
+
|
|
674
|
+
```python
|
|
675
|
+
from mx_remote import MeshOperationMessage
|
|
676
|
+
|
|
677
|
+
mx = mx_remote.Remote(mesh_device=True, serial='P9SN66660600', version='1.2.3')
|
|
678
|
+
mx.set_mesh_controller(mx.uid) # follow itself as controller; None for none
|
|
679
|
+
mx.set_mesh_bays_mapped(True) # the role is announced only with bays mapped
|
|
680
|
+
await mx.send_mesh_operation(MeshOperationMessage.report_controller(mx.uid, priority=1, installer=0x1234))
|
|
681
|
+
await mx.send_mesh_operation(MeshOperationMessage.register(unit.remote_id, mx.uid))
|
|
682
|
+
await mx.send_oneip_source_list(sources) # the mesh's whole source list
|
|
683
|
+
await mx.set_mesh_setup_completed(True)
|
|
684
|
+
await mx.set_mesh_installer(0x1234)
|
|
685
|
+
|
|
686
|
+
await output.set_oneip_mirroring(other_output) # None to stop
|
|
687
|
+
await output.set_oneip_source_filter([source.remote_id])
|
|
688
|
+
await output.set_oneip_tiling(pos_x=0, pos_y=0, width=1920, height=1080)
|
|
689
|
+
await unit.set_rc_settings(rc_target=RCType.CEC, ip=None, cec_enabled=True, cec_auto_on=True,
|
|
690
|
+
forward_rc=True, forward_ir=False)
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
`set_mesh_controller()` changes only the announcement, at once: membership
|
|
694
|
+
while there is a controller, and the controller role while it is this client
|
|
695
|
+
and `set_mesh_bays_mapped()` says it has bays, as a unit announces it.
|
|
696
|
+
`mx.features` is what the hello announces as of now. `send_oneip_detect_bays()`
|
|
697
|
+
asks every unit to detect its bays, which current firmware ignores.
|
|
698
|
+
Membership reports and the units that follow this client are the caller's to
|
|
699
|
+
keep in step. A mesh device's writes reach a unit only while that unit follows
|
|
700
|
+
it as its controller.
|
|
701
|
+
|
|
702
|
+
A unit named in a source list, hearing it from the controller it follows,
|
|
703
|
+
registers every unit listed and unregisters every source unit that is not, so
|
|
704
|
+
the list must always be complete. A list longer than 36 entries goes in
|
|
705
|
+
numbered pages.
|
|
706
|
+
|
|
707
|
+
What is set on an output is held once the frame is away: `mirroring` and
|
|
708
|
+
`filtered` on its output bay read it until the output reports again. Each write
|
|
709
|
+
is refused before anything is sent where a unit would drop it: a device that is
|
|
710
|
+
not a OneIP output (or unit, for remote control settings), an output whose bay
|
|
711
|
+
has not been reported, an output mirroring itself, a filter longer than
|
|
712
|
+
`ONEIP_SOURCE_FILTER_MAX` (89), or a field that does not fit its width.
|
|
713
|
+
|
|
714
|
+
A controller routes an output unit by the streams a source unit advertises for
|
|
715
|
+
its own input, so the output plays them whether or not it lists the source:
|
|
716
|
+
|
|
717
|
+
```python
|
|
718
|
+
await output.route_oneip_unit(source) # audio follows the video
|
|
719
|
+
await output.route_oneip_unit(source, audio=other) # audio from another unit's input
|
|
720
|
+
await output.route_oneip_unit(source, power_on=False) # leave the display as it is
|
|
721
|
+
await output.route_oneip_unit_with_arc(source, tv_unit) # audio from tv_unit's audio return
|
|
722
|
+
await output.unroute_oneip_unit() # names no streams
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
Each powers the output's display on with the switch unless `power_on=False`. An
|
|
726
|
+
output below protocol 0x27 is told the source's own audio rather than none, as
|
|
727
|
+
`select_video_source()` does. `route_oneip_unit_with_arc()` names the audio
|
|
728
|
+
return group the other unit last reported in `oneip_details.arc`; no unit maps
|
|
729
|
+
that group to a bay, so on current firmware the output plays it on the default
|
|
730
|
+
audio port, 50022, while the audio return is sent on 50023, and such a route
|
|
731
|
+
plays no audio. It is what a controller sends all the same. A route is refused
|
|
732
|
+
before anything is sent where the device is not a OneIP output, a source has no
|
|
733
|
+
input of its own or has not reported its stream addresses, or the other unit has
|
|
734
|
+
not reported a usable audio return group.
|
|
735
|
+
|
|
736
|
+
A unit's controller report is kept as `device.mesh_controller_report`, and its
|
|
737
|
+
membership report as `mesh_master`. Every other mesh operation a device sends
|
|
738
|
+
arrives at `MxrCallbacks.on_mesh_operation()`, one this library has no name for
|
|
739
|
+
with its raw value.
|
|
740
|
+
|
|
598
741
|
### Time zone and time
|
|
599
742
|
|
|
600
743
|
The mesh controller announces its time zone and clock with every periodic
|