Skip to content
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

Add zigbee-button for Vimar xx591 (Wall-mounted Remote Control) #1508

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions drivers/SmartThings/zigbee-button/fingerprints.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
zigbeeManufacturer:
- id: "Vimar/xx591-remote-control"
deviceLabel: Vimar Remote Control
manufacturer: Vimar
model: "RemoteControl_v1.0"
deviceProfileName: two-buttons-no-fw-update
- id: "LUMI/lumi.remote.b1acn02"
deviceLabel: Aqara Wireless Mini Switch T1
manufacturer: LUMI
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the FirmwareUpdate capability is handled completely by us and we'd prefer to have it for all of our zigbee devices

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Steven, thanks for your note. This product handles OTA firmware updates via Bluetooth, through the official app for installers. Other Vimar products certified in SmartThings have a similar profile for the same reason. I'll stick with this, if possible.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: two-buttons-no-fw-update
components:
- id: main
capabilities:
- id: button
version: 1
- id: refresh
version: 1
categories:
- name: RemoteController
- id: button1
capabilities:
- id: button
version: 1
categories:
- name: RemoteController
- id: button2
capabilities:
- id: button
version: 1
categories:
- name: RemoteController
215 changes: 215 additions & 0 deletions drivers/SmartThings/zigbee-button/src/test/test_vimar_button.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
-- Copyright 2022 SmartThings
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

date

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

--
-- 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 zigbee_test_utils = require "integration_test.zigbee_test_utils"
local clusters = require "st.zigbee.zcl.clusters"
local capabilities = require "st.capabilities"
local t_utils = require "integration_test.utils"
local OnOff = clusters.OnOff
local LevelControl = clusters.Level

local button_attr = capabilities.button.button

local mock_device = test.mock_device.build_test_zigbee_device(
{
profile = t_utils.get_profile_definition("two-buttons-no-fw-update.yml"),
zigbee_endpoints = {
[1] = {
id = 1,
manufacturer = "Vimar",
model = "RemoteControl_v1.0",
server_clusters = {0x0000, 0x0003},
client_clusters = {0x0006, 0x0008}
}
}
}
)

zigbee_test_utils.prepare_zigbee_env_info()
local function test_init()
test.mock_device.add_test_device(mock_device)
zigbee_test_utils.init_noop_health_check_timer()
end

test.set_test_init_function(test_init)

test.register_coroutine_test(
"Remote Control should be handled in added lifecycle event",
function()
test.socket.capability:__set_channel_ordering("relaxed")
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main",
capabilities.button.supportedButtonValues({ "pushed", "down_hold", "up" }, { visibility = { displayed = false } })
)
)

test.socket.capability:__expect_send(
mock_device:generate_test_message(
"main",
capabilities.button.numberOfButtons({ value = 2 }, { visibility = { displayed = false } })
)
)

for button_number = 1, 2 do
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"button" .. button_number,
capabilities.button.supportedButtonValues({ "pushed", "down_hold", "up" }, { visibility = { displayed = false } })
)
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
"button" .. button_number,
capabilities.button.numberOfButtons({ value = 1 }, { visibility = { displayed = false } })
)
)
end
test.socket.capability:__expect_send({
mock_device.id,
{
capability_id = "button", component_id = "main",
attribute_id = "button", state = { value = "pushed" }
}
})

test.socket.device_lifecycle:__queue_receive({ mock_device.id, "added" })
test.wait_for_events()
end
)

test.register_coroutine_test(
"Button UP (button1) should handle pushed event",
function()
test.wait_for_events()

test.socket.zigbee:__queue_receive({ mock_device.id, OnOff.server.commands.On.build_test_rx(mock_device) })
test.socket.capability:__expect_send(
mock_device:generate_test_message("button1", button_attr.pushed({ state_change = true }))
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button_attr.pushed({ state_change = true }))
)
end
)

test.register_coroutine_test(
"Button DOWN (button2) should handle pushed event",
function()
test.wait_for_events()

test.socket.zigbee:__queue_receive({ mock_device.id, OnOff.server.commands.Off.build_test_rx(mock_device) })
test.socket.capability:__expect_send(
mock_device:generate_test_message("button2", button_attr.pushed({ state_change = true }))
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button_attr.pushed({ state_change = true }))
)
end
)

test.register_coroutine_test(
"Button UP (button1) should handle down hold and up event",
function()
test.wait_for_events()

test.socket.zigbee:__queue_receive({ mock_device.id, LevelControl.server.commands.Move.build_test_rx(mock_device, LevelControl.types.MoveStepMode.UP, 255) })
test.socket.capability:__expect_send(
mock_device:generate_test_message("button1", button_attr.down_hold({ state_change = true }))
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button_attr.down_hold({ state_change = true }))
)

test.socket.zigbee:__queue_receive({ mock_device.id, LevelControl.server.commands.Stop.build_test_rx(mock_device) })
test.socket.capability:__expect_send(
mock_device:generate_test_message("button1", button_attr.up({ state_change = true }))
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button_attr.up({ state_change = true }))
)
end
)

test.register_coroutine_test(
"Button DOWN (button2) should handle down hold and up",
function()
test.wait_for_events()

test.socket.zigbee:__queue_receive({ mock_device.id, LevelControl.server.commands.Move.build_test_rx(mock_device, LevelControl.types.MoveStepMode.DOWN, 255) })
test.socket.capability:__expect_send(
mock_device:generate_test_message("button2", button_attr.down_hold({ state_change = true }))
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button_attr.down_hold({ state_change = true }))
)

test.socket.zigbee:__queue_receive({ mock_device.id, LevelControl.server.commands.Stop.build_test_rx(mock_device) })
test.socket.capability:__expect_send(
mock_device:generate_test_message("button2", button_attr.up({ state_change = true }))
)
test.socket.capability:__expect_send(
mock_device:generate_test_message("main", button_attr.up({ state_change = true }))
)
end
)

test.register_coroutine_test(
"Remote Control driver should handle configuration lifecycle",
function()
test.wait_for_events()

test.socket.device_lifecycle:__queue_receive({ mock_device.id, "doConfigure" })
test.socket.zigbee:__set_channel_ordering("relaxed")

test.socket.zigbee:__expect_send(
{
mock_device.id,
zigbee_test_utils.build_bind_request(mock_device,
zigbee_test_utils.mock_hub_eui,
OnOff.ID)
}
)
test.socket.zigbee:__expect_send(
{
mock_device.id,
zigbee_test_utils.build_bind_request(mock_device,
zigbee_test_utils.mock_hub_eui,
LevelControl.ID)
}
)

for _, component in pairs(mock_device.profile.components) do
local number_of_buttons = component.id == "main" and 2 or 1
test.socket.capability:__expect_send(
mock_device:generate_test_message(
component.id,
capabilities.button.supportedButtonValues({ "pushed", "down_hold", "up" }, { visibility = { displayed = true } })
)
)
test.socket.capability:__expect_send(
mock_device:generate_test_message(
component.id,
capabilities.button.numberOfButtons({ value = number_of_buttons }, { visibility = { displayed = true } })
)
)
end

mock_device:expect_metadata_update({ provisioning_state = "PROVISIONED" })
end
)

test.run_registered_tests()
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ local ZIGBEE_MULTI_BUTTON_FINGERPRINTS = {
{ mfr = "ROBB smarrt", model = "ROB_200-007-0" },
{ mfr = "ROBB smarrt", model = "ROB_200-008-0" },
{ mfr = "WALL HERO", model = "ACL-401SCA4" },
{ mfr = "Samsung Electronics", model = "SAMSUNG-ITM-Z-005" }
{ mfr = "Samsung Electronics", model = "SAMSUNG-ITM-Z-005" },
{ mfr = "Vimar", model = "RemoteControl_v1.0" }
}

local function can_handle_zigbee_multi_button(opts, driver, device, ...)
Expand Down Expand Up @@ -88,7 +89,8 @@ local zigbee_multi_button = {
require("zigbee-multi-button.shinasystems"),
require("zigbee-multi-button.robb"),
require("zigbee-multi-button.wallhero"),
require("zigbee-multi-button.SLED")
require("zigbee-multi-button.SLED"),
require("zigbee-multi-button.vimar")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ local devices = {
},
SUPPORTED_BUTTON_VALUES = { "pushed", "held", "double" },
NUMBER_OF_BUTTONS = 4
},
BUTTON_PUSH_DOWN_HOLD_UP_VIMAR_2 = {
MATCHING_MATRIX = {
{ mfr = "Vimar", model = "RemoteControl_v1.0" }
},
SUPPORTED_BUTTON_VALUES = { "pushed", "down_hold", "up" },
NUMBER_OF_BUTTONS = 2
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
-- Copyright 2024 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 device_management = require "st.zigbee.device_management"
local OnOff = clusters.OnOff
local LevelControl = clusters.Level
local log = require "log"

local VIMAR_HOLD_BUTTON = "Vimar_Holding_Button"

local emit_button_event = function(button_name, device, event)
local comp = device.profile.components[button_name]
if comp ~= nil then
device:emit_component_event(comp, event)
device:emit_event(event)
else
log.warn("Attempted to emit button event for unknown button: " .. button_name)
end
end

local function vimar_up_button_pushed(driver, device, zb_rx)
emit_button_event("button1", device, capabilities.button.button.pushed({state_change = true}))
end

local function vimar_down_button_pushed(driver, device, zb_rx)
emit_button_event("button2", device, capabilities.button.button.pushed({state_change = true}))
end

local function vimar_button_hold(driver, device, zb_rx)
if zb_rx.body.zcl_body.move_mode.value == LevelControl.types.MoveStepMode.UP then
device:set_field(VIMAR_HOLD_BUTTON, "button1")
emit_button_event("button1", device, capabilities.button.button.down_hold({state_change = true}))
elseif zb_rx.body.zcl_body.move_mode.value == LevelControl.types.MoveStepMode.DOWN then
device:set_field(VIMAR_HOLD_BUTTON, "button2")
emit_button_event("button2", device, capabilities.button.button.down_hold({state_change = true}))
else
log.warn("MoveStepMode value not supported")
end
end

local function vimar_release_button_hold(driver, device, zb_rx)
local hold_button = device:get_field(VIMAR_HOLD_BUTTON)
emit_button_event(hold_button, device, capabilities.button.button.up({state_change = true}))
end

local do_configure = function(self, device)
device:send(device_management.build_bind_request(device, OnOff.ID, self.environment_info.hub_zigbee_eui))
device:send(device_management.build_bind_request(device, LevelControl.ID, self.environment_info.hub_zigbee_eui))
for _, component in pairs(device.profile.components) do
local number_of_buttons = component.id == "main" and 2 or 1
device:emit_component_event(component,
capabilities.button.supportedButtonValues({ "pushed", "down_hold", "up" }, { visibility = { displayed = true } }))
device:emit_component_event(component,
capabilities.button.numberOfButtons({ value = number_of_buttons }, { visibility = { displayed = true } }))
end
end

local vimar_remote_control = {
NAME = "Vimar Remote Control",
zigbee_handlers = {
cluster = {
[OnOff.ID] = {
[OnOff.server.commands.Off.ID] = vimar_down_button_pushed,
[OnOff.server.commands.On.ID] = vimar_up_button_pushed
},
[LevelControl.ID] = {
[LevelControl.server.commands.Move.ID] = vimar_button_hold,
[LevelControl.server.commands.Stop.ID] = vimar_release_button_hold
}
}
},
lifecycle_handlers = {
doConfigure = do_configure
},
can_handle = function(opts, driver, device, ...)
return device:get_manufacturer() == "Vimar" and device:get_model() == "RemoteControl_v1.0"
end
}

return vimar_remote_control
Loading