Add Switchbot hygrometers (#75325)

* Switchbot add support for hygrometers

* Update CODEOWNERS

* Improve debug

* Remove redundant mention to temp unit

* Adopt FlowResultType

* Modify SwitchBot data within coordinator

* Increase logging for switchbot sensor

* Revert "Increase logging for switchbot sensor"

This reverts commit d8b377429c.

Co-authored-by: J. Nick Koston <nick@koston.org>
pull/75630/head
Diogo F. Andrade Murteira 2022-07-22 17:03:02 +01:00 committed by GitHub
parent e9697872c8
commit 148f963510
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 91 additions and 6 deletions

View File

@ -1038,8 +1038,8 @@ build.json @home-assistant/supervisor
/tests/components/switch/ @home-assistant/core /tests/components/switch/ @home-assistant/core
/homeassistant/components/switch_as_x/ @home-assistant/core /homeassistant/components/switch_as_x/ @home-assistant/core
/tests/components/switch_as_x/ @home-assistant/core /tests/components/switch_as_x/ @home-assistant/core
/homeassistant/components/switchbot/ @danielhiversen @RenierM26 /homeassistant/components/switchbot/ @danielhiversen @RenierM26 @murtas
/tests/components/switchbot/ @danielhiversen @RenierM26 /tests/components/switchbot/ @danielhiversen @RenierM26 @murtas
/homeassistant/components/switcher_kis/ @tomerfi @thecode /homeassistant/components/switcher_kis/ @tomerfi @thecode
/tests/components/switcher_kis/ @tomerfi @thecode /tests/components/switcher_kis/ @tomerfi @thecode
/homeassistant/components/switchmate/ @danielhiversen /homeassistant/components/switchmate/ @danielhiversen

View File

@ -9,6 +9,7 @@ from homeassistant.core import HomeAssistant
from .const import ( from .const import (
ATTR_BOT, ATTR_BOT,
ATTR_CURTAIN, ATTR_CURTAIN,
ATTR_HYGROMETER,
COMMON_OPTIONS, COMMON_OPTIONS,
CONF_RETRY_COUNT, CONF_RETRY_COUNT,
CONF_RETRY_TIMEOUT, CONF_RETRY_TIMEOUT,
@ -26,6 +27,7 @@ from .coordinator import SwitchbotDataUpdateCoordinator
PLATFORMS_BY_TYPE = { PLATFORMS_BY_TYPE = {
ATTR_BOT: [Platform.SWITCH, Platform.SENSOR], ATTR_BOT: [Platform.SWITCH, Platform.SENSOR],
ATTR_CURTAIN: [Platform.COVER, Platform.BINARY_SENSOR, Platform.SENSOR], ATTR_CURTAIN: [Platform.COVER, Platform.BINARY_SENSOR, Platform.SENSOR],
ATTR_HYGROMETER: [Platform.SENSOR],
} }

View File

@ -87,6 +87,8 @@ class SwitchbotConfigFlow(ConfigFlow, domain=DOMAIN):
try: try:
self._discovered_devices = await self._get_switchbots() self._discovered_devices = await self._get_switchbots()
for device in self._discovered_devices.values():
_LOGGER.debug("Found %s", device)
except NotConnectedError: except NotConnectedError:
return self.async_abort(reason="cannot_connect") return self.async_abort(reason="cannot_connect")

View File

@ -5,8 +5,13 @@ MANUFACTURER = "switchbot"
# Config Attributes # Config Attributes
ATTR_BOT = "bot" ATTR_BOT = "bot"
ATTR_CURTAIN = "curtain" ATTR_CURTAIN = "curtain"
ATTR_HYGROMETER = "hygrometer"
DEFAULT_NAME = "Switchbot" 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 # Config Defaults
DEFAULT_RETRY_COUNT = 3 DEFAULT_RETRY_COUNT = 3

View File

@ -14,6 +14,14 @@ from .const import DOMAIN
_LOGGER = logging.getLogger(__name__) _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 SwitchbotDataUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching switchbot data.""" """Class to manage fetching switchbot data."""
@ -47,4 +55,7 @@ class SwitchbotDataUpdateCoordinator(DataUpdateCoordinator):
if not switchbot_data: if not switchbot_data:
raise UpdateFailed("Unable to fetch switchbot services 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()
}

View File

@ -4,7 +4,7 @@
"documentation": "https://www.home-assistant.io/integrations/switchbot", "documentation": "https://www.home-assistant.io/integrations/switchbot",
"requirements": ["PySwitchbot==0.14.1"], "requirements": ["PySwitchbot==0.14.1"],
"config_flow": true, "config_flow": true,
"codeowners": ["@danielhiversen", "@RenierM26"], "codeowners": ["@danielhiversen", "@RenierM26", "@murtas"],
"bluetooth": [{ "service_uuid": "cba20d00-224d-11e6-9fb8-0002a5d5c51b" }], "bluetooth": [{ "service_uuid": "cba20d00-224d-11e6-9fb8-0002a5d5c51b" }],
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["switchbot"] "loggers": ["switchbot"]

View File

@ -12,6 +12,7 @@ from homeassistant.const import (
CONF_NAME, CONF_NAME,
PERCENTAGE, PERCENTAGE,
SIGNAL_STRENGTH_DECIBELS_MILLIWATT, SIGNAL_STRENGTH_DECIBELS_MILLIWATT,
TEMP_CELSIUS,
) )
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady from homeassistant.exceptions import PlatformNotReady
@ -43,6 +44,16 @@ SENSOR_TYPES: dict[str, SensorEntityDescription] = {
native_unit_of_measurement="Level", native_unit_of_measurement="Level",
device_class=SensorDeviceClass.ILLUMINANCE, 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,
),
} }

View File

@ -26,6 +26,12 @@ USER_INPUT_CURTAIN = {
CONF_MAC: "e7:89:43:90:90:90", 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 = { USER_INPUT_UNSUPPORTED_DEVICE = {
CONF_NAME: "test-name", CONF_NAME: "test-name",
CONF_PASSWORD: "test-password", CONF_PASSWORD: "test-password",

View File

@ -72,6 +72,19 @@ class MocGetSwitchbotDevices:
}, },
"modelName": "WoCurtain", "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 = { self._unsupported_device = {
"mac_address": "test", "mac_address": "test",
"isEncrypted": False, "isEncrypted": False,
@ -97,6 +110,8 @@ class MocGetSwitchbotDevices:
return self._unsupported_device return self._unsupported_device
if mac == "e7:89:43:90:90:90": if mac == "e7:89:43:90:90:90":
return self._curtain_all_services_data return self._curtain_all_services_data
if mac == "c0:ce:b0:d4:26:be":
return self._sensor_data
return None return None

View File

@ -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.const import CONF_MAC, CONF_NAME, CONF_PASSWORD, CONF_SENSOR_TYPE
from homeassistant.data_entry_flow import FlowResultType 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" DOMAIN = "switchbot"
@ -71,6 +77,33 @@ async def test_user_form_valid_mac(hass):
assert len(mock_setup_entry.mock_calls) == 1 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. # tests abort if no unconfigured devices are found.
result = await hass.config_entries.flow.async_init( result = await hass.config_entries.flow.async_init(