mirror of https://github.com/home-assistant/core
446 lines
14 KiB
Python
446 lines
14 KiB
Python
"""Test entity discovery for device-specific schemas for the Z-Wave JS integration."""
|
|
|
|
import pytest
|
|
from zwave_js_server.event import Event
|
|
from zwave_js_server.model.node import Node
|
|
|
|
from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN
|
|
from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN, SERVICE_PRESS
|
|
from homeassistant.components.light import ATTR_SUPPORTED_COLOR_MODES, ColorMode
|
|
from homeassistant.components.number import (
|
|
ATTR_VALUE,
|
|
DOMAIN as NUMBER_DOMAIN,
|
|
SERVICE_SET_VALUE,
|
|
)
|
|
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
|
from homeassistant.components.switch import (
|
|
DOMAIN as SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
SERVICE_TURN_ON,
|
|
)
|
|
from homeassistant.components.zwave_js.discovery import (
|
|
FirmwareVersionRange,
|
|
ZWaveDiscoverySchema,
|
|
ZWaveValueDiscoverySchema,
|
|
)
|
|
from homeassistant.components.zwave_js.discovery_data_template import (
|
|
DynamicCurrentTempClimateDataTemplate,
|
|
)
|
|
from homeassistant.components.zwave_js.helpers import get_device_id
|
|
from homeassistant.const import ATTR_ENTITY_ID, STATE_OFF, STATE_UNKNOWN, EntityCategory
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
|
|
|
from tests.common import MockConfigEntry
|
|
|
|
|
|
async def test_aeon_smart_switch_6_state(
|
|
hass: HomeAssistant, client, aeon_smart_switch_6, integration
|
|
) -> None:
|
|
"""Test that Smart Switch 6 has a meter reset button."""
|
|
state = hass.states.get("button.smart_switch_6_reset_accumulated_values")
|
|
assert state
|
|
|
|
|
|
async def test_iblinds_v2(hass: HomeAssistant, client, iblinds_v2, integration) -> None:
|
|
"""Test that an iBlinds v2.0 multilevel switch value is discovered as a cover."""
|
|
node = iblinds_v2
|
|
assert node.device_class.specific.label == "Unused"
|
|
|
|
state = hass.states.get("light.window_blind_controller")
|
|
assert not state
|
|
|
|
state = hass.states.get("cover.window_blind_controller")
|
|
assert state
|
|
|
|
|
|
async def test_zvidar_state(hass: HomeAssistant, client, zvidar, integration) -> None:
|
|
"""Test that an ZVIDAR Z-CM-V01 multilevel switch value is discovered as a cover."""
|
|
node = zvidar
|
|
assert node.device_class.specific.label == "Unused"
|
|
|
|
state = hass.states.get("light.window_blind_controller")
|
|
assert not state
|
|
|
|
state = hass.states.get("cover.window_blind_controller")
|
|
assert state
|
|
|
|
|
|
async def test_ge_12730(hass: HomeAssistant, client, ge_12730, integration) -> None:
|
|
"""Test GE 12730 Fan Controller v2.0 multilevel switch is discovered as a fan."""
|
|
node = ge_12730
|
|
assert node.device_class.specific.label == "Multilevel Power Switch"
|
|
|
|
state = hass.states.get("light.in_wall_smart_fan_control")
|
|
assert not state
|
|
|
|
state = hass.states.get("fan.in_wall_smart_fan_control")
|
|
assert state
|
|
|
|
|
|
async def test_inovelli_lzw36(
|
|
hass: HomeAssistant, client, inovelli_lzw36, integration
|
|
) -> None:
|
|
"""Test LZW36 Fan Controller multilevel switch endpoint 2 is discovered as a fan."""
|
|
node = inovelli_lzw36
|
|
assert node.device_class.specific.label == "Unused"
|
|
|
|
state = hass.states.get("light.family_room_combo")
|
|
assert state.state == "off"
|
|
|
|
state = hass.states.get("fan.family_room_combo_2")
|
|
assert state
|
|
|
|
|
|
async def test_vision_security_zl7432(
|
|
hass: HomeAssistant, client, vision_security_zl7432, integration
|
|
) -> None:
|
|
"""Test Vision Security ZL7432 is caught by the device specific discovery."""
|
|
for entity_id in (
|
|
"switch.in_wall_dual_relay_switch",
|
|
"switch.in_wall_dual_relay_switch_2",
|
|
):
|
|
state = hass.states.get(entity_id)
|
|
assert state
|
|
assert state.attributes["assumed_state"]
|
|
|
|
|
|
async def test_lock_popp_electric_strike_lock_control(
|
|
hass: HomeAssistant, client, lock_popp_electric_strike_lock_control, integration
|
|
) -> None:
|
|
"""Test that the Popp Electric Strike Lock Control gets discovered correctly."""
|
|
assert hass.states.get("lock.node_62") is not None
|
|
assert (
|
|
hass.states.get("binary_sensor.node_62_the_current_status_of_the_door")
|
|
is not None
|
|
)
|
|
assert hass.states.get("select.node_62_current_lock_mode") is not None
|
|
|
|
|
|
async def test_fortrez_ssa3_siren(
|
|
hass: HomeAssistant, client, fortrezz_ssa3_siren, integration
|
|
) -> None:
|
|
"""Test Fortrezz SSA3 siren gets discovered correctly."""
|
|
assert hass.states.get("select.siren_and_strobe_alarm") is not None
|
|
|
|
|
|
async def test_firmware_version_range_exception(hass: HomeAssistant) -> None:
|
|
"""Test FirmwareVersionRange exception."""
|
|
with pytest.raises(ValueError):
|
|
ZWaveDiscoverySchema(
|
|
"test",
|
|
ZWaveValueDiscoverySchema(command_class=1),
|
|
firmware_version_range=FirmwareVersionRange(),
|
|
)
|
|
|
|
|
|
async def test_dynamic_climate_data_discovery_template_failure(
|
|
hass: HomeAssistant, multisensor_6
|
|
) -> None:
|
|
"""Test that initing a DynamicCurrentTempClimateDataTemplate with no data raises."""
|
|
node = multisensor_6
|
|
with pytest.raises(ValueError):
|
|
DynamicCurrentTempClimateDataTemplate().resolve_data(
|
|
node.values[f"{node.node_id}-49-0-Ultraviolet"]
|
|
)
|
|
|
|
|
|
async def test_merten_507801(
|
|
hass: HomeAssistant, client, merten_507801, integration
|
|
) -> None:
|
|
"""Test that Merten 507801 multilevel switch value is discovered as a cover."""
|
|
node = merten_507801
|
|
assert node.device_class.specific.label == "Unused"
|
|
|
|
state = hass.states.get("light.connect_roller_shutter")
|
|
assert not state
|
|
|
|
state = hass.states.get("cover.connect_roller_shutter")
|
|
assert state
|
|
|
|
|
|
async def test_shelly_001p10_disabled_entities(
|
|
hass: HomeAssistant,
|
|
entity_registry: er.EntityRegistry,
|
|
client,
|
|
shelly_qnsh_001P10_shutter,
|
|
integration,
|
|
) -> None:
|
|
"""Test that Shelly 001P10 entity created by endpoint 2 is disabled."""
|
|
entity_ids = [
|
|
"cover.wave_shutter_2",
|
|
]
|
|
for entity_id in entity_ids:
|
|
state = hass.states.get(entity_id)
|
|
assert state is None
|
|
entry = entity_registry.async_get(entity_id)
|
|
assert entry
|
|
assert entry.disabled
|
|
assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION
|
|
|
|
# Test enabling entity
|
|
updated_entry = entity_registry.async_update_entity(
|
|
entry.entity_id, disabled_by=None
|
|
)
|
|
assert updated_entry != entry
|
|
assert updated_entry.disabled is False
|
|
|
|
# Test if the main entity from endpoint 1 was created.
|
|
state = hass.states.get("cover.wave_shutter")
|
|
assert state
|
|
|
|
|
|
async def test_merten_507801_disabled_enitites(
|
|
hass: HomeAssistant,
|
|
entity_registry: er.EntityRegistry,
|
|
client,
|
|
merten_507801,
|
|
integration,
|
|
) -> None:
|
|
"""Test that Merten 507801 entities created by endpoint 2 are disabled."""
|
|
entity_ids = [
|
|
"cover.connect_roller_shutter_2",
|
|
"select.connect_roller_shutter_local_protection_state_2",
|
|
"select.connect_roller_shutter_rf_protection_state_2",
|
|
]
|
|
for entity_id in entity_ids:
|
|
state = hass.states.get(entity_id)
|
|
assert state is None
|
|
entry = entity_registry.async_get(entity_id)
|
|
assert entry
|
|
assert entry.disabled
|
|
assert entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION
|
|
|
|
# Test enabling entity
|
|
updated_entry = entity_registry.async_update_entity(
|
|
entry.entity_id, disabled_by=None
|
|
)
|
|
assert updated_entry != entry
|
|
assert updated_entry.disabled is False
|
|
|
|
|
|
async def test_zooz_zen72(
|
|
hass: HomeAssistant,
|
|
entity_registry: er.EntityRegistry,
|
|
client,
|
|
switch_zooz_zen72,
|
|
integration,
|
|
) -> None:
|
|
"""Test that Zooz ZEN72 Indicators are discovered as number entities."""
|
|
assert len(hass.states.async_entity_ids(NUMBER_DOMAIN)) == 1
|
|
assert len(hass.states.async_entity_ids(BUTTON_DOMAIN)) == 2 # includes ping
|
|
entity_id = "number.z_wave_plus_700_series_dimmer_switch_indicator_value"
|
|
entry = entity_registry.async_get(entity_id)
|
|
assert entry
|
|
assert entry.entity_category == EntityCategory.CONFIG
|
|
state = hass.states.get(entity_id)
|
|
assert state
|
|
assert state.state == STATE_UNKNOWN
|
|
|
|
await hass.services.async_call(
|
|
NUMBER_DOMAIN,
|
|
SERVICE_SET_VALUE,
|
|
{
|
|
ATTR_ENTITY_ID: entity_id,
|
|
ATTR_VALUE: 5,
|
|
},
|
|
blocking=True,
|
|
)
|
|
assert len(client.async_send_command.call_args_list) == 1
|
|
args = client.async_send_command.call_args[0][0]
|
|
assert args["command"] == "node.set_value"
|
|
assert args["nodeId"] == switch_zooz_zen72.node_id
|
|
assert args["valueId"] == {
|
|
"commandClass": 135,
|
|
"endpoint": 0,
|
|
"property": "value",
|
|
}
|
|
assert args["value"] == 5
|
|
|
|
client.async_send_command.reset_mock()
|
|
|
|
entity_id = "button.z_wave_plus_700_series_dimmer_switch_identify"
|
|
entry = entity_registry.async_get(entity_id)
|
|
assert entry
|
|
assert entry.entity_category == EntityCategory.CONFIG
|
|
await hass.services.async_call(
|
|
BUTTON_DOMAIN,
|
|
SERVICE_PRESS,
|
|
{ATTR_ENTITY_ID: entity_id},
|
|
blocking=True,
|
|
)
|
|
assert len(client.async_send_command.call_args_list) == 1
|
|
args = client.async_send_command.call_args[0][0]
|
|
assert args["command"] == "node.set_value"
|
|
assert args["nodeId"] == switch_zooz_zen72.node_id
|
|
assert args["valueId"] == {
|
|
"commandClass": 135,
|
|
"endpoint": 0,
|
|
"property": "identify",
|
|
}
|
|
assert args["value"] is True
|
|
|
|
|
|
async def test_indicator_test(
|
|
hass: HomeAssistant,
|
|
device_registry: dr.DeviceRegistry,
|
|
entity_registry: er.EntityRegistry,
|
|
client,
|
|
indicator_test,
|
|
integration,
|
|
) -> None:
|
|
"""Test that Indicators are discovered properly.
|
|
|
|
This test covers indicators that we don't already have device fixtures for.
|
|
"""
|
|
device = device_registry.async_get_device(
|
|
identifiers={get_device_id(client.driver, indicator_test)}
|
|
)
|
|
assert device
|
|
entities = er.async_entries_for_device(entity_registry, device.id)
|
|
|
|
def len_domain(domain):
|
|
return len([entity for entity in entities if entity.domain == domain])
|
|
|
|
assert len_domain(NUMBER_DOMAIN) == 0
|
|
assert len_domain(BUTTON_DOMAIN) == 1 # only ping
|
|
assert len_domain(BINARY_SENSOR_DOMAIN) == 1
|
|
assert len_domain(SENSOR_DOMAIN) == 3 # include node status + last seen
|
|
assert len_domain(SWITCH_DOMAIN) == 1
|
|
|
|
entity_id = "binary_sensor.this_is_a_fake_device_binary_sensor"
|
|
entry = entity_registry.async_get(entity_id)
|
|
assert entry
|
|
assert entry.entity_category == EntityCategory.DIAGNOSTIC
|
|
state = hass.states.get(entity_id)
|
|
assert state
|
|
assert state.state == STATE_OFF
|
|
|
|
client.async_send_command.reset_mock()
|
|
|
|
entity_id = "sensor.this_is_a_fake_device_sensor"
|
|
entry = entity_registry.async_get(entity_id)
|
|
assert entry
|
|
assert entry.entity_category == EntityCategory.DIAGNOSTIC
|
|
state = hass.states.get(entity_id)
|
|
assert state
|
|
assert state.state == "0.0"
|
|
|
|
client.async_send_command.reset_mock()
|
|
|
|
entity_id = "switch.this_is_a_fake_device_switch"
|
|
entry = entity_registry.async_get(entity_id)
|
|
assert entry
|
|
assert entry.entity_category == EntityCategory.CONFIG
|
|
state = hass.states.get(entity_id)
|
|
assert state
|
|
assert state.state == STATE_OFF
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_ON,
|
|
{ATTR_ENTITY_ID: entity_id},
|
|
blocking=True,
|
|
)
|
|
assert len(client.async_send_command.call_args_list) == 1
|
|
args = client.async_send_command.call_args[0][0]
|
|
assert args["command"] == "node.set_value"
|
|
assert args["nodeId"] == indicator_test.node_id
|
|
assert args["valueId"] == {
|
|
"commandClass": 135,
|
|
"endpoint": 0,
|
|
"property": "Test",
|
|
"propertyKey": "Switch",
|
|
}
|
|
assert args["value"] is True
|
|
|
|
client.async_send_command.reset_mock()
|
|
|
|
await hass.services.async_call(
|
|
SWITCH_DOMAIN,
|
|
SERVICE_TURN_OFF,
|
|
{ATTR_ENTITY_ID: entity_id},
|
|
blocking=True,
|
|
)
|
|
assert len(client.async_send_command.call_args_list) == 1
|
|
args = client.async_send_command.call_args[0][0]
|
|
assert args["command"] == "node.set_value"
|
|
assert args["nodeId"] == indicator_test.node_id
|
|
assert args["valueId"] == {
|
|
"commandClass": 135,
|
|
"endpoint": 0,
|
|
"property": "Test",
|
|
"propertyKey": "Switch",
|
|
}
|
|
assert args["value"] is False
|
|
|
|
|
|
async def test_light_device_class_is_null(
|
|
hass: HomeAssistant, client, light_device_class_is_null, integration
|
|
) -> None:
|
|
"""Test that a Multilevel Switch CC value with a null device class is discovered as a light.
|
|
|
|
Tied to #117121.
|
|
"""
|
|
node = light_device_class_is_null
|
|
assert node.device_class is None
|
|
assert hass.states.get("light.bar_display_cases")
|
|
|
|
|
|
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
|
async def test_rediscovery(
|
|
hass: HomeAssistant,
|
|
siren_neo_coolcam: Node,
|
|
integration: MockConfigEntry,
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""Test that we don't rediscover known values."""
|
|
node = siren_neo_coolcam
|
|
entity_id = "select.siren_alarm_doorbell_sound_selection"
|
|
state = hass.states.get(entity_id)
|
|
|
|
assert state
|
|
assert state.state == "Beep"
|
|
|
|
event = Event(
|
|
type="value updated",
|
|
data={
|
|
"source": "node",
|
|
"event": "value updated",
|
|
"nodeId": 36,
|
|
"args": {
|
|
"commandClassName": "Configuration",
|
|
"commandClass": 112,
|
|
"endpoint": 0,
|
|
"property": 6,
|
|
"newValue": 9,
|
|
"prevValue": 10,
|
|
"propertyName": "Doorbell Sound Selection",
|
|
},
|
|
},
|
|
)
|
|
node.receive_event(event)
|
|
await hass.async_block_till_done()
|
|
|
|
state = hass.states.get(entity_id)
|
|
|
|
assert state
|
|
assert state.state == "Beep Beep"
|
|
assert "Platform zwave_js does not generate unique IDs" not in caplog.text
|
|
|
|
|
|
async def test_aeotec_smart_switch_7(
|
|
hass: HomeAssistant,
|
|
aeotec_smart_switch_7: Node,
|
|
integration: MockConfigEntry,
|
|
) -> None:
|
|
"""Test that Smart Switch 7 has a light and a switch entity."""
|
|
state = hass.states.get("light.smart_switch_7")
|
|
assert state
|
|
assert state.attributes[ATTR_SUPPORTED_COLOR_MODES] == [
|
|
ColorMode.HS,
|
|
]
|
|
|
|
state = hass.states.get("switch.smart_switch_7")
|
|
assert state
|