Skip to content

Commit

Permalink
Implement support for SwitchBot Meter, MeterPlus, and Outdoor Meter (#…
Browse files Browse the repository at this point in the history
…115522)

* Implement support for SwitchBot MeterPlus

Add temperature, humidity, and battery sensor entities for the MeterPlus device

* Rename GH username

* Update homeassistant/components/switchbot_cloud/coordinator.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* Refactor to use EntityDescriptions

Concat entity ID in SwitchBotCloudSensor init

* Remove __future__ import

* Make scan interval user configurable

* Add support for Meter and Outdoor Meter

* Revert "Make scan interval user configurable"

This reverts commit e256c35.

* Remove device-specific default scan intervals

* Update homeassistant/components/switchbot_cloud/sensor.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* Update homeassistant/components/switchbot_cloud/sensor.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* Update homeassistant/components/switchbot_cloud/sensor.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* Fix ruff errors

* Reorder manifest keys

* Update CODEOWNERS

* Add sensor.py to coveragerc

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
  • Loading branch information
laurence-presland and joostlek committed May 13, 2024
1 parent 548eb35 commit c3f95a4
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 9 deletions.
1 change: 1 addition & 0 deletions .coveragerc
Expand Up @@ -1377,6 +1377,7 @@ omit =
homeassistant/components/switchbot_cloud/climate.py
homeassistant/components/switchbot_cloud/coordinator.py
homeassistant/components/switchbot_cloud/entity.py
homeassistant/components/switchbot_cloud/sensor.py
homeassistant/components/switchbot_cloud/switch.py
homeassistant/components/switchmate/switch.py
homeassistant/components/syncthing/__init__.py
Expand Down
4 changes: 2 additions & 2 deletions CODEOWNERS
Validating CODEOWNERS rules …
Expand Up @@ -1365,8 +1365,8 @@ build.json @home-assistant/supervisor
/tests/components/switchbee/ @jafar-atili
/homeassistant/components/switchbot/ @danielhiversen @RenierM26 @murtas @Eloston @dsypniewski
/tests/components/switchbot/ @danielhiversen @RenierM26 @murtas @Eloston @dsypniewski
/homeassistant/components/switchbot_cloud/ @SeraphicRav
/tests/components/switchbot_cloud/ @SeraphicRav
/homeassistant/components/switchbot_cloud/ @SeraphicRav @laurence-presland
/tests/components/switchbot_cloud/ @SeraphicRav @laurence-presland
/homeassistant/components/switcher_kis/ @thecode
/tests/components/switcher_kis/ @thecode
/homeassistant/components/switchmate/ @danielhiversen @qiz-li
Expand Down
13 changes: 11 additions & 2 deletions homeassistant/components/switchbot_cloud/__init__.py
@@ -1,4 +1,4 @@
"""The SwitchBot via API integration."""
"""SwitchBot via API integration."""

from asyncio import gather
from dataclasses import dataclass, field
Expand All @@ -15,7 +15,7 @@
from .coordinator import SwitchBotCoordinator

_LOGGER = getLogger(__name__)
PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.SWITCH]
PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.SENSOR, Platform.SWITCH]


@dataclass
Expand All @@ -24,6 +24,7 @@ class SwitchbotDevices:

climates: list[Remote] = field(default_factory=list)
switches: list[Device | Remote] = field(default_factory=list)
sensors: list[Device] = field(default_factory=list)


@dataclass
Expand Down Expand Up @@ -72,6 +73,14 @@ def make_device_data(
devices_data.switches.append(
prepare_device(hass, api, device, coordinators_by_id)
)
if isinstance(device, Device) and device.device_type in [
"Meter",
"MeterPlus",
"WoIOSensor",
]:
devices_data.sensors.append(
prepare_device(hass, api, device, coordinators_by_id)
)
return devices_data


Expand Down
6 changes: 5 additions & 1 deletion homeassistant/components/switchbot_cloud/const.py
Expand Up @@ -5,4 +5,8 @@

DOMAIN: Final = "switchbot_cloud"
ENTRY_TITLE = "SwitchBot Cloud"
SCAN_INTERVAL = timedelta(seconds=600)
DEFAULT_SCAN_INTERVAL = timedelta(seconds=600)

SENSOR_KIND_TEMPERATURE = "temperature"
SENSOR_KIND_HUMIDITY = "humidity"
SENSOR_KIND_BATTERY = "battery"
5 changes: 2 additions & 3 deletions homeassistant/components/switchbot_cloud/coordinator.py
Expand Up @@ -9,7 +9,7 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed

from .const import DOMAIN, SCAN_INTERVAL
from .const import DEFAULT_SCAN_INTERVAL, DOMAIN

_LOGGER = getLogger(__name__)

Expand All @@ -21,7 +21,6 @@ class SwitchBotCoordinator(DataUpdateCoordinator[Status]):

_api: SwitchBotAPI
_device_id: str
_should_poll = False

def __init__(
self, hass: HomeAssistant, api: SwitchBotAPI, device: Device | Remote
Expand All @@ -31,7 +30,7 @@ def __init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
update_interval=DEFAULT_SCAN_INTERVAL,
)
self._api = api
self._device_id = device.device_id
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/switchbot_cloud/manifest.json
@@ -1,9 +1,10 @@
{
"domain": "switchbot_cloud",
"name": "SwitchBot Cloud",
"codeowners": ["@SeraphicRav"],
"codeowners": ["@SeraphicRav", "@laurence-presland"],
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/switchbot_cloud",
"integration_type": "hub",
"iot_class": "cloud_polling",
"loggers": ["switchbot-api"],
"requirements": ["switchbot-api==2.1.0"]
Expand Down
83 changes: 83 additions & 0 deletions homeassistant/components/switchbot_cloud/sensor.py
@@ -0,0 +1,83 @@
"""Platform for sensor integration."""

from switchbot_api import Device, SwitchBotAPI

from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import PERCENTAGE, UnitOfTemperature
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import SwitchbotCloudData
from .const import DOMAIN
from .coordinator import SwitchBotCoordinator
from .entity import SwitchBotCloudEntity

SENSOR_TYPE_TEMPERATURE = "temperature"
SENSOR_TYPE_HUMIDITY = "humidity"
SENSOR_TYPE_BATTERY = "battery"

METER_PLUS_SENSOR_DESCRIPTIONS = (
SensorEntityDescription(
key=SENSOR_TYPE_TEMPERATURE,
device_class=SensorDeviceClass.TEMPERATURE,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
),
SensorEntityDescription(
key=SENSOR_TYPE_HUMIDITY,
device_class=SensorDeviceClass.HUMIDITY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
),
SensorEntityDescription(
key=SENSOR_TYPE_BATTERY,
device_class=SensorDeviceClass.BATTERY,
state_class=SensorStateClass.MEASUREMENT,
native_unit_of_measurement=PERCENTAGE,
),
)


async def async_setup_entry(
hass: HomeAssistant,
config: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up SwitchBot Cloud entry."""
data: SwitchbotCloudData = hass.data[DOMAIN][config.entry_id]

async_add_entities(
SwitchBotCloudSensor(data.api, device, coordinator, description)
for device, coordinator in data.devices.sensors
for description in METER_PLUS_SENSOR_DESCRIPTIONS
)


class SwitchBotCloudSensor(SwitchBotCloudEntity, SensorEntity):
"""Representation of a SwitchBot Cloud sensor entity."""

def __init__(
self,
api: SwitchBotAPI,
device: Device,
coordinator: SwitchBotCoordinator,
description: SensorEntityDescription,
) -> None:
"""Initialize SwitchBot Cloud sensor entity."""
super().__init__(api, device, coordinator)
self.entity_description = description
self._attr_unique_id = f"{device.device_id}_{description.key}"

@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""
if not self.coordinator.data:
return
self._attr_native_value = self.coordinator.data.get(self.entity_description.key)
self.async_write_ha_state()

0 comments on commit c3f95a4

Please sign in to comment.