mx-remote 9.1.0__tar.gz → 9.3.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.1.0 → mx_remote-9.3.0}/PKG-INFO +1 -1
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/api.md +17 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/callbacks.md +25 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/oneip.md +22 -13
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/Interface.py +102 -15
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/Interface.pyi +22 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/const.py +1 -1
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/Constants.py +23 -1
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/Constants.pyi +5 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/Factory.py +3 -0
- mx_remote-9.3.0/mx_remote/proto/FrameThirdParty.py +50 -0
- mx_remote-9.3.0/mx_remote/proto/FrameThirdParty.pyi +15 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/Device.py +22 -8
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/Device.pyi +1 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/Remote.py +26 -2
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/Remote.pyi +5 -2
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/handlers.py +1 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/run.py +1 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/smoke.py +1 -0
- mx_remote-9.3.0/tests/thirdparty.py +146 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/v2ipsettings.py +41 -1
- {mx_remote-9.1.0 → mx_remote-9.3.0}/.gitignore +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/AGENTS.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/CLAUDE.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/LICENSE +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/README.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/README.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/_static/brand.css +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/_static/favicon.svg +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/_static/logo-dark.svg +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/_static/logo.svg +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/audio.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/conf.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/configuration.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/devices-and-bays.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/diagnostics.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/index.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/multiviewer.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/requirements.txt +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/docs/routing.md +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/hatch_build.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/Uid.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/Uid.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/__init__.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/__init__.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/compat.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/compat.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/const.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/deprecated.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/deprecated.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/main.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/main.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/BayConfig.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/BayConfig.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/Data.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/Data.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/Factory.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameAmpDolbySettings.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameAmpDolbySettings.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameAmpZoneSettings.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameAmpZoneSettings.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameAudioClip.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameAudioClip.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameAudioSetRoute.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameAudioSetRoute.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBase.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBase.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBayConfig.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBayConfig.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBayConfigSecondary.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBayConfigSecondary.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBayContent.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBayContent.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBayHide.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBayHide.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBayStatus.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameBayStatus.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameConnectStatus.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameConnectStatus.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameDebug.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameDebug.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameDiscover.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameDiscover.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameEDID.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameEDID.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameEDIDProfile.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameEDIDProfile.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameFactoryReset.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameFactoryReset.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameFilterStatus.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameFilterStatus.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameFirmwareVersion.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameFirmwareVersion.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameHeader.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameHeader.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameHello.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameHello.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameLinks.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameLinks.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameMeshOperation.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameMeshOperation.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameMirrorStatus.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameMirrorStatus.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameMonitoringPulse.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameMonitoringPulse.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameNetworkStatus.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameNetworkStatus.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FramePing.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FramePing.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FramePowerChange.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FramePowerChange.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameRCAction.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameRCAction.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameRCIr.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameRCIr.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameRCKey.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameRCKey.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameRCSettings.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameRCSettings.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameReboot.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameReboot.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameRoutingChange.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameRoutingChange.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSetInstaller.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSetInstaller.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSetName.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSetName.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSetRoute.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSetRoute.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSetupStatus.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSetupStatus.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSignalStatus.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSignalStatus.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSignalStatusNew.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSignalStatusNew.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSysTemperature.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSysTemperature.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSystemStatus.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameSystemStatus.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTXRCAction.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTXRCAction.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTXRCKey.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTXRCKey.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTime.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTime.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTimeZone.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTimeZone.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTopology.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTopology.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTxIR.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameTxIR.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameUnitCommand.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameUnitCommand.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameUnitStatus.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameUnitStatus.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameUpgradeFPGA.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameUpgradeFPGA.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPAudio.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPAudio.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPBayMapping.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPBayMapping.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPDetectBays.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPDetectBays.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPDeviceConfiguration.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPDeviceConfiguration.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPLink.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPLink.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPManualSourceSwitch.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPManualSourceSwitch.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPMultiviewer.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPMultiviewer.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPOsdSync.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPOsdSync.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPPowerSave.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPPowerSave.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPPrivacy.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPPrivacy.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPSetMaster.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPSetMaster.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPSettingsAll.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPSettingsAll.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPSinkSdp.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPSinkSdp.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPSourceSwitch.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPSourceSwitch.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPSources.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPSources.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPStats.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPStats.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPTestcard.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPTestcard.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPTiling.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPTiling.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPVideoWall.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameV2IPVideoWall.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameVolume.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameVolume.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameVolumeDown.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameVolumeDown.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameVolumeSet.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameVolumeSet.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameVolumeUp.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/FrameVolumeUp.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/LinkConfig.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/LinkConfig.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/Multiviewer.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/Multiviewer.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/Svd.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/Svd.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/V2IPConfig.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/V2IPConfig.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/V2IPStats.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/V2IPStats.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/__init__.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/__init__.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/proto/svd.csv +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/py.typed +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/Bay.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/Bay.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/ConnectionAsync.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/ConnectionAsync.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/Link.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/Link.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/OsdSync.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/OsdSync.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/State.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/State.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/V2IP.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/mx_remote/remote/V2IP.pyi +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/pyproject.toml +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/audio_ir.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/audiolock.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/baycontent.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/capture.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/complete.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/deprecated.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/downmix.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/e2e.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/entry.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/handlers_cover.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/hello.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/idempotent.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/lengths.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/merge.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/mesh.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/meshbays.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/newops.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/offline.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/offsets.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/osdmessage.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/osdsync.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/ping.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/privacy.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/proto_stamp.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/protogate.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/rcsettings.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/roundtrip.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/routes.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/sources.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/staterequest.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/stats.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/structs.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/txresult.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/uid.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/unit.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/unknowns.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/v2ipptp.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/v2ipscaling.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/v2ipsdp.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/v2ipsubject.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/v2iptestcard.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/v2ipvlan.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/vdetails.py +0 -0
- {mx_remote-9.1.0 → mx_remote-9.3.0}/tests/wirefix.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mx_remote
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.3.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/
|
|
@@ -288,6 +288,23 @@ How a source unit encrypts its streams, as it reports it.
|
|
|
288
288
|
:members:
|
|
289
289
|
```
|
|
290
290
|
|
|
291
|
+
## Application messages
|
|
292
|
+
|
|
293
|
+
Messages applications exchange over the mesh under their own vendor id.
|
|
294
|
+
|
|
295
|
+
```{eval-rst}
|
|
296
|
+
.. autoclass:: mx_remote.ThirdPartyMessage
|
|
297
|
+
:members:
|
|
298
|
+
|
|
299
|
+
.. autoclass:: mx_remote.ThirdPartyVendor
|
|
300
|
+
:members:
|
|
301
|
+
|
|
302
|
+
.. autoclass:: mx_remote.proto.Constants.ThirdPartyVendorFlag
|
|
303
|
+
:members:
|
|
304
|
+
|
|
305
|
+
.. autodata:: mx_remote.proto.Constants.THIRD_PARTY_PAYLOAD_MAX
|
|
306
|
+
```
|
|
307
|
+
|
|
291
308
|
## OneIP test card
|
|
292
309
|
|
|
293
310
|
A sink's test pattern, tone and lip-sync flash, and the ranges it accepts.
|
|
@@ -67,6 +67,7 @@ Available callback methods:
|
|
|
67
67
|
| `on_rc_type_changed` | remote control type changed |
|
|
68
68
|
| `on_amp_zone_settings_changed` | amplifier zone settings changed |
|
|
69
69
|
| `on_amp_dolby_settings_changed` | amplifier Dolby settings changed |
|
|
70
|
+
| `on_third_party_message` | another application sent a message under its vendor id |
|
|
70
71
|
|
|
71
72
|
You can also register per-device and per-bay callbacks:
|
|
72
73
|
|
|
@@ -85,6 +86,30 @@ device.unregister_callback(on_device_changed)
|
|
|
85
86
|
bay.unregister_callback(on_bay_changed)
|
|
86
87
|
```
|
|
87
88
|
|
|
89
|
+
## Application messages
|
|
90
|
+
|
|
91
|
+
Applications built on this library can message each other over the mesh. A
|
|
92
|
+
message carries a vendor id, then up to `THIRD_PARTY_PAYLOAD_MAX` (1444) bytes
|
|
93
|
+
that only the application reads. The vendor id is the vendor's IEEE OUI, or an
|
|
94
|
+
identifier it chose, such as three letters:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from mx_remote import ThirdPartyVendor
|
|
98
|
+
|
|
99
|
+
ODK = ThirdPartyVendor.private(b'ODK')
|
|
100
|
+
|
|
101
|
+
await mx.send_third_party(ODK, b'hello') # False when over 1444 bytes
|
|
102
|
+
|
|
103
|
+
class MyCallbacks(mx_remote.MxrCallbacks):
|
|
104
|
+
def on_third_party_message(self, dev, message):
|
|
105
|
+
if message.vendor.same_vendor(ODK):
|
|
106
|
+
print(f"{dev} says {message.payload!r}")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Every other client that has heard this one announce itself receives the
|
|
110
|
+
message; this client does not. No unit reads it, and nothing acknowledges it,
|
|
111
|
+
so an application that needs to know a message arrived has its peers answer.
|
|
112
|
+
|
|
88
113
|
---
|
|
89
114
|
|
|
90
115
|
[Documentation index](README.md) | [Project README](https://github.com/opdenkamp/mx-remote#readme)
|
|
@@ -225,27 +225,36 @@ goes out at 176 bytes: a receiver with the schedule ignores the settings of a
|
|
|
225
225
|
shorter frame, and one whose settings end at the idle minutes takes it all the
|
|
226
226
|
same.
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
228
|
+
Power save is switched on or off. Switched on, a device powers down by itself
|
|
229
|
+
to its power save level: reduced, low or off, each powering more down than the
|
|
230
|
+
one before. The device picks the level itself, the one that saves the most
|
|
231
|
+
while its input and display can still wake it, and reports it. Its unit status
|
|
232
|
+
says which levels it supports and what wakes it at each.
|
|
231
233
|
|
|
232
234
|
```python
|
|
233
235
|
from mx_remote import OneIPPowerSaveLevel, UnitPowerSave
|
|
234
236
|
|
|
235
|
-
print(settings.
|
|
237
|
+
print(settings.get(OneIPDeviceSetting.POWER_SAVE_DISABLED)) # False: switched on
|
|
238
|
+
print(settings.power_save_level) # the level it picked
|
|
236
239
|
print(device.unit_status.power_save_level(OneIPPowerSaveLevel.LOW)) # e.g. SUPPORTED|INPUT_WAKE
|
|
237
240
|
|
|
238
|
-
await device.
|
|
241
|
+
await device.set_oneip_power_save(True)
|
|
242
|
+
await mx.set_all_oneip_power_save(False) # every OneIP device of the mesh
|
|
239
243
|
```
|
|
240
244
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
Switched off, `OneIPDeviceSetting.POWER_SAVE_DISABLED` is on: the device does
|
|
246
|
+
not power down by itself, and a power save request still powers it down.
|
|
247
|
+
Switching on is written as a mesh controller writes it, with that switch clear
|
|
248
|
+
and the power save level at 0, so a device whose firmware still takes the level
|
|
249
|
+
as its setting returns to its own default.
|
|
250
|
+
|
|
251
|
+
`set_oneip_power_save_level()` writes a level, which switches power save on. A
|
|
252
|
+
current device ignores the level; one whose firmware predates picking it takes
|
|
253
|
+
it. A written level is never cached, from this client or another controller:
|
|
254
|
+
`power_save_level` is what the device reports. A level write without the switch
|
|
255
|
+
only clears `POWER_SAVE_DISABLED` on a device that reports it. A level this
|
|
256
|
+
library does not name is refused before it is sent. Firmware without the switch
|
|
257
|
+
never reports it, and switching is refused there.
|
|
249
258
|
|
|
250
259
|
`OneIPDeviceSetting.CLOUD` is a switch for whether a device connects to the
|
|
251
260
|
cloud service. A device that does not report it connects regardless, and the
|
|
@@ -2601,6 +2601,68 @@ class OneIPPrivacy:
|
|
|
2601
2601
|
def __str__(self) -> str:
|
|
2602
2602
|
return f"video {self.video}, audio {self.audio}, anc {self.anc}"
|
|
2603
2603
|
|
|
2604
|
+
@dataclass(frozen=True)
|
|
2605
|
+
class ThirdPartyVendor:
|
|
2606
|
+
"""
|
|
2607
|
+
Whose message a third-party message is: a flag byte, then three bytes
|
|
2608
|
+
naming the vendor.
|
|
2609
|
+
|
|
2610
|
+
The three bytes are the vendor's IEEE OUI, in the order it is written, or
|
|
2611
|
+
with ThirdPartyVendorFlag.NOT_OUI set an identifier of the vendor's own
|
|
2612
|
+
choosing, such as three ASCII letters. The flag keeps the two spaces apart,
|
|
2613
|
+
so a chosen identifier never collides with a registered OUI.
|
|
2614
|
+
"""
|
|
2615
|
+
flags: ThirdPartyVendorFlag
|
|
2616
|
+
"""The flag byte, at offset 0."""
|
|
2617
|
+
id: bytes
|
|
2618
|
+
"""The vendor's three identifying bytes, at offsets 1 to 3."""
|
|
2619
|
+
|
|
2620
|
+
def __post_init__(self) -> None:
|
|
2621
|
+
if (len(self.id) != 3):
|
|
2622
|
+
raise ValueError(f"a vendor id is 3 bytes, not {len(self.id)}")
|
|
2623
|
+
|
|
2624
|
+
@classmethod
|
|
2625
|
+
def oui(cls, oui:bytes) -> 'ThirdPartyVendor':
|
|
2626
|
+
"""A vendor named by its IEEE OUI."""
|
|
2627
|
+
return cls(flags=ThirdPartyVendorFlag(0), id=bytes(oui))
|
|
2628
|
+
|
|
2629
|
+
@classmethod
|
|
2630
|
+
def private(cls, id:bytes) -> 'ThirdPartyVendor':
|
|
2631
|
+
"""A vendor named by an identifier it chose, rather than an OUI."""
|
|
2632
|
+
return cls(flags=ThirdPartyVendorFlag.NOT_OUI, id=bytes(id))
|
|
2633
|
+
|
|
2634
|
+
def same_vendor(self, other:'ThirdPartyVendor') -> bool:
|
|
2635
|
+
"""Whether other names the same vendor.
|
|
2636
|
+
|
|
2637
|
+
Only ThirdPartyVendorFlag.NOT_OUI and the three identifying bytes say
|
|
2638
|
+
who the vendor is, so the other flag bits are left out."""
|
|
2639
|
+
return ((ThirdPartyVendorFlag.NOT_OUI in self.flags) == (ThirdPartyVendorFlag.NOT_OUI in other.flags)) \
|
|
2640
|
+
and (self.id == other.id)
|
|
2641
|
+
|
|
2642
|
+
def __str__(self) -> str:
|
|
2643
|
+
if (ThirdPartyVendorFlag.NOT_OUI in self.flags):
|
|
2644
|
+
return f"vendor {self.id!r}"
|
|
2645
|
+
return f"vendor OUI {self.id.hex(':')}"
|
|
2646
|
+
|
|
2647
|
+
@dataclass(frozen=True)
|
|
2648
|
+
class ThirdPartyMessage:
|
|
2649
|
+
"""
|
|
2650
|
+
A message an application sent to every device, under its own vendor id.
|
|
2651
|
+
|
|
2652
|
+
The library reads only the vendor id. What follows is the application's,
|
|
2653
|
+
and arrives exactly as it was sent.
|
|
2654
|
+
"""
|
|
2655
|
+
vendor: ThirdPartyVendor
|
|
2656
|
+
"""Which application's message this is. The library checks none: each
|
|
2657
|
+
application ignores every message whose vendor is not its own."""
|
|
2658
|
+
payload: bytes
|
|
2659
|
+
"""The application's bytes, at most THIRD_PARTY_PAYLOAD_MAX."""
|
|
2660
|
+
address: str|None
|
|
2661
|
+
"""Where the datagram came from."""
|
|
2662
|
+
|
|
2663
|
+
def __str__(self) -> str:
|
|
2664
|
+
return f"{self.vendor}: {len(self.payload)} bytes"
|
|
2665
|
+
|
|
2604
2666
|
@dataclass(frozen=True)
|
|
2605
2667
|
class OneIPTestTone:
|
|
2606
2668
|
"""A OneIP sink's test tone."""
|
|
@@ -2712,6 +2774,16 @@ class OneIPDeviceSettings:
|
|
|
2712
2774
|
self._power_save_level = int(power_save_level)
|
|
2713
2775
|
self._ptp_messages = ptp_messages if (ptp_messages is not None) else OneIPPtpMessages()
|
|
2714
2776
|
|
|
2777
|
+
@classmethod
|
|
2778
|
+
def power_save_switch(cls, enabled:bool) -> 'OneIPDeviceSettings':
|
|
2779
|
+
"""The settings that switch a device's power save on or off. On is both
|
|
2780
|
+
POWER_SAVE_DISABLED, clear, and POWER_SAVE_LEVEL, at 0."""
|
|
2781
|
+
disabled = OneIPDeviceSetting.POWER_SAVE_DISABLED
|
|
2782
|
+
if enabled:
|
|
2783
|
+
return cls(valid=(disabled | OneIPDeviceSetting.POWER_SAVE_LEVEL),
|
|
2784
|
+
power_save_level=OneIPPowerSaveLevel.NONE)
|
|
2785
|
+
return cls(valid=disabled, flags=disabled)
|
|
2786
|
+
|
|
2715
2787
|
@property
|
|
2716
2788
|
def valid(self) -> OneIPDeviceSetting:
|
|
2717
2789
|
"""The settings reported so far."""
|
|
@@ -2791,8 +2863,9 @@ class OneIPDeviceSettings:
|
|
|
2791
2863
|
|
|
2792
2864
|
@property
|
|
2793
2865
|
def power_save_level(self) -> OneIPPowerSaveLevel|int|None:
|
|
2794
|
-
"""The level the device powers down to in power save,
|
|
2795
|
-
this library does not name, None while it is
|
|
2866
|
+
"""The level the device powers down to in power save, which it picks
|
|
2867
|
+
itself, a raw int for one this library does not name, None while it is
|
|
2868
|
+
not reported."""
|
|
2796
2869
|
if (OneIPDeviceSetting.POWER_SAVE_LEVEL not in self._valid):
|
|
2797
2870
|
return None
|
|
2798
2871
|
return _wire_enum(OneIPPowerSaveLevel, self._power_save_level)
|
|
@@ -2812,18 +2885,14 @@ class OneIPDeviceSettings:
|
|
|
2812
2885
|
|
|
2813
2886
|
Each bit in this block's ``valid`` replaces its own setting and leaves
|
|
2814
2887
|
the others alone, so a write about one setting does not clear what was
|
|
2815
|
-
known about the rest.
|
|
2816
|
-
takes a power save level as switching it off.
|
|
2888
|
+
known about the rest.
|
|
2817
2889
|
"""
|
|
2818
2890
|
if (previous is None):
|
|
2819
2891
|
previous = OneIPDeviceSettings()
|
|
2820
2892
|
valid = self._valid
|
|
2821
|
-
flags = (previous._flags & ~valid) | (self._flags & valid)
|
|
2822
|
-
if (OneIPDeviceSetting.POWER_SAVE_LEVEL in valid) and (OneIPDeviceSetting.POWER_SAVE_DISABLED not in valid):
|
|
2823
|
-
flags &= ~OneIPDeviceSetting.POWER_SAVE_DISABLED
|
|
2824
2893
|
return OneIPDeviceSettings(
|
|
2825
2894
|
valid=(previous._valid | valid),
|
|
2826
|
-
flags=
|
|
2895
|
+
flags=((previous._flags & ~valid) | (self._flags & valid)),
|
|
2827
2896
|
ir_profiles=(self._ir_profiles if (OneIPDeviceSetting.IR_PROFILES in valid)
|
|
2828
2897
|
else previous._ir_profiles),
|
|
2829
2898
|
ir_profile=(self._ir_profile if (OneIPDeviceSetting.IR_PROFILE in valid)
|
|
@@ -2850,9 +2919,11 @@ class OneIPDeviceSettings:
|
|
|
2850
2919
|
write about it.
|
|
2851
2920
|
|
|
2852
2921
|
A device applies a setting only if it has it, a profile, PTP mode, PTP
|
|
2853
|
-
domain
|
|
2854
|
-
|
|
2855
|
-
|
|
2922
|
+
domain or PTP DSCP only within its range, a stream TTL only when it is
|
|
2923
|
+
not 0, a rotation only by its own step, and never what only it can
|
|
2924
|
+
report about itself. The step stays the device's. It picks its power
|
|
2925
|
+
save level itself, and takes a level it is written, without
|
|
2926
|
+
POWER_SAVE_DISABLED, as switching power save on.
|
|
2856
2927
|
|
|
2857
2928
|
reported may be only the bits the device reported, which says nothing
|
|
2858
2929
|
of its step, so a rotation other than 0 is then left out.
|
|
@@ -2871,15 +2942,20 @@ class OneIPDeviceSettings:
|
|
|
2871
2942
|
valid &= ~OneIPDeviceSetting.PTP_MODE
|
|
2872
2943
|
if (self._ptp_domain > ONEIP_PTP_DOMAIN_MAX):
|
|
2873
2944
|
valid &= ~OneIPDeviceSetting.PTP_DOMAIN
|
|
2874
|
-
if (self._power_save_level > OneIPPowerSaveLevel.OFF):
|
|
2875
|
-
valid &= ~OneIPDeviceSetting.POWER_SAVE_LEVEL
|
|
2876
2945
|
if (self._ptp_messages.dscp > ONEIP_DSCP_MAX):
|
|
2877
2946
|
valid &= ~OneIPDeviceSetting.PTP_DSCP
|
|
2878
2947
|
if (self._ptp_messages.stream_ttl == 0):
|
|
2879
2948
|
valid &= ~OneIPDeviceSetting.STREAM_TTL
|
|
2880
2949
|
if not own.takes_sink_rotation(self._ptp_messages.sink_rotation):
|
|
2881
2950
|
valid &= ~OneIPDeviceSetting.SINK_ROTATION
|
|
2882
|
-
|
|
2951
|
+
flags = self._flags
|
|
2952
|
+
if (OneIPDeviceSetting.POWER_SAVE_LEVEL in valid):
|
|
2953
|
+
valid &= ~OneIPDeviceSetting.POWER_SAVE_LEVEL
|
|
2954
|
+
if (OneIPDeviceSetting.POWER_SAVE_DISABLED not in valid) \
|
|
2955
|
+
and (OneIPDeviceSetting.POWER_SAVE_DISABLED in reported._valid):
|
|
2956
|
+
valid |= OneIPDeviceSetting.POWER_SAVE_DISABLED
|
|
2957
|
+
flags &= ~OneIPDeviceSetting.POWER_SAVE_DISABLED
|
|
2958
|
+
return OneIPDeviceSettings(valid=valid, flags=flags, ir_profiles=self._ir_profiles,
|
|
2883
2959
|
ir_profile=self._ir_profile, ir_profile_sink=self._ir_profile_sink,
|
|
2884
2960
|
auto_power_save=self._auto_power_save, power_save=self._power_save,
|
|
2885
2961
|
ptp_mode=self._ptp_mode, ptp_domain=self._ptp_domain,
|
|
@@ -3572,9 +3648,13 @@ class DeviceBase(ABC):
|
|
|
3572
3648
|
async def set_oneip_ptp_priority1(self, priority1:int) -> bool:
|
|
3573
3649
|
'''set the priority1 the device announces as a PTP grandmaster'''
|
|
3574
3650
|
|
|
3651
|
+
@abstractmethod
|
|
3652
|
+
async def set_oneip_power_save(self, enabled:bool) -> bool:
|
|
3653
|
+
'''switch power save on, so the device powers down by itself to the level it picks, or off'''
|
|
3654
|
+
|
|
3575
3655
|
@abstractmethod
|
|
3576
3656
|
async def set_oneip_power_save_level(self, level:OneIPPowerSaveLevel) -> bool:
|
|
3577
|
-
'''
|
|
3657
|
+
'''write the level the device powers down to in power save, which switches power save on'''
|
|
3578
3658
|
|
|
3579
3659
|
@abstractmethod
|
|
3580
3660
|
async def set_oneip_ptp_messages(self, which:OneIPDeviceSetting, messages:OneIPPtpMessages) -> bool:
|
|
@@ -4352,6 +4432,13 @@ class MxrCallbacks:
|
|
|
4352
4432
|
_LOGGER.debug(f"{device} dolby settings changed")
|
|
4353
4433
|
self.on_device_update(device)
|
|
4354
4434
|
|
|
4435
|
+
def on_third_party_message(self, dev:DeviceBase, message:ThirdPartyMessage) -> None:
|
|
4436
|
+
''' called when the application on 'dev' sent a message under its own
|
|
4437
|
+
vendor id. A message from a sender this client has not yet heard
|
|
4438
|
+
announce itself is dropped, as every frame is, and this client never
|
|
4439
|
+
receives its own. '''
|
|
4440
|
+
pass
|
|
4441
|
+
|
|
4355
4442
|
# The V2IP spelling of a name here, from before the rename to OneIP.
|
|
4356
4443
|
from .deprecated import alias_members as _alias_members, module_getattr as _module_getattr
|
|
4357
4444
|
# Installed through globals() so the generated stubs carry no __getattr__, which
|
|
@@ -930,6 +930,23 @@ class OneIPPrivacy:
|
|
|
930
930
|
audio: OneIPPrivacyStream
|
|
931
931
|
anc: OneIPPrivacyStream
|
|
932
932
|
|
|
933
|
+
@dataclass(frozen=True)
|
|
934
|
+
class ThirdPartyVendor:
|
|
935
|
+
flags: ThirdPartyVendorFlag
|
|
936
|
+
id: bytes
|
|
937
|
+
def __post_init__(self) -> None: ...
|
|
938
|
+
@classmethod
|
|
939
|
+
def oui(cls, oui: bytes) -> ThirdPartyVendor: ...
|
|
940
|
+
@classmethod
|
|
941
|
+
def private(cls, id: bytes) -> ThirdPartyVendor: ...
|
|
942
|
+
def same_vendor(self, other: ThirdPartyVendor) -> bool: ...
|
|
943
|
+
|
|
944
|
+
@dataclass(frozen=True)
|
|
945
|
+
class ThirdPartyMessage:
|
|
946
|
+
vendor: ThirdPartyVendor
|
|
947
|
+
payload: bytes
|
|
948
|
+
address: str | None
|
|
949
|
+
|
|
933
950
|
@dataclass(frozen=True)
|
|
934
951
|
class OneIPTestTone:
|
|
935
952
|
mode: OneIPToneMode | int = ...
|
|
@@ -962,6 +979,8 @@ class OneIPTestcard:
|
|
|
962
979
|
|
|
963
980
|
class OneIPDeviceSettings:
|
|
964
981
|
def __init__(self, valid: OneIPDeviceSetting = ..., flags: OneIPDeviceSetting = ..., ir_profiles: int = 0, ir_profile: int = 0, ir_profile_sink: int = 0, auto_power_save: int = 0, power_save: OneIPPowerSaveSchedule | None = None, ptp_mode: OneIPPtpMode | int = ..., ptp_domain: int = 0, ptp_priority1: int = 0, power_save_level: OneIPPowerSaveLevel | int = ..., ptp_messages: OneIPPtpMessages | None = None) -> None: ...
|
|
982
|
+
@classmethod
|
|
983
|
+
def power_save_switch(cls, enabled: bool) -> OneIPDeviceSettings: ...
|
|
965
984
|
@property
|
|
966
985
|
def valid(self) -> OneIPDeviceSetting: ...
|
|
967
986
|
@property
|
|
@@ -1330,6 +1349,8 @@ class DeviceBase(ABC):
|
|
|
1330
1349
|
@abstractmethod
|
|
1331
1350
|
async def set_oneip_ptp_priority1(self, priority1: int) -> bool: ...
|
|
1332
1351
|
@abstractmethod
|
|
1352
|
+
async def set_oneip_power_save(self, enabled: bool) -> bool: ...
|
|
1353
|
+
@abstractmethod
|
|
1333
1354
|
async def set_oneip_power_save_level(self, level: OneIPPowerSaveLevel) -> bool: ...
|
|
1334
1355
|
@abstractmethod
|
|
1335
1356
|
async def set_oneip_ptp_messages(self, which: OneIPDeviceSetting, messages: OneIPPtpMessages) -> bool: ...
|
|
@@ -1616,3 +1637,4 @@ class MxrCallbacks:
|
|
|
1616
1637
|
def on_rc_type_changed(self, bay: BayBase, rc_type: RCType | None) -> None: ...
|
|
1617
1638
|
def on_amp_zone_settings_changed(self, bay: BayBase, settings: AmpZoneSettings) -> None: ...
|
|
1618
1639
|
def on_amp_dolby_settings_changed(self, device: DeviceBase, settings: AmpDolbySettings) -> None: ...
|
|
1640
|
+
def on_third_party_message(self, dev: DeviceBase, message: ThirdPartyMessage) -> None: ...
|
|
@@ -123,6 +123,9 @@ MXR_OPCODE_VERSIONS: dict[int, int] = {
|
|
|
123
123
|
0x52: 0x2E, # V2IP_SINK_SDP
|
|
124
124
|
0x53: 0x2F, # V2IP_OSD_SYNC
|
|
125
125
|
0x54: 0x30, # V2IP_PRIVACY
|
|
126
|
+
# The lowest stamp, so that every receiver's ceiling passes it. No device
|
|
127
|
+
# decodes the payload, so there is no layout for a stamp to announce.
|
|
128
|
+
0x55: 0x01, # THIRD_PARTY
|
|
126
129
|
}
|
|
127
130
|
"""Per-opcode minimum compatible protocol version, mirroring the firmware's
|
|
128
131
|
own opcode table. Transmitters stamp the
|
|
@@ -349,7 +352,11 @@ class OneIPDeviceSetting(IntFlag):
|
|
|
349
352
|
AES67 = (1 << 17)
|
|
350
353
|
'''Sends the source's audio as an AES67 flow, where the source allows.'''
|
|
351
354
|
POWER_SAVE_LEVEL = (1 << 18)
|
|
352
|
-
'''The
|
|
355
|
+
'''The level the device powers down to in power save. A device picks it
|
|
356
|
+
itself: the level that saves the most while its input and display can
|
|
357
|
+
still wake it. Written without POWER_SAVE_DISABLED, it switches power save
|
|
358
|
+
on, and the level written is ignored, except by a device whose firmware
|
|
359
|
+
predates picking it, which takes the level as its setting.'''
|
|
353
360
|
PTP_ANNOUNCE = (1 << 19)
|
|
354
361
|
'''The interval between the device's PTP announce messages.'''
|
|
355
362
|
PTP_SYNC = (1 << 20)
|
|
@@ -818,6 +825,21 @@ ONEIP_VLAN_PORT_SFP = 0
|
|
|
818
825
|
ONEIP_VLAN_ID_MAX = 4094
|
|
819
826
|
"""The highest VLAN id: 0 means untagged, and 4095 is reserved by IEEE 802.1Q."""
|
|
820
827
|
|
|
828
|
+
class ThirdPartyVendorFlag(IntFlag):
|
|
829
|
+
'''The first byte of a third-party vendor id, ahead of its three
|
|
830
|
+
identifying bytes. Bits without a name are kept as they arrived.'''
|
|
831
|
+
NOT_OUI = (1 << 0)
|
|
832
|
+
'''The three bytes are an identifier the vendor chose, rather than its
|
|
833
|
+
IEEE OUI.'''
|
|
834
|
+
|
|
835
|
+
THIRD_PARTY_PAYLOAD_MAX = 1444
|
|
836
|
+
"""The largest payload a third-party message carries.
|
|
837
|
+
|
|
838
|
+
A frame this size fills one 1500-byte Ethernet frame exactly: 20 bytes of IPv4
|
|
839
|
+
header, 8 of UDP, the 24-byte frame header and the 4-byte vendor id leave 1444.
|
|
840
|
+
A larger send is refused rather than fragmented, because a lost fragment loses
|
|
841
|
+
the whole datagram and nothing on these paths retries."""
|
|
842
|
+
|
|
821
843
|
BAY_FEATURE_DOLBY_IN_POS = 24
|
|
822
844
|
|
|
823
845
|
class BayFeaturesMask(IntFlag):
|
|
@@ -340,6 +340,11 @@ class OneIPVlanFlag(IntFlag):
|
|
|
340
340
|
ONEIP_VLAN_PORTS: int
|
|
341
341
|
ONEIP_VLAN_PORT_SFP: int
|
|
342
342
|
ONEIP_VLAN_ID_MAX: int
|
|
343
|
+
|
|
344
|
+
class ThirdPartyVendorFlag(IntFlag):
|
|
345
|
+
NOT_OUI: Incomplete
|
|
346
|
+
|
|
347
|
+
THIRD_PARTY_PAYLOAD_MAX: int
|
|
343
348
|
BAY_FEATURE_DOLBY_IN_POS: int
|
|
344
349
|
|
|
345
350
|
class BayFeaturesMask(IntFlag):
|
|
@@ -249,6 +249,9 @@ def _mxr_frame_factory(hdr:FrameHeader, timestamp:float) -> FrameBase|None:
|
|
|
249
249
|
if hdr.opcode == 0x54:
|
|
250
250
|
from .FrameV2IPPrivacy import FrameV2IPPrivacy
|
|
251
251
|
return FrameV2IPPrivacy(header=hdr, timestamp=timestamp)
|
|
252
|
+
if hdr.opcode == 0x55:
|
|
253
|
+
from .FrameThirdParty import FrameThirdParty
|
|
254
|
+
return FrameThirdParty(header=hdr, timestamp=timestamp)
|
|
252
255
|
if hdr.opcode == 0x50:
|
|
253
256
|
from .FrameUnitCommand import FrameUnitCommand
|
|
254
257
|
return FrameUnitCommand(header=hdr, timestamp=timestamp)
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
'''Protocol frame for THIRD_PARTY (opcode 0x55): messages applications
|
|
8
|
+
exchange over the mesh.'''
|
|
9
|
+
|
|
10
|
+
from functools import cached_property
|
|
11
|
+
from ..compat import override
|
|
12
|
+
from .Constants import ThirdPartyVendorFlag
|
|
13
|
+
from .FrameBase import FrameBase
|
|
14
|
+
from ..Interface import DeviceRegistry, ThirdPartyMessage, ThirdPartyVendor
|
|
15
|
+
|
|
16
|
+
# The flag byte at 0 and the three identifying bytes at 1, then the
|
|
17
|
+
# application's bytes to the end of the frame.
|
|
18
|
+
VENDOR_SIZE = 4
|
|
19
|
+
|
|
20
|
+
class FrameThirdParty(FrameBase):
|
|
21
|
+
'''An application's own message, under its vendor id.'''
|
|
22
|
+
@staticmethod
|
|
23
|
+
def construct(mxr:DeviceRegistry, vendor:ThirdPartyVendor, payload:bytes) -> FrameBase|None:
|
|
24
|
+
'''A message to every device: the vendor's flag byte and three
|
|
25
|
+
identifying bytes, then payload as it was given.'''
|
|
26
|
+
data = bytes([int(vendor.flags)]) + vendor.id + bytes(payload)
|
|
27
|
+
return FrameBase.construct_base(mxr=mxr, opcode=0x55, payload=data)
|
|
28
|
+
|
|
29
|
+
@cached_property
|
|
30
|
+
def message(self) -> ThirdPartyMessage|None:
|
|
31
|
+
'''The message, whatever its vendor: telling them apart is the
|
|
32
|
+
application's. None for a payload too short for the vendor id.
|
|
33
|
+
Everything after the id is the application's, so the length is a
|
|
34
|
+
minimum and nothing past it is read.'''
|
|
35
|
+
p = self.payload or b''
|
|
36
|
+
if (len(p) < VENDOR_SIZE):
|
|
37
|
+
return None
|
|
38
|
+
vendor = ThirdPartyVendor(flags=ThirdPartyVendorFlag(p[0]), id=bytes(p[1:VENDOR_SIZE]))
|
|
39
|
+
return ThirdPartyMessage(vendor=vendor, payload=bytes(p[VENDOR_SIZE:]), address=self.address)
|
|
40
|
+
|
|
41
|
+
@override
|
|
42
|
+
def process(self) -> None:
|
|
43
|
+
'''Hand the message to the callbacks, as sent by the device it came from.'''
|
|
44
|
+
if ((message := self.message) is not None) and ((dev := self.remote_device) is not None):
|
|
45
|
+
dev.callbacks.on_third_party_message(dev, message)
|
|
46
|
+
|
|
47
|
+
def __str__(self) -> str:
|
|
48
|
+
if (self.message is None):
|
|
49
|
+
return "third-party message <dropped>"
|
|
50
|
+
return f"third-party message from {self.message}"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from ..Interface import DeviceRegistry as DeviceRegistry, ThirdPartyMessage as ThirdPartyMessage, ThirdPartyVendor as ThirdPartyVendor
|
|
2
|
+
from ..compat import override as override
|
|
3
|
+
from .Constants import ThirdPartyVendorFlag as ThirdPartyVendorFlag
|
|
4
|
+
from .FrameBase import FrameBase as FrameBase
|
|
5
|
+
from functools import cached_property
|
|
6
|
+
|
|
7
|
+
VENDOR_SIZE: int
|
|
8
|
+
|
|
9
|
+
class FrameThirdParty(FrameBase):
|
|
10
|
+
@staticmethod
|
|
11
|
+
def construct(mxr: DeviceRegistry, vendor: ThirdPartyVendor, payload: bytes) -> FrameBase | None: ...
|
|
12
|
+
@cached_property
|
|
13
|
+
def message(self) -> ThirdPartyMessage | None: ...
|
|
14
|
+
@override
|
|
15
|
+
def process(self) -> None: ...
|
|
@@ -76,7 +76,6 @@ from typing import Any, Callable
|
|
|
76
76
|
from ..compat import override
|
|
77
77
|
from datetime import datetime
|
|
78
78
|
from enum import Enum
|
|
79
|
-
import dataclasses
|
|
80
79
|
import logging
|
|
81
80
|
import time
|
|
82
81
|
|
|
@@ -1572,12 +1571,28 @@ class Device(DeviceBase):
|
|
|
1572
1571
|
return self._send_v2ip_settings(OneIPDeviceSettings(
|
|
1573
1572
|
valid=OneIPDeviceSetting.PTP_PRIORITY1, ptp_priority1=priority1))
|
|
1574
1573
|
|
|
1575
|
-
async def
|
|
1576
|
-
'''
|
|
1574
|
+
async def set_oneip_power_save(self, enabled:bool) -> bool:
|
|
1575
|
+
'''Switch this OneIP device's power save on, so it powers down by itself
|
|
1576
|
+
to the level it picks, or off, so it powers down only when asked.
|
|
1577
1577
|
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1578
|
+
On is written as a mesh controller writes it: both POWER_SAVE_DISABLED,
|
|
1579
|
+
clear, and POWER_SAVE_LEVEL, at 0, so a device whose firmware takes the
|
|
1580
|
+
level as its setting returns to its own default. The terms of
|
|
1581
|
+
set_oneip_setting() apply.
|
|
1582
|
+
'''
|
|
1583
|
+
return self._send_v2ip_settings(OneIPDeviceSettings.power_save_switch(enabled))
|
|
1584
|
+
|
|
1585
|
+
async def set_oneip_power_save_level(self, level:OneIPPowerSaveLevel) -> bool:
|
|
1586
|
+
'''Write the level this OneIP device powers down to in power save, which
|
|
1587
|
+
switches power save on.
|
|
1588
|
+
|
|
1589
|
+
A device picks its level itself and ignores the one written, except one
|
|
1590
|
+
whose firmware predates picking it, which takes the level as its
|
|
1591
|
+
setting. A level that unit does not offer is one its unit_status
|
|
1592
|
+
reports without UnitPowerSave.SUPPORTED. A level this library does not
|
|
1593
|
+
name is refused. The level is not cached: oneip_settings reads what the
|
|
1594
|
+
device reports. set_oneip_power_save() switches power save on or off
|
|
1595
|
+
without naming a level. The terms of set_oneip_setting() apply.
|
|
1581
1596
|
'''
|
|
1582
1597
|
if not (OneIPPowerSaveLevel.NONE <= level <= OneIPPowerSaveLevel.OFF):
|
|
1583
1598
|
_LOGGER.warning(f"not setting power save level {level} on {self}: no such level")
|
|
@@ -1612,7 +1627,6 @@ class Device(DeviceBase):
|
|
|
1612
1627
|
_LOGGER.warning(f"not turning the output of {self} by {messages.sink_rotation} degrees: "
|
|
1613
1628
|
f"it turns by steps of {own.sink_rotation_step}")
|
|
1614
1629
|
return False
|
|
1615
|
-
messages = dataclasses.replace(messages, sink_rotation_step=own.sink_rotation_step)
|
|
1616
1630
|
return self._send_v2ip_settings(OneIPDeviceSettings(valid=which, ptp_messages=messages))
|
|
1617
1631
|
|
|
1618
1632
|
def _on_oneip_downmix(self, downmix:OneIPDownmix|None, reported:bool) -> None:
|
|
@@ -1932,7 +1946,7 @@ class Device(DeviceBase):
|
|
|
1932
1946
|
return False
|
|
1933
1947
|
if self.registry.transmit(frame.frame) != len(frame.frame):
|
|
1934
1948
|
return False
|
|
1935
|
-
self._merge_v2ip_settings(settings)
|
|
1949
|
+
self._merge_v2ip_settings(settings.as_applied_to(reported))
|
|
1936
1950
|
return True
|
|
1937
1951
|
|
|
1938
1952
|
@property
|
|
@@ -270,6 +270,7 @@ class Device(DeviceBase):
|
|
|
270
270
|
async def set_oneip_ptp_mode(self, mode: OneIPPtpMode) -> bool: ...
|
|
271
271
|
async def set_oneip_ptp_domain(self, domain: int) -> bool: ...
|
|
272
272
|
async def set_oneip_ptp_priority1(self, priority1: int) -> bool: ...
|
|
273
|
+
async def set_oneip_power_save(self, enabled: bool) -> bool: ...
|
|
273
274
|
async def set_oneip_power_save_level(self, level: OneIPPowerSaveLevel) -> bool: ...
|
|
274
275
|
async def set_oneip_ptp_messages(self, which: OneIPDeviceSetting, messages: OneIPPtpMessages) -> bool: ...
|
|
275
276
|
async def set_oneip_downmix(self, downmix: OneIPDownmix) -> bool: ...
|
|
@@ -22,12 +22,13 @@ from datetime import datetime, timezone
|
|
|
22
22
|
from .ConnectionAsync import ConnectionAsync
|
|
23
23
|
from ..const import MXR_HELLO_INTERVAL_MIN, MXR_HELLO_INTERVAL_RAND, __version__
|
|
24
24
|
from .Device import Device
|
|
25
|
-
from ..Interface import ConnectionCallbacks, DeviceRegistry, MxrDeviceUid, BayLinks, BayBase, DeviceBase, MxrCallbacks, AudioEndpoint, mxr_broadcast_address, OneIPDeviceSettings, OneIPPowerSaveSchedule, OneIPPtpMessages, OsdSyncContent, OsdSyncRequest
|
|
26
|
-
from ..proto.Constants import MXR_PROTOCOL_VERSION, DeviceFeature, OsdSyncWhat
|
|
25
|
+
from ..Interface import ConnectionCallbacks, DeviceRegistry, MxrDeviceUid, BayLinks, BayBase, DeviceBase, MxrCallbacks, AudioEndpoint, mxr_broadcast_address, OneIPDeviceSettings, OneIPPowerSaveSchedule, OneIPPtpMessages, OsdSyncContent, OsdSyncRequest, ThirdPartyVendor
|
|
26
|
+
from ..proto.Constants import MXR_PROTOCOL_VERSION, THIRD_PARTY_PAYLOAD_MAX, DeviceFeature, OsdSyncWhat
|
|
27
27
|
from ..proto.FrameDiscover import FrameDiscover, constructFrameDiscover
|
|
28
28
|
from ..proto.Factory import process_mxr_frame
|
|
29
29
|
from ..proto.FrameHello import FrameHello, constructFrameHello
|
|
30
30
|
from ..proto.FramePing import FramePing
|
|
31
|
+
from ..proto.FrameThirdParty import FrameThirdParty
|
|
31
32
|
from ..proto.FrameBase import FrameBase
|
|
32
33
|
from ..proto.FrameTime import FrameTime
|
|
33
34
|
from ..proto.FrameTimeZone import FrameTimeZone
|
|
@@ -561,12 +562,35 @@ class Remote(DeviceRegistry, ConnectionCallbacks):
|
|
|
561
562
|
return False
|
|
562
563
|
return self._send_to_all(FrameV2IPSettingsAll.construct(mxr=self, settings=settings))
|
|
563
564
|
|
|
565
|
+
async def set_all_oneip_power_save(self, enabled:bool) -> bool:
|
|
566
|
+
'''Switch power save on or off on every OneIP device of the mesh with
|
|
567
|
+
one frame, written as Device.set_oneip_power_save() writes it.
|
|
568
|
+
Otherwise as set_all_oneip_device_settings().'''
|
|
569
|
+
return await self.set_all_oneip_device_settings(OneIPDeviceSettings.power_save_switch(enabled))
|
|
570
|
+
|
|
564
571
|
async def request_all_oneip_privacy(self) -> bool:
|
|
565
572
|
'''Ask every OneIP source unit of the mesh how its streams are
|
|
566
573
|
encrypted, as Device.request_oneip_privacy() asks one. A unit below
|
|
567
574
|
protocol 0x30 ignores it.'''
|
|
568
575
|
return self._send_to_all(FrameV2IPPrivacy.construct_request(mxr=self, target=None, target_uid=None))
|
|
569
576
|
|
|
577
|
+
async def send_third_party(self, vendor:ThirdPartyVendor, payload:bytes) -> bool:
|
|
578
|
+
'''Send an application's own message to every device, under vendor.
|
|
579
|
+
True when all of it was written.
|
|
580
|
+
|
|
581
|
+
Every other client of this library that has heard this one announce
|
|
582
|
+
itself receives it at MxrCallbacks.on_third_party_message(); this client
|
|
583
|
+
does not, and neither does one sharing its uid. No unit is meant to read
|
|
584
|
+
it. Nothing acknowledges it, so an application that needs to know it
|
|
585
|
+
arrived has to have its peers answer.
|
|
586
|
+
|
|
587
|
+
A payload over THIRD_PARTY_PAYLOAD_MAX bytes is refused.'''
|
|
588
|
+
if (len(payload) > THIRD_PARTY_PAYLOAD_MAX):
|
|
589
|
+
_LOGGER.warning(f"not sending a third-party message: {len(payload)} bytes, "
|
|
590
|
+
f"the most is {THIRD_PARTY_PAYLOAD_MAX}")
|
|
591
|
+
return False
|
|
592
|
+
return self._send_to_all(FrameThirdParty.construct(mxr=self, vendor=vendor, payload=payload))
|
|
593
|
+
|
|
570
594
|
async def set_mesh_time_zone(self, zone:str, rule:str) -> bool:
|
|
571
595
|
'''Set the time zone of every device that hears it.
|
|
572
596
|
|