-
Notifications
You must be signed in to change notification settings - Fork 516
WWSTCERT-8138: Add support for frient Zigbee Range Extender #2430
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 1 commit
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 |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| name: range-extender-battery-source | ||
| components: | ||
| - id: main | ||
| capabilities: | ||
| - id: firmwareUpdate | ||
| version: 1 | ||
| - id: refresh | ||
| version: 1 | ||
| - id: battery | ||
| version: 1 | ||
| - id: powerSource | ||
| version: 1 | ||
| config: | ||
| values: | ||
| - key: "powerSource.value" | ||
| enabledValues: | ||
| - battery | ||
| - mains | ||
| categories: | ||
| - name: Networking |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| -- Copyright 2025 SmartThings | ||
| -- | ||
| -- Licensed under the Apache License, Version 2.0 (the "License"); | ||
| -- you may not use this file except in compliance with the License. | ||
| -- You may obtain a copy of the License at | ||
| -- | ||
| -- http://www.apache.org/licenses/LICENSE-2.0 | ||
| -- | ||
| -- Unless required by applicable law or agreed to in writing, software | ||
| -- distributed under the License is distributed on an "AS IS" BASIS, | ||
| -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| -- See the License for the specific language governing permissions and | ||
| -- limitations under the License. | ||
|
|
||
| local capabilities = require "st.capabilities" | ||
| local clusters = require "st.zigbee.zcl.clusters" | ||
| local battery_defaults = require "st.zigbee.defaults.battery_defaults" | ||
|
|
||
| local IASZone = clusters.IASZone | ||
| local Basic = clusters.Basic | ||
| local PowerConfiguration = clusters.PowerConfiguration | ||
|
|
||
| local function generate_event_from_zone_status(driver, device, zone_status, zigbee_message) | ||
| device:emit_event_for_endpoint( | ||
| zigbee_message.address_header.src_endpoint.value, | ||
| zone_status:is_ac_mains_fault_set() and capabilities.powerSource.powerSource.battery() or capabilities.powerSource.powerSource.mains() | ||
| ) | ||
| end | ||
|
|
||
| local function ias_zone_status_attr_handler(driver, device, zone_status, zb_rx) | ||
| generate_event_from_zone_status(driver, device, zone_status, zb_rx) | ||
| end | ||
|
|
||
| local function ias_zone_status_change_handler(driver, device, zb_rx) | ||
| generate_event_from_zone_status(driver, device, zb_rx.body.zcl_body.zone_status, zb_rx) | ||
| end | ||
|
|
||
| local function device_added(driver, device) | ||
| device:emit_event(capabilities.powerSource.powerSource.mains()) | ||
| end | ||
|
|
||
| local function device_init(driver, device) | ||
| battery_defaults.build_linear_voltage_init(3.3, 4.1)(driver, device) | ||
| end | ||
|
|
||
| local function do_refresh(driver, device) | ||
| device:send(Basic.attributes.ZCLVersion:read(device)) | ||
| device:send(PowerConfiguration.attributes.BatteryVoltage:read(device)) | ||
| device:send(IASZone.attributes.ZoneStatus:read(device)) | ||
|
Comment on lines
+46
to
+47
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. Seems like the base driver's 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 tried to delete a custom refresh handler, but then it is only ZCLVersion attribute from Basic cluster that is being refreshed. Unit tests for refreshing fail when there is any attribute other than ZCLVersion. 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. Ah, I see. This is because for this driver, the IAS Zone attribute has not been configured. So the driver would also not be set up to receive reports from this attribute as-is, either. In the base driver, you'll need to add this line in roughly the same place. 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. After that is in place, your test results will be different. |
||
| end | ||
|
|
||
| local frient_range_extender = { | ||
| NAME = "frient Range Extender", | ||
| lifecycle_handlers = { | ||
| added = device_added, | ||
| init = device_init | ||
| }, | ||
| capability_handlers = { | ||
| [capabilities.refresh.ID] = { | ||
| [capabilities.refresh.commands.refresh.NAME] = do_refresh, | ||
| } | ||
| }, | ||
| zigbee_handlers = { | ||
| attr = { | ||
| [IASZone.ID] = { | ||
| [IASZone.attributes.ZoneStatus.ID] = ias_zone_status_attr_handler | ||
| } | ||
| }, | ||
| cluster = { | ||
| [IASZone.ID] = { | ||
| [IASZone.client.commands.ZoneStatusChangeNotification.ID] = ias_zone_status_change_handler | ||
| } | ||
| } | ||
| }, | ||
| can_handle = function(opts, driver, device, ...) | ||
| return device:get_manufacturer() == "frient A/S" and (device:get_model() == "REXZB-111") | ||
| end | ||
| } | ||
|
|
||
| return frient_range_extender | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,201 @@ | ||
| -- Copyright 2025 SmartThings | ||
| -- | ||
| -- Licensed under the Apache License, Version 2.0 (the "License"); | ||
| -- you may not use this file except in compliance with the License. | ||
| -- You may obtain a copy of the License at | ||
| -- | ||
| -- http://www.apache.org/licenses/LICENSE-2.0 | ||
| -- | ||
| -- Unless required by applicable law or agreed to in writing, software | ||
| -- distributed under the License is distributed on an "AS IS" BASIS, | ||
| -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| -- See the License for the specific language governing permissions and | ||
| -- limitations under the License. | ||
|
|
||
| -- Mock out globals | ||
| local test = require "integration_test" | ||
| local clusters = require "st.zigbee.zcl.clusters" | ||
| local capabilities = require "st.capabilities" | ||
| local t_utils = require "integration_test.utils" | ||
| local zigbee_test_utils = require "integration_test.zigbee_test_utils" | ||
|
|
||
| local Basic = clusters.Basic | ||
| local IASZone = clusters.IASZone | ||
| local PowerConfiguration = clusters.PowerConfiguration | ||
| local ZoneStatusAttribute = IASZone.attributes.ZoneStatus | ||
|
|
||
| local mock_device = test.mock_device.build_test_zigbee_device( | ||
| { | ||
| profile = t_utils.get_profile_definition("range-extender-battery-source.yml"), | ||
| zigbee_endpoints = { | ||
| [0x01] = { | ||
| id = 0x01, | ||
| manufacturer = "frient A/S", | ||
| model = "REXZB-111", | ||
| server_clusters = { Basic.ID, IASZone.ID, PowerConfiguration.ID } | ||
| } | ||
| } | ||
| } | ||
| ) | ||
|
|
||
| zigbee_test_utils.prepare_zigbee_env_info() | ||
| local function test_init() | ||
| test.mock_device.add_test_device(mock_device) | ||
| end | ||
|
|
||
| test.set_test_init_function(test_init) | ||
|
|
||
| test.register_coroutine_test( | ||
| "Refresh necessary attributes", | ||
| function() | ||
| test.socket.zigbee:__set_channel_ordering("relaxed") | ||
| test.socket.capability:__queue_receive({ mock_device.id, { capability = "refresh", component = "main", command = "refresh", args = {} } }) | ||
| test.socket.zigbee:__expect_send( | ||
| { | ||
| mock_device.id, | ||
| Basic.attributes.ZCLVersion:read(mock_device) | ||
| } | ||
| ) | ||
| test.socket.zigbee:__expect_send( | ||
| { | ||
| mock_device.id, | ||
| IASZone.attributes.ZoneStatus:read(mock_device) | ||
| } | ||
| ) | ||
| test.socket.zigbee:__expect_send( | ||
| { | ||
| mock_device.id, | ||
| PowerConfiguration.attributes.BatteryVoltage:read(mock_device) | ||
| } | ||
| ) | ||
| end | ||
| ) | ||
|
|
||
| test.register_coroutine_test( | ||
| "lifecycles - init and doConfigure test", | ||
| function() | ||
| test.socket.device_lifecycle:__queue_receive({ mock_device.id, "init" }) | ||
| test.wait_for_events() | ||
| test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" }) | ||
|
|
||
|
|
||
| test.socket.zigbee:__expect_send({ | ||
| mock_device.id, | ||
| Basic.attributes.ZCLVersion:read( mock_device ) | ||
| }) | ||
|
|
||
| test.socket.zigbee:__expect_send({ | ||
| mock_device.id, | ||
| PowerConfiguration.attributes.BatteryVoltage:read( mock_device ) | ||
| }) | ||
|
|
||
| test.socket.zigbee:__expect_send({ | ||
| mock_device.id, | ||
| IASZone.attributes.ZoneStatus:read( mock_device ) | ||
| }) | ||
|
|
||
| test.socket.zigbee:__expect_send({ | ||
| mock_device.id, | ||
| zigbee_test_utils.build_bind_request( | ||
| mock_device, | ||
| zigbee_test_utils.mock_hub_eui, | ||
| PowerConfiguration.ID | ||
| ) | ||
| }) | ||
|
|
||
| test.socket.zigbee:__expect_send({ | ||
| mock_device.id, | ||
| PowerConfiguration.attributes.BatteryVoltage:configure_reporting( | ||
| mock_device, | ||
| 30, | ||
| 21600, | ||
| 1 | ||
| ) | ||
| }) | ||
|
|
||
| mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" }) | ||
|
|
||
| end | ||
| ) | ||
|
|
||
| test.register_message_test( | ||
| "Power source / mains should be handled", | ||
| { | ||
| { | ||
| channel = "zigbee", | ||
| direction = "receive", | ||
| message = { mock_device.id, ZoneStatusAttribute:build_test_attr_report(mock_device, 0x0001) } | ||
| }, | ||
| { | ||
| channel = "capability", | ||
| direction = "send", | ||
| message = mock_device:generate_test_message("main", capabilities.powerSource.powerSource.mains()) | ||
| } | ||
| } | ||
| ) | ||
|
|
||
| test.register_message_test( | ||
| "Power source / battery should be handled", | ||
| { | ||
| { | ||
| channel = "zigbee", | ||
| direction = "receive", | ||
| message = { mock_device.id, ZoneStatusAttribute:build_test_attr_report(mock_device, 0x0081) } | ||
| }, | ||
| { | ||
| channel = "capability", | ||
| direction = "send", | ||
| message = mock_device:generate_test_message("main", capabilities.powerSource.powerSource.battery()) | ||
| } | ||
| } | ||
| ) | ||
|
|
||
| test.register_message_test( | ||
| "Min battery voltage report should be handled", | ||
| { | ||
| { | ||
| channel = "zigbee", | ||
| direction = "receive", | ||
| message = { mock_device.id, PowerConfiguration.attributes.BatteryVoltage:build_test_attr_report(mock_device, 33) } | ||
| }, | ||
| { | ||
| channel = "capability", | ||
| direction = "send", | ||
| message = mock_device:generate_test_message("main", capabilities.battery.battery(0)) | ||
| } | ||
| } | ||
| ) | ||
|
|
||
| test.register_message_test( | ||
| "Medium battery voltage report should be handled", | ||
| { | ||
| { | ||
| channel = "zigbee", | ||
| direction = "receive", | ||
| message = { mock_device.id, PowerConfiguration.attributes.BatteryVoltage:build_test_attr_report(mock_device, 37) } | ||
| }, | ||
| { | ||
| channel = "capability", | ||
| direction = "send", | ||
| message = mock_device:generate_test_message("main", capabilities.battery.battery(50)) | ||
| } | ||
| } | ||
| ) | ||
|
|
||
| test.register_message_test( | ||
| "Max battery voltage report should be handled", | ||
| { | ||
| { | ||
| channel = "zigbee", | ||
| direction = "receive", | ||
| message = { mock_device.id, PowerConfiguration.attributes.BatteryVoltage:build_test_attr_report(mock_device, 41) } | ||
| }, | ||
| { | ||
| channel = "capability", | ||
| direction = "send", | ||
| message = mock_device:generate_test_message("main", capabilities.battery.battery(100)) | ||
| } | ||
| } | ||
| ) | ||
|
|
||
| test.run_registered_tests() |
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 isn't being handled, so why does it need to be refreshed?