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 support for Inovelli VTM31-SN Dimmer Switch #1754

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
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
6 changes: 6 additions & 0 deletions drivers/SmartThings/matter-switch/fingerprints.yml
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ matterManufacturer:
vendorId: 0x1372
productId: 0x0002
deviceProfileName: switch-4
# Inovelli
- id: "4961/1"
deviceLabel: Inovelli Dimmer Switch
vendorId: 0x1361
productId: 0x0001
deviceProfileName: inovelli-vtm31-sn
#Legrand
- id: "4129/3"
deviceLabel: Smart Lights Smart Plug
Expand Down
127 changes: 127 additions & 0 deletions drivers/SmartThings/matter-switch/profiles/inovelli-vtm31-sn.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: inovelli-vtm31-sn
components:
- id: main
capabilities:
- id: switch
version: 1
- id: switchLevel
version: 1
- id: refresh
version: 1
- id: configuration
version: 1
- id: firmwareUpdate
version: 1
categories:
- name: Switch
- id: button1
label: Up Button
capabilities:
- id: button
version: 1
categories:
- name: RemoteController
- id: button2
label: Down Button
capabilities:
- id: button
version: 1
categories:
- name: RemoteController
- id: button3
label: Config Button
capabilities:
- id: button
version: 1
categories:
- name: RemoteController
preferences:
- name: "switchMode"
title: "1. Switch Mode"
description: "Use as a Dimmer or an On/Off switch"
required: false
preferenceType: enumeration
definition:
options:
"0": "On/Off + Single (default)"
"1": "On/OFf + Dumb"
"2": "On/Off + Aux"
"3": "On/OFf + Full Wave"
"4": "Dimmer + Single"
"5": "Dimmer + Dumb"
"6": "Dimmer + Aux"
default: 0
- name: "smartBulbMode"
title: "2. Smart Bulb Mode"
description: "For use with Smart Bulbs that need constant power and are controlled via commands rather than power. Smart Bulb Mode does not work in Sumb 3-Way Switch mode"
required: false
preferenceType: enumeration
definition:
options:
"0": "Disabled (default)"
"1": "Smart Bulb Mode"
default: 0
- name: "dimmingEdge"
title: "3. Dimming Edge"
description: "Change dimming type to leading edge (default) or trailing edge for better bulb compatibility"
required: false
preferenceType: enumeration
definition:
options:
"0": "Leading (default)"
"1": "Trailing"
default: 0
- name: "dimmingSpeed"
title: "4. Dimming Speed"
description: "This changes the speed that the light dims. A setting of '0' turns the light immediately on. Increasing the value slows down the transition speed. Default=25 (2500ms or 2.5s)"
required: false
preferenceType: enumeration
definition:
options:
"0": "Instant"
"1": "500ms"
"2": "800ms"
"3": "1s"
"4": "1.5s"
"5": "2s"
"6": "2.5s (default)"
"7": "3s"
"8": "3.5s"
"9": "4s"
"10": "5s"
"11": "6s"
"12": "7s"
"13": "8s"
"14": "10s"
default: 6
- name: "relayClick"
title: "5. Relay Click"
description: "Audible Click in On/Off mode"
required: false
preferenceType: enumeration
definition:
options:
"0": "Enabled (default)"
"1": "Disabled"
default: 0
- name: "ledIndicatorColor"
title: "6. LED Indicator Color (w/On)"
description: "Set the color of the Full LED Indicator when the load is on"
required: false
preferenceType: enumeration
definition:
options:
"0": "Red"
"1": "Orange"
"2": "Lemon"
"3": "Lime"
"4": "Green"
"5": "Teal"
"6": "Cyan"
"7": "Aqua"
"8": "Blue (default)"
"9": "Violet"
"10": "Magenta"
"11": "Pink"
"12": "White"
default: 8
67 changes: 67 additions & 0 deletions drivers/SmartThings/matter-switch/src/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ local MatterDriver = require "st.matter.driver"
local lua_socket = require "socket"
local utils = require "st.utils"
local device_lib = require "st.device"
local data_types = require "st.matter.data_types"

local MOST_RECENT_TEMP = "mostRecentTemp"
local RECEIVED_X = "receivedX"
Expand Down Expand Up @@ -154,6 +155,21 @@ local child_device_profile_overrides = {
{ vendor_id = 0x1321, product_id = 0x000D, child_profile = "switch-binary" },
}

local fingerprint_profile_overrides = {
{ vendor_id = 0x1361, product_id = 0x0001 }, -- Inovelli VTM31-SN
}

local LATEST_CLOCK_SET_TIMESTAMP = "latest_clock_set_timestamp"

local preference_map_inovelli_vtm31sn = {
Copy link
Contributor

Choose a reason for hiding this comment

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

It would make sense to move preferences that are specific to a certain device to a sub driver if we are able to. Or these configs could be moved to their own file at least to keep it separate from the base driver where we should try to handle things as generically as possible

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved the inovelli-specific handling to a subdriver in 2e8b4b5

switchMode = {parameter_number = 1, size = data_types.Uint8},
smartBulbMode = {parameter_number = 2, size = data_types.Uint8},
dimmingEdge = {parameter_number = 3, size = data_types.Uint8},
dimmingSpeed = {parameter_number = 4, size = data_types.Uint8},
relayClick = {parameter_number = 5, size = data_types.Uint8},
ledIndicatorColor = {parameter_number = 6, size = data_types.Uint8},
}

local detect_matter_thing

local CUMULATIVE_REPORTS_NOT_SUPPORTED = "__cumulative_reports_not_supported"
Expand Down Expand Up @@ -248,6 +264,14 @@ local function set_poll_report_timer_and_schedule(device, is_cumulative_report)
end
end

local preferences_to_numeric_value = function(new_value)
local numeric = tonumber(new_value)
if numeric == nil then -- in case the value is Boolean
numeric = new_value and 1 or 0
end
return numeric
end

local START_BUTTON_PRESS = "__start_button_press"
local TIMEOUT_THRESHOLD = 10 --arbitrary timeout
local HELD_THRESHOLD = 1
Expand Down Expand Up @@ -499,6 +523,16 @@ local function find_child(parent, ep_id)
return parent:get_child_by_parent_assigned_key(string.format("%d", ep_id))
end

local function check_fingerprint_profile_overrides(device)
for _, fingerprint in ipairs(fingerprint_profile_overrides) do
if device.manufacturer_info.vendor_id == fingerprint.vendor_id and
device.manufacturer_info.product_id == fingerprint.product_id then
return true
end
end
return false
end

local function initialize_switch(driver, device)
local switch_eps = device:get_endpoints(clusters.OnOff.ID)
local button_eps = device:get_endpoints(clusters.Switch.ID, {feature_bitmap=clusters.Switch.types.SwitchFeature.MOMENTARY_SWITCH})
Expand Down Expand Up @@ -570,6 +604,15 @@ local function initialize_switch(driver, device)
device:set_field(COMPONENT_TO_ENDPOINT_MAP_BUTTON, component_map, {persist = true})
end

-- If there is a custom static profile for the device, configure buttons if needed and
Copy link
Contributor

Choose a reason for hiding this comment

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

Since the inovelli switch already has a sub driver, I would say we should remove this override from the main driver and just add the special handling for the init event in the inovelli sub driver. This way we can try to keep the base driver as generic as possible (although there are already exceptions to this).

So, I would just copy whatever functionality is needed from the base driver and then put it in the sub driver for the init function, and then trim whatever is not needed. Better yet, you could move the configure_buttons function to its own file so that it can be shared between the base and sub driver, similar to what is done with the embedded-cluster-utils module.

-- then return to prevent profile from being updated.
if check_fingerprint_profile_overrides(device) then
if #button_eps > 0 then
configure_buttons(device)
end
return
end

if #button_eps > 0 and is_supported_combination_button_switch_device_type(device, main_endpoint) then
if #button_eps == 1 then
profile_name = "light-level-button"
Expand Down Expand Up @@ -1114,6 +1157,30 @@ local function info_changed(driver, device, event, args)
device:set_field(DEFERRED_CONFIGURE, nil)
end
end

if not device.preferences or device.network_type == device_lib.NETWORK_TYPE_CHILD then
return
end

if device.manufacturer_info.vendor_id == fingerprint_profile_overrides[1].vendor_id and
Copy link
Contributor

Choose a reason for hiding this comment

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

This code is somewhat confusing and I think it could use some comments or documentation somewhere since there is some device specific knowledge here. For example, why is the time diff here and what is the significance of the values?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe this code is used to prevent preferences from being changed faster than every two seconds. This was the same way that Inovelli implemented this for the zigbee driver, so it could be a device limitation. I will add some comments to make things a little more clear.

device.manufacturer_info.product_id == fingerprint_profile_overrides[1].product_id then
local time_diff = 3
local last_clock_set_time = device:get_field(LATEST_CLOCK_SET_TIMESTAMP)
if last_clock_set_time ~= nil then
time_diff = os.difftime(os.time(), last_clock_set_time)
end
device:set_field(LATEST_CLOCK_SET_TIMESTAMP, os.time(), {persist = true})
if time_diff > 2 then
local preferences = preference_map_inovelli_vtm31sn
for id, value in pairs(device.preferences) do
if args.old_st_store.preferences[id] ~= value and preferences and preferences[id] then
local new_parameter_value = preferences_to_numeric_value(device.preferences[id])
local req = clusters.ModeSelect.server.commands.ChangeToMode(device, preferences[id].parameter_number, new_parameter_value)
Copy link
Contributor

Choose a reason for hiding this comment

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

If the mode cluster is going to be used here, this would be the first use of it in the matter-switch driver. This means we'd need to add it as an embedded cluster so it could be used on older FW that doesn't support the mode cluster via the lua libs definitions yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It looks like ModeSelect has been been in the spec since matter 1.0. Is there a way to check when it was first added to the lua libs?

device:send(req)
end
end
end
end
end

local function device_added(driver, device)
Expand Down
Loading