Add shutter_tilt support for Fibaro FGR 223 (#96283)

* add support for shutter_tilt for Fibaro FGR 223
add tests for fgr 223

* Adjust comments and docstring

---------

Co-authored-by: Lennart <18117505+Ced4@users.noreply.github.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
pull/101386/head
lennart24 2023-09-28 12:59:02 +02:00 committed by Franck Nijhof
parent b02f64196b
commit 9ab340047d
No known key found for this signature in database
GPG Key ID: D62583BA8AB11CA3
4 changed files with 2530 additions and 7 deletions

View File

@ -160,6 +160,8 @@ class ZWaveValueDiscoverySchema(DataclassMustHaveAtLeastOne):
writeable: bool | None = None
# [optional] the value's states map must include ANY of these key/value pairs
any_available_states: set[tuple[int, str]] | None = None
# [optional] the value's value must match this value
value: Any | None = None
@dataclass
@ -378,6 +380,61 @@ DISCOVERY_SCHEMAS = [
)
],
),
# Fibaro Shutter Fibaro FGR223
# Combine both switch_multilevel endpoints into shutter_tilt
# if operating mode (151) is set to venetian blind (2)
ZWaveDiscoverySchema(
platform=Platform.COVER,
hint="shutter_tilt",
manufacturer_id={0x010F},
product_id={0x1000, 0x1001},
product_type={0x0303},
primary_value=ZWaveValueDiscoverySchema(
command_class={CommandClass.SWITCH_MULTILEVEL},
property={CURRENT_VALUE_PROPERTY},
endpoint={1},
type={ValueType.NUMBER},
),
data_template=CoverTiltDataTemplate(
current_tilt_value_id=ZwaveValueID(
property_=CURRENT_VALUE_PROPERTY,
command_class=CommandClass.SWITCH_MULTILEVEL,
endpoint=2,
),
target_tilt_value_id=ZwaveValueID(
property_=TARGET_VALUE_PROPERTY,
command_class=CommandClass.SWITCH_MULTILEVEL,
endpoint=2,
),
),
required_values=[
ZWaveValueDiscoverySchema(
command_class={CommandClass.CONFIGURATION},
property={151},
endpoint={0},
value={2},
)
],
),
# Fibaro Shutter Fibaro FGR223
# Disable endpoint 2 (slat),
# as these are either combined with endpoint one as shutter_tilt
# or it has no practical function.
# CC: Switch_Multilevel
ZWaveDiscoverySchema(
platform=Platform.COVER,
hint="shutter",
manufacturer_id={0x010F},
product_id={0x1000, 0x1001},
product_type={0x0303},
primary_value=ZWaveValueDiscoverySchema(
command_class={CommandClass.SWITCH_MULTILEVEL},
property={CURRENT_VALUE_PROPERTY},
endpoint={2},
type={ValueType.NUMBER},
),
entity_registry_enabled_default=False,
),
# Fibaro Nice BiDi-ZWave (IBT4ZWAVE)
ZWaveDiscoverySchema(
platform=Platform.COVER,
@ -1236,6 +1293,9 @@ def check_value(value: ZwaveValue, schema: ZWaveValueDiscoverySchema) -> bool:
)
):
return False
# check value
if schema.value is not None and value.value not in schema.value:
return False
return True

View File

@ -483,6 +483,12 @@ def fibaro_fgr222_shutter_state_fixture():
return json.loads(load_fixture("zwave_js/cover_fibaro_fgr222_state.json"))
@pytest.fixture(name="fibaro_fgr223_shutter_state", scope="session")
def fibaro_fgr223_shutter_state_fixture():
"""Load the Fibaro FGR223 node state fixture data."""
return json.loads(load_fixture("zwave_js/cover_fibaro_fgr223_state.json"))
@pytest.fixture(name="merten_507801_state", scope="session")
def merten_507801_state_fixture():
"""Load the Merten 507801 Shutter node state fixture data."""
@ -1054,6 +1060,14 @@ def fibaro_fgr222_shutter_cover_fixture(client, fibaro_fgr222_shutter_state):
return node
@pytest.fixture(name="fibaro_fgr223_shutter")
def fibaro_fgr223_shutter_cover_fixture(client, fibaro_fgr223_shutter_state):
"""Mock a Fibaro FGR223 Shutter node."""
node = Node(client, copy.deepcopy(fibaro_fgr223_shutter_state))
client.driver.controller.nodes[node.node_id] = node
return node
@pytest.fixture(name="merten_507801")
def merten_507801_cover_fixture(client, merten_507801_state):
"""Mock a Merten 507801 Shutter node."""

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,8 @@ GDC_COVER_ENTITY = "cover.aeon_labs_garage_door_controller_gen5"
BLIND_COVER_ENTITY = "cover.window_blind_controller"
SHUTTER_COVER_ENTITY = "cover.flush_shutter"
AEOTEC_SHUTTER_COVER_ENTITY = "cover.nano_shutter_v_3"
FIBARO_SHUTTER_COVER_ENTITY = "cover.fgr_222_test_cover"
FIBARO_FGR_222_SHUTTER_COVER_ENTITY = "cover.fgr_222_test_cover"
FIBARO_FGR_223_SHUTTER_COVER_ENTITY = "cover.fgr_223_test_cover"
LOGGER.setLevel(logging.DEBUG)
@ -238,7 +239,7 @@ async def test_fibaro_fgr222_shutter_cover(
hass: HomeAssistant, client, fibaro_fgr222_shutter, integration
) -> None:
"""Test tilt function of the Fibaro Shutter devices."""
state = hass.states.get(FIBARO_SHUTTER_COVER_ENTITY)
state = hass.states.get(FIBARO_FGR_222_SHUTTER_COVER_ENTITY)
assert state
assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.SHUTTER
@ -249,7 +250,7 @@ async def test_fibaro_fgr222_shutter_cover(
await hass.services.async_call(
DOMAIN,
SERVICE_OPEN_COVER_TILT,
{ATTR_ENTITY_ID: FIBARO_SHUTTER_COVER_ENTITY},
{ATTR_ENTITY_ID: FIBARO_FGR_222_SHUTTER_COVER_ENTITY},
blocking=True,
)
@ -271,7 +272,7 @@ async def test_fibaro_fgr222_shutter_cover(
await hass.services.async_call(
DOMAIN,
SERVICE_CLOSE_COVER_TILT,
{ATTR_ENTITY_ID: FIBARO_SHUTTER_COVER_ENTITY},
{ATTR_ENTITY_ID: FIBARO_FGR_222_SHUTTER_COVER_ENTITY},
blocking=True,
)
@ -293,7 +294,7 @@ async def test_fibaro_fgr222_shutter_cover(
await hass.services.async_call(
DOMAIN,
SERVICE_SET_COVER_TILT_POSITION,
{ATTR_ENTITY_ID: FIBARO_SHUTTER_COVER_ENTITY, ATTR_TILT_POSITION: 12},
{ATTR_ENTITY_ID: FIBARO_FGR_222_SHUTTER_COVER_ENTITY, ATTR_TILT_POSITION: 12},
blocking=True,
)
@ -330,7 +331,101 @@ async def test_fibaro_fgr222_shutter_cover(
},
)
fibaro_fgr222_shutter.receive_event(event)
state = hass.states.get(FIBARO_SHUTTER_COVER_ENTITY)
state = hass.states.get(FIBARO_FGR_222_SHUTTER_COVER_ENTITY)
assert state
assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 100
async def test_fibaro_fgr223_shutter_cover(
hass: HomeAssistant, client, fibaro_fgr223_shutter, integration
) -> None:
"""Test tilt function of the Fibaro Shutter devices."""
state = hass.states.get(FIBARO_FGR_223_SHUTTER_COVER_ENTITY)
assert state
assert state.attributes[ATTR_DEVICE_CLASS] == CoverDeviceClass.SHUTTER
assert state.state == STATE_OPEN
assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 0
# Test opening tilts
await hass.services.async_call(
DOMAIN,
SERVICE_OPEN_COVER_TILT,
{ATTR_ENTITY_ID: FIBARO_FGR_223_SHUTTER_COVER_ENTITY},
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"] == 10
assert args["valueId"] == {
"endpoint": 2,
"commandClass": 38,
"property": "targetValue",
}
assert args["value"] == 99
client.async_send_command.reset_mock()
# Test closing tilts
await hass.services.async_call(
DOMAIN,
SERVICE_CLOSE_COVER_TILT,
{ATTR_ENTITY_ID: FIBARO_FGR_223_SHUTTER_COVER_ENTITY},
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"] == 10
assert args["valueId"] == {
"endpoint": 2,
"commandClass": 38,
"property": "targetValue",
}
assert args["value"] == 0
client.async_send_command.reset_mock()
# Test setting tilt position
await hass.services.async_call(
DOMAIN,
SERVICE_SET_COVER_TILT_POSITION,
{ATTR_ENTITY_ID: FIBARO_FGR_223_SHUTTER_COVER_ENTITY, ATTR_TILT_POSITION: 12},
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"] == 10
assert args["valueId"] == {
"endpoint": 2,
"commandClass": 38,
"property": "targetValue",
}
assert args["value"] == 12
# Test some tilt
event = Event(
type="value updated",
data={
"source": "node",
"event": "value updated",
"nodeId": 10,
"args": {
"commandClassName": "Multilevel Switch",
"commandClass": 38,
"endpoint": 2,
"property": "currentValue",
"newValue": 99,
"prevValue": 0,
"propertyName": "currentValue",
},
},
)
fibaro_fgr223_shutter.receive_event(event)
state = hass.states.get(FIBARO_FGR_223_SHUTTER_COVER_ENTITY)
assert state
assert state.attributes[ATTR_CURRENT_TILT_POSITION] == 100
@ -694,13 +789,42 @@ async def test_fibaro_fgr222_shutter_cover_no_tilt(
client.driver.controller.emit("node added", {"node": node})
await hass.async_block_till_done()
state = hass.states.get(FIBARO_SHUTTER_COVER_ENTITY)
state = hass.states.get(FIBARO_FGR_222_SHUTTER_COVER_ENTITY)
assert state
assert state.state == STATE_UNKNOWN
assert ATTR_CURRENT_POSITION not in state.attributes
assert ATTR_CURRENT_TILT_POSITION not in state.attributes
async def test_fibaro_fgr223_shutter_cover_no_tilt(
hass: HomeAssistant, client, fibaro_fgr223_shutter_state, integration
) -> None:
"""Test absence of tilt function for Fibaro Shutter roller blind.
Fibaro Shutter devices can have operating mode set to roller blind (1).
"""
node_state = replace_value_of_zwave_value(
fibaro_fgr223_shutter_state,
[
ZwaveValueMatcher(
property_=151,
command_class=CommandClass.CONFIGURATION,
endpoint=0,
),
],
1,
)
node = Node(client, node_state)
client.driver.controller.emit("node added", {"node": node})
await hass.async_block_till_done()
state = hass.states.get(FIBARO_FGR_223_SHUTTER_COVER_ENTITY)
assert state
assert state.state == STATE_OPEN
assert ATTR_CURRENT_POSITION in state.attributes
assert ATTR_CURRENT_TILT_POSITION not in state.attributes
async def test_iblinds_v3_cover(
hass: HomeAssistant, client, iblinds_v3, integration
) -> None: