def events(hass): """Fixture that catches alexa events.""" events = [] hass.bus.async_listen( smart_home.EVENT_ALEXA_SMART_HOME, callback(lambda e: events.append(e)) ) yield events
def test_async_add_job_schedule_callback(): """Test that we schedule coroutines and add jobs to the job pool.""" hass = MagicMock() job = MagicMock() ha.HomeAssistant.async_add_job(hass, ha.callback(job)) assert len(hass.loop.call_soon.mock_calls) == 1 assert len(hass.loop.create_task.mock_calls) == 0 assert len(hass.add_job.mock_calls) == 0
def test_async_add_job_schedule_partial_callback(): """Test that we schedule partial coros and add jobs to the job pool.""" hass = MagicMock() job = MagicMock() partial = functools.partial(ha.callback(job)) ha.HomeAssistant.async_add_job(hass, partial) assert len(hass.loop.call_soon.mock_calls) == 1 assert len(hass.loop.create_task.mock_calls) == 0 assert len(hass.add_job.mock_calls) == 0
def test_async_run_job_calls_callback(): """Test that the callback annotation is respected.""" hass = MagicMock() calls = [] def job(): calls.append(1) ha.HomeAssistant.async_run_job(hass, ha.callback(job)) assert len(calls) == 1 assert len(hass.async_add_job.mock_calls) == 0
async def test_track_point_in_time(hass): """Test track point in time.""" before_birthday = datetime(1985, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) birthday_paulus = datetime(1986, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) after_birthday = datetime(1987, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) runs = [] async_track_point_in_utc_time(hass, callback(lambda x: runs.append(1)), birthday_paulus) async_fire_time_changed(hass, before_birthday) await hass.async_block_till_done() assert len(runs) == 0 async_fire_time_changed(hass, birthday_paulus) await hass.async_block_till_done() assert len(runs) == 1 # A point in time tracker will only fire once, this should do nothing async_fire_time_changed(hass, birthday_paulus) await hass.async_block_till_done() assert len(runs) == 1 async_track_point_in_utc_time(hass, callback(lambda x: runs.append(1)), birthday_paulus) async_fire_time_changed(hass, after_birthday) await hass.async_block_till_done() assert len(runs) == 2 unsub = async_track_point_in_time(hass, callback(lambda x: runs.append(1)), birthday_paulus) unsub() async_fire_time_changed(hass, after_birthday) await hass.async_block_till_done() assert len(runs) == 2
def test_track_point_in_time(self): """Test track point in time.""" before_birthday = datetime(1985, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) birthday_paulus = datetime(1986, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) after_birthday = datetime(1987, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) runs = [] track_point_in_utc_time( self.hass, callback(lambda x: runs.append(1)), birthday_paulus) self._send_time_changed(before_birthday) self.hass.block_till_done() self.assertEqual(0, len(runs)) self._send_time_changed(birthday_paulus) self.hass.block_till_done() self.assertEqual(1, len(runs)) # A point in time tracker will only fire once, this should do nothing self._send_time_changed(birthday_paulus) self.hass.block_till_done() self.assertEqual(1, len(runs)) track_point_in_time( self.hass, callback(lambda x: runs.append(1)), birthday_paulus) self._send_time_changed(after_birthday) self.hass.block_till_done() self.assertEqual(2, len(runs)) unsub = track_point_in_time( self.hass, callback(lambda x: runs.append(1)), birthday_paulus) unsub() self._send_time_changed(after_birthday) self.hass.block_till_done() self.assertEqual(2, len(runs))
def test_track_point_in_time(self): """Test track point in time.""" before_birthday = datetime(1985, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) birthday_paulus = datetime(1986, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) after_birthday = datetime(1987, 7, 9, 12, 0, 0, tzinfo=dt_util.UTC) runs = [] track_point_in_utc_time( self.hass, callback(lambda x: runs.append(1)), birthday_paulus) self._send_time_changed(before_birthday) self.hass.block_till_done() assert 0 == len(runs) self._send_time_changed(birthday_paulus) self.hass.block_till_done() assert 1 == len(runs) # A point in time tracker will only fire once, this should do nothing self._send_time_changed(birthday_paulus) self.hass.block_till_done() assert 1 == len(runs) track_point_in_time( self.hass, callback(lambda x: runs.append(1)), birthday_paulus) self._send_time_changed(after_birthday) self.hass.block_till_done() assert 2 == len(runs) unsub = track_point_in_time( self.hass, callback(lambda x: runs.append(1)), birthday_paulus) unsub() self._send_time_changed(after_birthday) self.hass.block_till_done() assert 2 == len(runs)
async def test_entity_without_progress_support_raising( hass: HomeAssistant, enable_custom_integrations: None, caplog: pytest.LogCaptureFixture, ) -> None: """Test update entity without progress support that raises during install. In that case, progress is still handled by Home Assistant. """ platform = getattr(hass.components, f"test.{DOMAIN}") platform.init() assert await async_setup_component(hass, DOMAIN, {DOMAIN: { CONF_PLATFORM: "test" }}) await hass.async_block_till_done() events = [] async_track_state_change_event( hass, "update.update_available", callback(lambda event: events.append(event))) with patch( "homeassistant.components.update.UpdateEntity.async_install", side_effect=RuntimeError, ), pytest.raises(RuntimeError): await hass.services.async_call( DOMAIN, SERVICE_INSTALL, {ATTR_ENTITY_ID: "update.update_available"}, blocking=True, ) assert len(events) == 2 assert events[0].data.get( "old_state").attributes[ATTR_IN_PROGRESS] is False assert events[0].data.get( "old_state").attributes[ATTR_CURRENT_VERSION] == "1.0.0" assert events[0].data.get("new_state").attributes[ATTR_IN_PROGRESS] is True assert events[0].data.get( "new_state").attributes[ATTR_CURRENT_VERSION] == "1.0.0" assert events[1].data.get("old_state").attributes[ATTR_IN_PROGRESS] is True assert events[1].data.get( "old_state").attributes[ATTR_CURRENT_VERSION] == "1.0.0" assert events[1].data.get( "new_state").attributes[ATTR_IN_PROGRESS] is False assert events[1].data.get( "new_state").attributes[ATTR_CURRENT_VERSION] == "1.0.0"
async def test_periodic_task_duplicate_time(hass): """Test periodic tasks not triggering on duplicate time.""" specific_runs = [] now = dt_util.utcnow() time_that_will_not_match_right_away = datetime(now.year + 1, 5, 24, 21, 59, 55, tzinfo=dt_util.UTC) with patch("homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away): unsub = async_track_utc_time_change( hass, callback(lambda x: specific_runs.append(x)), hour="/2", minute=0, second=0, ) async_fire_time_changed( hass, datetime(now.year + 1, 5, 24, 22, 0, 0, 999999, tzinfo=dt_util.UTC)) await hass.async_block_till_done() assert len(specific_runs) == 1 async_fire_time_changed( hass, datetime(now.year + 1, 5, 24, 22, 0, 0, 999999, tzinfo=dt_util.UTC)) await hass.async_block_till_done() assert len(specific_runs) == 1 async_fire_time_changed( hass, datetime(now.year + 1, 5, 25, 0, 0, 0, 999999, tzinfo=dt_util.UTC)) await hass.async_block_till_done() assert len(specific_runs) == 2 unsub()
async def test_periodic_task_wrong_input(hass): """Test periodic tasks with wrong input.""" specific_runs = [] now = dt_util.utcnow() with pytest.raises(ValueError): async_track_utc_time_change( hass, callback(lambda x: specific_runs.append(x)), hour="/two") async_fire_time_changed( hass, datetime(now.year + 1, 5, 2, 0, 0, 0, 999999, tzinfo=dt_util.UTC)) await hass.async_block_till_done() assert len(specific_runs) == 0
async def async_added_to_hass(self): """Subscribe to children and template state changes.""" @callback def _async_on_dependency_update(event): """Update ha state when dependencies update.""" self.async_set_context(event.context) self.async_schedule_update_ha_state(True) @callback def _async_on_template_update(event, updates): """Update ha state when dependencies update.""" result = updates.pop().result if isinstance(result, TemplateError): self._state_template_result = None else: self._state_template_result = result if event: self.async_set_context(event.context) self.async_schedule_update_ha_state(True) if self._state_template is not None: result = async_track_template_result( self.hass, [TrackTemplate(self._state_template, None)], _async_on_template_update, ) self.hass.bus.async_listen_once( EVENT_HOMEASSISTANT_START, callback(lambda _: result.async_refresh()) ) self.async_on_remove(result.async_remove) depend = copy(self._children) for entity in self._attrs.values(): depend.append(entity[0]) self.async_on_remove( self.hass.helpers.event.async_track_state_change_event( list(set(depend)), _async_on_dependency_update ) )
async def test_periodic_task_entering_dst(hass): """Test periodic task behavior when entering dst.""" timezone = dt_util.get_time_zone("Europe/Vienna") dt_util.set_default_time_zone(timezone) specific_runs = [] now = dt_util.utcnow() time_that_will_not_match_right_away = timezone.localize( datetime(now.year + 1, 3, 25, 2, 31, 0)) with patch("homeassistant.util.dt.utcnow", return_value=time_that_will_not_match_right_away): unsub = async_track_time_change( hass, callback(lambda x: specific_runs.append(x)), hour=2, minute=30, second=0, ) async_fire_time_changed( hass, timezone.localize(datetime(now.year + 1, 3, 25, 1, 50, 0, 999999))) await hass.async_block_till_done() assert len(specific_runs) == 0 async_fire_time_changed( hass, timezone.localize(datetime(now.year + 1, 3, 25, 3, 50, 0, 999999))) await hass.async_block_till_done() assert len(specific_runs) == 0 async_fire_time_changed( hass, timezone.localize(datetime(now.year + 1, 3, 26, 1, 50, 0, 999999))) await hass.async_block_till_done() assert len(specific_runs) == 0 async_fire_time_changed( hass, timezone.localize(datetime(now.year + 1, 3, 26, 2, 50, 0, 999999))) await hass.async_block_till_done() assert len(specific_runs) == 1 unsub()
async def test_master_state_with_template(hass): """Test the state_template option.""" hass.states.async_set("input_boolean.test", STATE_OFF) hass.states.async_set("media_player.mock1", STATE_OFF) templ = ( '{% if states.input_boolean.test.state == "off" %}on' "{% else %}{{ states.media_player.mock1.state }}{% endif %}" ) await async_setup_component( hass, "media_player", { "media_player": { "platform": "universal", "name": "tv", "state_template": templ, } }, ) await hass.async_block_till_done() assert len(hass.states.async_all()) == 3 await hass.async_start() await hass.async_block_till_done() assert hass.states.get("media_player.tv").state == STATE_ON events = [] hass.helpers.event.async_track_state_change_event( "media_player.tv", callback(lambda event: events.append(event)) ) context = Context() hass.states.async_set("input_boolean.test", STATE_ON, context=context) await hass.async_block_till_done() assert hass.states.get("media_player.tv").state == STATE_OFF assert events[0].context == context
async def test_state_triggers(hass): """Test sensor with state triggers.""" hass.states.async_set("sensor.test_monitored", STATE_OFF) config = { "binary_sensor": { "name": "Test_Binary", "platform": "bayesian", "observations": [ { "platform": "state", "entity_id": "sensor.test_monitored", "to_state": "off", "prob_given_true": 999.9, "prob_given_false": 999.4, }, ], "prior": 0.2, "probability_threshold": 0.32, } } await async_setup_component(hass, "binary_sensor", config) await hass.async_block_till_done() assert hass.states.get("binary_sensor.test_binary").state == STATE_OFF events = [] async_track_state_change_event( hass, "binary_sensor.test_binary", callback(lambda event: events.append(event))) context = Context() hass.states.async_set("sensor.test_monitored", STATE_ON, context=context) await hass.async_block_till_done() await hass.async_block_till_done() assert events[0].context == context
async def test_template_triggers(hass): """Test sensor with template triggers.""" hass.states.async_set("input_boolean.test", STATE_OFF) config = { "binary_sensor": { "name": "Test_Binary", "platform": "bayesian", "observations": [ { "platform": "template", "value_template": "{{ states.input_boolean.test.state }}", "prob_given_true": 1999.9, }, ], "prior": 0.2, "probability_threshold": 0.32, } } await async_setup_component(hass, "binary_sensor", config) await hass.async_block_till_done() assert hass.states.get("binary_sensor.test_binary").state == STATE_OFF events = [] async_track_state_change_event( hass, "binary_sensor.test_binary", callback(lambda event: events.append(event))) context = Context() hass.states.async_set("input_boolean.test", STATE_ON, context=context) await hass.async_block_till_done() await hass.async_block_till_done() assert events[0].context == context
def catch_log_exception( func: Callable[..., Any], format_err: Callable[..., Any], *args: Any) -> Callable[..., None | Coroutine[Any, Any, None]]: """Decorate a callback to catch and log exceptions.""" # Check for partials to properly determine if coroutine function check_func = func while isinstance(check_func, partial): check_func = check_func.func wrapper_func: Callable[..., None | Coroutine[Any, Any, None]] if asyncio.iscoroutinefunction(check_func): async_func = cast(Callable[..., Coroutine[Any, Any, None]], func) @wraps(async_func) async def async_wrapper(*args: Any) -> None: """Catch and log exception.""" try: await async_func(*args) except Exception: # pylint: disable=broad-except log_exception(format_err, *args) wrapper_func = async_wrapper else: @wraps(func) def wrapper(*args: Any) -> None: """Catch and log exception.""" try: func(*args) except Exception: # pylint: disable=broad-except log_exception(format_err, *args) if is_callback(check_func): wrapper = callback(wrapper) wrapper_func = wrapper return wrapper_func
async def test_update_with_progress(hass: HomeAssistant) -> None: """Test update with progress.""" state = hass.states.get("update.demo_update_with_progress") assert state assert state.state == STATE_ON assert state.attributes[ATTR_IN_PROGRESS] is False events = [] async_track_state_change_event( hass, "update.demo_update_with_progress", callback(lambda event: events.append(event)), ) with patch("homeassistant.components.demo.update.FAKE_INSTALL_SLEEP_TIME", new=0): await hass.services.async_call( DOMAIN, SERVICE_INSTALL, {ATTR_ENTITY_ID: "update.demo_update_with_progress"}, blocking=True, ) assert len(events) == 10 assert events[0].data["new_state"].state == STATE_ON assert events[0].data["new_state"].attributes[ATTR_IN_PROGRESS] == 10 assert events[1].data["new_state"].attributes[ATTR_IN_PROGRESS] == 20 assert events[2].data["new_state"].attributes[ATTR_IN_PROGRESS] == 30 assert events[3].data["new_state"].attributes[ATTR_IN_PROGRESS] == 40 assert events[4].data["new_state"].attributes[ATTR_IN_PROGRESS] == 50 assert events[5].data["new_state"].attributes[ATTR_IN_PROGRESS] == 60 assert events[6].data["new_state"].attributes[ATTR_IN_PROGRESS] == 70 assert events[7].data["new_state"].attributes[ATTR_IN_PROGRESS] == 80 assert events[8].data["new_state"].attributes[ATTR_IN_PROGRESS] == 90 assert events[9].data["new_state"].attributes[ATTR_IN_PROGRESS] is False assert events[9].data["new_state"].state == STATE_OFF
if entity_ids: entity_ids = async_filter_entities(hass, entity_ids) event_types = async_determine_event_types(hass, entity_ids, device_ids) event_processor = EventProcessor( hass, event_types, entity_ids, device_ids, None, timestamp=True, include_entity_name=False, ) if end_time and end_time <= utc_now: # Not live stream but we it might be a big query connection.subscriptions[msg_id] = callback(lambda: None) connection.send_result(msg_id) # Fetch everything from history await _async_send_historical_events( hass, connection, msg_id, start_time, end_time, messages.event_message, event_processor, partial=False, ) return subscriptions: list[CALLBACK_TYPE] = []
async def publish_updates(self): """Schedule call all registered callbacks.""" for callback in self._callbacks: callback()
def events(hass): """Fixture that catches alexa events.""" events = [] hass.bus.async_listen(smart_home.EVENT_ALEXA_SMART_HOME, callback(lambda e: events.append(e))) yield events
async def _subscribe_topics(sub_state, topics): # Optionally mark message handlers as callback for topic in topics.values(): if "msg_callback" in topic and "event_loop_safe" in topic: topic["msg_callback"] = callback(topic["msg_callback"]) return await async_subscribe_topics(hass, sub_state, topics)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: """Set up an input select.""" component = EntityComponent(_LOGGER, DOMAIN, hass) # Process integration platforms right away since # we will create entities before firing EVENT_COMPONENT_LOADED await async_process_integration_platform_for_component(hass, DOMAIN) id_manager = collection.IDManager() yaml_collection = collection.YamlCollection( logging.getLogger(f"{__name__}.yaml_collection"), id_manager) collection.sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component, yaml_collection, InputSelect.from_yaml) storage_collection = InputSelectStorageCollection( InputSelectStore(hass, STORAGE_VERSION, STORAGE_KEY, minor_version=STORAGE_VERSION_MINOR), logging.getLogger(f"{__name__}.storage_collection"), id_manager, ) collection.sync_entity_lifecycle(hass, DOMAIN, DOMAIN, component, storage_collection, InputSelect) await yaml_collection.async_load([{ CONF_ID: id_, **cfg } for id_, cfg in config.get(DOMAIN, {}).items()]) await storage_collection.async_load() collection.StorageCollectionWebsocket(storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS).async_setup(hass) async def reload_service_handler(service_call: ServiceCall) -> None: """Reload yaml entities.""" conf = await component.async_prepare_reload(skip_reset=True) if conf is None: conf = {DOMAIN: {}} await yaml_collection.async_load([{ CONF_ID: id_, **cfg } for id_, cfg in conf.get(DOMAIN, {}).items()]) homeassistant.helpers.service.async_register_admin_service( hass, DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=RELOAD_SERVICE_SCHEMA, ) component.async_register_entity_service( SERVICE_SELECT_OPTION, {vol.Required(ATTR_OPTION): cv.string}, "async_select_option", ) component.async_register_entity_service( SERVICE_SELECT_NEXT, {vol.Optional(ATTR_CYCLE, default=True): bool}, "async_next", ) component.async_register_entity_service( SERVICE_SELECT_PREVIOUS, {vol.Optional(ATTR_CYCLE, default=True): bool}, "async_previous", ) component.async_register_entity_service( SERVICE_SELECT_FIRST, {}, callback(lambda entity, call: entity.async_select_index(0)), ) component.async_register_entity_service( SERVICE_SELECT_LAST, {}, callback(lambda entity, call: entity.async_select_index(-1)), ) component.async_register_entity_service( SERVICE_SET_OPTIONS, { vol.Required(ATTR_OPTIONS): vol.All(cv.ensure_list, vol.Length(min=1), [cv.string]) }, "async_set_options", ) return True
async def async_setup(hass: HomeAssistantType, config: ConfigType) -> bool: """Set up an input select.""" component = hass.data[DOMAIN] = EntityComponent(_LOGGER, DOMAIN, hass) id_manager = collection.IDManager() yaml_collection = collection.YamlCollection( logging.getLogger(f"{__name__}.yaml_collection"), id_manager) collection.attach_entity_component_collection(component, yaml_collection, InputSelect.from_yaml) storage_collection = InputSelectStorageCollection( Store(hass, STORAGE_VERSION, STORAGE_KEY), logging.getLogger(f"{__name__}.storage_collection"), id_manager, ) collection.attach_entity_component_collection(component, storage_collection, InputSelect) await yaml_collection.async_load([{ CONF_ID: id_, **cfg } for id_, cfg in config.get(DOMAIN, {}).items()]) await storage_collection.async_load() collection.StorageCollectionWebsocket(storage_collection, DOMAIN, DOMAIN, CREATE_FIELDS, UPDATE_FIELDS).async_setup(hass) collection.attach_entity_registry_cleaner(hass, DOMAIN, DOMAIN, yaml_collection) collection.attach_entity_registry_cleaner(hass, DOMAIN, DOMAIN, storage_collection) async def reload_service_handler(service_call: ServiceCallType) -> None: """Reload yaml entities.""" conf = await component.async_prepare_reload(skip_reset=True) if conf is None: conf = {DOMAIN: {}} await yaml_collection.async_load([{ CONF_ID: id_, **cfg } for id_, cfg in conf.get(DOMAIN, {}).items()]) homeassistant.helpers.service.async_register_admin_service( hass, DOMAIN, SERVICE_RELOAD, reload_service_handler, schema=RELOAD_SERVICE_SCHEMA, ) component.async_register_entity_service( SERVICE_SELECT_OPTION, {vol.Required(ATTR_OPTION): cv.string}, "async_select_option", ) component.async_register_entity_service( SERVICE_SELECT_NEXT, {}, callback(lambda entity, call: entity.async_offset_index(1)), ) component.async_register_entity_service( SERVICE_SELECT_PREVIOUS, {}, callback(lambda entity, call: entity.async_offset_index(-1)), ) component.async_register_entity_service( SERVICE_SET_OPTIONS, { vol.Required(ATTR_OPTIONS): vol.All(cv.ensure_list, vol.Length(min=1), [cv.string]) }, "async_set_options", ) return True
def _notify(self, message): _LOGGER.debug("{} send notify".format(self._name)) for callback in self._subscribers: callback(message)
async def test_track_sunset(hass, legacy_patchable_time): """Test track the sunset.""" latitude = 32.87336 longitude = 117.22743 # Setup sun component hass.config.latitude = latitude hass.config.longitude = longitude assert await async_setup_component(hass, sun.DOMAIN, {sun.DOMAIN: { sun.CONF_ELEVATION: 0 }}) # Get next sunrise/sunset astral = Astral() utc_now = datetime(2014, 5, 24, 12, 0, 0, tzinfo=dt_util.UTC) utc_today = utc_now.date() mod = -1 while True: next_setting = astral.sunset_utc(utc_today + timedelta(days=mod), latitude, longitude) if next_setting > utc_now: break mod += 1 # Track sunset runs = [] with patch("homeassistant.util.dt.utcnow", return_value=utc_now): unsub = async_track_sunset(hass, callback(lambda: runs.append(1))) offset_runs = [] offset = timedelta(minutes=30) with patch("homeassistant.util.dt.utcnow", return_value=utc_now): unsub2 = async_track_sunset(hass, callback(lambda: offset_runs.append(1)), offset) # Run tests async_fire_time_changed(hass, next_setting - offset) await hass.async_block_till_done() assert len(runs) == 0 assert len(offset_runs) == 0 async_fire_time_changed(hass, next_setting) await hass.async_block_till_done() assert len(runs) == 1 assert len(offset_runs) == 0 async_fire_time_changed(hass, next_setting + offset) await hass.async_block_till_done() assert len(runs) == 1 assert len(offset_runs) == 1 unsub() unsub2() async_fire_time_changed(hass, next_setting + offset) await hass.async_block_till_done() assert len(runs) == 1 assert len(offset_runs) == 1
def events(hass): """Fixture that catches notify events.""" events = [] hass.bus.async_listen(demo.EVENT_NOTIFY, callback(lambda e: events.append(e))) yield events
async def _recv(self): while not self._connection.closed: try: data = await self._connection.receive() except aiohttp.client_exceptions.ClientError as err: _LOGGER.error('remote websocket connection closed: %s', err) break if not data: break if data.type in (aiohttp.WSMsgType.CLOSE, aiohttp.WSMsgType.CLOSED, aiohttp.WSMsgType.CLOSING): _LOGGER.debug('websocket connection is closing') break if data.type == aiohttp.WSMsgType.ERROR: _LOGGER.error('websocket connection had an error') break try: message = data.json() except TypeError as err: _LOGGER.error('could not decode data (%s) as json: %s', data, err) break if message is None: break _LOGGER.debug('received: %s', message) if message['type'] == api.TYPE_AUTH_OK: self.set_connection_state(STATE_CONNECTED) await self._init() elif message['type'] == api.TYPE_AUTH_REQUIRED: if not (self._access_token or self._password): _LOGGER.error( 'Access token or api password required, but not provided' ) self.set_connection_state(STATE_AUTH_REQUIRED) return if self._access_token: data = { 'type': api.TYPE_AUTH, 'access_token': self._access_token } else: data = { 'type': api.TYPE_AUTH, 'api_password': self._password } try: await self._connection.send_json(data) except Exception as err: _LOGGER.error( 'could not send data to remote connection: %s', err) break elif message['type'] == api.TYPE_AUTH_INVALID: _LOGGER.error( 'Auth invalid, check your access token or API password') self.set_connection_state(STATE_AUTH_INVALID) await self._connection.close() return else: callback = self._handlers.get(message['id']) if callback is not None: callback(message) await self._disconnected()
async def _recv(self): while not self._connection.closed: try: data = await self._connection.receive() except aiohttp.client_exceptions.ClientError as err: _LOGGER.error("remote websocket connection closed: %s", err) break if not data: break if data.type in ( aiohttp.WSMsgType.CLOSE, aiohttp.WSMsgType.CLOSED, aiohttp.WSMsgType.CLOSING, ): _LOGGER.debug("websocket connection is closing") break if data.type == aiohttp.WSMsgType.ERROR: _LOGGER.error("websocket connection had an error") break try: message = data.json() except TypeError as err: _LOGGER.error("could not decode data (%s) as json: %s", data, err) break if message is None: break _LOGGER.debug("received: %s", message) if message["type"] == api.TYPE_AUTH_OK: self.set_connection_state(STATE_CONNECTED) await self._init() elif message["type"] == api.TYPE_AUTH_REQUIRED: if self._access_token: data = { "type": api.TYPE_AUTH, "access_token": self._access_token } else: _LOGGER.error("Access token required, but not provided") self.set_connection_state(STATE_AUTH_REQUIRED) return try: await self._connection.send_json(data) except Exception as err: _LOGGER.error( "could not send data to remote connection: %s", err) break elif message["type"] == api.TYPE_AUTH_INVALID: _LOGGER.error("Auth invalid, check your access token") self.set_connection_state(STATE_AUTH_INVALID) await self._connection.close() return else: callback = self._handlers.get(message["id"]) if callback is not None: if inspect.iscoroutinefunction(callback): await callback(message) else: callback(message) await self._disconnected()
async def create_individual_sensors( hass: HomeAssistantType, sensor_config: dict, discovery_info: DiscoveryInfoType | None = None, ) -> list[SensorEntity, RealPowerSensor]: """Create entities (power, energy, utility_meters) which track the appliance.""" if discovery_info: source_entity = discovery_info.get(DISCOVERY_SOURCE_ENTITY) else: source_entity = await create_source_entity( sensor_config[CONF_ENTITY_ID], hass) if (source_entity.entity_id in hass.data[DOMAIN][DATA_CONFIGURED_ENTITIES] and source_entity.entity_id != DUMMY_ENTITY_ID): # Display an error when a power sensor was already configured for the same entity by the user # No log entry will be shown when the entity was auto discovered, we can silently continue if not discovery_info: existing_entities = hass.data[DOMAIN][ DATA_CONFIGURED_ENTITIES].get(source_entity.entity_id) raise SensorAlreadyConfiguredError(source_entity.entity_id, existing_entities) return [] entities_to_add = [] energy_sensor = None if CONF_DAILY_FIXED_ENERGY in sensor_config: energy_sensor = await create_daily_fixed_energy_sensor( hass, sensor_config) entities_to_add.append(energy_sensor) else: try: power_sensor = await create_power_sensor(hass, sensor_config, source_entity, discovery_info) except PowercalcSetupError: return [] entities_to_add.append(power_sensor) # Create energy sensor which integrates the power sensor if sensor_config.get(CONF_CREATE_ENERGY_SENSOR): energy_sensor = await create_energy_sensor(hass, sensor_config, power_sensor, source_entity) entities_to_add.append(energy_sensor) if energy_sensor: entities_to_add.extend(await create_utility_meters(hass, energy_sensor, sensor_config)) if discovery_info: hass.data[DOMAIN][DATA_DISCOVERED_ENTITIES].append( source_entity.entity_id) else: hass.data[DOMAIN][DATA_CONFIGURED_ENTITIES].update( {source_entity.entity_id: entities_to_add}) if source_entity.entity_entry and source_entity.device_entry: hass.bus.async_listen_once( EVENT_HOMEASSISTANT_STARTED, callback(lambda _: bind_entities_to_devices( hass, entities_to_add, source_entity.device_entry.id, )), ) if not source_entity.domain in hass.data[DOMAIN][DATA_DOMAIN_ENTITIES]: hass.data[DOMAIN][DATA_DOMAIN_ENTITIES][source_entity.domain] = [] hass.data[DOMAIN][DATA_DOMAIN_ENTITIES][source_entity.domain].extend( entities_to_add) return entities_to_add
def _notify(self, message): _LOGGER.debug("Send notify for container {}".format(self._name)) for callback in self._subscribers: callback(message)
def update_callbacks() -> None: if callback is not None: callback(self.hass, data.coordinator)