diff --git a/homeassistant/components/zwave_js/discovery.py b/homeassistant/components/zwave_js/discovery.py
index be6d9b698d453e0d5ba0a844ffc34721e804b3f9..62d45a5ae5e1564dbe73ae68113e29bf60753fe4 100644
--- a/homeassistant/components/zwave_js/discovery.py
+++ b/homeassistant/components/zwave_js/discovery.py
@@ -14,12 +14,14 @@ from homeassistant.core import callback
 class ZwaveDiscoveryInfo:
     """Info discovered from (primary) ZWave Value to create entity."""
 
-    node: ZwaveNode  # node to which the value(s) belongs
-    primary_value: ZwaveValue  # the value object itself for primary value
-    platform: str  # the home assistant platform for which an entity should be created
-    platform_hint: Optional[
-        str
-    ] = ""  # hint for the platform about this discovered entity
+    # node to which the value(s) belongs
+    node: ZwaveNode
+    # the value object itself for primary value
+    primary_value: ZwaveValue
+    # the home assistant platform for which an entity should be created
+    platform: str
+    # hint for the platform about this discovered entity
+    platform_hint: Optional[str] = ""
 
     @property
     def value_id(self) -> str:
@@ -27,38 +29,140 @@ class ZwaveDiscoveryInfo:
         return f"{self.node.node_id}.{self.primary_value.value_id}"
 
 
+@dataclass
+class ZWaveValueDiscoverySchema:
+    """Z-Wave Value discovery schema.
+
+    The Z-Wave Value must match these conditions.
+    Use the Z-Wave specifications to find out the values for these parameters:
+    https://github.com/zwave-js/node-zwave-js/tree/master/specs
+    """
+
+    # [optional] the value's command class must match ANY of these values
+    command_class: Optional[Set[int]] = None
+    # [optional] the value's endpoint must match ANY of these values
+    endpoint: Optional[Set[int]] = None
+    # [optional] the value's property must match ANY of these values
+    property: Optional[Set[Union[str, int]]] = None
+    # [optional] the value's metadata_type must match ANY of these values
+    type: Optional[Set[str]] = None
+
+
 @dataclass
 class ZWaveDiscoverySchema:
     """Z-Wave discovery schema.
 
-    The (primary) value for an entity must match these conditions.
+    The Z-Wave node and it's (primary) value for an entity must match these conditions.
     Use the Z-Wave specifications to find out the values for these parameters:
     https://github.com/zwave-js/node-zwave-js/tree/master/specs
     """
 
     # specify the hass platform for which this scheme applies (e.g. light, sensor)
     platform: str
+    # primary value belonging to this discovery scheme
+    primary_value: ZWaveValueDiscoverySchema
     # [optional] hint for platform
     hint: Optional[str] = None
+    # [optional] the node's manufacturer_id must match ANY of these values
+    manufacturer_id: Optional[Set[int]] = None
+    # [optional] the node's product_id must match ANY of these values
+    product_id: Optional[Set[int]] = None
+    # [optional] the node's product_type must match ANY of these values
+    product_type: Optional[Set[int]] = None
+    # [optional] the node's firmware_version must match ANY of these values
+    firmware_version: Optional[Set[str]] = None
     # [optional] the node's basic device class must match ANY of these values
     device_class_basic: Optional[Set[str]] = None
     # [optional] the node's generic device class must match ANY of these values
     device_class_generic: Optional[Set[str]] = None
     # [optional] the node's specific device class must match ANY of these values
     device_class_specific: Optional[Set[str]] = None
-    # [optional] the value's command class must match ANY of these values
-    command_class: Optional[Set[int]] = None
-    # [optional] the value's endpoint must match ANY of these values
-    endpoint: Optional[Set[int]] = None
-    # [optional] the value's property must match ANY of these values
-    property: Optional[Set[Union[str, int]]] = None
-    # [optional] the value's metadata_type must match ANY of these values
-    type: Optional[Set[str]] = None
+    # [optional] additional values that ALL need to be present on the node for this scheme to pass
+    required_values: Optional[Set[ZWaveValueDiscoverySchema]] = None
+    # [optional] bool to specify if this primary value may be discovered by multiple platforms
+    allow_multi: bool = False
 
 
 # For device class mapping see:
 # https://github.com/zwave-js/node-zwave-js/blob/master/packages/config/config/deviceClasses.json
 DISCOVERY_SCHEMAS = [
+    # ====== START OF DEVICE SPECIFIC MAPPING SCHEMAS =======
+    # Honeywell 39358 In-Wall Fan Control using switch multilevel CC
+    ZWaveDiscoverySchema(
+        platform="fan",
+        manufacturer_id={0x0039},
+        product_id={0x3131},
+        product_type={0x4944},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.SWITCH_MULTILEVEL},
+            property={"currentValue"},
+            type={"number"},
+        ),
+    ),
+    # GE/Jasco fan controllers using switch multilevel CC
+    ZWaveDiscoverySchema(
+        platform="fan",
+        manufacturer_id={0x0063},
+        product_id={0x3034, 0x3131, 0x3138},
+        product_type={0x4944},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.SWITCH_MULTILEVEL},
+            property={"currentValue"},
+            type={"number"},
+        ),
+    ),
+    # Leviton ZW4SF fan controllers using switch multilevel CC
+    ZWaveDiscoverySchema(
+        platform="fan",
+        manufacturer_id={0x001D},
+        product_id={0x0002},
+        product_type={0x0038},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.SWITCH_MULTILEVEL},
+            property={"currentValue"},
+            type={"number"},
+        ),
+    ),
+    # Fibaro Shutter Fibaro FGS222
+    ZWaveDiscoverySchema(
+        platform="cover",
+        hint="fibaro_fgs222",
+        manufacturer_id={0x010F},
+        product_id={0x1000},
+        product_type={0x0302},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.SWITCH_MULTILEVEL},
+            property={"currentValue"},
+            type={"number"},
+        ),
+    ),
+    # Qubino flush shutter
+    ZWaveDiscoverySchema(
+        platform="cover",
+        hint="fibaro_fgs222",
+        manufacturer_id={0x0159},
+        product_id={0x0052},
+        product_type={0x0003},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.SWITCH_MULTILEVEL},
+            property={"currentValue"},
+            type={"number"},
+        ),
+    ),
+    # Graber/Bali/Spring Fashion Covers
+    ZWaveDiscoverySchema(
+        platform="cover",
+        hint="fibaro_fgs222",
+        manufacturer_id={0x026E},
+        product_id={0x5A31},
+        product_type={0x4353},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.SWITCH_MULTILEVEL},
+            property={"currentValue"},
+            type={"number"},
+        ),
+    ),
+    # ====== START OF GENERIC MAPPING SCHEMAS =======
     # locks
     ZWaveDiscoverySchema(
         platform="lock",
@@ -69,12 +173,14 @@ DISCOVERY_SCHEMAS = [
             "Secure Keypad Door Lock",
             "Secure Lockbox",
         },
-        command_class={
-            CommandClass.LOCK,
-            CommandClass.DOOR_LOCK,
-        },
-        property={"currentMode", "locked"},
-        type={"number", "boolean"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={
+                CommandClass.LOCK,
+                CommandClass.DOOR_LOCK,
+            },
+            property={"currentMode", "locked"},
+            type={"number", "boolean"},
+        ),
     ),
     # door lock door status
     ZWaveDiscoverySchema(
@@ -87,12 +193,14 @@ DISCOVERY_SCHEMAS = [
             "Secure Keypad Door Lock",
             "Secure Lockbox",
         },
-        command_class={
-            CommandClass.LOCK,
-            CommandClass.DOOR_LOCK,
-        },
-        property={"doorStatus"},
-        type={"any"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={
+                CommandClass.LOCK,
+                CommandClass.DOOR_LOCK,
+            },
+            property={"doorStatus"},
+            type={"any"},
+        ),
     ),
     # climate
     ZWaveDiscoverySchema(
@@ -102,10 +210,14 @@ DISCOVERY_SCHEMAS = [
             "Setback Thermostat",
             "Thermostat General",
             "Thermostat General V2",
+            "General Thermostat",
+            "General Thermostat V2",
         },
-        command_class={CommandClass.THERMOSTAT_MODE},
-        property={"mode"},
-        type={"number"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.THERMOSTAT_MODE},
+            property={"mode"},
+            type={"number"},
+        ),
     ),
     # climate
     # setpoint thermostats
@@ -115,9 +227,11 @@ DISCOVERY_SCHEMAS = [
         device_class_specific={
             "Setpoint Thermostat",
         },
-        command_class={CommandClass.THERMOSTAT_SETPOINT},
-        property={"setpoint"},
-        type={"number"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.THERMOSTAT_SETPOINT},
+            property={"setpoint"},
+            type={"number"},
+        ),
     ),
     # lights
     # primary value is the currentValue (brightness)
@@ -132,85 +246,104 @@ DISCOVERY_SCHEMAS = [
             "Multilevel Scene Switch",
             "Unused",
         },
-        command_class={CommandClass.SWITCH_MULTILEVEL},
-        property={"currentValue"},
-        type={"number"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.SWITCH_MULTILEVEL},
+            property={"currentValue"},
+            type={"number"},
+        ),
     ),
     # binary sensors
     ZWaveDiscoverySchema(
         platform="binary_sensor",
         hint="boolean",
-        command_class={
-            CommandClass.SENSOR_BINARY,
-            CommandClass.BATTERY,
-            CommandClass.SENSOR_ALARM,
-        },
-        type={"boolean"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={
+                CommandClass.SENSOR_BINARY,
+                CommandClass.BATTERY,
+                CommandClass.SENSOR_ALARM,
+            },
+            type={"boolean"},
+        ),
     ),
     ZWaveDiscoverySchema(
         platform="binary_sensor",
         hint="notification",
-        command_class={
-            CommandClass.NOTIFICATION,
-        },
-        type={"number"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={
+                CommandClass.NOTIFICATION,
+            },
+            type={"number"},
+        ),
+        allow_multi=True,
     ),
     # generic text sensors
     ZWaveDiscoverySchema(
         platform="sensor",
         hint="string_sensor",
-        command_class={
-            CommandClass.SENSOR_ALARM,
-            CommandClass.INDICATOR,
-        },
-        type={"string"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={
+                CommandClass.SENSOR_ALARM,
+                CommandClass.INDICATOR,
+            },
+            type={"string"},
+        ),
     ),
     # generic numeric sensors
     ZWaveDiscoverySchema(
         platform="sensor",
         hint="numeric_sensor",
-        command_class={
-            CommandClass.SENSOR_MULTILEVEL,
-            CommandClass.SENSOR_ALARM,
-            CommandClass.INDICATOR,
-            CommandClass.BATTERY,
-        },
-        type={"number"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={
+                CommandClass.SENSOR_MULTILEVEL,
+                CommandClass.SENSOR_ALARM,
+                CommandClass.INDICATOR,
+                CommandClass.BATTERY,
+            },
+            type={"number"},
+        ),
     ),
     # numeric sensors for Meter CC
     ZWaveDiscoverySchema(
         platform="sensor",
         hint="numeric_sensor",
-        command_class={
-            CommandClass.METER,
-        },
-        type={"number"},
-        property={"value"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={
+                CommandClass.METER,
+            },
+            type={"number"},
+            property={"value"},
+        ),
     ),
     # special list sensors (Notification CC)
     ZWaveDiscoverySchema(
         platform="sensor",
         hint="list_sensor",
-        command_class={
-            CommandClass.NOTIFICATION,
-        },
-        type={"number"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={
+                CommandClass.NOTIFICATION,
+            },
+            type={"number"},
+        ),
+        allow_multi=True,
     ),
     # sensor for basic CC
     ZWaveDiscoverySchema(
         platform="sensor",
         hint="numeric_sensor",
-        command_class={
-            CommandClass.BASIC,
-        },
-        type={"number"},
-        property={"currentValue"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={
+                CommandClass.BASIC,
+            },
+            type={"number"},
+            property={"currentValue"},
+        ),
     ),
     # binary switches
     ZWaveDiscoverySchema(
         platform="switch",
-        command_class={CommandClass.SWITCH_BINARY},
-        property={"currentValue"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.SWITCH_BINARY}, property={"currentValue"}
+        ),
     ),
     # cover
     ZWaveDiscoverySchema(
@@ -223,9 +356,11 @@ DISCOVERY_SCHEMAS = [
             "Motor Control Class C",
             "Multiposition Motor",
         },
-        command_class={CommandClass.SWITCH_MULTILEVEL},
-        property={"currentValue"},
-        type={"number"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.SWITCH_MULTILEVEL},
+            property={"currentValue"},
+            type={"number"},
+        ),
     ),
     # fan
     ZWaveDiscoverySchema(
@@ -233,9 +368,11 @@ DISCOVERY_SCHEMAS = [
         hint="fan",
         device_class_generic={"Multilevel Switch"},
         device_class_specific={"Fan Switch"},
-        command_class={CommandClass.SWITCH_MULTILEVEL},
-        property={"currentValue"},
-        type={"number"},
+        primary_value=ZWaveValueDiscoverySchema(
+            command_class={CommandClass.SWITCH_MULTILEVEL},
+            property={"currentValue"},
+            type={"number"},
+        ),
     ),
 ]
 
@@ -243,8 +380,33 @@ DISCOVERY_SCHEMAS = [
 @callback
 def async_discover_values(node: ZwaveNode) -> Generator[ZwaveDiscoveryInfo, None, None]:
     """Run discovery on ZWave node and return matching (primary) values."""
+    # pylint: disable=too-many-nested-blocks
     for value in node.values.values():
         for schema in DISCOVERY_SCHEMAS:
+            # check manufacturer_id
+            if (
+                schema.manufacturer_id is not None
+                and value.node.manufacturer_id not in schema.manufacturer_id
+            ):
+                continue
+            # check product_id
+            if (
+                schema.product_id is not None
+                and value.node.product_id not in schema.product_id
+            ):
+                continue
+            # check product_type
+            if (
+                schema.product_type is not None
+                and value.node.product_type not in schema.product_type
+            ):
+                continue
+            # check firmware_version
+            if (
+                schema.firmware_version is not None
+                and value.node.firmware_version not in schema.firmware_version
+            ):
+                continue
             # check device_class_basic
             if (
                 schema.device_class_basic is not None
@@ -263,21 +425,19 @@ def async_discover_values(node: ZwaveNode) -> Generator[ZwaveDiscoveryInfo, None
                 and value.node.device_class.specific not in schema.device_class_specific
             ):
                 continue
-            # check command_class
-            if (
-                schema.command_class is not None
-                and value.command_class not in schema.command_class
-            ):
-                continue
-            # check endpoint
-            if schema.endpoint is not None and value.endpoint not in schema.endpoint:
-                continue
-            # check property
-            if schema.property is not None and value.property_ not in schema.property:
-                continue
-            # check metadata_type
-            if schema.type is not None and value.metadata.type not in schema.type:
+            # check primary value
+            if not check_value(value, schema.primary_value):
                 continue
+            # check additional required values
+            if schema.required_values is not None:
+                required_values_present = True
+                for val_scheme in schema.required_values:
+                    for val in node.values.values():
+                        if not check_value(val, val_scheme):
+                            required_values_present = False
+                            break
+                if not required_values_present:
+                    continue
             # all checks passed, this value belongs to an entity
             yield ZwaveDiscoveryInfo(
                 node=value.node,
@@ -285,3 +445,27 @@ def async_discover_values(node: ZwaveNode) -> Generator[ZwaveDiscoveryInfo, None
                 platform=schema.platform,
                 platform_hint=schema.hint,
             )
+            if not schema.allow_multi:
+                # break out of loop, this value may not be discovered by other schemas/platforms
+                break
+
+
+@callback
+def check_value(value: ZwaveValue, schema: ZWaveValueDiscoverySchema) -> bool:
+    """Check if value matches scheme."""
+    # check command_class
+    if (
+        schema.command_class is not None
+        and value.command_class not in schema.command_class
+    ):
+        return False
+    # check endpoint
+    if schema.endpoint is not None and value.endpoint not in schema.endpoint:
+        return False
+    # check property
+    if schema.property is not None and value.property_ not in schema.property:
+        return False
+    # check metadata_type
+    if schema.type is not None and value.metadata.type not in schema.type:
+        return False
+    return True