Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions homeassistant/components/zwave_js/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
is_opening_state_notification_value,
)
from .models import (
FirmwareVersionRange,
NewZWaveDiscoverySchema,
ValueType,
ZwaveDiscoveryInfo,
Expand Down Expand Up @@ -1346,6 +1347,38 @@ def __init__(
),
entity_class=ZWaveBooleanBinarySensor,
),
NewZWaveDiscoverySchema(
# Fibaro FGMS001 Motion Sensor:
# On firmware <= 2.8 the device supports Binary Sensor CC v1, which
# does not give us any information about the type of the sensor.
# As a result it is exposed via the generic "Any" sensor type,
# which fits no other discovery schema.
platform=Platform.BINARY_SENSOR,
manufacturer_id={0x010F},
product_type={0x0800, 0x0801, 0x8800},
product_id={
0x1001,
0x1002,
0x2001,
0x2002,
0x3001,
0x3002,
0x4001,
0x4002,
0x6001,
},
firmware_version_range=FirmwareVersionRange(max="2.8"),
primary_value=ZWaveValueDiscoverySchema(
command_class={CommandClass.SENSOR_BINARY},
property={"Any"},
type={ValueType.BOOLEAN},
),
Comment thread
AlCalzone marked this conversation as resolved.
entity_description=BinarySensorEntityDescription(
key="motion",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not necessarily needed, right? By default, the sensor's name matches the device.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nice to include the sensor feature in the name since there often is more than one sensor on a device. Lights and switches etc don't need an extra name since those seldom are more than one per device.

device_class=BinarySensorDeviceClass.MOTION,
),
entity_class=ZWaveBooleanBinarySensor,
),
NewZWaveDiscoverySchema(
platform=Platform.BINARY_SENSOR,
primary_value=ZWaveValueDiscoverySchema(
Expand Down
21 changes: 21 additions & 0 deletions tests/components/zwave_js/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,17 @@ def hoppe_ehandle_connectsense_state_fixture() -> NodeDataType:
)


@pytest.fixture(name="fibaro_fgms001_v2_8_state")
def fibaro_fgms001_v2_8_state_fixture() -> NodeDataType:
"""Load node state fixture data for Fibaro FGMS001 on firmware 2.8."""
return cast(
NodeDataType,
# Note: this fixture was created from a simulated device.
# If necessary, replace it with one created from a real FGMS001
load_json_object_fixture("fibaro_fgms001_v2_8_state.json", DOMAIN),
)


# model fixtures


Expand Down Expand Up @@ -1492,3 +1503,13 @@ def hoppe_ehandle_connectsense_fixture(
node = Node(client, hoppe_ehandle_connectsense_state)
client.driver.controller.nodes[node.node_id] = node
return node


@pytest.fixture(name="fibaro_fgms001_v2_8")
def fibaro_fgms001_v2_8_fixture(
client: MagicMock, fibaro_fgms001_v2_8_state: NodeDataType
) -> Node:
"""Load node for Fibaro FGMS001 on firmware 2.8."""
node = Node(client, fibaro_fgms001_v2_8_state)
client.driver.controller.nodes[node.node_id] = node
return node
198 changes: 198 additions & 0 deletions tests/components/zwave_js/fixtures/fibaro_fgms001_v2_8_state.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
{
"nodeId": 2,
"index": 0,
"status": 4,
"ready": true,
"isListening": true,
"isRouting": true,
"isSecure": false,
"manufacturerId": 271,
"productId": 4097,
"productType": 2048,
"firmwareVersion": "2.8",
"deviceConfig": {
"filename": "/data/db/devices/0x010f/fgms001.json",
"manufacturerId": 271,
"manufacturer": "Fibargroup",
"label": "FGMS001",
"description": "Motion Sensor",
"devices": [
{
"productType": 2048,
"productId": 4097
}
],
"firmwareVersion": {
"min": "0.0",
"max": "255.255"
},
"paramInformation": {
"_map": {}
}
},
"label": "FGMS001",
"interviewAttempts": 1,
"isFrequentListening": false,
"maxDataRate": 100000,
"supportedDataRates": [40000, 9600, 100000],
"protocolVersion": 3,
"supportsBeaming": true,
"supportsSecurity": false,
"nodeType": 1,
"deviceClass": {
"basic": {
"key": 4,
"label": "Routing End Node"
},
"generic": {
"key": 7,
"label": "Notification Sensor"
},
"specific": {
"key": 1,
"label": "Notification Sensor"
}
},
"interviewStage": "Complete",
"deviceDatabaseUrl": "https://devices.zwave-js.io/?jumpTo=0x010f:0x0800:0x1001:2.8",
"highestSecurityClass": -1,
"isControllerNode": false,
"keepAwake": false,
"protocol": 0,
"values": [
{
"endpoint": 0,
"commandClass": 48,
"commandClassName": "Binary Sensor",
"property": "Any",
"propertyName": "Any",
"ccVersion": 1,
"metadata": {
"type": "boolean",
"readable": true,
"writeable": false,
"label": "Sensor state (Any)",
"ccSpecific": {
"sensorType": 255
},
"stateful": true,
"secret": false
},
"value": false
},
{
"endpoint": 0,
"commandClass": 114,
"commandClassName": "Manufacturer Specific",
"property": "productId",
"propertyName": "productId",
"ccVersion": 1,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Product ID",
"min": 0,
"max": 65535,
"stateful": true,
"secret": false
},
"value": 4097
},
{
"endpoint": 0,
"commandClass": 114,
"commandClassName": "Manufacturer Specific",
"property": "productType",
"propertyName": "productType",
"ccVersion": 1,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Product type",
"min": 0,
"max": 65535,
"stateful": true,
"secret": false
},
"value": 2048
},
{
"endpoint": 0,
"commandClass": 114,
"commandClassName": "Manufacturer Specific",
"property": "manufacturerId",
"propertyName": "manufacturerId",
"ccVersion": 1,
"metadata": {
"type": "number",
"readable": true,
"writeable": false,
"label": "Manufacturer ID",
"min": 0,
"max": 65535,
"stateful": true,
"secret": false
},
"value": 271
},
{
"endpoint": 0,
"commandClass": 134,
"commandClassName": "Version",
"property": "firmwareVersions",
"propertyName": "firmwareVersions",
"ccVersion": 1,
"metadata": {
"type": "string[]",
"readable": true,
"writeable": false,
"label": "Z-Wave chip firmware versions",
"stateful": true,
"secret": false
},
"value": ["2.8"]
}
],
"endpoints": [
{
"nodeId": 2,
"index": 0,
"deviceClass": {
"basic": {
"key": 4,
"label": "Routing End Node"
},
"generic": {
"key": 7,
"label": "Notification Sensor"
},
"specific": {
"key": 1,
"label": "Notification Sensor"
}
},
"commandClasses": [
{
"id": 134,
"name": "Version",
"version": 1,
"isSecure": false
},
{
"id": 114,
"name": "Manufacturer Specific",
"version": 1,
"isSecure": false
},
{
"id": 48,
"name": "Binary Sensor",
"version": 1,
"isSecure": false
}
]
}
]
}
50 changes: 50 additions & 0 deletions tests/components/zwave_js/test_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
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,
BinarySensorDeviceClass,
)
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 (
Expand All @@ -28,8 +32,10 @@
from homeassistant.components.zwave_js.discovery_data_template import (
DynamicCurrentTempClimateDataTemplate,
)
from homeassistant.components.zwave_js.helpers import get_device_id
from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_ENTITY_ID,
STATE_OFF,
STATE_UNKNOWN,
Expand Down Expand Up @@ -658,3 +664,47 @@ async def test_nabu_casa_zwa2_legacy(
assert state.attributes["friendly_name"] == "Home Assistant Connect ZWA-2 LED", (
"The LED should have the correct friendly name"
)


@pytest.mark.parametrize("platforms", [[Platform.BINARY_SENSOR, Platform.LIGHT]])
async def test_fibaro_fgms001_v2_8_motion_discovery(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry,
client: MagicMock,
fibaro_fgms001_v2_8: Node,
integration: MockConfigEntry,
) -> None:
"""Test the Fibaro FGMS001 on firmware 2.8 is discovered as a motion sensor.

The device exposes its motion state via the Sensor Binary CC under the
"Any" sensor type. Without the device-specific discovery override the
value would either fall through to the disabled legacy boolean schema
or be misclassified, so we assert that exactly one binary_sensor entity
with device_class=motion is created and no light entity exists.
"""
device = device_registry.async_get_device(
identifiers={get_device_id(client.driver, fibaro_fgms001_v2_8)}
)
assert device is not None

entries = er.async_entries_for_device(
entity_registry, device.id, include_disabled_entities=True
)

motion_entries = [
entry
for entry in entries
if entry.domain == BINARY_SENSOR_DOMAIN
and entry.original_device_class == BinarySensorDeviceClass.MOTION
]
assert len(motion_entries) == 1
motion_entry = motion_entries[0]
assert motion_entry.disabled_by is None

state = hass.states.get(motion_entry.entity_id)
assert state is not None
assert state.state == STATE_OFF
assert state.attributes[ATTR_DEVICE_CLASS] == BinarySensorDeviceClass.MOTION

assert not [entry for entry in entries if entry.domain == Platform.LIGHT]
Loading