feat: add roller mode for switch2pm#457
Conversation
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR adds roller/cover mode support for SwitchbotRelaySwitch2PM by having it additionally inherit from SwitchbotBaseCover. It also refactors the SwitchbotBaseCover constructor to accept reverse as a keyword argument, updates the advertisement parser for 2PM to include mode, position, and calibration fields, and updates tests accordingly.
Changes:
SwitchbotRelaySwitch2PMnow inherits from bothSwitchbotRelaySwitchandSwitchbotBaseCover, gaining cover/roller commands (open,close,stop,set_position) and properties (position,mode).SwitchbotBaseCover.__init__changedreversefrom a positional to a keyword argument; all subclass call sites updated.- Advertisement parser and
_parse_common_dataextended withmode,position,calibrationfields for 2PM devices.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
switchbot/devices/base_cover.py |
Refactored constructor to accept reverse as keyword-only via kwargs.pop |
switchbot/devices/relay_switch.py |
Added cover inheritance, roller commands, position/mode properties, motion direction logic, and refactored 2PM get_basic_info |
switchbot/adv_parsers/relay_switch.py |
Added mode, position, calibration fields to 2PM advertisement parser |
switchbot/devices/curtain.py |
Updated super().__init__ call for new keyword arg signature |
switchbot/devices/blind_tilt.py |
Updated super().__init__ call for new keyword arg signature |
switchbot/devices/roller_shade.py |
Updated super().__init__ call for new keyword arg signature |
tests/test_relay_switch.py |
Added tests for cover operations, position, mode, and motion direction |
tests/test_base_cover.py |
Updated SwitchbotBaseCover instantiation to match new keyword arg signature |
tests/test_adv_parser.py |
Added expected mode, position, calibration fields to 2PM test cases |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
|
Out of time to finish reviewing this toda. Will try to pickup reviews on this repo later this week. |
PR sblibs#457 changed `SwitchbotBaseCover.__init__` to read `reverse` from `**kwargs` so it can travel through the MRO when `SwitchbotRelaySwitch2PM` inherits from both `SwitchbotRelaySwitch` and `SwitchbotBaseCover`. That silently broke any external caller passing `reverse` as a leading positional arg — `reverse` would be ignored and the bool would be forwarded to `super().__init__` as `device`. Restore backward compatibility by accepting either form: a leading positional bool (legacy) or a `reverse=` kwarg (preferred). The bool sniff is unambiguous because the only other candidate for the first positional is `BLEDevice`. Adds tests covering the legacy positional form, the kwarg form, and the omitted-default form.
| return success | ||
|
|
||
| @update_after_operation | ||
| async def set_position(self, position: int, mode: int = 0) -> bool: |
There was a problem hiding this comment.
This doesn't appear to honor reverse
There was a problem hiding this comment.
Now honored. set_position applies 100 - position when reversed.
| return success | ||
|
|
||
| @update_after_operation | ||
| async def set_position(self, position: int, mode: int = 0) -> bool: |
There was a problem hiding this comment.
Resolved. mode parameter removed from set_position.
bdraco
left a comment
There was a problem hiding this comment.
Please see review comments above
|
Thanks for the detailed review! All issues have been addressed in the latest commit (c62e071): Copilot comments:
@bdraco comments:
Also added a regression test ( |
|
|
bluetoothbot
left a comment
There was a problem hiding this comment.
Blocking issues found.
- COMMAND_OPEN / COMMAND_CLOSE are dead code
- Two different encodings for "position" (basic-info vs advertisement)
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
switchbot/devices/relay_switch.py:214
SwitchbotRelaySwitch2PMnow inheritsSwitchbotBaseCover, but unlike the other cover devices it does not consumereverse_mode(orreverse) from**kwargs. BecauseSwitchbotBaseCover.__init__only popsreverse, passingreverse_mode=to the constructor will be silently ignored, making it impossible to flip direction consistently withSwitchbotCurtain/SwitchbotRollerShade/SwitchbotBlindTilt.
class SwitchbotRelaySwitch2PM(SwitchbotRelaySwitch, SwitchbotBaseCover):
"""Representation of a Switchbot relay switch 2pm."""
_model = SwitchbotModel.RELAY_SWITCH_2PM
_channel: int = 2
| def process_relay_switch_2pm( | ||
| data: bytes | None, mfr_data: bytes | None | ||
| ) -> dict[int, dict[str, Any]]: | ||
| """Process Relay Switch 2PM services data.""" | ||
| if mfr_data is None: | ||
| # Highest index read below is mfr_data[14] (roller position), so guard | ||
| # against truncated advertisements that would otherwise raise IndexError. | ||
| if mfr_data is None or len(mfr_data) < 15: | ||
| return {} |
| def _parse_common_data(self, raw_data: bytes) -> dict[str, Any]: | ||
| """Parse common data from raw bytes.""" | ||
| return { | ||
| "sequence_number": raw_data[1], | ||
| "isOn": bool(raw_data[2] & SWITCH1_ON_MASK), | ||
| "firmware": raw_data[16] / 10.0, |
|
All review feedback addressed — summary of changes in this push:
Tests: 55 relay-switch tests pass, 1238 total pass. ruff check + format clean. |
- Add open/close/set_position commands for 2PM roller/cover mode - Add _send_position() helper to deduplicate position command logic - Fix type annotation in adv_parsers/relay_switch.py - Clean up dead code (COMMAND_OPEN/COMMAND_CLOSE) - Add comprehensive tests for roller mode and short-response guards
|
All review feedback has been addressed:
Could you please take another look? Thanks! |
|
|
bluetoothbot
left a comment
There was a problem hiding this comment.
Blocking issues found.
- Two different position encodings reach Home Assistant for the same logical value
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Fixed the position encoding inconsistency flagged by bluetoothbot. Root cause: Fix: Removed the Changes:
All CI checks passing. |
Range aligned, source still unconfirmed. Advertisement reads |
PR Review — feat: add roller mode for switch2pmSolid mechanics; the remaining risk is position semantics, which only a hardware capture can settle. What's done well:
What needs attention:
🟡 Important
1. Position from advertisement (mfr_data[14]) and basic_info (raw_data[9]) are unverified to share the same scale
|
bluetoothbot
left a comment
There was a problem hiding this comment.
Blocking issues found.
- Position from advertisement (mfr_data[14]) and basic_info (raw_data[9]) are unverified to share the same scale
- 2PM position orientation is inverted relative to sibling covers / HA convention
Summary
Add roller/cover mode support for
SwitchbotRelaySwitch2PMand fix related test cases.Changes
Features
SwitchbotRelaySwitch2PMnow inherits fromSwitchbotBaseCover, enabling cover/roller control support.open(),close(),stop(),set_position()commands for roller mode.positionandmodeproperties toSwitchbotRelaySwitch2PM.COMMAND_OPEN,COMMAND_CLOSE,COMMAND_POSITION,COMMAND_STOP.Parser
process_relay_switch_2pm: Addedmode,position,calibrationfields to both channel 1 and channel 2 advertisement data.Refactor
SwitchbotBaseCover.__init__: Changedreverseto a keyword-only argument, updated all subclass call sites (curtain.py,blind_tilt.py,roller_shade.py)._parse_common_datainSwitchbotRelaySwitch:sequence_numbernow reads fromraw_data[0](wasraw_data[1]).Tests
test_adv_parser.py: Added expectedmode,position,calibrationfields toSwitchbotRelaySwitch2PMtest cases.test_base_cover.py: FixedSwitchbotBaseCoverinstantiation to use new keyword argument signature.