mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 10:13:52 -05:00
Add Leviton VRF01 fan controller discovery to zwave_js (#175631)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0c614fae39
commit
ea8c1fd076
@@ -258,6 +258,18 @@ DISCOVERY_SCHEMAS = [
|
||||
FanValueMapping(speeds=[(1, 25), (26, 50), (51, 75), (76, 99)]),
|
||||
),
|
||||
),
|
||||
# Leviton VRF01 fan controllers using switch multilevel CC
|
||||
ZWaveDiscoverySchema(
|
||||
platform=Platform.FAN,
|
||||
hint="has_fan_value_mapping",
|
||||
manufacturer_id={0x001D},
|
||||
product_id={0x0209, 0x0334},
|
||||
product_type={0x1001},
|
||||
primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
|
||||
data_template=FixedFanValueMappingDataTemplate(
|
||||
FanValueMapping(speeds=[(1, 32), (33, 66), (67, 99)]),
|
||||
),
|
||||
),
|
||||
# Inovelli LZW36 light / fan controller combo using switch multilevel CC
|
||||
# The fan is endpoint 2, the light is endpoint 1.
|
||||
ZWaveDiscoverySchema(
|
||||
|
||||
@@ -256,6 +256,12 @@ def leviton_zw4sf_state_fixture() -> dict[str, Any]:
|
||||
return load_json_object_fixture("leviton_zw4sf_state.json", DOMAIN)
|
||||
|
||||
|
||||
@pytest.fixture(name="leviton_vrf01_state", scope="package")
|
||||
def leviton_vrf01_state_fixture() -> dict[str, Any]:
|
||||
"""Load the Leviton VRF01 node state fixture data."""
|
||||
return load_json_object_fixture("leviton_vrf01_state.json", DOMAIN)
|
||||
|
||||
|
||||
@pytest.fixture(name="fan_honeywell_39358_state", scope="package")
|
||||
def fan_honeywell_39358_state_fixture() -> dict[str, Any]:
|
||||
"""Load the fan node state fixture data."""
|
||||
@@ -1071,6 +1077,14 @@ def leviton_zw4sf_fixture(client, leviton_zw4sf_state) -> Node:
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="leviton_vrf01")
|
||||
def leviton_vrf01_fixture(client, leviton_vrf01_state) -> Node:
|
||||
"""Mock a fan node."""
|
||||
node = Node(client, copy.deepcopy(leviton_vrf01_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="fan_honeywell_39358")
|
||||
def fan_honeywell_39358_fixture(client, fan_honeywell_39358_state) -> Node:
|
||||
"""Mock a fan node."""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -139,6 +139,17 @@ async def test_inovelli_lzw36(
|
||||
assert state
|
||||
|
||||
|
||||
async def test_leviton_vrf01(
|
||||
hass: HomeAssistant, client, leviton_vrf01, integration
|
||||
) -> None:
|
||||
"""Test Leviton VRF01 multilevel switch is discovered as a fan, not a light."""
|
||||
node = leviton_vrf01
|
||||
assert node.device_class.specific.label == "Multilevel Scene Switch"
|
||||
|
||||
assert hass.states.get("fan.fan")
|
||||
assert not hass.states.get("light.fan")
|
||||
|
||||
|
||||
async def test_vision_security_zl7432(
|
||||
hass: HomeAssistant, client, vision_security_zl7432, integration
|
||||
) -> None:
|
||||
|
||||
@@ -719,6 +719,76 @@ async def test_leviton_zw4sf_fan(
|
||||
assert state.attributes[ATTR_PRESET_MODES] == []
|
||||
|
||||
|
||||
async def test_leviton_vrf01_fan(
|
||||
hass: HomeAssistant, client, leviton_vrf01, integration
|
||||
) -> None:
|
||||
"""Test a Leviton VRF01 fan with 3 fixed speeds."""
|
||||
node = leviton_vrf01
|
||||
node_id = node.node_id
|
||||
entity_id = "fan.fan"
|
||||
|
||||
async def get_zwave_speed_from_percentage(percentage):
|
||||
"""Set the fan to a particular percentage and get the resulting Zwave speed."""
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"fan",
|
||||
"turn_on",
|
||||
{"entity_id": entity_id, "percentage": percentage},
|
||||
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"] == node_id
|
||||
return args["value"]
|
||||
|
||||
async def get_percentage_from_zwave_speed(zwave_speed):
|
||||
"""Set the underlying device speed and get the resulting percentage."""
|
||||
event = Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": node_id,
|
||||
"args": {
|
||||
"commandClassName": "Multilevel Switch",
|
||||
"commandClass": 38,
|
||||
"endpoint": 0,
|
||||
"property": "currentValue",
|
||||
"newValue": zwave_speed,
|
||||
"prevValue": 0,
|
||||
"propertyName": "currentValue",
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
state = hass.states.get(entity_id)
|
||||
return state.attributes[ATTR_PERCENTAGE]
|
||||
|
||||
# This device has the speeds:
|
||||
# 1 = 1-32, 2 = 33-66, 3 = 67-99
|
||||
percentages_to_zwave_speeds = [
|
||||
[[0], [0]],
|
||||
[range(1, 34), range(1, 33)],
|
||||
[range(34, 67), range(33, 67)],
|
||||
[range(67, 101), range(67, 100)],
|
||||
]
|
||||
|
||||
for percentages, zwave_speeds in percentages_to_zwave_speeds:
|
||||
for percentage in percentages:
|
||||
actual_zwave_speed = await get_zwave_speed_from_percentage(percentage)
|
||||
assert actual_zwave_speed in zwave_speeds
|
||||
for zwave_speed in zwave_speeds:
|
||||
actual_percentage = await get_percentage_from_zwave_speed(zwave_speed)
|
||||
assert actual_percentage in percentages
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.attributes[ATTR_PERCENTAGE_STEP] == pytest.approx(100 / 3, rel=1e-3)
|
||||
assert state.attributes[ATTR_PRESET_MODES] == []
|
||||
|
||||
|
||||
async def test_enbrighten_55258_zw4002_fan(
|
||||
hass: HomeAssistant, client, enbrighten_55258_zw4002, integration
|
||||
) -> None:
|
||||
|
||||
Reference in New Issue
Block a user