Skip to content

Comments

Fix cover handling#62

Merged
seime merged 2 commits intoseime:masterfrom
ccutrer:fix-covers
Jan 4, 2026
Merged

Fix cover handling#62
seime merged 2 commits intoseime:masterfrom
ccutrer:fix-covers

Conversation

@ccutrer
Copy link
Contributor

@ccutrer 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.

 * 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>
@seime
Copy link
Owner

seime commented Dec 20, 2025

FYI I'm currently away from my computer, but will take a look ASAP.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 supportsTilt flag

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +174 to +186
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);
}
Copy link

Copilot AI Jan 1, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines 96 to 103
} 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);
Copy link

Copilot AI Jan 1, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
Comment on lines +162 to +172
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);
Copy link

Copilot AI Jan 1, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
@seime seime merged commit a14a439 into seime:master Jan 4, 2026
8 checks passed
@seime
Copy link
Owner

seime commented Jan 4, 2026

Thanks for contributing @ccutrer !

@ccutrer ccutrer deleted the fix-covers branch January 4, 2026 07:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants