1 """Support for BSH Home Connect appliances."""
3 from __future__
import annotations
5 from datetime
import timedelta
8 from typing
import Any, cast
10 from requests
import HTTPError
11 import voluptuous
as vol
17 config_entry_oauth2_flow,
18 config_validation
as cv,
19 device_registry
as dr,
34 OLD_NEW_UNIQUE_ID_SUFFIX_MAP,
35 SERVICE_OPTION_ACTIVE,
36 SERVICE_OPTION_SELECTED,
37 SERVICE_PAUSE_PROGRAM,
38 SERVICE_RESUME_PROGRAM,
39 SERVICE_SELECT_PROGRAM,
41 SERVICE_START_PROGRAM,
46 _LOGGER = logging.getLogger(__name__)
48 RE_CAMEL_CASE = re.compile(
r"(?<!^)(?=[A-Z])|(?=\d)(?<=\D)")
52 CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
54 SERVICE_SETTING_SCHEMA = vol.Schema(
56 vol.Required(ATTR_DEVICE_ID): str,
57 vol.Required(ATTR_KEY): str,
58 vol.Required(ATTR_VALUE): vol.Any(str, int, bool),
62 SERVICE_OPTION_SCHEMA = vol.Schema(
64 vol.Required(ATTR_DEVICE_ID): str,
65 vol.Required(ATTR_KEY): str,
66 vol.Required(ATTR_VALUE): vol.Any(str, int, bool),
67 vol.Optional(ATTR_UNIT): str,
71 SERVICE_PROGRAM_SCHEMA = vol.Any(
73 vol.Required(ATTR_DEVICE_ID): str,
74 vol.Required(ATTR_PROGRAM): str,
75 vol.Required(ATTR_KEY): str,
76 vol.Required(ATTR_VALUE): vol.Any(int, str),
77 vol.Optional(ATTR_UNIT): str,
80 vol.Required(ATTR_DEVICE_ID): str,
81 vol.Required(ATTR_PROGRAM): str,
85 SERVICE_COMMAND_SCHEMA = vol.Schema({vol.Required(ATTR_DEVICE_ID): str})
88 Platform.BINARY_SENSOR,
100 device_id: str |
None =
None,
101 device_entry: dr.DeviceEntry |
None =
None,
102 entry: HomeConnectConfigEntry |
None =
None,
103 ) -> api.HomeConnectAppliance:
104 """Return a Home Connect appliance instance given a device id or a device entry."""
105 if device_id
is not None and device_entry
is None:
106 device_registry = dr.async_get(hass)
107 device_entry = device_registry.async_get(device_id)
108 assert device_entry,
"Either a device id or a device entry must be provided"
113 for identifier
in device_entry.identifiers
114 if identifier[0] == DOMAIN
121 entry: HomeConnectConfigEntry,
122 ) -> api.HomeConnectAppliance |
None:
123 for device
in entry.runtime_data.devices:
124 appliance = device.appliance
125 if appliance.haId == ha_id:
130 for entry_id
in device_entry.config_entries:
131 entry = hass.config_entries.async_get_entry(entry_id)
133 if entry.domain == DOMAIN:
134 entry = cast(HomeConnectConfigEntry, entry)
135 if (appliance := find_appliance(entry))
is not None:
137 elif (appliance := find_appliance(entry))
is not None:
139 raise ValueError(f
"Appliance for device id {device_entry.id} not found")
142 async
def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
143 """Set up Home Connect component."""
145 async
def _async_service_program(call, method):
146 """Execute calls to services taking a program."""
147 program = call.data[ATTR_PROGRAM]
148 device_id = call.data[ATTR_DEVICE_ID]
152 option_key = call.data.get(ATTR_KEY)
153 if option_key
is not None:
154 option = {ATTR_KEY: option_key, ATTR_VALUE: call.data[ATTR_VALUE]}
156 option_unit = call.data.get(ATTR_UNIT)
157 if option_unit
is not None:
158 option[ATTR_UNIT] = option_unit
160 options.append(option)
163 await hass.async_add_executor_job(getattr(appliance, method), program, options)
165 async
def _async_service_command(call, command):
166 """Execute calls to services executing a command."""
167 device_id = call.data[ATTR_DEVICE_ID]
170 await hass.async_add_executor_job(appliance.execute_command, command)
172 async
def _async_service_key_value(call, method):
173 """Execute calls to services taking a key and value."""
174 key = call.data[ATTR_KEY]
175 value = call.data[ATTR_VALUE]
176 unit = call.data.get(ATTR_UNIT)
177 device_id = call.data[ATTR_DEVICE_ID]
181 await hass.async_add_executor_job(
182 getattr(appliance, method),
188 await hass.async_add_executor_job(
189 getattr(appliance, method),
194 async
def async_service_option_active(call):
195 """Service for setting an option for an active program."""
196 await _async_service_key_value(call,
"set_options_active_program")
198 async
def async_service_option_selected(call):
199 """Service for setting an option for a selected program."""
200 await _async_service_key_value(call,
"set_options_selected_program")
202 async
def async_service_setting(call):
203 """Service for changing a setting."""
204 await _async_service_key_value(call,
"set_setting")
206 async
def async_service_pause_program(call):
207 """Service for pausing a program."""
208 await _async_service_command(call, BSH_PAUSE)
210 async
def async_service_resume_program(call):
211 """Service for resuming a paused program."""
212 await _async_service_command(call, BSH_RESUME)
214 async
def async_service_select_program(call):
215 """Service for selecting a program."""
216 await _async_service_program(call,
"select_program")
218 async
def async_service_start_program(call):
219 """Service for starting a program."""
220 await _async_service_program(call,
"start_program")
222 hass.services.async_register(
224 SERVICE_OPTION_ACTIVE,
225 async_service_option_active,
226 schema=SERVICE_OPTION_SCHEMA,
228 hass.services.async_register(
230 SERVICE_OPTION_SELECTED,
231 async_service_option_selected,
232 schema=SERVICE_OPTION_SCHEMA,
234 hass.services.async_register(
235 DOMAIN, SERVICE_SETTING, async_service_setting, schema=SERVICE_SETTING_SCHEMA
237 hass.services.async_register(
239 SERVICE_PAUSE_PROGRAM,
240 async_service_pause_program,
241 schema=SERVICE_COMMAND_SCHEMA,
243 hass.services.async_register(
245 SERVICE_RESUME_PROGRAM,
246 async_service_resume_program,
247 schema=SERVICE_COMMAND_SCHEMA,
249 hass.services.async_register(
251 SERVICE_SELECT_PROGRAM,
252 async_service_select_program,
253 schema=SERVICE_PROGRAM_SCHEMA,
255 hass.services.async_register(
257 SERVICE_START_PROGRAM,
258 async_service_start_program,
259 schema=SERVICE_PROGRAM_SCHEMA,
266 """Set up Home Connect from a config entry."""
268 await config_entry_oauth2_flow.async_get_config_entry_implementation(
277 await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
283 hass: HomeAssistant, entry: HomeConnectConfigEntry
285 """Unload a config entry."""
286 return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
289 @Throttle(SCAN_INTERVAL)
291 hass: HomeAssistant, entry: HomeConnectConfigEntry
293 """Update all the devices."""
294 hc_api = entry.runtime_data
297 await hass.async_add_executor_job(hc_api.get_devices)
298 for device
in hc_api.devices:
299 await hass.async_add_executor_job(device.initialize)
300 except HTTPError
as err:
301 _LOGGER.warning(
"Cannot update devices: %s", err.response.status_code)
305 hass: HomeAssistant, entry: HomeConnectConfigEntry
307 """Migrate old entry."""
308 _LOGGER.debug(
"Migrating from version %s", entry.version)
310 if entry.version == 1
and entry.minor_version == 1:
314 entity_entry: RegistryEntry,
315 ) -> dict[str, Any] |
None:
316 """Update unique ID of entity entry."""
317 for old_id_suffix, new_id_suffix
in OLD_NEW_UNIQUE_ID_SUFFIX_MAP.items():
318 if entity_entry.unique_id.endswith(f
"-{old_id_suffix}"):
320 "new_unique_id": entity_entry.unique_id.replace(
321 old_id_suffix, new_id_suffix
328 hass.config_entries.async_update_entry(entry, minor_version=2)
330 _LOGGER.debug(
"Migration to version %s successful", entry.version)
335 """Return a dict from a Home Connect error."""
337 "description": cast(dict[str, Any], err.args[0]).
get(
"description",
"?")
338 if len(err.args) > 0
and isinstance(err.args[0], dict)
340 if len(err.args) > 0
and isinstance(err.args[0], str)
346 """Convert a BSH key to a translation key format.
348 This function takes a BSH key, such as `Dishcare.Dishwasher.Program.Eco50`,
349 and converts it to a translation key format, such as `dishcare_dishwasher_bsh_key_eco50`.
352 RE_CAMEL_CASE.sub(
"_", split)
for split
in bsh_key.split(
".")
None async_migrate_entries(HomeAssistant hass, dict[str, AdapterDetails] adapters, str default_adapter)
web.Response get(self, web.Request request, str config_key)
dict[str, str]|None update_unique_id(er.RegistryEntry entity_entry, str unique_id)
dict[str, Any] get_dict_from_home_connect_error(api.HomeConnectError err)
bool async_migrate_entry(HomeAssistant hass, HomeConnectConfigEntry entry)
bool async_unload_entry(HomeAssistant hass, HomeConnectConfigEntry entry)
api.HomeConnectAppliance _get_appliance(HomeAssistant hass, str|None device_id=None, dr.DeviceEntry|None device_entry=None, HomeConnectConfigEntry|None entry=None)
str bsh_key_to_translation_key(str bsh_key)
None update_all_devices(HomeAssistant hass, HomeConnectConfigEntry entry)
bool async_setup(HomeAssistant hass, ConfigType config)
bool async_setup_entry(HomeAssistant hass, HomeConnectConfigEntry entry)