Conversation
ccutrer
commented
Dec 19, 2025
- Don't create a legacy state channel anymore; current ESPHome doesn't even support it.
- ALWAYS create the position channel - even if a cover doesn't support position, it reports and accepts position states/commands of 0 and 1 as closed/open.
- Still send legacy cover commands when sending UP or DOWN to the position channel, in case it's an old device.
- Revert the "always show tilt channel" commit; the has_tilt option in the entities response is accurate.
This reverts commit fc7a5dd.
* Don't create a legacy state channel anymore; current ESPHome doesn't even support it. * ALWAYS create the position channel - even if a cover doesn't support position, it reports and accepts position states/commands of 0 and 1 as closeed/open. * Still send legacy cover commands when sending UP or DOWN to the position channel. Signed-off-by: Cody Cutrer <cody@cutrer.us>
|
FYI I'm currently away from my computer, but will take a look ASAP. |
There was a problem hiding this comment.
Pull request overview
This PR refactors cover handling to align with current ESPHome behavior by removing deprecated legacy state channel support and adjusting how position and tilt channels are created and managed.
Key Changes:
- Removes the legacy state channel that ESPHome no longer supports
- Always creates the position channel for all covers (even those without explicit position support)
- Adds legacy command support when UP/DOWN commands are sent to the position channel for backwards compatibility
- Reverts to conditional tilt channel creation based on the
supportsTiltflag
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (rsp.getSupportsTilt()) { | ||
| semanticTags = createSemanticTags("Tilt", deviceClass); | ||
|
|
||
| ChannelType channelTypeState = addChannelType(rsp.getObjectId() + LEGACY_CHANNEL_STATE, "Legacy State", | ||
| deviceClass.getItemType(), createSemanticTags("OpenClose", deviceClass), icon, rsp.getEntityCategory(), | ||
| rsp.getDisabledByDefault()); | ||
| StateDescription stateDescription = patternStateDescription("%s"); | ||
| ChannelType channelTypeTilt = addChannelType(rsp.getObjectId() + CHANNEL_TILT, "Tilt", | ||
| deviceClass.getItemType(), semanticTags, icon, rsp.getEntityCategory(), rsp.getDisabledByDefault()); | ||
| stateDescription = patternStateDescription("%d %%"); | ||
|
|
||
| Channel channelState = ChannelBuilder.create(createChannelUID(rsp.getObjectId(), LEGACY_CHANNEL_STATE)) | ||
| .withLabel(createLabel(rsp.getName(), "Legacy State")).withKind(ChannelKind.STATE) | ||
| .withType(channelTypeState.getUID()).withAcceptedItemType(deviceClass.getItemType()) | ||
| .withConfiguration(configuration(EntityTypes.COVER, rsp.getKey(), LEGACY_CHANNEL_STATE)).build(); | ||
| super.registerChannel(channelState, channelTypeState, stateDescription); | ||
| Channel channelTilt = ChannelBuilder.create(createChannelUID(rsp.getObjectId(), CHANNEL_TILT)) | ||
| .withLabel(createLabel(rsp.getName(), "Tilt")).withKind(ChannelKind.STATE) | ||
| .withType(channelTypeTilt.getUID()).withAcceptedItemType(deviceClass.getItemType()) | ||
| .withConfiguration(configuration(EntityTypes.COVER, rsp.getKey(), CHANNEL_TILT)).build(); | ||
| super.registerChannel(channelTilt, channelTypeTilt, stateDescription); | ||
| } |
There was a problem hiding this comment.
The change to conditionally create the tilt channel based on the supportsTilt flag (reverting the "always show tilt channel" behavior) should be covered by tests. Consider adding test cases to verify that the tilt channel is only created when supportsTilt is true.
| } else if (command == UpDownType.UP) { | ||
| builder.setHasLegacyCommand(true); | ||
| builder.setLegacyCommand(LegacyCoverCommand.LEGACY_COVER_COMMAND_OPEN); | ||
| builder.setPosition(1); | ||
| } else if (command == UpDownType.DOWN) { | ||
| builder.setHasLegacyCommand(true); | ||
| builder.setLegacyCommand(LegacyCoverCommand.LEGACY_COVER_COMMAND_CLOSE); | ||
| builder.setPosition(0); |
There was a problem hiding this comment.
The new behavior of sending legacy commands (LEGACY_COVER_COMMAND_OPEN and LEGACY_COVER_COMMAND_CLOSE) alongside position values when handling UP and DOWN commands on the position channel lacks test coverage. Consider adding unit tests similar to ClimateMessageHandlerTest to verify this command aggregation behavior.
| Set<String> semanticTags = createSemanticTags("OpenLevel", deviceClass); | ||
|
|
||
| Channel channelPosition = ChannelBuilder.create(createChannelUID(rsp.getObjectId(), CHANNEL_POSITION)) | ||
| .withLabel(createLabel(rsp.getName(), "Position")).withKind(ChannelKind.STATE) | ||
| .withType(channelTypePosition.getUID()).withAcceptedItemType(deviceClass.getItemType()) | ||
| .withConfiguration(configuration(EntityTypes.COVER, rsp.getKey(), CHANNEL_POSITION)).build(); | ||
| super.registerChannel(channelPosition, channelTypePosition, stateDescription); | ||
| } | ||
|
|
||
| Set<String> semanticTags = createSemanticTags("Tilt", deviceClass); | ||
|
|
||
| ChannelType channelTypeTilt = addChannelType(rsp.getObjectId() + CHANNEL_TILT, "Tilt", | ||
| ChannelType channelTypePosition = addChannelType(rsp.getObjectId() + CHANNEL_POSITION, "Position", | ||
| deviceClass.getItemType(), semanticTags, icon, rsp.getEntityCategory(), rsp.getDisabledByDefault()); | ||
| StateDescription stateDescriptionTilt = patternStateDescription("%d %%"); | ||
| StateDescription stateDescription = patternStateDescription("%d %%"); | ||
|
|
||
| Channel channelTilt = ChannelBuilder.create(createChannelUID(rsp.getObjectId(), CHANNEL_TILT)) | ||
| .withLabel(createLabel(rsp.getName(), "Tilt")).withKind(ChannelKind.STATE) | ||
| .withType(channelTypeTilt.getUID()).withAcceptedItemType(deviceClass.getItemType()) | ||
| .withConfiguration(configuration(EntityTypes.COVER, rsp.getKey(), CHANNEL_TILT)).build(); | ||
| super.registerChannel(channelTilt, channelTypeTilt, stateDescriptionTilt); | ||
| Channel channelPosition = ChannelBuilder.create(createChannelUID(rsp.getObjectId(), CHANNEL_POSITION)) | ||
| .withLabel(createLabel(rsp.getName(), "Position")).withKind(ChannelKind.STATE) | ||
| .withType(channelTypePosition.getUID()).withAcceptedItemType(deviceClass.getItemType()) | ||
| .withConfiguration(configuration(EntityTypes.COVER, rsp.getKey(), CHANNEL_POSITION)).build(); | ||
| super.registerChannel(channelPosition, channelTypePosition, stateDescription); |
There was a problem hiding this comment.
The change to always create the position channel regardless of the supportsPosition flag represents a significant behavioral change that should be covered by tests. Consider adding test cases to verify that the position channel is created for all covers, including those where supportsPosition is false.
|
Thanks for contributing @ccutrer ! |