Change device class on Z-Wave device

I just migrated all of my Z-wave devices from Smartthings to HomeAssistant. I’m using the Z-Wave JS UI add-on.

Everything is working great, except my 3-speed fan controllers are showing up as lights in Home Assistant.

I did some searching, and see this has been a problem for some time, with some workarounds like this, but using YAML to trick Home Assistant didn’t seem like the ideal solution, and the solution from that post doesn’t work as written anymore.

I also saw some posts on the Z-Wave JS UI github about this issue, but it looks like the issue was merged with another and I don’t see any specific resolution.

I was wondering if there is a way to fix this in either Home Assistant or Z-Wave JS UI to natively map the device to the correct class?

A discovery schema matching the product ids would need to be added to the integration code.

https://github.com/home-assistant/core/blob/9921a67a05bdb1b1a17b08b1bebbde7f90b250c3/homeassistant/components/zwave_js/discovery.py#L268-L363

DISCOVERY_SCHEMAS = [
    # ====== START OF DEVICE SPECIFIC MAPPING SCHEMAS =======
    # Honeywell 39358 In-Wall Fan Control using switch multilevel CC
    ZWaveDiscoverySchema(
        platform=Platform.FAN,
        hint="has_fan_value_mapping",
        manufacturer_id={0x0039},
        product_id={0x3131},
        product_type={0x4944},
        primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
        required_values=[SWITCH_MULTILEVEL_TARGET_VALUE_SCHEMA],
        data_template=FixedFanValueMappingDataTemplate(
            FanValueMapping(speeds=[(1, 32), (33, 66), (67, 99)]),
        ),
    ),
    # GE/Jasco - In-Wall Smart Fan Control - 12730 / ZW4002
    ZWaveDiscoverySchema(
        platform=Platform.FAN,
        hint="has_fan_value_mapping",
        manufacturer_id={0x0063},
        product_id={0x3034},
        product_type={0x4944},
        primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
        data_template=FixedFanValueMappingDataTemplate(
            FanValueMapping(speeds=[(1, 33), (34, 67), (68, 99)]),
        ),
    ),
    # GE/Jasco - In-Wall Smart Fan Control - 14287 / 55258 / ZW4002
    ZWaveDiscoverySchema(
        platform=Platform.FAN,
        hint="has_fan_value_mapping",
        manufacturer_id={0x0063},
        product_id={0x3131, 0x3337},
        product_type={0x4944},
        primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
        data_template=FixedFanValueMappingDataTemplate(
            FanValueMapping(speeds=[(1, 32), (33, 66), (67, 99)]),
        ),
    ),
    # GE/Jasco - In-Wall Smart Fan Control - 14314 / ZW4002
    ZWaveDiscoverySchema(
        platform=Platform.FAN,
        manufacturer_id={0x0063},
        product_id={0x3138},
        product_type={0x4944},
        primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
    ),
    # Leviton ZW4SF fan controllers using switch multilevel CC
    ZWaveDiscoverySchema(
        platform=Platform.FAN,
        hint="has_fan_value_mapping",
        manufacturer_id={0x001D},
        product_id={0x0002},
        product_type={0x0038},
        primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
        data_template=FixedFanValueMappingDataTemplate(
            FanValueMapping(speeds=[(1, 25), (26, 50), (51, 75), (76, 99)]),
        ),
    ),
    # Inovelli LZW36 light / fan controller combo using switch multilevel CC
    # The fan is endpoint 2, the light is endpoint 1.
    ZWaveDiscoverySchema(
        platform=Platform.FAN,
        hint="has_fan_value_mapping",
        manufacturer_id={0x031E},
        product_id={0x0001},
        product_type={0x000E},
        primary_value=ZWaveValueDiscoverySchema(
            command_class={CommandClass.SWITCH_MULTILEVEL},
            endpoint={2},
            property={CURRENT_VALUE_PROPERTY},
            type={ValueType.NUMBER},
        ),
        data_template=FixedFanValueMappingDataTemplate(
            FanValueMapping(
                presets={1: "breeze"}, speeds=[(2, 33), (34, 66), (67, 99)]
            ),
        ),
    ),
    # HomeSeer HS-FC200+
    ZWaveDiscoverySchema(
        platform=Platform.FAN,
        hint="has_fan_value_mapping",
        manufacturer_id={0x000C},
        product_id={0x0001},
        product_type={0x0203},
        primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
        data_template=ConfigurableFanValueMappingDataTemplate(
            configuration_option=ZwaveValueID(
                property_=5, command_class=CommandClass.CONFIGURATION, endpoint=0
            ),
            configuration_value_to_fan_value_mapping={
                0: FanValueMapping(speeds=[(1, 33), (34, 66), (67, 99)]),
                1: FanValueMapping(speeds=[(1, 24), (25, 49), (50, 74), (75, 99)]),
            },
        ),
    ),

Excellent! I think this is exactly what I’m looking for - this must be what became of the issue request I linked in the original post.

Is there a thread somewhere where I could submit device info to get the Leviton VRF01 added? Or would I need to figure out the code myself and put in a pull request?

Most likely you’ll need to do it yourself. I’d say it would be one of the simpler PRs for a first time contribution as you just need to copy & paste from what’s existing (including tests). The most difficult part of that will be getting the environment setup. Feel free to post any questions here if you go this route, or pop into Discord.

Otherwise, there is #feature-requests. You could make a post there but it will likely go unanswered.

You could map it into a fan template in YAML.

It may be less work to swap it out with a fan controller that is supported.

To me it seems like a bug and an issue should be submitted.

If you can figure out what the code should be and so some local testing, that would help getting a PR put together.

@PeteRage - you’re probably right, swapping these out with a different model would like be much easier… however these fan controllers are part of a matching set of about 35-40 switches/dimmers in my house, so I’m quite attached to them :smiling_face:

With that said, I’m happy to do all that I can to get them working properly in Home Assistant!

Based on what @freshcoast linked above in the discovery schema, the code should be pretty straightforward. Could you guys tip me off to how I could test this locally to try and confirm the code for a PR?

One approach is to try to directly edit the python code in place in your installation. This requires getting the file and then transferring it into the home assistant container. If you know how to do that and can right the python code, that’s pretty quick. But, you don’t get any debugging, help with syntax, etc.

Alternatively is to get a dev environment setup on your laptop. The docs are pretty good. This is probably the best approach, though you will bang your head against the wall on one thing or another - depending on your level of skill with python, pip, etc.

Exposing port 3000 in the add-on configuration and running a dev environment on another host (e.g. laptop) would be the easiest and least disruptive. The zwave-js-server accepts multiple clients, so you can use a standalone integration instance to communicate with it.

Appreciate the continued assistance! I beleive the code would be as follows:

# Leviton VRF01 fan controller with specific speed settings
ZWaveDiscoverySchema(
    platform=Platform.FAN,
    hint="has_fan_value_mapping",
    manufacturer_id={0x001D},  # Manufacturer ID for Leviton
    product_id={0x0210},        # 521 in hex
    product_type={0x1001},      # 4097 in hex
    primary_value=SWITCH_MULTILEVEL_CURRENT_VALUE_SCHEMA,
    data_template=FixedFanValueMappingDataTemplate(
        FanValueMapping(speeds=[(1, 32), (33, 66), (67, 100)]), 
    ),
)

Admittedly, software development has never been my forte, so I suspect getting this code in to test may be the hardest part.

Perhaps this is an ignorant question, but could I create a custom component to test this rather than setting up a dev environment or trying to edit the python code in place?

Sure, you could use a custom component if that’s easier for you.

I prefer to leave my production system undisturbed as much as possible when doing work like this.

There are several existing tests you can copy from that exercise the fan mapping implementation, for example core/tests/components/zwave_js/test_fan.py at 40dbfab6719e5eea392de6e664b55f4d063e5759 · home-assistant/core · GitHub. You just need to add the fixture data into the test. Creating the fixture data from your device is documenated at core/homeassistant/components/zwave_js/README.md at 40dbfab6719e5eea392de6e664b55f4d063e5759 · home-assistant/core · GitHub.

Of course, to run the tests you need to setup a dev environment.

Excellent, appreciate the continued assistance.

I’ve added the modified discovery.py file as a custom component, but I’m assuming at this point I’ll need to exclude and re-include the device to get it to apply the discovery schema? Re-interviewing didn’t seem to do anything.

No, you don’t need to reinclude the device, nor do you even need to re-interview, just reload the integration, or restart HA if you haven’t to load the custom integration.

Hmm, even after an HA restart, not seeing any change. Placing the modified discovery.py at /homeassistant/custom_components/zwave_js was the correct move, right?

Not seeing any errors in the logs, so assuming it was loaded properly.

The path would be /config/custom_components/zwave_js and you need to copy the entire directory, not a single file, and where /config is the location of your config directory. Then inspect the logs to make sure the component is being loaded.

Truthfully I’m beginning to wonder if I’m just in over my head here… Everytime I uncover an answer, I have three more questions.

Assuming I could even figure any of this out, I think getting the PR successfully done will be it’s own mountain to climb.

Are there any “bounty” programs or similar for HA to offer a reward to anyone willing to work on something like this?