feat: add wintermode switch entity (fixes #331)#332
Conversation
WalkthroughAdds a REST-configurable Robonect Winter Mode switch class and registers it when REST is enabled; updates translations for English, German, French, and Dutch to include Winter Mode labels. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant HA as Home Assistant
participant Switch as RobonectConfigSwitch
participant Entry as ConfigEntry
rect rgb(238,247,255)
note right of HA: Setup (REST enabled)
HA->>Switch: Register Winter Mode (".winter_mode")
Switch->>Entry: Read config entry.data[CONF_WINTER_MODE]
Switch-->>HA: initial state
end
rect rgb(243,255,240)
User->>HA: Toggle Winter Mode
HA->>Switch: async_turn_on / async_turn_off
Switch->>Entry: Update entry.data[CONF_WINTER_MODE]=True/False
Entry-->>Switch: confirm update
Switch-->>HA: State changed (is_on)
end
rect rgb(255,247,230)
HA->>Switch: Query attributes
Switch-->>HA: extra_state_attributes (last_synced, category)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Pre-merge checks (3 passed)✅ Passed checks (3 passed)
Poem
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (5)
custom_components/robonect/switch.py (5)
85-99: Broaden entities list type to include the new config switchYou append RobonectConfigSwitch to a list typed as list[RobonectRestSwitch]. Loosen the annotation to avoid type/lint warnings.
Apply:
- entities: list[RobonectRestSwitch] = [] + entities: list[SwitchEntity] = []
100-100: Nit: log message says “sensors” but this is the switch platformMinor wording fix for clarity.
- _LOGGER.debug("Creating REST sensors") + _LOGGER.debug("Creating REST switches")
396-401: Remove redundant RestoreEntity in MRORobonectEntity already derives from RestoreEntity. Duplicating it is unnecessary.
-class RobonectConfigSwitch(RobonectEntity, SwitchEntity, RestoreEntity): +class RobonectConfigSwitch(RobonectEntity, SwitchEntity):
424-439: Switch state isn’t refreshed after togglingAfter async_update_entry, the UI may not reflect the new state until another update. Refresh the entity state immediately.
async def async_turn_on(self, **kwargs: Any) -> None: """Turn the switch on.""" if self.is_on is True: return if self.entity_description.key == f".{CONF_WINTER_MODE}": new_data = {**self.entry.data, CONF_WINTER_MODE: True} self.hass.config_entries.async_update_entry(self.entry, data=new_data) + self.update_ha_state() async def async_turn_off(self, **kwargs: Any) -> None: """Turn the switch off.""" if self.is_on is False: return if self.entity_description.key == f".{CONF_WINTER_MODE}": new_data = {**self.entry.data, CONF_WINTER_MODE: False} self.hass.config_entries.async_update_entry(self.entry, data=new_data) + self.update_ha_state()
91-96: Optional: align category with semanticsCategory is set to "NONE" and exposed in attributes. Consider "config" for clearer telemetry and uniqueness that reads better in UIs. No functional impact.
- category="NONE", + category="config",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
custom_components/robonect/switch.py(3 hunks)custom_components/robonect/translations/de.json(1 hunks)custom_components/robonect/translations/en.json(1 hunks)custom_components/robonect/translations/fr.json(1 hunks)custom_components/robonect/translations/nl.json(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
custom_components/robonect/switch.py (4)
custom_components/robonect/definitions.py (1)
RobonectSwitchEntityDescription(22-30)custom_components/robonect/entity.py (1)
RobonectEntity(34-128)custom_components/robonect/binary_sensor.py (3)
is_on(139-141)is_on(205-208)extra_state_attributes(230-233)custom_components/robonect/sensor.py (3)
extra_state_attributes(259-264)extra_state_attributes(343-346)extra_state_attributes(382-384)
🔇 Additional comments (5)
custom_components/robonect/translations/en.json (1)
769-772: LGTM: adds switch label for Winter modeLabel matches the entity key and other locales. No issues.
custom_components/robonect/translations/de.json (1)
769-772: LGTM: German switch label added"Wintermodus" aligns with other locales.
custom_components/robonect/translations/fr.json (1)
769-772: LGTM: French switch label added"Mode hiver" consistent across UI.
custom_components/robonect/translations/nl.json (1)
769-772: LGTM: Dutch switch label added"Wintermodus" consistent with other languages.
custom_components/robonect/switch.py (1)
420-431: Defaults and migrations for CONF_WINTER_MODE are correctly implemented
New entries require explicit winter_mode input in the config flow, and existing entries are migrated with winter_mode=False (version <4) in init.py.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
custom_components/robonect/switch.py (1)
417-423: Good fix: safe access to CONF_WINTER_MODEUsing .get(..., False) prevents KeyError on older entries. Thanks for addressing the earlier concern.
🧹 Nitpick comments (3)
custom_components/robonect/switch.py (3)
86-99: Fix entities list type: allow RobonectConfigSwitchentities is annotated as list[RobonectRestSwitch] but now also contains RobonectConfigSwitch; adjust the type to avoid mypy/static typing issues.
- entities: list[RobonectRestSwitch] = [] + entities: list[SwitchEntity] = []
100-100: Nit: make the log reflect both switch typesThis block now adds a config switch as well; tweak the message for clarity.
- _LOGGER.debug("Creating REST switches") + _LOGGER.debug("Creating config and REST switches")
396-401: Unify attribution via a constantOther entities use constants (e.g., ATTRIBUTION_REST/MQTT). Consider adding ATTRIBUTION_CONFIG in const.py and importing it here instead of a magic string for consistency.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
custom_components/robonect/switch.py(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
custom_components/robonect/switch.py (3)
custom_components/robonect/definitions.py (1)
RobonectSwitchEntityDescription(22-30)custom_components/robonect/entity.py (2)
RobonectEntity(34-128)update_ha_state(125-128)custom_components/robonect/binary_sensor.py (2)
is_on(139-141)is_on(205-208)
🔇 Additional comments (2)
custom_components/robonect/switch.py (2)
24-24: LGTM: winter-mode constant importImporting CONF_WINTER_MODE here is correct and needed for the new switch.
394-395: No-op changeOnly whitespace/newlines here; nothing to review.
| async def async_turn_on(self, **kwargs: Any) -> None: | ||
| """Turn the switch on.""" | ||
| if self.is_on is True: | ||
| return | ||
| if self.entity_description.key == f".{CONF_WINTER_MODE}": | ||
| new_data = {**self.entry.data, CONF_WINTER_MODE: True} | ||
| self.hass.config_entries.async_update_entry(self.entry, data=new_data) | ||
| self.update_ha_state() | ||
|
|
||
| async def async_turn_off(self, **kwargs: Any) -> None: | ||
| """Turn the switch off.""" | ||
| if self.is_on is False: | ||
| return | ||
| if self.entity_description.key == f".{CONF_WINTER_MODE}": | ||
| new_data = {**self.entry.data, CONF_WINTER_MODE: False} | ||
| self.hass.config_entries.async_update_entry(self.entry, data=new_data) | ||
| self.update_ha_state() | ||
|
|
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
Verify state propagation to dependent entities
Toggling this switch updates entry.data and this entity’s state, but the winter_mode binary_sensor (reads entry.data directly) may not auto-refresh. Ensure it reacts immediately (dispatcher, update listener, or explicit refresh).
Run to locate other uses and confirm update flow:
If it doesn’t auto-update, I can propose a minimal dispatcher-based update to keep the binary_sensor in sync when the switch toggles.
🏁 Script executed:
#!/bin/bash
# Find all references to CONF_WINTER_MODE and nearby state handling.
rg -nC3 '\bCONF_WINTER_MODE\b' custom_components/robonect
# Inspect binary sensor update hooks that might react to config entry changes.
rg -nC3 'binary_sensor|async_added_to_hass|dispatcher|update_entry' custom_components/robonectLength of output: 20618
Propagate winter_mode changes to the binary_sensor via dispatcher
In custom_components/robonect/switch.py (lines 428–431, 437–440), after calling async_update_entry and update_ha_state, emit a signal (e.g. async_dispatcher_send(self.hass, SIGNAL_WINTER_MODE_UPDATED)). In custom_components/robonect/binary_sensor.py’s async_added_to_hass, subscribe to that signal and call self.async_write_ha_state() in the callback. This ensures the winter_mode binary_sensor (which reads entry.data) refreshes immediately when the switch toggles.
Summary by CodeRabbit
New Features
Documentation