diff --git a/CODEOWNERS b/CODEOWNERS index 792a6302b79..3b38b6e1a5a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1038,8 +1038,8 @@ build.json @home-assistant/supervisor /tests/components/switch/ @home-assistant/core /homeassistant/components/switch_as_x/ @home-assistant/core /tests/components/switch_as_x/ @home-assistant/core -/homeassistant/components/switchbot/ @danielhiversen @RenierM26 -/tests/components/switchbot/ @danielhiversen @RenierM26 +/homeassistant/components/switchbot/ @danielhiversen @RenierM26 @murtas +/tests/components/switchbot/ @danielhiversen @RenierM26 @murtas /homeassistant/components/switcher_kis/ @tomerfi @thecode /tests/components/switcher_kis/ @tomerfi @thecode /homeassistant/components/switchmate/ @danielhiversen diff --git a/homeassistant/components/switchbot/__init__.py b/homeassistant/components/switchbot/__init__.py index 0a3d01f3382..d4418685cff 100644 --- a/homeassistant/components/switchbot/__init__.py +++ b/homeassistant/components/switchbot/__init__.py @@ -9,6 +9,7 @@ from homeassistant.core import HomeAssistant from .const import ( ATTR_BOT, ATTR_CURTAIN, + ATTR_HYGROMETER, COMMON_OPTIONS, CONF_RETRY_COUNT, CONF_RETRY_TIMEOUT, @@ -26,6 +27,7 @@ from .coordinator import SwitchbotDataUpdateCoordinator PLATFORMS_BY_TYPE = { ATTR_BOT: [Platform.SWITCH, Platform.SENSOR], ATTR_CURTAIN: [Platform.COVER, Platform.BINARY_SENSOR, Platform.SENSOR], + ATTR_HYGROMETER: [Platform.SENSOR], } diff --git a/homeassistant/components/switchbot/config_flow.py b/homeassistant/components/switchbot/config_flow.py index 362f3b01ae7..b35ba052d0a 100644 --- a/homeassistant/components/switchbot/config_flow.py +++ b/homeassistant/components/switchbot/config_flow.py @@ -87,6 +87,8 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN): try: self._discovered_devices = await self._get_switchbots() + for device in self._discovered_devices.values(): + _LOGGER.debug("Found %s", device) except NotConnectedError: return self.async_abort(reason="cannot_connect") diff --git a/homeassistant/components/switchbot/const.py b/homeassistant/components/switchbot/const.py index b1587e97c10..a363c030eb1 100644 --- a/homeassistant/components/switchbot/const.py +++ b/homeassistant/components/switchbot/const.py @@ -5,8 +5,13 @@ MANUFACTURER = "switchbot" # Config Attributes ATTR_BOT = "bot" ATTR_CURTAIN = "curtain" +ATTR_HYGROMETER = "hygrometer" DEFAULT_NAME = "Switchbot" -SUPPORTED_MODEL_TYPES = {"WoHand": ATTR_BOT, "WoCurtain": ATTR_CURTAIN} +SUPPORTED_MODEL_TYPES = { + "WoHand": ATTR_BOT, + "WoCurtain": ATTR_CURTAIN, + "WoSensorTH": ATTR_HYGROMETER, +} # Config Defaults DEFAULT_RETRY_COUNT = 3 diff --git a/homeassistant/components/switchbot/coordinator.py b/homeassistant/components/switchbot/coordinator.py index e8e2e240dc6..4a4831f2cdb 100644 --- a/homeassistant/components/switchbot/coordinator.py +++ b/homeassistant/components/switchbot/coordinator.py @@ -14,6 +14,14 @@ from .const import DOMAIN _LOGGER = logging.getLogger(__name__) +def flatten_sensors_data(sensor): + """Deconstruct SwitchBot library temp object C/FÂș readings from dictionary.""" + if "temp" in sensor["data"]: + sensor["data"]["temperature"] = sensor["data"]["temp"]["c"] + + return sensor + + class SwitchbotDataUpdateCoordinator(DataUpdateCoordinator): """Class to manage fetching switchbot data.""" @@ -47,4 +55,7 @@ class SwitchbotDataUpdateCoordinator(DataUpdateCoordinator): if not switchbot_data: raise UpdateFailed("Unable to fetch switchbot services data") - return switchbot_data + return { + identifier: flatten_sensors_data(sensor) + for identifier, sensor in switchbot_data.items() + } diff --git a/homeassistant/components/switchbot/manifest.json b/homeassistant/components/switchbot/manifest.json index ccce534c6df..76b43628ab3 100644 --- a/homeassistant/components/switchbot/manifest.json +++ b/homeassistant/components/switchbot/manifest.json @@ -4,7 +4,7 @@ "documentation": "https://www.home-assistant.io/integrations/switchbot", "requirements": ["PySwitchbot==0.14.1"], "config_flow": true, - "codeowners": ["@danielhiversen", "@RenierM26"], + "codeowners": ["@danielhiversen", "@RenierM26", "@murtas"], "bluetooth": [{ "service_uuid": "cba20d00-224d-11e6-9fb8-0002a5d5c51b" }], "iot_class": "local_polling", "loggers": ["switchbot"] diff --git a/homeassistant/components/switchbot/sensor.py b/homeassistant/components/switchbot/sensor.py index 759a504d19a..0bc7fe8a3b2 100644 --- a/homeassistant/components/switchbot/sensor.py +++ b/homeassistant/components/switchbot/sensor.py @@ -12,6 +12,7 @@ from homeassistant.const import ( CONF_NAME, PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, + TEMP_CELSIUS, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import PlatformNotReady @@ -43,6 +44,16 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = { native_unit_of_measurement="Level", device_class=SensorDeviceClass.ILLUMINANCE, ), + "humidity": SensorEntityDescription( + key="humidity", + native_unit_of_measurement=PERCENTAGE, + device_class=SensorDeviceClass.HUMIDITY, + ), + "temperature": SensorEntityDescription( + key="temperature", + native_unit_of_measurement=TEMP_CELSIUS, + device_class=SensorDeviceClass.TEMPERATURE, + ), } diff --git a/tests/components/switchbot/__init__.py b/tests/components/switchbot/__init__.py index 376406ac50c..8f1501bfa9a 100644 --- a/tests/components/switchbot/__init__.py +++ b/tests/components/switchbot/__init__.py @@ -26,6 +26,12 @@ USER_INPUT_CURTAIN = { CONF_MAC: "e7:89:43:90:90:90", } +USER_INPUT_SENSOR = { + CONF_NAME: "test-name", + CONF_PASSWORD: "test-password", + CONF_MAC: "c0:ce:b0:d4:26:be", +} + USER_INPUT_UNSUPPORTED_DEVICE = { CONF_NAME: "test-name", CONF_PASSWORD: "test-password", diff --git a/tests/components/switchbot/conftest.py b/tests/components/switchbot/conftest.py index 550aeb08082..2e6421f22a4 100644 --- a/tests/components/switchbot/conftest.py +++ b/tests/components/switchbot/conftest.py @@ -72,6 +72,19 @@ class MocGetSwitchbotDevices: }, "modelName": "WoCurtain", } + self._sensor_data = { + "mac_address": "c0:ce:b0:d4:26:be", + "isEncrypted": False, + "data": { + "temp": {"c": 21.6, "f": 70.88}, + "fahrenheit": False, + "humidity": 73, + "battery": 100, + "rssi": -58, + }, + "model": "T", + "modelName": "WoSensorTH", + } self._unsupported_device = { "mac_address": "test", "isEncrypted": False, @@ -97,6 +110,8 @@ class MocGetSwitchbotDevices: return self._unsupported_device if mac == "e7:89:43:90:90:90": return self._curtain_all_services_data + if mac == "c0:ce:b0:d4:26:be": + return self._sensor_data return None diff --git a/tests/components/switchbot/test_config_flow.py b/tests/components/switchbot/test_config_flow.py index 814e79cf591..aa1adb3a16e 100644 --- a/tests/components/switchbot/test_config_flow.py +++ b/tests/components/switchbot/test_config_flow.py @@ -11,7 +11,13 @@ from homeassistant.config_entries import SOURCE_USER from homeassistant.const import CONF_MAC, CONF_NAME, CONF_PASSWORD, CONF_SENSOR_TYPE from homeassistant.data_entry_flow import FlowResultType -from . import USER_INPUT, USER_INPUT_CURTAIN, init_integration, patch_async_setup_entry +from . import ( + USER_INPUT, + USER_INPUT_CURTAIN, + USER_INPUT_SENSOR, + init_integration, + patch_async_setup_entry, +) DOMAIN = "switchbot" @@ -71,6 +77,33 @@ async def test_user_form_valid_mac(hass): assert len(mock_setup_entry.mock_calls) == 1 + # test sensor device creation. + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] == FlowResultType.FORM + assert result["step_id"] == "user" + assert result["errors"] == {} + + with patch_async_setup_entry() as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + USER_INPUT_SENSOR, + ) + await hass.async_block_till_done() + + assert result["type"] == FlowResultType.CREATE_ENTRY + assert result["title"] == "test-name" + assert result["data"] == { + CONF_MAC: "c0:ce:b0:d4:26:be", + CONF_NAME: "test-name", + CONF_PASSWORD: "test-password", + CONF_SENSOR_TYPE: "hygrometer", + } + + assert len(mock_setup_entry.mock_calls) == 1 + # tests abort if no unconfigured devices are found. result = await hass.config_entries.flow.async_init(