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