Skip to content

Conversation

uvNikita
Copy link
Contributor

@uvNikita uvNikita commented Jun 17, 2025

Proposed change

  • Convert to quirks v2
  • Define missing attributes using spec doc
  • Add support for extra models (NHPB/SHUTTER/1 and PUCK/SHUTTER/1)
  • Expose extra entities to home assistant

Fixes #1685
Supersedes #3939

Additional information

#1705
ZB Spec - Micro Module Shutter Blinds - 110422.pdf

Checklist

  • The changes are tested and work correctly
  • pre-commit checks pass / the code has been formatted using Black
  • Tests have been added to verify that the new code works

Copy link

codecov bot commented Jun 17, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 91.24%. Comparing base (e3124b5) to head (a72e3c8).

Additional details and impacted files
@@           Coverage Diff           @@
##              dev    #4130   +/-   ##
=======================================
  Coverage   91.24%   91.24%           
=======================================
  Files         338      338           
  Lines       10921    10925    +4     
=======================================
+ Hits         9965     9969    +4     
  Misses        956      956           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@uvNikita
Copy link
Contributor Author

Unfortunately I didn't see #3939 until creating the PR, but this one should include everything #3939 has + quirks v2, extra models and exposed entities.

@uvNikita uvNikita force-pushed the se/shutter branch 2 times, most recently from ad1b567 to fa10863 Compare June 18, 2025 08:04
@TheJulianJES TheJulianJES added the needs review This PR should be reviewed soon, as it generally looks good. label Jun 19, 2025
@Mookunicorn
Copy link

Do you have any idea if it's possible to add a choice for the curtain type? It's currently "Tilt_blind_tilt_and_lift" when it should be an awning?

I saw in the documentation that you posted that only the following values ​​can be chosen:
0x00 = Rollershade (LIFT ONLY)
0x08 = Tilt Blind-Lift and Tilt (LIFT AND TILT)

In my case, the Rollershade would be suitable for my situation, but when I try to change it via ZHA (on the Cluster "SEWindowCovering (Endpoint id: 5, Id: 0x0102, Type: in)" and the attribute "window_covering_type (id: 0x0000)" to 0, nothing happens.

@uvNikita
Copy link
Contributor Author

@Mookunicorn I don't have this shutter modules, so couldn't try it myself yet. But I'm planning to order them the following mouths.

Can you try to set the value to WindowCoveringType.Rollershade in "Manage Zigbee Device"? If it works, I can add this option as an entity too.

@uvNikita
Copy link
Contributor Author

uvNikita commented Jun 23, 2025

@Mookunicorn

Actually, it looks like this attribute is readonly, but in the spec doc it says the following:

The WindowCoveringType attribute identifies the type of window covering being controlled by this endpoint.
If you set TiltOpenCloseAndStepTime attribute to 0, WindowCoveringType is set automaticaly to 0 (lift only), if TiltOpenCloseAndStepTime attribute is non zero, value of
WindowCoveringType attribute is automaticaly set 8. Meaning of values is following

Have you tried setting TiltOpenCloseAndStepTime to 0?

@Mookunicorn
Copy link

@Mookunicorn I don't have this shutter modules, so couldn't try it myself yet. But I'm planning to order them the following mouths.

Can you try to set the value to WindowCoveringType.Rollershade in "Manage Zigbee Device"? If it works, I can add this option as an entity too.

Ok it works using "WindowCoveringType.Rollershade" and I feel slightly stupid right now ☠️

@Mookunicorn
Copy link

@Mookunicorn

Actually, it looks like this attribute is readonly, but in the spec doc it says the following:

The WindowCoveringType attribute identifies the type of window covering being controlled by this endpoint.
If you set TiltOpenCloseAndStepTime attribute to 0, WindowCoveringType is set automaticaly to 0 (lift only), if TiltOpenCloseAndStepTime attribute is non zero, value of
WindowCoveringType attribute is automaticaly set 8. Meaning of values is following

Have you tried setting TiltOpenCloseAndStepTime to 0?

After some tests, I noticed that changing the value of TiltOpenCloseAndStepTime to anything other than 0 does not produce any changes. However, when I read the attribute via "Manage Zigbee Device", it appears as Tilt_blind_tilt_and_lift.

The same principle applies to Rollershade.

@uvNikita
Copy link
Contributor Author

So just to check if I understand it correctly.

  • Writing the value WindowCoveringType.Rollershade to WindowCoveringType doesn't work, only reading it?
  • Changing the value of TiltOpenCloseAndStepTime works, but seemingly doesn't change the type of the blinds until you reread WindowCoveringType?

@Mookunicorn
Copy link

That's right, I can try in the evening to change the value of TiltOpenCloseAndStepTime and see how long it updates (if there is a delay).

Comment on lines 77 to 140
async def test_1gang_shutter_1_go_to_lift_percentage_cmd(zigpy_device_from_quirk):
"""Asserts that the go_to_lift_percentage command inverts the percentage value."""

device = zigpy_device_from_quirk(
zhaquirks.schneiderelectric.shutters.OneGangShutter1
)
window_covering_cluster = device.endpoints[5].window_covering

p = mock.patch.object(window_covering_cluster, "request", mock.AsyncMock())
with p as request_mock:
request_mock.return_value = (foundation.Status.SUCCESS, "done")

await window_covering_cluster.go_to_lift_percentage(58)

assert request_mock.call_count == 1
assert request_mock.call_args[0][1] == (
WindowCovering.ServerCommandDefs.go_to_lift_percentage.id
)
assert request_mock.call_args[0][3] == 42 # 100 - 58


async def test_1gang_shutter_1_unpatched_cmd(zigpy_device_from_quirk):
"""Asserts that unpatched ZCL commands keep working."""

device = zigpy_device_from_quirk(
zhaquirks.schneiderelectric.shutters.OneGangShutter1
)
window_covering_cluster = device.endpoints[5].window_covering

p = mock.patch.object(window_covering_cluster, "request", mock.AsyncMock())
with p as request_mock:
request_mock.return_value = (foundation.Status.SUCCESS, "done")

await window_covering_cluster.up_open()

assert request_mock.call_count == 1
assert request_mock.call_args[0][1] == (
WindowCovering.ServerCommandDefs.up_open.id
)


async def test_1gang_shutter_1_lift_percentage_updates(zigpy_device_from_quirk):
"""Asserts that updates to the ``current_position_lift_percentage`` attribute.
(e.g., by the device) invert the reported percentage value.
"""

device = zigpy_device_from_quirk(
zhaquirks.schneiderelectric.shutters.OneGangShutter1
)
window_covering_cluster = device.endpoints[5].window_covering
cluster_listener = ClusterListener(window_covering_cluster)

window_covering_cluster.update_attribute(
WindowCovering.AttributeDefs.current_position_lift_percentage.id,
77,
)

assert len(cluster_listener.attribute_updates) == 1
assert cluster_listener.attribute_updates[0] == (
WindowCovering.AttributeDefs.current_position_lift_percentage.id,
23, # 100 - 77
)
assert len(cluster_listener.cluster_commands) == 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ideally, we should keep the tests (except for the signature test). You should be able to use zigpy_device_from_v2_quirk for the tests. Might need to search the repo for some examples.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, done.

@uvNikita uvNikita force-pushed the se/shutter branch 3 times, most recently from e622641 to 721c3f9 Compare June 24, 2025 10:43
@uvNikita
Copy link
Contributor Author

  • Added tests back
  • Added window_covering_type read-only enum entity with reporting_config to hopefully help with automatic update of this attribute when TiltOpenCloseAndStepTime is updated

@Mookunicorn
Copy link

  • Added window_covering_type read-only enum entity with reporting_config to hopefully help with automatic update of this attribute when TiltOpenCloseAndStepTime is updated

It exposes a new entity but it ends up being duplicated. window_covering_type was already read without quirks. And unfortunately neither entity updates when TiltOpenCloseAndStepTime changes.

Screen

image

@uvNikita
Copy link
Contributor Author

It exposes a new entity but it ends up being duplicated. window_covering_type was already read without quirks. And unfortunately neither entity updates when TiltOpenCloseAndStepTime changes.

I was not aware of that, thank you for testing. Removed the duplicate entry again now.

Then I'm not sure what else we can do here. @TheJulianJES let me know if you have any ideas how to make sure window_covering_type is reread on TiltOpenCloseAndStepTime change. Otherwise I think it should be ready to be merged.

Add support for NHPB/SHUTTER/1 and PUCK/SHUTTER/1
Copy link
Collaborator

@TheJulianJES TheJulianJES left a comment

Choose a reason for hiding this comment

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

Looks good IMO. Thanks!

@TheJulianJES
Copy link
Collaborator

TheJulianJES commented Jun 24, 2025

Maybe open an issue for the window_covering_type stuff and tag me there and explain why window_covering_type should change there.

@TheJulianJES TheJulianJES merged commit bd6124a into zigpy:dev Jun 24, 2025
7 checks passed
@uvNikita uvNikita deleted the se/shutter branch June 24, 2025 21:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs review This PR should be reviewed soon, as it generally looks good.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Device Support Request] NHPB/SHUTTER/1 by Schneider Electric
3 participants