Implement support for SwitchBot Meter, MeterPlus, and Outdoor Meter (#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 e256c35bb71e598cf879e05e1df21dff8456b09d.

* 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>
This commit is contained in:
Laurence Presland 2024-05-13 23:18:28 +10:00 committed by GitHub
parent 548eb35b79
commit c3f95a4f7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 106 additions and 9 deletions

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
"""The SwitchBot via API integration."""
"""SwitchBot via API integration."""
from asyncio import gather
from dataclasses import dataclass, field
@ -15,7 +15,7 @@ from .const import DOMAIN
from .coordinator import SwitchBotCoordinator
_LOGGER = getLogger(__name__)
PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.SWITCH]
PLATFORMS: list[Platform] = [Platform.CLIMATE, Platform.SENSOR, Platform.SWITCH]
@dataclass
@ -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
@ -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

View File

@ -5,4 +5,8 @@ from typing import Final
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"

View File

@ -9,7 +9,7 @@ from switchbot_api import CannotConnect, Device, Remote, SwitchBotAPI
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__)
@ -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
@ -31,7 +30,7 @@ class SwitchBotCoordinator(DataUpdateCoordinator[Status]):
hass,
_LOGGER,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
update_interval=DEFAULT_SCAN_INTERVAL,
)
self._api = api
self._device_id = device.device_id

View File

@ -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"]

View File

@ -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()