Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for Switchbot devices."""
2 
3 import logging
4 
5 import switchbot
6 
7 from homeassistant.components import bluetooth
8 from homeassistant.config_entries import ConfigEntry
9 from homeassistant.const import (
10  CONF_ADDRESS,
11  CONF_MAC,
12  CONF_NAME,
13  CONF_PASSWORD,
14  CONF_SENSOR_TYPE,
15  Platform,
16 )
17 from homeassistant.core import HomeAssistant
18 from homeassistant.exceptions import ConfigEntryNotReady
19 from homeassistant.helpers import device_registry as dr
20 
21 from .const import (
22  CONF_ENCRYPTION_KEY,
23  CONF_KEY_ID,
24  CONF_RETRY_COUNT,
25  CONNECTABLE_SUPPORTED_MODEL_TYPES,
26  DEFAULT_RETRY_COUNT,
27  HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL,
28  SupportedModels,
29 )
30 from .coordinator import SwitchbotConfigEntry, SwitchbotDataUpdateCoordinator
31 
32 PLATFORMS_BY_TYPE = {
33  SupportedModels.BULB.value: [Platform.SENSOR, Platform.LIGHT],
34  SupportedModels.LIGHT_STRIP.value: [Platform.SENSOR, Platform.LIGHT],
35  SupportedModels.CEILING_LIGHT.value: [Platform.SENSOR, Platform.LIGHT],
36  SupportedModels.BOT.value: [Platform.SWITCH, Platform.SENSOR],
37  SupportedModels.PLUG.value: [Platform.SWITCH, Platform.SENSOR],
38  SupportedModels.CURTAIN.value: [
39  Platform.COVER,
40  Platform.BINARY_SENSOR,
41  Platform.SENSOR,
42  ],
43  SupportedModels.HYGROMETER.value: [Platform.SENSOR],
44  SupportedModels.HYGROMETER_CO2.value: [Platform.SENSOR],
45  SupportedModels.CONTACT.value: [Platform.BINARY_SENSOR, Platform.SENSOR],
46  SupportedModels.MOTION.value: [Platform.BINARY_SENSOR, Platform.SENSOR],
47  SupportedModels.HUMIDIFIER.value: [Platform.HUMIDIFIER, Platform.SENSOR],
48  SupportedModels.LOCK.value: [
49  Platform.BINARY_SENSOR,
50  Platform.LOCK,
51  Platform.SENSOR,
52  ],
53  SupportedModels.LOCK_PRO.value: [
54  Platform.BINARY_SENSOR,
55  Platform.LOCK,
56  Platform.SENSOR,
57  ],
58  SupportedModels.BLIND_TILT.value: [
59  Platform.COVER,
60  Platform.BINARY_SENSOR,
61  Platform.SENSOR,
62  ],
63  SupportedModels.HUB2.value: [Platform.SENSOR],
64 }
65 CLASS_BY_DEVICE = {
66  SupportedModels.CEILING_LIGHT.value: switchbot.SwitchbotCeilingLight,
67  SupportedModels.CURTAIN.value: switchbot.SwitchbotCurtain,
68  SupportedModels.BOT.value: switchbot.Switchbot,
69  SupportedModels.PLUG.value: switchbot.SwitchbotPlugMini,
70  SupportedModels.BULB.value: switchbot.SwitchbotBulb,
71  SupportedModels.LIGHT_STRIP.value: switchbot.SwitchbotLightStrip,
72  SupportedModels.HUMIDIFIER.value: switchbot.SwitchbotHumidifier,
73  SupportedModels.LOCK.value: switchbot.SwitchbotLock,
74  SupportedModels.LOCK_PRO.value: switchbot.SwitchbotLock,
75  SupportedModels.BLIND_TILT.value: switchbot.SwitchbotBlindTilt,
76 }
77 
78 
79 _LOGGER = logging.getLogger(__name__)
80 
81 
82 async def async_setup_entry(hass: HomeAssistant, entry: SwitchbotConfigEntry) -> bool:
83  """Set up Switchbot from a config entry."""
84  assert entry.unique_id is not None
85  if CONF_ADDRESS not in entry.data and CONF_MAC in entry.data:
86  # Bleak uses addresses not mac addresses which are actually
87  # UUIDs on some platforms (MacOS).
88  mac = entry.data[CONF_MAC]
89  if "-" not in mac:
90  mac = dr.format_mac(mac)
91  hass.config_entries.async_update_entry(
92  entry,
93  data={**entry.data, CONF_ADDRESS: mac},
94  )
95 
96  if not entry.options:
97  hass.config_entries.async_update_entry(
98  entry,
99  options={CONF_RETRY_COUNT: DEFAULT_RETRY_COUNT},
100  )
101 
102  sensor_type: str = entry.data[CONF_SENSOR_TYPE]
103  switchbot_model = HASS_SENSOR_TYPE_TO_SWITCHBOT_MODEL[sensor_type]
104  # connectable means we can make connections to the device
105  connectable = switchbot_model in CONNECTABLE_SUPPORTED_MODEL_TYPES
106  address: str = entry.data[CONF_ADDRESS]
107 
108  await switchbot.close_stale_connections_by_address(address)
109 
110  ble_device = bluetooth.async_ble_device_from_address(
111  hass, address.upper(), connectable
112  )
113  if not ble_device:
114  raise ConfigEntryNotReady(
115  f"Could not find Switchbot {sensor_type} with address {address}"
116  )
117 
118  cls = CLASS_BY_DEVICE.get(sensor_type, switchbot.SwitchbotDevice)
119  if cls is switchbot.SwitchbotLock:
120  try:
121  device = switchbot.SwitchbotLock(
122  device=ble_device,
123  key_id=entry.data.get(CONF_KEY_ID),
124  encryption_key=entry.data.get(CONF_ENCRYPTION_KEY),
125  retry_count=entry.options[CONF_RETRY_COUNT],
126  model=switchbot_model,
127  )
128  except ValueError as error:
129  raise ConfigEntryNotReady(
130  "Invalid encryption configuration provided"
131  ) from error
132  else:
133  device = cls(
134  device=ble_device,
135  password=entry.data.get(CONF_PASSWORD),
136  retry_count=entry.options[CONF_RETRY_COUNT],
137  )
138 
139  coordinator = entry.runtime_data = SwitchbotDataUpdateCoordinator(
140  hass,
141  _LOGGER,
142  ble_device,
143  device,
144  entry.unique_id,
145  entry.data.get(CONF_NAME, entry.title),
146  connectable,
147  switchbot_model,
148  )
149  entry.async_on_unload(coordinator.async_start())
150  if not await coordinator.async_wait_ready():
151  raise ConfigEntryNotReady(f"{address} is not advertising state")
152 
153  entry.async_on_unload(entry.add_update_listener(_async_update_listener))
154  await hass.config_entries.async_forward_entry_setups(
155  entry, PLATFORMS_BY_TYPE[sensor_type]
156  )
157 
158  return True
159 
160 
161 async def _async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
162  """Handle options update."""
163  await hass.config_entries.async_reload(entry.entry_id)
164 
165 
166 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
167  """Unload a config entry."""
168  sensor_type = entry.data[CONF_SENSOR_TYPE]
169  return await hass.config_entries.async_unload_platforms(
170  entry, PLATFORMS_BY_TYPE[sensor_type]
171  )
bool async_setup_entry(HomeAssistant hass, SwitchbotConfigEntry entry)
Definition: __init__.py:82
None _async_update_listener(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:161
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:166