Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cover platform for switchbee integration #78383

Merged
merged 25 commits into from Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
36f5135
Added Platform cover for switchbee integration
jafar-atili Sep 13, 2022
8423c50
added cover to .coveragerc
jafar-atili Sep 13, 2022
1257c03
Applied code review feedback from other PR
jafar-atili Sep 13, 2022
be38b39
Addressed comments from other PRs
jafar-atili Sep 16, 2022
9507b25
rebased
jafar-atili Sep 24, 2022
c668cb0
Re-add carriage return
epenet Sep 26, 2022
f7c60c6
Update homeassistant/components/switchbee/cover.py
jafar-atili Sep 26, 2022
5ce22be
Update homeassistant/components/switchbee/cover.py
jafar-atili Sep 26, 2022
6889e28
Update homeassistant/components/switchbee/cover.py
jafar-atili Sep 26, 2022
bc843f1
Update homeassistant/components/switchbee/cover.py
jafar-atili Sep 26, 2022
df04bb7
addressed CR comments
jafar-atili Sep 26, 2022
f36d217
fixes
jafar-atili Sep 28, 2022
b60d0bf
fixes
jafar-atili Sep 28, 2022
6486131
more fixes
jafar-atili Sep 28, 2022
ad248aa
more fixes
jafar-atili Sep 28, 2022
b6e722d
separate entities for cover and somfy cover
jafar-atili Sep 28, 2022
0ee377b
fixed isort
jafar-atili Sep 28, 2022
907013a
more fixes
jafar-atili Sep 28, 2022
13f745e
Merge branch 'dev' into switchbee_cover
jafar-atili Sep 28, 2022
2978945
more fixes
jafar-atili Sep 29, 2022
fdffd29
Update homeassistant/components/switchbee/cover.py
jafar-atili Sep 29, 2022
57a356e
Update homeassistant/components/switchbee/cover.py
jafar-atili Sep 29, 2022
c7bba9b
more fixes
jafar-atili Sep 29, 2022
368c27f
more fixes
jafar-atili Sep 29, 2022
f918ec8
more
jafar-atili Sep 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .coveragerc
Expand Up @@ -1231,6 +1231,7 @@ omit =
homeassistant/components/switchbee/__init__.py
homeassistant/components/switchbee/button.py
homeassistant/components/switchbee/coordinator.py
homeassistant/components/switchbee/cover.py
homeassistant/components/switchbee/entity.py
homeassistant/components/switchbee/light.py
homeassistant/components/switchbee/switch.py
Expand Down
7 changes: 6 additions & 1 deletion homeassistant/components/switchbee/__init__.py
Expand Up @@ -13,7 +13,12 @@
from .const import DOMAIN
from .coordinator import SwitchBeeCoordinator

PLATFORMS: list[Platform] = [Platform.BUTTON, Platform.LIGHT, Platform.SWITCH]
PLATFORMS: list[Platform] = [
Platform.BUTTON,
Platform.COVER,
Platform.LIGHT,
Platform.SWITCH,
]


async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
Expand Down
2 changes: 2 additions & 0 deletions homeassistant/components/switchbee/coordinator.py
Expand Up @@ -62,6 +62,8 @@ async def _async_update_data(self) -> Mapping[int, SwitchBeeBaseDevice]:
DeviceType.TimedPowerSwitch,
DeviceType.Scenario,
DeviceType.Dimmer,
DeviceType.Shutter,
DeviceType.Somfy,
]
)
except SwitchBeeError as exp:
Expand Down
152 changes: 152 additions & 0 deletions homeassistant/components/switchbee/cover.py
@@ -0,0 +1,152 @@
"""Support for SwitchBee cover."""

from __future__ import annotations

from typing import Any

from switchbee.api import SwitchBeeError, SwitchBeeTokenError
from switchbee.const import SomfyCommand
from switchbee.device import SwitchBeeShutter, SwitchBeeSomfy

from homeassistant.components.cover import (
ATTR_POSITION,
CoverDeviceClass,
CoverEntity,
CoverEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DOMAIN
from .coordinator import SwitchBeeCoordinator
from .entity import SwitchBeeDeviceEntity


async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Set up SwitchBee switch."""
coordinator: SwitchBeeCoordinator = hass.data[DOMAIN][entry.entry_id]
entities: list[CoverEntity] = []

for device in coordinator.data.values():
if isinstance(device, SwitchBeeShutter):
entities.append(SwitchBeeCoverEntity(device, coordinator))
elif isinstance(device, SwitchBeeSomfy):
entities.append(SwitchBeeSomfyEntity(device, coordinator))

async_add_entities(entities)


class SwitchBeeSomfyEntity(SwitchBeeDeviceEntity[SwitchBeeSomfy], CoverEntity):
"""Representation of a SwitchBee Somfy cover."""

_attr_device_class = CoverDeviceClass.SHUTTER
_attr_supported_features = (
CoverEntityFeature.CLOSE | CoverEntityFeature.OPEN | CoverEntityFeature.STOP
)
_attr_is_closed = None

async def _fire_somfy_command(self, command: str) -> None:
"""Async function to fire Somfy device command."""
try:
await self.coordinator.api.set_state(self._device.id, command)
except (SwitchBeeError, SwitchBeeTokenError) as exp:
raise HomeAssistantError(
f"Failed to fire {command} for {self.name}, {str(exp)}"
) from exp

async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
return await self._fire_somfy_command(SomfyCommand.UP)

async def async_close_cover(self, **kwargs: Any) -> None:
"""Close the cover."""
return await self._fire_somfy_command(SomfyCommand.DOWN)

async def async_stop_cover(self, **kwargs: Any) -> None:
"""Stop a moving cover."""
return await self._fire_somfy_command(SomfyCommand.MY)


class SwitchBeeCoverEntity(SwitchBeeDeviceEntity[SwitchBeeShutter], CoverEntity):
"""Representation of a SwitchBee cover."""

_attr_device_class = CoverDeviceClass.SHUTTER
_attr_supported_features = (
CoverEntityFeature.CLOSE
| CoverEntityFeature.OPEN
| CoverEntityFeature.SET_POSITION
| CoverEntityFeature.STOP
)
_attr_is_closed = None

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
self._update_from_coordinator()
super()._handle_coordinator_update()

def _update_from_coordinator(self) -> None:
"""Update the entity attributes from the coordinator data."""

coordinator_device = self._get_coordinator_device()

if coordinator_device.position == -1:
self._check_if_became_offline()
return

# check if the device was offline (now online) and bring it back
self._check_if_became_online()

self._attr_current_cover_position = coordinator_device.position

if self.current_cover_position == 0:
self._attr_is_closed = True
else:
self._attr_is_closed = False
super()._handle_coordinator_update()

async def async_open_cover(self, **kwargs: Any) -> None:
"""Open the cover."""
if self.current_cover_position == 100:
return

await self.async_set_cover_position(position=100)

async def async_close_cover(self, **kwargs: Any) -> None:
"""Close the cover."""
if self.current_cover_position == 0:
return

await self.async_set_cover_position(position=0)

async def async_stop_cover(self, **kwargs: Any) -> None:
"""Stop a moving cover."""
# to stop the shutter, we just interrupt it with any state during operation
await self.async_set_cover_position(
position=self.current_cover_position, force=True
)

# fetch data from the Central Unit to get the new position
await self.coordinator.async_request_refresh()

async def async_set_cover_position(self, **kwargs: Any) -> None:
"""Async function to set position to cover."""
if (
self.current_cover_position == kwargs[ATTR_POSITION]
and "force" not in kwargs
):
return
try:
await self.coordinator.api.set_state(self._device.id, kwargs[ATTR_POSITION])
except (SwitchBeeError, SwitchBeeTokenError) as exp:
raise HomeAssistantError(
f"Failed to set {self.name} position to {kwargs[ATTR_POSITION]}, error: {str(exp)}"
) from exp

self._get_coordinator_device().position = kwargs[ATTR_POSITION]
self.coordinator.async_set_updated_data(self.coordinator.data)
self.async_write_ha_state()
5 changes: 4 additions & 1 deletion homeassistant/components/switchbee/entity.py
@@ -1,6 +1,6 @@
"""Support for SwitchBee entity."""
import logging
from typing import Generic, TypeVar
from typing import Generic, TypeVar, cast

from switchbee import SWITCHBEE_BRAND
from switchbee.api import SwitchBeeDeviceOfflineError, SwitchBeeError
Expand Down Expand Up @@ -108,3 +108,6 @@ def _check_if_became_online(self) -> None:
self.name,
)
self._is_online = True

def _get_coordinator_device(self) -> _DeviceTypeT:
return cast(_DeviceTypeT, self.coordinator.data[self._device.id])
10 changes: 4 additions & 6 deletions homeassistant/components/switchbee/light.py
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from typing import Any, cast
from typing import Any

from switchbee.api import SwitchBeeDeviceOfflineError, SwitchBeeError
from switchbee.device import ApiStateCommand, DeviceType, SwitchBeeDimmer
Expand Down Expand Up @@ -72,9 +72,7 @@ def _handle_coordinator_update(self) -> None:

def _update_attrs_from_coordinator(self) -> None:

coordinator_device = cast(
SwitchBeeDimmer, self.coordinator.data[self._device.id]
)
coordinator_device = self._get_coordinator_device()
brightness = coordinator_device.brightness

# module is offline
Expand Down Expand Up @@ -112,7 +110,7 @@ async def async_turn_on(self, **kwargs: Any) -> None:
return

# update the coordinator data manually we already know the Central Unit brightness data for this light
cast(SwitchBeeDimmer, self.coordinator.data[self._device.id]).brightness = state
self._get_coordinator_device().brightness = state
self.coordinator.async_set_updated_data(self.coordinator.data)

async def async_turn_off(self, **kwargs: Any) -> None:
Expand All @@ -125,5 +123,5 @@ async def async_turn_off(self, **kwargs: Any) -> None:
) from exp

# update the coordinator manually
cast(SwitchBeeDimmer, self.coordinator.data[self._device.id]).brightness = 0
self._get_coordinator_device().brightness = 0
self.coordinator.async_set_updated_data(self.coordinator.data)
4 changes: 2 additions & 2 deletions homeassistant/components/switchbee/switch.py
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from typing import Any, TypeVar, Union, cast
from typing import Any, TypeVar, Union

from switchbee.api import SwitchBeeDeviceOfflineError, SwitchBeeError
from switchbee.device import (
Expand Down Expand Up @@ -76,7 +76,7 @@ def _handle_coordinator_update(self) -> None:
def _update_from_coordinator(self) -> None:
"""Update the entity attributes from the coordinator data."""

coordinator_device = cast(_DeviceTypeT, self.coordinator.data[self._device.id])
coordinator_device = self._get_coordinator_device()

if coordinator_device.state == -1:

Expand Down