1 """The Shelly integration."""
3 from __future__
import annotations
5 from typing
import Final
7 from aioshelly.block_device
import BlockDevice
8 from aioshelly.common
import ConnectionOptions
9 from aioshelly.const
import DEFAULT_COAP_PORT, RPC_GENERATIONS
10 from aioshelly.exceptions
import (
11 DeviceConnectionError,
13 MacAddressMismatchError,
15 from aioshelly.rpc_device
import RpcDevice
16 import voluptuous
as vol
22 config_validation
as cv,
23 device_registry
as dr,
24 entity_registry
as er,
32 BLOCK_EXPECTED_SLEEP_PERIOD,
33 BLOCK_WRONG_SLEEP_PERIOD,
37 FIRMWARE_UNSUPPORTED_ISSUE_ID,
39 MODELS_WITH_WRONG_SLEEP_PERIOD,
42 from .coordinator
import (
43 ShellyBlockCoordinator,
46 ShellyRestCoordinator,
48 ShellyRpcPollingCoordinator,
51 async_create_issue_unsupported_firmware,
59 Platform.BINARY_SENSOR,
73 BLOCK_SLEEPING_PLATFORMS: Final = [
74 Platform.BINARY_SENSOR,
80 RPC_SLEEPING_PLATFORMS: Final = [
81 Platform.BINARY_SENSOR,
86 COAP_SCHEMA: Final = vol.Schema(
88 vol.Optional(CONF_COAP_PORT, default=DEFAULT_COAP_PORT): cv.port,
91 CONFIG_SCHEMA: Final = vol.Schema({DOMAIN: COAP_SCHEMA}, extra=vol.ALLOW_EXTRA)
94 async
def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
95 """Set up the Shelly component."""
96 if (conf := config.get(DOMAIN))
is not None:
97 hass.data[DOMAIN] = {CONF_COAP_PORT: conf[CONF_COAP_PORT]}
103 """Set up Shelly from a config entry."""
109 if not entry.data.get(CONF_HOST):
112 "The config entry %s probably comes from a custom integration, please"
113 " remove it if you want to use core Shelly integration"
126 hass: HomeAssistant, entry: ShellyConfigEntry
128 """Set up Shelly block based device from a config entry."""
129 options = ConnectionOptions(
130 entry.data[CONF_HOST],
131 entry.data.get(CONF_USERNAME),
132 entry.data.get(CONF_PASSWORD),
133 device_mac=entry.unique_id,
138 device = await BlockDevice.create(
144 dev_reg = dr.async_get(hass)
146 if entry.unique_id
is not None:
147 device_entry = dev_reg.async_get_device(
148 connections={(CONNECTION_NETWORK_MAC, dr.format_mac(entry.unique_id))},
151 if device_entry
and entry.entry_id
not in device_entry.config_entries:
154 sleep_period = entry.data.get(CONF_SLEEP_PERIOD)
155 runtime_data = entry.runtime_data =
ShellyEntryData(BLOCK_SLEEPING_PLATFORMS)
160 sleep_period == BLOCK_WRONG_SLEEP_PERIOD
161 and entry.data[
"model"]
in MODELS_WITH_WRONG_SLEEP_PERIOD
164 "Updating stored sleep period for %s: from %s to %s",
167 BLOCK_EXPECTED_SLEEP_PERIOD,
169 data = {**entry.data}
170 data[CONF_SLEEP_PERIOD] = sleep_period = BLOCK_EXPECTED_SLEEP_PERIOD
171 hass.config_entries.async_update_entry(entry, data=data)
173 if sleep_period == 0:
175 LOGGER.debug(
"Setting up online block device %s", entry.title)
176 runtime_data.platforms = PLATFORMS
178 await device.initialize()
179 if not device.firmware_supported:
181 await device.shutdown()
182 raise ConfigEntryNotReady
183 except (DeviceConnectionError, MacAddressMismatchError)
as err:
184 await device.shutdown()
186 except InvalidAuthError
as err:
187 await device.shutdown()
191 runtime_data.block.async_setup()
193 await hass.config_entries.async_forward_entry_setups(
194 entry, runtime_data.platforms
198 or device_entry
is None
199 or not er.async_entries_for_device(er.async_get(hass), device_entry.id)
206 "Setup for device %s will resume when device is online", entry.title
209 runtime_data.block.async_setup(runtime_data.platforms)
212 LOGGER.debug(
"Setting up offline block device %s", entry.title)
214 runtime_data.block.async_setup()
215 await hass.config_entries.async_forward_entry_setups(
216 entry, runtime_data.platforms
219 ir.async_delete_issue(
220 hass, DOMAIN, FIRMWARE_UNSUPPORTED_ISSUE_ID.format(unique=entry.unique_id)
226 """Set up Shelly RPC based device from a config entry."""
227 options = ConnectionOptions(
228 entry.data[CONF_HOST],
229 entry.data.get(CONF_USERNAME),
230 entry.data.get(CONF_PASSWORD),
231 device_mac=entry.unique_id,
237 device = await RpcDevice.create(
243 dev_reg = dr.async_get(hass)
245 if entry.unique_id
is not None:
246 device_entry = dev_reg.async_get_device(
247 connections={(CONNECTION_NETWORK_MAC, dr.format_mac(entry.unique_id))},
250 if device_entry
and entry.entry_id
not in device_entry.config_entries:
253 sleep_period = entry.data.get(CONF_SLEEP_PERIOD)
254 runtime_data = entry.runtime_data =
ShellyEntryData(RPC_SLEEPING_PLATFORMS)
256 if sleep_period == 0:
258 LOGGER.debug(
"Setting up online RPC device %s", entry.title)
259 runtime_data.platforms = PLATFORMS
261 await device.initialize()
262 if not device.firmware_supported:
264 await device.shutdown()
265 raise ConfigEntryNotReady
266 except (DeviceConnectionError, MacAddressMismatchError)
as err:
267 await device.shutdown()
269 except InvalidAuthError
as err:
270 await device.shutdown()
274 runtime_data.rpc.async_setup()
276 await hass.config_entries.async_forward_entry_setups(
277 entry, runtime_data.platforms
281 or device_entry
is None
282 or not er.async_entries_for_device(er.async_get(hass), device_entry.id)
289 "Setup for device %s will resume when device is online", entry.title
292 runtime_data.rpc.async_setup(runtime_data.platforms)
297 await runtime_data.rpc.async_device_online(
"setup")
300 LOGGER.debug(
"Setting up offline RPC device %s", entry.title)
302 runtime_data.rpc.async_setup()
303 await hass.config_entries.async_forward_entry_setups(
304 entry, runtime_data.platforms
307 ir.async_delete_issue(
308 hass, DOMAIN, FIRMWARE_UNSUPPORTED_ISSUE_ID.format(unique=entry.unique_id)
314 """Unload a config entry."""
317 "Deleting issue %s", PUSH_UPDATE_ISSUE_ID.format(unique=entry.unique_id)
319 ir.async_delete_issue(
320 hass, DOMAIN, PUSH_UPDATE_ISSUE_ID.format(unique=entry.unique_id)
323 runtime_data = entry.runtime_data
326 await runtime_data.rpc.shutdown()
328 if runtime_data.block:
329 await runtime_data.block.shutdown()
331 return await hass.config_entries.async_unload_platforms(
332 entry, runtime_data.platforms
WsServer get_ws_context(HomeAssistant hass)
COAP get_coap_context(HomeAssistant hass)
None async_create_issue_unsupported_firmware(HomeAssistant hass, ConfigEntry entry)
int get_device_entry_gen(ConfigEntry entry)
int get_http_port(MappingProxyType[str, Any] data)
bool async_setup_entry(HomeAssistant hass, ShellyConfigEntry entry)
bool _async_setup_block_entry(HomeAssistant hass, ShellyConfigEntry entry)
bool _async_setup_rpc_entry(HomeAssistant hass, ShellyConfigEntry entry)
bool async_unload_entry(HomeAssistant hass, ShellyConfigEntry entry)
bool async_setup(HomeAssistant hass, ConfigType config)
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)