Use httpx in generic camera (#46576)

* Use httpx in generic camera

* Remove commented out code
This commit is contained in:
uvjustin 2021-02-15 23:37:53 +08:00 committed by GitHub
parent c5b9ad83c2
commit 9917bb76fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 63 deletions

View File

@ -2,10 +2,7 @@
import asyncio
import logging
import aiohttp
import async_timeout
import requests
from requests.auth import HTTPDigestAuth
import httpx
import voluptuous as vol
from homeassistant.components.camera import (
@ -25,7 +22,7 @@ from homeassistant.const import (
)
from homeassistant.exceptions import TemplateError
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.httpx_client import get_async_client
from homeassistant.helpers.reload import async_setup_reload_service
from . import DOMAIN, PLATFORMS
@ -39,6 +36,7 @@ CONF_STREAM_SOURCE = "stream_source"
CONF_FRAMERATE = "framerate"
DEFAULT_NAME = "Generic Camera"
GET_IMAGE_TIMEOUT = 10
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
@ -93,9 +91,9 @@ class GenericCamera(Camera):
if username and password:
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
self._auth = HTTPDigestAuth(username, password)
self._auth = httpx.DigestAuth(username, password)
else:
self._auth = aiohttp.BasicAuth(username, password=password)
self._auth = httpx.BasicAuth(username, password=password)
else:
self._auth = None
@ -129,40 +127,19 @@ class GenericCamera(Camera):
if url == self._last_url and self._limit_refetch:
return self._last_image
# aiohttp don't support DigestAuth yet
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
def fetch():
"""Read image from a URL."""
try:
response = requests.get(
url, timeout=10, auth=self._auth, verify=self.verify_ssl
)
return response.content
except requests.exceptions.RequestException as error:
_LOGGER.error(
"Error getting new camera image from %s: %s", self._name, error
)
return self._last_image
self._last_image = await self.hass.async_add_executor_job(fetch)
# async
else:
try:
websession = async_get_clientsession(
self.hass, verify_ssl=self.verify_ssl
)
with async_timeout.timeout(10):
response = await websession.get(url, auth=self._auth)
self._last_image = await response.read()
except asyncio.TimeoutError:
_LOGGER.error("Timeout getting camera image from %s", self._name)
return self._last_image
except aiohttp.ClientError as err:
_LOGGER.error(
"Error getting new camera image from %s: %s", self._name, err
)
return self._last_image
try:
async_client = get_async_client(self.hass, verify_ssl=self.verify_ssl)
response = await async_client.get(
url, auth=self._auth, timeout=GET_IMAGE_TIMEOUT
)
response.raise_for_status()
self._last_image = response.content
except httpx.TimeoutException:
_LOGGER.error("Timeout getting camera image from %s", self._name)
return self._last_image
except (httpx.RequestError, httpx.HTTPStatusError) as err:
_LOGGER.error("Error getting new camera image from %s: %s", self._name, err)
return self._last_image
self._last_url = url
return self._last_image

View File

@ -3,6 +3,8 @@ import asyncio
from os import path
from unittest.mock import patch
import respx
from homeassistant import config as hass_config
from homeassistant.components.generic import DOMAIN
from homeassistant.components.websocket_api.const import TYPE_RESULT
@ -14,9 +16,10 @@ from homeassistant.const import (
from homeassistant.setup import async_setup_component
async def test_fetching_url(aioclient_mock, hass, hass_client):
@respx.mock
async def test_fetching_url(hass, hass_client):
"""Test that it fetches the given url."""
aioclient_mock.get("http://example.com", text="hello world")
respx.get("http://example.com").respond(text="hello world")
await async_setup_component(
hass,
@ -38,12 +41,12 @@ async def test_fetching_url(aioclient_mock, hass, hass_client):
resp = await client.get("/api/camera_proxy/camera.config_test")
assert resp.status == 200
assert aioclient_mock.call_count == 1
assert respx.calls.call_count == 1
body = await resp.text()
assert body == "hello world"
resp = await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 2
assert respx.calls.call_count == 2
async def test_fetching_without_verify_ssl(aioclient_mock, hass, hass_client):
@ -100,12 +103,13 @@ async def test_fetching_url_with_verify_ssl(aioclient_mock, hass, hass_client):
assert resp.status == 200
async def test_limit_refetch(aioclient_mock, hass, hass_client):
@respx.mock
async def test_limit_refetch(hass, hass_client):
"""Test that it fetches the given url."""
aioclient_mock.get("http://example.com/5a", text="hello world")
aioclient_mock.get("http://example.com/10a", text="hello world")
aioclient_mock.get("http://example.com/15a", text="hello planet")
aioclient_mock.get("http://example.com/20a", status=HTTP_NOT_FOUND)
respx.get("http://example.com/5a").respond(text="hello world")
respx.get("http://example.com/10a").respond(text="hello world")
respx.get("http://example.com/15a").respond(text="hello planet")
respx.get("http://example.com/20a").respond(status_code=HTTP_NOT_FOUND)
await async_setup_component(
hass,
@ -129,19 +133,19 @@ async def test_limit_refetch(aioclient_mock, hass, hass_client):
with patch("async_timeout.timeout", side_effect=asyncio.TimeoutError()):
resp = await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 0
assert respx.calls.call_count == 0
assert resp.status == HTTP_INTERNAL_SERVER_ERROR
hass.states.async_set("sensor.temp", "10")
resp = await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 1
assert respx.calls.call_count == 1
assert resp.status == 200
body = await resp.text()
assert body == "hello world"
resp = await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 1
assert respx.calls.call_count == 1
assert resp.status == 200
body = await resp.text()
assert body == "hello world"
@ -150,7 +154,7 @@ async def test_limit_refetch(aioclient_mock, hass, hass_client):
# Url change = fetch new image
resp = await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 2
assert respx.calls.call_count == 2
assert resp.status == 200
body = await resp.text()
assert body == "hello planet"
@ -158,7 +162,7 @@ async def test_limit_refetch(aioclient_mock, hass, hass_client):
# Cause a template render error
hass.states.async_remove("sensor.temp")
resp = await client.get("/api/camera_proxy/camera.config_test")
assert aioclient_mock.call_count == 2
assert respx.calls.call_count == 2
assert resp.status == 200
body = await resp.text()
assert body == "hello planet"
@ -285,11 +289,12 @@ async def test_no_stream_source(aioclient_mock, hass, hass_client, hass_ws_clien
}
async def test_camera_content_type(aioclient_mock, hass, hass_client):
@respx.mock
async def test_camera_content_type(hass, hass_client):
"""Test generic camera with custom content_type."""
svg_image = "<some image>"
urlsvg = "https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg"
aioclient_mock.get(urlsvg, text=svg_image)
respx.get(urlsvg).respond(text=svg_image)
cam_config_svg = {
"name": "config_test_svg",
@ -309,23 +314,24 @@ async def test_camera_content_type(aioclient_mock, hass, hass_client):
client = await hass_client()
resp_1 = await client.get("/api/camera_proxy/camera.config_test_svg")
assert aioclient_mock.call_count == 1
assert respx.calls.call_count == 1
assert resp_1.status == 200
assert resp_1.content_type == "image/svg+xml"
body = await resp_1.text()
assert body == svg_image
resp_2 = await client.get("/api/camera_proxy/camera.config_test_jpg")
assert aioclient_mock.call_count == 2
assert respx.calls.call_count == 2
assert resp_2.status == 200
assert resp_2.content_type == "image/jpeg"
body = await resp_2.text()
assert body == svg_image
async def test_reloading(aioclient_mock, hass, hass_client):
@respx.mock
async def test_reloading(hass, hass_client):
"""Test we can cleanly reload."""
aioclient_mock.get("http://example.com", text="hello world")
respx.get("http://example.com").respond(text="hello world")
await async_setup_component(
hass,
@ -347,7 +353,7 @@ async def test_reloading(aioclient_mock, hass, hass_client):
resp = await client.get("/api/camera_proxy/camera.config_test")
assert resp.status == 200
assert aioclient_mock.call_count == 1
assert respx.calls.call_count == 1
body = await resp.text()
assert body == "hello world"
@ -374,7 +380,7 @@ async def test_reloading(aioclient_mock, hass, hass_client):
resp = await client.get("/api/camera_proxy/camera.reload")
assert resp.status == 200
assert aioclient_mock.call_count == 2
assert respx.calls.call_count == 2
body = await resp.text()
assert body == "hello world"