matter-python-client 1.5.0a0.dev20261009__tar.gz → 1.5.0a0.dev20261014__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.
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/PKG-INFO +32 -1
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/README.md +31 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_python_client.egg-info/PKG-INFO +32 -1
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_python_client.egg-info/SOURCES.txt +1 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/client/client.py +211 -5
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/client/models/node.py +61 -41
- matter_python_client-1.5.0a0.dev20261014/matter_server/common/errors.py +289 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/common/models.py +377 -2
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/pyproject.toml +1 -1
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_bridge_node_fixture.py +39 -6
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_bridge_topology.py +151 -15
- matter_python_client-1.5.0a0.dev20261014/tests/test_camera.py +549 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_event_dispatch.py +55 -1
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_integration.py +2 -2
- matter_python_client-1.5.0a0.dev20261014/tests/test_webrtc_protocol.py +127 -0
- matter_python_client-1.5.0a0.dev20261009/matter_server/common/errors.py +0 -139
- matter_python_client-1.5.0a0.dev20261009/tests/test_webrtc_protocol.py +0 -36
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/ChipUtility.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/__init__.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/ClusterObjects.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/Objects.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/Types.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/__init__.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/AccessControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/AccountLogin.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Actions.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ActivatedCarbonFilterMonitoring.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/AdministratorCommissioning.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/AirQuality.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/AmbientContextSensing.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ApplicationBasic.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ApplicationLauncher.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/AqaraAmbientSensingConfigurationCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/AqaraOccupantLocationCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/AqaraRadarSensingUnionCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/AudioOutput.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/BasicInformation.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Binding.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/BooleanState.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/BooleanStateConfiguration.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/BridgedDeviceBasicInformation.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/CameraAvSettingsUserLevelManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/CameraAvStreamManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/CarbonDioxideConcentrationMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/CarbonMonoxideConcentrationMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Channel.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Chime.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ClosureControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ClosureDimension.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ColorControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/CommissionerControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/CommodityMetering.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/CommodityPrice.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/CommodityTariff.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ContentAppObserver.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ContentControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ContentLauncher.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Descriptor.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/DeviceEnergyManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/DeviceEnergyManagementMode.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/DiagnosticLogs.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/DishwasherAlarm.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/DishwasherMode.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/DoorLock.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/DraftElectricalMeasurementCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/EcosystemInformation.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ElectricalEnergyMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ElectricalGridConditions.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ElectricalPowerMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/EnergyEvse.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/EnergyEvseMode.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/EnergyPreference.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/EthernetNetworkDiagnostics.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/EveCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/FanControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/FixedLabel.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/FlowMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/FormaldehydeConcentrationMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/GeneralCommissioning.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/GeneralDiagnostics.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Globals.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/GroupKeyManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Groupcast.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Groups.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/HeimanCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/HepaFilterMonitoring.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/IcdManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Identify.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/IkeaValveCalibrationCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/IlluminanceMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/InovelliCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/JointFabricAdministrator.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/JointFabricDatastore.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/KeypadInput.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/LaundryDryerControls.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/LaundryWasherControls.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/LaundryWasherMode.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/LevelControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/LocalizationConfiguration.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/LowPower.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/MediaInput.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/MediaPlayback.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Messages.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/MeterIdentification.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/MicrowaveOvenControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/MicrowaveOvenMode.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ModeSelect.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/NeoCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/NetworkCommissioning.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/NitrogenDioxideConcentrationMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/OccupancySensing.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/OnOff.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/OperationalCredentials.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/OperationalState.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/OtaSoftwareUpdateProvider.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/OtaSoftwareUpdateRequestor.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/OvenCavityOperationalState.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/OvenMode.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/OzoneConcentrationMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Pm10ConcentrationMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Pm1ConcentrationMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Pm25ConcentrationMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/PowerSource.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/PowerSourceConfiguration.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/PowerTopology.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/PressureMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/PumpConfigurationAndControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/PushAvStreamTransport.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/RadonConcentrationMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/RefrigeratorAlarm.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/RefrigeratorAndTemperatureControlledCabinetMode.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/RelativeHumidityMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/RvcCleanMode.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/RvcOperationalState.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/RvcRunMode.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ScenesManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ServiceArea.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/SmokeCoAlarm.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/SoftwareDiagnostics.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/SoilMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Switch.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/TargetNavigator.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/TclDehumidifierCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/TemperatureAlarm.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/TemperatureControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/TemperatureMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/Thermostat.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ThermostatUserInterfaceConfiguration.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ThirdRealityMeteringCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ThreadBorderRouterManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ThreadNetworkDiagnostics.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ThreadNetworkDirectory.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/TimeFormatLocalization.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/TimeSynchronization.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/TlsCertificateManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/TlsClientManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/TotalVolatileOrganicCompoundsConcentrationMeasurement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/UnitLocalization.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/UserLabel.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ValveConfigurationAndControl.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WagoCluster.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WakeOnLan.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WaterHeaterManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WaterHeaterMode.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WaterTankLevelMonitoring.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WebRtcTransportDefinitions.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WebRtcTransportProvider.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WebRtcTransportRequestor.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WiFiNetworkDiagnostics.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WiFiNetworkManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/WindowCovering.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/ZoneManagement.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/cluster_defs/__init__.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/clusters/enum.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/tlv/__init__.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/chip/tlv/tlvlist.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_python_client.egg-info/dependency_links.txt +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_python_client.egg-info/not-zip-safe +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_python_client.egg-info/requires.txt +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_python_client.egg-info/top_level.txt +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/__init__.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/client/__init__.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/client/connection.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/client/exceptions.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/client/models/__init__.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/client/models/device_types.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/common/__init__.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/common/const.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/common/custom_clusters.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/common/helpers/__init__.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/common/helpers/api.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/common/helpers/json.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/common/helpers/util.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/matter_server/py.typed +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/setup.cfg +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_bigint_wire.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_client.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_client_integration.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_credentials.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_icd.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_imports.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_network_topology.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_ota_upload.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_unix_socket.py +0 -0
- {matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/tests/test_util.py +0 -0
{matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matter-python-client
|
|
3
|
-
Version: 1.5.0a0.
|
|
3
|
+
Version: 1.5.0a0.dev20261014
|
|
4
4
|
Summary: Python Client for the OHF Matter Server
|
|
5
5
|
Author-email: Open Home Foundation <hello@openhomefoundation.io>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -110,6 +110,37 @@ Beyond the `python-matter-server` API, the client exposes commands the Matter.js
|
|
|
110
110
|
then streams the bytes to `POST /ota-upload/<upload_id>`. Raises `OtaUploadError` (code 101) for a
|
|
111
111
|
corrupt image, an expired/spent id, or disabled OTA support.
|
|
112
112
|
- `get_icd_state`, `register_icd`, `unregister_icd`, `resync_icd` — ICD Check-In registration
|
|
113
|
+
- `camera_get_capabilities`, `camera_start_stream`, `camera_provide_answer`,
|
|
114
|
+
`camera_provide_ice_candidates`, `camera_stop_stream`, `camera_snapshot`, `camera_release_stream`
|
|
115
|
+
— the camera API (schema 14+). `camera_get_capabilities`, `camera_start_stream`, `camera_stop_stream`
|
|
116
|
+
and `camera_snapshot` return dataclasses from `matter_server.common.models`; the other three return
|
|
117
|
+
`None`.
|
|
118
|
+
`camera_start_stream`'s `video` / `audio` take `None` (the server decides), a `CameraVideoHints` /
|
|
119
|
+
`CameraAudioHints` dict, or `False` (no such track). `talkback=True` lets the offer ask the camera to
|
|
120
|
+
play your audio; without it the server makes the offer's audio sections not send, so a browser-style
|
|
121
|
+
`sendrecv` audio offer goes to the camera as `recvonly`. `camera_session_ended` and
|
|
122
|
+
`camera_stream_evicted` events arrive as `CameraSessionEndedData` / `CameraStreamEvictedData`;
|
|
123
|
+
`webrtc_callback` stays a dict. Errors 102 to 106 (`CameraStreamIncompatible`, ...) expose their
|
|
124
|
+
details as attributes, e.g. `err.reason` and `err.bound` on `CameraStreamIncompatible`
|
|
125
|
+
|
|
126
|
+
The minimal `camera_start_stream` call is `node_id`, `endpoint_id`, `stream_usage` and your SDP
|
|
127
|
+
offer: the server reuses or allocates the best stream the camera can serve that still fits what
|
|
128
|
+
the offer can decode, and adds audio when it can. Pass `video` / `audio` hints only to set hard bounds. A
|
|
129
|
+
session opened this way is driven with `camera_provide_answer`, `camera_provide_ice_candidates`
|
|
130
|
+
and `camera_stop_stream` only, not with `send_webrtc_provider_command` or a `send_device_command`
|
|
131
|
+
`EndSession`: the server tracks the session and its streams, and only the `camera_*` methods keep
|
|
132
|
+
that record correct. The exception is a re-offer (ICE restart), which goes through
|
|
133
|
+
`send_webrtc_provider_command`. See `docs/websockets_api.md` for the flow and every option.
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
try:
|
|
137
|
+
result = await client.camera_start_stream(node_id, 1, "LiveView", sdp=offer)
|
|
138
|
+
except CameraStreamIncompatible as err:
|
|
139
|
+
print(err.reason, err.bound)
|
|
140
|
+
else:
|
|
141
|
+
# The camera's answer arrives as a webrtc_callback "answer" event for this session.
|
|
142
|
+
print(result.webrtc_session_id, result.video.codec if result.video else None)
|
|
143
|
+
```
|
|
113
144
|
|
|
114
145
|
```python
|
|
115
146
|
version = await client.upload_ota_file("/path/to/firmware.ota")
|
{matter_python_client-1.5.0a0.dev20261009 → matter_python_client-1.5.0a0.dev20261014}/README.md
RENAMED
|
@@ -78,6 +78,37 @@ Beyond the `python-matter-server` API, the client exposes commands the Matter.js
|
|
|
78
78
|
then streams the bytes to `POST /ota-upload/<upload_id>`. Raises `OtaUploadError` (code 101) for a
|
|
79
79
|
corrupt image, an expired/spent id, or disabled OTA support.
|
|
80
80
|
- `get_icd_state`, `register_icd`, `unregister_icd`, `resync_icd` — ICD Check-In registration
|
|
81
|
+
- `camera_get_capabilities`, `camera_start_stream`, `camera_provide_answer`,
|
|
82
|
+
`camera_provide_ice_candidates`, `camera_stop_stream`, `camera_snapshot`, `camera_release_stream`
|
|
83
|
+
— the camera API (schema 14+). `camera_get_capabilities`, `camera_start_stream`, `camera_stop_stream`
|
|
84
|
+
and `camera_snapshot` return dataclasses from `matter_server.common.models`; the other three return
|
|
85
|
+
`None`.
|
|
86
|
+
`camera_start_stream`'s `video` / `audio` take `None` (the server decides), a `CameraVideoHints` /
|
|
87
|
+
`CameraAudioHints` dict, or `False` (no such track). `talkback=True` lets the offer ask the camera to
|
|
88
|
+
play your audio; without it the server makes the offer's audio sections not send, so a browser-style
|
|
89
|
+
`sendrecv` audio offer goes to the camera as `recvonly`. `camera_session_ended` and
|
|
90
|
+
`camera_stream_evicted` events arrive as `CameraSessionEndedData` / `CameraStreamEvictedData`;
|
|
91
|
+
`webrtc_callback` stays a dict. Errors 102 to 106 (`CameraStreamIncompatible`, ...) expose their
|
|
92
|
+
details as attributes, e.g. `err.reason` and `err.bound` on `CameraStreamIncompatible`
|
|
93
|
+
|
|
94
|
+
The minimal `camera_start_stream` call is `node_id`, `endpoint_id`, `stream_usage` and your SDP
|
|
95
|
+
offer: the server reuses or allocates the best stream the camera can serve that still fits what
|
|
96
|
+
the offer can decode, and adds audio when it can. Pass `video` / `audio` hints only to set hard bounds. A
|
|
97
|
+
session opened this way is driven with `camera_provide_answer`, `camera_provide_ice_candidates`
|
|
98
|
+
and `camera_stop_stream` only, not with `send_webrtc_provider_command` or a `send_device_command`
|
|
99
|
+
`EndSession`: the server tracks the session and its streams, and only the `camera_*` methods keep
|
|
100
|
+
that record correct. The exception is a re-offer (ICE restart), which goes through
|
|
101
|
+
`send_webrtc_provider_command`. See `docs/websockets_api.md` for the flow and every option.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
try:
|
|
105
|
+
result = await client.camera_start_stream(node_id, 1, "LiveView", sdp=offer)
|
|
106
|
+
except CameraStreamIncompatible as err:
|
|
107
|
+
print(err.reason, err.bound)
|
|
108
|
+
else:
|
|
109
|
+
# The camera's answer arrives as a webrtc_callback "answer" event for this session.
|
|
110
|
+
print(result.webrtc_session_id, result.video.codec if result.video else None)
|
|
111
|
+
```
|
|
81
112
|
|
|
82
113
|
```python
|
|
83
114
|
version = await client.upload_ota_file("/path/to/firmware.ota")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matter-python-client
|
|
3
|
-
Version: 1.5.0a0.
|
|
3
|
+
Version: 1.5.0a0.dev20261014
|
|
4
4
|
Summary: Python Client for the OHF Matter Server
|
|
5
5
|
Author-email: Open Home Foundation <hello@openhomefoundation.io>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -110,6 +110,37 @@ Beyond the `python-matter-server` API, the client exposes commands the Matter.js
|
|
|
110
110
|
then streams the bytes to `POST /ota-upload/<upload_id>`. Raises `OtaUploadError` (code 101) for a
|
|
111
111
|
corrupt image, an expired/spent id, or disabled OTA support.
|
|
112
112
|
- `get_icd_state`, `register_icd`, `unregister_icd`, `resync_icd` — ICD Check-In registration
|
|
113
|
+
- `camera_get_capabilities`, `camera_start_stream`, `camera_provide_answer`,
|
|
114
|
+
`camera_provide_ice_candidates`, `camera_stop_stream`, `camera_snapshot`, `camera_release_stream`
|
|
115
|
+
— the camera API (schema 14+). `camera_get_capabilities`, `camera_start_stream`, `camera_stop_stream`
|
|
116
|
+
and `camera_snapshot` return dataclasses from `matter_server.common.models`; the other three return
|
|
117
|
+
`None`.
|
|
118
|
+
`camera_start_stream`'s `video` / `audio` take `None` (the server decides), a `CameraVideoHints` /
|
|
119
|
+
`CameraAudioHints` dict, or `False` (no such track). `talkback=True` lets the offer ask the camera to
|
|
120
|
+
play your audio; without it the server makes the offer's audio sections not send, so a browser-style
|
|
121
|
+
`sendrecv` audio offer goes to the camera as `recvonly`. `camera_session_ended` and
|
|
122
|
+
`camera_stream_evicted` events arrive as `CameraSessionEndedData` / `CameraStreamEvictedData`;
|
|
123
|
+
`webrtc_callback` stays a dict. Errors 102 to 106 (`CameraStreamIncompatible`, ...) expose their
|
|
124
|
+
details as attributes, e.g. `err.reason` and `err.bound` on `CameraStreamIncompatible`
|
|
125
|
+
|
|
126
|
+
The minimal `camera_start_stream` call is `node_id`, `endpoint_id`, `stream_usage` and your SDP
|
|
127
|
+
offer: the server reuses or allocates the best stream the camera can serve that still fits what
|
|
128
|
+
the offer can decode, and adds audio when it can. Pass `video` / `audio` hints only to set hard bounds. A
|
|
129
|
+
session opened this way is driven with `camera_provide_answer`, `camera_provide_ice_candidates`
|
|
130
|
+
and `camera_stop_stream` only, not with `send_webrtc_provider_command` or a `send_device_command`
|
|
131
|
+
`EndSession`: the server tracks the session and its streams, and only the `camera_*` methods keep
|
|
132
|
+
that record correct. The exception is a re-offer (ICE restart), which goes through
|
|
133
|
+
`send_webrtc_provider_command`. See `docs/websockets_api.md` for the flow and every option.
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
try:
|
|
137
|
+
result = await client.camera_start_stream(node_id, 1, "LiveView", sdp=offer)
|
|
138
|
+
except CameraStreamIncompatible as err:
|
|
139
|
+
print(err.reason, err.bound)
|
|
140
|
+
else:
|
|
141
|
+
# The camera's answer arrives as a webrtc_callback "answer" event for this session.
|
|
142
|
+
print(result.webrtc_session_id, result.video.codec if result.video else None)
|
|
143
|
+
```
|
|
113
144
|
|
|
114
145
|
```python
|
|
115
146
|
version = await client.upload_ota_file("/path/to/firmware.ota")
|
|
@@ -186,6 +186,7 @@ matter_server/common/helpers/util.py
|
|
|
186
186
|
tests/test_bigint_wire.py
|
|
187
187
|
tests/test_bridge_node_fixture.py
|
|
188
188
|
tests/test_bridge_topology.py
|
|
189
|
+
tests/test_camera.py
|
|
189
190
|
tests/test_client.py
|
|
190
191
|
tests/test_client_integration.py
|
|
191
192
|
tests/test_credentials.py
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
from dataclasses import asdict
|
|
6
7
|
import logging
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from typing import TYPE_CHECKING, Any, Final, Literal, Self, cast
|
|
@@ -25,6 +26,16 @@ from matter_server.common.helpers.util import (
|
|
|
25
26
|
)
|
|
26
27
|
from matter_server.common.models import (
|
|
27
28
|
APICommand,
|
|
29
|
+
CameraAudioHints,
|
|
30
|
+
CameraCapabilities,
|
|
31
|
+
CameraIceServer,
|
|
32
|
+
CameraResolutionHint,
|
|
33
|
+
CameraSessionEndedData,
|
|
34
|
+
CameraSnapshotResult,
|
|
35
|
+
CameraStartStreamResult,
|
|
36
|
+
CameraStopStreamResult,
|
|
37
|
+
CameraStreamEvictedData,
|
|
38
|
+
CameraVideoHints,
|
|
28
39
|
CommandMessage,
|
|
29
40
|
CommissionableNodeData,
|
|
30
41
|
CommissioningParameters,
|
|
@@ -43,6 +54,7 @@ from matter_server.common.models import (
|
|
|
43
54
|
ServerDiagnostics,
|
|
44
55
|
ServerInfoMessage,
|
|
45
56
|
SuccessResultMessage,
|
|
57
|
+
WebRTCIceCandidate,
|
|
46
58
|
)
|
|
47
59
|
|
|
48
60
|
from .connection import MatterClientConnection
|
|
@@ -61,7 +73,7 @@ from .models.node import (
|
|
|
61
73
|
)
|
|
62
74
|
|
|
63
75
|
if TYPE_CHECKING:
|
|
64
|
-
from collections.abc import Callable
|
|
76
|
+
from collections.abc import Callable, Sequence
|
|
65
77
|
from os import PathLike
|
|
66
78
|
from types import TracebackType
|
|
67
79
|
|
|
@@ -71,6 +83,25 @@ if TYPE_CHECKING:
|
|
|
71
83
|
|
|
72
84
|
SUB_WILDCARD: Final = "*"
|
|
73
85
|
|
|
86
|
+
WebRtcProviderCommandName = Literal[
|
|
87
|
+
"ProvideOffer",
|
|
88
|
+
"SolicitOffer",
|
|
89
|
+
"ProvideAnswer",
|
|
90
|
+
"ProvideIceCandidates",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
# Relayed from schema 14; the offer commands need only schema 12.
|
|
94
|
+
SIGNALLING_PROVIDER_COMMANDS: Final = frozenset({"ProvideAnswer", "ProvideIceCandidates"})
|
|
95
|
+
|
|
96
|
+
# Payload carries node_id, so node_filter subscribers receive these events.
|
|
97
|
+
NODE_SCOPED_CAMERA_EVENTS: Final = frozenset(
|
|
98
|
+
{
|
|
99
|
+
EventType.WEBRTC_CALLBACK,
|
|
100
|
+
EventType.CAMERA_SESSION_ENDED,
|
|
101
|
+
EventType.CAMERA_STREAM_EVICTED,
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
|
|
74
105
|
# pylint: disable=too-many-public-methods,too-many-locals,too-many-branches
|
|
75
106
|
|
|
76
107
|
|
|
@@ -718,23 +749,190 @@ class MatterClient:
|
|
|
718
749
|
self,
|
|
719
750
|
node_id: int,
|
|
720
751
|
endpoint_id: int,
|
|
721
|
-
command_name:
|
|
752
|
+
command_name: WebRtcProviderCommandName,
|
|
722
753
|
payload: dict,
|
|
723
|
-
) -> dict[str, Any]:
|
|
754
|
+
) -> dict[str, Any] | None:
|
|
724
755
|
"""Invoke a WebRTCTransportProvider command on a commissioned camera.
|
|
725
756
|
|
|
726
757
|
The server hard-codes the cluster id (0x0553) and injects
|
|
727
758
|
originatingEndpointId — payload should omit both.
|
|
759
|
+
|
|
760
|
+
``ProvideOffer`` and ``SolicitOffer`` start a session and return the camera's response.
|
|
761
|
+
``ProvideAnswer`` and ``ProvideIceCandidates`` act on an existing session, return ``None``
|
|
762
|
+
and need schema 14. For ``EndSession``, use ``camera_stop_stream``.
|
|
728
763
|
"""
|
|
729
764
|
response = await self.send_command(
|
|
730
765
|
APICommand.SEND_WEBRTC_PROVIDER_COMMAND,
|
|
731
|
-
require_schema=12,
|
|
766
|
+
require_schema=14 if command_name in SIGNALLING_PROVIDER_COMMANDS else 12,
|
|
732
767
|
node_id=node_id,
|
|
733
768
|
endpoint_id=endpoint_id,
|
|
734
769
|
command_name=command_name,
|
|
735
770
|
payload=payload,
|
|
736
771
|
)
|
|
737
|
-
return cast(dict[str, Any], response)
|
|
772
|
+
return cast("dict[str, Any] | None", response)
|
|
773
|
+
|
|
774
|
+
async def camera_get_capabilities(self, node_id: int, endpoint_id: int) -> CameraCapabilities:
|
|
775
|
+
"""Return what the camera states and what is allocated on it. Allocates nothing.
|
|
776
|
+
|
|
777
|
+
Requires schema 14.
|
|
778
|
+
"""
|
|
779
|
+
data = await self.send_command(
|
|
780
|
+
APICommand.CAMERA_GET_CAPABILITIES, require_schema=14, node_id=node_id, endpoint_id=endpoint_id
|
|
781
|
+
)
|
|
782
|
+
return dataclass_from_dict(CameraCapabilities, data)
|
|
783
|
+
|
|
784
|
+
async def camera_start_stream( # noqa: PLR0913
|
|
785
|
+
self,
|
|
786
|
+
node_id: int,
|
|
787
|
+
endpoint_id: int,
|
|
788
|
+
stream_usage: str,
|
|
789
|
+
sdp: str | None = None,
|
|
790
|
+
video: CameraVideoHints | Literal[False] | None = None,
|
|
791
|
+
audio: CameraAudioHints | Literal[False] | None = None,
|
|
792
|
+
ice_servers: list[CameraIceServer] | None = None,
|
|
793
|
+
ice_transport_policy: str | None = None,
|
|
794
|
+
metadata_enabled: bool | None = None,
|
|
795
|
+
allow_eviction: bool | None = None,
|
|
796
|
+
talkback: bool | None = None,
|
|
797
|
+
) -> CameraStartStreamResult:
|
|
798
|
+
"""Open a WebRTC session, allocating or reusing the streams it needs.
|
|
799
|
+
|
|
800
|
+
Sends ``ProvideOffer`` when ``sdp`` is given, ``SolicitOffer`` otherwise; then the camera's
|
|
801
|
+
offer arrives as a ``webrtc_callback`` ``offer`` event. For ``video`` and ``audio``, ``None``
|
|
802
|
+
lets the server decide, a hints dict sets hard requirements, and ``False`` leaves the track
|
|
803
|
+
out. ``talkback=True`` (requires ``sdp``) forwards the offer unchanged so the camera can play
|
|
804
|
+
the caller's audio; otherwise the server makes the offer's audio sections not send. That only
|
|
805
|
+
asks the camera: it may still answer ``sendrecv``, so a caller that must not send audio keeps
|
|
806
|
+
the track off its audio sender. Arguments left ``None`` are not sent.
|
|
807
|
+
|
|
808
|
+
Requires schema 14.
|
|
809
|
+
"""
|
|
810
|
+
optional = {
|
|
811
|
+
"sdp": sdp,
|
|
812
|
+
"video": video,
|
|
813
|
+
"audio": audio,
|
|
814
|
+
"ice_servers": ice_servers,
|
|
815
|
+
"ice_transport_policy": ice_transport_policy,
|
|
816
|
+
"metadata_enabled": metadata_enabled,
|
|
817
|
+
"allow_eviction": allow_eviction,
|
|
818
|
+
"talkback": talkback,
|
|
819
|
+
}
|
|
820
|
+
data = await self.send_command(
|
|
821
|
+
APICommand.CAMERA_START_STREAM,
|
|
822
|
+
require_schema=14,
|
|
823
|
+
node_id=node_id,
|
|
824
|
+
endpoint_id=endpoint_id,
|
|
825
|
+
stream_usage=stream_usage,
|
|
826
|
+
**{key: value for key, value in optional.items() if value is not None},
|
|
827
|
+
)
|
|
828
|
+
return dataclass_from_dict(CameraStartStreamResult, data)
|
|
829
|
+
|
|
830
|
+
async def camera_provide_answer(self, node_id: int, endpoint_id: int, webrtc_session_id: int, sdp: str) -> None:
|
|
831
|
+
"""Send the SDP answer for a session started without ``sdp``.
|
|
832
|
+
|
|
833
|
+
Requires schema 14.
|
|
834
|
+
"""
|
|
835
|
+
await self.send_command(
|
|
836
|
+
APICommand.CAMERA_PROVIDE_ANSWER,
|
|
837
|
+
require_schema=14,
|
|
838
|
+
node_id=node_id,
|
|
839
|
+
endpoint_id=endpoint_id,
|
|
840
|
+
webrtc_session_id=webrtc_session_id,
|
|
841
|
+
sdp=sdp,
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
async def camera_provide_ice_candidates(
|
|
845
|
+
self,
|
|
846
|
+
node_id: int,
|
|
847
|
+
endpoint_id: int,
|
|
848
|
+
webrtc_session_id: int,
|
|
849
|
+
ice_candidates: Sequence[WebRTCIceCandidate | dict[str, Any]],
|
|
850
|
+
) -> None:
|
|
851
|
+
"""Send one or more ICE candidates to an existing session.
|
|
852
|
+
|
|
853
|
+
A candidate from a ``webrtc_callback`` ``ice_candidates`` event can be passed unchanged.
|
|
854
|
+
|
|
855
|
+
Requires schema 14.
|
|
856
|
+
"""
|
|
857
|
+
await self.send_command(
|
|
858
|
+
APICommand.CAMERA_PROVIDE_ICE_CANDIDATES,
|
|
859
|
+
require_schema=14,
|
|
860
|
+
node_id=node_id,
|
|
861
|
+
endpoint_id=endpoint_id,
|
|
862
|
+
webrtc_session_id=webrtc_session_id,
|
|
863
|
+
ice_candidates=[
|
|
864
|
+
asdict(candidate) if isinstance(candidate, WebRTCIceCandidate) else candidate
|
|
865
|
+
for candidate in ice_candidates
|
|
866
|
+
],
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
async def camera_stop_stream(
|
|
870
|
+
self, node_id: int, endpoint_id: int, webrtc_session_id: int
|
|
871
|
+
) -> CameraStopStreamResult:
|
|
872
|
+
"""End a WebRTC session; the stream allocation is kept.
|
|
873
|
+
|
|
874
|
+
Requires schema 14.
|
|
875
|
+
"""
|
|
876
|
+
data = await self.send_command(
|
|
877
|
+
APICommand.CAMERA_STOP_STREAM,
|
|
878
|
+
require_schema=14,
|
|
879
|
+
node_id=node_id,
|
|
880
|
+
endpoint_id=endpoint_id,
|
|
881
|
+
webrtc_session_id=webrtc_session_id,
|
|
882
|
+
)
|
|
883
|
+
return dataclass_from_dict(CameraStopStreamResult, data)
|
|
884
|
+
|
|
885
|
+
async def camera_snapshot(
|
|
886
|
+
self,
|
|
887
|
+
node_id: int,
|
|
888
|
+
endpoint_id: int,
|
|
889
|
+
max_resolution: CameraResolutionHint | None = None,
|
|
890
|
+
codec: str | None = None,
|
|
891
|
+
watermark_enabled: bool | None = None,
|
|
892
|
+
osd_enabled: bool | None = None,
|
|
893
|
+
) -> CameraSnapshotResult:
|
|
894
|
+
"""Capture one image. An argument left ``None`` is not sent.
|
|
895
|
+
|
|
896
|
+
The snapshot stream stays allocated; free it with ``camera_release_stream``.
|
|
897
|
+
|
|
898
|
+
Requires schema 14.
|
|
899
|
+
"""
|
|
900
|
+
optional = {
|
|
901
|
+
"max_resolution": max_resolution,
|
|
902
|
+
"codec": codec,
|
|
903
|
+
"watermark_enabled": watermark_enabled,
|
|
904
|
+
"osd_enabled": osd_enabled,
|
|
905
|
+
}
|
|
906
|
+
data = await self.send_command(
|
|
907
|
+
APICommand.CAMERA_SNAPSHOT,
|
|
908
|
+
require_schema=14,
|
|
909
|
+
node_id=node_id,
|
|
910
|
+
endpoint_id=endpoint_id,
|
|
911
|
+
**{key: value for key, value in optional.items() if value is not None},
|
|
912
|
+
)
|
|
913
|
+
return dataclass_from_dict(CameraSnapshotResult, data)
|
|
914
|
+
|
|
915
|
+
async def camera_release_stream(
|
|
916
|
+
self,
|
|
917
|
+
node_id: int,
|
|
918
|
+
endpoint_id: int,
|
|
919
|
+
kind: Literal["video", "audio", "snapshot"],
|
|
920
|
+
stream_id: int,
|
|
921
|
+
) -> None:
|
|
922
|
+
"""Deallocate a stream, whoever allocated it.
|
|
923
|
+
|
|
924
|
+
Raises ``CameraStreamInUse`` while the camera still references the stream.
|
|
925
|
+
|
|
926
|
+
Requires schema 14.
|
|
927
|
+
"""
|
|
928
|
+
await self.send_command(
|
|
929
|
+
APICommand.CAMERA_RELEASE_STREAM,
|
|
930
|
+
require_schema=14,
|
|
931
|
+
node_id=node_id,
|
|
932
|
+
endpoint_id=endpoint_id,
|
|
933
|
+
kind=kind,
|
|
934
|
+
stream_id=stream_id,
|
|
935
|
+
)
|
|
738
936
|
|
|
739
937
|
def _prepare_message(
|
|
740
938
|
self,
|
|
@@ -957,6 +1155,14 @@ class MatterClient:
|
|
|
957
1155
|
topology = dataclass_from_dict(NetworkTopology, msg.data)
|
|
958
1156
|
self._signal_event(EventType.NETWORK_TOPOLOGY_UPDATED, data=topology)
|
|
959
1157
|
return
|
|
1158
|
+
if msg.event in NODE_SCOPED_CAMERA_EVENTS:
|
|
1159
|
+
payload: Any = msg.data
|
|
1160
|
+
if msg.event == EventType.CAMERA_SESSION_ENDED:
|
|
1161
|
+
payload = dataclass_from_dict(CameraSessionEndedData, msg.data)
|
|
1162
|
+
elif msg.event == EventType.CAMERA_STREAM_EVICTED:
|
|
1163
|
+
payload = dataclass_from_dict(CameraStreamEvictedData, msg.data)
|
|
1164
|
+
self._signal_event(msg.event, data=payload, node_id=msg.data["node_id"])
|
|
1165
|
+
return
|
|
960
1166
|
# An event type unknown to this (older) client is passed through by parse_value as a raw
|
|
961
1167
|
# string; forwarding it would crash on `event.value` in _signal_event. Drop it instead so a
|
|
962
1168
|
# newer server emitting a new event type can never disconnect the client.
|
|
@@ -434,22 +434,70 @@ class MatterNode:
|
|
|
434
434
|
def _map_endpoint_parents(self) -> None:
|
|
435
435
|
"""Resolve each endpoint to its closest parent endpoint, split by parent kind.
|
|
436
436
|
|
|
437
|
+
The parents come from the server's `endpoint_parents` when it sends them, else they are
|
|
438
|
+
derived from the partsLists (see `_derive_endpoint_parents`). Only the endpoints of the
|
|
439
|
+
last snapshot take part.
|
|
440
|
+
|
|
441
|
+
A snapshot replaces the relation of an endpoint where it names a parent for it that decides
|
|
442
|
+
the relation: one that bridges or composes it, or with server parents any parent that
|
|
443
|
+
reports device types. Derived parents are a guess, so one that makes the endpoint neither
|
|
444
|
+
bridged nor a part only replaces the relation when it is the parent the endpoint already
|
|
445
|
+
had. Where a snapshot decides nothing - the endpoint unlisted or unreported, or its parent
|
|
446
|
+
without a Descriptor or device types - the endpoint keeps its relation until its own
|
|
447
|
+
removal or that of the parent is announced, so a consumer handling the announcement still
|
|
448
|
+
finds the device the endpoint is a part of. A kept relation that would close a cycle with a
|
|
449
|
+
named one is dropped.
|
|
450
|
+
"""
|
|
451
|
+
snapshot_ids = self._snapshot_endpoint_ids & self.endpoints.keys()
|
|
452
|
+
server_parents = self.node_data.endpoint_parents
|
|
453
|
+
if server_parents is None:
|
|
454
|
+
parents = self._derive_endpoint_parents(snapshot_ids)
|
|
455
|
+
else:
|
|
456
|
+
parents = {
|
|
457
|
+
child_id: parent_id
|
|
458
|
+
for child_id, parent_id in server_parents.items()
|
|
459
|
+
if child_id in snapshot_ids and (parent_id == ROOT_ENDPOINT_ID or parent_id in snapshot_ids)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
previous_composed = self._composed_endpoints
|
|
463
|
+
previous_bridge = self._bridge_parents
|
|
464
|
+
self._composed_endpoints = {}
|
|
465
|
+
self._bridge_parents = {}
|
|
466
|
+
decided = {
|
|
467
|
+
child_id for child_id, parent_id in parents.items() if self._map_endpoint_parent(child_id, parent_id)
|
|
468
|
+
}
|
|
469
|
+
related = self._composed_endpoints | self._bridge_parents
|
|
470
|
+
for relations, previous in (
|
|
471
|
+
(self._composed_endpoints, previous_composed),
|
|
472
|
+
(self._bridge_parents, previous_bridge),
|
|
473
|
+
):
|
|
474
|
+
for child_id, parent_id in sorted(previous.items()):
|
|
475
|
+
if child_id in related or (
|
|
476
|
+
child_id in decided and (server_parents is not None or parents[child_id] == parent_id)
|
|
477
|
+
):
|
|
478
|
+
continue
|
|
479
|
+
if _reaches(parent_id, child_id, related):
|
|
480
|
+
LOGGER.warning(
|
|
481
|
+
"Dropping parent relation that a snapshot made cyclic: Node %s, endpoint %s below %s",
|
|
482
|
+
self.node_id,
|
|
483
|
+
child_id,
|
|
484
|
+
parent_id,
|
|
485
|
+
)
|
|
486
|
+
continue
|
|
487
|
+
relations[child_id] = parent_id
|
|
488
|
+
related[child_id] = parent_id
|
|
489
|
+
|
|
490
|
+
self._report_bridged_devices_without_info()
|
|
491
|
+
|
|
492
|
+
def _derive_endpoint_parents(self, snapshot_ids: set[int]) -> dict[int, int]:
|
|
493
|
+
"""Derive the closest parent of each endpoint from the partsLists of the snapshot.
|
|
494
|
+
|
|
437
495
|
An endpoint's partsList may enumerate its whole family - every descendant, not just
|
|
438
496
|
the direct children - which is the pattern a bridge uses. Of the endpoints listing a
|
|
439
497
|
given endpoint, the closest one is therefore the endpoint that lists none of the
|
|
440
498
|
others - on contradictory lists the smaller family wins, then the lower endpoint
|
|
441
499
|
number - and a parent is only accepted while the relations stay a tree.
|
|
442
|
-
|
|
443
|
-
Only the endpoints of the last snapshot take part. A snapshot replaces the relation of an
|
|
444
|
-
endpoint only where it names a parent for it: one that bridges or composes it, or the
|
|
445
|
-
parent it already had, whose classification then wins. Where it names none - the endpoint
|
|
446
|
-
unlisted or unreported, its parent without a Descriptor or device types, or listed only by
|
|
447
|
-
endpoints that make it neither bridged nor a part - the endpoint keeps its relation until
|
|
448
|
-
its own removal or that of the parent is announced, so a consumer handling the
|
|
449
|
-
announcement still finds the device the endpoint is a part of. A kept relation that would
|
|
450
|
-
close a cycle with a named one is dropped.
|
|
451
500
|
"""
|
|
452
|
-
snapshot_ids = self._snapshot_endpoint_ids & self.endpoints.keys()
|
|
453
501
|
families: dict[int, set[int]] = {}
|
|
454
502
|
for endpoint in self.endpoints.values():
|
|
455
503
|
if endpoint.endpoint_id not in snapshot_ids:
|
|
@@ -494,34 +542,7 @@ class MatterNode:
|
|
|
494
542
|
continue
|
|
495
543
|
parents[child_id] = parent_id
|
|
496
544
|
break
|
|
497
|
-
|
|
498
|
-
previous_composed = self._composed_endpoints
|
|
499
|
-
previous_bridge = self._bridge_parents
|
|
500
|
-
self._composed_endpoints = {}
|
|
501
|
-
self._bridge_parents = {}
|
|
502
|
-
decided = {
|
|
503
|
-
child_id for child_id, parent_id in parents.items() if self._map_endpoint_parent(child_id, parent_id)
|
|
504
|
-
}
|
|
505
|
-
related = self._composed_endpoints | self._bridge_parents
|
|
506
|
-
for relations, previous in (
|
|
507
|
-
(self._composed_endpoints, previous_composed),
|
|
508
|
-
(self._bridge_parents, previous_bridge),
|
|
509
|
-
):
|
|
510
|
-
for child_id, parent_id in sorted(previous.items()):
|
|
511
|
-
if child_id in related or (child_id in decided and parents[child_id] == parent_id):
|
|
512
|
-
continue
|
|
513
|
-
if _reaches(parent_id, child_id, related):
|
|
514
|
-
LOGGER.warning(
|
|
515
|
-
"Dropping parent relation that a snapshot made cyclic: Node %s, endpoint %s below %s",
|
|
516
|
-
self.node_id,
|
|
517
|
-
child_id,
|
|
518
|
-
parent_id,
|
|
519
|
-
)
|
|
520
|
-
continue
|
|
521
|
-
relations[child_id] = parent_id
|
|
522
|
-
related[child_id] = parent_id
|
|
523
|
-
|
|
524
|
-
self._report_bridged_devices_without_info()
|
|
545
|
+
return parents
|
|
525
546
|
|
|
526
547
|
def _report_bridged_devices_without_info(self) -> None:
|
|
527
548
|
"""Warn about bridged devices the bridge does not describe, once per endpoint.
|
|
@@ -550,14 +571,13 @@ class MatterNode:
|
|
|
550
571
|
bridged device; any other child stands alone, whether or not the Aggregator is itself a
|
|
551
572
|
Bridged Node (so ids derived from the compose parent match older clients). Below any other endpoint, a child
|
|
552
573
|
is a part of it - also when the device types of that endpoint are unknown to this client,
|
|
553
|
-
so only a parent
|
|
574
|
+
so only a parent without a Descriptor or device types decides nothing.
|
|
554
575
|
"""
|
|
555
576
|
if parent_id == ROOT_ENDPOINT_ID:
|
|
556
577
|
return True
|
|
557
578
|
parent = self.endpoints[parent_id]
|
|
558
579
|
descriptor = parent.get_cluster(Clusters.Descriptor)
|
|
559
|
-
|
|
560
|
-
if not descriptor.deviceTypeList:
|
|
580
|
+
if descriptor is None or not descriptor.deviceTypeList:
|
|
561
581
|
return False
|
|
562
582
|
if RootNode in parent.device_types:
|
|
563
583
|
return True
|