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
Beispiel #6
0
    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))
Beispiel #7
0
    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)
Beispiel #8
0
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"
Beispiel #9
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()
Beispiel #10
0
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
Beispiel #11
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
            )
        )
Beispiel #12
0
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
Beispiel #14
0
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
Beispiel #15
0
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
Beispiel #16
0
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
Beispiel #17
0
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
Beispiel #18
0
    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()
Beispiel #20
0
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
Beispiel #23
0
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
Beispiel #24
0
 def _notify(self, message):
     _LOGGER.debug("{} send notify".format(self._name))
     for callback in self._subscribers:
         callback(message)
Beispiel #25
0
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()
Beispiel #28
0
    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()
Beispiel #29
0
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
Beispiel #30
0
 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)