mx-remote 5.4.0__tar.gz → 5.5.1__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-5.4.0 → mx_remote-5.5.1}/AGENTS.md +22 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/PKG-INFO +17 -11
- {mx_remote-5.4.0 → mx_remote-5.5.1}/README.md +14 -9
- {mx_remote-5.4.0 → mx_remote-5.5.1}/docs/README.md +1 -1
- mx_remote-5.5.1/docs/_static/brand.css +34 -0
- mx_remote-5.5.1/docs/_static/favicon.svg +3 -0
- mx_remote-5.5.1/docs/_static/logo-dark.svg +8 -0
- mx_remote-5.5.1/docs/_static/logo.svg +8 -0
- mx_remote-5.5.1/docs/api.md +59 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/docs/audio.md +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/docs/callbacks.md +1 -1
- mx_remote-5.5.1/docs/conf.py +102 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/docs/configuration.md +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/docs/devices-and-bays.md +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/docs/diagnostics.md +1 -1
- mx_remote-5.5.1/docs/index.md +34 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/docs/multiviewer.md +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/docs/oneip.md +1 -1
- mx_remote-5.5.1/docs/requirements.txt +3 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/docs/routing.md +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/Interface.py +2 -2
- mx_remote-5.5.1/mx_remote/compat.py +22 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/const.py +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/Constants.py +5 -6
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameBayConfig.py +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameFactoryReset.py +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameLinks.py +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSystemStatus.py +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSystemStatus.pyi +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPAudio.py +2 -2
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPAudio.pyi +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPBayMapping.py +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPBayMapping.pyi +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPMultiviewer.py +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPMultiviewer.pyi +1 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPVideoWall.py +2 -3
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/V2IPConfig.py +2 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/V2IPConfig.pyi +2 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/V2IPStats.py +3 -3
- mx_remote-5.5.1/mx_remote/py.typed +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/Bay.py +2 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/Bay.pyi +2 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/ConnectionAsync.py +2 -2
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/Device.py +2 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/Device.pyi +2 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/Remote.py +4 -3
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/Remote.pyi +2 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/pyproject.toml +3 -1
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/capture.py +2 -3
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/hello.py +2 -2
- {mx_remote-5.4.0 → mx_remote-5.5.1}/.gitignore +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/CLAUDE.md +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/LICENSE +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/hatch_build.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/Interface.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/Uid.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/Uid.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/__init__.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/__init__.pyi +0 -0
- /mx_remote-5.4.0/mx_remote/py.typed → /mx_remote-5.5.1/mx_remote/compat.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/const.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/main.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/main.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/BayConfig.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/BayConfig.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/Constants.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/Data.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/Data.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/Factory.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/Factory.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameAmpDolbySettings.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameAmpDolbySettings.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameAmpZoneSettings.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameAmpZoneSettings.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameAudioClip.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameAudioClip.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameAudioSetRoute.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameAudioSetRoute.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameBase.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameBase.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameBayConfig.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameBayConfigSecondary.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameBayConfigSecondary.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameBayHide.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameBayHide.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameBayStatus.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameBayStatus.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameConnectStatus.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameConnectStatus.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameDebug.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameDebug.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameDiscover.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameDiscover.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameEDID.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameEDID.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameEDIDProfile.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameEDIDProfile.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameFactoryReset.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameFilterStatus.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameFilterStatus.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameFirmwareVersion.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameFirmwareVersion.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameHeader.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameHeader.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameHello.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameHello.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameLinks.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameMeshOperation.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameMeshOperation.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameMirrorStatus.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameMirrorStatus.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameMonitoringPulse.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameMonitoringPulse.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameNetworkStatus.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameNetworkStatus.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FramePowerChange.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FramePowerChange.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameRCAction.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameRCAction.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameRCIr.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameRCIr.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameRCKey.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameRCKey.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameRCSettings.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameRCSettings.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameReboot.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameReboot.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameRoutingChange.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameRoutingChange.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSetInstaller.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSetInstaller.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSetName.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSetName.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSetRoute.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSetRoute.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSetupStatus.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSetupStatus.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSignalStatus.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSignalStatus.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSignalStatusNew.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSignalStatusNew.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSysTemperature.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameSysTemperature.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameTXRCAction.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameTXRCAction.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameTXRCKey.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameTXRCKey.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameTopology.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameTopology.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameTxIR.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameTxIR.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameUpgradeFPGA.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameUpgradeFPGA.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPDetectBays.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPDetectBays.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPDeviceConfiguration.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPDeviceConfiguration.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPLink.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPLink.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPManualSourceSwitch.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPManualSourceSwitch.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPPowerSave.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPPowerSave.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPSetMaster.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPSetMaster.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPSourceSwitch.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPSourceSwitch.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPSources.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPSources.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPStats.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPStats.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPTiling.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPTiling.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameV2IPVideoWall.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameVolume.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameVolume.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameVolumeDown.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameVolumeDown.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameVolumeSet.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameVolumeSet.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameVolumeUp.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/FrameVolumeUp.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/LinkConfig.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/LinkConfig.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/Multiviewer.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/Multiviewer.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/Svd.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/Svd.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/V2IPStats.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/__init__.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/__init__.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/proto/svd.csv +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/ConnectionAsync.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/Link.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/Link.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/State.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/State.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/V2IP.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/mx_remote/remote/V2IP.pyi +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/audio_ir.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/e2e.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/entry.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/handlers.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/handlers_cover.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/idempotent.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/merge.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/newops.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/offsets.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/proto_stamp.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/protogate.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/rcsettings.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/roundtrip.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/run.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/smoke.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/sources.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/stats.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/structs.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/txresult.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/unknowns.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/vdetails.py +0 -0
- {mx_remote-5.4.0 → mx_remote-5.5.1}/tests/wirefix.py +0 -0
|
@@ -73,6 +73,13 @@ A client's uid is persisted at `~/.mxr-uid`, one file per account, and
|
|
|
73
73
|
account are a single identity to the mesh and each discards everything the other
|
|
74
74
|
sends, so a second one needs its own `uid_path`.
|
|
75
75
|
|
|
76
|
+
### Python versions
|
|
77
|
+
`requires-python` is 3.11, and `.github/workflows/tests.yml` runs the suites on
|
|
78
|
+
3.11, 3.12 and 3.13 so that floor is checked rather than assumed. A name that
|
|
79
|
+
only exists on a newer version goes through `mx_remote/compat.py`, the one place
|
|
80
|
+
that tests `sys.version_info`, and its fallback needs a dependency marked
|
|
81
|
+
`python_version < '<the version that added it>'` in `pyproject.toml`.
|
|
82
|
+
|
|
76
83
|
### Versioning
|
|
77
84
|
Version is single-sourced in `mx_remote/const.py` (`VERSION = '...'`); `pyproject.toml`
|
|
78
85
|
reads it via regex, so bump it there only. Release commits follow the convention
|
|
@@ -83,6 +90,21 @@ reads it via regex, so bump it there only. Release commits follow the convention
|
|
|
83
90
|
force-includes them in the wheel (they are gitignored). `mx_remote` ships as typed
|
|
84
91
|
(`py.typed`). If stubgen produces nothing the build fails by design.
|
|
85
92
|
|
|
93
|
+
### Documentation
|
|
94
|
+
`docs/` is both the GitHub-readable guide set and the Sphinx source for
|
|
95
|
+
[the published site](https://opdenkamp.github.io/mx-remote/), which
|
|
96
|
+
`.github/workflows/docs.yml` rebuilds on every push to master. Build it the way
|
|
97
|
+
the workflow does:
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
pip install . -r docs/requirements.txt
|
|
101
|
+
sphinx-build -b html -W --keep-going docs site
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Autodoc emits reStructuredText, so a directive belongs in an ```` ```{eval-rst} ````
|
|
105
|
+
block. Outside one it renders as its own source and the build still reports
|
|
106
|
+
success — the workflow greps the output for that.
|
|
107
|
+
|
|
86
108
|
## Architecture
|
|
87
109
|
|
|
88
110
|
Three layers, decoupled by abstract base classes:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mx_remote
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.5.1
|
|
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
|
-
Project-URL: Documentation, https://github.
|
|
6
|
+
Project-URL: Documentation, https://opdenkamp.github.io/mx-remote/
|
|
7
7
|
Project-URL: Repository, https://github.com/opdenkamp/mx-remote.git
|
|
8
8
|
Project-URL: Bug Tracker, https://github.com/opdenkamp/mx-remote/issues
|
|
9
9
|
Author-email: Lars Op den Kamp <lars@opdenkamp-it.nl>
|
|
@@ -28,6 +28,7 @@ Requires-Python: >=3.11
|
|
|
28
28
|
Requires-Dist: aiofiles>=24.1.0
|
|
29
29
|
Requires-Dist: aiohttp>=3.10.5
|
|
30
30
|
Requires-Dist: netifaces>=0.11.0
|
|
31
|
+
Requires-Dist: typing-extensions>=4.4; python_version < '3.12'
|
|
31
32
|
Description-Content-Type: text/markdown
|
|
32
33
|
|
|
33
34
|
# MX Remote - Python Client for Pulse-Eight MatrixOS devices
|
|
@@ -41,6 +42,8 @@ streaming, multiviewer control, and more.
|
|
|
41
42
|
If you are looking to integrate Pulse-Eight **neo**, **OneIP**, or **ProAmp8** devices into
|
|
42
43
|
your own software or home-automation system, this is the library for it.
|
|
43
44
|
|
|
45
|
+
**Documentation:** [opdenkamp.github.io/mx-remote](https://opdenkamp.github.io/mx-remote/)
|
|
46
|
+
|
|
44
47
|
## What is MX Remote?
|
|
45
48
|
|
|
46
49
|
MX Remote is the network protocol these Pulse-Eight devices use to discover and control one
|
|
@@ -97,18 +100,21 @@ asyncio.run(main())
|
|
|
97
100
|
|
|
98
101
|
## Documentation
|
|
99
102
|
|
|
100
|
-
The
|
|
103
|
+
The guides below are published at
|
|
104
|
+
**[opdenkamp.github.io/mx-remote](https://opdenkamp.github.io/mx-remote/)** alongside an
|
|
105
|
+
API reference generated from the docstrings, and are readable here in
|
|
106
|
+
[docs/](docs/README.md).
|
|
101
107
|
|
|
102
108
|
| Page | Covers |
|
|
103
109
|
| --- | --- |
|
|
104
|
-
| [Configuration](configuration.md) | Opening a connection and keeping it in step with the mesh. |
|
|
105
|
-
| [Devices and bays](devices-and-bays.md) | Naming, hiding, EDID and the device registry. |
|
|
106
|
-
| [Routing](routing.md) | Selecting video and audio sources for an output. |
|
|
107
|
-
| [Callbacks](callbacks.md) | Reacting to state changes rather than polling for them. |
|
|
108
|
-
| [Audio](audio.md) | Volume, mute and remote-control passthrough. |
|
|
109
|
-
| [OneIP and V2IP](oneip.md) | Streaming endpoints, stream sources and statistics. |
|
|
110
|
-
| [Multiviewer](multiviewer.md) | Layout, sources and output configuration. |
|
|
111
|
-
| [Diagnostics](diagnostics.md) | Network status, the mxr console app and capture replay. |
|
|
110
|
+
| [Configuration](docs/configuration.md) | Opening a connection and keeping it in step with the mesh. |
|
|
111
|
+
| [Devices and bays](docs/devices-and-bays.md) | Naming, hiding, EDID and the device registry. |
|
|
112
|
+
| [Routing](docs/routing.md) | Selecting video and audio sources for an output. |
|
|
113
|
+
| [Callbacks](docs/callbacks.md) | Reacting to state changes rather than polling for them. |
|
|
114
|
+
| [Audio](docs/audio.md) | Volume, mute and remote-control passthrough. |
|
|
115
|
+
| [OneIP and V2IP](docs/oneip.md) | Streaming endpoints, stream sources and statistics. |
|
|
116
|
+
| [Multiviewer](docs/multiviewer.md) | Layout, sources and output configuration. |
|
|
117
|
+
| [Diagnostics](docs/diagnostics.md) | Network status, the mxr console app and capture replay. |
|
|
112
118
|
|
|
113
119
|
## Other languages
|
|
114
120
|
|
|
@@ -9,6 +9,8 @@ streaming, multiviewer control, and more.
|
|
|
9
9
|
If you are looking to integrate Pulse-Eight **neo**, **OneIP**, or **ProAmp8** devices into
|
|
10
10
|
your own software or home-automation system, this is the library for it.
|
|
11
11
|
|
|
12
|
+
**Documentation:** [opdenkamp.github.io/mx-remote](https://opdenkamp.github.io/mx-remote/)
|
|
13
|
+
|
|
12
14
|
## What is MX Remote?
|
|
13
15
|
|
|
14
16
|
MX Remote is the network protocol these Pulse-Eight devices use to discover and control one
|
|
@@ -65,18 +67,21 @@ asyncio.run(main())
|
|
|
65
67
|
|
|
66
68
|
## Documentation
|
|
67
69
|
|
|
68
|
-
The
|
|
70
|
+
The guides below are published at
|
|
71
|
+
**[opdenkamp.github.io/mx-remote](https://opdenkamp.github.io/mx-remote/)** alongside an
|
|
72
|
+
API reference generated from the docstrings, and are readable here in
|
|
73
|
+
[docs/](docs/README.md).
|
|
69
74
|
|
|
70
75
|
| Page | Covers |
|
|
71
76
|
| --- | --- |
|
|
72
|
-
| [Configuration](configuration.md) | Opening a connection and keeping it in step with the mesh. |
|
|
73
|
-
| [Devices and bays](devices-and-bays.md) | Naming, hiding, EDID and the device registry. |
|
|
74
|
-
| [Routing](routing.md) | Selecting video and audio sources for an output. |
|
|
75
|
-
| [Callbacks](callbacks.md) | Reacting to state changes rather than polling for them. |
|
|
76
|
-
| [Audio](audio.md) | Volume, mute and remote-control passthrough. |
|
|
77
|
-
| [OneIP and V2IP](oneip.md) | Streaming endpoints, stream sources and statistics. |
|
|
78
|
-
| [Multiviewer](multiviewer.md) | Layout, sources and output configuration. |
|
|
79
|
-
| [Diagnostics](diagnostics.md) | Network status, the mxr console app and capture replay. |
|
|
77
|
+
| [Configuration](docs/configuration.md) | Opening a connection and keeping it in step with the mesh. |
|
|
78
|
+
| [Devices and bays](docs/devices-and-bays.md) | Naming, hiding, EDID and the device registry. |
|
|
79
|
+
| [Routing](docs/routing.md) | Selecting video and audio sources for an output. |
|
|
80
|
+
| [Callbacks](docs/callbacks.md) | Reacting to state changes rather than polling for them. |
|
|
81
|
+
| [Audio](docs/audio.md) | Volume, mute and remote-control passthrough. |
|
|
82
|
+
| [OneIP and V2IP](docs/oneip.md) | Streaming endpoints, stream sources and statistics. |
|
|
83
|
+
| [Multiviewer](docs/multiviewer.md) | Layout, sources and output configuration. |
|
|
84
|
+
| [Diagnostics](docs/diagnostics.md) | Network status, the mxr console app and capture replay. |
|
|
80
85
|
|
|
81
86
|
## Other languages
|
|
82
87
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Documentation
|
|
2
2
|
|
|
3
|
-
The API reference for `mx_remote`. Start at the [project README](
|
|
3
|
+
The API reference for `mx_remote`. Start at the [project README](https://github.com/opdenkamp/mx-remote#readme) for installation and a quick start.
|
|
4
4
|
|
|
5
5
|
| Page | Covers |
|
|
6
6
|
| --- | --- |
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* Shapes Furo to the house style. The palette itself is set in conf.py, where
|
|
2
|
+
Furo wants it; this file holds what a variable cannot express. */
|
|
3
|
+
|
|
4
|
+
/* The mark is portrait, so a width bound keeps it from filling the sidebar. */
|
|
5
|
+
.sidebar-logo {
|
|
6
|
+
max-width: 4.5rem;
|
|
7
|
+
margin: 0 auto 0.5rem;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.sidebar-brand-text {
|
|
11
|
+
font-weight: 600;
|
|
12
|
+
letter-spacing: -0.025em;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
h1, h2, h3, h4 {
|
|
16
|
+
letter-spacing: -0.025em;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Furo underlines links only on hover; the guides lean on them heavily enough
|
|
20
|
+
that a reader should see where they are while scanning. */
|
|
21
|
+
article a.reference.internal,
|
|
22
|
+
article a.reference.external {
|
|
23
|
+
text-decoration-color: var(--color-brand-content);
|
|
24
|
+
text-decoration-line: underline;
|
|
25
|
+
text-decoration-thickness: 1px;
|
|
26
|
+
text-underline-offset: 0.15em;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* Signatures are the spine of the API reference: give them the brand rule so
|
|
30
|
+
they read as landmarks rather than as another grey block. */
|
|
31
|
+
dl.py > dt {
|
|
32
|
+
border-left: 3px solid var(--color-brand-content);
|
|
33
|
+
padding-left: 0.6rem;
|
|
34
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="20 0 120 190" width="120" height="190" role="img" aria-label="Op den Kamp IT Solutions">
|
|
2
|
+
<circle cx="70" cy="50" r="30" fill="none" stroke="#0588c4" stroke-width="26"/>
|
|
3
|
+
<g fill="#5aafc0">
|
|
4
|
+
<rect x="27" y="117" width="16" height="66"/>
|
|
5
|
+
<rect x="57" y="117" width="76" height="16"/>
|
|
6
|
+
<rect x="87" y="117" width="16" height="66"/>
|
|
7
|
+
</g>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="20 0 120 190" width="120" height="190" role="img" aria-label="Op den Kamp IT Solutions">
|
|
2
|
+
<circle cx="70" cy="50" r="30" fill="none" stroke="#0588c4" stroke-width="26"/>
|
|
3
|
+
<g fill="#117a8b">
|
|
4
|
+
<rect x="27" y="117" width="16" height="66"/>
|
|
5
|
+
<rect x="57" y="117" width="76" height="16"/>
|
|
6
|
+
<rect x="87" y="117" width="16" height="66"/>
|
|
7
|
+
</g>
|
|
8
|
+
</svg>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# API reference
|
|
2
|
+
|
|
3
|
+
Generated from the docstrings in the package. The guides cover how these fit
|
|
4
|
+
together; this page is the exhaustive list.
|
|
5
|
+
|
|
6
|
+
Autodoc emits reStructuredText, which the markdown parser does not read, so
|
|
7
|
+
every directive below sits in an `eval-rst` block. Without it the page renders
|
|
8
|
+
the directive source as prose and the build still succeeds.
|
|
9
|
+
|
|
10
|
+
```{eval-rst}
|
|
11
|
+
.. automodule:: mx_remote
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Client
|
|
15
|
+
|
|
16
|
+
`Remote` owns the socket, the device registry and the background probe that
|
|
17
|
+
keeps both in step with the mesh.
|
|
18
|
+
|
|
19
|
+
```{eval-rst}
|
|
20
|
+
.. automodule:: mx_remote.remote.Remote
|
|
21
|
+
:members:
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Devices, bays and callbacks
|
|
25
|
+
|
|
26
|
+
A client works against these types rather than the classes implementing them:
|
|
27
|
+
they are what the registry hands out and what a callback receives.
|
|
28
|
+
|
|
29
|
+
```{eval-rst}
|
|
30
|
+
.. automodule:: mx_remote.Interface
|
|
31
|
+
:members:
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Device identity
|
|
35
|
+
|
|
36
|
+
```{eval-rst}
|
|
37
|
+
.. automodule:: mx_remote.Uid
|
|
38
|
+
:members:
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Remote-control keys and actions
|
|
42
|
+
|
|
43
|
+
The key and action codes a bay reports, and the target a key was aimed at.
|
|
44
|
+
|
|
45
|
+
```{eval-rst}
|
|
46
|
+
.. autoclass:: mx_remote.proto.Constants.RCKey
|
|
47
|
+
:members:
|
|
48
|
+
:undoc-members:
|
|
49
|
+
|
|
50
|
+
.. autoclass:: mx_remote.proto.Constants.RCAction
|
|
51
|
+
:members:
|
|
52
|
+
:undoc-members:
|
|
53
|
+
|
|
54
|
+
.. autoclass:: mx_remote.proto.Constants.RCType
|
|
55
|
+
:members:
|
|
56
|
+
:undoc-members:
|
|
57
|
+
|
|
58
|
+
.. autodata:: mx_remote.proto.Constants.MXR_PROTOCOL_VERSION
|
|
59
|
+
```
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
######################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp-it.nl) ##
|
|
5
|
+
## copyright (c) 2021-2026 Op den Kamp IT Solutions ##
|
|
6
|
+
######################################################
|
|
7
|
+
'''Sphinx configuration for the mx_remote documentation site.
|
|
8
|
+
|
|
9
|
+
The guides in this directory are the prose; the API reference is generated from
|
|
10
|
+
the package's own docstrings, so `mx_remote` must be importable when this runs.
|
|
11
|
+
The workflow installs it rather than mocking it: a reference built against
|
|
12
|
+
mocks documents the mocks on the day an import breaks.
|
|
13
|
+
'''
|
|
14
|
+
|
|
15
|
+
import pathlib
|
|
16
|
+
import re
|
|
17
|
+
|
|
18
|
+
_ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
19
|
+
|
|
20
|
+
project = 'mx_remote'
|
|
21
|
+
author = 'Lars Op den Kamp'
|
|
22
|
+
copyright = '2021-2026 Op den Kamp IT Solutions'
|
|
23
|
+
|
|
24
|
+
# Read from the package the same way pyproject.toml does, so the site cannot
|
|
25
|
+
# report a version the package does not carry.
|
|
26
|
+
release = re.search(r"VERSION = '([^']+)'",
|
|
27
|
+
(_ROOT / 'mx_remote' / 'const.py').read_text(encoding='utf-8')).group(1)
|
|
28
|
+
version = release
|
|
29
|
+
|
|
30
|
+
extensions = [
|
|
31
|
+
'myst_parser', # the guides are markdown, and stay readable on GitHub
|
|
32
|
+
'sphinx.ext.autodoc',
|
|
33
|
+
'sphinx.ext.napoleon',
|
|
34
|
+
'sphinx.ext.viewcode',
|
|
35
|
+
'sphinx.ext.intersphinx',
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# Anchors for the '#section' links the guides use between each other.
|
|
39
|
+
myst_heading_anchors = 3
|
|
40
|
+
|
|
41
|
+
exclude_patterns = ['_build', 'requirements.txt']
|
|
42
|
+
|
|
43
|
+
autodoc_default_options = {
|
|
44
|
+
'members': True,
|
|
45
|
+
'show-inheritance': True,
|
|
46
|
+
'member-order': 'bysource',
|
|
47
|
+
}
|
|
48
|
+
# A member with no docstring says nothing a reader cannot get from the
|
|
49
|
+
# signature, and there are enough of them to bury the ones that do.
|
|
50
|
+
autodoc_typehints = 'description'
|
|
51
|
+
autodoc_class_signature = 'separated'
|
|
52
|
+
|
|
53
|
+
intersphinx_mapping = {'python': ('https://docs.python.org/3', None)}
|
|
54
|
+
|
|
55
|
+
html_theme = 'furo'
|
|
56
|
+
html_title = f'mx_remote {release}'
|
|
57
|
+
html_static_path = ['_static']
|
|
58
|
+
html_css_files = ['brand.css']
|
|
59
|
+
# The ring alone, because the full mark is portrait and would letterbox down
|
|
60
|
+
# to a few unreadable pixels in a tab.
|
|
61
|
+
html_favicon = '_static/favicon.svg'
|
|
62
|
+
|
|
63
|
+
# The palette is the one opdenkamp-it.nl uses, so the site and the docs read as
|
|
64
|
+
# one place: slate text, teal accents, and a near-black teal ground when dark.
|
|
65
|
+
# Furo derives the rest of its colours from these, and applies the light set to
|
|
66
|
+
# both themes, so the dark set only names what differs.
|
|
67
|
+
html_theme_options = {
|
|
68
|
+
'light_logo': 'logo.svg',
|
|
69
|
+
'dark_logo': 'logo-dark.svg',
|
|
70
|
+
'light_css_variables': {
|
|
71
|
+
'font-stack': '"Inter Variable", Inter, system-ui, -apple-system, sans-serif',
|
|
72
|
+
'font-stack--monospace': '"JetBrains Mono Variable", "JetBrains Mono", ui-monospace, monospace',
|
|
73
|
+
'color-brand-primary': '#0f4c5c',
|
|
74
|
+
'color-brand-content': '#117a8b',
|
|
75
|
+
'color-brand-visited': '#0f4c5c',
|
|
76
|
+
'color-foreground-primary': '#0f172a',
|
|
77
|
+
'color-foreground-secondary': '#475569',
|
|
78
|
+
'color-foreground-muted': '#64748b',
|
|
79
|
+
'color-foreground-border': '#e2e8f0',
|
|
80
|
+
'color-background-primary': '#ffffff',
|
|
81
|
+
'color-background-secondary': '#f8fafc',
|
|
82
|
+
'color-background-border': '#e2e8f0',
|
|
83
|
+
'color-inline-code-background': '#f1f5f9',
|
|
84
|
+
'color-api-name': '#0f4c5c',
|
|
85
|
+
'color-api-pre-name': '#117a8b',
|
|
86
|
+
},
|
|
87
|
+
'dark_css_variables': {
|
|
88
|
+
'color-brand-primary': '#5aafc0',
|
|
89
|
+
'color-brand-content': '#5aafc0',
|
|
90
|
+
'color-brand-visited': '#8fc9d5',
|
|
91
|
+
'color-foreground-primary': '#e2e8f0',
|
|
92
|
+
'color-foreground-secondary': '#94a3b8',
|
|
93
|
+
'color-foreground-muted': '#7d8ea0',
|
|
94
|
+
'color-foreground-border': '#1c414d',
|
|
95
|
+
'color-background-primary': '#0a1f26',
|
|
96
|
+
'color-background-secondary': '#0d2b34',
|
|
97
|
+
'color-background-border': '#1c414d',
|
|
98
|
+
'color-inline-code-background': '#12333d',
|
|
99
|
+
'color-api-name': '#5aafc0',
|
|
100
|
+
'color-api-pre-name': '#8fc9d5',
|
|
101
|
+
},
|
|
102
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# mx_remote
|
|
2
|
+
|
|
3
|
+
Python 3 library for discovering and controlling [Pulse-Eight](https://www.pulse-eight.com/)
|
|
4
|
+
AV distribution hardware — neo matrices, OneIP HDMI-over-IP units, multiviewers and
|
|
5
|
+
ProAmp8 amplifiers — over the MX Remote protocol they share.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pip install mx_remote
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
import mx_remote
|
|
13
|
+
|
|
14
|
+
mx = mx_remote.Remote()
|
|
15
|
+
await mx.start_async()
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The guides below cover the library task by task. The [API reference](api.md) is
|
|
19
|
+
generated from the package's docstrings and lists everything.
|
|
20
|
+
|
|
21
|
+
```{toctree}
|
|
22
|
+
:maxdepth: 2
|
|
23
|
+
|
|
24
|
+
README
|
|
25
|
+
configuration
|
|
26
|
+
devices-and-bays
|
|
27
|
+
routing
|
|
28
|
+
callbacks
|
|
29
|
+
audio
|
|
30
|
+
oneip
|
|
31
|
+
multiviewer
|
|
32
|
+
diagnostics
|
|
33
|
+
api
|
|
34
|
+
```
|
|
@@ -1394,7 +1394,7 @@ def v2ip_stream_valid(stream:'V2IPStreamSource|None') -> bool:
|
|
|
1394
1394
|
"""
|
|
1395
1395
|
True when a stream source carries an address a frame actually meant to send.
|
|
1396
1396
|
|
|
1397
|
-
Mirrors
|
|
1397
|
+
Mirrors the firmware's own test: a multicast ip and a non-zero port. A
|
|
1398
1398
|
controller write that offers no addresses leaves the block zeroed, which
|
|
1399
1399
|
fails both tests.
|
|
1400
1400
|
"""
|
|
@@ -1409,7 +1409,7 @@ def v2ip_av_source_valid(video:'V2IPStreamSource|None', anc:'V2IPStreamSource|No
|
|
|
1409
1409
|
"""
|
|
1410
1410
|
True when an av source block carries addresses a frame meant to send.
|
|
1411
1411
|
|
|
1412
|
-
Mirrors
|
|
1412
|
+
Mirrors the firmware's own test: video and anc must both be valid, audio
|
|
1413
1413
|
is optional and is carried along with them.
|
|
1414
1414
|
"""
|
|
1415
1415
|
return v2ip_stream_valid(video) and v2ip_stream_valid(anc)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
######################################################
|
|
2
|
+
## MX Remote Python Interface ##
|
|
3
|
+
## ##
|
|
4
|
+
## author: Lars Op den Kamp (lars@opdenkamp-it.nl) ##
|
|
5
|
+
## copyright (c) 2021-2026 Op den Kamp IT Solutions ##
|
|
6
|
+
######################################################
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
Names that live in different modules across the Python versions this package
|
|
10
|
+
supports. Import them from here so the version test is written once.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
if sys.version_info >= (3, 12):
|
|
16
|
+
from typing import override
|
|
17
|
+
else:
|
|
18
|
+
# typing.override arrived in 3.12; typing_extensions carries the same
|
|
19
|
+
# decorator further back, and is a dependency only below that version.
|
|
20
|
+
from typing_extensions import override
|
|
21
|
+
|
|
22
|
+
__all__ = ['override']
|
|
@@ -113,7 +113,7 @@ MXR_DEVICE_NAME_LEN = 16
|
|
|
113
113
|
|
|
114
114
|
The field carries no terminator of its own when the value fills it, so read
|
|
115
115
|
exactly this many bytes and only then cut at a NUL - scanning on runs into the
|
|
116
|
-
neighbouring struct member
|
|
116
|
+
neighbouring struct member."""
|
|
117
117
|
|
|
118
118
|
MXR_FW_VERSION_LEN = 128
|
|
119
119
|
"""Width of the fixed-size firmware version name field (mxr_fw_version.name)."""
|
|
@@ -341,8 +341,8 @@ class BayStatusMask(IntFlag):
|
|
|
341
341
|
return "none"
|
|
342
342
|
|
|
343
343
|
|
|
344
|
-
# Bay status bit-field accessors (not flag bits)
|
|
345
|
-
# and MXR_BAY_STATUS_HDCP_STATUS
|
|
344
|
+
# Bay status bit-field accessors (not flag bits), mirroring the firmware's
|
|
345
|
+
# MXR_BAY_STATUS_RC_TYPE and MXR_BAY_STATUS_HDCP_STATUS.
|
|
346
346
|
BAY_STATUS_RC_TYPE_SHIFT = 16
|
|
347
347
|
BAY_STATUS_RC_TYPE_MASK = (0xF << BAY_STATUS_RC_TYPE_SHIFT)
|
|
348
348
|
BAY_STATUS_HDCP_STATUS_SHIFT = 22
|
|
@@ -369,7 +369,7 @@ _MXR_SIG_BPP_VALUES: dict[int, int] = {
|
|
|
369
369
|
def mxr_sig_bpp_get(bpp: int) -> int:
|
|
370
370
|
'''Map an mxr_signal_type bpp *index* to the bit depth it stands for.
|
|
371
371
|
|
|
372
|
-
|
|
372
|
+
The field is an index, not a value:
|
|
373
373
|
1=8, 2=10, 3=12, 4=16, 0=unknown and 5 the unset sentinel - reading it as a
|
|
374
374
|
bit depth is a common trap. Returns 0 for both unknown and unset.'''
|
|
375
375
|
return _MXR_SIG_BPP_VALUES.get(bpp, MXR_SIG_BPP_UNKNOWN)
|
|
@@ -441,8 +441,7 @@ class MxrSignalType:
|
|
|
441
441
|
class VideoWallOperation(IntEnum):
|
|
442
442
|
'''What a V2IP_VIDEOWALL frame asks the addressed sink to do with its window.
|
|
443
443
|
|
|
444
|
-
Mirrors
|
|
445
|
-
(github.com/opdenkamp/mod-v2ip-videowall, src/v2ip_videowall.h).'''
|
|
444
|
+
Mirrors vw_mesh_op in the v2ipwall module, which owns this opcode.'''
|
|
446
445
|
PREVIEW = 0
|
|
447
446
|
'''Show the window without storing it, so it dies on reboot or revert.'''
|
|
448
447
|
|
|
@@ -17,7 +17,7 @@ class FrameBayConfig(FrameBase):
|
|
|
17
17
|
'''Bay configuration for the bays a remote device advertises.
|
|
18
18
|
|
|
19
19
|
A device pages its bays across several of these frames: firmware sizes each
|
|
20
|
-
page against
|
|
20
|
+
page against its own maximum payload length and shrinks it further on OOM, so the
|
|
21
21
|
record count varies from frame to frame and no single frame holds the whole
|
|
22
22
|
list. Each page is a valid stand-alone frame with the same per-record
|
|
23
23
|
format - the receiver merges records into its cache and must never treat
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
'''Protocol frame for SYS_FACTORY_RESET (opcode 0x3A).
|
|
8
8
|
|
|
9
9
|
A mesh controller or management app broadcasts this to factory-reset peers.
|
|
10
|
-
Payload variants
|
|
10
|
+
Payload variants the firmware accepts:
|
|
11
11
|
empty controller-only target (just the sender)
|
|
12
12
|
1 byte = 0xFF broadcast to all peers (controller/management only)
|
|
13
13
|
16 bytes single target uid
|
|
@@ -15,7 +15,7 @@ class FrameLinks(FrameBase):
|
|
|
15
15
|
'''Configured links for the device that sent this frame.
|
|
16
16
|
|
|
17
17
|
Paged the same way as the bay config: a device with many bays splits its
|
|
18
|
-
links across several frames sized against
|
|
18
|
+
links across several frames sized against its own maximum payload, so the
|
|
19
19
|
record count varies per frame and the receiver merges records into its
|
|
20
20
|
cache rather than replacing it.'''
|
|
21
21
|
@cached_property
|
|
@@ -10,7 +10,7 @@ from enum import IntEnum
|
|
|
10
10
|
from functools import cached_property
|
|
11
11
|
import logging
|
|
12
12
|
import struct
|
|
13
|
-
from
|
|
13
|
+
from ..compat import override
|
|
14
14
|
|
|
15
15
|
from .FrameHeader import FrameHeader
|
|
16
16
|
from .FrameBase import FrameBase
|
|
@@ -646,7 +646,7 @@ def _pack_audio_entry_route_in(ep_id: int, supported: int, active: int) -> bytes
|
|
|
646
646
|
def pack_audio_features(dev: AudioDeviceData) -> bytes:
|
|
647
647
|
'''Pack the mxr_v2ip_audio_dev FEATURES body.
|
|
648
648
|
|
|
649
|
-
Entry order matches
|
|
649
|
+
Entry order matches the audio module's own serialiser:
|
|
650
650
|
optional device control address first, then for each endpoint the
|
|
651
651
|
ENDPOINT entry, optional ADDRESS, optional PARENT, optional ROUTE_IN.
|
|
652
652
|
'''
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
from ..Interface import AudioChangeSource as AudioChangeSource, AudioEndpoint as AudioEndpoint, AudioEndpoints as AudioEndpoints, AudioFeatures as AudioFeatures, AudioLink as AudioLink, AudioLinks as AudioLinks, DeviceRegistry as DeviceRegistry, MxrDeviceUid as MxrDeviceUid, V2IPStreamSource as V2IPStreamSource
|
|
2
|
+
from ..compat import override as override
|
|
2
3
|
from .FrameBase import FrameBase as FrameBase
|
|
3
4
|
from .FrameHeader import FrameHeader as FrameHeader
|
|
4
5
|
from _typeshed import Incomplete
|
|
5
6
|
from enum import IntEnum
|
|
6
7
|
from functools import cached_property
|
|
7
|
-
from typing import override
|
|
8
8
|
|
|
9
9
|
V2IP_AUDIO_EP_ID_NONE: int
|
|
10
10
|
|