Fix vera config ids not being converted to integers (#35070)

This commit is contained in:
Robert Van Gorkom 2020-05-02 14:10:49 -07:00 committed by GitHub
parent 88b7aba1c8
commit ab08c1bef8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 103 additions and 18 deletions

View File

@ -25,7 +25,7 @@ from homeassistant.util import convert, slugify
from homeassistant.util.dt import utc_from_timestamp from homeassistant.util.dt import utc_from_timestamp
from .common import ControllerData, get_configured_platforms from .common import ControllerData, get_configured_platforms
from .config_flow import new_options from .config_flow import fix_device_id_list, new_options
from .const import ( from .const import (
ATTR_CURRENT_ENERGY_KWH, ATTR_CURRENT_ENERGY_KWH,
ATTR_CURRENT_POWER_W, ATTR_CURRENT_POWER_W,
@ -81,9 +81,18 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
), ),
) )
saved_light_ids = config_entry.options.get(CONF_LIGHTS, [])
saved_exclude_ids = config_entry.options.get(CONF_EXCLUDE, [])
base_url = config_entry.data[CONF_CONTROLLER] base_url = config_entry.data[CONF_CONTROLLER]
light_ids = config_entry.options.get(CONF_LIGHTS, []) light_ids = fix_device_id_list(saved_light_ids)
exclude_ids = config_entry.options.get(CONF_EXCLUDE, []) exclude_ids = fix_device_id_list(saved_exclude_ids)
# If the ids were corrected. Update the config entry.
if light_ids != saved_light_ids or exclude_ids != saved_exclude_ids:
hass.config_entries.async_update_entry(
entry=config_entry, options=new_options(light_ids, exclude_ids)
)
# Initialize the Vera controller. # Initialize the Vera controller.
controller = veraApi.VeraController(base_url) controller = veraApi.VeraController(base_url)

View File

@ -1,7 +1,7 @@
"""Config flow for Vera.""" """Config flow for Vera."""
import logging import logging
import re import re
from typing import List, cast from typing import Any, List
import pyvera as pv import pyvera as pv
from requests.exceptions import RequestException from requests.exceptions import RequestException
@ -17,20 +17,22 @@ LIST_REGEX = re.compile("[^0-9]+")
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def str_to_int_list(data: str) -> List[str]: def fix_device_id_list(data: List[Any]) -> List[int]:
"""Fix the id list by converting it to a supported int list."""
return str_to_int_list(list_to_str(data))
def str_to_int_list(data: str) -> List[int]:
"""Convert a string to an int list.""" """Convert a string to an int list."""
if isinstance(str, list): return [int(s) for s in LIST_REGEX.split(data) if len(s) > 0]
return cast(List[str], data)
return [s for s in LIST_REGEX.split(data) if len(s) > 0]
def int_list_to_str(data: List[str]) -> str: def list_to_str(data: List[Any]) -> str:
"""Convert an int list to a string.""" """Convert an int list to a string."""
return " ".join([str(i) for i in data]) return " ".join([str(i) for i in data])
def new_options(lights: List[str], exclude: List[str]) -> dict: def new_options(lights: List[int], exclude: List[int]) -> dict:
"""Create a standard options object.""" """Create a standard options object."""
return {CONF_LIGHTS: lights, CONF_EXCLUDE: exclude} return {CONF_LIGHTS: lights, CONF_EXCLUDE: exclude}
@ -40,10 +42,10 @@ def options_schema(options: dict = None) -> dict:
options = options or {} options = options or {}
return { return {
vol.Optional( vol.Optional(
CONF_LIGHTS, default=int_list_to_str(options.get(CONF_LIGHTS, [])), CONF_LIGHTS, default=list_to_str(options.get(CONF_LIGHTS, [])),
): str, ): str,
vol.Optional( vol.Optional(
CONF_EXCLUDE, default=int_list_to_str(options.get(CONF_EXCLUDE, [])), CONF_EXCLUDE, default=list_to_str(options.get(CONF_EXCLUDE, [])),
): str, ): str,
} }

View File

@ -44,8 +44,8 @@ async def test_async_step_user_success(hass: HomeAssistant) -> None:
assert result["data"] == { assert result["data"] == {
CONF_CONTROLLER: "http://127.0.0.1:123", CONF_CONTROLLER: "http://127.0.0.1:123",
CONF_SOURCE: config_entries.SOURCE_USER, CONF_SOURCE: config_entries.SOURCE_USER,
CONF_LIGHTS: ["12", "13"], CONF_LIGHTS: [12, 13],
CONF_EXCLUDE: ["14", "15"], CONF_EXCLUDE: [14, 15],
} }
assert result["result"].unique_id == controller.serial_number assert result["result"].unique_id == controller.serial_number
@ -154,6 +154,6 @@ async def test_options(hass):
) )
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["data"] == { assert result["data"] == {
CONF_LIGHTS: ["1", "2", "3", "4", "5", "6", "7"], CONF_LIGHTS: [1, 2, 3, 4, 5, 6, 7],
CONF_EXCLUDE: ["8", "9", "10", "11", "12", "13", "14"], CONF_EXCLUDE: [8, 9, 10, 11, 12, 13, 14],
} }

View File

@ -1,8 +1,14 @@
"""Vera tests.""" """Vera tests."""
import pytest
import pyvera as pv import pyvera as pv
from requests.exceptions import RequestException from requests.exceptions import RequestException
from homeassistant.components.vera import CONF_CONTROLLER, DOMAIN from homeassistant.components.vera import (
CONF_CONTROLLER,
CONF_EXCLUDE,
CONF_LIGHTS,
DOMAIN,
)
from homeassistant.config_entries import ENTRY_STATE_NOT_LOADED from homeassistant.config_entries import ENTRY_STATE_NOT_LOADED
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -110,3 +116,71 @@ async def test_async_setup_entry_error(
entry.add_to_hass(hass) entry.add_to_hass(hass)
assert not await hass.config_entries.async_setup(entry.entry_id) assert not await hass.config_entries.async_setup(entry.entry_id)
@pytest.mark.parametrize(
["options"],
[
[{CONF_LIGHTS: [4, 10, 12, "AAA"], CONF_EXCLUDE: [1, "BBB"]}],
[{CONF_LIGHTS: ["4", "10", "12", "AAA"], CONF_EXCLUDE: ["1", "BBB"]}],
],
)
async def test_exclude_and_light_ids(
hass: HomeAssistant, vera_component_factory: ComponentFactory, options
) -> None:
"""Test device exclusion, marking switches as lights and fixing the data type."""
vera_device1 = MagicMock(spec=pv.VeraBinarySensor) # type: pv.VeraBinarySensor
vera_device1.device_id = 1
vera_device1.vera_device_id = 1
vera_device1.name = "dev1"
vera_device1.is_tripped = False
entity_id1 = "binary_sensor.dev1_1"
vera_device2 = MagicMock(spec=pv.VeraBinarySensor) # type: pv.VeraBinarySensor
vera_device2.device_id = 2
vera_device2.vera_device_id = 2
vera_device2.name = "dev2"
vera_device2.is_tripped = False
entity_id2 = "binary_sensor.dev2_2"
vera_device3 = MagicMock(spec=pv.VeraSwitch) # type: pv.VeraSwitch
vera_device3.device_id = 3
vera_device3.name = "dev3"
vera_device3.category = pv.CATEGORY_SWITCH
vera_device3.is_switched_on = MagicMock(return_value=False)
entity_id3 = "switch.dev3_3"
vera_device4 = MagicMock(spec=pv.VeraSwitch) # type: pv.VeraSwitch
vera_device4.device_id = 4
vera_device4.name = "dev4"
vera_device4.category = pv.CATEGORY_SWITCH
vera_device4.is_switched_on = MagicMock(return_value=False)
entity_id4 = "light.dev4_4"
component_data = await vera_component_factory.configure_component(
hass=hass,
controller_config=new_simple_controller_config(
devices=(vera_device1, vera_device2, vera_device3, vera_device4),
config={**{CONF_CONTROLLER: "http://127.0.0.1:123"}, **options},
),
)
# Assert the entries were setup correctly.
config_entry = next(iter(hass.config_entries.async_entries(DOMAIN)))
assert config_entry.options == {
CONF_LIGHTS: [4, 10, 12],
CONF_EXCLUDE: [1],
}
update_callback = component_data.controller_data.update_callback
update_callback(vera_device1)
update_callback(vera_device2)
update_callback(vera_device3)
update_callback(vera_device4)
await hass.async_block_till_done()
assert hass.states.get(entity_id1) is None
assert hass.states.get(entity_id2) is not None
assert hass.states.get(entity_id3) is not None
assert hass.states.get(entity_id4) is not None