Skip to content

Commit

Permalink
[Aqara] Cube T1 Pro (#930)
Browse files Browse the repository at this point in the history
* add Aqara Cube T1 Pro

* Rename the profile from "aqara-cube-t1-pro" to "cube-t1-pro"

* fixed thread call and removed the unused local

* fixed the thread

* changing folders

* remove can_handle

* add (1) refresh capability (2) BatteryVoltage handler (3) Testcases

* remove unused variable utils
  • Loading branch information
seojune79 authored Dec 1, 2023
1 parent d599a49 commit 31329ec
Show file tree
Hide file tree
Showing 7 changed files with 388 additions and 0 deletions.
29 changes: 29 additions & 0 deletions drivers/Aqara/aqara-cube/capabilities/cubeAction.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
id: stse.cubeAction
version: 1
status: proposed
name: Cube Action
ephemeral: false
attributes:
cubeAction:
schema:
type: object
properties:
value:
title: ActionType
type: string
enum:
- noAction
- shake
- rotate
- pickUpAndHold
- flipToSide1
- flipToSide2
- flipToSide3
- flipToSide4
- flipToSide5
- flipToSide6
additionalProperties: false
required:
- value
enumCommands: []
commands: {}
25 changes: 25 additions & 0 deletions drivers/Aqara/aqara-cube/capabilities/cubeFace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
id: stse.cubeFace
version: 1
status: proposed
name: Cube Face
ephemeral: false
attributes:
cubeFace:
schema:
type: object
properties:
value:
title: CubeFace
type: string
enum:
- face1Up
- face2Up
- face3Up
- face4Up
- face5Up
- face6Up
additionalProperties: false
required:
- value
enumCommands: []
commands: {}
6 changes: 6 additions & 0 deletions drivers/Aqara/aqara-cube/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: 'Aqara Cube'
packageKey: 'aqara-cube'
permissions:
zigbee: {}
description: "SmartThings driver for Aqara Cube devices"
vendorSupportInformation: "https://www.aqara.com/en/support/"
6 changes: 6 additions & 0 deletions drivers/Aqara/aqara-cube/fingerprints.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
zigbeeManufacturer:
- id: "Lumi/lumi.remote.cagl02"
deviceLabel: Aqara Cube T1 Pro
manufacturer: LUMI
model: lumi.remote.cagl02
deviceProfileName: cube-t1-pro
19 changes: 19 additions & 0 deletions drivers/Aqara/aqara-cube/profiles/cube-t1-pro.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: cube-t1-pro
components:
- id: main
capabilities:
- id: stse.cubeAction
version: 1
- id: stse.cubeFace
version: 1
- id: battery
version: 1
- id: firmwareUpdate
version: 1
- id: refresh
version: 1
categories:
- name: RemoteController
metadata:
mnmn: SolutionsEngineering
vid: SmartThings-smartthings-Aqara_CubeT1Pro
133 changes: 133 additions & 0 deletions drivers/Aqara/aqara-cube/src/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
local ZigbeeDriver = require "st.zigbee"
local capabilities = require "st.capabilities"
local data_types = require "st.zigbee.data_types"
local cluster_base = require "st.zigbee.cluster_base"
local battery_defaults = require "st.zigbee.defaults.battery_defaults"
local clusters = require "st.zigbee.zcl.clusters"
local PowerConfiguration = clusters.PowerConfiguration

local PRI_CLU = 0xFCC0
local PRI_ATTR = 0x0009
local MFG_CODE = 0x115F

local ROTATE_CLU = 0x000C
local EVENT_CLU = 0x0012
local FACE_ATTR = 0x0149
local ACTION_ATTR = 0x0055
local CUBE_MODE = 0x0148

local cubeAction = capabilities["stse.cubeAction"]
local cubeFace = capabilities["stse.cubeFace"]
local cubeFaceVal = { "face1Up", "face2Up", "face3Up", "face4Up", "face5Up", "face6Up" }
local cubeFlipToSideVal = { "flipToSide1", "flipToSide2", "flipToSide3", "flipToSide4", "flipToSide5", "flipToSide6" }

local CUBEACTION_TIMER = "cubeAction_timer"
local CUBEACTION_TIME = 3

local callback_timer = function(device)
return function()
device:emit_event(cubeAction.cubeAction("noAction"))
end
end

local function reset_thread(device)
local timer = device:get_field(CUBEACTION_TIMER)
if timer then
device.thread:cancel_timer(timer)
device:set_field(CUBEACTION_TIMER, nil)
end
device:set_field(CUBEACTION_TIMER, device.thread:call_with_delay(CUBEACTION_TIME, callback_timer(device)))
end

local function data_handler(driver, device, value, zb_rx)
local val = value.value
if val == 0x0000 then -- Shake
reset_thread(device)
device:emit_event(cubeAction.cubeAction("shake"))
elseif val == 0x0004 then -- hold
reset_thread(device)
device:emit_event(cubeAction.cubeAction("pickUpAndHold"))
elseif val & 0x0400 == 0x0400 then -- Flip to side
local faceNum = val & 0x0007
reset_thread(device)
device:emit_event(cubeAction.cubeAction(cubeFlipToSideVal[faceNum + 0x1]))
end
end

local function rotate_handler(driver, device, value, zb_rx)
-- Rotation
reset_thread(device)
device:emit_event(cubeAction.cubeAction("rotate"))
end

local function face_handler(driver, device, value, zb_rx)
local faceNum = value.value
device:emit_event(cubeFace.cubeFace(cubeFaceVal[faceNum + 1]))
end

local function do_refresh(driver, device)
-- refresh
device:send(PowerConfiguration.attributes.BatteryVoltage:read(device))
end

local function device_init(driver, device)
local power_configuration = {
cluster = PowerConfiguration.ID,
attribute = PowerConfiguration.attributes.BatteryVoltage.ID,
minimum_interval = 30,
maximum_interval = 3600,
data_type = PowerConfiguration.attributes.BatteryVoltage.base_type,
reportable_change = 1
}

battery_defaults.build_linear_voltage_init(2.6, 3.0)(driver, device)

device:add_configured_attribute(power_configuration)
device:add_monitored_attribute(power_configuration)
end

local function device_added(self, device)
-- Set private attribute
device:send(cluster_base.write_manufacturer_specific_attribute(device,
PRI_CLU, PRI_ATTR, MFG_CODE, data_types.Uint8, 1))

device:send(cluster_base.write_manufacturer_specific_attribute(device,
PRI_CLU, CUBE_MODE, MFG_CODE, data_types.Uint8, 1))
device:emit_event(cubeAction.cubeAction("noAction"))
device:emit_event(cubeFace.cubeFace("face1Up"))
do_refresh(self, device)
end

-- [[ register ]]
local aqara_cube_t1_pro_handler = {
NAME = "Aqara Cube T1 Pro",
capability_handlers = {
[capabilities.refresh.ID] = {
[capabilities.refresh.commands.refresh.NAME] = do_refresh
}
},
zigbee_handlers = {
attr = {
[EVENT_CLU] = {
[ACTION_ATTR] = data_handler
},
[ROTATE_CLU] = {
[ACTION_ATTR] = rotate_handler,
},
[PRI_CLU] = {
[FACE_ATTR] = face_handler
},
[PowerConfiguration.ID] = {
[PowerConfiguration.attributes.BatteryVoltage.ID] = battery_defaults.battery_volt_attr_handler
}
}
},
lifecycle_handlers = {
init = device_init,
added = device_added
}
}

local aqara_cube_t1_pro_driver = ZigbeeDriver("aqara_cube_t1_pro", aqara_cube_t1_pro_handler)
aqara_cube_t1_pro_driver:run()

170 changes: 170 additions & 0 deletions drivers/Aqara/aqara-cube/src/test/test_aqara_cube_t1_pro.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
local test = require "integration_test"
local cluster_base = require "st.zigbee.cluster_base"
local t_utils = require "integration_test.utils"
local zigbee_test_utils = require "integration_test.zigbee_test_utils"
local capabilities = require "st.capabilities"
local data_types = require "st.zigbee.data_types"
local clusters = require "st.zigbee.zcl.clusters"
local PowerConfiguration = clusters.PowerConfiguration

local PRI_CLU = 0xFCC0
local PRI_ATTR = 0x0009
local MFG_CODE = 0x115F

local ROTATE_CLU = 0x000C
local EVENT_CLU = 0x0012
local FACE_ATTR = 0x0149
local ACTION_ATTR = 0x0055
local CUBE_MODE = 0x0148

local cubeAction = capabilities["stse.cubeAction"]
local cubeFace = capabilities["stse.cubeFace"]
test.add_package_capability("cubeAction.yaml")
test.add_package_capability("cubeFace.yaml")

local CUBEACTION_TIME = 3


local mock_device = test.mock_device.build_test_zigbee_device(
{
profile = t_utils.get_profile_definition("cube-t1-pro.yml"),
zigbee_endpoints = {
[1] = {
id = 1,
manufacturer = "LUMI",
model = "lumi.remote.cagl02",
server_clusters = { PRI_CLU }
}
}
}
)

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(
"lifecycle - added test",
function()
test.socket.zigbee:__set_channel_ordering("relaxed")
test.socket.capability:__set_channel_ordering("relaxed")
test.socket.device_lifecycle:__queue_receive({ mock_device.id, "added" })
-- private protocol enable
test.socket.zigbee:__expect_send({ mock_device.id,
cluster_base.write_manufacturer_specific_attribute(mock_device, PRI_CLU, PRI_ATTR, MFG_CODE,
data_types.Uint8, 1) })
-- init
test.socket.zigbee:__expect_send({ mock_device.id,
cluster_base.write_manufacturer_specific_attribute(mock_device, PRI_CLU, CUBE_MODE, MFG_CODE,
data_types.Uint8, 1) })
test.socket.capability:__expect_send(mock_device:generate_test_message("main", cubeAction.cubeAction("noAction")))
test.socket.capability:__expect_send(mock_device:generate_test_message("main", cubeFace.cubeFace("face1Up")))
test.socket.zigbee:__expect_send({ mock_device.id, PowerConfiguration.attributes.BatteryVoltage:read(mock_device) })
end
)

test.register_coroutine_test(
"capability - refresh",
function()
test.socket.capability:__queue_receive({ mock_device.id,
{ capability = "refresh", component = "main", command = "refresh", args = {} } })
test.socket.zigbee:__expect_send({ mock_device.id, PowerConfiguration.attributes.BatteryVoltage:read(mock_device) })
end
)

test.register_coroutine_test(
"data_handler test - shake",
function()
local attr_report_data = {
{ ACTION_ATTR, data_types.Uint16.ID, 0x0000 }
}
test.socket.zigbee:__queue_receive({
mock_device.id,
zigbee_test_utils.build_attribute_report(mock_device, EVENT_CLU, attr_report_data, MFG_CODE)
})
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
cubeAction.cubeAction("shake")))
test.timer.__create_and_queue_test_time_advance_timer(CUBEACTION_TIME, "oneshot")
test.mock_time.advance_time(CUBEACTION_TIME)
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
cubeAction.cubeAction("noAction")))
end
)

test.register_coroutine_test(
"data_handler test - pick up and hold",
function()
local attr_report_data = {
{ ACTION_ATTR, data_types.Uint16.ID, 0x0004 }
}
test.socket.zigbee:__queue_receive({
mock_device.id,
zigbee_test_utils.build_attribute_report(mock_device, EVENT_CLU, attr_report_data, MFG_CODE)
})
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
cubeAction.cubeAction("pickUpAndHold")))
test.timer.__create_and_queue_test_time_advance_timer(CUBEACTION_TIME, "oneshot")
test.mock_time.advance_time(CUBEACTION_TIME)
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
cubeAction.cubeAction("noAction")))
end
)

test.register_coroutine_test(
"data_handler test - flip to side 6",
function()
local attr_report_data = {
{ ACTION_ATTR, data_types.Uint16.ID, 0x0405 }
}
test.socket.zigbee:__queue_receive({
mock_device.id,
zigbee_test_utils.build_attribute_report(mock_device, EVENT_CLU, attr_report_data, MFG_CODE)
})
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
cubeAction.cubeAction("flipToSide6")))
test.timer.__create_and_queue_test_time_advance_timer(CUBEACTION_TIME, "oneshot")
test.mock_time.advance_time(CUBEACTION_TIME)
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
cubeAction.cubeAction("noAction")))
end
)

test.register_coroutine_test(
"rotate_handler test - rotation",
function()
local attr_report_data = {
{ ACTION_ATTR, data_types.Uint16.ID, 0x0000 }
}
test.socket.zigbee:__queue_receive({
mock_device.id,
zigbee_test_utils.build_attribute_report(mock_device, ROTATE_CLU, attr_report_data, MFG_CODE)
})
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
cubeAction.cubeAction("rotate")))
test.timer.__create_and_queue_test_time_advance_timer(CUBEACTION_TIME, "oneshot")
test.mock_time.advance_time(CUBEACTION_TIME)
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
cubeAction.cubeAction("noAction")))
end
)

test.register_coroutine_test(
"face_handler test - face up 1",
function()
local attr_report_data = {
{ FACE_ATTR, data_types.Uint8.ID, 0x00 }
}
test.socket.zigbee:__queue_receive({
mock_device.id,
zigbee_test_utils.build_attribute_report(mock_device, PRI_CLU, attr_report_data, MFG_CODE)
})
test.socket.capability:__expect_send(mock_device:generate_test_message("main",
cubeFace.cubeFace("face1Up")))
end
)

test.run_registered_tests()

0 comments on commit 31329ec

Please sign in to comment.