def async_see(self,
                  mac: str = None,
                  dev_id: str = None,
                  host_name: str = None,
                  location_name: str = None,
                  gps: GPSType = None,
                  gps_accuracy=None,
                  battery: str = None,
                  attributes: dict = None,
                  source_type: str = SOURCE_TYPE_GPS):
        """Notify the device tracker that you see a device.

        This method is a coroutine.
        """
        if mac is None and dev_id is None:
            raise HomeAssistantError('Neither mac or device id passed in')
        elif mac is not None:
            mac = str(mac).upper()
            device = self.mac_to_dev.get(mac)
            if not device:
                dev_id = util.slugify(host_name or '') or util.slugify(mac)
        else:
            dev_id = cv.slug(str(dev_id).lower())
            device = self.devices.get(dev_id)

        if device:
            yield from device.async_seen(host_name, location_name, gps,
                                         gps_accuracy, battery, attributes,
                                         source_type)
            if device.track:
                yield from device.async_update_ha_state()
            return

        # If no device can be found, create it
        dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
        device = Device(self.hass, self.consider_home, self.track_new, dev_id,
                        mac, (host_name or dev_id).replace('_', ' '))
        self.devices[dev_id] = device
        if mac is not None:
            self.mac_to_dev[mac] = device

        yield from device.async_seen(host_name, location_name, gps,
                                     gps_accuracy, battery, attributes,
                                     source_type)

        if device.track:
            yield from device.async_update_ha_state()

        self.hass.bus.async_fire(EVENT_NEW_DEVICE, {
            ATTR_ENTITY_ID: device.entity_id,
            ATTR_HOST_NAME: device.host_name,
        })

        # During init, we ignore the group
        if self.group is not None:
            yield from self.group.async_update_tracked_entity_ids(
                list(self.group.tracking) + [device.entity_id])

        # lookup mac vendor string to be stored in config
        yield from device.set_vendor_for_mac()

        # update known_devices.yaml
        self.hass.async_add_job(
            self.async_update_config(self.hass.config.path(YAML_DEVICES),
                                     dev_id, device))
    def start_casting(call):
        """service called."""

        from pychromecast.controllers.spotify import SpotifyController
        import spotipy
        transfer_playback = False

        uri = call.data.get(CONF_SPOTIFY_URI)
        random_song = call.data.get(CONF_RANDOM, False)

        # Get device name from tiehr device_name or entity_id
        device_name = None
        if call.data.get(CONF_DEVICE_NAME) is None:
            entity_id = call.data.get(CONF_ENTITY_ID)
            if entity_id is None:
                raise HomeAssistantError(
                    'Either entity_id or device_name must be specified')
            entity_states = hass.states.get(entity_id)
            if entity_states is None:
                _LOGGER.error('Could not find entity_id: %s', entity_id)
            else:
                device_name = entity_states.attributes.get('friendly_name')
        else:
            device_name = call.data.get(CONF_DEVICE_NAME)

        if device_name is None or device_name.strip() == '':
            raise HomeAssistantError('device_name is empty')

        # Find chromecast device
        cast = get_chromcast_device(device_name)
        _LOGGER.debug('Found cast device: %s', cast)
        cast.wait()

        account = call.data.get(CONF_SPOTIFY_ACCOUNT)
        user = username
        pwd = password
        if account is not None:
            _LOGGER.debug('setting up with different account than default %s',
                          account)
            user = accounts.get(account).get(CONF_USERNAME)
            pwd = accounts.get(account).get(CONF_PASSWORD)

        # login as real browser to get powerful token
        access_token, expires = get_spotify_token(username=user, password=pwd)

        # get the spotify web api client
        client = spotipy.Spotify(auth=access_token)

        # Check if something is playing

        if uri is None or uri.strip() == '' or call.data.get(
                CONF_TRANSFER_PLAYBACK):
            current_playback = client.current_playback()
            if current_playback is not None:
                _LOGGER.debug('current_playback from spotipy: %s',
                              current_playback)
                transfer_playback = True

        # launch the app on chromecast
        sp = SpotifyController(access_token, expires)
        cast.register_handler(sp)
        sp.launch_app()

        if not sp.is_launched and not sp.credential_error:
            raise HomeAssistantError(
                'Failed to launch spotify controller due to timeout')
        if not sp.is_launched and sp.credential_error:
            raise HomeAssistantError(
                'Failed to launch spotify controller due to credentials error')

        spotify_device_id = None
        devices_available = client.devices()
        for device in devices_available['devices']:
            if device['id'] == sp.device:
                spotify_device_id = device['id']
                break

        if not spotify_device_id:
            _LOGGER.error('No device with id "{}" known by Spotify'.format(
                sp.device))
            _LOGGER.error('Known devices: {}'.format(
                devices_available['devices']))
            return

        if transfer_playback == True:
            transfer_pb(client, spotify_device_id)
        else:
            play(client, spotify_device_id, uri, random_song)
Beispiel #3
0
})
@websocket_api.async_response
async def websocket_remove_config_entry_from_device(
        hass: HomeAssistant, connection: websocket_api.ActiveConnection,
        msg: dict) -> None:
    """Remove config entry from a device."""
    registry = async_get(hass)
    config_entry_id = msg["config_entry_id"]
    device_id = msg["device_id"]

    if (config_entry :=
            hass.config_entries.async_get_entry(config_entry_id)) is None:
        raise HomeAssistantError("Unknown config entry")

    if not config_entry.supports_remove_device:
        raise HomeAssistantError(
            "Config entry does not support device removal")

    if (device_entry := registry.async_get(device_id)) is None:
        raise HomeAssistantError("Unknown device")

    if config_entry_id not in device_entry.config_entries:
        raise HomeAssistantError("Config entry not in device")

    try:
        integration = await loader.async_get_integration(
            hass, config_entry.domain)
        component = integration.get_component()
    except (ImportError, loader.IntegrationNotFound) as exc:
        raise HomeAssistantError("Integration not found") from exc

    if not await component.async_remove_config_entry_device(
Beispiel #4
0
class TestImageProcessing:
    """Test class for image processing."""
    def setup_method(self):
        """Set up things to be run when tests are started."""
        self.hass = get_test_home_assistant()

        setup_component(
            self.hass,
            http.DOMAIN,
            {http.DOMAIN: {
                http.CONF_SERVER_PORT: get_test_instance_port()
            }},
        )

        config = {
            ip.DOMAIN: {
                "platform": "test"
            },
            "camera": {
                "platform": "demo"
            }
        }

        setup_component(self.hass, ip.DOMAIN, config)
        self.hass.block_till_done()

        state = self.hass.states.get("camera.demo_camera")
        self.url = f"{self.hass.config.internal_url}{state.attributes.get(ATTR_ENTITY_PICTURE)}"

    def teardown_method(self):
        """Stop everything that was started."""
        self.hass.stop()

    @patch(
        "homeassistant.components.demo.camera.Path.read_bytes",
        return_value=b"Test",
    )
    def test_get_image_from_camera(self, mock_camera_read):
        """Grab an image from camera entity."""
        common.scan(self.hass, entity_id="image_processing.test")
        self.hass.block_till_done()

        state = self.hass.states.get("image_processing.test")

        assert mock_camera_read.called
        assert state.state == "1"
        assert state.attributes["image"] == b"Test"

    @patch(
        "homeassistant.components.camera.async_get_image",
        side_effect=HomeAssistantError(),
    )
    def test_get_image_without_exists_camera(self, mock_image):
        """Try to get image without exists camera."""
        self.hass.states.remove("camera.demo_camera")

        common.scan(self.hass, entity_id="image_processing.test")
        self.hass.block_till_done()

        state = self.hass.states.get("image_processing.test")

        assert mock_image.called
        assert state.state == "0"
Beispiel #5
0
    async def async_see(
        self,
        mac: str = None,
        dev_id: str = None,
        host_name: str = None,
        location_name: str = None,
        gps: GPSType = None,
        gps_accuracy: int = None,
        battery: int = None,
        attributes: dict = None,
        source_type: str = SOURCE_TYPE_GPS,
        picture: str = None,
        icon: str = None,
        consider_home: timedelta = None,
    ):
        """Notify the device tracker that you see a device.

        This method is a coroutine.
        """
        registry = await async_get_registry(self.hass)
        if mac is None and dev_id is None:
            raise HomeAssistantError("Neither mac or device id passed in")
        if mac is not None:
            mac = str(mac).upper()
            device = self.mac_to_dev.get(mac)
            if not device:
                dev_id = util.slugify(host_name or "") or util.slugify(mac)
        else:
            dev_id = cv.slug(str(dev_id).lower())
            device = self.devices.get(dev_id)

        if device:
            await device.async_seen(
                host_name,
                location_name,
                gps,
                gps_accuracy,
                battery,
                attributes,
                source_type,
                consider_home,
            )
            if device.track:
                device.async_write_ha_state()
            return

        # Guard from calling see on entity registry entities.
        entity_id = f"{DOMAIN}.{dev_id}"
        if registry.async_is_registered(entity_id):
            LOGGER.error("The see service is not supported for this entity %s",
                         entity_id)
            return

        # If no device can be found, create it
        dev_id = util.ensure_unique_string(dev_id, self.devices.keys())
        device = Device(
            self.hass,
            consider_home or self.consider_home,
            self.track_new,
            dev_id,
            mac,
            picture=picture,
            icon=icon,
        )
        self.devices[dev_id] = device
        if mac is not None:
            self.mac_to_dev[mac] = device

        await device.async_seen(
            host_name,
            location_name,
            gps,
            gps_accuracy,
            battery,
            attributes,
            source_type,
        )

        if device.track:
            device.async_write_ha_state()

        self.hass.bus.async_fire(
            EVENT_NEW_DEVICE,
            {
                ATTR_ENTITY_ID: device.entity_id,
                ATTR_HOST_NAME: device.host_name,
                ATTR_MAC: device.mac,
            },
        )

        # update known_devices.yaml
        self.hass.async_create_task(
            self.async_update_config(self.hass.config.path(YAML_DEVICES),
                                     dev_id, device))
    async def _async_add_entity(self, entity, update_before_add,
                                entity_registry, device_registry):
        """Add an entity to the platform."""
        if entity is None:
            raise ValueError("Entity cannot be None")

        entity.hass = self.hass
        entity.platform = self
        entity.parallel_updates = self._get_parallel_updates_semaphore(
            hasattr(entity, "async_update"))

        # Update properties before we generate the entity_id
        if update_before_add:
            try:
                await entity.async_device_update(warning=False)
            except Exception:  # pylint: disable=broad-except
                self.logger.exception("%s: Error on device update!",
                                      self.platform_name)
                return

        suggested_object_id = None

        # Get entity_id from unique ID registration
        if entity.unique_id is not None:
            if entity.entity_id is not None:
                suggested_object_id = split_entity_id(entity.entity_id)[1]
            else:
                suggested_object_id = entity.name

            if self.entity_namespace is not None:
                suggested_object_id = f"{self.entity_namespace} {suggested_object_id}"

            if self.config_entry is not None:
                config_entry_id = self.config_entry.entry_id
            else:
                config_entry_id = None

            device_info = entity.device_info
            device_id = None

            if config_entry_id is not None and device_info is not None:
                processed_dev_info = {"config_entry_id": config_entry_id}
                for key in (
                        "connections",
                        "identifiers",
                        "manufacturer",
                        "model",
                        "name",
                        "sw_version",
                        "via_device",
                ):
                    if key in device_info:
                        processed_dev_info[key] = device_info[key]

                device = device_registry.async_get_or_create(
                    **processed_dev_info)
                if device:
                    device_id = device.id

            disabled_by: Optional[str] = None
            if not entity.entity_registry_enabled_default:
                disabled_by = DISABLED_INTEGRATION

            entry = entity_registry.async_get_or_create(
                self.domain,
                self.platform_name,
                entity.unique_id,
                suggested_object_id=suggested_object_id,
                config_entry=self.config_entry,
                device_id=device_id,
                known_object_ids=self.entities.keys(),
                disabled_by=disabled_by,
                capabilities=entity.capability_attributes,
                supported_features=entity.supported_features,
                device_class=entity.device_class,
                unit_of_measurement=entity.unit_of_measurement,
                original_name=entity.name,
                original_icon=entity.icon,
            )

            entity.registry_entry = entry
            entity.entity_id = entry.entity_id

            if entry.disabled:
                self.logger.info(
                    "Not adding entity %s because it's disabled",
                    entry.name or entity.name
                    or f'"{self.platform_name} {entity.unique_id}"',
                )
                return

        # We won't generate an entity ID if the platform has already set one
        # We will however make sure that platform cannot pick a registered ID
        elif entity.entity_id is not None and entity_registry.async_is_registered(
                entity.entity_id):
            # If entity already registered, convert entity id to suggestion
            suggested_object_id = split_entity_id(entity.entity_id)[1]
            entity.entity_id = None

        # Generate entity ID
        if entity.entity_id is None:
            suggested_object_id = (suggested_object_id or entity.name
                                   or DEVICE_DEFAULT_NAME)

            if self.entity_namespace is not None:
                suggested_object_id = f"{self.entity_namespace} {suggested_object_id}"
            entity.entity_id = entity_registry.async_generate_entity_id(
                self.domain, suggested_object_id, self.entities.keys())

        # Make sure it is valid in case an entity set the value themselves
        if not valid_entity_id(entity.entity_id):
            raise HomeAssistantError(f"Invalid entity id: {entity.entity_id}")

        already_exists = entity.entity_id in self.entities

        if not already_exists:
            existing = self.hass.states.get(entity.entity_id)

            if existing and not existing.attributes.get("restored"):
                already_exists = True

        if already_exists:
            msg = f"Entity id already exists: {entity.entity_id}"
            if entity.unique_id is not None:
                msg += f". Platform {self.platform_name} does not generate unique IDs"
            raise HomeAssistantError(msg)

        entity_id = entity.entity_id
        self.entities[entity_id] = entity
        entity.async_on_remove(lambda: self.entities.pop(entity_id))

        await entity.async_internal_added_to_hass()
        await entity.async_added_to_hass()

        await entity.async_update_ha_state()
Beispiel #7
0
        return await event.async_attach_trigger(hass,
                                                event_config,
                                                action,
                                                trigger_info,
                                                platform_type="device")

    if trigger_platform == "state":
        if trigger_type == NODE_STATUS:
            state_config = {state.CONF_PLATFORM: "state"}

            state_config[state.CONF_ENTITY_ID] = config[CONF_ENTITY_ID]
            copy_available_params(
                config, state_config,
                [state.CONF_FOR, state.CONF_FROM, state.CONF_TO])
        else:
            raise HomeAssistantError(f"Unhandled trigger type {trigger_type}")

        state_config = await state.async_validate_trigger_config(
            hass, state_config)
        return await state.async_attach_trigger(hass,
                                                state_config,
                                                action,
                                                trigger_info,
                                                platform_type="device")

    if trigger_platform == VALUE_UPDATED_PLATFORM_TYPE:
        zwave_js_config = {
            state.CONF_PLATFORM: trigger_platform,
            CONF_DEVICE_ID: config[CONF_DEVICE_ID],
        }
        copy_available_params(
Beispiel #8
0
def _get_camera_from_entity_id(hass: HomeAssistant, entity_id: str) -> Camera:
    """Get camera component from entity_id."""
    if (component := hass.data.get(DOMAIN)) is None:
        raise HomeAssistantError("Camera integration not set up")
Beispiel #9
0
        offset = 0

        if playqueue_id := src.pop("playqueue_id", None):
            try:
                playqueue = self.plex_server.get_playqueue(playqueue_id)
            except plexapi.exceptions.NotFound as err:
                raise HomeAssistantError(
                    f"PlayQueue '{playqueue_id}' could not be found") from err
        else:
            shuffle = src.pop("shuffle", 0)
            offset = src.pop("offset", 0) * 1000
            resume = src.pop("resume", False)
            media = self.plex_server.lookup_media(media_type, **src)

            if media is None:
                raise HomeAssistantError(
                    f"Media could not be found: {media_id}")

            if resume and not offset:
                offset = media.viewOffset

            _LOGGER.debug("Attempting to play %s on %s", media, self.name)
            playqueue = self.plex_server.create_playqueue(media,
                                                          shuffle=shuffle)

        try:
            self.device.playMedia(playqueue, offset=offset)
        except requests.exceptions.ConnectTimeout as exc:
            raise HomeAssistantError(
                f"Request failed when playing on {self.name}") from exc

    @property
Beispiel #10
0
class ScreenLogicClimate(ScreenlogicEntity, ClimateEntity, RestoreEntity):
    """Represents a ScreenLogic climate entity."""

    _attr_supported_features = (ClimateEntityFeature.TARGET_TEMPERATURE
                                | ClimateEntityFeature.PRESET_MODE)

    def __init__(self, coordinator, body):
        """Initialize a ScreenLogic climate entity."""
        super().__init__(coordinator, body)
        self._configured_heat_modes = []
        # Is solar listed as available equipment?
        if self.coordinator.data["config"][
                "equipment_flags"] & EQUIPMENT.FLAG_SOLAR:
            self._configured_heat_modes.extend(
                [HEAT_MODE.SOLAR, HEAT_MODE.SOLAR_PREFERRED])
        self._configured_heat_modes.append(HEAT_MODE.HEATER)
        self._last_preset = None

    @property
    def name(self) -> str:
        """Name of the heater."""
        ent_name = self.body["heat_status"]["name"]
        return f"{self.gateway_name} {ent_name}"

    @property
    def min_temp(self) -> float:
        """Minimum allowed temperature."""
        return self.body["min_set_point"]["value"]

    @property
    def max_temp(self) -> float:
        """Maximum allowed temperature."""
        return self.body["max_set_point"]["value"]

    @property
    def current_temperature(self) -> float:
        """Return water temperature."""
        return self.body["last_temperature"]["value"]

    @property
    def target_temperature(self) -> float:
        """Target temperature."""
        return self.body["heat_set_point"]["value"]

    @property
    def temperature_unit(self) -> str:
        """Return the unit of measurement."""
        if self.config_data["is_celsius"]["value"] == 1:
            return TEMP_CELSIUS
        return TEMP_FAHRENHEIT

    @property
    def hvac_mode(self) -> str:
        """Return the current hvac mode."""
        if self.body["heat_mode"]["value"] > 0:
            return HVAC_MODE_HEAT
        return HVAC_MODE_OFF

    @property
    def hvac_modes(self):
        """Return th supported hvac modes."""
        return SUPPORTED_MODES

    @property
    def hvac_action(self) -> str:
        """Return the current action of the heater."""
        if self.body["heat_status"]["value"] > 0:
            return CURRENT_HVAC_HEAT
        if self.hvac_mode == HVAC_MODE_HEAT:
            return CURRENT_HVAC_IDLE
        return CURRENT_HVAC_OFF

    @property
    def preset_mode(self) -> str:
        """Return current/last preset mode."""
        if self.hvac_mode == HVAC_MODE_OFF:
            return HEAT_MODE.NAME_FOR_NUM[self._last_preset]
        return HEAT_MODE.NAME_FOR_NUM[self.body["heat_mode"]["value"]]

    @property
    def preset_modes(self):
        """All available presets."""
        return [
            HEAT_MODE.NAME_FOR_NUM[mode_num]
            for mode_num in self._configured_heat_modes
        ]

    async def async_set_temperature(self, **kwargs) -> None:
        """Change the setpoint of the heater."""
        if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
            raise ValueError(f"Expected attribute {ATTR_TEMPERATURE}")

        if await self.gateway.async_set_heat_temp(int(self._data_key),
                                                  int(temperature)):
            await self._async_refresh()
        else:
            raise HomeAssistantError(
                f"Failed to set_temperature {temperature} on body {self.body['body_type']['value']}"
            )
Beispiel #11
0
            if last_image is None:
                await write_to_mjpeg_stream(img_bytes)
            last_image = img_bytes

        await asyncio.sleep(interval)

    return response


def _get_camera_from_entity_id(hass: HomeAssistant, entity_id: str) -> Camera:
    """Get camera component from entity_id."""
    if (component := hass.data.get(DOMAIN)) is None:
        raise HomeAssistantError("Camera integration not set up")

    if (camera := component.get_entity(entity_id)) is None:
        raise HomeAssistantError("Camera not found")

    if not camera.is_on:
        raise HomeAssistantError("Camera is off")

    return cast(Camera, camera)


async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
    """Set up the camera component."""
    component = hass.data[DOMAIN] = EntityComponent(
        _LOGGER, DOMAIN, hass, SCAN_INTERVAL
    )

    prefs = CameraPreferences(hass)
    await prefs.async_initialize()
Beispiel #12
0
 def _getelk(service):
     prefix = service.data["prefix"]
     elk = _find_elk_by_prefix(hass, prefix)
     if elk is None:
         raise HomeAssistantError(f"No ElkM1 with prefix '{prefix}' found")
     return elk
Beispiel #13
0
class MusicCastMediaPlayer(MusicCastDeviceEntity, MediaPlayerEntity):
    """The musiccast media player."""
    def __init__(self, zone_id, name, entry_id, coordinator):
        """Initialize the musiccast device."""
        self._player_state = STATE_PLAYING
        self._volume_muted = False
        self._shuffle = False
        self._zone_id = zone_id

        super().__init__(
            name=name,
            icon="mdi:speaker",
            coordinator=coordinator,
        )

        self._volume_min = self.coordinator.data.zones[
            self._zone_id].min_volume
        self._volume_max = self.coordinator.data.zones[
            self._zone_id].max_volume

        self._cur_track = 0
        self._repeat = REPEAT_MODE_OFF

    async def async_added_to_hass(self):
        """Run when this Entity has been added to HA."""
        await super().async_added_to_hass()
        self.coordinator.entities.append(self)
        # Sensors should also register callbacks to HA when their state changes
        self.coordinator.musiccast.register_group_update_callback(
            self.update_all_mc_entities)
        self.coordinator.async_add_listener(
            self.async_schedule_check_client_list)

    async def async_will_remove_from_hass(self):
        """Entity being removed from hass."""
        await super().async_will_remove_from_hass()
        self.coordinator.entities.remove(self)
        # The opposite of async_added_to_hass. Remove any registered call backs here.
        self.coordinator.musiccast.remove_group_update_callback(
            self.update_all_mc_entities)
        self.coordinator.async_remove_listener(
            self.async_schedule_check_client_list)

    @property
    def should_poll(self):
        """Push an update after each command."""
        return False

    @property
    def ip_address(self):
        """Return the ip address of the musiccast device."""
        return self.coordinator.musiccast.ip

    @property
    def zone_id(self):
        """Return the zone id of the musiccast device."""
        return self._zone_id

    @property
    def _is_netusb(self):
        return (self.coordinator.data.netusb_input ==
                self.coordinator.data.zones[self._zone_id].input)

    @property
    def _is_tuner(self):
        return self.coordinator.data.zones[self._zone_id].input == "tuner"

    @property
    def media_content_id(self):
        """Return the content ID of current playing media."""
        return None

    @property
    def media_content_type(self):
        """Return the content type of current playing media."""
        return MEDIA_TYPE_MUSIC

    @property
    def state(self):
        """Return the state of the player."""
        if self.coordinator.data.zones[self._zone_id].power == "on":
            if self._is_netusb and self.coordinator.data.netusb_playback == "pause":
                return STATE_PAUSED
            if self._is_netusb and self.coordinator.data.netusb_playback == "stop":
                return STATE_IDLE
            return STATE_PLAYING
        return STATE_OFF

    @property
    def volume_level(self):
        """Return the volume level of the media player (0..1)."""
        if ZoneFeature.VOLUME in self.coordinator.data.zones[
                self._zone_id].features:
            volume = self.coordinator.data.zones[self._zone_id].current_volume
            return (volume - self._volume_min) / (self._volume_max -
                                                  self._volume_min)
        return None

    @property
    def is_volume_muted(self):
        """Return boolean if volume is currently muted."""
        if ZoneFeature.VOLUME in self.coordinator.data.zones[
                self._zone_id].features:
            return self.coordinator.data.zones[self._zone_id].mute
        return None

    @property
    def shuffle(self):
        """Boolean if shuffling is enabled."""
        return (self.coordinator.data.netusb_shuffle == "on"
                if self._is_netusb else False)

    @property
    def sound_mode(self):
        """Return the current sound mode."""
        return self.coordinator.data.zones[self._zone_id].sound_program

    @property
    def sound_mode_list(self):
        """Return a list of available sound modes."""
        return self.coordinator.data.zones[self._zone_id].sound_program_list

    @property
    def zone(self):
        """Return the zone of the media player."""
        return self._zone_id

    @property
    def unique_id(self) -> str:
        """Return the unique ID for this media_player."""
        return f"{self.coordinator.data.device_id}_{self._zone_id}"

    async def async_turn_on(self):
        """Turn the media player on."""
        await self.coordinator.musiccast.turn_on(self._zone_id)
        self.async_write_ha_state()

    async def async_turn_off(self):
        """Turn the media player off."""
        await self.coordinator.musiccast.turn_off(self._zone_id)
        self.async_write_ha_state()

    async def async_mute_volume(self, mute):
        """Mute the volume."""

        await self.coordinator.musiccast.mute_volume(self._zone_id, mute)
        self.async_write_ha_state()

    async def async_set_volume_level(self, volume):
        """Set the volume level, range 0..1."""
        await self.coordinator.musiccast.set_volume_level(
            self._zone_id, volume)
        self.async_write_ha_state()

    async def async_volume_up(self):
        """Turn volume up for media player."""
        await self.coordinator.musiccast.volume_up(self._zone_id)

    async def async_volume_down(self):
        """Turn volume down for media player."""
        await self.coordinator.musiccast.volume_down(self._zone_id)

    async def async_media_play(self):
        """Send play command."""
        if self._is_netusb:
            await self.coordinator.musiccast.netusb_play()
        else:
            raise HomeAssistantError(
                "Service play is not supported for non NetUSB sources.")

    async def async_media_pause(self):
        """Send pause command."""
        if self._is_netusb:
            await self.coordinator.musiccast.netusb_pause()
        else:
            raise HomeAssistantError(
                "Service pause is not supported for non NetUSB sources.")

    async def async_media_stop(self):
        """Send stop command."""
        if self._is_netusb:
            await self.coordinator.musiccast.netusb_stop()
        else:
            raise HomeAssistantError(
                "Service stop is not supported for non NetUSB sources.")

    async def async_set_shuffle(self, shuffle):
        """Enable/disable shuffle mode."""
        if self._is_netusb:
            await self.coordinator.musiccast.netusb_shuffle(shuffle)
        else:
            raise HomeAssistantError(
                "Service shuffle is not supported for non NetUSB sources.")

    async def async_play_media(self, media_type: str, media_id: str,
                               **kwargs) -> None:
        """Play media."""
        if media_source.is_media_source_id(media_id):
            play_item = await media_source.async_resolve_media(
                self.hass, media_id)
            media_id = play_item.url

        if self.state == STATE_OFF:
            await self.async_turn_on()

        if media_id:
            parts = media_id.split(":")

            if parts[0] == "list":
                if (index := parts[3]) == "-1":
                    index = "0"

                await self.coordinator.musiccast.play_list_media(
                    index, self._zone_id)
                return

            if parts[0] == "presets":
                index = parts[1]
                await self.coordinator.musiccast.recall_netusb_preset(
                    self._zone_id, index)
                return

            if parts[0] in ("http", "https") or media_id.startswith("/"):
                media_id = async_process_play_media_url(self.hass, media_id)

                await self.coordinator.musiccast.play_url_media(
                    self._zone_id, media_id, "HomeAssistant")
                return

        raise HomeAssistantError(
            "Only presets, media from media browser and http URLs are supported"
        )
Beispiel #14
0
    async def async_browse_media(self,
                                 media_content_type=None,
                                 media_content_id=None):
        """Implement the websocket media browsing helper."""
        if media_content_id and media_source.is_media_source_id(
                media_content_id):
            return await media_source.async_browse_media(
                self.hass,
                media_content_id,
                content_filter=lambda item: item.media_content_type.startswith(
                    "audio/"),
            )

        if self.state == STATE_OFF:
            raise HomeAssistantError(
                "The device has to be turned on to be able to browse media.")

        if media_content_id:
            media_content_path = media_content_id.split(":")
            media_content_provider = await MusicCastMediaContent.browse_media(
                self.coordinator.musiccast, self._zone_id, media_content_path,
                24)
            add_media_source = False

        else:
            media_content_provider = MusicCastMediaContent.categories(
                self.coordinator.musiccast, self._zone_id)
            add_media_source = True

        def get_content_type(item):
            if item.can_play:
                return MEDIA_CLASS_TRACK
            return MEDIA_CLASS_DIRECTORY

        children = [
            BrowseMedia(
                title=child.title,
                media_class=MEDIA_CLASS_MAPPING.get(child.content_type),
                media_content_id=child.content_id,
                media_content_type=get_content_type(child),
                can_play=child.can_play,
                can_expand=child.can_browse,
                thumbnail=child.thumbnail,
            ) for child in media_content_provider.children
        ]

        if add_media_source:
            with contextlib.suppress(media_source.BrowseError):
                item = await media_source.async_browse_media(
                    self.hass,
                    None,
                    content_filter=lambda item: item.media_content_type.
                    startswith("audio/"),
                )
                # If domain is None, it's overview of available sources
                if item.domain is None:
                    children.extend(item.children)
                else:
                    children.append(item)

        overview = BrowseMedia(
            title=media_content_provider.title,
            media_class=MEDIA_CLASS_MAPPING.get(
                media_content_provider.content_type),
            media_content_id=media_content_provider.content_id,
            media_content_type=get_content_type(media_content_provider),
            can_play=False,
            can_expand=media_content_provider.can_browse,
            children=children,
        )

        return overview
Beispiel #15
0
 async def async_delete(self):
     """Delete config."""
     # pylint: disable=no-self-use
     raise HomeAssistantError("Not supported")
Beispiel #16
0
 async def async_set_color_mode(service_call: ServiceCall):
     if not (screenlogic_entry_ids := await
             extract_screenlogic_config_entry_ids(service_call)):
         raise HomeAssistantError(
             f"Failed to call service '{SERVICE_SET_COLOR_MODE}'. Config entry for target not found"
         )
Beispiel #17
0
class TestComponentsCore(unittest.TestCase):
    """Test homeassistant.components module."""

    # pylint: disable=invalid-name
    def setUp(self):
        """Set up things to be run when tests are started."""
        self.hass = get_test_home_assistant()
        assert asyncio.run_coroutine_threadsafe(
            async_setup_component(self.hass, "homeassistant", {}),
            self.hass.loop).result()

        self.hass.states.set("light.Bowl", STATE_ON)
        self.hass.states.set("light.Ceiling", STATE_OFF)

    # pylint: disable=invalid-name
    def tearDown(self):
        """Stop everything that was started."""
        self.hass.stop()

    def test_is_on(self):
        """Test is_on method."""
        assert comps.is_on(self.hass, "light.Bowl")
        assert not comps.is_on(self.hass, "light.Ceiling")
        assert comps.is_on(self.hass)
        assert not comps.is_on(self.hass, "non_existing.entity")

    def test_turn_on_without_entities(self):
        """Test turn_on method without entities."""
        calls = mock_service(self.hass, "light", SERVICE_TURN_ON)
        turn_on(self.hass)
        self.hass.block_till_done()
        assert 0 == len(calls)

    def test_turn_on(self):
        """Test turn_on method."""
        calls = mock_service(self.hass, "light", SERVICE_TURN_ON)
        turn_on(self.hass, "light.Ceiling")
        self.hass.block_till_done()
        assert 1 == len(calls)

    def test_turn_off(self):
        """Test turn_off method."""
        calls = mock_service(self.hass, "light", SERVICE_TURN_OFF)
        turn_off(self.hass, "light.Bowl")
        self.hass.block_till_done()
        assert 1 == len(calls)

    def test_toggle(self):
        """Test toggle method."""
        calls = mock_service(self.hass, "light", SERVICE_TOGGLE)
        toggle(self.hass, "light.Bowl")
        self.hass.block_till_done()
        assert 1 == len(calls)

    @patch("homeassistant.config.os.path.isfile", Mock(return_value=True))
    def test_reload_core_conf(self):
        """Test reload core conf service."""
        ent = entity.Entity()
        ent.entity_id = "test.entity"
        ent.hass = self.hass
        ent.schedule_update_ha_state()
        self.hass.block_till_done()

        state = self.hass.states.get("test.entity")
        assert state is not None
        assert state.state == "unknown"
        assert state.attributes == {}

        files = {
            config.YAML_CONFIG_FILE:
            yaml.dump({
                ha.DOMAIN: {
                    "latitude": 10,
                    "longitude": 20,
                    "customize": {
                        "test.Entity": {
                            "hello": "world"
                        }
                    },
                }
            })
        }
        with patch_yaml_files(files, True):
            reload_core_config(self.hass)
            self.hass.block_till_done()

        assert self.hass.config.latitude == 10
        assert self.hass.config.longitude == 20

        ent.schedule_update_ha_state()
        self.hass.block_till_done()

        state = self.hass.states.get("test.entity")
        assert state is not None
        assert state.state == "unknown"
        assert state.attributes.get("hello") == "world"

    @patch("homeassistant.config.os.path.isfile", Mock(return_value=True))
    @patch("homeassistant.components.homeassistant._LOGGER.error")
    @patch("homeassistant.config.async_process_ha_core_config")
    def test_reload_core_with_wrong_conf(self, mock_process, mock_error):
        """Test reload core conf service."""
        files = {config.YAML_CONFIG_FILE: yaml.dump(["invalid", "config"])}
        with patch_yaml_files(files, True):
            reload_core_config(self.hass)
            self.hass.block_till_done()

        assert mock_error.called
        assert mock_process.called is False

    @patch("homeassistant.core.HomeAssistant.async_stop", return_value=None)
    def test_stop_homeassistant(self, mock_stop):
        """Test stop service."""
        stop(self.hass)
        self.hass.block_till_done()
        assert mock_stop.called

    @patch("homeassistant.core.HomeAssistant.async_stop", return_value=None)
    @patch("homeassistant.config.async_check_ha_config_file",
           return_value=None)
    def test_restart_homeassistant(self, mock_check, mock_restart):
        """Test stop service."""
        restart(self.hass)
        self.hass.block_till_done()
        assert mock_restart.called
        assert mock_check.called

    @patch("homeassistant.core.HomeAssistant.async_stop", return_value=None)
    @patch(
        "homeassistant.config.async_check_ha_config_file",
        side_effect=HomeAssistantError("Test error"),
    )
    def test_restart_homeassistant_wrong_conf(self, mock_check, mock_restart):
        """Test stop service."""
        restart(self.hass)
        self.hass.block_till_done()
        assert mock_check.called
        assert not mock_restart.called

    @patch("homeassistant.core.HomeAssistant.async_stop", return_value=None)
    @patch("homeassistant.config.async_check_ha_config_file",
           return_value=None)
    def test_check_config(self, mock_check, mock_stop):
        """Test stop service."""
        check_config(self.hass)
        self.hass.block_till_done()
        assert mock_check.called
        assert not mock_stop.called
Beispiel #18
0
 def alarm_disarm(self, code=None):
     """Send disarm command."""
     if self._client.disarm(self._location_id) is not True:
         raise HomeAssistantError(
             f"TotalConnect failed to disarm {self._name}.")
    async def _async_add_entity(self, entity, update_before_add,
                                entity_registry, device_registry):
        """Add an entity to the platform."""
        if entity is None:
            raise ValueError('Entity cannot be None')

        entity.hass = self.hass
        entity.platform = self

        # Async entity
        # PARALLEL_UPDATE == None: entity.parallel_updates = None
        # PARALLEL_UPDATE == 0:    entity.parallel_updates = None
        # PARALLEL_UPDATE > 0:     entity.parallel_updates = Semaphore(p)
        # Sync entity
        # PARALLEL_UPDATE == None: entity.parallel_updates = Semaphore(1)
        # PARALLEL_UPDATE == 0:    entity.parallel_updates = None
        # PARALLEL_UPDATE > 0:     entity.parallel_updates = Semaphore(p)
        if hasattr(entity, 'async_update') and not self.parallel_updates:
            entity.parallel_updates = None
        elif (not hasattr(entity, 'async_update')
              and self.parallel_updates == 0):
            entity.parallel_updates = None
        else:
            entity.parallel_updates = self._get_parallel_updates_semaphore()

        # Update properties before we generate the entity_id
        if update_before_add:
            try:
                await entity.async_device_update(warning=False)
            except Exception:  # pylint: disable=broad-except
                self.logger.exception("%s: Error on device update!",
                                      self.platform_name)
                return

        suggested_object_id = None

        # Get entity_id from unique ID registration
        if entity.unique_id is not None:
            if entity.entity_id is not None:
                suggested_object_id = split_entity_id(entity.entity_id)[1]
            else:
                suggested_object_id = entity.name

            if self.entity_namespace is not None:
                suggested_object_id = '{} {}'.format(self.entity_namespace,
                                                     suggested_object_id)

            if self.config_entry is not None:
                config_entry_id = self.config_entry.entry_id
            else:
                config_entry_id = None

            device_info = entity.device_info
            device_id = None

            if config_entry_id is not None and device_info is not None:
                processed_dev_info = {'config_entry_id': config_entry_id}
                for key in (
                        'connections',
                        'identifiers',
                        'manufacturer',
                        'model',
                        'name',
                        'sw_version',
                        'via_device',
                ):
                    if key in device_info:
                        processed_dev_info[key] = device_info[key]

                device = device_registry.async_get_or_create(
                    **processed_dev_info)
                if device:
                    device_id = device.id

            entry = entity_registry.async_get_or_create(
                self.domain,
                self.platform_name,
                entity.unique_id,
                suggested_object_id=suggested_object_id,
                config_entry_id=config_entry_id,
                device_id=device_id,
                known_object_ids=self.entities.keys())

            if entry.disabled:
                self.logger.info(
                    "Not adding entity %s because it's disabled", entry.name
                    or entity.name
                    or '"{} {}"'.format(self.platform_name, entity.unique_id))
                return

            entity.registry_entry = entry
            entity.entity_id = entry.entity_id

        # We won't generate an entity ID if the platform has already set one
        # We will however make sure that platform cannot pick a registered ID
        elif (entity.entity_id is not None
              and entity_registry.async_is_registered(entity.entity_id)):
            # If entity already registered, convert entity id to suggestion
            suggested_object_id = split_entity_id(entity.entity_id)[1]
            entity.entity_id = None

        # Generate entity ID
        if entity.entity_id is None:
            suggested_object_id = \
                suggested_object_id or entity.name or DEVICE_DEFAULT_NAME

            if self.entity_namespace is not None:
                suggested_object_id = '{} {}'.format(self.entity_namespace,
                                                     suggested_object_id)
            entity.entity_id = entity_registry.async_generate_entity_id(
                self.domain, suggested_object_id, self.entities.keys())

        # Make sure it is valid in case an entity set the value themselves
        if not valid_entity_id(entity.entity_id):
            raise HomeAssistantError('Invalid entity id: {}'.format(
                entity.entity_id))
        if (entity.entity_id in self.entities or entity.entity_id
                in self.hass.states.async_entity_ids(self.domain)):
            msg = 'Entity id already exists: {}'.format(entity.entity_id)
            if entity.unique_id is not None:
                msg += '. Platform {} does not generate unique IDs'.format(
                    self.platform_name)
            raise HomeAssistantError(msg)

        entity_id = entity.entity_id
        self.entities[entity_id] = entity
        entity.async_on_remove(lambda: self.entities.pop(entity_id))

        await entity.async_internal_added_to_hass()
        await entity.async_added_to_hass()

        await entity.async_update_ha_state()
Beispiel #20
0
 def alarm_arm_night(self, code=None):
     """Send arm night command."""
     if self._client.arm_stay_night(self._location_id) is not True:
         raise HomeAssistantError(
             f"TotalConnect failed to arm night {self._name}.")
Beispiel #21
0
 def ha_error_call(_):
     raise HomeAssistantError("error_message")
Beispiel #22
0
class SpotifyCastDevice:
    """Represents a spotify device."""

    hass = None
    castDevice = None
    spotifyController = None

    def __init__(self, hass, call_device_name, call_entity_id):
        """Initialize a spotify cast device."""
        self.hass = hass

        # Get device name from either device_name or entity_id
        device_name = None
        if call_device_name is None:
            entity_id = call_entity_id
            if entity_id is None:
                raise HomeAssistantError(
                    "Either entity_id or device_name must be specified"
                )
            entity_states = hass.states.get(entity_id)
            if entity_states is None:
                _LOGGER.error("Could not find entity_id: %s", entity_id)
            else:
                device_name = entity_states.attributes.get("friendly_name")
        else:
            device_name = call_device_name

        if device_name is None or device_name.strip() == "":
            raise HomeAssistantError("device_name is empty")

        # Find chromecast device
        self.castDevice = self.getChromecastDevice(device_name)
        _LOGGER.debug("Found cast device: %s", self.castDevice)
        self.castDevice.wait()

    def getChromecastDevice(self, device_name):
        # Get cast from discovered devices of cast platform
        known_devices = get_cast_devices(self.hass)

        _LOGGER.debug("Chromecast devices: %s", known_devices)

        cast_info = next(
            (
                castinfo
                for castinfo in known_devices
                if castinfo.friendly_name == device_name
            ),
            None,
        )

        _LOGGER.debug("cast info: %s", cast_info)

        if cast_info:
            return pychromecast.get_chromecast_from_cast_info(
                cast_info, ChromeCastZeroconf.get_zeroconf()
            )
        _LOGGER.error(
            "Could not find device %s from hass.data",
            device_name,
        )

        raise HomeAssistantError(
            "Could not find device with name {}".format(device_name)
        )

    def startSpotifyController(self, access_token, expires):
        sp = SpotifyController(access_token, expires)
        self.castDevice.register_handler(sp)
        sp.launch_app()

        if not sp.is_launched and not sp.credential_error:
            raise HomeAssistantError(
                "Failed to launch spotify controller due to timeout"
            )
        if not sp.is_launched and sp.credential_error:
            raise HomeAssistantError(
                "Failed to launch spotify controller due to credentials error"
            )

        self.spotifyController = sp

    def getSpotifyDeviceId(self, devices_available):
        # Look for device to make sure we can start playback
        _LOGGER.debug(
            "devices_available: %s %s", devices_available, self.spotifyController.device
        )

        if devices := devices_available["devices"]:
            for device in devices:
                if device["id"] == self.spotifyController.device:
                    return device["id"]

        _LOGGER.error(
            'No device with id "{}" known by Spotify'.format(
                self.spotifyController.device
            )
        )
        _LOGGER.error("Known devices: {}".format(devices))

        raise HomeAssistantError("Failed to get device id from Spotify")
Beispiel #23
0
        content_type = DOMAIN

    plex_server_name = content.pop("plex_server", None)
    plex_server = get_plex_server(hass, plex_server_name)

    if playqueue_id := content.pop("playqueue_id", None):
        try:
            playqueue = plex_server.get_playqueue(playqueue_id)
        except NotFound as err:
            raise HomeAssistantError(
                f"PlayQueue '{playqueue_id}' could not be found") from err
    else:
        shuffle = content.pop("shuffle", 0)
        media = plex_server.lookup_media(content_type, **content)
        if media is None:
            raise HomeAssistantError(
                f"Plex media not found using payload: '{content_id}'")
        playqueue = plex_server.create_playqueue(media, shuffle=shuffle)

    return (playqueue, plex_server)


def play_on_sonos(hass, content_type, content_id, speaker_name):
    """Play music on a connected Sonos speaker using Plex APIs.

    Called by Sonos 'media_player.play_media' service.
    """
    media, plex_server = lookup_plex_media(hass, content_type, content_id)
    try:
        sonos_speaker = plex_server.account.sonos_speaker(speaker_name)
    except BadRequest as exc:
        raise HomeAssistantError(
    async def async_set_fan_mode(self, fan_mode: str) -> None:
        """Set new target fan mode."""
        if "fanLevel" not in self.device_data.active_features:
            raise HomeAssistantError("Current mode doesn't support setting Fanlevel")

        await self._async_set_ac_state_property("fanLevel", fan_mode)
Beispiel #25
0
 def _async_has_action_or_raise(self, action: str) -> None:
     """Raise HomeAssistantError if the device does not support an action."""
     if not self._device.has_action(action):
         raise HomeAssistantError(
             f"{self.entity_id} does not support {action}")
    async def async_set_swing_mode(self, swing_mode: str) -> None:
        """Set new target swing operation."""
        if "swing" not in self.device_data.active_features:
            raise HomeAssistantError("Current mode doesn't support setting Swing")

        await self._async_set_ac_state_property("swing", swing_mode)
Beispiel #27
0
def async_prepare_call_from_config(
    hass: HomeAssistantType,
    config: ConfigType,
    variables: TemplateVarsType = None,
    validate_config: bool = False,
) -> ServiceParams:
    """Prepare to call a service based on a config hash."""
    if validate_config:
        try:
            config = cv.SERVICE_SCHEMA(config)
        except vol.Invalid as ex:
            raise HomeAssistantError(
                f"Invalid config for calling service: {ex}") from ex

    if CONF_SERVICE in config:
        domain_service = config[CONF_SERVICE]
    else:
        domain_service = config[CONF_SERVICE_TEMPLATE]

    if isinstance(domain_service, template.Template):
        try:
            domain_service.hass = hass
            domain_service = domain_service.async_render(variables)
            domain_service = cv.service(domain_service)
        except TemplateError as ex:
            raise HomeAssistantError(
                f"Error rendering service name template: {ex}") from ex
        except vol.Invalid as ex:
            raise HomeAssistantError(
                f"Template rendered invalid service: {domain_service}") from ex

    domain, service = domain_service.split(".", 1)

    target = {}
    if CONF_TARGET in config:
        conf = config.get(CONF_TARGET)
        try:
            template.attach(hass, conf)
            target.update(template.render_complex(conf, variables))
            if CONF_ENTITY_ID in target:
                target[CONF_ENTITY_ID] = cv.comp_entity_ids(
                    target[CONF_ENTITY_ID])
        except TemplateError as ex:
            raise HomeAssistantError(
                f"Error rendering service target template: {ex}") from ex
        except vol.Invalid as ex:
            raise HomeAssistantError(
                f"Template rendered invalid entity IDs: {target[CONF_ENTITY_ID]}"
            ) from ex

    service_data = {}

    for conf in [CONF_SERVICE_DATA, CONF_SERVICE_DATA_TEMPLATE]:
        if conf not in config:
            continue
        try:
            template.attach(hass, config[conf])
            service_data.update(
                template.render_complex(config[conf], variables))
        except TemplateError as ex:
            raise HomeAssistantError(
                f"Error rendering data template: {ex}") from ex

    if CONF_SERVICE_ENTITY_ID in config:
        if target:
            target[ATTR_ENTITY_ID] = config[CONF_SERVICE_ENTITY_ID]
        else:
            target = {ATTR_ENTITY_ID: config[CONF_SERVICE_ENTITY_ID]}

    return {
        "domain": domain,
        "service": service,
        "service_data": service_data,
        "target": target,
    }
Beispiel #28
0
 async def async_save(self, config):
     """Save config."""
     # pylint: disable=no-self-use
     raise HomeAssistantError("Not supported")
Beispiel #29
0
    async def async_setup_platform(p_type, p_config=None, discovery_info=None):
        """Set up a notify platform."""
        if p_config is None:
            p_config = {}

        platform = await async_prepare_setup_platform(hass, config, DOMAIN,
                                                      p_type)

        if platform is None:
            _LOGGER.error("Unknown notification service specified")
            return

        _LOGGER.info("Setting up %s.%s", DOMAIN, p_type)
        notify_service = None
        try:
            if hasattr(platform, 'async_get_service'):
                notify_service = await \
                    platform.async_get_service(hass, p_config, discovery_info)
            elif hasattr(platform, 'get_service'):
                notify_service = await hass.async_add_job(
                    platform.get_service, hass, p_config, discovery_info)
            else:
                raise HomeAssistantError("Invalid notify platform.")

            if notify_service is None:
                # Platforms can decide not to create a service based
                # on discovery data.
                if discovery_info is None:
                    _LOGGER.error(
                        "Failed to initialize notification service %s", p_type)
                return

        except Exception:  # pylint: disable=broad-except
            _LOGGER.exception('Error setting up platform %s', p_type)
            return

        notify_service.hass = hass

        if discovery_info is None:
            discovery_info = {}

        async def async_notify_message(service):
            """Handle sending notification message service calls."""
            kwargs = {}
            message = service.data[ATTR_MESSAGE]
            title = service.data.get(ATTR_TITLE)

            if title:
                title.hass = hass
                kwargs[ATTR_TITLE] = title.async_render()

            if targets.get(service.service) is not None:
                kwargs[ATTR_TARGET] = [targets[service.service]]
            elif service.data.get(ATTR_TARGET) is not None:
                kwargs[ATTR_TARGET] = service.data.get(ATTR_TARGET)

            message.hass = hass
            kwargs[ATTR_MESSAGE] = message.async_render()
            kwargs[ATTR_DATA] = service.data.get(ATTR_DATA)

            await notify_service.async_send_message(**kwargs)

        if hasattr(notify_service, 'targets'):
            platform_name = (p_config.get(CONF_NAME)
                             or discovery_info.get(CONF_NAME) or p_type)
            for name, target in notify_service.targets.items():
                target_name = slugify('{}_{}'.format(platform_name, name))
                targets[target_name] = target
                hass.services.async_register(DOMAIN,
                                             target_name,
                                             async_notify_message,
                                             schema=NOTIFY_SERVICE_SCHEMA)

        platform_name = (p_config.get(CONF_NAME)
                         or discovery_info.get(CONF_NAME) or SERVICE_NOTIFY)
        platform_name_slug = slugify(platform_name)

        hass.services.async_register(DOMAIN,
                                     platform_name_slug,
                                     async_notify_message,
                                     schema=NOTIFY_SERVICE_SCHEMA)

        hass.config.components.add('{}.{}'.format(DOMAIN, p_type))

        return True
    async def async_update(self):  # pylint: disable=r0912,r0915
        """Update the sensor state."""
        wdata = self._hass.states.get(self._weather_entity)

        if wdata is None:
            raise HomeAssistantError(
                f"Unable to find an entity called {self._weather_entity}")

        tmpu = self._hass.config.units.temperature_unit
        temp = wdata.attributes.get(ATTR_WEATHER_TEMPERATURE)
        cond = wdata.state
        forecast = wdata.attributes.get(ATTR_FORECAST)

        if forecast is None:
            raise HomeAssistantError(
                "Can't get forecast data! Are you sure it's the weather provider?"
            )

        _LOGGER.debug("Current temperature %s, condition '%s'", temp, cond)

        temp = self._temp2c(temp, tmpu)

        if cond in BAD_CONDITIONS:
            _LOGGER.debug("Detected bad weather condition")
            self._state = False
            return

        cur_date = datetime.now().strftime("%F")
        stop_date = datetime.fromtimestamp(datetime.now().timestamp() + 86400 *
                                           (self._days + 1)).strftime("%F")

        _LOGGER.debug("Inspect weather forecast from now till %s", stop_date)
        for fcast in forecast:
            fc_date = fcast.get(ATTR_FORECAST_TIME)
            if isinstance(fc_date, int):
                fc_date = dt_util.as_local(
                    datetime.utcfromtimestamp(fc_date / 1000)).isoformat()
            elif isinstance(fc_date, datetime):
                fc_date = dt_util.as_local(fc_date).isoformat()
            fc_date = fc_date[:10]
            if fc_date < cur_date:
                continue
            if fc_date == stop_date:
                break
            _LOGGER.debug("Inspect weather forecast for %s", fc_date)

            prec = fcast.get(ATTR_FORECAST_PRECIPITATION)
            cond = fcast.get(ATTR_FORECAST_CONDITION)
            tmin = fcast.get(ATTR_FORECAST_TEMP_LOW)
            tmax = fcast.get(ATTR_FORECAST_TEMP)
            _LOGGER.debug(
                "Precipitation %s, Condition '%s',"
                " Min temperature: %s, Max temperature %s",
                prec,
                cond,
                tmin,
                tmax,
            )

            if prec:
                _LOGGER.debug("Precipitation detected")
                self._state = False
                return
            if cond in BAD_CONDITIONS:
                _LOGGER.debug("Detected bad weather condition")
                self._state = False
                return
            if tmin is not None and fc_date != cur_date:
                tmin = self._temp2c(tmin, tmpu)
                if temp < 0 <= tmin:
                    _LOGGER.debug(
                        "Detected passage of temperature through melting point"
                    )
                    self._state = False
                    return
                temp = tmin
            if tmax is not None:
                tmax = self._temp2c(tmax, tmpu)
                if temp < 0 <= tmax:
                    _LOGGER.debug(
                        "Detected passage of temperature through melting point"
                    )
                    self._state = False
                    return
                temp = tmax

        _LOGGER.debug("Inspection done. No bad forecast detected")
        self._state = True