Improve legacy support for Hunter Douglas PowerView (#50918)
Co-authored-by: J. Nick Koston <nick@koston.org>pull/51014/head
parent
dbefa8fac0
commit
c1a1a38ffc
|
@ -3,6 +3,7 @@ from datetime import timedelta
|
|||
import logging
|
||||
|
||||
from aiopvapi.helpers.aiorequest import AioRequest
|
||||
from aiopvapi.helpers.api_base import ApiEntryPoint
|
||||
from aiopvapi.helpers.constants import ATTR_ID
|
||||
from aiopvapi.helpers.tools import base64_to_unicode
|
||||
from aiopvapi.rooms import Rooms
|
||||
|
@ -20,7 +21,9 @@ import homeassistant.helpers.config_validation as cv
|
|||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import (
|
||||
API_PATH_FWVERSION,
|
||||
COORDINATOR,
|
||||
DEFAULT_LEGACY_MAINPROCESSOR,
|
||||
DEVICE_FIRMWARE,
|
||||
DEVICE_INFO,
|
||||
DEVICE_MAC_ADDRESS,
|
||||
|
@ -29,24 +32,18 @@ from .const import (
|
|||
DEVICE_REVISION,
|
||||
DEVICE_SERIAL_NUMBER,
|
||||
DOMAIN,
|
||||
FIRMWARE_BUILD,
|
||||
FIRMWARE_IN_USERDATA,
|
||||
FIRMWARE_SUB_REVISION,
|
||||
FIRMWARE,
|
||||
FIRMWARE_MAINPROCESSOR,
|
||||
FIRMWARE_NAME,
|
||||
FIRMWARE_REVISION,
|
||||
HUB_EXCEPTIONS,
|
||||
HUB_NAME,
|
||||
LEGACY_DEVICE_BUILD,
|
||||
LEGACY_DEVICE_MODEL,
|
||||
LEGACY_DEVICE_REVISION,
|
||||
LEGACY_DEVICE_SUB_REVISION,
|
||||
MAC_ADDRESS_IN_USERDATA,
|
||||
MAINPROCESSOR_IN_USERDATA_FIRMWARE,
|
||||
MODEL_IN_MAINPROCESSOR,
|
||||
PV_API,
|
||||
PV_ROOM_DATA,
|
||||
PV_SCENE_DATA,
|
||||
PV_SHADE_DATA,
|
||||
PV_SHADES,
|
||||
REVISION_IN_MAINPROCESSOR,
|
||||
ROOM_DATA,
|
||||
SCENE_DATA,
|
||||
SERIAL_NUMBER_IN_USERDATA,
|
||||
|
@ -137,26 +134,25 @@ async def async_get_device_info(pv_request):
|
|||
resources = await userdata.get_resources()
|
||||
userdata_data = resources[USER_DATA]
|
||||
|
||||
if FIRMWARE_IN_USERDATA in userdata_data:
|
||||
main_processor_info = userdata_data[FIRMWARE_IN_USERDATA][
|
||||
MAINPROCESSOR_IN_USERDATA_FIRMWARE
|
||||
]
|
||||
else:
|
||||
if FIRMWARE in userdata_data:
|
||||
main_processor_info = userdata_data[FIRMWARE][FIRMWARE_MAINPROCESSOR]
|
||||
elif userdata_data:
|
||||
# Legacy devices
|
||||
main_processor_info = {
|
||||
REVISION_IN_MAINPROCESSOR: LEGACY_DEVICE_REVISION,
|
||||
FIRMWARE_SUB_REVISION: LEGACY_DEVICE_SUB_REVISION,
|
||||
FIRMWARE_BUILD: LEGACY_DEVICE_BUILD,
|
||||
MODEL_IN_MAINPROCESSOR: LEGACY_DEVICE_MODEL,
|
||||
}
|
||||
fwversion = ApiEntryPoint(pv_request, API_PATH_FWVERSION)
|
||||
resources = await fwversion.get_resources()
|
||||
|
||||
if FIRMWARE in resources:
|
||||
main_processor_info = resources[FIRMWARE][FIRMWARE_MAINPROCESSOR]
|
||||
else:
|
||||
main_processor_info = DEFAULT_LEGACY_MAINPROCESSOR
|
||||
|
||||
return {
|
||||
DEVICE_NAME: base64_to_unicode(userdata_data[HUB_NAME]),
|
||||
DEVICE_MAC_ADDRESS: userdata_data[MAC_ADDRESS_IN_USERDATA],
|
||||
DEVICE_SERIAL_NUMBER: userdata_data[SERIAL_NUMBER_IN_USERDATA],
|
||||
DEVICE_REVISION: main_processor_info[REVISION_IN_MAINPROCESSOR],
|
||||
DEVICE_REVISION: main_processor_info[FIRMWARE_REVISION],
|
||||
DEVICE_FIRMWARE: main_processor_info,
|
||||
DEVICE_MODEL: main_processor_info[MODEL_IN_MAINPROCESSOR],
|
||||
DEVICE_MODEL: main_processor_info[FIRMWARE_NAME],
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,14 +19,11 @@ USER_DATA = "userData"
|
|||
|
||||
MAC_ADDRESS_IN_USERDATA = "macAddress"
|
||||
SERIAL_NUMBER_IN_USERDATA = "serialNumber"
|
||||
FIRMWARE_IN_USERDATA = "firmware"
|
||||
MAINPROCESSOR_IN_USERDATA_FIRMWARE = "mainProcessor"
|
||||
REVISION_IN_MAINPROCESSOR = "revision"
|
||||
MODEL_IN_MAINPROCESSOR = "name"
|
||||
HUB_NAME = "hubName"
|
||||
|
||||
FIRMWARE_IN_SHADE = "firmware"
|
||||
|
||||
FIRMWARE = "firmware"
|
||||
FIRMWARE_MAINPROCESSOR = "mainProcessor"
|
||||
FIRMWARE_NAME = "name"
|
||||
FIRMWARE_REVISION = "revision"
|
||||
FIRMWARE_SUB_REVISION = "subRevision"
|
||||
FIRMWARE_BUILD = "build"
|
||||
|
@ -70,4 +67,14 @@ HUB_EXCEPTIONS = (ServerDisconnectedError, asyncio.TimeoutError, PvApiConnection
|
|||
LEGACY_DEVICE_SUB_REVISION = 1
|
||||
LEGACY_DEVICE_REVISION = 0
|
||||
LEGACY_DEVICE_BUILD = 0
|
||||
LEGACY_DEVICE_MODEL = "PV Hub1.0"
|
||||
LEGACY_DEVICE_MODEL = "PowerView Hub"
|
||||
|
||||
DEFAULT_LEGACY_MAINPROCESSOR = {
|
||||
FIRMWARE_REVISION: LEGACY_DEVICE_REVISION,
|
||||
FIRMWARE_SUB_REVISION: LEGACY_DEVICE_SUB_REVISION,
|
||||
FIRMWARE_BUILD: LEGACY_DEVICE_BUILD,
|
||||
FIRMWARE_NAME: LEGACY_DEVICE_MODEL,
|
||||
}
|
||||
|
||||
|
||||
API_PATH_FWVERSION = "api/fwversion"
|
||||
|
|
|
@ -12,8 +12,8 @@ from .const import (
|
|||
DEVICE_NAME,
|
||||
DEVICE_SERIAL_NUMBER,
|
||||
DOMAIN,
|
||||
FIRMWARE,
|
||||
FIRMWARE_BUILD,
|
||||
FIRMWARE_IN_SHADE,
|
||||
FIRMWARE_REVISION,
|
||||
FIRMWARE_SUB_REVISION,
|
||||
MANUFACTURER,
|
||||
|
@ -71,20 +71,21 @@ class ShadeEntity(HDEntity):
|
|||
"name": self._shade_name,
|
||||
"suggested_area": self._room_name,
|
||||
"manufacturer": MANUFACTURER,
|
||||
"model": self._shade.raw_data[ATTR_TYPE],
|
||||
"via_device": (DOMAIN, self._device_info[DEVICE_SERIAL_NUMBER]),
|
||||
}
|
||||
|
||||
if FIRMWARE_IN_SHADE not in self._shade.raw_data:
|
||||
return device_info
|
||||
|
||||
firmware = self._shade.raw_data[FIRMWARE_IN_SHADE]
|
||||
sw_version = f"{firmware[FIRMWARE_REVISION]}.{firmware[FIRMWARE_SUB_REVISION]}.{firmware[FIRMWARE_BUILD]}"
|
||||
model = self._shade.raw_data[ATTR_TYPE]
|
||||
for shade in self._shade.shade_types:
|
||||
if shade.shade_type == model:
|
||||
model = shade.description
|
||||
if shade.shade_type == device_info["model"]:
|
||||
device_info["model"] = shade.description
|
||||
break
|
||||
|
||||
if FIRMWARE not in self._shade.raw_data:
|
||||
return device_info
|
||||
|
||||
firmware = self._shade.raw_data[FIRMWARE]
|
||||
sw_version = f"{firmware[FIRMWARE_REVISION]}.{firmware[FIRMWARE_SUB_REVISION]}.{firmware[FIRMWARE_BUILD]}"
|
||||
|
||||
device_info["sw_version"] = sw_version
|
||||
device_info["model"] = model
|
||||
|
||||
return device_info
|
||||
|
|
|
@ -41,12 +41,34 @@ def _get_mock_powerview_userdata(userdata=None, get_resources=None):
|
|||
if not userdata:
|
||||
userdata = json.loads(load_fixture("hunterdouglas_powerview/userdata.json"))
|
||||
if get_resources:
|
||||
type(mock_powerview_userdata).get_resources = AsyncMock(
|
||||
mock_powerview_userdata.get_resources = AsyncMock(side_effect=get_resources)
|
||||
else:
|
||||
mock_powerview_userdata.get_resources = AsyncMock(return_value=userdata)
|
||||
return mock_powerview_userdata
|
||||
|
||||
|
||||
def _get_mock_powerview_legacy_userdata(userdata=None, get_resources=None):
|
||||
mock_powerview_userdata_legacy = MagicMock()
|
||||
if not userdata:
|
||||
userdata = json.loads(load_fixture("hunterdouglas_powerview/userdata_v1.json"))
|
||||
if get_resources:
|
||||
mock_powerview_userdata_legacy.get_resources = AsyncMock(
|
||||
side_effect=get_resources
|
||||
)
|
||||
else:
|
||||
type(mock_powerview_userdata).get_resources = AsyncMock(return_value=userdata)
|
||||
return mock_powerview_userdata
|
||||
mock_powerview_userdata_legacy.get_resources = AsyncMock(return_value=userdata)
|
||||
return mock_powerview_userdata_legacy
|
||||
|
||||
|
||||
def _get_mock_powerview_fwversion(fwversion=None, get_resources=None):
|
||||
mock_powerview_fwversion = MagicMock()
|
||||
if not fwversion:
|
||||
fwversion = json.loads(load_fixture("hunterdouglas_powerview/fwversion.json"))
|
||||
if get_resources:
|
||||
mock_powerview_fwversion.get_resources = AsyncMock(side_effect=get_resources)
|
||||
else:
|
||||
mock_powerview_fwversion.get_resources = AsyncMock(return_value=fwversion)
|
||||
return mock_powerview_fwversion
|
||||
|
||||
|
||||
async def test_user_form(hass):
|
||||
|
@ -92,6 +114,53 @@ async def test_user_form(hass):
|
|||
assert result4["type"] == "abort"
|
||||
|
||||
|
||||
async def test_user_form_legacy(hass):
|
||||
"""Test we get the user form with a legacy device."""
|
||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == "form"
|
||||
assert result["errors"] == {}
|
||||
|
||||
mock_powerview_userdata = _get_mock_powerview_legacy_userdata()
|
||||
mock_powerview_fwversion = _get_mock_powerview_fwversion()
|
||||
with patch(
|
||||
"homeassistant.components.hunterdouglas_powerview.UserData",
|
||||
return_value=mock_powerview_userdata,
|
||||
), patch(
|
||||
"homeassistant.components.hunterdouglas_powerview.ApiEntryPoint",
|
||||
return_value=mock_powerview_fwversion,
|
||||
), patch(
|
||||
"homeassistant.components.hunterdouglas_powerview.async_setup_entry",
|
||||
return_value=True,
|
||||
) as mock_setup_entry:
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{"host": "1.2.3.4"},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == "create_entry"
|
||||
assert result2["title"] == "PowerView Hub Gen 1"
|
||||
assert result2["data"] == {
|
||||
"host": "1.2.3.4",
|
||||
}
|
||||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
result3 = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result3["type"] == "form"
|
||||
assert result3["errors"] == {}
|
||||
|
||||
result4 = await hass.config_entries.flow.async_configure(
|
||||
result3["flow_id"],
|
||||
{"host": "1.2.3.4"},
|
||||
)
|
||||
assert result4["type"] == "abort"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("source, discovery_info", DISCOVERY_DATA)
|
||||
async def test_form_homekit_and_dhcp_cannot_connect(hass, source, discovery_info):
|
||||
"""Test we get the form with homekit and dhcp source."""
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"firmware": {
|
||||
"mainProcessor": {
|
||||
"name": "PowerView Hub",
|
||||
"revision": 1,
|
||||
"subRevision": 1,
|
||||
"build": 857
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue