core/tests/helpers/test_icon.py

237 lines
8.0 KiB
Python
Raw Normal View History

Xiaomi vacuum as platform of new `vacuum` component derived from ToggleEntity, and services (#8623) * Xiaomi vacuum as component with switch, sensors and services - Conversion from switch platform to async component. - Add services proposed in #8416 to the new component, with shorter names. - Add sensors for the vacuum robot as a selectable list from `battery`, `state`, `error`, `fanspeed`, `clean_time` and `clean_area` (the state attributes of the switch). The sensors don't poll, but listen to a signal to update the state, the switch fires this signal when updating. - Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!) * path change in requirements_all (from switch platform to component) * copy pasting is a bad habit * services to the components services.yaml, modify .coveragerc * review: use with multiple hosts, fix calls to async_add_devices, fix ranges for services * `icon_for_battery_level` util method * Xiaomi vacuum as platform of new component vacuum - Created new component `vacuum` from a ToggleEntity. - Add services `turn_on`, `turn_off`, `cleaning_play_pause`, `stop`, `return_to_base`, `locate`, `set_fanspeed` and `send_command`. - Remove the main switch for the xiaomi vacuum (the toggable main entity is the switch). - Add `support flags` for the common services - Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!) - Move services descriptions to a yaml file for the new component. - Update requirements_all. - Update coveragerc. * fix coveragerc * fix battery icon helper to use more icons * remove sensors, create properties and support flags for custom UI * cleaning * updated state_attrs for filtering in UI, renamed platform to simply `xiaomi` * fix platform rename * change fanspeed and expose `fanspeed_list` to use speed steps * minor fixes - Rename service `start_pause` - Add 'Error' attribute only if `got_error`. - Minor changes * rename state attrs * rename state attrs * review changes: cut fan__speed, style changes, remove logging, and more * add ATTR_COMMAND = 'command' to const * pop entity_id from service data * remove property accessor for vacuum object * lint fix * fix extra attrs names * module level functions for calling the services * params as optional keyword for `send_command` * params as optional keyword for `send_command`, remove debug logs * explicit parameters for `set_fan_speed` and `send_command` * Demo platform for the vacuum component * vacuum tests for the Demo platform * some fixes * don't omit vacuum * vacuum tests for the Xiaomi platform * fix test * fix * fix xiaomi test * fix coveragerc * test send command * fix coveragerc * fix string formatting * The coverage is to low. It need 93% or more
2017-08-04 13:27:10 +00:00
"""Test Home Assistant icon util methods."""
import pathlib
from unittest.mock import Mock, patch
import pytest
from homeassistant.core import HomeAssistant
from homeassistant.helpers import icon
from homeassistant.loader import IntegrationNotFound
from homeassistant.setup import async_setup_component
Xiaomi vacuum as platform of new `vacuum` component derived from ToggleEntity, and services (#8623) * Xiaomi vacuum as component with switch, sensors and services - Conversion from switch platform to async component. - Add services proposed in #8416 to the new component, with shorter names. - Add sensors for the vacuum robot as a selectable list from `battery`, `state`, `error`, `fanspeed`, `clean_time` and `clean_area` (the state attributes of the switch). The sensors don't poll, but listen to a signal to update the state, the switch fires this signal when updating. - Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!) * path change in requirements_all (from switch platform to component) * copy pasting is a bad habit * services to the components services.yaml, modify .coveragerc * review: use with multiple hosts, fix calls to async_add_devices, fix ranges for services * `icon_for_battery_level` util method * Xiaomi vacuum as platform of new component vacuum - Created new component `vacuum` from a ToggleEntity. - Add services `turn_on`, `turn_off`, `cleaning_play_pause`, `stop`, `return_to_base`, `locate`, `set_fanspeed` and `send_command`. - Remove the main switch for the xiaomi vacuum (the toggable main entity is the switch). - Add `support flags` for the common services - Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!) - Move services descriptions to a yaml file for the new component. - Update requirements_all. - Update coveragerc. * fix coveragerc * fix battery icon helper to use more icons * remove sensors, create properties and support flags for custom UI * cleaning * updated state_attrs for filtering in UI, renamed platform to simply `xiaomi` * fix platform rename * change fanspeed and expose `fanspeed_list` to use speed steps * minor fixes - Rename service `start_pause` - Add 'Error' attribute only if `got_error`. - Minor changes * rename state attrs * rename state attrs * review changes: cut fan__speed, style changes, remove logging, and more * add ATTR_COMMAND = 'command' to const * pop entity_id from service data * remove property accessor for vacuum object * lint fix * fix extra attrs names * module level functions for calling the services * params as optional keyword for `send_command` * params as optional keyword for `send_command`, remove debug logs * explicit parameters for `set_fan_speed` and `send_command` * Demo platform for the vacuum component * vacuum tests for the Demo platform * some fixes * don't omit vacuum * vacuum tests for the Xiaomi platform * fix test * fix * fix xiaomi test * fix coveragerc * test send command * fix coveragerc * fix string formatting * The coverage is to low. It need 93% or more
2017-08-04 13:27:10 +00:00
def test_battery_icon() -> None:
"""Test icon generator for battery sensor."""
assert icon.icon_for_battery_level(None, True) == "mdi:battery-unknown"
assert icon.icon_for_battery_level(None, False) == "mdi:battery-unknown"
Xiaomi vacuum as platform of new `vacuum` component derived from ToggleEntity, and services (#8623) * Xiaomi vacuum as component with switch, sensors and services - Conversion from switch platform to async component. - Add services proposed in #8416 to the new component, with shorter names. - Add sensors for the vacuum robot as a selectable list from `battery`, `state`, `error`, `fanspeed`, `clean_time` and `clean_area` (the state attributes of the switch). The sensors don't poll, but listen to a signal to update the state, the switch fires this signal when updating. - Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!) * path change in requirements_all (from switch platform to component) * copy pasting is a bad habit * services to the components services.yaml, modify .coveragerc * review: use with multiple hosts, fix calls to async_add_devices, fix ranges for services * `icon_for_battery_level` util method * Xiaomi vacuum as platform of new component vacuum - Created new component `vacuum` from a ToggleEntity. - Add services `turn_on`, `turn_off`, `cleaning_play_pause`, `stop`, `return_to_base`, `locate`, `set_fanspeed` and `send_command`. - Remove the main switch for the xiaomi vacuum (the toggable main entity is the switch). - Add `support flags` for the common services - Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!) - Move services descriptions to a yaml file for the new component. - Update requirements_all. - Update coveragerc. * fix coveragerc * fix battery icon helper to use more icons * remove sensors, create properties and support flags for custom UI * cleaning * updated state_attrs for filtering in UI, renamed platform to simply `xiaomi` * fix platform rename * change fanspeed and expose `fanspeed_list` to use speed steps * minor fixes - Rename service `start_pause` - Add 'Error' attribute only if `got_error`. - Minor changes * rename state attrs * rename state attrs * review changes: cut fan__speed, style changes, remove logging, and more * add ATTR_COMMAND = 'command' to const * pop entity_id from service data * remove property accessor for vacuum object * lint fix * fix extra attrs names * module level functions for calling the services * params as optional keyword for `send_command` * params as optional keyword for `send_command`, remove debug logs * explicit parameters for `set_fan_speed` and `send_command` * Demo platform for the vacuum component * vacuum tests for the Demo platform * some fixes * don't omit vacuum * vacuum tests for the Xiaomi platform * fix test * fix * fix xiaomi test * fix coveragerc * test send command * fix coveragerc * fix string formatting * The coverage is to low. It need 93% or more
2017-08-04 13:27:10 +00:00
assert icon.icon_for_battery_level(5, True) == "mdi:battery-outline"
assert icon.icon_for_battery_level(5, False) == "mdi:battery-alert"
Xiaomi vacuum as platform of new `vacuum` component derived from ToggleEntity, and services (#8623) * Xiaomi vacuum as component with switch, sensors and services - Conversion from switch platform to async component. - Add services proposed in #8416 to the new component, with shorter names. - Add sensors for the vacuum robot as a selectable list from `battery`, `state`, `error`, `fanspeed`, `clean_time` and `clean_area` (the state attributes of the switch). The sensors don't poll, but listen to a signal to update the state, the switch fires this signal when updating. - Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!) * path change in requirements_all (from switch platform to component) * copy pasting is a bad habit * services to the components services.yaml, modify .coveragerc * review: use with multiple hosts, fix calls to async_add_devices, fix ranges for services * `icon_for_battery_level` util method * Xiaomi vacuum as platform of new component vacuum - Created new component `vacuum` from a ToggleEntity. - Add services `turn_on`, `turn_off`, `cleaning_play_pause`, `stop`, `return_to_base`, `locate`, `set_fanspeed` and `send_command`. - Remove the main switch for the xiaomi vacuum (the toggable main entity is the switch). - Add `support flags` for the common services - Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!) - Move services descriptions to a yaml file for the new component. - Update requirements_all. - Update coveragerc. * fix coveragerc * fix battery icon helper to use more icons * remove sensors, create properties and support flags for custom UI * cleaning * updated state_attrs for filtering in UI, renamed platform to simply `xiaomi` * fix platform rename * change fanspeed and expose `fanspeed_list` to use speed steps * minor fixes - Rename service `start_pause` - Add 'Error' attribute only if `got_error`. - Minor changes * rename state attrs * rename state attrs * review changes: cut fan__speed, style changes, remove logging, and more * add ATTR_COMMAND = 'command' to const * pop entity_id from service data * remove property accessor for vacuum object * lint fix * fix extra attrs names * module level functions for calling the services * params as optional keyword for `send_command` * params as optional keyword for `send_command`, remove debug logs * explicit parameters for `set_fan_speed` and `send_command` * Demo platform for the vacuum component * vacuum tests for the Demo platform * some fixes * don't omit vacuum * vacuum tests for the Xiaomi platform * fix test * fix * fix xiaomi test * fix coveragerc * test send command * fix coveragerc * fix string formatting * The coverage is to low. It need 93% or more
2017-08-04 13:27:10 +00:00
assert icon.icon_for_battery_level(100, True) == "mdi:battery-charging-100"
assert icon.icon_for_battery_level(100, False) == "mdi:battery"
Xiaomi vacuum as platform of new `vacuum` component derived from ToggleEntity, and services (#8623) * Xiaomi vacuum as component with switch, sensors and services - Conversion from switch platform to async component. - Add services proposed in #8416 to the new component, with shorter names. - Add sensors for the vacuum robot as a selectable list from `battery`, `state`, `error`, `fanspeed`, `clean_time` and `clean_area` (the state attributes of the switch). The sensors don't poll, but listen to a signal to update the state, the switch fires this signal when updating. - Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!) * path change in requirements_all (from switch platform to component) * copy pasting is a bad habit * services to the components services.yaml, modify .coveragerc * review: use with multiple hosts, fix calls to async_add_devices, fix ranges for services * `icon_for_battery_level` util method * Xiaomi vacuum as platform of new component vacuum - Created new component `vacuum` from a ToggleEntity. - Add services `turn_on`, `turn_off`, `cleaning_play_pause`, `stop`, `return_to_base`, `locate`, `set_fanspeed` and `send_command`. - Remove the main switch for the xiaomi vacuum (the toggable main entity is the switch). - Add `support flags` for the common services - Assign default icons to sensors and the switch (`mdi:google-circles-group` looks like the robot!) - Move services descriptions to a yaml file for the new component. - Update requirements_all. - Update coveragerc. * fix coveragerc * fix battery icon helper to use more icons * remove sensors, create properties and support flags for custom UI * cleaning * updated state_attrs for filtering in UI, renamed platform to simply `xiaomi` * fix platform rename * change fanspeed and expose `fanspeed_list` to use speed steps * minor fixes - Rename service `start_pause` - Add 'Error' attribute only if `got_error`. - Minor changes * rename state attrs * rename state attrs * review changes: cut fan__speed, style changes, remove logging, and more * add ATTR_COMMAND = 'command' to const * pop entity_id from service data * remove property accessor for vacuum object * lint fix * fix extra attrs names * module level functions for calling the services * params as optional keyword for `send_command` * params as optional keyword for `send_command`, remove debug logs * explicit parameters for `set_fan_speed` and `send_command` * Demo platform for the vacuum component * vacuum tests for the Demo platform * some fixes * don't omit vacuum * vacuum tests for the Xiaomi platform * fix test * fix * fix xiaomi test * fix coveragerc * test send command * fix coveragerc * fix string formatting * The coverage is to low. It need 93% or more
2017-08-04 13:27:10 +00:00
2019-07-31 19:25:30 +00:00
iconbase = "mdi:battery"
for level in range(0, 100, 5):
print( # noqa: T201
2019-07-31 19:25:30 +00:00
"Level: %d. icon: %s, charging: %s"
% (
level,
icon.icon_for_battery_level(level, False),
icon.icon_for_battery_level(level, True),
2019-07-31 19:25:30 +00:00
)
)
if level <= 10:
2019-07-31 19:25:30 +00:00
postfix_charging = "-outline"
elif level <= 30:
2019-07-31 19:25:30 +00:00
postfix_charging = "-charging-20"
elif level <= 50:
2019-07-31 19:25:30 +00:00
postfix_charging = "-charging-40"
elif level <= 70:
2019-07-31 19:25:30 +00:00
postfix_charging = "-charging-60"
elif level <= 90:
2019-07-31 19:25:30 +00:00
postfix_charging = "-charging-80"
else:
2019-07-31 19:25:30 +00:00
postfix_charging = "-charging-100"
if 5 < level < 95:
2021-04-09 16:58:27 +00:00
postfix = f"-{int(round(level / 10 - 0.01)) * 10}"
elif level <= 5:
2019-07-31 19:25:30 +00:00
postfix = "-alert"
else:
2019-07-31 19:25:30 +00:00
postfix = ""
assert iconbase + postfix == icon.icon_for_battery_level(level, False)
assert iconbase + postfix_charging == icon.icon_for_battery_level(level, True)
def test_signal_icon() -> None:
"""Test icon generator for signal sensor."""
assert icon.icon_for_signal_level(None) == "mdi:signal-cellular-outline"
assert icon.icon_for_signal_level(0) == "mdi:signal-cellular-outline"
assert icon.icon_for_signal_level(5) == "mdi:signal-cellular-1"
assert icon.icon_for_signal_level(40) == "mdi:signal-cellular-2"
assert icon.icon_for_signal_level(80) == "mdi:signal-cellular-3"
assert icon.icon_for_signal_level(100) == "mdi:signal-cellular-3"
def test_load_icons_files(hass: HomeAssistant) -> None:
"""Test the load icons files function."""
file1 = hass.config.path("custom_components", "test", "icons.json")
file2 = hass.config.path("custom_components", "test", "invalid.json")
assert icon._load_icons_files({"test": file1, "invalid": file2}) == {
"test": {
"entity": {
"switch": {
"something": {
"state": {"away": "mdi:home-outline", "home": "mdi:home"}
}
}
},
},
"invalid": {},
}
@pytest.mark.usefixtures("enable_custom_integrations")
async def test_get_icons(hass: HomeAssistant) -> None:
"""Test the get icon helper."""
icons = await icon.async_get_icons(hass, "entity")
assert icons == {}
icons = await icon.async_get_icons(hass, "entity_component")
assert icons == {}
# Set up test switch component
assert await async_setup_component(hass, "switch", {"switch": {"platform": "test"}})
# Test getting icons for the entity component
icons = await icon.async_get_icons(hass, "entity_component")
assert icons["switch"]["_"]["default"] == "mdi:toggle-switch-variant"
# Test services icons are available
icons = await icon.async_get_icons(hass, "services")
assert len(icons) == 1
assert icons["switch"]["turn_off"] == "mdi:toggle-switch-variant-off"
# Ensure icons file for platform isn't loaded, as that isn't supported
icons = await icon.async_get_icons(hass, "entity")
assert icons == {}
with pytest.raises(ValueError, match="test.switch"):
await icon.async_get_icons(hass, "entity", ["test.switch"])
# Load up an custom integration
hass.config.components.add("test_package")
await hass.async_block_till_done()
icons = await icon.async_get_icons(hass, "entity")
assert len(icons) == 1
assert icons == {
"test_package": {
"switch": {
"something": {"state": {"away": "mdi:home-outline", "home": "mdi:home"}}
}
}
}
icons = await icon.async_get_icons(hass, "services")
assert len(icons) == 2
assert icons["test_package"]["enable_god_mode"] == "mdi:shield"
# Load another one
hass.config.components.add("test_embedded")
await hass.async_block_till_done()
icons = await icon.async_get_icons(hass, "entity")
assert len(icons) == 2
assert icons["test_package"] == {
"switch": {
"something": {"state": {"away": "mdi:home-outline", "home": "mdi:home"}}
}
}
# Test getting non-existing integration
with pytest.raises(
IntegrationNotFound, match="Integration 'non_existing' not found"
):
await icon.async_get_icons(hass, "entity", ["non_existing"])
async def test_get_icons_while_loading_components(hass: HomeAssistant) -> None:
"""Test the get icons helper loads icons."""
integration = Mock(file_path=pathlib.Path(__file__))
integration.name = "Component 1"
hass.config.components.add("component1")
load_count = 0
def mock_load_icons_files(files):
"""Mock load icon files."""
nonlocal load_count
load_count += 1
return {"component1": {"entity": {"climate": {"test": {"icon": "mdi:home"}}}}}
with (
patch(
"homeassistant.helpers.icon._component_icons_path",
return_value="choochoo.json",
),
patch(
"homeassistant.helpers.icon._load_icons_files",
mock_load_icons_files,
),
patch(
"homeassistant.helpers.icon.async_get_integrations",
return_value={"component1": integration},
),
):
times = 5
all_icons = [await icon.async_get_icons(hass, "entity") for _ in range(times)]
assert all_icons == [
{"component1": {"climate": {"test": {"icon": "mdi:home"}}}}
for _ in range(times)
]
assert load_count == 1
async def test_caching(hass: HomeAssistant) -> None:
"""Test we cache data."""
hass.config.components.add("binary_sensor")
hass.config.components.add("switch")
# Patch with same method so we can count invocations
with patch(
"homeassistant.helpers.icon.build_resources",
side_effect=icon.build_resources,
) as mock_build:
load1 = await icon.async_get_icons(hass, "entity_component")
assert len(mock_build.mock_calls) == 2
load2 = await icon.async_get_icons(hass, "entity_component")
assert len(mock_build.mock_calls) == 2
assert load1 == load2
assert load1["binary_sensor"]
assert load1["switch"]
load_switch_only = await icon.async_get_icons(
hass, "entity_component", integrations={"switch"}
)
assert load_switch_only
assert list(load_switch_only) == ["switch"]
load_binary_sensor_only = await icon.async_get_icons(
hass, "entity_component", integrations={"binary_sensor"}
)
assert load_binary_sensor_only
assert list(load_binary_sensor_only) == ["binary_sensor"]
# Check if new loaded component, trigger load
hass.config.components.add("media_player")
with patch(
"homeassistant.helpers.icon._load_icons_files",
side_effect=icon._load_icons_files,
) as mock_load:
load_sensor_only = await icon.async_get_icons(
hass, "entity_component", integrations={"switch"}
)
assert load_sensor_only
assert len(mock_load.mock_calls) == 0
await icon.async_get_icons(
hass, "entity_component", integrations={"media_player"}
)
assert len(mock_load.mock_calls) == 1