From a3e38b0fb90178d3c47bd5c0456c28ea7b7911ad Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Wed, 25 Jan 2023 14:40:13 +1300 Subject: [PATCH 1/6] Implement Switchbot blinds --- .../components/switchbot/__init__.py | 6 ++ homeassistant/components/switchbot/const.py | 2 + homeassistant/components/switchbot/cover.py | 72 ++++++++++++++++++- 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/switchbot/__init__.py b/homeassistant/components/switchbot/__init__.py index 5d4f29b9dfe72d..445920ad276f43 100644 --- a/homeassistant/components/switchbot/__init__.py +++ b/homeassistant/components/switchbot/__init__.py @@ -50,6 +50,11 @@ Platform.LOCK, Platform.SENSOR, ], + SupportedModels.BLIND_TILT.value: [ + Platform.COVER, + Platform.BINARY_SENSOR, + Platform.SENSOR, + ], } CLASS_BY_DEVICE = { SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight, @@ -60,6 +65,7 @@ SupportedModels.LIGHT_STRIP.value: switchbot.SwitchbotLightStrip, SupportedModels.HUMIDIFIER.value: switchbot.SwitchbotHumidifier, SupportedModels.LOCK.value: switchbot.SwitchbotLock, + SupportedModels.BLIND_TILT.value: switchbot.SwitchbotBlindTilt, } diff --git a/homeassistant/components/switchbot/const.py b/homeassistant/components/switchbot/const.py index 3d606d93169d18..a21bd859efc9ad 100644 --- a/homeassistant/components/switchbot/const.py +++ b/homeassistant/components/switchbot/const.py @@ -25,6 +25,7 @@ class SupportedModels(StrEnum): MOTION = "motion" HUMIDIFIER = "humidifier" LOCK = "lock" + BLIND_TILT = "blind_tilt" CONNECTABLE_SUPPORTED_MODEL_TYPES = { @@ -36,6 +37,7 @@ class SupportedModels(StrEnum): SwitchbotModel.CEILING_LIGHT: SupportedModels.CEILING_LIGHT, SwitchbotModel.HUMIDIFIER: SupportedModels.HUMIDIFIER, SwitchbotModel.LOCK: SupportedModels.LOCK, + SwitchbotModel.BLIND_TILT: SupportedModels.BLIND_TILT, } NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = { diff --git a/homeassistant/components/switchbot/cover.py b/homeassistant/components/switchbot/cover.py index 7450653e9a9b98..61f2d2ec18c393 100644 --- a/homeassistant/components/switchbot/cover.py +++ b/homeassistant/components/switchbot/cover.py @@ -8,7 +8,9 @@ from homeassistant.components.cover import ( ATTR_CURRENT_POSITION, + ATTR_CURRENT_TILT_POSITION, ATTR_POSITION, + ATTR_TILT_POSITION, CoverDeviceClass, CoverEntity, CoverEntityFeature, @@ -32,7 +34,10 @@ async def async_setup_entry( ) -> None: """Set up Switchbot curtain based on a config entry.""" coordinator: SwitchbotDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] - async_add_entities([SwitchBotCurtainEntity(coordinator)]) + if isinstance(coordinator.device, switchbot.SwitchbotBlindTilt): + async_add_entities([SwitchBotBlindTiltEntity(coordinator)]) + else: + async_add_entities([SwitchBotCurtainEntity(coordinator)]) class SwitchBotCurtainEntity(SwitchbotEntity, CoverEntity, RestoreEntity): @@ -102,3 +107,68 @@ def _handle_coordinator_update(self) -> None: self._attr_is_closed = self.parsed_data["position"] <= 20 self._attr_is_opening = self.parsed_data["inMotion"] self.async_write_ha_state() + + +class SwitchBotBlindTiltEntity(SwitchbotEntity, CoverEntity, RestoreEntity): + """Representation of a Switchbot.""" + + _device: switchbot.SwitchbotCurtain + _attr_device_class = CoverDeviceClass.CURTAIN + _attr_supported_features = ( + CoverEntityFeature.OPEN_TILT + | CoverEntityFeature.CLOSE_TILT + | CoverEntityFeature.STOP_TILT + | CoverEntityFeature.SET_TILT_POSITION + ) + + def __init__(self, coordinator: SwitchbotDataUpdateCoordinator) -> None: + """Initialize the Switchbot.""" + super().__init__(coordinator) + self._attr_is_closed = None + + async def async_added_to_hass(self) -> None: + """Run when entity about to be added.""" + await super().async_added_to_hass() + last_state = await self.async_get_last_state() + if not last_state or ATTR_CURRENT_TILT_POSITION not in last_state.attributes: + return + + self._attr_current_cover_tilt_position = last_state.attributes.get( + ATTR_CURRENT_TILT_POSITION + ) + self._last_run_success = last_state.attributes.get("last_run_success") + + async def async_open_cover_tilt(self, **kwargs: Any) -> None: + """Open the tilt.""" + + _LOGGER.debug("Switchbot to open blind tilt %s", self._address) + self._last_run_success = bool(await self._device.open()) + self.async_write_ha_state() + + async def async_close_cover_tilt(self, **kwargs: Any) -> None: + """Close the tilt.""" + + _LOGGER.debug("Switchbot to close the blind tilt %s", self._address) + self._last_run_success = bool(await self._device.close()) + self.async_write_ha_state() + + async def async_stop_cover_tilt(self, **kwargs: Any) -> None: + """Stop the moving of this device.""" + + _LOGGER.debug("Switchbot to stop %s", self._address) + self._last_run_success = bool(await self._device.stop()) + self.async_write_ha_state() + + async def async_set_cover_tilt_position(self, **kwargs: Any) -> None: + """Move the cover tilt to a specific position.""" + position = kwargs.get(ATTR_TILT_POSITION) + + _LOGGER.debug("Switchbot to move at %d %s", position, self._address) + self._last_run_success = bool(await self._device.set_position(position)) + self.async_write_ha_state() + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + self._attr_current_cover_tilt_position = self.parsed_data["tilt"] + self.async_write_ha_state() From 3dec6dc73cb58a08aa1c54f6205208833f9793ee Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 25 Jan 2023 00:41:06 -1000 Subject: [PATCH 2/6] bump lib --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index a95a97d723eedb..5e0bb87fa8d207 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.36.4"], + "requirements": ["PySwitchbot==0.37.0"], "config_flow": true, "dependencies": ["bluetooth_adapters"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 5ebf0779d19267..4e0ab463267ae0 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -40,7 +40,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.36.4 +PySwitchbot==0.37.0 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 794a722521255a..fde4e2974ba092 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -36,7 +36,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.36.4 +PySwitchbot==0.37.0 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From 62871579ae39f772cd84dda218d173cf6be859e3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 25 Jan 2023 10:18:47 -1000 Subject: [PATCH 3/6] Update homeassistant/components/switchbot/cover.py --- homeassistant/components/switchbot/cover.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/homeassistant/components/switchbot/cover.py b/homeassistant/components/switchbot/cover.py index 61f2d2ec18c393..a4749fe1f112a1 100644 --- a/homeassistant/components/switchbot/cover.py +++ b/homeassistant/components/switchbot/cover.py @@ -112,7 +112,7 @@ def _handle_coordinator_update(self) -> None: class SwitchBotBlindTiltEntity(SwitchbotEntity, CoverEntity, RestoreEntity): """Representation of a Switchbot.""" - _device: switchbot.SwitchbotCurtain + _device: switchbot.SwitchbotBlindTilt _attr_device_class = CoverDeviceClass.CURTAIN _attr_supported_features = ( CoverEntityFeature.OPEN_TILT From 3fcaff2e7e2875e308406d7258a4b89053535ffa Mon Sep 17 00:00:00 2001 From: BelowAverageDeveloper <90269575+BelowAverageDev@users.noreply.github.com> Date: Sun, 29 Jan 2023 13:00:18 -0800 Subject: [PATCH 4/6] Add Switchbot cover is_closed, is_opening, is_closing logic (#86833) --- homeassistant/components/switchbot/cover.py | 31 +++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/switchbot/cover.py b/homeassistant/components/switchbot/cover.py index a4749fe1f112a1..70272c4bd8468b 100644 --- a/homeassistant/components/switchbot/cover.py +++ b/homeassistant/components/switchbot/cover.py @@ -113,13 +113,15 @@ class SwitchBotBlindTiltEntity(SwitchbotEntity, CoverEntity, RestoreEntity): """Representation of a Switchbot.""" _device: switchbot.SwitchbotBlindTilt - _attr_device_class = CoverDeviceClass.CURTAIN + _attr_device_class = CoverDeviceClass.BLIND _attr_supported_features = ( CoverEntityFeature.OPEN_TILT | CoverEntityFeature.CLOSE_TILT | CoverEntityFeature.STOP_TILT | CoverEntityFeature.SET_TILT_POSITION ) + CLOSED_UP_THRESHOLD = 80 + CLOSED_DOWN_THRESHOLD = 20 def __init__(self, coordinator: SwitchbotDataUpdateCoordinator) -> None: """Initialize the Switchbot.""" @@ -137,6 +139,10 @@ async def async_added_to_hass(self) -> None: ATTR_CURRENT_TILT_POSITION ) self._last_run_success = last_state.attributes.get("last_run_success") + if (_tilt := self._attr_current_cover_position) is not None: + self._attr_is_closed = (_tilt < self.CLOSED_DOWN_THRESHOLD) or ( + _tilt > self.CLOSED_UP_THRESHOLD + ) async def async_open_cover_tilt(self, **kwargs: Any) -> None: """Open the tilt.""" @@ -170,5 +176,26 @@ async def async_set_cover_tilt_position(self, **kwargs: Any) -> None: @callback def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" - self._attr_current_cover_tilt_position = self.parsed_data["tilt"] + _tilt = self.parsed_data["tilt"] + _moving_up = ( + self.parsed_data["motionDirection"]["up"] and self.parsed_data["inMotion"] + ) + _moving_down = ( + self.parsed_data["motionDirection"]["down"] and self.parsed_data["inMotion"] + ) + # NOTE: when motion is down, motion up is also set to true for some reason + if _moving_up: + _opening = bool(_tilt > 50) + _closing = not _opening + elif _moving_down: + _opening = bool(_tilt < 50) + _closing = not _opening + else: + _opening = _closing = False + self._attr_current_cover_tilt_position = _tilt + self._attr_is_closed = (_tilt < self.CLOSED_DOWN_THRESHOLD) or ( + _tilt > self.CLOSED_UP_THRESHOLD + ) + self._attr_is_opening = _opening + self._attr_is_closing = _closing self.async_write_ha_state() From bfa533311f8ef491075ab9155a1e098962ca65aa Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 30 Jan 2023 14:23:45 +1300 Subject: [PATCH 5/6] Bump library --- homeassistant/components/switchbot/manifest.json | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index 5e0bb87fa8d207..102248108aef45 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -2,7 +2,7 @@ "domain": "switchbot", "name": "SwitchBot", "documentation": "https://www.home-assistant.io/integrations/switchbot", - "requirements": ["PySwitchbot==0.37.0"], + "requirements": ["PySwitchbot==0.37.1"], "config_flow": true, "dependencies": ["bluetooth_adapters"], "codeowners": [ diff --git a/requirements_all.txt b/requirements_all.txt index 6434e7f5524257..b280918b5dbe06 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -40,7 +40,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.37.0 +PySwitchbot==0.37.1 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 097492a2697103..cf0f7bad8878f8 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -36,7 +36,7 @@ PyRMVtransport==0.3.3 PySocks==1.7.1 # homeassistant.components.switchbot -PySwitchbot==0.37.0 +PySwitchbot==0.37.1 # homeassistant.components.transport_nsw PyTransportNSW==0.1.1 From f3444820bce3dba79b177b41a1e1b345b1c2e77c Mon Sep 17 00:00:00 2001 From: BelowAverageDeveloper <90269575+BelowAverageDev@users.noreply.github.com> Date: Mon, 30 Jan 2023 17:17:34 -0800 Subject: [PATCH 6/6] Update _is_opening, _is_closing state attribute setting logic to use PySwitchbot==0.37.1 api (#86990) update _is_opening, _is_closing to use 0.37.1 api --- homeassistant/components/switchbot/cover.py | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/homeassistant/components/switchbot/cover.py b/homeassistant/components/switchbot/cover.py index 70272c4bd8468b..495ce0468a0557 100644 --- a/homeassistant/components/switchbot/cover.py +++ b/homeassistant/components/switchbot/cover.py @@ -177,25 +177,10 @@ async def async_set_cover_tilt_position(self, **kwargs: Any) -> None: def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" _tilt = self.parsed_data["tilt"] - _moving_up = ( - self.parsed_data["motionDirection"]["up"] and self.parsed_data["inMotion"] - ) - _moving_down = ( - self.parsed_data["motionDirection"]["down"] and self.parsed_data["inMotion"] - ) - # NOTE: when motion is down, motion up is also set to true for some reason - if _moving_up: - _opening = bool(_tilt > 50) - _closing = not _opening - elif _moving_down: - _opening = bool(_tilt < 50) - _closing = not _opening - else: - _opening = _closing = False self._attr_current_cover_tilt_position = _tilt self._attr_is_closed = (_tilt < self.CLOSED_DOWN_THRESHOLD) or ( _tilt > self.CLOSED_UP_THRESHOLD ) - self._attr_is_opening = _opening - self._attr_is_closing = _closing + self._attr_is_opening = self.parsed_data["motionDirection"]["opening"] + self._attr_is_closing = self.parsed_data["motionDirection"]["closing"] self.async_write_ha_state()