# Zigbee2MQTT and TRV602z

**URL:** <https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465>\
**Category:** General\
**Tags:** docker, mqtt\
**Created:** [27 February 2026 12:24 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465 "2026-02-27T12:24:11Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![SomersetSmartHome](https://avatars.discourse-cdn.com/v4/letter/s/22d042/32.png) [@SomersetSmartHome](https://discourse.nodered.org/u/SomersetSmartHome)\
**Post date:** [27 February 2026 12:24 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/1 "2026-02-27T12:24:11Z")

</div>

I haven't used Zigbee before but I need local control of TRV's - my broadband has been poor over the last few months and relying on the Tuya cloud anymore isn't an option for me.

I've installed and run zigbee2mqtt on my RPI5. I use the same Pi for Node Red and MQTT broker, but I don't run Docker. I can run zigbee2mqtt manually by typing

```auto
sudo pnpm start 

```

from the /opt/zigbee2mqtt folder. However if I try running zigbee2mqtt as a service, I get the following response to

```auto
sudo systemctl status zigbee2mqtt.service

× zigbee2mqtt.service - zigbee2mqtt
     Loaded: loaded (/etc/systemd/system/zigbee2mqtt.service; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Fri 2026-02-27 12:00:03 GMT; 34s ago
   Duration: 5ms
 Invocation: 9e7d3815f2fd4e6ebb7542f85c9c7825
    Process: 1055 ExecStart=/opt/zigbee2mqtt/pnpm start (code=exited, status=217/USER)
   Main PID: 1055 (code=exited, status=217/USER)

Feb 27 12:00:03 RPI5Node-Red systemd[1]: zigbee2mqtt.service: Scheduled restart job, restart counter is at 5.
Feb 27 12:00:03 RPI5Node-Red systemd[1]: zigbee2mqtt.service: Start request repeated too quickly.
Feb 27 12:00:03 RPI5Node-Red systemd[1]: zigbee2mqtt.service: Failed with result 'exit-code'.
Feb 27 12:00:03 RPI5Node-Red systemd[1]: Failed to start zigbee2mqtt.service - zigbee2mqtt.

```

My zigbee2mqtt.service file looks like this

```auto
[Unit]
Description=zigbee2mqtt
After=network.target

[Service]
#ExecStart=/usr/bin/npm start
ExecStart=/opt/zigbee2mqtt/pnpm start
WorkingDirectory=/opt/zigbee2mqtt
StandardOutput=inherit
StandardError=inherit
Restart=always
User=pi

[Install]
WantedBy=multi-user.target

```

I'm also struggling to get my head around writing a device specific descriptor for the TRV602z.  
I can see and control the TRV from the zigbee2mqtt dashboard and NR reports the TRV as connected and recognises its 'friendly name' but I can't get it to report status or issue any dp commands.  
I've put a suggested file called trv602z.js in /opt/zigbee2mqtt (also in /data sub folder) which looks like this...

```auto
const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const modernExtend = require('zigbee-herdsman-converters/lib/modernExtend');
const tuya = require('zigbee-herdsman-converters/lib/tuya');
const e = exposes.presets;
const ea = exposes.access;

const fixValueConverter = {
    thermostatScheduleDayMultiDP_TRV601Z: {
        from: (v) => {
            const schedule = [];
            for (let in

```

and added

```auto
external_converters:
  - trv602z.js

```

at the end of configuration.yaml in the /opt/zigbee2mqtt/data folder, but this isn't doing anything.  
Any help appreciated as always.

---

<div class="post-metadata">

**Author:** ![SomersetSmartHome](https://avatars.discourse-cdn.com/v4/letter/s/22d042/32.png) [@SomersetSmartHome](https://discourse.nodered.org/u/SomersetSmartHome)\
**Post date:** [27 February 2026 12:26 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/2 "2026-02-27T12:26:51Z")

</div>

The trv602z.js file goes on beyond what is shown; I'm still working out how to include the whole file in my post....

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [27 February 2026 13:27 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/3 "2026-02-27T13:27:10Z")

</div>

> [@SomersetSmartHome](#):
>
> ```auto
> #ExecStart=/usr/bin/npm start
> ExecStart=/opt/zigbee2mqtt/pnpm start
> 
> ```

Did you change that? Mine uses the first line that is commented out in yours.

Are you sure that is the right path to pnpm? Mine is in /usr/bin/pnpm.

---

<div class="post-metadata">

**Author:** ![SomersetSmartHome](https://avatars.discourse-cdn.com/v4/letter/s/22d042/32.png) [@SomersetSmartHome](https://discourse.nodered.org/u/SomersetSmartHome)\
**Post date:** [27 February 2026 13:54 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/4 "2026-02-27T13:54:28Z")

</div>

Yes, I changed that. I just tried using the original line and the result is the same.

I saw a suggestion somewhere that it was user related hence the message I get when I run

sudo systemctl status zigbee2mqtt.service

`(code=exited, status=217/USER)`

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [27 February 2026 14:00 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/6 "2026-02-27T14:00:40Z")

</div>

Oh, I have just noticed that you are using sudo when you run it manually. You should not need to do that. You should be able to run it without sudo from the user that you used when you installed it. what does this command show?  
`ls -l /opt/zigbee2mqtt/data`

Also  
`ls -l /opt/zigbee2mqtt`

---

<div class="post-metadata">

**Author:** ![SomersetSmartHome](https://avatars.discourse-cdn.com/v4/letter/s/22d042/32.png) [@SomersetSmartHome](https://discourse.nodered.org/u/SomersetSmartHome)\
**Post date:** [27 February 2026 14:56 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/7 "2026-02-27T14:56:23Z")

</div>

chrissaich@RPI5Node-Red:/opt/zigbee2mqtt/data $

```auto
ls -l /opt/zigbee2mqtt/data
total 40
-rw-r--r-- 1 root root 1491 Feb 25 12:10 configuration.example.yaml
-rw-r--r-- 1 root root 658 Feb 26 13:11 configuration.yaml
-rw-r--r-- 1 root root 1491 Feb 25 13:56 configuration.yml
-rw-r--r-- 1 root root 702 Feb 27 13:52 coordinator_backup.json
-rw-r--r-- 1 root root 3127 Feb 27 13:52 database.db
drwxr-xr-x 8 root root 4096 Feb 27 13:50 log
-rw-r--r-- 1 root root 1500 Feb 27 13:52 state.json
-rw-r--r-- 1 root root 10696 Feb 26 13:06 trv602z.js

```

and chrissaich@RPI5Node-Red:/opt/zigbee2mqtt/data $

```auto
ls -l /opt/zigbee2mqtt
total 636
-rw-r--r-- 1 root root 10847 Feb 25 12:10 AGENTS.md
-rw-r--r-- 1 root root 3448 Feb 25 12:10 biome.json
-rw-r--r-- 1 root root 304994 Feb 25 12:10 CHANGELOG.md
-rwxr-xr-x 1 root root 175 Feb 25 12:10 cli.js
-rw-r--r-- 1 root root 5447 Feb 25 12:10 CODE_OF_CONDUCT.md
-rw-r--r-- 1 root root 1350 Feb 25 12:10 CONTRIBUTING.md
drwxr-xr-x 3 root root 4096 Feb 27 13:52 data
drwxr-xr-x 6 root root 4096 Feb 25 12:17 dist
drwxr-xr-x 2 root root 4096 Feb 25 12:10 docker
drwxr-xr-x 2 root root 4096 Feb 25 12:10 images
-rw-r--r-- 1 root root 5248 Feb 25 12:10 index.js
drwxr-xr-x 6 root root 4096 Feb 25 12:10 lib
-rw-r--r-- 1 root root 35141 Feb 25 12:10 LICENSE
drwxr-xr-x 7 root root 4096 Feb 25 12:16 node_modules
-rw-r--r-- 1 root root 3304 Feb 25 12:10 package.json
-rw-r--r-- 1 root root 86164 Feb 25 12:10 pnpm-lock.yaml
-rw-r--r-- 1 root root 7291 Feb 25 12:10 README.md
-rw-r--r-- 1 root root 375 Feb 25 12:10 release-please-config.json
drwxr-xr-x 2 root root 4096 Feb 26 13:43 scripts
drwxr-xr-x 5 root root 4096 Feb 25 12:10 test
-rw-r--r-- 1 root root 10696 Feb 26 13:41 trv602z.js
-rw-r--r-- 1 root root 623 Feb 25 12:10 tsconfig.json
-rw-r--r-- 1 root root 101712 Feb 25 12:17 tsconfig.tsbuildinfo
-rw-r--r-- 1 root root 100 Feb 25 12:10 tsconfig.types.json
-rwxr-xr-x 1 root root 1626 Feb 25 12:10 update.sh

```

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [27 February 2026 15:10 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/8 "2026-02-27T15:10:34Z")

</div>

Which instructions did you follow to install it? They shouldn't be owned by root.

---

<div class="post-metadata">

**Author:** ![SomersetSmartHome](https://avatars.discourse-cdn.com/v4/letter/s/22d042/32.png) [@SomersetSmartHome](https://discourse.nodered.org/u/SomersetSmartHome)\
**Post date:** [27 February 2026 15:17 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/9 "2026-02-27T15:17:53Z")

</div>

I used the instructions on zigbee2mqtt site for Linux

```auto
https://www.zigbee2mqtt.io/guide/installation/01_linux.html

```

I previously didn't have a user called 'pi'. I have just created one. The response from running  
sudo systemctl status zigbee2mqtt has changed

```auto
× zigbee2mqtt.service - zigbee2mqtt
     Loaded: loaded (/etc/systemd/system/zigbee2mqtt.service; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Fri 2026-02-27 15:10:23 GMT; 41s ago
   Duration: 499ms
 Invocation: b739fe601f9c4dae84153ebf349b0bd0
    Process: 1269 ExecStart=/usr/bin/pnpm start (code=exited, status=1/FAILURE)
   Main PID: 1269 (code=exited, status=1/FAILURE)

Feb 27 15:10:23 RPI5Node-Red systemd[1]: zigbee2mqtt.service: Scheduled restart job, restart counter is at 5.
Feb 27 15:10:23 RPI5Node-Red systemd[1]: zigbee2mqtt.service: Start request repeated too quickly.
Feb 27 15:10:23 RPI5Node-Red systemd[1]: zigbee2mqtt.service: Failed with result 'exit-code'.
Feb 27 15:10:23 RPI5Node-Red systemd[1]: Failed to start zigbee2mqtt.service - zigbee2mqtt.

```

Now Status 1/Failure,  
Previously Status 217/USER

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [27 February 2026 15:27 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/10 "2026-02-27T15:27:26Z")

</div>

I assume you used sudo when running the install script, hence all the files are owned by root, which is why it is now failing. I wouldn't use a user called pi as that is the default on the pi and any hacker will try and attack that user. If you want to run it as your normal user, which appears to be chrissaich, then change all the files to be owned by yourself

```auto
cd /opt
sudo chown -R chrissaich:chrissaich zigbee2mqtt

```

then change the user in the service file to match that, and don't use sudo when starting it manually (or it will set any files that it writes back to root again).

---

<div class="post-metadata">

**Author:** ![SomersetSmartHome](https://avatars.discourse-cdn.com/v4/letter/s/22d042/32.png) [@SomersetSmartHome](https://discourse.nodered.org/u/SomersetSmartHome)\
**Post date:** [27 February 2026 16:24 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/11 "2026-02-27T16:24:00Z")

</div>

Yep, that's worked. Thanks very much!

How are you on descriptor files for non-standard zigbee devices? (That was the second part of my original question 😉

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [27 February 2026 17:09 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/12 "2026-02-27T17:09:07Z")

</div>

According to [this](https://www.zigbee2mqtt.io/devices/TRV602Z.html) it should be recognised by the standard software, though it seems that there are [some issues](https://github.com/Koenkk/zigbee2mqtt/discussions/25269). It should basically function, I think, without the workaround there (is that the one you have tried to use), so I would first get it going without the workaround. It should report its status without the workaround. Have you tried using MQTTExplorer, which is invaluable in such cases, as it will easily show you exactly what it is publishing.

---

<div class="post-metadata">

**Author:** ![SomersetSmartHome](https://avatars.discourse-cdn.com/v4/letter/s/22d042/32.png) [@SomersetSmartHome](https://discourse.nodered.org/u/SomersetSmartHome)\
**Post date:** [27 February 2026 17:39 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/13 "2026-02-27T17:39:20Z")

</div>

Definitely getting closer.

I seem to be making progress by using the MQTT In and Out nodes. Whenever a change occurs on the TRV, an MQTT In node reports back and I can read;

```auto
zigbee2mqtt/TRV1 : msg.payload : Object
object
battery: 47
boost_timeset_countdown: 0
child_lock: "UNLOCK"
comfort_temperature: 19
current_heating_setpoint: 19.5
display_brightness: "high"
eco_temperature: 15
frost_protection: "ON"
holiday_temperature: 5
hysteresis: "comfort"
linkquality: 97
local_temperature: 18.2
local_temperature_calibration: 0
max_temperature: 30
min_temperature: 5
motor_thrust: "middle"
position: 61
preset: "auto"
running_mode: "auto"
running_state: "heat"
schedule_friday: "06:00/19.0 08:00/15.0 12:00/19.0 14:00/15.0 18:00/19.0 22:00/15.0"
schedule_monday: "06:00/19.0 08:00/15.0 12:00/19.0 14:00/15.0 18:00/19.0 22:00/15.0"
schedule_saturday: "06:00/19.0 08:00/15.0 12:00/19.0 14:00/15.0 18:00/19.0 22:00/15.0"
schedule_sunday: "06:00/19.0 08:00/15.0 12:00/19.0 14:00/15.0 18:00/19.0 22:00/15.0"
schedule_thursday: "06:00/19.0 08:00/15.0 12:00/19.0 14:00/15.0 18:00/19.0 22:00/15.0"
schedule_tuesday: "06:00/19.0 08:00/15.0 12:00/19.0 14:00/15.0 18:00/19.0 22:00/15.0"
schedule_wednesday: "06:00/19.0 08:00/15.0 12:00/19.0 14:00/15.0 18:00/19.0 22:00/15.0"
screen_orientation: "down"
system_mode: "auto"
window: "CLOSE"
window_detection: "OFF"

```

I need to read 'battery', 'position' and 'local\_temperature'.  
I want to set 'current\_heating\_setpoint'

I don't know how to extract the input values or how to set 'current\_heating\_setpoint'

---

<div class="post-metadata">

**Author:** ![dynamicdave](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dynamicdave/32/96_2.png) [@dynamicdave](https://discourse.nodered.org/u/dynamicdave)\
**Post date:** [27 February 2026 18:30 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/14 "2026-02-27T18:30:56Z")

</div>

That looks like a JavaScript object, so using a function node in Node-RED you could extract, for example the value of battery, with this...

```auto
let battery = msg.payload.battery;
msg.battery = battery; // optional: store it elsewhere in msg
return msg;

```

You could do the same for 'position' and 'local\_temperature'.

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [27 February 2026 18:43 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/15 "2026-02-27T18:43:22Z")

</div>

Or you can do the same using a Change node. Have a look at the node red docs 'working with messages'

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [27 February 2026 20:46 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/16 "2026-02-27T20:46:21Z")

</div>

Checkout [Tuya TRV601 control via MQTT | Zigbee2MQTT](https://www.zigbee2mqtt.io/devices/TRV601.html)

```auto
current_heating_setpoint: Temperature setpoint. To control publish a message to topic
zigbee2mqtt/FRIENDLY_NAME/set with payload {"current_heating_setpoint": VALUE}
where VALUE is the °C between 5 and 35. Reading (/get) this attribute is not possible

```

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [27 February 2026 22:01 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/17 "2026-02-27T22:01:05Z")

</div>

Also try subscribing to `zigbee2mqtt/TRV1/#` to see if there is a regular state update posted.

---

<div class="post-metadata">

**Author:** ![Buckskin](https://avatars.discourse-cdn.com/v4/letter/b/b9e5f3/32.png) [@Buckskin](https://discourse.nodered.org/u/Buckskin)\
**Post date:** [27 February 2026 23:02 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/18 "2026-02-27T23:02:29Z")

</div>

Note; You cannot `'get'` or `'set'` either `battery` or `local_temperature` they can only be read when a TRV update occurs. (as shown in your `Definitely getting closer` post and dynamicdave's example)

`current_heating_setpoint` can only be `'set'`, again it can only be read when the TRV updates

with an `inject` node

```auto
msg.topic = 'zigbee2mqtt/TRV1/set'
msg.payload = {"current_heating_setpoint": 20}

```

---

<div class="post-metadata">

**Author:** ![SomersetSmartHome](https://avatars.discourse-cdn.com/v4/letter/s/22d042/32.png) [@SomersetSmartHome](https://discourse.nodered.org/u/SomersetSmartHome)\
**Post date:** [1 March 2026 10:07 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/19 "2026-03-01T10:07:47Z")

</div>

Thanks for all the help, guys, I'm up and running! 👍

 ![zigbee trv](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/6/b/6bf48cf19f4d304e7a4fde728c7c7541549fee0e.jpeg)

```auto
let battery = msg.payload.battery;
msg.payload = battery; // optional: store it elsewhere in msg
return msg;

```

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [30 May 2026 10:08 UTC](https://discourse.nodered.org/t/zigbee2mqtt-and-trv602z/100465/20 "2026-05-30T10:08:35Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
