-
Notifications
You must be signed in to change notification settings - Fork 556
Hager rotary dimmer implementation #3215
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
Open
JanJakubiszyn
wants to merge
1
commit into
SmartThingsCommunity:main
Choose a base branch
from
JanJakubiszyn:Hager_waasys_rotrary_dim
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -23,6 +23,7 @@ local PRODUCT_ID = { | |||||
| SWITCH_2G = 0x0006, | ||||||
| PIR_1_1M = 0x0007, | ||||||
| PIR_2_2M = 0x000A, | ||||||
| ROTARY_DIMMER = 0x000C | ||||||
| } | ||||||
|
|
||||||
| if version.api < 16 then | ||||||
|
|
@@ -33,6 +34,12 @@ local function subscribe (device, endpoint_id, cluster_id, attr_id, event_id) | |||||
| device:send(cluster_base.subscribe(device, endpoint_id, cluster_id, attr_id, event_id)) | ||||||
| end | ||||||
|
|
||||||
| local function is_standalone_device (device) | ||||||
| if switch_utils.get_product_override_field(device, "is_standalone") then | ||||||
| return true | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| local function get_parent (driver, device) | ||||||
| return driver:get_device_info(device:get_field(PARENT_ID) or nil) | ||||||
| end | ||||||
|
|
@@ -51,12 +58,12 @@ local function get_matter_device(device) | |||||
| return nil | ||||||
| end | ||||||
|
|
||||||
| local function extract_reported_endpoints(ib) | ||||||
| local function extract_reported_endpoints(ib, device) | ||||||
| local eps = {} | ||||||
| if ib.data and ib.data.elements then | ||||||
| for _, el in ipairs(ib.data.elements) do | ||||||
| local ep_id = el.value | ||||||
| if type(ep_id) == "number" and ep_id ~= 1 and ep_id ~= 2 then | ||||||
| if type(ep_id) == "number" and ep_id ~= 1 and ep_id ~= 2 or is_standalone_device(device) then | ||||||
| table.insert(eps, ep_id) | ||||||
| end | ||||||
| end | ||||||
|
|
@@ -188,11 +195,16 @@ local function info_changed(driver, device, event, args) | |||||
| return | ||||||
| end | ||||||
|
|
||||||
| if is_standalone_device(device) then | ||||||
| device:subscribe() | ||||||
| return | ||||||
| end | ||||||
|
|
||||||
| if device.profile.id ~= args.old_st_store.profile.id or device.network_type == device_lib.NETWORK_TYPE_CHILD then | ||||||
| local parent = device:get_parent_device() | ||||||
| local matter_device = get_matter_device(parent) | ||||||
| local map = {} | ||||||
| device.thread:call_with_delay(2, function() | ||||||
| local parent = device:get_parent_device() | ||||||
| local matter_device = get_matter_device(parent) | ||||||
| local map = {} | ||||||
| if device:supports_capability(capabilities.button) then | ||||||
| local button_eps = parent:get_field(BUTTON_EPS) | ||||||
| local clean_eps = {} | ||||||
|
|
@@ -225,7 +237,7 @@ local function info_changed(driver, device, event, args) | |||||
| subscribe(device, nil, clusters.WindowCovering.ID, clusters.WindowCovering.attributes.OperationalStatus.ID) | ||||||
| subscribe(device, nil, clusters.WindowCovering.ID, clusters.WindowCovering.attributes.CurrentPositionLiftPercent100ths.ID, nil) | ||||||
| end | ||||||
| if device.id == matter_device.id then | ||||||
| if not is_standalone_device(device) and device.id == matter_device.id then | ||||||
| parent:set_field(fields.COMPONENT_TO_ENDPOINT_MAP, map, { persist = true }) | ||||||
| matter_device:set_field(fields.COMPONENT_TO_ENDPOINT_MAP, map, { persist = true }) | ||||||
| end | ||||||
|
|
@@ -234,6 +246,13 @@ local function info_changed(driver, device, event, args) | |||||
| end | ||||||
|
|
||||||
| local function device_init (driver, device) | ||||||
| if is_standalone_device(device) then | ||||||
| device:set_find_child(switch_utils.find_child) | ||||||
| device:set_field(PARENT_ID, device.id) | ||||||
| device:set_field(MATTER_DEVICE_ID, device.id) | ||||||
| subscribe(device, nil, clusters.Descriptor.ID, clusters.Descriptor.attributes.PartsList.ID, nil) | ||||||
| return | ||||||
| end | ||||||
| if device.network_type ~= device_lib.NETWORK_TYPE_MATTER then | ||||||
| device.thread:call_with_delay(4, function() | ||||||
| info_changed(driver, device, nil, { | ||||||
|
|
@@ -265,7 +284,6 @@ local function device_init (driver, device) | |||||
| end) | ||||||
| end) | ||||||
| end | ||||||
|
|
||||||
| device:set_component_to_endpoint_fn(switch_utils.component_to_endpoint) | ||||||
| device:set_endpoint_to_component_fn(switch_utils.endpoint_to_component) | ||||||
| end | ||||||
|
|
@@ -274,21 +292,26 @@ local function handle_descriptor_report(driver, device, ib, response) | |||||
| local product_id = device.manufacturer_info and device.manufacturer_info.product_id | ||||||
| local parent = get_parent(driver, device) | ||||||
|
|
||||||
| if not parent or ib.endpoint_id ~= 2 then | ||||||
| return | ||||||
| if is_standalone_device(device) then | ||||||
| if ib.endpoint_id ~= 0 then | ||||||
| return | ||||||
| end | ||||||
| else | ||||||
| if ib.endpoint_id ~= 2 or not parent then | ||||||
| return | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| local matter_device = get_matter_device(device) | ||||||
| local matter_device = get_matter_device(device) or device | ||||||
| local new_eps = extract_reported_endpoints(ib, device) or {} | ||||||
|
|
||||||
| local new_eps = extract_reported_endpoints(ib) or {} | ||||||
| table.sort(new_eps) | ||||||
| local removed_eps, added_eps = detect_endpoint_changes(device, new_eps) | ||||||
|
|
||||||
| local removed_eps, added_eps = detect_endpoint_changes(device, new_eps) | ||||||
| device:set_field(ACTIVE_EPS, new_eps, { persist = true }) | ||||||
|
|
||||||
| for _, ep_id in ipairs(added_eps or {}) do | ||||||
| subscribe(parent, ep_id, clusters.Descriptor.ID, clusters.Descriptor.attributes.DeviceTypeList.ID, nil) | ||||||
|
|
||||||
| if product_id == PRODUCT_ID.SWITCH_1G and ep_id == 3 then | ||||||
| matter_device:try_update_metadata({ profile = "light-binary" }) | ||||||
| elseif product_id == PRODUCT_ID.SWITCH_2G then | ||||||
|
|
@@ -347,6 +370,7 @@ local function device_type_handler (driver, device, ib) | |||||
| end | ||||||
|
|
||||||
| local parent = get_parent(driver, device) | ||||||
|
|
||||||
| if not parent then | ||||||
| return | ||||||
| end | ||||||
|
|
@@ -356,10 +380,19 @@ local function device_type_handler (driver, device, ib) | |||||
| local new_btn_eps = {} | ||||||
| local ep_id = ib.endpoint_id | ||||||
| local value = ib.data.elements | ||||||
| local reports_dimmable = false | ||||||
|
|
||||||
| for _, element in ipairs(value) do | ||||||
|
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.
Suggested change
|
||||||
| local device_type_field = element.elements.device_type | ||||||
| if device_type_field and device_type_field.value == fields.DEVICE_TYPE_ID.LIGHT.DIMMABLE then | ||||||
| reports_dimmable = true | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| for _, v in ipairs(stored_btn_eps) do | ||||||
| table.insert(new_btn_eps, v) | ||||||
| end | ||||||
|
|
||||||
| for _, element in ipairs(value) do | ||||||
| local device_type_field = element.elements.device_type | ||||||
| local device_type_id = device_type_field and device_type_field.value | ||||||
|
|
@@ -396,11 +429,17 @@ local function device_type_handler (driver, device, ib) | |||||
| driver:try_delete_device(ep_id) | ||||||
| end | ||||||
| return | ||||||
| elseif ep_id == 1 and device.manufacturer_info.product_id == PRODUCT_ID.ROTARY_DIMMER then | ||||||
| if not reports_dimmable then | ||||||
| device:try_update_metadata({ profile = "light-binary" }) | ||||||
| end | ||||||
| end | ||||||
| create_child(driver, parent, { ep_id }, 1, assign_profile_for_endpoint(device_type_id)) | ||||||
| elseif device_type_id == fields.DEVICE_TYPE_ID.LIGHT.DIMMABLE then | ||||||
| if ep_id == 4 and device.manufacturer_info.product_id == PRODUCT_ID.SWITCH_1G then | ||||||
| return | ||||||
| elseif ep_id == 1 and device.manufacturer_info.product_id == PRODUCT_ID.ROTARY_DIMMER then | ||||||
| device:try_update_metadata({ profile = "light-level" }) | ||||||
| end | ||||||
| create_child(driver, parent, { ep_id }, 1, assign_profile_for_endpoint(device_type_id)) | ||||||
| elseif device_type_id == fields.DEVICE_TYPE_ID.WINDOW_COVERING then | ||||||
|
|
@@ -433,7 +472,7 @@ local function do_configure (driver, device) | |||||
| device:try_update_metadata({ profile = "4-button" }) | ||||||
| elseif #bt_eps == 2 then | ||||||
| device:try_update_metadata({ profile = "2-button" }) | ||||||
| elseif #lvl_eps > 0 and product_id == PRODUCT_ID.SWITCH_1G then | ||||||
| elseif #lvl_eps > 0 and product_id == PRODUCT_ID.SWITCH_1G or is_standalone_device(device) then | ||||||
| device:try_update_metadata({ profile = "light-level" }) | ||||||
| end | ||||||
| device:set_field(BUTTON_EPS, bt_eps, { persist = true }) | ||||||
|
|
||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
what does this function imply?
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.
This function is created to separate standalone devices which are from WAASYS series but do not utilize the matter bridge device type. They differ in the endpoint architecture and have to be handled in a different way but they also support manually set functions like adding a child device responsible for remote light control.
This function allows to determine this type of device and eliminate/include them the driver behavior.
for example, standalone devices do not require mapping done in the info_changed function.
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.
So you are saying that these are just devices from Hager, rather than bridged devices from the Hager Bridge? I am curious, but I also think this would be a worthwhile thing to explain in a comment, since
is_standalonedoes not express a lot (in my opinion) about what is being checked, especially when the internals are extremely device specific.I also suggest that you add some commentary in the function itself to explain what is being checked, since the endpoint id checks are not understandable without Hager-specific context.
Uh oh!
There was an error while loading. Please reload this page.
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.
Yes, they are just simple Hager devices without the bridge functionality.
I will change the function name to "is_non_bridge_type" and add comments for better clarity
and understanding of the architectural differences between them.
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'd maybe just check then if the device is 1. an Aggregator device type and 2. is a Matter (not EDGE child) device? Or something like that. If it isn't, then we'd know it is a non-bridge Matter device. I am not sure we need this extremely specific endpoint checking to know whether it is a regular device or not