mirror of
https://github.com/home-assistant/core.git
synced 2025-04-26 02:07:54 +00:00
UniFi: Add UDM/P (UniFi OS) support (#33766)
* Fix get_controller and all tests:wq * Bump dependency to v16
This commit is contained in:
parent
2d1002d40d
commit
15ab63a4c2
@ -327,6 +327,7 @@ async def get_controller(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
with async_timeout.timeout(10):
|
with async_timeout.timeout(10):
|
||||||
|
await controller.check_unifi_os()
|
||||||
await controller.login()
|
await controller.login()
|
||||||
return controller
|
return controller
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Ubiquiti UniFi",
|
"name": "Ubiquiti UniFi",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/unifi",
|
"documentation": "https://www.home-assistant.io/integrations/unifi",
|
||||||
"requirements": ["aiounifi==15"],
|
"requirements": ["aiounifi==16"],
|
||||||
"codeowners": ["@kane610"],
|
"codeowners": ["@kane610"],
|
||||||
"quality_scale": "platinum"
|
"quality_scale": "platinum"
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ aiopylgtv==0.3.3
|
|||||||
aioswitcher==1.1.0
|
aioswitcher==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.unifi
|
# homeassistant.components.unifi
|
||||||
aiounifi==15
|
aiounifi==16
|
||||||
|
|
||||||
# homeassistant.components.wwlln
|
# homeassistant.components.wwlln
|
||||||
aiowwlln==2.0.2
|
aiowwlln==2.0.2
|
||||||
|
@ -94,7 +94,7 @@ aiopylgtv==0.3.3
|
|||||||
aioswitcher==1.1.0
|
aioswitcher==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.unifi
|
# homeassistant.components.unifi
|
||||||
aiounifi==15
|
aiounifi==16
|
||||||
|
|
||||||
# homeassistant.components.wwlln
|
# homeassistant.components.wwlln
|
||||||
aiowwlln==2.0.2
|
aiowwlln==2.0.2
|
||||||
|
@ -52,6 +52,8 @@ async def test_flow_works(hass, aioclient_mock, mock_discovery):
|
|||||||
CONF_VERIFY_SSL: False,
|
CONF_VERIFY_SSL: False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aioclient_mock.get("https://1.2.3.4:1234", status=302)
|
||||||
|
|
||||||
aioclient_mock.post(
|
aioclient_mock.post(
|
||||||
"https://1.2.3.4:1234/api/login",
|
"https://1.2.3.4:1234/api/login",
|
||||||
json={"data": "login successful", "meta": {"rc": "ok"}},
|
json={"data": "login successful", "meta": {"rc": "ok"}},
|
||||||
@ -101,6 +103,8 @@ async def test_flow_works_multiple_sites(hass, aioclient_mock):
|
|||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
aioclient_mock.get("https://1.2.3.4:1234", status=302)
|
||||||
|
|
||||||
aioclient_mock.post(
|
aioclient_mock.post(
|
||||||
"https://1.2.3.4:1234/api/login",
|
"https://1.2.3.4:1234/api/login",
|
||||||
json={"data": "login successful", "meta": {"rc": "ok"}},
|
json={"data": "login successful", "meta": {"rc": "ok"}},
|
||||||
@ -150,6 +154,8 @@ async def test_flow_fails_site_already_configured(hass, aioclient_mock):
|
|||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
aioclient_mock.get("https://1.2.3.4:1234", status=302)
|
||||||
|
|
||||||
aioclient_mock.post(
|
aioclient_mock.post(
|
||||||
"https://1.2.3.4:1234/api/login",
|
"https://1.2.3.4:1234/api/login",
|
||||||
json={"data": "login successful", "meta": {"rc": "ok"}},
|
json={"data": "login successful", "meta": {"rc": "ok"}},
|
||||||
@ -188,6 +194,8 @@ async def test_flow_fails_user_credentials_faulty(hass, aioclient_mock):
|
|||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
aioclient_mock.get("https://1.2.3.4:1234", status=302)
|
||||||
|
|
||||||
with patch("aiounifi.Controller.login", side_effect=aiounifi.errors.Unauthorized):
|
with patch("aiounifi.Controller.login", side_effect=aiounifi.errors.Unauthorized):
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
@ -213,6 +221,8 @@ async def test_flow_fails_controller_unavailable(hass, aioclient_mock):
|
|||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
aioclient_mock.get("https://1.2.3.4:1234", status=302)
|
||||||
|
|
||||||
with patch("aiounifi.Controller.login", side_effect=aiounifi.errors.RequestError):
|
with patch("aiounifi.Controller.login", side_effect=aiounifi.errors.RequestError):
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
@ -238,6 +248,8 @@ async def test_flow_fails_unknown_problem(hass, aioclient_mock):
|
|||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
|
||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
|
|
||||||
|
aioclient_mock.get("https://1.2.3.4:1234", status=302)
|
||||||
|
|
||||||
with patch("aiounifi.Controller.login", side_effect=Exception):
|
with patch("aiounifi.Controller.login", side_effect=Exception):
|
||||||
result = await hass.config_entries.flow.async_configure(
|
result = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"],
|
result["flow_id"],
|
||||||
|
@ -4,7 +4,7 @@ from copy import deepcopy
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
import aiounifi
|
import aiounifi
|
||||||
from asynctest import Mock, patch
|
from asynctest import patch
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import unifi
|
from homeassistant.components import unifi
|
||||||
@ -68,11 +68,7 @@ async def setup_unifi_integration(
|
|||||||
controllers=None,
|
controllers=None,
|
||||||
):
|
):
|
||||||
"""Create the UniFi controller."""
|
"""Create the UniFi controller."""
|
||||||
configuration = {}
|
assert await async_setup_component(hass, unifi.DOMAIN, {})
|
||||||
if controllers:
|
|
||||||
configuration = {unifi.DOMAIN: {unifi.CONF_CONTROLLERS: controllers}}
|
|
||||||
|
|
||||||
assert await async_setup_component(hass, unifi.DOMAIN, configuration)
|
|
||||||
|
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=unifi.DOMAIN,
|
domain=unifi.DOMAIN,
|
||||||
@ -108,20 +104,21 @@ async def setup_unifi_integration(
|
|||||||
async def mock_request(self, method, path, json=None):
|
async def mock_request(self, method, path, json=None):
|
||||||
mock_requests.append({"method": method, "path": path, "json": json})
|
mock_requests.append({"method": method, "path": path, "json": json})
|
||||||
|
|
||||||
if path == "s/{site}/stat/sta" and mock_client_responses:
|
if path == "/stat/sta" and mock_client_responses:
|
||||||
return mock_client_responses.popleft()
|
return mock_client_responses.popleft()
|
||||||
if path == "s/{site}/stat/device" and mock_device_responses:
|
if path == "/stat/device" and mock_device_responses:
|
||||||
return mock_device_responses.popleft()
|
return mock_device_responses.popleft()
|
||||||
if path == "s/{site}/rest/user" and mock_client_all_responses:
|
if path == "/rest/user" and mock_client_all_responses:
|
||||||
return mock_client_all_responses.popleft()
|
return mock_client_all_responses.popleft()
|
||||||
if path == "s/{site}/rest/wlanconf" and mock_wlans_responses:
|
if path == "/rest/wlanconf" and mock_wlans_responses:
|
||||||
return mock_wlans_responses.popleft()
|
return mock_wlans_responses.popleft()
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
# "aiounifi.Controller.start_websocket", return_value=True
|
with patch("aiounifi.Controller.check_unifi_os", return_value=True), patch(
|
||||||
with patch("aiounifi.Controller.login", return_value=True), patch(
|
"aiounifi.Controller.login", return_value=True,
|
||||||
"aiounifi.Controller.sites", return_value=sites
|
), patch("aiounifi.Controller.sites", return_value=sites), patch(
|
||||||
), patch("aiounifi.Controller.request", new=mock_request), patch.object(
|
"aiounifi.Controller.request", new=mock_request
|
||||||
|
), patch.object(
|
||||||
aiounifi.websocket.WSClient, "start", return_value=True
|
aiounifi.websocket.WSClient, "start", return_value=True
|
||||||
):
|
):
|
||||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
@ -244,7 +241,9 @@ async def test_wireless_client_event_calls_update_wireless_devices(hass):
|
|||||||
|
|
||||||
async def test_get_controller(hass):
|
async def test_get_controller(hass):
|
||||||
"""Successful call."""
|
"""Successful call."""
|
||||||
with patch("aiounifi.Controller.login", return_value=Mock()):
|
with patch("aiounifi.Controller.check_unifi_os", return_value=True), patch(
|
||||||
|
"aiounifi.Controller.login", return_value=True
|
||||||
|
):
|
||||||
assert await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
|
assert await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
|
||||||
|
|
||||||
|
|
||||||
@ -252,13 +251,15 @@ async def test_get_controller_verify_ssl_false(hass):
|
|||||||
"""Successful call with verify ssl set to false."""
|
"""Successful call with verify ssl set to false."""
|
||||||
controller_data = dict(CONTROLLER_DATA)
|
controller_data = dict(CONTROLLER_DATA)
|
||||||
controller_data[CONF_VERIFY_SSL] = False
|
controller_data[CONF_VERIFY_SSL] = False
|
||||||
with patch("aiounifi.Controller.login", return_value=Mock()):
|
with patch("aiounifi.Controller.check_unifi_os", return_value=True), patch(
|
||||||
|
"aiounifi.Controller.login", return_value=True
|
||||||
|
):
|
||||||
assert await unifi.controller.get_controller(hass, **controller_data)
|
assert await unifi.controller.get_controller(hass, **controller_data)
|
||||||
|
|
||||||
|
|
||||||
async def test_get_controller_login_failed(hass):
|
async def test_get_controller_login_failed(hass):
|
||||||
"""Check that get_controller can handle a failed login."""
|
"""Check that get_controller can handle a failed login."""
|
||||||
with patch(
|
with patch("aiounifi.Controller.check_unifi_os", return_value=True), patch(
|
||||||
"aiounifi.Controller.login", side_effect=aiounifi.Unauthorized
|
"aiounifi.Controller.login", side_effect=aiounifi.Unauthorized
|
||||||
), pytest.raises(unifi.errors.AuthenticationRequired):
|
), pytest.raises(unifi.errors.AuthenticationRequired):
|
||||||
await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
|
await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
|
||||||
@ -266,7 +267,7 @@ async def test_get_controller_login_failed(hass):
|
|||||||
|
|
||||||
async def test_get_controller_controller_unavailable(hass):
|
async def test_get_controller_controller_unavailable(hass):
|
||||||
"""Check that get_controller can handle controller being unavailable."""
|
"""Check that get_controller can handle controller being unavailable."""
|
||||||
with patch(
|
with patch("aiounifi.Controller.check_unifi_os", return_value=True), patch(
|
||||||
"aiounifi.Controller.login", side_effect=aiounifi.RequestError
|
"aiounifi.Controller.login", side_effect=aiounifi.RequestError
|
||||||
), pytest.raises(unifi.errors.CannotConnect):
|
), pytest.raises(unifi.errors.CannotConnect):
|
||||||
await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
|
await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
|
||||||
@ -274,7 +275,7 @@ async def test_get_controller_controller_unavailable(hass):
|
|||||||
|
|
||||||
async def test_get_controller_unknown_error(hass):
|
async def test_get_controller_unknown_error(hass):
|
||||||
"""Check that get_controller can handle unknown errors."""
|
"""Check that get_controller can handle unknown errors."""
|
||||||
with patch(
|
with patch("aiounifi.Controller.check_unifi_os", return_value=True), patch(
|
||||||
"aiounifi.Controller.login", side_effect=aiounifi.AiounifiException
|
"aiounifi.Controller.login", side_effect=aiounifi.AiounifiException
|
||||||
), pytest.raises(unifi.errors.AuthenticationRequired):
|
), pytest.raises(unifi.errors.AuthenticationRequired):
|
||||||
await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
|
await unifi.controller.get_controller(hass, **CONTROLLER_DATA)
|
||||||
|
@ -286,7 +286,7 @@ async def test_switches(hass):
|
|||||||
assert controller.mock_requests[4] == {
|
assert controller.mock_requests[4] == {
|
||||||
"json": {"mac": "00:00:00:00:01:01", "cmd": "block-sta"},
|
"json": {"mac": "00:00:00:00:01:01", "cmd": "block-sta"},
|
||||||
"method": "post",
|
"method": "post",
|
||||||
"path": "s/{site}/cmd/stamgr/",
|
"path": "/cmd/stamgr",
|
||||||
}
|
}
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
@ -296,7 +296,7 @@ async def test_switches(hass):
|
|||||||
assert controller.mock_requests[5] == {
|
assert controller.mock_requests[5] == {
|
||||||
"json": {"mac": "00:00:00:00:01:01", "cmd": "unblock-sta"},
|
"json": {"mac": "00:00:00:00:01:01", "cmd": "unblock-sta"},
|
||||||
"method": "post",
|
"method": "post",
|
||||||
"path": "s/{site}/cmd/stamgr/",
|
"path": "/cmd/stamgr",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -397,7 +397,7 @@ async def test_new_client_discovered_on_poe_control(hass):
|
|||||||
"port_overrides": [{"port_idx": 1, "portconf_id": "1a1", "poe_mode": "off"}]
|
"port_overrides": [{"port_idx": 1, "portconf_id": "1a1", "poe_mode": "off"}]
|
||||||
},
|
},
|
||||||
"method": "put",
|
"method": "put",
|
||||||
"path": "s/{site}/rest/device/mock-id",
|
"path": "/rest/device/mock-id",
|
||||||
}
|
}
|
||||||
|
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
@ -411,7 +411,7 @@ async def test_new_client_discovered_on_poe_control(hass):
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"method": "put",
|
"method": "put",
|
||||||
"path": "s/{site}/rest/device/mock-id",
|
"path": "/rest/device/mock-id",
|
||||||
}
|
}
|
||||||
|
|
||||||
switch_2 = hass.states.get("switch.poe_client_2")
|
switch_2 = hass.states.get("switch.poe_client_2")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user