async def get(self, request): """Get discovery information.""" hass = request.app["hass"] uuid = await hass.helpers.instance_id.async_get() system_info = await async_get_system_info(hass) data = { ATTR_UUID: uuid, ATTR_BASE_URL: None, ATTR_EXTERNAL_URL: None, ATTR_INTERNAL_URL: None, ATTR_LOCATION_NAME: hass.config.location_name, ATTR_INSTALLATION_TYPE: system_info[ATTR_INSTALLATION_TYPE], # always needs authentication ATTR_REQUIRES_API_PASSWORD: True, ATTR_VERSION: __version__, } with suppress(NoURLAvailableError): data["external_url"] = get_url(hass, allow_internal=False) with suppress(NoURLAvailableError): data["internal_url"] = get_url(hass, allow_external=False) # Set old base URL based on external or internal data["base_url"] = data["external_url"] or data["internal_url"] return self.json(data)
async def async_getAppImageUrl(self, hass, appTitle): """Check app image is present.""" if appTitle == self._lastAppTitle: return self._appImageUrl self._appImageUrl = None appImageUrl = APP_IMAGE_URL_BASE.format(appTitle.casefold()) websession = async_get_clientsession(hass) base_url = get_url(hass) request_url = base_url + appImageUrl certok = False if self._use_internal: certok = await self._async_check_for_image(websession, appTitle, appImageUrl, request_url) if not self._use_internal or not certok: self._use_internal = False base_url = get_url(hass, allow_internal=False) request_url = base_url + appImageUrl certok = await self._async_check_for_image(websession, appTitle, request_url, request_url) return self._appImageUrl
async def async_getAppImageUrl(self, hass, appTitle): """Check app image is present.""" if appTitle == self._lastAppTitle: return self._appImageUrl self._appImageUrl = None appImageUrl = APP_IMAGE_URL_BASE + f"/{appTitle.casefold()}.png" websession = async_get_clientsession(hass) base_url = get_url(hass) request_url = base_url + appImageUrl certok = False if self._use_internal: certok = await self._async_check_for_image(websession, appTitle, appImageUrl, request_url) if not self._use_internal or not certok: try: base_url = get_url(hass, allow_internal=False) self._use_internal = False request_url = base_url + appImageUrl certok = await self._async_check_for_image( websession, appTitle, request_url, request_url) except NoURLAvailableError: _LOGGER.error( "E0010U - Base URL not found, have you configured them in HA General Settings?" ) return self._appImageUrl
def _register_hass_zc_service(hass, zeroconf, uuid): # Get instance UUID valid_location_name = _truncate_location_name_to_valid( hass.config.location_name) params = { "location_name": valid_location_name, "uuid": uuid, "version": __version__, "external_url": "", "internal_url": "", # Old base URL, for backward compatibility "base_url": "", # Always needs authentication "requires_api_password": True, } # Get instance URL's try: params["external_url"] = get_url(hass, allow_internal=False) except NoURLAvailableError: pass try: params["internal_url"] = get_url(hass, allow_external=False) except NoURLAvailableError: pass # Set old base URL based on external or internal params["base_url"] = params["external_url"] or params["internal_url"] host_ip = util.get_local_ip() try: host_ip_pton = socket.inet_pton(socket.AF_INET, host_ip) except OSError: host_ip_pton = socket.inet_pton(socket.AF_INET6, host_ip) _suppress_invalid_properties(params) info = ServiceInfo( ZEROCONF_TYPE, name=f"{valid_location_name}.{ZEROCONF_TYPE}", server=f"{uuid}.local.", addresses=[host_ip_pton], port=hass.http.server_port, properties=params, ) _LOGGER.info("Starting Zeroconf broadcast") try: zeroconf.register_service(info) except NonUniqueNameException: _LOGGER.error( "Home Assistant instance with identical name present in the local network" )
async def _async_register_hass_zc_service( hass: HomeAssistant, aio_zc: HaAsyncZeroconf, uuid: str ) -> None: # Get instance UUID valid_location_name = _truncate_location_name_to_valid( hass.config.location_name or "Home" ) params = { "location_name": valid_location_name, "uuid": uuid, "version": __version__, "external_url": "", "internal_url": "", # Old base URL, for backward compatibility "base_url": "", # Always needs authentication "requires_api_password": True, } # Get instance URL's with suppress(NoURLAvailableError): params["external_url"] = get_url(hass, allow_internal=False) with suppress(NoURLAvailableError): params["internal_url"] = get_url(hass, allow_external=False) # Set old base URL based on external or internal params["base_url"] = params["external_url"] or params["internal_url"] adapters = await network.async_get_adapters(hass) # Puts the default IPv4 address first in the list to preserve compatibility, # because some mDNS implementations ignores anything but the first announced address. host_ip = await async_get_source_ip(hass, target_ip=MDNS_TARGET_IP) host_ip_pton = None if host_ip: host_ip_pton = socket.inet_pton(socket.AF_INET, host_ip) address_list = _get_announced_addresses(adapters, host_ip_pton) _suppress_invalid_properties(params) info = AsyncServiceInfo( ZEROCONF_TYPE, name=f"{valid_location_name}.{ZEROCONF_TYPE}", server=f"{uuid}.local.", addresses=address_list, port=hass.http.server_port, properties=params, ) _LOGGER.info("Starting Zeroconf broadcast") await aio_zc.async_register_service(info, allow_name_change=True)
def new_media_status(self, media_status): """Handle updates of the media status.""" if (media_status and media_status.player_is_idle and media_status.idle_reason == "ERROR"): external_url = None internal_url = None tts_base_url = None url_description = "" if "tts" in self.hass.config.components: try: tts_base_url = self.hass.components.tts.get_base_url( self.hass) except KeyError: # base_url not configured, ignore pass try: external_url = get_url(self.hass, allow_internal=False) except NoURLAvailableError: # external_url not configured, ignore pass try: internal_url = get_url(self.hass, allow_external=False) except NoURLAvailableError: # internal_url not configured, ignore pass if media_status.content_id: if tts_base_url and media_status.content_id.startswith( tts_base_url): url_description = f" from tts.base_url ({tts_base_url})" if external_url and media_status.content_id.startswith( external_url): url_description = f" from external_url ({external_url})" if internal_url and media_status.content_id.startswith( internal_url): url_description = f" from internal_url ({internal_url})" _LOGGER.error( "Failed to cast media %s%s. Please make sure the URL is: " "Reachable from the cast device and either a publicly resolvable " "hostname or an IP address", media_status.content_id, url_description, ) self.media_status = media_status self.media_status_received = dt_util.utcnow() self.schedule_update_ha_state()
async def set_state(self, data: RequestData, state: dict[str, Any]) -> dict[str, Any] | None: entity_id = self.state.entity_id stream = await self._async_request_stream(entity_id) if self._config.use_cloud_stream: cloud_stream = self.hass.data[DOMAIN][CLOUD_STREAMS].get(entity_id) if not cloud_stream: cloud_stream = CloudStream(self.hass, stream, async_get_clientsession(self.hass)) self.hass.data[DOMAIN][CLOUD_STREAMS][entity_id] = cloud_stream await cloud_stream.start() stream_url = cloud_stream.stream_url else: try: external_url = network.get_url(self.hass, allow_internal=False) except network.NoURLAvailableError: raise SmartHomeError( ERR_NOT_SUPPORTED_IN_CURRENT_MODE, 'Unable to get Home Assistant external URL. Have you set external URLs in Configuration -> General?' ) endpoint_url = stream.endpoint_url(VIDEO_STREAM_FORMAT) stream_url = f'{external_url}{endpoint_url}' return {'stream_url': stream_url, 'protocol': 'hls'}
async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia: """Resolve media to a url.""" parsed = URL(item.identifier) if "message" not in parsed.query: raise Unresolvable("No message specified.") options = dict(parsed.query) kwargs: dict[str, Any] = { "engine": parsed.name, "message": options.pop("message"), "language": options.pop("language", None), "options": options, } if "cache" in options: kwargs["cache"] = options.pop("cache") == "true" manager: SpeechManager = self.hass.data[DOMAIN] try: url = await manager.async_get_url_path(**kwargs) except HomeAssistantError as err: raise Unresolvable(str(err)) from err mime_type = mimetypes.guess_type(url)[0] or "audio/mpeg" if manager.base_url and manager.base_url != get_url(self.hass): url = f"{manager.base_url}{url}" return PlayMedia(url, mime_type)
def setup(hass, config): # 没有配置和已经运行则不操作 if DOMAIN not in config or DOMAIN in hass.data: return True # 默认使用自定义的外部链接 base_url = get_url(hass).strip('/') # 显示插件信息 _LOGGER.info(''' ------------------------------------------------------------------- QQ邮箱通知插件【作者QQ:635147515】 版本:''' + VERSION + ''' API地址:''' + URL + ''' HA地址:''' + base_url + ''' 项目地址:https://github.com/shaonianzhentan/ha_qqmail -------------------------------------------------------------------''') # 读取配置 cfg = config[DOMAIN] _qq = str(cfg.get('qq')) + '@qq.com' _code = cfg.get('code') # 定义QQ邮箱实例 qm = QQMail(hass, _qq, _code, base_url + URL) hass.data[DOMAIN] = qm # 设置QQ邮箱通知服务 if hass.services.has_service(DOMAIN, 'notify') == False: hass.services.async_register(DOMAIN, 'notify', qm.notify) # 注册静态目录 hass.http.register_static_path(ROOT_PATH, hass.config.path('custom_components/' + DOMAIN + '/local'), False) # 注册事件网关 hass.http.register_view(HassGateView) return True
async def subscribe(self, event_id): """Subscribe to motion events and set the webhook as callback.""" self._event_id = event_id self._webhook_id = await self.register_webhook() self._webhook_url = "{}{}".format( get_url(self._hass, prefer_external=False), self._hass.components.webhook.async_generate_path( self._webhook_id), ) self._sman = Manager(self._host, self._port, self._username, self._password) if await self._sman.subscribe(self._webhook_url): _LOGGER.info( "Host %s subscribed successfully to webhook %s", self._host, self._webhook_url, ) await self.set_available(True) else: _LOGGER.error( "Host %s subscription failed to its webhook, base object state will be set to NotAvailable", self._host, ) await self.set_available(False) return True
async def async_play_media(self, media_type: str, media_id: str, **kwargs) -> None: """Play media from a URL or file, launch an application, or tune to a channel.""" extra: dict[str, Any] = kwargs.get(ATTR_MEDIA_EXTRA) or {} # Handle media_source if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media( self.hass, media_id) media_type = MEDIA_TYPE_URL media_id = sourced_media.url # Sign and prefix with URL if playing a relative URL if media_id[0] == "/": media_id = async_sign_path( self.hass, quote(media_id), dt.timedelta(seconds=media_source.DEFAULT_EXPIRY_TIME), ) # prepend external URL hass_url = get_url(self.hass) media_id = f"{hass_url}{media_id}" if media_type not in PLAY_MEDIA_SUPPORTED_TYPES: _LOGGER.error( "Invalid media type %s. Only %s, %s, %s, and camera HLS streams are supported", media_type, MEDIA_TYPE_APP, MEDIA_TYPE_CHANNEL, MEDIA_TYPE_URL, ) return if media_type == MEDIA_TYPE_APP: params = { param: extra[attr] for attr, param in ATTRS_TO_LAUNCH_PARAMS.items() if attr in extra } await self.coordinator.roku.launch(media_id, params) elif media_type == MEDIA_TYPE_CHANNEL: await self.coordinator.roku.tune(media_id) elif media_type == MEDIA_TYPE_URL: params = { param: extra[attr] for (attr, param) in ATTRS_TO_PLAY_VIDEO_PARAMS.items() if attr in extra } await self.coordinator.roku.play_on_roku(media_id, params) elif media_type == FORMAT_CONTENT_TYPE[HLS_PROVIDER]: params = { "MediaType": "hls", } await self.coordinator.roku.play_on_roku(media_id, params) await self.coordinator.async_request_refresh()
async def async_play_media(self, media_type, media_id, **kwargs): """Play a piece of media.""" # Handle media_source if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media( self.hass, media_id) media_type = sourced_media.mime_type media_id = sourced_media.url # If media ID is a relative URL, we serve it from HA. # Create a signed path. if media_id[0] == "/": # Sign URL with Safegate Pro Cast User config_entry_id = self.registry_entry.config_entry_id config_entry = self.hass.config_entries.async_get_entry( config_entry_id) user_id = config_entry.data["user_id"] user = await self.hass.auth.async_get_user(user_id) if user.refresh_tokens: refresh_token: RefreshToken = list( user.refresh_tokens.values())[0] media_id = async_sign_path( self.hass, refresh_token.id, quote(media_id), timedelta(seconds=media_source.DEFAULT_EXPIRY_TIME), ) # prepend external URL hass_url = get_url(self.hass, prefer_external=True) media_id = f"{hass_url}{media_id}" await self.hass.async_add_executor_job( ft.partial(self.play_media, media_type, media_id, **kwargs))
async def sync_serialize(self, agent_user_id): """Serialize entity for a SYNC response. https://developers.google.com/actions/smarthome/create-app#actiondevicessync """ state = self.state entity_config = self.config.entity_config.get(state.entity_id, {}) name = (entity_config.get(CONF_NAME) or state.name).strip() domain = state.domain device_class = state.attributes.get(ATTR_DEVICE_CLASS) entity_entry, device_entry = await _get_entity_and_device( self.hass, state.entity_id ) traits = self.traits() device_type = get_google_type(domain, device_class) device = { "id": state.entity_id, "name": {"name": name}, "attributes": {}, "traits": [trait.name for trait in traits], "willReportState": self.config.should_report_state, "type": device_type, } # use aliases aliases = entity_config.get(CONF_ALIASES) if aliases: device["name"]["nicknames"] = [name] + aliases if self.config.is_local_sdk_active and self.should_expose_local(): device["otherDeviceIds"] = [{"deviceId": self.entity_id}] device["customData"] = { "webhookId": self.config.local_sdk_webhook_id, "httpPort": self.hass.http.server_port, "httpSSL": self.hass.config.api.use_ssl, "uuid": await self.hass.helpers.instance_id.async_get(), "baseUrl": get_url(self.hass, prefer_external=True), "proxyDeviceId": agent_user_id, } for trt in traits: device["attributes"].update(trt.sync_attributes()) room = entity_config.get(CONF_ROOM_HINT) if room: device["roomHint"] = room else: area = await _get_area(self.hass, entity_entry, device_entry) if area and area.name: device["roomHint"] = area.name device_info = await _get_device_info(device_entry) if device_info: device["deviceInfo"] = device_info return device
async def async_play_media(self, media_type, media_id, **kwargs): """Play a piece of media.""" # Handle media_source if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media(self.hass, media_id) media_type = sourced_media.mime_type media_id = sourced_media.url # If media ID is a relative URL, we serve it from HA. # Create a signed path. if media_id[0] == "/": # Sign URL with Home Assistant Cast User config_entries = self.hass.config_entries.async_entries(CAST_DOMAIN) user_id = config_entries[0].data["user_id"] user = await self.hass.auth.async_get_user(user_id) if user.refresh_tokens: refresh_token: RefreshToken = list(user.refresh_tokens.values())[0] media_id = async_sign_path( self.hass, refresh_token.id, media_id, timedelta(minutes=5), ) # prepend external URL hass_url = get_url(self.hass, prefer_external=True) media_id = f"{hass_url}{media_id}" await self.hass.async_add_executor_job( ft.partial(self.play_media, media_type, media_id, **kwargs) )
async def post(self, request: web.Request) -> web.Response: """Generate speech and provide url.""" try: data = await request.json() except ValueError: return self.json_message("Invalid JSON specified", HTTPStatus.BAD_REQUEST) if not data.get(ATTR_PLATFORM) and data.get(ATTR_MESSAGE): return self.json_message( "Must specify platform and message", HTTPStatus.BAD_REQUEST ) p_type = data[ATTR_PLATFORM] message = data[ATTR_MESSAGE] cache = data.get(ATTR_CACHE) language = data.get(ATTR_LANGUAGE) options = data.get(ATTR_OPTIONS) try: path = await self.tts.async_get_url_path( p_type, message, cache=cache, language=language, options=options ) except HomeAssistantError as err: _LOGGER.error("Error on init tts: %s", err) return self.json({"error": err}, HTTPStatus.BAD_REQUEST) base = self.tts.base_url or get_url(self.tts.hass) url = base + path return self.json({"url": url, "path": path})
def get_local_ip_from_internal_url(hass: HomeAssistant): """Get the stream ip address from the internal_url.""" try: url = get_url( hass, allow_internal=True, allow_external=False, allow_cloud=False, allow_ip=True, ) except NoURLAvailableError: LOGGER.warning( "Unable to retrieve the internal URL from Home Assistant, " "this may cause issues resolving the correct internal stream ip. " "Please set a valid internal url in the Home Assistant configuration" ) return hass.config.api.local_ip parsed_uri = urlparse(url) if parsed_uri.netloc == "": return hass.config.api.local_ip try: return socket.gethostbyname(parsed_uri.netloc) except socket.gaierror: # url is set as ip instead of hostname return hass.config.api.local_ip
async def async_say_handle(service): """Service handle for say.""" entity_ids = service.data[ATTR_ENTITY_ID] message = service.data.get(ATTR_MESSAGE) cache = service.data.get(ATTR_CACHE) language = service.data.get(ATTR_LANGUAGE) options = service.data.get(ATTR_OPTIONS) try: url = await tts.async_get_url_path( p_type, message, cache=cache, language=language, options=options ) except HomeAssistantError as err: _LOGGER.error("Error on init TTS: %s", err) return base = tts.base_url or get_url(hass) url = base + url data = { ATTR_MEDIA_CONTENT_ID: url, ATTR_MEDIA_CONTENT_TYPE: MEDIA_TYPE_MUSIC, ATTR_ENTITY_ID: entity_ids, } await hass.services.async_call( DOMAIN_MP, SERVICE_PLAY_MEDIA, data, blocking=True, context=service.context, )
def valid_external_url(hass: HomeAssistant) -> bool: """Return whether a valid external URL for HA is available.""" try: get_url(hass, allow_internal=False, prefer_cloud=True) return True except NoURLAvailableError: _LOGGER.error( "You do not have an external URL for your Home Assistant instance " "configured which is needed to set up the Zoom integration. " "You need to set the `external_url` property in the " "`homeassistant` section of your `configuration.yaml`, or set the " "`External URL` property in the Home Assistant `General " "Configuration` UI, before trying to setup the Zoom integration " "again. You can learn more about configuring this parameter at " "https://www.home-assistant.io/docs/configuration/basic") return False
def get_local_media_list(self, search_type): file_path = '' singer = "默认列表" if search_type != 'library_music': file_path = search_type.replace('library_', '') singer = file_path hass = self.hass children = [] base_url = get_url(hass).strip('/') path = hass.config.path("media/ha_cloud_music") file_path = file_path.replace('library_', '') # 获取所有文件 file_dir = os.path.join(path, file_path) for filename in os.listdir(file_dir): if os.path.isfile(os.path.join(file_dir, filename)) and '.mp3' in filename: songid = f"{base_url}/media-local/" if file_path != '': songid += urllib.parse.quote(f"{file_path}/{filename}") else: songid += urllib.parse.quote(filename) song = filename.replace('.mp3', '') children.append({ "name": song, "song": song, "singer": singer, "album": "媒体库", "image": f"{base_url}/static/icons/favicon-192x192.png", "type": "url", "url": songid }) return children
async def put(self, request): hass = request.app["hass"] query = request.query tts_path = hass.config.path("tts/") try: # 读取文件 reader = await request.multipart() file = await reader.next() filename = f"voice-{uuid1()}.mp3" size = 0 with open(f"{tts_path}{filename}", 'wb') as f: while True: chunk = await file.read_chunk() # 默认是8192个字节。 if not chunk: break size += len(chunk) f.write(chunk) # 播放音频 source = query.get('source', '') if source == '': mp = hass.data[DOMAIN] await mp.api_tts.speak(filename) # 本地URL local_url = get_url(hass).strip('/') + '/tts-local/' + filename return self.json({'code': 0, 'msg': '发送成功', 'data': local_url}) except Exception as e: return self.json({'code': 1, 'msg': '出现异常'})
def get_url(hass): """Gets the required Home-Assistant URL for validation. Args: hass: Hass instance. Returns: Home-Assistant URL. """ if network: try: return network.get_url( hass, allow_external=True, allow_internal=True, allow_ip=True, prefer_external=True, require_ssl=False) except AttributeError: logging.debug( 'Hass version does not have get_url helper, using fall back.') base_url = hass.config.api.base_url if base_url: return base_url raise ValueError('Unable to obtain HASS url.')
async def async_play_media(self, media_type: str, media_id: str, **kwargs: Any) -> None: """Play media from a URL or file.""" # Handle media_source if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media( self.hass, media_id) media_type = MEDIA_TYPE_MUSIC media_id = sourced_media.url # Sign and prefix with URL if playing a relative URL if media_id[0] == "/": media_id = async_sign_path( self.hass, quote(media_id), timedelta(seconds=media_source.DEFAULT_EXPIRY_TIME), ) # prepend external URL hass_url = get_url(self.hass) media_id = f"{hass_url}{media_id}" if media_type != MEDIA_TYPE_MUSIC: LOGGER.error( "Invalid media type %s. Only %s is supported", media_type, MEDIA_TYPE_MUSIC, ) return await self._vlc.add(media_id) self._state = STATE_PLAYING
async def async_api_initialize_camera_stream(hass, config, directive, context): """Process a InitializeCameraStreams request.""" entity = directive.entity stream_source = await camera.async_request_stream(hass, entity.entity_id, fmt="hls") camera_image = hass.states.get(entity.entity_id).attributes["entity_picture"] try: external_url = network.get_url( hass, allow_internal=False, allow_ip=False, require_ssl=True, require_standard_port=True, ) except network.NoURLAvailableError: raise AlexaInvalidValueError("Failed to find suitable URL to serve to Alexa") payload = { "cameraStreams": [ { "uri": f"{external_url}{stream_source}", "protocol": "HLS", "resolution": {"width": 1280, "height": 720}, "authorizationType": "NONE", "videoCodec": "H264", "audioCodec": "AAC", } ], "imageUri": f"{external_url}{camera_image}", } return directive.response( name="Response", namespace="Alexa.CameraStreamController", payload=payload )
def async_generate_motioneye_webhook(hass: HomeAssistant, webhook_id: str) -> str: """Generate the full local URL for a webhook_id.""" return "{}{}".format( get_url(hass, allow_cloud=False), async_generate_path(webhook_id), )
def setup(hass, config): # 显示插件信息 _LOGGER.info(''' ------------------------------------------------------------------- QQ邮箱通知插件【作者QQ:635147515】 版本:''' + VERSION + ''' API地址:''' + URL + ''' 项目地址:https://github.com/shaonianzhentan/ha_qqmail -------------------------------------------------------------------''') # 注册静态目录 local = hass.config.path('custom_components/' + DOMAIN + '/local') if os.path.isdir(local): hass.http.register_static_path(ROOT_PATH, local, False) # 读取配置 cfg = config[DOMAIN] _qq = str(cfg.get('qq')) + '@qq.com' _code = cfg.get('code') base_url = get_url(hass) # 定义QQ邮箱实例 qm = QQMail(hass, _qq, _code, base_url.strip('/') + URL) # 设置QQ邮箱通知服务 if hass.services.has_service(DOMAIN, 'notify') == False: hass.services.register(DOMAIN, 'notify', qm.notify) # 注册事件网关 hass.http.register_view(HassGateView) return True
async def subscribe(self, event_id): """Subscribe to motion events and set the webhook as callback.""" global warnedAboutNoURLAvailableError self._event_id = event_id self._webhook_id = await self.register_webhook() try: self._webhook_url = "{}{}".format( get_url(self._hass, prefer_external=False), self._hass.components.webhook.async_generate_path( self._webhook_id), ) except NoURLAvailableError as ex: if not warnedAboutNoURLAvailableError: warnedAboutNoURLAvailableError = True _LOGGER.warning( "Your are using HTTP for internal URL while using HTTPS for external URL in HA which is" " not supported anymore by HomeAssistant starting 2022.3." "Please change your configuration to use HTTPS for internal URL or disable HTTPS for external." ) try: self._webhook_url = "{}{}".format( get_url(self._hass, prefer_external=True), self._hass.components.webhook.async_generate_path( self._webhook_id), ) except NoURLAvailableError as ex: # If we can't get a URL for external or internal, we will still mark the camara as available await self.set_available(True) return True self._sman = Manager(self._host, self._port, self._username, self._password) if await self._sman.subscribe(self._webhook_url): _LOGGER.info( "Host %s subscribed successfully to webhook %s", self._host, self._webhook_url, ) await self.set_available(True) else: _LOGGER.error( "Host %s subscription failed to its webhook, base object state will be set to NotAvailable", self._host, ) await self.set_available(False) return True
async def async_play_media(self, media_type, media_id, **kwargs): """Play media from media_source.""" if media_source.is_media_source_id(media_id): sourced_media = await media_source.async_resolve_media(self.hass, media_id) media_type = sourced_media.mime_type media_id = sourced_media.url # If media ID is a relative URL, we serve it from HA. if media_id[0] == "/": user = await self.hass.auth.async_get_owner() if user.refresh_tokens: refresh_token: RefreshToken = list(user.refresh_tokens.values())[0] # Use kwargs so it works both before and after the change in Home Assistant 2022.2 media_id = async_sign_path( hass=self.hass, refresh_token_id=refresh_token.id, path=media_id, expiration=timedelta(minutes=5) ) # Prepend external URL. hass_url = get_url(self.hass, allow_internal=True) media_id = f"{hass_url}{media_id}" _LOGGER.info("Meural device %s: Playing media. Media type is %s, previewing image from %s", self.name, media_type, media_id) await self.local_meural.send_postcard(media_id, media_type) # Play gallery (playlist or album) by ID. elif media_type in ['playlist']: _LOGGER.info("Meural device %s: Playing media. Media type is %s, playing gallery %s", self.name, media_type, media_id) await self.local_meural.send_change_gallery(media_id) # "Preview image from URL. elif media_type in [ 'image/jpg', 'image/png', 'image/jpeg' ]: _LOGGER.info("Meural device %s: Playing media. Media type is %s, previewing image from %s", self.name, media_type, media_id) await self.local_meural.send_postcard(media_id, media_type) # Play item (artwork) by ID. Play locally if item is in currently displayed gallery. If not, play using Meural server.""" elif media_type in ['item']: if media_id.isdigit(): currentgallery_id = self._gallery_status["current_gallery"] currentitems = await self.local_meural.send_get_items_by_gallery(currentgallery_id) in_playlist = next((g["title"] for g in currentitems if g["id"] == media_id), None) if in_playlist is None: _LOGGER.info("Meural device %s: Playing media. Item %s is not in current gallery, trying to display via Meural server", self.name, media_id) try: await self.meural.device_load_item(self.meural_device_id, media_id) except: _LOGGER.error("Meural device %s: Playing media. Error while trying to display %s item %s via Meural server", self.name, media_type, media_id, exc_info=True) else: _LOGGER.info("Meural device %s: Playing media. Item %s is in current gallery %s, trying to display via local device", self.name, media_id, self._gallery_status["current_gallery_name"]) await self.local_meural.send_change_item(media_id) else: _LOGGER.error("Meural device %s: Playing media. ID %s is not an item", self.name, media_id) # This is an unsupported media type. else: _LOGGER.error("Meural device %s: Playing media. Does not support displaying this %s media with ID %s", self.name, media_type, media_id)
async def async_setup_platform(hass, config): """Set up the Telegram webhooks platform.""" bot = initialize_bot(config) current_status = await hass.async_add_executor_job(bot.getWebhookInfo) if not (base_url := config.get(CONF_URL)): base_url = get_url(hass, require_ssl=True, allow_internal=False)
def async_process_play_media_url( hass: HomeAssistant, media_content_id: str, *, allow_relative_url: bool = False, for_supervisor_network: bool = False, ) -> str: """Update a media URL with authentication if it points at Home Assistant.""" parsed = yarl.URL(media_content_id) if parsed.scheme and parsed.scheme not in ("http", "https"): return media_content_id if parsed.is_absolute(): if not is_hass_url(hass, media_content_id): return media_content_id else: if media_content_id[0] != "/": raise ValueError("URL is relative, but does not start with a /") if parsed.query: logging.getLogger(__name__).debug( "Not signing path for content with query param" ) elif parsed.path.startswith(PATHS_WITHOUT_AUTH): # We don't sign this path if it doesn't need auth. Although signing itself can't hurt, # some devices are unable to handle long URLs and the auth signature might push it over. pass else: signed_path = async_sign_path( hass, quote(parsed.path), timedelta(seconds=CONTENT_AUTH_EXPIRY_TIME), ) media_content_id = str(parsed.join(yarl.URL(signed_path))) # convert relative URL to absolute URL if not parsed.is_absolute() and not allow_relative_url: base_url = None if for_supervisor_network: base_url = get_supervisor_network_url(hass) if not base_url: try: base_url = get_url(hass) except NoURLAvailableError as err: msg = "Unable to determine Home Assistant URL to send to device" if ( hass.config.api and hass.config.api.use_ssl and (not hass.config.external_url or not hass.config.internal_url) ): msg += ". Configure internal and external URL in general settings." raise HomeAssistantError(msg) from err media_content_id = f"{base_url}{media_content_id}" return media_content_id
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Reolink from a config entry.""" hass.data.setdefault(DOMAIN, {}) base = ReolinkBase( hass, entry.data, entry.options ) base.sync_functions.append(entry.add_update_listener(update_listener)) if not await base.connect_api(): return False webhook_id = await register_webhook(hass, base.event_id) webhook_url = "{}{}".format( get_url(hass, prefer_external=False), hass.components.webhook.async_generate_path(webhook_id) ) await base.subscribe(webhook_url) hass.data[DOMAIN][entry.entry_id] = {BASE: base} async def async_update_data(): """Perform the actual updates.""" async with async_timeout.timeout(base.timeout): await base.renew() await base.update_states() coordinator = DataUpdateCoordinator( hass, _LOGGER, name="reolink", update_method=async_update_data, update_interval=SCAN_INTERVAL, ) # Fetch initial data so we have data when entities subscribe await coordinator.async_refresh() for component in PLATFORMS: hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, component) ) hass.data[DOMAIN][entry.entry_id][COORDINATOR] = coordinator hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, base.stop()) return True