Skip to content

Commit 3fbfe68

Browse files
Add NodOn TRV-4-1-00 Thermostatic Radiator Valve support
Add support for the NodOn TRV-4-1-00 Thermostatic Radiator Valve to the zigbee-thermostat driver. Key features: - Battery-optimized reporting intervals (temperature: 5s-1h, setpoint/mode: 1s-24h, battery: 5min-24h) - Setpoint range validation (8-28°C) with clamping - Standard Zigbee thermostat cluster support (heating only) - Proper cluster binding for bidirectional communication
1 parent 4524087 commit 3fbfe68

File tree

4 files changed

+140
-0
lines changed

4 files changed

+140
-0
lines changed

drivers/SmartThings/zigbee-thermostat/fingerprints.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ zigbeeManufacturer:
1919
manufacturer: Danfoss
2020
model: eTRV0100
2121
deviceProfileName: thermostat-popp-danfoss
22+
- id: "NodOn/TRV-4-1-00"
23+
deviceLabel: NodOn Thermostatic Radiator Valve
24+
manufacturer: NodOn
25+
model: TRV-4-1-00
26+
deviceProfileName: thermostat-nodon-trv
2227
- id: "D5X84YU/eT093WRO"
2328
deviceLabel: POPP Smart Thermostat (Zigbee)
2429
manufacturer: D5X84YU
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: thermostat-nodon-trv
2+
components:
3+
- id: main
4+
capabilities:
5+
- id: temperatureMeasurement
6+
version: 1
7+
- id: thermostatHeatingSetpoint
8+
version: 1
9+
config:
10+
values:
11+
- key: "heatingSetpoint.value"
12+
range: [ 8, 28 ]
13+
- id: thermostatMode
14+
version: 1
15+
- id: battery
16+
version: 1
17+
- id: firmwareUpdate
18+
version: 1
19+
- id: refresh
20+
version: 1
21+
categories:
22+
- name: Thermostat

drivers/SmartThings/zigbee-thermostat/src/init.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,7 @@ local zigbee_thermostat_driver = {
371371
require("lux-konoz"),
372372
require("leviton"),
373373
require("danfoss"),
374+
require("nodon-trv"),
374375
require("popp"),
375376
require("vimar"),
376377
require("resideo_korea"),
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
-- Copyright 2024 SmartThings
2+
--
3+
-- Licensed under the Apache License, Version 2.0 (the "License");
4+
-- you may not use this file except in compliance with the License.
5+
-- You may obtain a copy of the License at
6+
--
7+
-- http://www.apache.org/licenses/LICENSE-2.0
8+
--
9+
-- Unless required by applicable law or agreed to in writing, software
10+
-- distributed under the License is distributed on an "AS IS" BASIS,
11+
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
-- See the License for the specific language governing permissions and
13+
-- limitations under the License.
14+
15+
local device_management = require "st.zigbee.device_management"
16+
local battery_defaults = require "st.zigbee.defaults.battery_defaults"
17+
local clusters = require "st.zigbee.zcl.clusters"
18+
local capabilities = require "st.capabilities"
19+
local log = require "log"
20+
21+
local Thermostat = clusters.Thermostat
22+
local PowerConfiguration = clusters.PowerConfiguration
23+
24+
local NODON_TRV_FINGERPRINTS = {
25+
{ mfr = "NodOn", model = "TRV-4-1-00" }
26+
}
27+
28+
local MIN_SETPOINT = 8.0
29+
local MAX_SETPOINT = 28.0
30+
31+
local is_nodon_trv_thermostat = function(opts, driver, device)
32+
for _, fingerprint in ipairs(NODON_TRV_FINGERPRINTS) do
33+
if device:get_manufacturer() == fingerprint.mfr and device:get_model() == fingerprint.model then
34+
return true
35+
end
36+
end
37+
return false
38+
end
39+
40+
-- Handle setHeatingSetpoint command with range validation
41+
local function set_heating_setpoint_handler(driver, device, command)
42+
local setpoint_celsius = command.args.setpoint
43+
44+
-- Enforce device min/max limits (8-28°C for NodOn TRV)
45+
if setpoint_celsius < MIN_SETPOINT then
46+
log.warn(string.format("Setpoint %.1f°C below minimum, clamping to %.1f°C", setpoint_celsius, MIN_SETPOINT))
47+
setpoint_celsius = MIN_SETPOINT
48+
elseif setpoint_celsius > MAX_SETPOINT then
49+
log.warn(string.format("Setpoint %.1f°C above maximum, clamping to %.1f°C", setpoint_celsius, MAX_SETPOINT))
50+
setpoint_celsius = MAX_SETPOINT
51+
end
52+
53+
local setpoint_zigbee = math.floor(setpoint_celsius * 100) -- Convert to 0.01°C units
54+
log.info(string.format("Setting heating setpoint: %.1f°C (Zigbee: %d)", setpoint_celsius, setpoint_zigbee))
55+
56+
-- Write to thermostat cluster
57+
device:send(Thermostat.attributes.OccupiedHeatingSetpoint:write(device, setpoint_zigbee))
58+
59+
-- Read back to confirm
60+
device.thread:call_with_delay(1, function(d)
61+
device:send(Thermostat.attributes.OccupiedHeatingSetpoint:read(device))
62+
end)
63+
end
64+
65+
-- Configure battery-optimized reporting for NodOn TRV
66+
local function do_configure(driver, device)
67+
device:configure()
68+
69+
-- Bind clusters
70+
device:send(device_management.build_bind_request(device, Thermostat.ID, driver.environment_info.hub_zigbee_eui))
71+
device:send(device_management.build_bind_request(device, PowerConfiguration.ID, driver.environment_info.hub_zigbee_eui))
72+
73+
-- Configure battery-optimized reporting
74+
-- Temperature: report every 5s-1h, when it changes by 0.5°C (50 = 0.50°C in 0.01°C units)
75+
device:send(Thermostat.attributes.LocalTemperature:configure_reporting(device, 5, 3600, 50))
76+
77+
-- Setpoint: report every 1s-24h, when it changes by 0.5°C
78+
device:send(Thermostat.attributes.OccupiedHeatingSetpoint:configure_reporting(device, 1, 86400, 50))
79+
80+
-- Mode: report every 1s-24h, when it changes
81+
device:send(Thermostat.attributes.SystemMode:configure_reporting(device, 1, 86400, 1))
82+
83+
-- Battery: report every 5min-24h, when it changes by 1% (2 = 1% in 0-200 scale)
84+
device:send(PowerConfiguration.attributes.BatteryPercentageRemaining:configure_reporting(device, 300, 86400, 2))
85+
86+
-- Read device capabilities
87+
device:send(Thermostat.attributes.ControlSequenceOfOperation:read(device))
88+
device:send(Thermostat.attributes.MinHeatSetpointLimit:read(device))
89+
device:send(Thermostat.attributes.MaxHeatSetpointLimit:read(device))
90+
end
91+
92+
local nodon_trv_thermostat = {
93+
NAME = "NodOn Thermostatic Radiator Valve Handler",
94+
capability_handlers = {
95+
[capabilities.thermostatHeatingSetpoint.ID] = {
96+
[capabilities.thermostatHeatingSetpoint.commands.setHeatingSetpoint.NAME] = set_heating_setpoint_handler
97+
}
98+
},
99+
zigbee_handlers = {
100+
attr = {
101+
[PowerConfiguration.ID] = {
102+
[PowerConfiguration.attributes.BatteryPercentageRemaining.ID] = battery_defaults.battery_percentage_handler
103+
}
104+
}
105+
},
106+
lifecycle_handlers = {
107+
doConfigure = do_configure
108+
},
109+
can_handle = is_nodon_trv_thermostat
110+
}
111+
112+
return nodon_trv_thermostat

0 commit comments

Comments
 (0)