Pilot wire heating module - Powernity

Hello,

Following on from this topic, I am making the DDF file I created available. It does not allow you to use all the device’s features or read all its information, but the main features I wanted are there, namely:

  • Mode control (comfort, eco, off, etc.)
  • Child lock
  • Temperature
  • Humidity

I’m not sure if this file is correct, as I’m really just a hobbyist.

I haven’t had time to use it with multiple devices over a long period of time yet, but if I encounter any problems, I will post them here.

DDF file : :white_check_mark: Solution

{
    "schema": "devcap1.schema.json",
    "manufacturername": "_TZE204_d6i25bwg",
    "modelid": "TS0601",
    "vendor": "Tuya",
    "product": "Pilot_wire_heating_module",
    "sleeper": false,
    "status": "Gold",
    "ddfvalidate": false,
    "subdevices": [
        {
            "type": "$TYPE_THERMOSTAT",
            "restapi": "/sensors",
            "uuid": [
                "$address.ext",
                "0x01",
                "0x0201"
            ],
            "meta": {
                "values": {
                    "config/mode": {
                        "confort": 0,
                        "confort-1": 1,
                        "confort-2": 2,
                        "eco": 3,
                        "hors gel": 4,
                        "off": 5
                    }
                }
            },
            "items": [
                {
                    "name": "attr/swversion",
                    "parse": {
                        "fn": "zcl:attr",
                        "ep": 1,
                        "cl": "0x0000",
                        "at": "0x0001",
                        "script": "../tuya/tuya_swversion.js"
                    },
                    "read": {
                        "fn": "zcl:attr",
                        "ep": 1,
                        "cl": "0x0000",
                        "at": "0x0001"
                    }
                },
                {
                    "name": "attr/id"
                },
                {
                    "name": "attr/lastannounced"
                },
                {
                    "name": "attr/lastseen"
                },
                {
                    "name": "attr/manufacturername"
                },
                {
                    "name": "attr/modelid"
                },
                {
                    "name": "attr/productname",
                    "default": "Powernity_PO-BOCO-ELEC"
                },
                {
                    "name": "attr/type"
                },
                {
                    "name": "attr/uniqueid"
                },
                {
                    "name": "config/locked",
                    "read": {
                        "fn": "none"
                    },
                    "write": {
                        "dpid": 39,
                        "dt": "0x10",
                        "eval": "Item.val;",
                        "fn": "tuya"
                    },
                    "parse": {
                        "dpid": 39,
                        "eval": "Item.val = Attr.val;",
                        "fn": "tuya"
                    },
                    "default": false
                },
                {
                    "name": "config/mode",
                    "read": {
                        "fn": "tuya"
                    },
                    "write": {
                        "dpid": 127,
                        "dt": "0x30",
                        "eval": "if (Item.val == 'confort') {0} else if (Item.val == 'confort-1') {1} else if (Item.val == 'confort-2') {2} else if (Item.val == 'eco') {3} else if (Item.val == 'hors gel') {4} else if (Item.val == 'off') {5}",
                        "fn": "tuya"
                    },
                    "parse": {
                        "dpid": 127,
                        "eval": "if (Attr.val == 0) { Item.val = 'confort';} else if (Attr.val == 1) { Item.val = 'confort-1';} else if (Attr.val == 2) { Item.val = 'confort-2';} else if (Attr.val == 3) { Item.val = 'eco';} else if (Attr.val == 4) { Item.val = 'hors gel';} else if (Attr.val == 5) { Item.val = 'off';}",
                        "fn": "tuya"
                    }
                },
                {
                    "name": "config/on"
                },
                {
                    "name": "config/reachable"
                },
                {
                    "name": "state/temperature",
                    "read": {
                        "fn": "none"
                    },
                    "parse": {
                        "dpid": 16,
                        "eval": "Item.val = Attr.val * 10;",
                        "fn": "tuya"
                    }
                },
                {
                    "name": "state/lastupdated"
                }
            ]
        },
        {
            "type": "$TYPE_HUMIDITY_SENSOR",
            "restapi": "/sensors",
            "uuid": [
                "$address.ext",
                "0x01",
                "0x0405"
            ],
            "fingerprint": {
                "endpoint": "0x02",
                "profile": "0x0104",
                "device": "0x0107",
                "in": [
                    "0x0402"
                ]
            },
            "items": [
                {
                    "name": "attr/swversion",
                    "parse": {
                        "fn": "zcl:attr",
                        "ep": 1,
                        "cl": "0x0000",
                        "at": "0x0001",
                        "script": "../tuya/tuya_swversion.js"
                    },
                    "read": {
                        "fn": "zcl:attr",
                        "ep": 1,
                        "cl": "0x0000",
                        "at": "0x0001"
                    }
                },
                {
                    "name": "attr/id"
                },
                {
                    "name": "attr/lastannounced"
                },
                {
                    "name": "attr/lastseen"
                },
                {
                    "name": "attr/manufacturername"
                },
                {
                    "name": "attr/modelid"
                },
                {
                    "name": "attr/productname",
                    "default": "Powernity_PO-BOCO-ELEC"
                },
                {
                    "name": "attr/type"
                },
                {
                    "name": "attr/uniqueid"
                },
                {
                    "name": "config/on"
                },
                {
                    "name": "config/reachable"
                },
                {
                    "name": "state/humidity",
                    "read": {
                        "fn": "none"
                    },
                    "parse": {
                        "dpid": 8,
                        "eval": "Item.val = Attr.val * 100;",
                        "fn": "tuya"
                    },
                    "default": 0
                },
                {
                    "name": "state/lastupdated"
                }
            ]
        }
    ]
}

Preview in Phoscon

{
    "capabilities": {
        "sleeper": false
    },
    "config": {
        "locked": false,
        "mode": "confort",
        "on": true,
        "reachable": true
    },
    "etag": "56ad954d63c85f547dae3e1c8fb6be75",
    "lastannounced": "2025-08-15T12:50:49Z",
    "lastseen": "2025-08-15T15:44Z",
    "manufacturername": "_TZE204_d6i25bwg",
    "modelid": "TS0601",
    "name": "Capteur",
    "nwkaddress": "0x736E",
    "productname": "Powernity_PO-BOCO-ELEC",
    "state": {
        "humidity": 56,
        "lastupdated": "2025-08-15T15:45:24.867",
        "temperature": 30
    },
    "swversion": "",
    "type": "ZHAThermostat",
    "uniqueid": "a4:c1:38:da:ba:1c:ce:8e-01-0201"
}

Brand: Powernity
Model: PO-BOCO-ELEC

For me the DDF is fine except you can remove all binds, you are not using them and tuya device generaly don’t use them too.

Do you want to make a PR to submit the device officialy ? or want I make it ?

I think you can add too

“ddfvalidate”: false,

If you have problem when submitting the device. I m not sure it’s autorised to have “humidity” on a ZHAThermostat, but for me it’s better like this (no need to create a ZHAHumidity just for it.

There you go, I modified the DDF as you advised and it works.

However, I don’t understand why the temperature is misinterpreted in Phoscon; it always shows 0.5°C. :thinking:
In Home Assistant, I have the same temperature reading, and I only have three possible modes in the default thermostat. But fortunately, in Home Assistant, I can still control the modes correctly with the REST-API commands.

A PR ? Is that what I started doing here last time ? :sweat_smile:

I think it’s the defaut value

"default": 50

But the dpid is good (16), and the humidity is working.
Perhaps you have a clone with another dpid (or Z2M use the bad one), try with log enabled on deconz (help/debug view) with flag “DDF” “info” and “info_l2” you will see tuya request.

PR are here Pull requests · dresden-elektronik/deconz-rest-plugin · GitHub

You can submit one even your device is not 100% supported.

Yes, I think the temperature dpid is correct because when I heat the device, the temperature rises.
And then state/temperature confirms this.

"state": {
            "humidity": 51,
            "lastupdated": "2025-08-17T11:57:43.760",
            "temperature": 26
        }

For example, state/temperature is at 26°C, but the Phoscon interface shows 0.5°C. :thinking:

The same than in Phoscon ?

Perhaps they are waiting a ZHAThemperature, but all ZHAThermostat have a state/temperature and are working with it, so strange Phoscon is not able to handle them …

If it work in HA don’t care of it, all third app can do what they want with the API result.

Yes, the temperatures are almost the same in the Phoscon interface and in the Home Assistant interface. However, it is displayed correctly in the Phoscon REST API.

Phoscon interface : 0.5°C
Home Assistant interface : 0.3°C
Phoscon REST API : 28°C ( :+1:)

Another strange thing is that I don’t get any feedback in Home Assistant, even when listening to deconz_event. :thinking:

Lol magic.
No it’s not normal, HA can’t imagine the temperature, if you can see a bad temperature it come from something else.

Can be a fake sensor created during your tests but for information temperature in Deconz are * 100 to use decimal, so

    "state": {
        "humidity": 56,
        "lastupdated": "2025-08-15T15:45:24.867",
        "temperature": 30
    },

Mean 0.3 °C.

Try

                {
                    "name": "state/temperature",
                    "read": {
                        "fn": "none"
                    },
                    "parse": {
                        "dpid": 16,
                        "eval": "Item.val = Attr.val * 10;",
                        "fn": "tuya"
                    },
                    "default": 50
                },

Indeed, the temperature and humidity values had to be multiplied by ten. Now the values are displayed correctly in Phoscon and Home Assistant. :+1:

However, I had to create a separate humidity sensor to get the humidity readings in Home Assistant. It works well.

There is still a display issue in Home Assistant: the thermostat only displays three modes instead of five, but that is a Home Assistant issue. Until I find a solution, I have created buttons instead, so it is not a major issue.

Thank you again, @smanar.

And BTW, if all is working now, do you want to make the PR to submit the DDF ? To have it officialy.

Else I can do it, but it will be under my name instead of yours.

If you have a github account it will take 10 mn.
Just try to create a new file here deconz-rest-plugin/devices/tuya at master · dresden-elektronik/deconz-rest-plugin · GitHub
You will have a error message saying you haven’t right, it will create a fork under your name, and start the Pull Request procedure.
No danger, rollbacks possibles (all will be synchronised, a change on your fork will happen on the PR too)

Yes I plan to do the PR (I have a Github account and it will save you to do it)

But wait, I have one last adjustment to make, I think I need to multiply the humidity value by 100 and not by 10, I need to check that again.

1 Like

There you go, I had to multiply by 100. :+1:

Here is the link to the PR request. I’m not sure if I did it right…

Something is wrong,

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

I m seing your change on your fork

So all is fine, but not finished, there is a commit to add the file, but the Pr is not submitted

Return on your fork GitHub - Sekenfo/deconz-rest-plugin at patch-1
Select

  • “Pull request”
  • “New pull request”
  • “Patch-1> master” (so from your:patch-1 fork to main:master)
  • Create

And the rest will appear, choose a title, put a comment and ect … (Remember it’s not a one shoot, you can edit and rollback, so don’t worry for error)

Okay, I continued, and now I see it appearing in the pull requests tab.

Missed ^^.

Sekenfo wants to merge 1 commit into master from patch-1

It’s a PR from your fork (branch patch-1) to your fork again (branch master)
So visible on your fork but not visible on deconz PR list.

The “from” is good

  • branch : patch-1
  • Github : http://github.com/Sekenfo
  • Link = https://github.com/Sekenfo/deconz-rest-plugin/tree/patch-1
  • called too “head repositary”

But the “to” is bad

  • branch : master
  • github : https://github.com/dresden-elektronik
  • Called too “base repositary”

You need to have something like

labor4 wants to merge 4 commits into dresden-elektronik:master from labor4:TS011F-DDF

Here the branch is TS011F-DDF, and the fork is the labor4 one.

I’m really sorry :sweat_smile:, this ends up giving you even more work, but I hope it’s okay this time. There is now one more “Pull request” in dresden-elektronik/deconz-rest-plugin.

If it’s still not right, I’ll leave it up to you @smanar.

It’s fine ^^, and the PR have passed the automatic verification.
Now need to wait the devs validation

Oups have missed something, sorry my bad, haven’t see it before.
You can remove all
"refresh.interval": 3600,

This device use the tuya cluster, for this cluster no need to force a pull and this one is disabled with this part just after

                    "read": {
                        "fn": "tuya"
                    },

So using " refresh.interval" is useless.

and you miss this part on all entries

        {
          "name": "attr/swversion",
          "parse": {
            "fn": "zcl:attr",
            "ep": 1,
            "cl": "0x0000",
            "at": "0x0001",
            "script": "../tuya/tuya_swversion.js"
          },
          "read": {
            "fn": "zcl:attr",
            "ep": 1,
            "cl": "0x0000",
            "at": "0x0001"
          }
        },

But don’t worry now the PR is in place, so will be easy
Just edit the file deconz-rest-plugin/devices/tuya/_TZE204_d6i25bwg_pilote_wire_heating_module.json at patch-1 · Sekenfo/deconz-rest-plugin · GitHub

It’s on your fork, so you have right.
Then the PR will be updated immediatly (it’s synchronised).
I will relaunch the automatic valation after.

There you go, I think I’ve made all the necessary changes to the file and updated it on GitHub. I tested the file at home and it still works fine. :+1:

Please let me know if I need to make any further changes.

All checks are fines.
And I don’t see problems on my side, now need to wait for devs validation.

Thx ^^

For explanation, this device use a ZHAThermostat, but not have heatpoint, so we are forced to use "ddfvalidate": false else the “checker” will refuse the device, not standard for it.
The problem is if we forget an important field like swversion, the checker don’t see it too.