Skip to content

Commit

Permalink
Add zigbee-button for Vimar xx591 (Wall-mounted Remote Control) (#1508)
Browse files Browse the repository at this point in the history
* add vimar remote control (zigbee-button)

* docs: update copyright year in vimar zigbee button test
  • Loading branch information
sciaccom-vimar authored Mar 7, 2025
1 parent c44ffd5 commit aa2ccde
Show file tree
Hide file tree
Showing 6 changed files with 346 additions and 2 deletions.
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
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 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.

-- 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

0 comments on commit aa2ccde

Please sign in to comment.