Repository navigation
Matter Camera: Exclude audioVolume on unusable volume range
#3242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9866fc1
3ffcbca
0750c21
b6314ae
d29da26
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,34 +57,48 @@ end | |
|
|
||
| function CameraAttributeHandlers.volume_level_handler(driver, device, ib, response) | ||
| local component = device:endpoint_to_component(ib) | ||
| local max_volume = device:get_field(camera_fields.MAX_VOLUME_LEVEL .. "_" .. component) or camera_fields.ABS_VOL_MAX | ||
| local min_volume = device:get_field(camera_fields.MIN_VOLUME_LEVEL .. "_" .. component) or camera_fields.ABS_VOL_MIN | ||
| -- Convert from [min_volume, max_volume] to [0, 100] before emitting capability | ||
| local max_volume = camera_utils.get_field_for_component(device, camera_fields.MAX_VOLUME_LEVEL, component) or camera_fields.ABS_VOL_MAX | ||
| local min_volume = camera_utils.get_field_for_component(device, camera_fields.MIN_VOLUME_LEVEL, component) or camera_fields.ABS_VOL_MIN | ||
| local limited_range = max_volume - min_volume | ||
| if limited_range <= 0 then | ||
| device:emit_event_for_endpoint(ib, capabilities.audioVolume.volume(0)) | ||
| return | ||
| end | ||
| -- Convert from [min_volume, max_volume] to [0, 100] before emitting capability | ||
| local normalized_volume = utils.round((ib.data.value - min_volume) * 100.0 / limited_range) | ||
| device:emit_event_for_endpoint(ib, capabilities.audioVolume.volume(normalized_volume)) | ||
| end | ||
|
|
||
| function CameraAttributeHandlers.max_volume_level_handler(driver, device, ib, response) | ||
| local component = device:endpoint_to_component(ib) | ||
| local max_volume = ib.data.value | ||
| local min_volume = device:get_field(camera_fields.MIN_VOLUME_LEVEL .. "_" .. component) | ||
| if max_volume > camera_fields.ABS_VOL_MAX or (min_volume and max_volume <= min_volume) then | ||
| local min_volume = camera_utils.get_field_for_component(device, camera_fields.MIN_VOLUME_LEVEL, component) | ||
| -- min == max is a valid (non-adjustable) range per the Matter spec, so only max < min is malformed; | ||
| -- an out-of-bounds value is clamped, but a malformed relationship is only logged and left as-is, | ||
| -- since volume_level_handler already guards against a non-positive range. | ||
| if max_volume > camera_fields.ABS_VOL_MAX then | ||
| device.log.warn(string.format("Device reported invalid maximum (%d) %s volume level range value", ib.data.value, component)) | ||
| max_volume = camera_fields.ABS_VOL_MAX | ||
| elseif min_volume and max_volume < min_volume then | ||
| device.log.warn(string.format("Device reported invalid maximum (%d) %s volume level range value", ib.data.value, component)) | ||
| end | ||
| device:set_field(camera_fields.MAX_VOLUME_LEVEL .. "_" .. component, max_volume) | ||
| camera_utils.set_field_for_component(device, camera_fields.MAX_VOLUME_LEVEL, component, max_volume) | ||
| camera_cfg.reconcile_profile_and_capabilities(device) | ||
| end | ||
|
|
||
| function CameraAttributeHandlers.min_volume_level_handler(driver, device, ib, response) | ||
| local component = device:endpoint_to_component(ib) | ||
| local min_volume = ib.data.value | ||
| local max_volume = device:get_field(camera_fields.MAX_VOLUME_LEVEL .. "_" .. component) | ||
| if min_volume < camera_fields.ABS_VOL_MIN or (max_volume and min_volume >= max_volume) then | ||
| local max_volume = camera_utils.get_field_for_component(device, camera_fields.MAX_VOLUME_LEVEL, component) | ||
| -- See max_volume_level_handler: min == max is valid, only min > max is malformed and only logged. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same thing here about unnecessary comment |
||
| if min_volume < camera_fields.ABS_VOL_MIN then | ||
| device.log.warn(string.format("Device reported invalid minimum (%d) %s volume level range value", ib.data.value, component)) | ||
| min_volume = camera_fields.ABS_VOL_MIN | ||
| elseif max_volume and min_volume > max_volume then | ||
| device.log.warn(string.format("Device reported invalid minimum (%d) %s volume level range value", ib.data.value, component)) | ||
| end | ||
| device:set_field(camera_fields.MIN_VOLUME_LEVEL .. "_" .. component, min_volume) | ||
| camera_utils.set_field_for_component(device, camera_fields.MIN_VOLUME_LEVEL, component, min_volume) | ||
|
hcarter-775 marked this conversation as resolved.
|
||
| camera_cfg.reconcile_profile_and_capabilities(device) | ||
| end | ||
|
|
||
| function CameraAttributeHandlers.status_light_enabled_handler(driver, device, ib, response) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -82,6 +82,14 @@ function CameraUtils.feature_supported(device, cluster_id, feature_flag) | |
| return #device:get_endpoints(cluster_id, { feature_bitmap = feature_flag }) > 0 | ||
| end | ||
|
|
||
| function CameraUtils.get_field_for_component(device, field, component) | ||
| return device:get_field(string.format("%s_%s", field, component)) | ||
| end | ||
|
|
||
| function CameraUtils.set_field_for_component(device, field, component, value, additional_params) | ||
| device:set_field(string.format("%s_%s", field, component), value, additional_params) | ||
| end | ||
|
|
||
| function CameraUtils.update_supported_attributes(device, ib, capability, attribute) | ||
| local attribute_set = device:get_latest_state( | ||
| camera_fields.profile_components.main, capability.ID, capability.supportedAttributes.NAME | ||
|
|
@@ -212,18 +220,22 @@ function CameraUtils.subscribe(device) | |
| clusters.CameraAvSettingsUserLevelManagement.attributes.TiltMax, | ||
| clusters.CameraAvSettingsUserLevelManagement.attributes.TiltMin | ||
| }, | ||
| -- SpeakerMaxLevel/MinLevel and MicrophoneMaxLevel/MinLevel are subscribed alongside audioMute | ||
| -- (unconditional on the AUDIO/SPEAKER feature) rather than audioVolume, since audioVolume's own | ||
| -- presence in the profile depends on first discovering these values -- gating their subscription | ||
| -- on audioVolume already being present would make that discovery impossible. | ||
|
Comment on lines
+223
to
+226
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a bit of work (kinda outdated but not too much I don't think) trying to solve this exact problem a little more clearly (that is, adding the ability to subscribe to attributes/events based on other values besides a capability.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That looks neat! We can move to those |
||
| [capabilities.audioMute.ID] = { | ||
| clusters.CameraAvStreamManagement.attributes.SpeakerMuted, | ||
| clusters.CameraAvStreamManagement.attributes.MicrophoneMuted | ||
| }, | ||
| [capabilities.audioVolume.ID] = { | ||
| clusters.CameraAvStreamManagement.attributes.SpeakerVolumeLevel, | ||
| clusters.CameraAvStreamManagement.attributes.MicrophoneMuted, | ||
| clusters.CameraAvStreamManagement.attributes.SpeakerMaxLevel, | ||
| clusters.CameraAvStreamManagement.attributes.SpeakerMinLevel, | ||
| clusters.CameraAvStreamManagement.attributes.MicrophoneVolumeLevel, | ||
| clusters.CameraAvStreamManagement.attributes.MicrophoneMaxLevel, | ||
| clusters.CameraAvStreamManagement.attributes.MicrophoneMinLevel | ||
| }, | ||
| [capabilities.audioVolume.ID] = { | ||
| clusters.CameraAvStreamManagement.attributes.SpeakerVolumeLevel, | ||
| clusters.CameraAvStreamManagement.attributes.MicrophoneVolumeLevel | ||
| }, | ||
| [capabilities.mode.ID] = { | ||
| clusters.CameraAvStreamManagement.attributes.StatusLightBrightness | ||
| }, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove comment? Seems unnecessary to mention this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally think the comment is useful since it documents atypical behavior (min == max being allowed)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think that part is fine. Mostly I was mentioning the extra stuff about only logging the malformed relationship, which is explicitly written in code just below. No strong feelings, but that is what I was primarily pointing out