Пример #1
0
async def test_sensor_with_forecast(hass):
    """Test states of the sensor with forecast."""
    await init_integration(hass, forecast=True)
    registry = er.async_get(hass)

    state = hass.states.get("sensor.home_hours_of_sun_0d")
    assert state
    assert state.state == "7.2"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-partly-cloudy"
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TIME_HOURS

    entry = registry.async_get("sensor.home_hours_of_sun_0d")
    assert entry
    assert entry.unique_id == "0123456-hoursofsun-0"

    state = hass.states.get("sensor.home_realfeel_temperature_max_0d")
    assert state
    assert state.state == "29.8"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TEMPERATURE

    entry = registry.async_get("sensor.home_realfeel_temperature_max_0d")
    assert entry

    state = hass.states.get("sensor.home_realfeel_temperature_min_0d")
    assert state
    assert state.state == "15.1"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TEMPERATURE

    entry = registry.async_get("sensor.home_realfeel_temperature_min_0d")
    assert entry
    assert entry.unique_id == "0123456-realfeeltemperaturemin-0"

    state = hass.states.get("sensor.home_thunderstorm_probability_day_0d")
    assert state
    assert state.state == "40"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-lightning"
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE

    entry = registry.async_get("sensor.home_thunderstorm_probability_day_0d")
    assert entry
    assert entry.unique_id == "0123456-thunderstormprobabilityday-0"

    state = hass.states.get("sensor.home_thunderstorm_probability_night_0d")
    assert state
    assert state.state == "40"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-lightning"
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE

    entry = registry.async_get("sensor.home_thunderstorm_probability_night_0d")
    assert entry
    assert entry.unique_id == "0123456-thunderstormprobabilitynight-0"

    state = hass.states.get("sensor.home_uv_index_0d")
    assert state
    assert state.state == "5"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-sunny"
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == UV_INDEX
    assert state.attributes.get("level") == "Moderate"

    entry = registry.async_get("sensor.home_uv_index_0d")
    assert entry
    assert entry.unique_id == "0123456-uvindex-0"
async def test_loading_extra_values(hass, hass_storage):
    """Test we load extra data from the registry."""
    hass_storage[er.STORAGE_KEY] = {
        "version": er.STORAGE_VERSION,
        "data": {
            "entities": [
                {
                    "entity_id": "test.named",
                    "platform": "super_platform",
                    "unique_id": "with-name",
                    "name": "registry override",
                },
                {
                    "entity_id": "test.no_name",
                    "platform": "super_platform",
                    "unique_id": "without-name",
                },
                {
                    "entity_id": "test.disabled_user",
                    "platform": "super_platform",
                    "unique_id": "disabled-user",
                    "disabled_by": er.DISABLED_USER,
                },
                {
                    "entity_id": "test.disabled_hass",
                    "platform": "super_platform",
                    "unique_id": "disabled-hass",
                    "disabled_by": er.DISABLED_HASS,
                },
                {
                    "entity_id": "test.invalid__entity",
                    "platform": "super_platform",
                    "unique_id": "invalid-hass",
                    "disabled_by": er.DISABLED_HASS,
                },
            ]
        },
    }

    await er.async_load(hass)
    registry = er.async_get(hass)

    assert len(registry.entities) == 4

    entry_with_name = registry.async_get_or_create("test", "super_platform",
                                                   "with-name")
    entry_without_name = registry.async_get_or_create("test", "super_platform",
                                                      "without-name")
    assert entry_with_name.name == "registry override"
    assert entry_without_name.name is None
    assert not entry_with_name.disabled

    entry_disabled_hass = registry.async_get_or_create("test",
                                                       "super_platform",
                                                       "disabled-hass")
    entry_disabled_user = registry.async_get_or_create("test",
                                                       "super_platform",
                                                       "disabled-user")
    assert entry_disabled_hass.disabled
    assert entry_disabled_hass.disabled_by == er.DISABLED_HASS
    assert entry_disabled_user.disabled
    assert entry_disabled_user.disabled_by == er.DISABLED_USER
async def test_async_get_device_class_lookup(hass):
    """Test registry device class lookup."""
    hass.state = CoreState.not_running

    ent_reg = er.async_get(hass)

    ent_reg.async_get_or_create(
        "binary_sensor",
        "light",
        "battery_charging",
        device_id="light_device_entry_id",
        device_class="battery_charging",
    )
    ent_reg.async_get_or_create(
        "sensor",
        "light",
        "battery",
        device_id="light_device_entry_id",
        device_class="battery",
    )
    ent_reg.async_get_or_create("light",
                                "light",
                                "demo",
                                device_id="light_device_entry_id")
    ent_reg.async_get_or_create(
        "binary_sensor",
        "vacuum",
        "battery_charging",
        device_id="vacuum_device_entry_id",
        device_class="battery_charging",
    )
    ent_reg.async_get_or_create(
        "sensor",
        "vacuum",
        "battery",
        device_id="vacuum_device_entry_id",
        device_class="battery",
    )
    ent_reg.async_get_or_create("vacuum",
                                "vacuum",
                                "demo",
                                device_id="vacuum_device_entry_id")
    ent_reg.async_get_or_create(
        "binary_sensor",
        "remote",
        "battery_charging",
        device_id="remote_device_entry_id",
        device_class="battery_charging",
    )
    ent_reg.async_get_or_create("remote",
                                "remote",
                                "demo",
                                device_id="remote_device_entry_id")

    device_lookup = ent_reg.async_get_device_class_lookup({
        ("binary_sensor", "battery_charging"), ("sensor", "battery")
    })

    assert device_lookup == {
        "remote_device_entry_id": {
            (
                "binary_sensor",
                "battery_charging",
            ):
            "binary_sensor.remote_battery_charging"
        },
        "light_device_entry_id": {
            (
                "binary_sensor",
                "battery_charging",
            ): "binary_sensor.light_battery_charging",
            ("sensor", "battery"): "sensor.light_battery",
        },
        "vacuum_device_entry_id": {
            (
                "binary_sensor",
                "battery_charging",
            ): "binary_sensor.vacuum_battery_charging",
            ("sensor", "battery"): "sensor.vacuum_battery",
        },
    }
Пример #4
0
async def test_sensors(
    hass,
    connect,
    mock_devices,
    mock_available_temps,
    create_device_registry_devices,
):
    """Test creating an AsusWRT sensor."""
    entity_reg = er.async_get(hass)

    # init config entry
    config_entry = MockConfigEntry(
        domain=DOMAIN,
        data=CONFIG_DATA,
        options={CONF_CONSIDER_HOME: 60},
    )

    # init variable
    unique_id = DOMAIN
    obj_prefix = slugify(DEFAULT_PREFIX)
    sensor_prefix = f"{sensor.DOMAIN}.{obj_prefix}"

    # Pre-enable the status sensor
    for sensor_name in SENSOR_NAMES:
        sensor_id = slugify(sensor_name)
        entity_reg.async_get_or_create(
            sensor.DOMAIN,
            DOMAIN,
            f"{unique_id} {DEFAULT_PREFIX} {sensor_name}",
            suggested_object_id=f"{obj_prefix}_{sensor_id}",
            disabled_by=None,
        )

    config_entry.add_to_hass(hass)

    # initial devices setup
    assert await hass.config_entries.async_setup(config_entry.entry_id)
    await hass.async_block_till_done()
    async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
    await hass.async_block_till_done()

    assert hass.states.get(f"{device_tracker.DOMAIN}.test").state == STATE_HOME
    assert hass.states.get(
        f"{device_tracker.DOMAIN}.testtwo").state == STATE_HOME
    assert hass.states.get(f"{sensor_prefix}_download_speed").state == "160.0"
    assert hass.states.get(f"{sensor_prefix}_download").state == "60.0"
    assert hass.states.get(f"{sensor_prefix}_upload_speed").state == "80.0"
    assert hass.states.get(f"{sensor_prefix}_upload").state == "50.0"
    assert hass.states.get(f"{sensor_prefix}_load_avg_1m").state == "1.1"
    assert hass.states.get(f"{sensor_prefix}_load_avg_5m").state == "1.2"
    assert hass.states.get(f"{sensor_prefix}_load_avg_15m").state == "1.3"
    assert hass.states.get(f"{sensor_prefix}_devices_connected").state == "2"

    # assert temperature availability exception is handled correctly
    assert not hass.states.get(f"{sensor_prefix}_2_4ghz_temperature")
    assert not hass.states.get(f"{sensor_prefix}_5ghz_temperature")
    assert not hass.states.get(f"{sensor_prefix}_cpu_temperature")

    # add one device and remove another
    mock_devices.pop(MOCK_MAC_1)
    mock_devices[MOCK_MAC_3] = Device(MOCK_MAC_3, "192.168.1.4", "TestThree")

    async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
    await hass.async_block_till_done()

    # consider home option set, all devices still home
    assert hass.states.get(f"{device_tracker.DOMAIN}.test").state == STATE_HOME
    assert hass.states.get(
        f"{device_tracker.DOMAIN}.testtwo").state == STATE_HOME
    assert hass.states.get(
        f"{device_tracker.DOMAIN}.testthree").state == STATE_HOME
    assert hass.states.get(f"{sensor_prefix}_devices_connected").state == "2"

    hass.config_entries.async_update_entry(config_entry,
                                           options={CONF_CONSIDER_HOME: 0})
    await hass.async_block_till_done()
    async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
    await hass.async_block_till_done()

    # consider home option not set, device "test" not home
    assert hass.states.get(
        f"{device_tracker.DOMAIN}.test").state == STATE_NOT_HOME

    # checking temperature sensors without exceptions
    mock_available_temps.append(True)
    await hass.config_entries.async_reload(config_entry.entry_id)
    await hass.async_block_till_done()
    async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
    await hass.async_block_till_done()

    assert hass.states.get(f"{sensor_prefix}_load_avg_15m").state == "1.3"
    assert hass.states.get(
        f"{sensor_prefix}_2_4ghz_temperature").state == "40.0"
    assert not hass.states.get(f"{sensor_prefix}_5ghz_temperature")
    assert hass.states.get(f"{sensor_prefix}_cpu_temperature").state == "71.2"
Пример #5
0
async def async_create_miio_device_and_coordinator(
        hass: core.HomeAssistant, entry: config_entries.ConfigEntry):
    """Set up a data coordinator and one miio device to service multiple entities."""
    model: str = entry.data[CONF_MODEL]
    host = entry.data[CONF_HOST]
    token = entry.data[CONF_TOKEN]
    name = entry.title
    device = None
    migrate = False
    update_method = _async_update_data_default
    coordinator_class = DataUpdateCoordinator

    if (model not in MODELS_HUMIDIFIER and model not in MODELS_FAN
            and model not in MODELS_VACUUM
            and not model.startswith(ROBOROCK_GENERIC)
            and not model.startswith(ROCKROBO_GENERIC)):
        return

    _LOGGER.debug("Initializing with host %s (token %s...)", host, token[:5])

    # Humidifiers
    if model in MODELS_HUMIDIFIER_MIOT:
        device = AirHumidifierMiot(host, token)
        migrate = True
    elif model in MODELS_HUMIDIFIER_MJJSQ:
        device = AirHumidifierMjjsq(host, token, model=model)
        migrate = True
    elif model in MODELS_HUMIDIFIER_MIIO:
        device = AirHumidifier(host, token, model=model)
        migrate = True
    # Airpurifiers and Airfresh
    elif model in MODEL_AIRPURIFIER_3C:
        device = AirPurifierMB4(host, token)
    elif model in MODELS_PURIFIER_MIOT:
        device = AirPurifierMiot(host, token)
    elif model.startswith("zhimi.airpurifier."):
        device = AirPurifier(host, token)
    elif model.startswith("zhimi.airfresh."):
        device = AirFresh(host, token)
    elif (model in MODELS_VACUUM or model.startswith(ROBOROCK_GENERIC)
          or model.startswith(ROCKROBO_GENERIC)):
        device = RoborockVacuum(host, token)
        update_method = _async_update_data_vacuum
        coordinator_class = DataUpdateCoordinator[VacuumCoordinatorData]
    # Pedestal fans
    elif model in MODEL_TO_CLASS_MAP:
        device = MODEL_TO_CLASS_MAP[model](host, token)
    elif model in MODELS_FAN_MIIO:
        device = Fan(host, token, model=model)
    else:
        _LOGGER.error(
            "Unsupported device found! Please create an issue at "
            "https://github.com/syssi/xiaomi_airpurifier/issues "
            "and provide the following data: %s",
            model,
        )
        return

    if migrate:
        # Removing fan platform entity for humidifiers and migrate the name to the config entry for migration
        entity_registry = er.async_get(hass)
        entity_id = entity_registry.async_get_entity_id(
            "fan", DOMAIN, entry.unique_id)
        if entity_id:
            # This check is entities that have a platform migration only and should be removed in the future
            if migrate_entity_name := entity_registry.async_get(
                    entity_id).name:
                hass.config_entries.async_update_entry(
                    entry, title=migrate_entity_name)
            entity_registry.async_remove(entity_id)
Пример #6
0
    if not color_modes:
        return False
    return ColorMode.COLOR_TEMP in color_modes


def get_supported_color_modes(hass: HomeAssistant,
                              entity_id: str) -> set | None:
    """Get supported color modes for a light entity.

    First try the statemachine, then entity registry.
    This is the equivalent of entity helper get_supported_features.
    """
    if state := hass.states.get(entity_id):
        return state.attributes.get(ATTR_SUPPORTED_COLOR_MODES)

    entity_registry = er.async_get(hass)
    if not (entry := entity_registry.async_get(entity_id)):
        raise HomeAssistantError(f"Unknown entity {entity_id}")
    if not entry.capabilities:
        return None

    return entry.capabilities.get(ATTR_SUPPORTED_COLOR_MODES)


# Float that represents transition time in seconds to make change.
ATTR_TRANSITION = "transition"

# Lists holding color values
ATTR_RGB_COLOR = "rgb_color"
ATTR_RGBW_COLOR = "rgbw_color"
ATTR_RGBWW_COLOR = "rgbww_color"
Пример #7
0
async def test_switches(hass, aioclient_mock):
    """Test the update_items function with some clients."""
    config_entry = await setup_unifi_integration(
        hass,
        aioclient_mock,
        options={
            CONF_BLOCK_CLIENT: [BLOCKED["mac"], UNBLOCKED["mac"]],
            CONF_TRACK_CLIENTS: False,
            CONF_TRACK_DEVICES: False,
        },
        clients_response=[CLIENT_1, CLIENT_4],
        devices_response=[DEVICE_1],
        clients_all_response=[BLOCKED, UNBLOCKED, CLIENT_1],
        dpigroup_response=DPI_GROUPS,
        dpiapp_response=DPI_APPS,
    )
    controller = hass.data[UNIFI_DOMAIN][config_entry.entry_id]

    assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 4

    switch_1 = hass.states.get("switch.poe_client_1")
    assert switch_1 is not None
    assert switch_1.state == "on"
    assert switch_1.attributes["power"] == "2.56"
    assert switch_1.attributes[SWITCH_DOMAIN] == "00:00:00:00:01:01"
    assert switch_1.attributes["port"] == 1
    assert switch_1.attributes["poe_mode"] == "auto"

    switch_4 = hass.states.get("switch.poe_client_4")
    assert switch_4 is None

    blocked = hass.states.get("switch.block_client_1")
    assert blocked is not None
    assert blocked.state == "off"

    unblocked = hass.states.get("switch.block_client_2")
    assert unblocked is not None
    assert unblocked.state == "on"

    dpi_switch = hass.states.get("switch.block_media_streaming")
    assert dpi_switch is not None
    assert dpi_switch.state == "on"
    assert dpi_switch.attributes["icon"] == "mdi:network"

    ent_reg = er.async_get(hass)
    for entry_id in (
        "switch.poe_client_1",
        "switch.block_client_1",
        "switch.block_media_streaming",
    ):
        assert ent_reg.async_get(entry_id).entity_category is EntityCategory.CONFIG

    # Block and unblock client

    aioclient_mock.post(
        f"https://{controller.host}:1234/api/s/{controller.site}/cmd/stamgr",
    )

    await hass.services.async_call(
        SWITCH_DOMAIN, "turn_off", {"entity_id": "switch.block_client_1"}, blocking=True
    )
    assert aioclient_mock.call_count == 11
    assert aioclient_mock.mock_calls[10][2] == {
        "mac": "00:00:00:00:01:01",
        "cmd": "block-sta",
    }

    await hass.services.async_call(
        SWITCH_DOMAIN, "turn_on", {"entity_id": "switch.block_client_1"}, blocking=True
    )
    assert aioclient_mock.call_count == 12
    assert aioclient_mock.mock_calls[11][2] == {
        "mac": "00:00:00:00:01:01",
        "cmd": "unblock-sta",
    }

    # Enable and disable DPI

    aioclient_mock.put(
        f"https://{controller.host}:1234/api/s/{controller.site}/rest/dpiapp/5f976f62e3c58f018ec7e17d",
    )

    await hass.services.async_call(
        SWITCH_DOMAIN,
        "turn_off",
        {"entity_id": "switch.block_media_streaming"},
        blocking=True,
    )
    assert aioclient_mock.call_count == 13
    assert aioclient_mock.mock_calls[12][2] == {"enabled": False}

    await hass.services.async_call(
        SWITCH_DOMAIN,
        "turn_on",
        {"entity_id": "switch.block_media_streaming"},
        blocking=True,
    )
    assert aioclient_mock.call_count == 14
    assert aioclient_mock.mock_calls[13][2] == {"enabled": True}

    # Make sure no duplicates arise on generic signal update
    async_dispatcher_send(hass, controller.signal_update)
    await hass.async_block_till_done()
    assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 4
Пример #8
0
async def async_setup_entry(  # noqa: C901
        hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up Z-Wave JS from a config entry."""
    use_addon = entry.data.get(CONF_USE_ADDON)
    if use_addon:
        await async_ensure_addon_running(hass, entry)

    client = ZwaveClient(entry.data[CONF_URL], async_get_clientsession(hass))
    dev_reg = device_registry.async_get(hass)
    ent_reg = entity_registry.async_get(hass)
    entry_hass_data: dict = hass.data[DOMAIN].setdefault(entry.entry_id, {})

    entry_hass_data[DATA_CLIENT] = client
    entry_hass_data[DATA_PLATFORM_SETUP] = {}

    registered_unique_ids: dict[str, dict[str, set[str]]] = defaultdict(dict)
    discovered_value_ids: dict[str, set[str]] = defaultdict(set)

    async def async_handle_discovery_info(
        device: device_registry.DeviceEntry,
        disc_info: ZwaveDiscoveryInfo,
        value_updates_disc_info: dict[str, ZwaveDiscoveryInfo],
    ) -> None:
        """Handle discovery info and all dependent tasks."""
        # This migration logic was added in 2021.3 to handle a breaking change to
        # the value_id format. Some time in the future, this call (as well as the
        # helper functions) can be removed.
        async_migrate_discovered_value(
            hass,
            ent_reg,
            registered_unique_ids[device.id][disc_info.platform],
            device,
            client,
            disc_info,
        )

        platform_setup_tasks = entry_hass_data[DATA_PLATFORM_SETUP]
        platform = disc_info.platform
        if platform not in platform_setup_tasks:
            platform_setup_tasks[platform] = hass.async_create_task(
                hass.config_entries.async_forward_entry_setup(entry, platform))
        await platform_setup_tasks[platform]

        LOGGER.debug("Discovered entity: %s", disc_info)
        async_dispatcher_send(hass,
                              f"{DOMAIN}_{entry.entry_id}_add_{platform}",
                              disc_info)

        # If we don't need to watch for updates return early
        if not disc_info.assumed_state:
            return
        value_updates_disc_info[disc_info.primary_value.value_id] = disc_info
        # If this is the first time we found a value we want to watch for updates,
        # return early
        if len(value_updates_disc_info) != 1:
            return
        # add listener for value updated events
        entry.async_on_unload(
            disc_info.node.on(
                "value updated",
                lambda event: async_on_value_updated_fire_event(
                    value_updates_disc_info, event["value"]),
            ))

    async def async_on_node_ready(node: ZwaveNode) -> None:
        """Handle node ready event."""
        LOGGER.debug("Processing node %s", node)
        # register (or update) node in device registry
        device = register_node_in_dev_reg(hass, entry, dev_reg, client, node)
        # We only want to create the defaultdict once, even on reinterviews
        if device.id not in registered_unique_ids:
            registered_unique_ids[device.id] = defaultdict(set)

        value_updates_disc_info: dict[str, ZwaveDiscoveryInfo] = {}

        # run discovery on all node values and create/update entities
        await asyncio.gather(*(async_handle_discovery_info(
            device, disc_info, value_updates_disc_info)
                               for disc_info in async_discover_node_values(
                                   node, device, discovered_value_ids)))

        # add listeners to handle new values that get added later
        for event in ("value added", "value updated", "metadata updated"):
            entry.async_on_unload(
                node.on(
                    event,
                    lambda event: hass.async_create_task(
                        async_on_value_added(value_updates_disc_info, event[
                            "value"])),
                ))

        # add listener for stateless node value notification events
        entry.async_on_unload(
            node.on(
                "value notification",
                lambda event: async_on_value_notification(event[
                    "value_notification"]),
            ))
        # add listener for stateless node notification events
        entry.async_on_unload(
            node.on(
                "notification",
                lambda event: async_on_notification(event["notification"]),
            ))

    async def async_on_node_added(node: ZwaveNode) -> None:
        """Handle node added event."""
        platform_setup_tasks = entry_hass_data[DATA_PLATFORM_SETUP]

        # We need to set up the sensor platform if it hasn't already been setup in
        # order to create the node status sensor
        if SENSOR_DOMAIN not in platform_setup_tasks:
            platform_setup_tasks[SENSOR_DOMAIN] = hass.async_create_task(
                hass.config_entries.async_forward_entry_setup(
                    entry, SENSOR_DOMAIN))

        # This guard ensures that concurrent runs of this function all await the
        # platform setup task
        if not platform_setup_tasks[SENSOR_DOMAIN].done():
            await platform_setup_tasks[SENSOR_DOMAIN]

        # Create a node status sensor for each device
        async_dispatcher_send(
            hass, f"{DOMAIN}_{entry.entry_id}_add_node_status_sensor", node)

        # we only want to run discovery when the node has reached ready state,
        # otherwise we'll have all kinds of missing info issues.
        if node.ready:
            await async_on_node_ready(node)
            return
        # if node is not yet ready, register one-time callback for ready state
        LOGGER.debug("Node added: %s - waiting for it to become ready",
                     node.node_id)
        node.once(
            "ready",
            lambda event: hass.async_create_task(
                async_on_node_ready(event["node"])),
        )
        # we do submit the node to device registry so user has
        # some visual feedback that something is (in the process of) being added
        register_node_in_dev_reg(hass, entry, dev_reg, client, node)

    async def async_on_value_added(value_updates_disc_info: dict[
        str, ZwaveDiscoveryInfo], value: Value) -> None:
        """Fire value updated event."""
        # If node isn't ready or a device for this node doesn't already exist, we can
        # let the node ready event handler perform discovery. If a value has already
        # been processed, we don't need to do it again
        device_id = get_device_id(client, value.node)
        if (not value.node.ready
                or not (device := dev_reg.async_get_device({device_id}))
                or value.value_id in discovered_value_ids[device.id]):
            return

        LOGGER.debug("Processing node %s added value %s", value.node, value)
        await asyncio.gather(*(async_handle_discovery_info(
            device, disc_info, value_updates_disc_info)
                               for disc_info in async_discover_single_value(
                                   value, device, discovered_value_ids)))
Пример #9
0
async def test_roku_binary_sensors(hass: HomeAssistant,
                                   init_integration: MockConfigEntry) -> None:
    """Test the Roku binary sensors."""
    entity_registry = er.async_get(hass)
    device_registry = dr.async_get(hass)

    state = hass.states.get("binary_sensor.my_roku_3_headphones_connected")
    entry = entity_registry.async_get(
        "binary_sensor.my_roku_3_headphones_connected")
    assert entry
    assert state
    assert entry.unique_id == f"{UPNP_SERIAL}_headphones_connected"
    assert entry.entity_category is None
    assert state.state == STATE_OFF
    assert state.attributes.get(
        ATTR_FRIENDLY_NAME) == "My Roku 3 Headphones Connected"
    assert state.attributes.get(ATTR_ICON) == "mdi:headphones"
    assert ATTR_DEVICE_CLASS not in state.attributes

    state = hass.states.get("binary_sensor.my_roku_3_supports_airplay")
    entry = entity_registry.async_get(
        "binary_sensor.my_roku_3_supports_airplay")
    assert entry
    assert state
    assert entry.unique_id == f"{UPNP_SERIAL}_supports_airplay"
    assert entry.entity_category == EntityCategory.DIAGNOSTIC
    assert state.state == STATE_OFF
    assert state.attributes.get(
        ATTR_FRIENDLY_NAME) == "My Roku 3 Supports AirPlay"
    assert state.attributes.get(ATTR_ICON) == "mdi:cast-variant"
    assert ATTR_DEVICE_CLASS not in state.attributes

    state = hass.states.get("binary_sensor.my_roku_3_supports_ethernet")
    entry = entity_registry.async_get(
        "binary_sensor.my_roku_3_supports_ethernet")
    assert entry
    assert state
    assert entry.unique_id == f"{UPNP_SERIAL}_supports_ethernet"
    assert entry.entity_category == EntityCategory.DIAGNOSTIC
    assert state.state == STATE_ON
    assert state.attributes.get(
        ATTR_FRIENDLY_NAME) == "My Roku 3 Supports Ethernet"
    assert state.attributes.get(ATTR_ICON) == "mdi:ethernet"
    assert ATTR_DEVICE_CLASS not in state.attributes

    state = hass.states.get("binary_sensor.my_roku_3_supports_find_remote")
    entry = entity_registry.async_get(
        "binary_sensor.my_roku_3_supports_find_remote")
    assert entry
    assert state
    assert entry.unique_id == f"{UPNP_SERIAL}_supports_find_remote"
    assert entry.entity_category == EntityCategory.DIAGNOSTIC
    assert state.state == STATE_OFF
    assert state.attributes.get(
        ATTR_FRIENDLY_NAME) == "My Roku 3 Supports Find Remote"
    assert state.attributes.get(ATTR_ICON) == "mdi:remote"
    assert ATTR_DEVICE_CLASS not in state.attributes

    assert entry.device_id
    device_entry = device_registry.async_get(entry.device_id)
    assert device_entry
    assert device_entry.identifiers == {(DOMAIN, UPNP_SERIAL)}
    assert device_entry.connections == {
        (dr.CONNECTION_NETWORK_MAC, "b0:a7:37:96:4d:fb"),
        (dr.CONNECTION_NETWORK_MAC, "b0:a7:37:96:4d:fa"),
    }
    assert device_entry.manufacturer == "Roku"
    assert device_entry.model == "Roku 3"
    assert device_entry.name == "My Roku 3"
    assert device_entry.entry_type is None
    assert device_entry.sw_version == "7.5.0"
    assert device_entry.hw_version == "4200X"
    assert device_entry.suggested_area is None
Пример #10
0
async def async_remove_orphaned_entries_service(gateway: DeconzGateway) -> None:
    """Remove orphaned deCONZ entries from device and entity registries."""
    device_registry = dr.async_get(gateway.hass)
    entity_registry = er.async_get(gateway.hass)

    entity_entries = async_entries_for_config_entry(
        entity_registry, gateway.config_entry.entry_id
    )

    entities_to_be_removed = []
    devices_to_be_removed = [
        entry.id
        for entry in device_registry.devices.values()
        if gateway.config_entry.entry_id in entry.config_entries
    ]

    # Don't remove the Gateway host entry
    if gateway.api.config.mac:
        gateway_host = device_registry.async_get_device(
            connections={(CONNECTION_NETWORK_MAC, gateway.api.config.mac)},
            identifiers=set(),
        )
        if gateway_host and gateway_host.id in devices_to_be_removed:
            devices_to_be_removed.remove(gateway_host.id)

    # Don't remove the Gateway service entry
    gateway_service = device_registry.async_get_device(
        identifiers={(DOMAIN, gateway.api.config.bridge_id)}, connections=set()
    )
    if gateway_service and gateway_service.id in devices_to_be_removed:
        devices_to_be_removed.remove(gateway_service.id)

    # Don't remove devices belonging to available events
    for event in gateway.events:
        if event.device_id in devices_to_be_removed:
            devices_to_be_removed.remove(event.device_id)

    for entry in entity_entries:

        # Don't remove available entities
        if entry.unique_id in gateway.entities[entry.domain]:

            # Don't remove devices with available entities
            if entry.device_id in devices_to_be_removed:
                devices_to_be_removed.remove(entry.device_id)
            continue
        # Remove entities that are not available
        entities_to_be_removed.append(entry.entity_id)

    # Remove unavailable entities
    for entity_id in entities_to_be_removed:
        entity_registry.async_remove(entity_id)

    # Remove devices that don't belong to any entity
    for device_id in devices_to_be_removed:
        if (
            len(
                async_entries_for_device(
                    entity_registry, device_id, include_disabled_entities=True
                )
            )
            == 0
        ):
            device_registry.async_remove_device(device_id)
Пример #11
0
async def test_shabbat_times_sensor(
    hass,
    legacy_patchable_time,
    language,
    now,
    candle_lighting,
    havdalah,
    diaspora,
    tzname,
    latitude,
    longitude,
    result,
):
    """Test sensor output for upcoming shabbat/yomtov times."""
    time_zone = dt_util.get_time_zone(tzname)
    test_time = now.replace(tzinfo=time_zone)

    hass.config.time_zone = tzname
    hass.config.latitude = latitude
    hass.config.longitude = longitude

    registry = er.async_get(hass)

    with alter_time(test_time):
        assert await async_setup_component(
            hass,
            jewish_calendar.DOMAIN,
            {
                "jewish_calendar": {
                    "name": "test",
                    "language": language,
                    "diaspora": diaspora,
                    "candle_lighting_minutes_before_sunset": candle_lighting,
                    "havdalah_minutes_after_sunset": havdalah,
                }
            },
        )
        await hass.async_block_till_done()

        future = dt_util.utcnow() + timedelta(seconds=30)
        async_fire_time_changed(hass, future)
        await hass.async_block_till_done()

    for sensor_type, result_value in result.items():
        if not sensor_type.startswith(language):
            continue

        sensor_type = sensor_type.replace(f"{language}_", "")

        result_value = (dt_util.as_utc(result_value).isoformat() if isinstance(
            result_value, dt) else result_value)

        assert hass.states.get(f"sensor.test_{sensor_type}").state == str(
            result_value), f"Value for {sensor_type}"

        entity = registry.async_get(f"sensor.test_{sensor_type}")
        target_sensor_type = sensor_type.replace("parshat_hashavua",
                                                 "weekly_portion")
        target_uid = "_".join(
            map(
                str,
                [
                    latitude,
                    longitude,
                    tzname,
                    HDATE_DEFAULT_ALTITUDE,
                    diaspora,
                    language,
                    candle_lighting,
                    havdalah,
                    target_sensor_type,
                ],
            ))
        assert entity.unique_id == target_uid
Пример #12
0
async def test_issur_melacha_sensor(
    hass,
    legacy_patchable_time,
    now,
    candle_lighting,
    havdalah,
    diaspora,
    tzname,
    latitude,
    longitude,
    result,
):
    """Test Issur Melacha sensor output."""
    time_zone = dt_util.get_time_zone(tzname)
    test_time = time_zone.localize(now)

    hass.config.time_zone = time_zone
    hass.config.latitude = latitude
    hass.config.longitude = longitude

    registry = er.async_get(hass)

    with alter_time(test_time):
        assert await async_setup_component(
            hass,
            jewish_calendar.DOMAIN,
            {
                "jewish_calendar": {
                    "name": "test",
                    "language": "english",
                    "diaspora": diaspora,
                    "candle_lighting_minutes_before_sunset": candle_lighting,
                    "havdalah_minutes_after_sunset": havdalah,
                }
            },
        )
        await hass.async_block_till_done()

        future = dt_util.utcnow() + timedelta(seconds=30)
        async_fire_time_changed(hass, future)
        await hass.async_block_till_done()

        assert (hass.states.get(
            "binary_sensor.test_issur_melacha_in_effect").state == result)
        entity = registry.async_get(
            "binary_sensor.test_issur_melacha_in_effect")
        target_uid = "_".join(
            map(
                str,
                [
                    latitude,
                    longitude,
                    time_zone,
                    HDATE_DEFAULT_ALTITUDE,
                    diaspora,
                    "english",
                    candle_lighting,
                    havdalah,
                    "issur_melacha_in_effect",
                ],
            ))
        assert entity.unique_id == target_uid
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up this integration using UI."""

    client = FrigateApiClient(entry.data.get(CONF_URL),
                              async_get_clientsession(hass))
    coordinator = FrigateDataUpdateCoordinator(hass, client=client)
    await coordinator.async_config_entry_first_refresh()

    try:
        server_version = await client.async_get_version()
        config = await client.async_get_config()
    except FrigateApiClientError as exc:
        raise ConfigEntryNotReady from exc

    if AwesomeVersion(server_version) <= AwesomeVersion(
            FRIGATE_VERSION_ERROR_CUTOFF):
        _LOGGER.error(
            "Using a Frigate server (%s) with version %s <= %s which is not "
            "compatible -- you must upgrade: %s",
            entry.data[CONF_URL],
            server_version,
            FRIGATE_VERSION_ERROR_CUTOFF,
            FRIGATE_RELEASES_URL,
        )
        return False

    model = f"{(await async_get_integration(hass, DOMAIN)).version}/{server_version}"

    hass.data[DOMAIN][entry.entry_id] = {
        ATTR_COORDINATOR: coordinator,
        ATTR_CLIENT: client,
        ATTR_CONFIG: config,
        ATTR_MODEL: model,
    }

    # Remove old devices associated with cameras that have since been removed
    # from the Frigate server, keeping the 'master' device for this config
    # entry.
    current_devices: set[tuple[str, str]] = set(
        {get_frigate_device_identifier(entry)})
    for item in get_cameras_and_zones(config):
        current_devices.add(get_frigate_device_identifier(entry, item))

    device_registry = dr.async_get(hass)
    for device_entry in dr.async_entries_for_config_entry(
            device_registry, entry.entry_id):
        for identifier in device_entry.identifiers:
            if identifier in current_devices:
                break
        else:
            device_registry.async_remove_device(device_entry.id)

    # Cleanup old clips switch (<v0.9.0) if it exists.
    entity_registry = er.async_get(hass)
    for camera in config["cameras"].keys():
        unique_id = get_frigate_entity_unique_id(entry.entry_id, SWITCH_DOMAIN,
                                                 f"{camera}_clips")
        entity_id = entity_registry.async_get_entity_id(
            SWITCH_DOMAIN, DOMAIN, unique_id)
        if entity_id:
            entity_registry.async_remove(entity_id)

    # Remove old `camera_image_height` option.
    if CONF_CAMERA_STATIC_IMAGE_HEIGHT in entry.options:
        new_options = entry.options.copy()
        new_options.pop(CONF_CAMERA_STATIC_IMAGE_HEIGHT)
        hass.config_entries.async_update_entry(entry, options=new_options)

    hass.config_entries.async_setup_platforms(entry, PLATFORMS)
    entry.async_on_unload(entry.add_update_listener(_async_entry_updated))

    return True
Пример #14
0
async def test_default_setup(hass, dsmr_connection_fixture):
    """Test the default setup."""
    (connection_factory, transport, protocol) = dsmr_connection_fixture

    from dsmr_parser.obis_references import (
        CURRENT_ELECTRICITY_USAGE,
        ELECTRICITY_ACTIVE_TARIFF,
        GAS_METER_READING,
    )
    from dsmr_parser.objects import CosemObject, MBusObject

    entry_data = {
        "port": "/dev/ttyUSB0",
        "dsmr_version": "2.2",
        "precision": 4,
        "reconnect_interval": 30,
        "serial_id": "1234",
        "serial_id_gas": "5678",
    }
    entry_options = {
        "time_between_update": 0,
    }

    telegram = {
        CURRENT_ELECTRICITY_USAGE: CosemObject(
            [{"value": Decimal("0.0"), "unit": ENERGY_KILO_WATT_HOUR}]
        ),
        ELECTRICITY_ACTIVE_TARIFF: CosemObject([{"value": "0001", "unit": ""}]),
        GAS_METER_READING: MBusObject(
            [
                {"value": datetime.datetime.fromtimestamp(1551642213)},
                {"value": Decimal(745.695), "unit": "m3"},
            ]
        ),
    }

    mock_entry = MockConfigEntry(
        domain="dsmr", unique_id="/dev/ttyUSB0", data=entry_data, options=entry_options
    )

    mock_entry.add_to_hass(hass)

    await hass.config_entries.async_setup(mock_entry.entry_id)
    await hass.async_block_till_done()

    registry = er.async_get(hass)

    entry = registry.async_get("sensor.power_consumption")
    assert entry
    assert entry.unique_id == "1234_Power_Consumption"

    entry = registry.async_get("sensor.gas_consumption")
    assert entry
    assert entry.unique_id == "5678_Gas_Consumption"

    telegram_callback = connection_factory.call_args_list[0][0][2]

    # make sure entities have been created and return 'unknown' state
    power_consumption = hass.states.get("sensor.power_consumption")
    assert power_consumption.state == STATE_UNKNOWN
    assert (
        power_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.POWER
    )
    assert power_consumption.attributes.get(ATTR_ICON) is None
    assert (
        power_consumption.attributes.get(ATTR_STATE_CLASS)
        == SensorStateClass.MEASUREMENT
    )
    assert power_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None

    # simulate a telegram pushed from the smartmeter and parsed by dsmr_parser
    telegram_callback(telegram)

    # after receiving telegram entities need to have the chance to update
    await asyncio.sleep(0)

    # ensure entities have new state value after incoming telegram
    power_consumption = hass.states.get("sensor.power_consumption")
    assert power_consumption.state == "0.0"
    assert (
        power_consumption.attributes.get("unit_of_measurement") == ENERGY_KILO_WATT_HOUR
    )

    # tariff should be translated in human readable and have no unit
    power_tariff = hass.states.get("sensor.power_tariff")
    assert power_tariff.state == "low"
    assert power_tariff.attributes.get(ATTR_DEVICE_CLASS) is None
    assert power_tariff.attributes.get(ATTR_ICON) == "mdi:flash"
    assert power_tariff.attributes.get(ATTR_STATE_CLASS) is None
    assert power_tariff.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ""

    # check if gas consumption is parsed correctly
    gas_consumption = hass.states.get("sensor.gas_consumption")
    assert gas_consumption.state == "745.695"
    assert gas_consumption.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.GAS
    assert (
        gas_consumption.attributes.get(ATTR_STATE_CLASS)
        == SensorStateClass.TOTAL_INCREASING
    )
    assert (
        gas_consumption.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == VOLUME_CUBIC_METERS
    )
async def test_tracked_devices(hass):
    """Test the update_items function with some devices."""
    controller = await setup_unifi_integration(
        hass,
        devices_response=[DEVICE_1, DEVICE_2],
    )
    assert len(hass.states.async_entity_ids(TRACKER_DOMAIN)) == 2

    device_1 = hass.states.get("device_tracker.device_1")
    assert device_1
    assert device_1.state == "home"

    device_2 = hass.states.get("device_tracker.device_2")
    assert device_2
    assert device_2.state == "not_home"

    # State change signalling work
    device_1_copy = copy(DEVICE_1)
    device_1_copy["next_interval"] = 20
    event = {"meta": {"message": MESSAGE_DEVICE}, "data": [device_1_copy]}
    controller.api.message_handler(event)
    device_2_copy = copy(DEVICE_2)
    device_2_copy["next_interval"] = 50
    event = {"meta": {"message": MESSAGE_DEVICE}, "data": [device_2_copy]}
    controller.api.message_handler(event)
    await hass.async_block_till_done()

    device_1 = hass.states.get("device_tracker.device_1")
    assert device_1.state == "home"
    device_2 = hass.states.get("device_tracker.device_2")
    assert device_2.state == "home"

    async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=90))
    await hass.async_block_till_done()

    device_1 = hass.states.get("device_tracker.device_1")
    assert device_1.state == "not_home"
    device_2 = hass.states.get("device_tracker.device_2")
    assert device_2.state == "home"

    # Disabled device is unavailable
    device_1_copy = copy(DEVICE_1)
    device_1_copy["disabled"] = True
    event = {"meta": {"message": MESSAGE_DEVICE}, "data": [device_1_copy]}
    controller.api.message_handler(event)
    await hass.async_block_till_done()

    device_1 = hass.states.get("device_tracker.device_1")
    assert device_1.state == STATE_UNAVAILABLE

    # Update device registry when device is upgraded
    device_2_copy = copy(DEVICE_2)
    device_2_copy["version"] = EVENT_DEVICE_2_UPGRADED["version_to"]
    message = {"meta": {"message": MESSAGE_DEVICE}, "data": [device_2_copy]}
    controller.api.message_handler(message)
    event = {"meta": {"message": MESSAGE_EVENT}, "data": [EVENT_DEVICE_2_UPGRADED]}
    controller.api.message_handler(event)
    await hass.async_block_till_done()

    # Verify device registry has been updated
    entity_registry = await hass.helpers.entity_registry.async_get_registry()
    entry = entity_registry.async_get("device_tracker.device_2")
    device_registry = await hass.helpers.device_registry.async_get_registry()
    device = device_registry.async_get(entry.device_id)
    assert device.sw_version == EVENT_DEVICE_2_UPGRADED["version_to"]
Пример #16
0
async def test_rokutv_binary_sensors(
    hass: HomeAssistant,
    init_integration: MockConfigEntry,
    mock_device: RokuDevice,
    mock_roku: MagicMock,
) -> None:
    """Test the Roku binary sensors."""
    entity_registry = er.async_get(hass)
    device_registry = dr.async_get(hass)

    state = hass.states.get(
        "binary_sensor.58_onn_roku_tv_headphones_connected")
    entry = entity_registry.async_get(
        "binary_sensor.58_onn_roku_tv_headphones_connected")
    assert entry
    assert state
    assert entry.unique_id == "YN00H5555555_headphones_connected"
    assert entry.entity_category is None
    assert state.state == STATE_OFF
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            '58" Onn Roku TV Headphones Connected')
    assert state.attributes.get(ATTR_ICON) == "mdi:headphones"
    assert ATTR_DEVICE_CLASS not in state.attributes

    state = hass.states.get("binary_sensor.58_onn_roku_tv_supports_airplay")
    entry = entity_registry.async_get(
        "binary_sensor.58_onn_roku_tv_supports_airplay")
    assert entry
    assert state
    assert entry.unique_id == "YN00H5555555_supports_airplay"
    assert entry.entity_category == EntityCategory.DIAGNOSTIC
    assert state.state == STATE_ON
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            '58" Onn Roku TV Supports AirPlay')
    assert state.attributes.get(ATTR_ICON) == "mdi:cast-variant"
    assert ATTR_DEVICE_CLASS not in state.attributes

    state = hass.states.get("binary_sensor.58_onn_roku_tv_supports_ethernet")
    entry = entity_registry.async_get(
        "binary_sensor.58_onn_roku_tv_supports_ethernet")
    assert entry
    assert state
    assert entry.unique_id == "YN00H5555555_supports_ethernet"
    assert entry.entity_category == EntityCategory.DIAGNOSTIC
    assert state.state == STATE_ON
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            '58" Onn Roku TV Supports Ethernet')
    assert state.attributes.get(ATTR_ICON) == "mdi:ethernet"
    assert ATTR_DEVICE_CLASS not in state.attributes

    state = hass.states.get(
        "binary_sensor.58_onn_roku_tv_supports_find_remote")
    entry = entity_registry.async_get(
        "binary_sensor.58_onn_roku_tv_supports_find_remote")
    assert entry
    assert state
    assert entry.unique_id == "YN00H5555555_supports_find_remote"
    assert entry.entity_category == EntityCategory.DIAGNOSTIC
    assert state.state == STATE_ON
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            '58" Onn Roku TV Supports Find Remote')
    assert state.attributes.get(ATTR_ICON) == "mdi:remote"
    assert ATTR_DEVICE_CLASS not in state.attributes

    assert entry.device_id
    device_entry = device_registry.async_get(entry.device_id)
    assert device_entry
    assert device_entry.identifiers == {(DOMAIN, "YN00H5555555")}
    assert device_entry.connections == {
        (dr.CONNECTION_NETWORK_MAC, "d8:13:99:f8:b0:c6"),
        (dr.CONNECTION_NETWORK_MAC, "d4:3a:2e:07:fd:cb"),
    }
    assert device_entry.manufacturer == "Onn"
    assert device_entry.model == "100005844"
    assert device_entry.name == '58" Onn Roku TV'
    assert device_entry.entry_type is None
    assert device_entry.sw_version == "9.2.0"
    assert device_entry.hw_version == "7820X"
    assert device_entry.suggested_area == "Living room"
Пример #17
0
async def test_single_shot_status_sensor_state_via_mqtt(
        hass, mqtt_mock, setup_tasmota):
    """Test state update via MQTT."""
    entity_reg = er.async_get(hass)

    # Pre-enable the status sensor
    entity_reg.async_get_or_create(
        Platform.SENSOR,
        "tasmota",
        "00000049A3BC_status_sensor_status_sensor_status_restart_reason",
        suggested_object_id="tasmota_status",
        disabled_by=None,
    )

    config = copy.deepcopy(DEFAULT_CONFIG)
    mac = config["mac"]

    async_fire_mqtt_message(
        hass,
        f"{DEFAULT_PREFIX}/{mac}/config",
        json.dumps(config),
    )
    await hass.async_block_till_done()
    await hass.async_block_till_done()

    state = hass.states.get("sensor.tasmota_status")
    assert state.state == "unavailable"
    assert not state.attributes.get(ATTR_ASSUMED_STATE)

    async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online")
    await hass.async_block_till_done()
    state = hass.states.get("sensor.tasmota_status")
    assert state.state == STATE_UNKNOWN
    assert not state.attributes.get(ATTR_ASSUMED_STATE)

    # Test polled state update
    async_fire_mqtt_message(
        hass,
        "tasmota_49A3BC/stat/STATUS1",
        '{"StatusPRM":{"RestartReason":"Some reason"}}',
    )
    await hass.async_block_till_done()
    state = hass.states.get("sensor.tasmota_status")
    assert state.state == "Some reason"

    # Test polled state update is ignored
    async_fire_mqtt_message(
        hass,
        "tasmota_49A3BC/stat/STATUS1",
        '{"StatusPRM":{"RestartReason":"Another reason"}}',
    )
    await hass.async_block_till_done()
    state = hass.states.get("sensor.tasmota_status")
    assert state.state == "Some reason"

    # Device signals online again
    async_fire_mqtt_message(hass, "tasmota_49A3BC/tele/LWT", "Online")
    await hass.async_block_till_done()
    state = hass.states.get("sensor.tasmota_status")
    assert state.state == "Some reason"

    # Test polled state update
    async_fire_mqtt_message(
        hass,
        "tasmota_49A3BC/stat/STATUS1",
        '{"StatusPRM":{"RestartReason":"Another reason"}}',
    )
    await hass.async_block_till_done()
    state = hass.states.get("sensor.tasmota_status")
    assert state.state == "Another reason"

    # Test polled state update is ignored
    async_fire_mqtt_message(
        hass,
        "tasmota_49A3BC/stat/STATUS1",
        '{"StatusPRM":{"RestartReason":"Third reason"}}',
    )
    await hass.async_block_till_done()
    state = hass.states.get("sensor.tasmota_status")
    assert state.state == "Another reason"
Пример #18
0
async def test_sensors(
    hass: HomeAssistant,
    mock_config_entry: MockConfigEntry,
    mock_wled: MagicMock,
) -> None:
    """Test the creation and values of the WLED sensors."""
    registry = er.async_get(hass)

    # Pre-create registry entries for disabled by default sensors
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "aabbccddeeff_uptime",
        suggested_object_id="wled_rgb_light_uptime",
        disabled_by=None,
    )

    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "aabbccddeeff_free_heap",
        suggested_object_id="wled_rgb_light_free_memory",
        disabled_by=None,
    )

    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "aabbccddeeff_wifi_signal",
        suggested_object_id="wled_rgb_light_wifi_signal",
        disabled_by=None,
    )

    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "aabbccddeeff_wifi_rssi",
        suggested_object_id="wled_rgb_light_wifi_rssi",
        disabled_by=None,
    )

    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "aabbccddeeff_wifi_channel",
        suggested_object_id="wled_rgb_light_wifi_channel",
        disabled_by=None,
    )

    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "aabbccddeeff_wifi_bssid",
        suggested_object_id="wled_rgb_light_wifi_bssid",
        disabled_by=None,
    )

    # Setup
    mock_config_entry.add_to_hass(hass)
    test_time = datetime(2019, 11, 11, 9, 10, 32, tzinfo=dt_util.UTC)
    with patch("homeassistant.components.wled.sensor.utcnow", return_value=test_time):
        await hass.config_entries.async_setup(mock_config_entry.entry_id)
        await hass.async_block_till_done()

    state = hass.states.get("sensor.wled_rgb_light_estimated_current")
    assert state
    assert (
        state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ELECTRIC_CURRENT_MILLIAMPERE
    )
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_CURRENT
    assert state.state == "470"

    entry = registry.async_get("sensor.wled_rgb_light_estimated_current")
    assert entry
    assert entry.unique_id == "aabbccddeeff_estimated_current"
    assert entry.entity_category == ENTITY_CATEGORY_DIAGNOSTIC

    state = hass.states.get("sensor.wled_rgb_light_uptime")
    assert state
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TIMESTAMP
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None
    assert state.state == "2019-11-11T09:10:00+00:00"

    entry = registry.async_get("sensor.wled_rgb_light_uptime")
    assert entry
    assert entry.unique_id == "aabbccddeeff_uptime"
    assert entry.entity_category == ENTITY_CATEGORY_DIAGNOSTIC

    state = hass.states.get("sensor.wled_rgb_light_free_memory")
    assert state
    assert state.attributes.get(ATTR_ICON) == "mdi:memory"
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == DATA_BYTES
    assert state.state == "14600"
    assert entry.entity_category == ENTITY_CATEGORY_DIAGNOSTIC

    entry = registry.async_get("sensor.wled_rgb_light_free_memory")
    assert entry
    assert entry.unique_id == "aabbccddeeff_free_heap"
    assert entry.entity_category == ENTITY_CATEGORY_DIAGNOSTIC

    state = hass.states.get("sensor.wled_rgb_light_wifi_signal")
    assert state
    assert state.attributes.get(ATTR_ICON) == "mdi:wifi"
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
    assert state.state == "76"
    assert entry.entity_category == ENTITY_CATEGORY_DIAGNOSTIC

    entry = registry.async_get("sensor.wled_rgb_light_wifi_signal")
    assert entry
    assert entry.unique_id == "aabbccddeeff_wifi_signal"
    assert entry.entity_category == ENTITY_CATEGORY_DIAGNOSTIC

    state = hass.states.get("sensor.wled_rgb_light_wifi_rssi")
    assert state
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_SIGNAL_STRENGTH
    assert (
        state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
        == SIGNAL_STRENGTH_DECIBELS_MILLIWATT
    )
    assert state.state == "-62"

    entry = registry.async_get("sensor.wled_rgb_light_wifi_rssi")
    assert entry
    assert entry.unique_id == "aabbccddeeff_wifi_rssi"
    assert entry.entity_category == ENTITY_CATEGORY_DIAGNOSTIC

    state = hass.states.get("sensor.wled_rgb_light_wifi_channel")
    assert state
    assert state.attributes.get(ATTR_ICON) == "mdi:wifi"
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None
    assert state.state == "11"

    entry = registry.async_get("sensor.wled_rgb_light_wifi_channel")
    assert entry
    assert entry.unique_id == "aabbccddeeff_wifi_channel"
    assert entry.entity_category == ENTITY_CATEGORY_DIAGNOSTIC

    state = hass.states.get("sensor.wled_rgb_light_wifi_bssid")
    assert state
    assert state.attributes.get(ATTR_ICON) == "mdi:wifi"
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None
    assert state.state == "AA:AA:AA:AA:AA:BB"

    entry = registry.async_get("sensor.wled_rgb_light_wifi_bssid")
    assert entry
    assert entry.unique_id == "aabbccddeeff_wifi_bssid"
    assert entry.entity_category == ENTITY_CATEGORY_DIAGNOSTIC
Пример #19
0
async def test_device_exists(hass, ev_entry):
    """Test subaru lock entity exists."""
    entity_registry = er.async_get(hass)
    entry = entity_registry.async_get(DEVICE_ID)
    assert entry
Пример #20
0
async def test_sensors(
    hass: HomeAssistant,
    init_integration: MockConfigEntry,
) -> None:
    """Test the Forecast.Solar sensors."""
    entry_id = init_integration.entry_id
    entity_registry = er.async_get(hass)
    device_registry = dr.async_get(hass)

    state = hass.states.get("sensor.energy_production_today")
    entry = entity_registry.async_get("sensor.energy_production_today")
    assert entry
    assert state
    assert entry.unique_id == f"{entry_id}_energy_production_today"
    assert state.state == "100.0"
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            "Estimated Energy Production - Today")
    assert state.attributes.get(ATTR_STATE_CLASS) is None
    assert state.attributes.get(
        ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.energy_production_tomorrow")
    entry = entity_registry.async_get("sensor.energy_production_tomorrow")
    assert entry
    assert state
    assert entry.unique_id == f"{entry_id}_energy_production_tomorrow"
    assert state.state == "200.0"
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            "Estimated Energy Production - Tomorrow")
    assert state.attributes.get(ATTR_STATE_CLASS) is None
    assert state.attributes.get(
        ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.power_highest_peak_time_today")
    entry = entity_registry.async_get("sensor.power_highest_peak_time_today")
    assert entry
    assert state
    assert entry.unique_id == f"{entry_id}_power_highest_peak_time_today"
    assert state.state == "2021-06-27T13:00:00+00:00"
    assert state.attributes.get(
        ATTR_FRIENDLY_NAME) == "Highest Power Peak Time - Today"
    assert state.attributes.get(ATTR_STATE_CLASS) is None
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TIMESTAMP
    assert ATTR_UNIT_OF_MEASUREMENT not in state.attributes
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.power_highest_peak_time_tomorrow")
    entry = entity_registry.async_get(
        "sensor.power_highest_peak_time_tomorrow")
    assert entry
    assert state
    assert entry.unique_id == f"{entry_id}_power_highest_peak_time_tomorrow"
    assert state.state == "2021-06-27T14:00:00+00:00"
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            "Highest Power Peak Time - Tomorrow")
    assert state.attributes.get(ATTR_STATE_CLASS) is None
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TIMESTAMP
    assert ATTR_UNIT_OF_MEASUREMENT not in state.attributes
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.power_production_now")
    entry = entity_registry.async_get("sensor.power_production_now")
    assert entry
    assert state
    assert entry.unique_id == f"{entry_id}_power_production_now"
    assert state.state == "300000"
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            "Estimated Power Production - Now")
    assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_MEASUREMENT
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_POWER
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.energy_current_hour")
    entry = entity_registry.async_get("sensor.energy_current_hour")
    assert entry
    assert state
    assert entry.unique_id == f"{entry_id}_energy_current_hour"
    assert state.state == "800.0"
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            "Estimated Energy Production - This Hour")
    assert state.attributes.get(ATTR_STATE_CLASS) is None
    assert state.attributes.get(
        ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.energy_next_hour")
    entry = entity_registry.async_get("sensor.energy_next_hour")
    assert entry
    assert state
    assert entry.unique_id == f"{entry_id}_energy_next_hour"
    assert state.state == "900.0"
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            "Estimated Energy Production - Next Hour")
    assert state.attributes.get(ATTR_STATE_CLASS) is None
    assert state.attributes.get(
        ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_ENERGY
    assert ATTR_ICON not in state.attributes

    assert entry.device_id
    device_entry = device_registry.async_get(entry.device_id)
    assert device_entry
    assert device_entry.identifiers == {(DOMAIN, f"{entry_id}")}
    assert device_entry.manufacturer == "Forecast.Solar"
    assert device_entry.name == "Solar Production Forecast"
    assert device_entry.entry_type == ENTRY_TYPE_SERVICE
    assert not device_entry.model
    assert not device_entry.sw_version
Пример #21
0
async def test_restore_client_succeed(hass, aioclient_mock):
    """Test that RestoreEntity works as expected."""
    POE_DEVICE = {
        "device_id": "12345",
        "ip": "1.0.1.1",
        "mac": "00:00:00:00:01:01",
        "last_seen": 1562600145,
        "model": "US16P150",
        "name": "POE Switch",
        "port_overrides": [
            {
                "poe_mode": "off",
                "port_idx": 1,
                "portconf_id": "5f3edd2aba4cc806a19f2db2",
            }
        ],
        "port_table": [
            {
                "media": "GE",
                "name": "Port 1",
                "op_mode": "switch",
                "poe_caps": 7,
                "poe_class": "Unknown",
                "poe_current": "0.00",
                "poe_enable": False,
                "poe_good": False,
                "poe_mode": "off",
                "poe_power": "0.00",
                "poe_voltage": "0.00",
                "port_idx": 1,
                "port_poe": True,
                "portconf_id": "5f3edd2aba4cc806a19f2db2",
                "up": False,
            },
        ],
        "state": 1,
        "type": "usw",
        "version": "4.0.42.10433",
    }
    POE_CLIENT = {
        "hostname": "poe_client",
        "ip": "1.0.0.1",
        "is_wired": True,
        "last_seen": 1562600145,
        "mac": "00:00:00:00:00:01",
        "name": "POE Client",
        "oui": "Producer",
    }

    fake_state = core.State(
        "switch.poe_client",
        "off",
        {
            "power": "0.00",
            "switch": POE_DEVICE["mac"],
            "port": 1,
            "poe_mode": "auto",
        },
    )

    config_entry = config_entries.ConfigEntry(
        version=1,
        domain=UNIFI_DOMAIN,
        title="Mock Title",
        data=ENTRY_CONFIG,
        source="test",
        options={},
        entry_id=DEFAULT_CONFIG_ENTRY_ID,
    )

    registry = er.async_get(hass)
    registry.async_get_or_create(
        SWITCH_DOMAIN,
        UNIFI_DOMAIN,
        f'{POE_SWITCH}-{POE_CLIENT["mac"]}',
        suggested_object_id=POE_CLIENT["hostname"],
        config_entry=config_entry,
    )

    with patch(
        "homeassistant.helpers.restore_state.RestoreEntity.async_get_last_state",
        return_value=fake_state,
    ):
        await setup_unifi_integration(
            hass,
            aioclient_mock,
            options={
                CONF_TRACK_CLIENTS: False,
                CONF_TRACK_DEVICES: False,
            },
            clients_response=[],
            devices_response=[POE_DEVICE],
            clients_all_response=[POE_CLIENT],
        )

    assert len(hass.states.async_entity_ids(SWITCH_DOMAIN)) == 1

    poe_client = hass.states.get("switch.poe_client")
    assert poe_client.state == "off"
Пример #22
0
    async def _device_connect(self, location: str) -> None:
        """Connect to the device now that it's available."""
        _LOGGER.debug("Connecting to device at %s", location)

        async with self._device_lock:
            if self._device:
                _LOGGER.debug(
                    "Trying to connect when device already connected")
                return

            domain_data = get_domain_data(self.hass)

            # Connect to the base UPNP device
            upnp_device = await domain_data.upnp_factory.async_create_device(
                location)

            # Create/get event handler that is reachable by the device, using
            # the connection's local IP to listen only on the relevant interface
            _, event_ip = await async_get_local_ip(location, self.hass.loop)
            self._event_addr = self._event_addr._replace(host=event_ip)
            event_handler = await domain_data.async_get_event_notifier(
                self._event_addr, self.hass)

            # Create profile wrapper
            self._device = DmrDevice(upnp_device, event_handler)

            self.location = location

            # Subscribe to event notifications
            try:
                self._device.on_event = self._on_event
                await self._device.async_subscribe_services(
                    auto_resubscribe=True)
            except UpnpResponseError as err:
                # Device rejected subscription request. This is OK, variables
                # will be polled instead.
                _LOGGER.debug("Device rejected subscription: %r", err)
            except UpnpError as err:
                # Don't leave the device half-constructed
                self._device.on_event = None
                self._device = None
                await domain_data.async_release_event_notifier(self._event_addr
                                                               )
                _LOGGER.debug(
                    "Error while subscribing during device connect: %r", err)
                raise

        if (not self.registry_entry or not self.registry_entry.config_entry_id
                or self.registry_entry.device_id):
            return

        # Create linked HA DeviceEntry now the information is known.
        dev_reg = device_registry.async_get(self.hass)
        device_entry = dev_reg.async_get_or_create(
            config_entry_id=self.registry_entry.config_entry_id,
            # Connections are based on the root device's UDN, and the DMR
            # embedded device's UDN. They may be the same, if the DMR is the
            # root device.
            connections={
                (
                    device_registry.CONNECTION_UPNP,
                    self._device.profile_device.root_device.udn,
                ),
                (device_registry.CONNECTION_UPNP, self._device.udn),
            },
            identifiers={(DOMAIN, self.unique_id)},
            default_manufacturer=self._device.manufacturer,
            default_model=self._device.model_name,
            default_name=self._device.name,
        )

        # Update entity registry to link to the device
        ent_reg = entity_registry.async_get(self.hass)
        ent_reg.async_get_or_create(
            self.registry_entry.domain,
            self.registry_entry.platform,
            self.unique_id,
            device_id=device_entry.id,
        )
Пример #23
0
async def test_ecobee3_setup(hass):
    """Test that a Ecbobee 3 can be correctly setup in HA."""
    accessories = await setup_accessories_from_file(hass, "ecobee3.json")
    config_entry, pairing = await setup_test_accessories(hass, accessories)

    entity_registry = er.async_get(hass)

    climate = entity_registry.async_get("climate.homew")
    assert climate.unique_id == "homekit-123456789012-16"

    climate_helper = Helper(hass, "climate.homew", pairing, accessories[0],
                            config_entry)
    climate_state = await climate_helper.poll_and_get_state()
    assert climate_state.attributes["friendly_name"] == "HomeW"
    assert climate_state.attributes["supported_features"] == (
        SUPPORT_TARGET_TEMPERATURE
        | SUPPORT_TARGET_TEMPERATURE_RANGE
        | SUPPORT_TARGET_HUMIDITY)

    assert climate_state.attributes["hvac_modes"] == [
        "off",
        "heat",
        "cool",
        "heat_cool",
    ]

    assert climate_state.attributes["min_temp"] == 7.2
    assert climate_state.attributes["max_temp"] == 33.3
    assert climate_state.attributes["min_humidity"] == 20
    assert climate_state.attributes["max_humidity"] == 50

    climate_sensor = entity_registry.async_get(
        "sensor.homew_current_temperature")
    assert climate_sensor.unique_id == "homekit-123456789012-aid:1-sid:16-cid:19"

    occ1 = entity_registry.async_get("binary_sensor.kitchen")
    assert occ1.unique_id == "homekit-AB1C-56"

    occ1_helper = Helper(hass, "binary_sensor.kitchen", pairing,
                         accessories[0], config_entry)
    occ1_state = await occ1_helper.poll_and_get_state()
    assert occ1_state.attributes["friendly_name"] == "Kitchen"

    occ2 = entity_registry.async_get("binary_sensor.porch")
    assert occ2.unique_id == "homekit-AB2C-56"

    occ3 = entity_registry.async_get("binary_sensor.basement")
    assert occ3.unique_id == "homekit-AB3C-56"

    device_registry = dr.async_get(hass)

    climate_device = device_registry.async_get(climate.device_id)
    assert climate_device.manufacturer == "ecobee Inc."
    assert climate_device.name == "HomeW"
    assert climate_device.model == "ecobee3"
    assert climate_device.sw_version == "4.2.394"
    assert climate_device.via_device_id is None

    # Check that an attached sensor has its own device entity that
    # is linked to the bridge
    sensor_device = device_registry.async_get(occ1.device_id)
    assert sensor_device.manufacturer == "ecobee Inc."
    assert sensor_device.name == "Kitchen"
    assert sensor_device.model == "REMOTE SENSOR"
    assert sensor_device.sw_version == "1.0.0"
    assert sensor_device.via_device_id == climate_device.id
Пример #24
0
class ScannerEntity(BaseTrackerEntity):
    """Base class for a tracked device that is on a scanned network."""
    @property
    def ip_address(self) -> str | None:
        """Return the primary ip address of the device."""
        return None

    @property
    def mac_address(self) -> str | None:
        """Return the mac address of the device."""
        return None

    @property
    def hostname(self) -> str | None:
        """Return hostname of the device."""
        return None

    @property
    def state(self) -> str:
        """Return the state of the device."""
        if self.is_connected:
            return STATE_HOME
        return STATE_NOT_HOME

    @property
    def is_connected(self) -> bool:
        """Return true if the device is connected to the network."""
        raise NotImplementedError

    @property
    def unique_id(self) -> str | None:
        """Return unique ID of the entity."""
        return self.mac_address

    @final
    @property
    def device_info(self) -> DeviceInfo | None:
        """Device tracker entities should not create device registry entries."""
        return None

    @property
    def entity_registry_enabled_default(self) -> bool:
        """Return if entity is enabled by default."""
        # If mac_address is None, we can never find a device entry.
        return (
            # Do not disable if we won't activate our attach to device logic
            self.mac_address is None or self.device_info is not None
            # Disable if we automatically attach but there is no device
            or self.find_device_entry() is not None)

    @callback
    def add_to_platform_start(
        self,
        hass: HomeAssistant,
        platform: EntityPlatform,
        parallel_updates: asyncio.Semaphore | None,
    ) -> None:
        """Start adding an entity to a platform."""
        super().add_to_platform_start(hass, platform, parallel_updates)
        if self.mac_address and self.unique_id:
            _async_register_mac(
                hass,
                platform.platform_name,
                self.mac_address,
                self.unique_id,
            )
            if self.is_connected:
                _async_connected_device_registered(
                    hass,
                    self.mac_address,
                    self.ip_address,
                    self.hostname,
                )

    @callback
    def find_device_entry(self) -> dr.DeviceEntry | None:
        """Return device entry."""
        assert self.mac_address is not None

        return dr.async_get(self.hass).async_get_device(
            set(), {(dr.CONNECTION_NETWORK_MAC, self.mac_address)})

    async def async_internal_added_to_hass(self) -> None:
        """Handle added to Home Assistant."""
        # Entities without a unique ID don't have a device
        if (not self.registry_entry or not self.platform
                or not self.platform.config_entry or not self.mac_address
                or (device_entry := self.find_device_entry()) is None
                # Entities should not have a device info. We opt them out
                # of this logic if they do.
                or self.device_info):
            if self.device_info:
                LOGGER.debug("Entity %s unexpectedly has a device info",
                             self.entity_id)
            await super().async_internal_added_to_hass()
            return

        # Attach entry to device
        if self.registry_entry.device_id != device_entry.id:
            self.registry_entry = er.async_get(self.hass).async_update_entity(
                self.entity_id, device_id=device_entry.id)

        # Attach device to config entry
        if self.platform.config_entry.entry_id not in device_entry.config_entries:
            dr.async_get(self.hass).async_update_device(
                device_entry.id,
                add_config_entry_id=self.platform.config_entry.entry_id,
            )

        # Do this last or else the entity registry update listener has been installed
        await super().async_internal_added_to_hass()
Пример #25
0
async def test_all_optional_config(hass, calls):
    """Test: including all optional templates is ok."""
    with assert_setup_component(1, "template"):
        assert await setup.async_setup_component(
            hass,
            "template",
            {
                "template": {
                    "unique_id": "test",
                    "button": {
                        "press": {
                            "service": "test.automation"
                        },
                        "device_class": "restart",
                        "unique_id": "test",
                        "name": "test",
                        "icon": "mdi:test",
                    },
                }
            },
        )

    await hass.async_block_till_done()
    await hass.async_start()
    await hass.async_block_till_done()

    _verify(
        hass,
        STATE_UNKNOWN,
        {
            CONF_DEVICE_CLASS: "restart",
            CONF_FRIENDLY_NAME: "test",
            CONF_ICON: "mdi:test",
        },
        _TEST_OPTIONS_BUTTON,
    )

    now = dt.datetime.now(dt.timezone.utc)

    with patch("homeassistant.util.dt.utcnow", return_value=now):
        await hass.services.async_call(
            BUTTON_DOMAIN,
            SERVICE_PRESS,
            {CONF_ENTITY_ID: _TEST_OPTIONS_BUTTON},
            blocking=True,
        )

    assert len(calls) == 1

    _verify(
        hass,
        now.isoformat(),
        {
            CONF_DEVICE_CLASS: "restart",
            CONF_FRIENDLY_NAME: "test",
            CONF_ICON: "mdi:test",
        },
        _TEST_OPTIONS_BUTTON,
    )

    er = async_get(hass)
    assert er.async_get_entity_id("button", "template", "test-test")
Пример #26
0
def _async_migrate_unique_id(
    hass: HomeAssistant, discovery_info: DiscoveryInfoType | None
) -> None:
    """Change unique_ids used in 2021.4 to exchange individual color switch address for brightness address."""
    entity_registry = er.async_get(hass)
    if not discovery_info or not discovery_info["platform_config"]:
        return

    platform_config = discovery_info["platform_config"]
    for entity_config in platform_config:
        individual_colors_config = entity_config.get(LightSchema.CONF_INDIVIDUAL_COLORS)
        if individual_colors_config is None:
            continue
        try:
            ga_red_switch = individual_colors_config[LightSchema.CONF_RED][KNX_ADDRESS][
                0
            ]
            ga_green_switch = individual_colors_config[LightSchema.CONF_GREEN][
                KNX_ADDRESS
            ][0]
            ga_blue_switch = individual_colors_config[LightSchema.CONF_BLUE][
                KNX_ADDRESS
            ][0]
        except KeyError:
            continue
        # normalize group address strings
        ga_red_switch = parse_device_group_address(ga_red_switch)
        ga_green_switch = parse_device_group_address(ga_green_switch)
        ga_blue_switch = parse_device_group_address(ga_blue_switch)
        # white config is optional so it has to be checked for `None` extra
        white_config = individual_colors_config.get(LightSchema.CONF_WHITE)
        white_switch = (
            white_config.get(KNX_ADDRESS) if white_config is not None else None
        )
        ga_white_switch = (
            parse_device_group_address(white_switch[0])
            if white_switch is not None
            else None
        )

        old_uid = (
            f"{ga_red_switch}_"
            f"{ga_green_switch}_"
            f"{ga_blue_switch}_"
            f"{ga_white_switch}"
        )
        entity_id = entity_registry.async_get_entity_id("light", DOMAIN, old_uid)
        if entity_id is None:
            continue

        ga_red_brightness = parse_device_group_address(
            individual_colors_config[LightSchema.CONF_RED][
                LightSchema.CONF_BRIGHTNESS_ADDRESS
            ][0]
        )
        ga_green_brightness = parse_device_group_address(
            individual_colors_config[LightSchema.CONF_GREEN][
                LightSchema.CONF_BRIGHTNESS_ADDRESS
            ][0]
        )
        ga_blue_brightness = parse_device_group_address(
            individual_colors_config[LightSchema.CONF_BLUE][
                LightSchema.CONF_BRIGHTNESS_ADDRESS
            ][0]
        )

        new_uid = f"{ga_red_brightness}_{ga_green_brightness}_{ga_blue_brightness}"
        entity_registry.async_update_entity(entity_id, new_unique_id=new_uid)
async def test_restore_states(hass):
    """Test restoring states."""
    hass.state = CoreState.not_running

    registry = er.async_get(hass)

    registry.async_get_or_create(
        "light",
        "hue",
        "1234",
        suggested_object_id="simple",
    )
    # Should not be created
    registry.async_get_or_create(
        "light",
        "hue",
        "5678",
        suggested_object_id="disabled",
        disabled_by=er.DISABLED_HASS,
    )
    registry.async_get_or_create(
        "light",
        "hue",
        "9012",
        suggested_object_id="all_info_set",
        capabilities={"max": 100},
        supported_features=5,
        device_class="mock-device-class",
        original_name="Mock Original Name",
        original_icon="hass:original-icon",
    )

    hass.bus.async_fire(EVENT_HOMEASSISTANT_START, {})
    await hass.async_block_till_done()

    simple = hass.states.get("light.simple")
    assert simple is not None
    assert simple.state == STATE_UNAVAILABLE
    assert simple.attributes == {"restored": True, "supported_features": 0}

    disabled = hass.states.get("light.disabled")
    assert disabled is None

    all_info_set = hass.states.get("light.all_info_set")
    assert all_info_set is not None
    assert all_info_set.state == STATE_UNAVAILABLE
    assert all_info_set.attributes == {
        "max": 100,
        "supported_features": 5,
        "device_class": "mock-device-class",
        "restored": True,
        "friendly_name": "Mock Original Name",
        "icon": "hass:original-icon",
    }

    registry.async_remove("light.disabled")
    registry.async_remove("light.simple")
    registry.async_remove("light.all_info_set")

    await hass.async_block_till_done()

    assert hass.states.get("light.simple") is None
    assert hass.states.get("light.disabled") is None
    assert hass.states.get("light.all_info_set") is None
Пример #28
0
async def test_eve_degree_setup(hass):
    """Test that the accessory can be correctly setup in HA."""
    accessories = await setup_accessories_from_file(hass, "eve_degree.json")
    config_entry, pairing = await setup_test_accessories(hass, accessories)

    entity_registry = er.async_get(hass)
    device_registry = dr.async_get(hass)

    sensors = [
        (
            "sensor.eve_degree_aa11_temperature",
            "homekit-AA00A0A00000-22",
            "Eve Degree AA11 Temperature",
        ),
        (
            "sensor.eve_degree_aa11_humidity",
            "homekit-AA00A0A00000-27",
            "Eve Degree AA11 Humidity",
        ),
        (
            "sensor.eve_degree_aa11_air_pressure",
            "homekit-AA00A0A00000-aid:1-sid:30-cid:32",
            "Eve Degree AA11 - Air Pressure",
        ),
        (
            "sensor.eve_degree_aa11_battery",
            "homekit-AA00A0A00000-17",
            "Eve Degree AA11 Battery",
        ),
        (
            "number.eve_degree_aa11",
            "homekit-AA00A0A00000-aid:1-sid:30-cid:33",
            "Eve Degree AA11",
        ),
    ]

    device_ids = set()

    for (entity_id, unique_id, friendly_name) in sensors:
        entry = entity_registry.async_get(entity_id)
        assert entry.unique_id == unique_id

        helper = Helper(
            hass,
            entity_id,
            pairing,
            accessories[0],
            config_entry,
        )
        state = await helper.poll_and_get_state()
        assert state.attributes["friendly_name"] == friendly_name

        device = device_registry.async_get(entry.device_id)
        assert device.manufacturer == "Elgato"
        assert device.name == "Eve Degree AA11"
        assert device.model == "Eve Degree 00AAA0000"
        assert device.sw_version == "1.2.8"
        assert device.via_device_id is None

        device_ids.add(entry.device_id)

    # All entities should be part of same device
    assert len(device_ids) == 1
Пример #29
0
async def test_sensors(
    hass: HomeAssistant,
    init_integration: MockConfigEntry,
) -> None:
    """Test the PVOutput sensors."""
    entity_registry = er.async_get(hass)
    device_registry = dr.async_get(hass)

    state = hass.states.get("sensor.frenck_s_solar_farm_energy_consumed")
    entry = entity_registry.async_get(
        "sensor.frenck_s_solar_farm_energy_consumed")
    assert entry
    assert state
    assert entry.unique_id == "12345_energy_consumption"
    assert entry.entity_category is None
    assert state.state == "1000"
    assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENERGY
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            "Frenck's Solar Farm Energy consumed")
    assert state.attributes.get(
        ATTR_STATE_CLASS) is SensorStateClass.TOTAL_INCREASING
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_WATT_HOUR
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.frenck_s_solar_farm_energy_generated")
    entry = entity_registry.async_get(
        "sensor.frenck_s_solar_farm_energy_generated")
    assert entry
    assert state
    assert entry.unique_id == "12345_energy_generation"
    assert entry.entity_category is None
    assert state.state == "500"
    assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.ENERGY
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            "Frenck's Solar Farm Energy generated")
    assert state.attributes.get(
        ATTR_STATE_CLASS) is SensorStateClass.TOTAL_INCREASING
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_WATT_HOUR
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.frenck_s_solar_farm_efficiency")
    entry = entity_registry.async_get("sensor.frenck_s_solar_farm_efficiency")
    assert entry
    assert state
    assert entry.unique_id == "12345_normalized_output"
    assert entry.entity_category is None
    assert state.state == "0.5"
    assert state.attributes.get(
        ATTR_FRIENDLY_NAME) == "Frenck's Solar Farm Efficiency"
    assert state.attributes.get(
        ATTR_STATE_CLASS) is SensorStateClass.MEASUREMENT
    assert (state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) ==
            f"{ENERGY_KILO_WATT_HOUR}/{POWER_KILO_WATT}")
    assert ATTR_DEVICE_CLASS not in state.attributes
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.frenck_s_solar_farm_power_consumed")
    entry = entity_registry.async_get(
        "sensor.frenck_s_solar_farm_power_consumed")
    assert entry
    assert state
    assert entry.unique_id == "12345_power_consumption"
    assert entry.entity_category is None
    assert state.state == "2500"
    assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.POWER
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            "Frenck's Solar Farm Power consumed")
    assert state.attributes.get(
        ATTR_STATE_CLASS) is SensorStateClass.MEASUREMENT
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.frenck_s_solar_farm_power_generated")
    entry = entity_registry.async_get(
        "sensor.frenck_s_solar_farm_power_generated")
    assert entry
    assert state
    assert entry.unique_id == "12345_power_generation"
    assert entry.entity_category is None
    assert state.state == "1500"
    assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.POWER
    assert (state.attributes.get(ATTR_FRIENDLY_NAME) ==
            "Frenck's Solar Farm Power generated")
    assert state.attributes.get(
        ATTR_STATE_CLASS) is SensorStateClass.MEASUREMENT
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == POWER_WATT
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.frenck_s_solar_farm_temperature")
    entry = entity_registry.async_get("sensor.frenck_s_solar_farm_temperature")
    assert entry
    assert state
    assert entry.unique_id == "12345_temperature"
    assert entry.entity_category is None
    assert state.state == "20.2"
    assert state.attributes.get(
        ATTR_DEVICE_CLASS) == SensorDeviceClass.TEMPERATURE
    assert state.attributes.get(
        ATTR_FRIENDLY_NAME) == "Frenck's Solar Farm Temperature"
    assert state.attributes.get(
        ATTR_STATE_CLASS) is SensorStateClass.MEASUREMENT
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
    assert ATTR_ICON not in state.attributes

    state = hass.states.get("sensor.frenck_s_solar_farm_voltage")
    entry = entity_registry.async_get("sensor.frenck_s_solar_farm_voltage")
    assert entry
    assert state
    assert entry.unique_id == "12345_voltage"
    assert entry.entity_category is None
    assert state.state == "220.5"
    assert state.attributes.get(ATTR_DEVICE_CLASS) == SensorDeviceClass.VOLTAGE
    assert state.attributes.get(
        ATTR_FRIENDLY_NAME) == "Frenck's Solar Farm Voltage"
    assert state.attributes.get(
        ATTR_STATE_CLASS) is SensorStateClass.MEASUREMENT
    assert state.attributes.get(
        ATTR_UNIT_OF_MEASUREMENT) == ELECTRIC_POTENTIAL_VOLT
    assert ATTR_ICON not in state.attributes

    assert entry.device_id
    device_entry = device_registry.async_get(entry.device_id)
    assert device_entry
    assert device_entry.identifiers == {(DOMAIN, "12345")}
    assert device_entry.manufacturer == "PVOutput"
    assert device_entry.model == "Super Inverters Inc."
    assert device_entry.name == "Frenck's Solar Farm"
    assert device_entry.configuration_url == "https://pvoutput.org/list.jsp?sid=12345"
    assert device_entry.entry_type is None
    assert device_entry.sw_version is None
    assert device_entry.hw_version is None
Пример #30
0
async def test_sensor_enabled_without_forecast(hass):
    """Test enabling an advanced sensor."""
    registry = er.async_get(hass)

    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-apparenttemperature",
        suggested_object_id="home_apparent_temperature",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-cloudcover",
        suggested_object_id="home_cloud_cover",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-dewpoint",
        suggested_object_id="home_dew_point",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-realfeeltemperatureshade",
        suggested_object_id="home_realfeel_temperature_shade",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-wetbulbtemperature",
        suggested_object_id="home_wet_bulb_temperature",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-wind",
        suggested_object_id="home_wind",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-windchilltemperature",
        suggested_object_id="home_wind_chill_temperature",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-windgust",
        suggested_object_id="home_wind_gust",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-cloudcoverday-0",
        suggested_object_id="home_cloud_cover_day_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-cloudcovernight-0",
        suggested_object_id="home_cloud_cover_night_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-grass-0",
        suggested_object_id="home_grass_pollen_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-mold-0",
        suggested_object_id="home_mold_pollen_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-ozone-0",
        suggested_object_id="home_ozone_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-ragweed-0",
        suggested_object_id="home_ragweed_pollen_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-realfeeltemperatureshademax-0",
        suggested_object_id="home_realfeel_temperature_shade_max_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-realfeeltemperatureshademin-0",
        suggested_object_id="home_realfeel_temperature_shade_min_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-tree-0",
        suggested_object_id="home_tree_pollen_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-windgustday-0",
        suggested_object_id="home_wind_gust_day_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-windgustnight-0",
        suggested_object_id="home_wind_gust_night_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-windday-0",
        suggested_object_id="home_wind_day_0d",
        disabled_by=None,
    )
    registry.async_get_or_create(
        SENSOR_DOMAIN,
        DOMAIN,
        "0123456-windnight-0",
        suggested_object_id="home_wind_night_0d",
        disabled_by=None,
    )

    await init_integration(hass, forecast=True)

    state = hass.states.get("sensor.home_apparent_temperature")
    assert state
    assert state.state == "22.8"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TEMPERATURE

    entry = registry.async_get("sensor.home_apparent_temperature")
    assert entry
    assert entry.unique_id == "0123456-apparenttemperature"

    state = hass.states.get("sensor.home_cloud_cover")
    assert state
    assert state.state == "10"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-cloudy"

    entry = registry.async_get("sensor.home_cloud_cover")
    assert entry
    assert entry.unique_id == "0123456-cloudcover"

    state = hass.states.get("sensor.home_dew_point")
    assert state
    assert state.state == "16.2"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TEMPERATURE

    entry = registry.async_get("sensor.home_dew_point")
    assert entry
    assert entry.unique_id == "0123456-dewpoint"

    state = hass.states.get("sensor.home_realfeel_temperature_shade")
    assert state
    assert state.state == "21.1"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TEMPERATURE

    entry = registry.async_get("sensor.home_realfeel_temperature_shade")
    assert entry
    assert entry.unique_id == "0123456-realfeeltemperatureshade"

    state = hass.states.get("sensor.home_wet_bulb_temperature")
    assert state
    assert state.state == "18.6"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TEMPERATURE

    entry = registry.async_get("sensor.home_wet_bulb_temperature")
    assert entry
    assert entry.unique_id == "0123456-wetbulbtemperature"

    state = hass.states.get("sensor.home_wind_chill_temperature")
    assert state
    assert state.state == "22.8"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TEMPERATURE

    entry = registry.async_get("sensor.home_wind_chill_temperature")
    assert entry
    assert entry.unique_id == "0123456-windchilltemperature"

    state = hass.states.get("sensor.home_wind_gust")
    assert state
    assert state.state == "20.3"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy"

    entry = registry.async_get("sensor.home_wind_gust")
    assert entry
    assert entry.unique_id == "0123456-windgust"

    state = hass.states.get("sensor.home_wind")
    assert state
    assert state.state == "14.5"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy"

    entry = registry.async_get("sensor.home_wind")
    assert entry
    assert entry.unique_id == "0123456-wind"

    state = hass.states.get("sensor.home_cloud_cover_day_0d")
    assert state
    assert state.state == "58"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-cloudy"

    entry = registry.async_get("sensor.home_cloud_cover_day_0d")
    assert entry
    assert entry.unique_id == "0123456-cloudcoverday-0"

    state = hass.states.get("sensor.home_cloud_cover_night_0d")
    assert state
    assert state.state == "65"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == PERCENTAGE
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-cloudy"

    entry = registry.async_get("sensor.home_cloud_cover_night_0d")
    assert entry

    state = hass.states.get("sensor.home_grass_pollen_0d")
    assert state
    assert state.state == "0"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert (
        state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
        == CONCENTRATION_PARTS_PER_CUBIC_METER
    )
    assert state.attributes.get("level") == "Low"
    assert state.attributes.get(ATTR_ICON) == "mdi:grass"

    entry = registry.async_get("sensor.home_grass_pollen_0d")
    assert entry
    assert entry.unique_id == "0123456-grass-0"

    state = hass.states.get("sensor.home_mold_pollen_0d")
    assert state
    assert state.state == "0"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert (
        state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
        == CONCENTRATION_PARTS_PER_CUBIC_METER
    )
    assert state.attributes.get("level") == "Low"
    assert state.attributes.get(ATTR_ICON) == "mdi:blur"

    entry = registry.async_get("sensor.home_mold_pollen_0d")
    assert entry
    assert entry.unique_id == "0123456-mold-0"

    state = hass.states.get("sensor.home_ozone_0d")
    assert state
    assert state.state == "32"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get("level") == "Good"
    assert state.attributes.get(ATTR_ICON) == "mdi:vector-triangle"

    entry = registry.async_get("sensor.home_ozone_0d")
    assert entry
    assert entry.unique_id == "0123456-ozone-0"

    state = hass.states.get("sensor.home_ragweed_pollen_0d")
    assert state
    assert state.state == "0"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert (
        state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
        == CONCENTRATION_PARTS_PER_CUBIC_METER
    )
    assert state.attributes.get("level") == "Low"
    assert state.attributes.get(ATTR_ICON) == "mdi:sprout"

    entry = registry.async_get("sensor.home_ragweed_pollen_0d")
    assert entry
    assert entry.unique_id == "0123456-ragweed-0"

    state = hass.states.get("sensor.home_realfeel_temperature_shade_max_0d")
    assert state
    assert state.state == "28.0"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TEMPERATURE

    entry = registry.async_get("sensor.home_realfeel_temperature_shade_max_0d")
    assert entry
    assert entry.unique_id == "0123456-realfeeltemperatureshademax-0"

    state = hass.states.get("sensor.home_realfeel_temperature_shade_min_0d")
    assert state
    assert state.state == "15.1"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == TEMP_CELSIUS
    assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TEMPERATURE

    entry = registry.async_get("sensor.home_realfeel_temperature_shade_min_0d")
    assert entry
    assert entry.unique_id == "0123456-realfeeltemperatureshademin-0"

    state = hass.states.get("sensor.home_tree_pollen_0d")
    assert state
    assert state.state == "0"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert (
        state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
        == CONCENTRATION_PARTS_PER_CUBIC_METER
    )
    assert state.attributes.get("level") == "Low"
    assert state.attributes.get(ATTR_ICON) == "mdi:tree-outline"

    entry = registry.async_get("sensor.home_tree_pollen_0d")
    assert entry
    assert entry.unique_id == "0123456-tree-0"

    state = hass.states.get("sensor.home_wind_day_0d")
    assert state
    assert state.state == "13.0"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR
    assert state.attributes.get("direction") == "SSE"
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy"

    entry = registry.async_get("sensor.home_wind_day_0d")
    assert entry
    assert entry.unique_id == "0123456-windday-0"

    state = hass.states.get("sensor.home_wind_night_0d")
    assert state
    assert state.state == "7.4"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR
    assert state.attributes.get("direction") == "WNW"
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy"

    entry = registry.async_get("sensor.home_wind_night_0d")
    assert entry
    assert entry.unique_id == "0123456-windnight-0"

    state = hass.states.get("sensor.home_wind_gust_day_0d")
    assert state
    assert state.state == "29.6"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR
    assert state.attributes.get("direction") == "S"
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy"

    entry = registry.async_get("sensor.home_wind_gust_day_0d")
    assert entry
    assert entry.unique_id == "0123456-windgustday-0"

    state = hass.states.get("sensor.home_wind_gust_night_0d")
    assert state
    assert state.state == "18.5"
    assert state.attributes.get(ATTR_ATTRIBUTION) == ATTRIBUTION
    assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == SPEED_KILOMETERS_PER_HOUR
    assert state.attributes.get("direction") == "WSW"
    assert state.attributes.get(ATTR_ICON) == "mdi:weather-windy"

    entry = registry.async_get("sensor.home_wind_gust_night_0d")
    assert entry
    assert entry.unique_id == "0123456-windgustnight-0"