async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Smappee from a zeroconf or config entry.""" if CONF_IP_ADDRESS in entry.data: if helper.is_smappee_genius(entry.data[CONF_SERIALNUMBER]): # next generation: local mqtt broker smappee_mqtt = mqtt.SmappeeLocalMqtt( serial_number=entry.data[CONF_SERIALNUMBER]) await hass.async_add_executor_job( smappee_mqtt.start_and_wait_for_config) smappee = Smappee(api=smappee_mqtt, serialnumber=entry.data[CONF_SERIALNUMBER]) else: # legacy devices through local api smappee_api = api.api.SmappeeLocalApi( ip=entry.data[CONF_IP_ADDRESS]) smappee = Smappee(api=smappee_api, serialnumber=entry.data[CONF_SERIALNUMBER]) await hass.async_add_executor_job(smappee.load_local_service_location) else: implementation = ( await config_entry_oauth2_flow.async_get_config_entry_implementation( hass, entry)) smappee_api = api.ConfigEntrySmappeeApi(hass, entry, implementation) smappee = Smappee(api=smappee_api) await hass.async_add_executor_job(smappee.load_service_locations) hass.data[DOMAIN][entry.entry_id] = SmappeeBase(hass, smappee) hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True
async def async_step_local(self, user_input=None): """Handle local flow.""" if user_input is None: return self.async_show_form( step_id="local", data_schema=vol.Schema({vol.Required(CONF_HOST): str}), errors={}, ) # In a LOCAL setup we still need to resolve the host to serial number ip_address = user_input["host"] serial_number = None # Attempt 1: try to use the local api (older generation) to resolve host to serialnumber smappee_api = api.api.SmappeeLocalApi(ip=ip_address) logon = await self.opp.async_add_executor_job(smappee_api.logon) if logon is not None: advanced_config = await self.opp.async_add_executor_job( smappee_api.load_advanced_config) for config_item in advanced_config: if config_item["key"] == "mdnsHostName": serial_number = config_item["value"] else: # Attempt 2: try to use the local mqtt broker (newer generation) to resolve host to serialnumber smappee_mqtt = mqtt.SmappeeLocalMqtt() connect = await self.opp.async_add_executor_job( smappee_mqtt.start_attempt) if not connect: return self.async_abort(reason="cannot_connect") serial_number = await self.opp.async_add_executor_job( smappee_mqtt.start_and_wait_for_config) await self.opp.async_add_executor_job(smappee_mqtt.stop) if serial_number is None: return self.async_abort(reason="cannot_connect") if serial_number is None or not serial_number.startswith( SUPPORTED_LOCAL_DEVICES): return self.async_abort(reason="invalid_mdns") serial_number = serial_number.replace("Smappee", "") # Check if already configured (local) await self.async_set_unique_id(serial_number, raise_on_progress=False) self._abort_if_unique_id_configured() return self.async_create_entry( title=f"{DOMAIN}{serial_number}", data={ CONF_IP_ADDRESS: ip_address, CONF_SERIALNUMBER: serial_number }, )
async def async_step_zeroconf_confirm(self, user_input=None): """Confirm zeroconf flow.""" errors = {} # Check if already configured (cloud) if self.is_cloud_device_already_added(): return self.async_abort(reason="already_configured_device") if user_input is None: serialnumber = self.context.get(CONF_SERIALNUMBER) return self.async_show_form( step_id="zeroconf_confirm", description_placeholders={"serialnumber": serialnumber}, errors=errors, ) ip_address = self.context.get(CONF_IP_ADDRESS) serial_number = self.context.get(CONF_SERIALNUMBER) # Attempt to make a connection to the local device if helper.is_smappee_genius(serial_number): # next generation device, attempt connect to the local mqtt broker smappee_mqtt = mqtt.SmappeeLocalMqtt(serial_number=serial_number) connect = await self.opp.async_add_executor_job( smappee_mqtt.start_attempt) if not connect: return self.async_abort(reason="cannot_connect") else: # legacy devices, without local mqtt broker, try api access smappee_api = api.api.SmappeeLocalApi(ip=ip_address) logon = await self.opp.async_add_executor_job(smappee_api.logon) if logon is None: return self.async_abort(reason="cannot_connect") return self.async_create_entry( title=f"{DOMAIN}{serial_number}", data={ CONF_IP_ADDRESS: ip_address, CONF_SERIALNUMBER: serial_number }, )