1
0
mirror of https://github.com/home-assistant/core.git synced 2026-03-02 15:52:29 +00:00

Control time display format on SwitchBot Meter Pro CO2 (#163008)

Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
elgris
2026-02-16 22:58:09 +01:00
committed by GitHub
parent 73fa9925c4
commit ba62d95715
7 changed files with 215 additions and 5 deletions

View File

@@ -53,7 +53,7 @@ PLATFORMS_BY_TYPE = {
Platform.SENSOR,
],
SupportedModels.HYGROMETER.value: [Platform.SENSOR],
SupportedModels.HYGROMETER_CO2.value: [Platform.SENSOR],
SupportedModels.HYGROMETER_CO2.value: [Platform.SENSOR, Platform.SELECT],
SupportedModels.CONTACT.value: [Platform.BINARY_SENSOR, Platform.SENSOR],
SupportedModels.MOTION.value: [Platform.BINARY_SENSOR, Platform.SENSOR],
SupportedModels.PRESENCE_SENSOR.value: [Platform.BINARY_SENSOR, Platform.SENSOR],
@@ -164,6 +164,7 @@ CLASS_BY_DEVICE = {
SupportedModels.ART_FRAME.value: switchbot.SwitchbotArtFrame,
SupportedModels.KEYPAD_VISION.value: switchbot.SwitchbotKeypadVision,
SupportedModels.KEYPAD_VISION_PRO.value: switchbot.SwitchbotKeypadVision,
SupportedModels.HYGROMETER_CO2.value: switchbot.SwitchbotMeterProCO2,
}

View File

@@ -106,13 +106,13 @@ CONNECTABLE_SUPPORTED_MODEL_TYPES = {
SwitchbotModel.ART_FRAME: SupportedModels.ART_FRAME,
SwitchbotModel.KEYPAD_VISION: SupportedModels.KEYPAD_VISION,
SwitchbotModel.KEYPAD_VISION_PRO: SupportedModels.KEYPAD_VISION_PRO,
SwitchbotModel.METER_PRO_C: SupportedModels.HYGROMETER_CO2,
}
NON_CONNECTABLE_SUPPORTED_MODEL_TYPES = {
SwitchbotModel.METER: SupportedModels.HYGROMETER,
SwitchbotModel.IO_METER: SupportedModels.HYGROMETER,
SwitchbotModel.METER_PRO: SupportedModels.HYGROMETER,
SwitchbotModel.METER_PRO_C: SupportedModels.HYGROMETER_CO2,
SwitchbotModel.CONTACT_SENSOR: SupportedModels.CONTACT,
SwitchbotModel.MOTION_SENSOR: SupportedModels.MOTION,
SwitchbotModel.PRESENCE_SENSOR: SupportedModels.PRESENCE_SENSOR,

View File

@@ -0,0 +1,75 @@
"""Select platform for SwitchBot."""
from __future__ import annotations
from datetime import timedelta
import logging
import switchbot
from switchbot.devices.device import SwitchbotOperationError
from homeassistant.components.select import SelectEntity
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from .coordinator import SwitchbotConfigEntry, SwitchbotDataUpdateCoordinator
from .entity import SwitchbotEntity, exception_handler
_LOGGER = logging.getLogger(__name__)
PARALLEL_UPDATES = 0
SCAN_INTERVAL = timedelta(days=7)
TIME_FORMAT_12H = "12h"
TIME_FORMAT_24H = "24h"
TIME_FORMAT_OPTIONS = [TIME_FORMAT_12H, TIME_FORMAT_24H]
async def async_setup_entry(
hass: HomeAssistant,
entry: SwitchbotConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up SwitchBot select platform."""
coordinator = entry.runtime_data
if isinstance(coordinator.device, switchbot.SwitchbotMeterProCO2):
async_add_entities([SwitchBotMeterProCO2TimeFormatSelect(coordinator)], True)
class SwitchBotMeterProCO2TimeFormatSelect(SwitchbotEntity, SelectEntity):
"""Select entity to set time display format on Meter Pro CO2."""
_attr_should_poll = True
_attr_entity_registry_enabled_default = False
_device: switchbot.SwitchbotMeterProCO2
_attr_entity_category = EntityCategory.CONFIG
_attr_translation_key = "time_format"
_attr_options = TIME_FORMAT_OPTIONS
def __init__(self, coordinator: SwitchbotDataUpdateCoordinator) -> None:
"""Initialize the select entity."""
super().__init__(coordinator)
self._attr_unique_id = f"{coordinator.base_unique_id}_time_format"
@exception_handler
async def async_select_option(self, option: str) -> None:
"""Change the time display format."""
_LOGGER.debug("Setting time format to %s for %s", option, self._address)
is_12h_mode = option == TIME_FORMAT_12H
await self._device.set_time_display_format(is_12h_mode)
self._attr_current_option = option
self.async_write_ha_state()
async def async_update(self) -> None:
"""Fetch the latest time format from the device."""
try:
device_time = await self._device.get_datetime()
except SwitchbotOperationError:
_LOGGER.debug(
"Failed to update time format for %s", self._address, exc_info=True
)
return
self._attr_current_option = (
TIME_FORMAT_12H if device_time["12h_mode"] else TIME_FORMAT_24H
)

View File

@@ -265,6 +265,15 @@
}
}
},
"select": {
"time_format": {
"name": "Time format",
"state": {
"12h": "12-hour (AM/PM)",
"24h": "24-hour"
}
}
},
"sensor": {
"aqi_quality_level": {
"name": "Air quality level",

View File

@@ -222,7 +222,7 @@ WOMETERTHPC_SERVICE_INFO = BluetoothServiceInfoBleak(
},
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"5\x00d"},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
address="AA:BB:CC:DD:EE:AA",
address="AA:BB:CC:DD:EE:FF",
rssi=-60,
source="local",
advertisement=generate_advertisement_data(
@@ -233,7 +233,7 @@ WOMETERTHPC_SERVICE_INFO = BluetoothServiceInfoBleak(
service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"5\x00d"},
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
),
device=generate_ble_device("AA:BB:CC:DD:EE:AA", "WoTHPc"),
device=generate_ble_device("AA:BB:CC:DD:EE:FF", "WoTHPc"),
time=0,
connectable=True,
tx_power=-127,

View File

@@ -0,0 +1,125 @@
"""Tests for the switchbot select platform."""
from collections.abc import Callable
from unittest.mock import AsyncMock, patch
import pytest
from homeassistant.components.select import (
ATTR_OPTION,
DOMAIN as SELECT_DOMAIN,
SERVICE_SELECT_OPTION,
)
from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from . import DOMAIN, WOMETERTHPC_SERVICE_INFO
from tests.common import MockConfigEntry
from tests.components.bluetooth import inject_bluetooth_service_info
@pytest.mark.parametrize(
("mode", "expected_state"),
[
(False, "24h"),
(True, "12h"),
],
)
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
async def test_time_format_select_initial_state(
hass: HomeAssistant,
mock_entry_factory: Callable[[str], MockConfigEntry],
mode: bool,
expected_state: str,
) -> None:
"""Test the time format select entity initial state."""
await async_setup_component(hass, DOMAIN, {})
inject_bluetooth_service_info(hass, WOMETERTHPC_SERVICE_INFO)
entry = mock_entry_factory("hygrometer_co2")
entry.add_to_hass(hass)
with patch(
"switchbot.SwitchbotMeterProCO2.get_datetime",
return_value={
"12h_mode": mode,
"year": 2025,
"month": 1,
"day": 9,
"hour": 12,
"minute": 0,
"second": 0,
},
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get("select.test_name_time_format")
assert state is not None
assert state.state == expected_state
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize(
("origin_mode", "expected_state"),
[
(False, "24h"),
(True, "12h"),
],
)
async def test_set_time_format(
hass: HomeAssistant,
mock_entry_factory: Callable[[str], MockConfigEntry],
origin_mode: bool,
expected_state: str,
) -> None:
"""Test changing time format to 12h."""
await async_setup_component(hass, DOMAIN, {})
inject_bluetooth_service_info(hass, WOMETERTHPC_SERVICE_INFO)
entry = mock_entry_factory("hygrometer_co2")
entry.add_to_hass(hass)
mock_get_datetime = AsyncMock(
return_value={
"12h_mode": origin_mode,
"year": 2025,
"month": 1,
"day": 9,
"hour": 12,
"minute": 0,
"second": 0,
}
)
mock_set_time_display_format = AsyncMock(return_value=True)
with (
patch(
"switchbot.SwitchbotMeterProCO2.get_datetime",
mock_get_datetime,
),
patch(
"switchbot.SwitchbotMeterProCO2.set_time_display_format",
mock_set_time_display_format,
),
):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
await hass.services.async_call(
SELECT_DOMAIN,
SERVICE_SELECT_OPTION,
{
ATTR_ENTITY_ID: "select.test_name_time_format",
ATTR_OPTION: expected_state,
},
blocking=True,
)
mock_set_time_display_format.assert_awaited_once_with(origin_mode)
state = hass.states.get("select.test_name_time_format")
assert state is not None
assert state.state == expected_state

View File

@@ -93,7 +93,7 @@ async def test_co2_sensor(hass: HomeAssistant) -> None:
entry = MockConfigEntry(
domain=DOMAIN,
data={
CONF_ADDRESS: "AA:BB:CC:DD:EE:AA",
CONF_ADDRESS: "AA:BB:CC:DD:EE:FF",
CONF_NAME: "test-name",
CONF_PASSWORD: "test-password",
CONF_SENSOR_TYPE: "hygrometer_co2",