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:
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
75
homeassistant/components/switchbot/select.py
Normal file
75
homeassistant/components/switchbot/select.py
Normal 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
|
||||
)
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
125
tests/components/switchbot/test_select.py
Normal file
125
tests/components/switchbot/test_select.py
Normal 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
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user