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)
}) @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(
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"
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()
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(
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")
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
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']}" )
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()
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
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" )
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
async def async_delete(self): """Delete config.""" # pylint: disable=no-self-use raise HomeAssistantError("Not supported")
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" )
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
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()
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}.")
def ha_error_call(_): raise HomeAssistantError("error_message")
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")
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)
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)
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, }
async def async_save(self, config): """Save config.""" # pylint: disable=no-self-use raise HomeAssistantError("Not supported")
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