SWV-ZF2 (Hydro Duo): irrigation_plan_* properties stay null after pairing, /set validation errors reference wrong field, /get has no converter
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 15.7k
- Forks
- 2k
- Avg merge
- 18h 55m
- Merged PRs (30d)
- 35
Description
What happened?
After pairing/re-pairing a SONOFF SWV-ZF2 (Hydro Duo), all irrigation_plan_* properties for both endpoints remain permanently null in the published state, even right after a successful device interview. This was also observed with update.installed_version/latest_version showing -1 until a manual OTA check was triggered from the frontend, after which those specific fields populated correctly, suggesting some attributes are not read proactively after interview.
Attempting to write any single irrigation_plan_*_1/_2 property (e.g. irrigation_plan_enable_date_1) via /set is rejected with a validation error -but the error frequently references a different field than the one that was actually sent, and persists even with a correctly formatted value (tested with realistic dates and a date near the Zigbee epoch to rule out clock-sync causes).
What did you expect to happen?
Writing a single irrigation_plan_* property should either succeed, or fail with an error referencing the actual invalid field.
How to reproduce it (minimal and precise)
- Pair a SONOFF SWV-ZF2 (Hydro Duo) fresh in Zigbee2MQTT.
- Confirm irrigation_plan_enable_date_1, irrigation_plan_start_time_1, etc. are all null in the published state.
- Publish to zigbee2mqtt/<friendly_name>/set: {"irrigation_plan_enable_date_1": "2026-07-23"}
- Observe log output: zhc:sonoff: irrigation_plan_settings invalid enable_date, expected local date in YYYY-MM-DD format. (despite the value matching the documented format exactly)
- Publish instead: {"irrigation_plan_start_time_1": "05:00"}
- Observe a different error: zhc:sonoff: irrigation_plan_settings invalid start_time, expected HH:mm. (again despite matching the documented format)
- Sending a complete payload with all plan fields for one zone at once produces the same class of errors, with the specific field named in the error varying between attempts.
Zigbee2MQTT version
2.12.1
Adapter firmware version
8.0.2 (EmberZNet)
Adapter
SLZB-06MU, firmware v3.3.1
Setup
Running Z2M in a Docker Container
Device database.db entry
{"id":7,"type":"EndDevice","ieeeAddr":"0xa4c138aa7da2a27c","nwkAddr":6535,"manufId":4742,"manufName":"SONOFF","powerSource":"Battery","modelId":"SWV-ZF2","epList":[1,2],"endpoints":{"1":{"profId":260,"epId":1,"devId":2,"inClusterList":[0,1,3,6,32,64599,64529],"outClusterList":[3,25],"clusters":{"genBasic":{"attributes":{"hwVersion":0,"dateCode":"20260428","swBuildId":"1.0.7"}},"genPollCtrl":{"attributes":{"checkinInterval":14400}},"genOnOff":{"attributes":{"onOff":0}},"customClusterEwelink":{"attributes":{"20504":[0,0,0,0],"valveAbnormalState":0,"childLock":0,"realTimeIrrigationDuration":0,"realTimeIrrigationVolume":0,"hourIrrigationDuration":0,"hourIrrigationVolume":0,"manualDefaultSettings":[0,0,10,0,0,0,0,1,0,0,0,0],"rainDelayEndDatetime":0,"valveAlarmSettings":[7,5,1,5],"seasonalWateringAdjustment":[10,10,10,10,10,10,10,10,10,10,10,10]}},"genPowerCfg":{"attributes":{"batteryPercentageRemaining":200}}},"binds":[{"cluster":32,"type":"endpoint","deviceIeeeAddress":"0x70d07efffece30a1","endpointID":1},{"cluster":6,"type":"endpoint","deviceIeeeAddress":"0x70d07efffece30a1","endpointID":1},{"cluster":1,"type":"endpoint","deviceIeeeAddress":"0x70d07efffece30a1","endpointID":1}],"configuredReportings":[{"cluster":1,"attrId":33,"minRepIntval":3600,"maxRepIntval":65000,"repChange":10}],"meta":{}},"2":{"profId":260,"epId":2,"devId":2,"inClusterList":[0,6],"outClusterList":[],"clusters":{"genBasic":{"attributes":{"hwVersion":0}},"genOnOff":{"attributes":{"onOff":0}},"customClusterEwelink":{"attributes":{"realTimeIrrigationDuration":0,"hourIrrigationDuration":0}}},"binds":[{"cluster":6,"type":"endpoint","deviceIeeeAddress":"0x70d07efffece30a1","endpointID":1}],"configuredReportings":[],"meta":{}}},"appVersion":16,"hwVersion":0,"dateCode":"20260428","swBuildId":"1.0.7","zclVersion":8,"interviewCompleted":true,"interviewState":"SUCCESSFUL","meta":{"configured":"0.0.0"},"lastSeen":1784723431979,"checkinInterval":3600}
Debug log
Notes
Full published state after fresh pairing shows all irrigation_plan_*_1/_2 fields as null. The same device model's single-zone sibling (SWV-ZFE, "Hydro ONE") behaves correctly with the equivalent nested irrigation_plan_settings composite. This issue appears specific to the SWV-ZF2's flattened per-endpoint property structure. Furthermore, attempting to read a plan property via /get also fails: publishing {"irrigation_plan_enable_date_1": ""} to the /get topic returns "No converter available for 'get' 'irrigation_plan_enable_date' (undefined)" in the logs, indicating the read path for this property group is not implemented, in addition to the write-path validation issues described above.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by comparing the SWV-ZFE irrigation_plan_settings handling with the SWV-ZF2 flattened irrigation_plan_*_1/_2 properties, then reproduce the /set validation and /get converter failures described above. Done means irrigation plan fields populate after pairing, writes validate the field actually sent, and reads have a converter.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design, embedded-iot
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100