mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 01:38:02 +00:00
Use httpx in generic camera (#46576)
* Use httpx in generic camera * Remove commented out code
This commit is contained in:
parent
c5b9ad83c2
commit
9917bb76fb
@ -2,10 +2,7 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import aiohttp
|
import httpx
|
||||||
import async_timeout
|
|
||||||
import requests
|
|
||||||
from requests.auth import HTTPDigestAuth
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.camera import (
|
from homeassistant.components.camera import (
|
||||||
@ -25,7 +22,7 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.exceptions import TemplateError
|
from homeassistant.exceptions import TemplateError
|
||||||
from homeassistant.helpers import config_validation as cv
|
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 homeassistant.helpers.reload import async_setup_reload_service
|
||||||
|
|
||||||
from . import DOMAIN, PLATFORMS
|
from . import DOMAIN, PLATFORMS
|
||||||
@ -39,6 +36,7 @@ CONF_STREAM_SOURCE = "stream_source"
|
|||||||
CONF_FRAMERATE = "framerate"
|
CONF_FRAMERATE = "framerate"
|
||||||
|
|
||||||
DEFAULT_NAME = "Generic Camera"
|
DEFAULT_NAME = "Generic Camera"
|
||||||
|
GET_IMAGE_TIMEOUT = 10
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
@ -93,9 +91,9 @@ class GenericCamera(Camera):
|
|||||||
|
|
||||||
if username and password:
|
if username and password:
|
||||||
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
|
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
|
||||||
self._auth = HTTPDigestAuth(username, password)
|
self._auth = httpx.DigestAuth(username, password)
|
||||||
else:
|
else:
|
||||||
self._auth = aiohttp.BasicAuth(username, password=password)
|
self._auth = httpx.BasicAuth(username, password=password)
|
||||||
else:
|
else:
|
||||||
self._auth = None
|
self._auth = None
|
||||||
|
|
||||||
@ -129,40 +127,19 @@ class GenericCamera(Camera):
|
|||||||
if url == self._last_url and self._limit_refetch:
|
if url == self._last_url and self._limit_refetch:
|
||||||
return self._last_image
|
return self._last_image
|
||||||
|
|
||||||
# aiohttp don't support DigestAuth yet
|
try:
|
||||||
if self._authentication == HTTP_DIGEST_AUTHENTICATION:
|
async_client = get_async_client(self.hass, verify_ssl=self.verify_ssl)
|
||||||
|
response = await async_client.get(
|
||||||
def fetch():
|
url, auth=self._auth, timeout=GET_IMAGE_TIMEOUT
|
||||||
"""Read image from a URL."""
|
)
|
||||||
try:
|
response.raise_for_status()
|
||||||
response = requests.get(
|
self._last_image = response.content
|
||||||
url, timeout=10, auth=self._auth, verify=self.verify_ssl
|
except httpx.TimeoutException:
|
||||||
)
|
_LOGGER.error("Timeout getting camera image from %s", self._name)
|
||||||
return response.content
|
return self._last_image
|
||||||
except requests.exceptions.RequestException as error:
|
except (httpx.RequestError, httpx.HTTPStatusError) as err:
|
||||||
_LOGGER.error(
|
_LOGGER.error("Error getting new camera image from %s: %s", self._name, err)
|
||||||
"Error getting new camera image from %s: %s", self._name, error
|
return self._last_image
|
||||||
)
|
|
||||||
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
|
|
||||||
|
|
||||||
self._last_url = url
|
self._last_url = url
|
||||||
return self._last_image
|
return self._last_image
|
||||||
|
@ -3,6 +3,8 @@ import asyncio
|
|||||||
from os import path
|
from os import path
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import respx
|
||||||
|
|
||||||
from homeassistant import config as hass_config
|
from homeassistant import config as hass_config
|
||||||
from homeassistant.components.generic import DOMAIN
|
from homeassistant.components.generic import DOMAIN
|
||||||
from homeassistant.components.websocket_api.const import TYPE_RESULT
|
from homeassistant.components.websocket_api.const import TYPE_RESULT
|
||||||
@ -14,9 +16,10 @@ from homeassistant.const import (
|
|||||||
from homeassistant.setup import async_setup_component
|
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."""
|
"""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(
|
await async_setup_component(
|
||||||
hass,
|
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")
|
resp = await client.get("/api/camera_proxy/camera.config_test")
|
||||||
|
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
assert aioclient_mock.call_count == 1
|
assert respx.calls.call_count == 1
|
||||||
body = await resp.text()
|
body = await resp.text()
|
||||||
assert body == "hello world"
|
assert body == "hello world"
|
||||||
|
|
||||||
resp = await client.get("/api/camera_proxy/camera.config_test")
|
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):
|
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
|
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."""
|
"""Test that it fetches the given url."""
|
||||||
aioclient_mock.get("http://example.com/5a", text="hello world")
|
respx.get("http://example.com/5a").respond(text="hello world")
|
||||||
aioclient_mock.get("http://example.com/10a", text="hello world")
|
respx.get("http://example.com/10a").respond(text="hello world")
|
||||||
aioclient_mock.get("http://example.com/15a", text="hello planet")
|
respx.get("http://example.com/15a").respond(text="hello planet")
|
||||||
aioclient_mock.get("http://example.com/20a", status=HTTP_NOT_FOUND)
|
respx.get("http://example.com/20a").respond(status_code=HTTP_NOT_FOUND)
|
||||||
|
|
||||||
await async_setup_component(
|
await async_setup_component(
|
||||||
hass,
|
hass,
|
||||||
@ -129,19 +133,19 @@ async def test_limit_refetch(aioclient_mock, hass, hass_client):
|
|||||||
|
|
||||||
with patch("async_timeout.timeout", side_effect=asyncio.TimeoutError()):
|
with patch("async_timeout.timeout", side_effect=asyncio.TimeoutError()):
|
||||||
resp = await client.get("/api/camera_proxy/camera.config_test")
|
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
|
assert resp.status == HTTP_INTERNAL_SERVER_ERROR
|
||||||
|
|
||||||
hass.states.async_set("sensor.temp", "10")
|
hass.states.async_set("sensor.temp", "10")
|
||||||
|
|
||||||
resp = await client.get("/api/camera_proxy/camera.config_test")
|
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
|
assert resp.status == 200
|
||||||
body = await resp.text()
|
body = await resp.text()
|
||||||
assert body == "hello world"
|
assert body == "hello world"
|
||||||
|
|
||||||
resp = await client.get("/api/camera_proxy/camera.config_test")
|
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
|
assert resp.status == 200
|
||||||
body = await resp.text()
|
body = await resp.text()
|
||||||
assert body == "hello world"
|
assert body == "hello world"
|
||||||
@ -150,7 +154,7 @@ async def test_limit_refetch(aioclient_mock, hass, hass_client):
|
|||||||
|
|
||||||
# Url change = fetch new image
|
# Url change = fetch new image
|
||||||
resp = await client.get("/api/camera_proxy/camera.config_test")
|
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
|
assert resp.status == 200
|
||||||
body = await resp.text()
|
body = await resp.text()
|
||||||
assert body == "hello planet"
|
assert body == "hello planet"
|
||||||
@ -158,7 +162,7 @@ async def test_limit_refetch(aioclient_mock, hass, hass_client):
|
|||||||
# Cause a template render error
|
# Cause a template render error
|
||||||
hass.states.async_remove("sensor.temp")
|
hass.states.async_remove("sensor.temp")
|
||||||
resp = await client.get("/api/camera_proxy/camera.config_test")
|
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
|
assert resp.status == 200
|
||||||
body = await resp.text()
|
body = await resp.text()
|
||||||
assert body == "hello planet"
|
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."""
|
"""Test generic camera with custom content_type."""
|
||||||
svg_image = "<some image>"
|
svg_image = "<some image>"
|
||||||
urlsvg = "https://upload.wikimedia.org/wikipedia/commons/0/02/SVG_logo.svg"
|
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 = {
|
cam_config_svg = {
|
||||||
"name": "config_test_svg",
|
"name": "config_test_svg",
|
||||||
@ -309,23 +314,24 @@ async def test_camera_content_type(aioclient_mock, hass, hass_client):
|
|||||||
client = await hass_client()
|
client = await hass_client()
|
||||||
|
|
||||||
resp_1 = await client.get("/api/camera_proxy/camera.config_test_svg")
|
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.status == 200
|
||||||
assert resp_1.content_type == "image/svg+xml"
|
assert resp_1.content_type == "image/svg+xml"
|
||||||
body = await resp_1.text()
|
body = await resp_1.text()
|
||||||
assert body == svg_image
|
assert body == svg_image
|
||||||
|
|
||||||
resp_2 = await client.get("/api/camera_proxy/camera.config_test_jpg")
|
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.status == 200
|
||||||
assert resp_2.content_type == "image/jpeg"
|
assert resp_2.content_type == "image/jpeg"
|
||||||
body = await resp_2.text()
|
body = await resp_2.text()
|
||||||
assert body == svg_image
|
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."""
|
"""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(
|
await async_setup_component(
|
||||||
hass,
|
hass,
|
||||||
@ -347,7 +353,7 @@ async def test_reloading(aioclient_mock, hass, hass_client):
|
|||||||
resp = await client.get("/api/camera_proxy/camera.config_test")
|
resp = await client.get("/api/camera_proxy/camera.config_test")
|
||||||
|
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
assert aioclient_mock.call_count == 1
|
assert respx.calls.call_count == 1
|
||||||
body = await resp.text()
|
body = await resp.text()
|
||||||
assert body == "hello world"
|
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")
|
resp = await client.get("/api/camera_proxy/camera.reload")
|
||||||
|
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
assert aioclient_mock.call_count == 2
|
assert respx.calls.call_count == 2
|
||||||
body = await resp.text()
|
body = await resp.text()
|
||||||
assert body == "hello world"
|
assert body == "hello world"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user