Inovelli Z-Wave Red Series Notification LED

https://github.com/kschlichter/Home-Assistant-Inovelli-Red-Dimmer-Switch

Based on BrianHanifin’s work I’ve added support for the LZW36 Fan / Light Switch, which has two LED strips. The updated script is backward compatible with old automations, but for the LZW36 it needs either “Type: Fan” or “Type: Light” in order to set the appropriate parameter.

2 Likes

Nice work Kevin! I’ve been away for a bit, but I’m starting to get caught up on what I’ve missed.

I really like the concept of extracting the model from the Product Name attribute. Unfortunately my product_name appears as Unknown: type=0001, id=0001. So, I can’t use that part of your code.

What Zwave integration are you using? I’m using the old default Zwave integration. I have been waiting to try the OpenZwave addon (beta) as I have some complex items to support: smoke detectors, deadbolt, Inovelli dimmer taps and leds… :slight_smile:

I also use the open z-wave 1.4 that comes with Home Assistant. Looking forward to 1.6, when that’s ready, but my wife won’t have the patience for anything beta. Inovelli’s config files (and many others, in my experience) aren’t included by default but they’re simple to add. At a high level, you’ll remove the device, add the Inovelli configuration files, and then re-add the switch to the network. It’s a lot of steps, but it isn’t as complicated as it looks. Here’s how I did it (in as much detail as I can muster, in case anybody else needs this too):

  1. You’ll want to jot down the names of your devices and entities so you can use the same names later. That way your automations and templates won’t need to change.

  2. Remove your switches from the z-wave network. It’s the opposite of pairing, if I remember correctly, but check the Inovelli manual for whichever switches you have. The switch, dimmer, fan / light combo switch, and the LED bulb each have a different process.

  3. Get the Inovelli configuration files for your devices from the Inovelli github. Be sure you download the contents of the xml file itself and not the page displaying the xml file, as I’ve done before. :slight_smile: I open the file with the “raw” button and copy / paste, but you can also do a git clone of the whole directory.

  4. I’m running Home Assistant in docker and if you’re running core, a VM, or HassIO on a Raspberry Pi the files will be in a different location for you but the rest of the instructions would be the same. In docker, I start a bash session with “docker exec -it hass bash” where hass is the name of my container. From there, I put the files in the directory /usr/local/lib/python3.7/site-packages/python_openzwave/ozw_config/inovelli/ but you might try a “find / -type d -iname ozw_config” to locate it. If you have more than one, I’m not sure how to tell which one Home Assistant is using. Maybe just make the modifications to both?

  5. Next you have to tell open z-wave how to find them and what they are. Open “/usr/local/lib/python3.7/site-packages/python_openzwave/ozw_config/manufacturer_specific.xml” and find the Inovelli section. It’ll look like this:

        <Manufacturer id="0312" name="Inovelli">                               
                <Product type="1e01" id="1e01" name="NZW30 Smart Switch" config="inovelli/nzw30.xml"/>  
                <Product type="1e00" id="1e00" name="NZW30 Smart Switch (w/Scene)" config="inovelli/nzw30.xml"/>
                <Product type="1e02" id="1e02" name="NZW30 Smart Toggle Switch (w/Scene)" config="inovelli/nzw30.xml"/>
                <Product type="1f01" id="1f01" name="NZW31 Smart Dimmer" config="inovelli/nzw31.xml"/>
                <Product type="1f00" id="1f00" name="NZW31 Smart Dimmer (w/Scene)" config="inovelli/nzw31.xml"/>
                <Product type="1f02" id="1f02" name="NZW31 Smart Toggle Dimmer (w/Scene)" config="inovelli/nzw31.xml"/>
                <Product type="b211" id="241c" name="NZW36 1-Channel Smart Plug-In Module" config="inovelli/simple_module.xml"/>
                <Product type="2400" id="2400" name="NZW36 1-Channel Smart Plug (w/Scene)" config="inovelli/nzw36.xml"/>
                <Product type="b221" id="251c" name="NZW37 2-Channel Smart Plug" config="inovelli/simple_module.xml"/>
                <Product type="2500" id="2500" name="NZW37 2-Channel Smart Plug (w/Scene)" config="inovelli/nzw37.xml"/>
                <Product type="b212" id="271c" name="NZW39 Smart Plug Dimmer" config="inovelli/simple_module.xml"/>
                <Product type="2700" id="2700" name="NZW39 Smart Plug Dimmer (w/Scene)" config="inovelli/nzw39.xml"/>
                <Product type="6000" id="6000" name="NZW96 1-Channel Outdoor Smart Plug" config="inovelli/nzw96.xml"/>
                <Product type="6100" id="6100" name="NZW97 2-Channel Outdoor Smart Plug" config="inovelli/nzw97.xml"/>
        </Manufacturer>   
        <Manufacturer id="031E" name="Inovelli">                                      
        </Manufacturer>                                
  1. You want the second Inovelli section; the one with manufacturer ID 031E. The first Inovelli section has ID 0312 and you don’t want that one. The section with ID 031E is probably empty and you need to point it at the configuration files you just added. You should be able to copy and paste these “<product…>” lines, but double check that the file names are right. If you’re adding something in the future that isn’t in this list, you can get the type and id from the .xml file you downloaded.
        <Manufacturer id="031E" name="Inovelli">                                      
                <Product type="0002" id="0001" name="LZW30-SN Switch Red Series" config="inovelli/lzw30-sn.xml"/>
                <Product type="0001" id="0001" name="LZW31-SN Dimmer Red Series" config="inovelli/lzw31-sn.xml"/>
                <Product type="0005" id="0001" name="LZW42 Multi-Color Bulb" config="inovelli/lzw42.xml"/>
                <Product type="0006" id="0001" name="LZW41 Multi-White Bulb" config="inovelli/lzw41.xml"/>
                <Product type="0007" id="0001" name="LZW40 Dimmable  Bulb" config="inovelli/lzw40.xml"/>
                <Product type="000e" id="0001" name="LZW36 Fan/Light Dimmer" config="inovelli/lzw36.xml"/>
        </Manufacturer>
  1. Add your device back to the z-wave network (see step 2). You should see the make and model for your device once the status is “complete”.
    image
    If not, it could be an issue with the xml file, or that the product type or id didn’t match. You’ll have to remove the device from the network and debug from there.

  2. Rename the devices and entities with the originals from step one, and check an automation or two to ensure everything is still working.

  3. Always remember to “heal” your network once you’re done adding all your switches back.

It’s worth noting, for anybody running docker, that every time the container shuts down and restarts these modifications disappear. Your device config and naming persists via a cache in zwcfg_*.xml though. So once you’ve got it added you don’t have to do this again. It’ll still be known to be an LZW31-SN (or whatever). If the container is restarted though, and you want to add a new device, you’ll have to re-do the modifications above. I keep a copy of section 031E in a “z-wave notes.txt” and copies of the xml files in a subdirectory of my hass directory for convenience.

P.S. I was listening to the Home Assistant podcast as I was typing this up, and they specifically mentioned you while discussing the Inovelli switches. :slight_smile: If you don’t listen to the podcast regularly, it’s early in the most recent episode. Congratulations!

1 Like

Thanks for letting me know. I haven’t listened to the new one yet. I like to wait until I’m ready to install the version the episode is about. Right now that means I have to wait for Browser_Mod to be updated so my pop up centric 1-page UI can work with it. :slight_smile:

If I can figure out that the new OpenZwave integration supports my deadbolt, smoke alarms, and the Inovelli dimmer, then I will give it a shot soon.

Hey all – thanks so much for all you do – I may not fully understand everything (Eric M. is the Home Assistant guy), but I do regularly read these threads and love seeing what you all can do with these switches.

If you have any questions, I’m here to help – feel free to tag me and if I can’t get an answer, I’ll ask the other Eric!

PS - @kschlichter – if you don’t mind, could I post those instructions in our forum and/or knowledge-base? There’s a lot of people that ask this question.

3 Likes

Yeah, sure! If Eric M. knows where to find the files for non-docker solutions, please modify the instructions and build on that.

1 Like

Hey folks, been trying to implement this for a while and I’m probably doing something silly. Home Assistant 0.113.1 in Docker on a Synology.

Been trying to implement @BrianHanifin’s awesome script. Here’s what I’m trying to do:

This exact thing literally works from Developer Tools > Services. When I run it from the automation I get this error in the logs:

Error executing script. Unexpected error for call_service at pos 1: ‘list’ object has no attribute ‘lower’

Traceback in case it helps

Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/helpers/script.py”, line 181, in _async_step
await getattr(
File “/usr/src/homeassistant/homeassistant/helpers/script.py”, line 373, in _async_call_service_step
await self._async_run_long_action(service_task)
File “/usr/src/homeassistant/homeassistant/helpers/script.py”, line 332, in _async_run_long_action
long_task.result()
File “/usr/src/homeassistant/homeassistant/core.py”, line 1265, in async_call
task.result()
File “/usr/src/homeassistant/homeassistant/core.py”, line 1300, in _execute_service
await handler.func(service_call)
File “/usr/src/homeassistant/homeassistant/components/script/init.py”, line 212, in service_handler
await script_entity.async_turn_on(
File “/usr/src/homeassistant/homeassistant/components/script/init.py”, line 314, in async_turn_on
await coro
File “/usr/src/homeassistant/homeassistant/helpers/script.py”, line 764, in async_run
await asyncio.shield(run.async_run())
File “/usr/src/homeassistant/homeassistant/helpers/script.py”, line 173, in async_run
await self._async_step(log_exceptions=False)
File “/usr/src/homeassistant/homeassistant/helpers/script.py”, line 181, in _async_step
await getattr(
File “/usr/src/homeassistant/homeassistant/helpers/script.py”, line 342, in _async_call_service_step
domain, service, service_data = async_prepare_call_from_config(
File “/usr/src/homeassistant/homeassistant/helpers/service.py”, line 132, in async_prepare_call_from_config
template.render_complex(config[CONF_SERVICE_DATA_TEMPLATE], variables)
File “/usr/src/homeassistant/homeassistant/helpers/template.py”, line 73, in render_complex
return {key: render_complex(item, variables) for key, item in value.items()}
File “/usr/src/homeassistant/homeassistant/helpers/template.py”, line 73, in
return {key: render_complex(item, variables) for key, item in value.items()}
File “/usr/src/homeassistant/homeassistant/helpers/template.py”, line 75, in render_complex
return value.async_render(variables)
File “/usr/src/homeassistant/homeassistant/helpers/template.py”, line 229, in async_render
return compiled.render(kwargs).strip()
File “/usr/local/lib/python3.8/site-packages/jinja2/environment.py”, line 1090, in render
self.environment.handle_exception()
File “/usr/local/lib/python3.8/site-packages/jinja2/environment.py”, line 832, in handle_exception
reraise(*rewrite_traceback_stack(source=source))
File “/usr/local/lib/python3.8/site-packages/jinja2/_compat.py”, line 28, in reraise
raise value.with_traceback(tb)
File “”, line 1, in top-level template code
File “/usr/local/lib/python3.8/site-packages/jinja2/sandbox.py”, line 462, in call
return __context.call(__obj, *args, **kwargs)
File “/usr/src/homeassistant/homeassistant/helpers/template.py”, line 1023, in wrapper
return func(hass, *args[1:], **kwargs)
File “/usr/src/homeassistant/homeassistant/helpers/template.py”, line 671, in state_attr
state_obj = _get_state(hass, entity_id)
File “/usr/src/homeassistant/homeassistant/helpers/template.py”, line 480, in _get_state
state = hass.states.get(entity_id)
File “/usr/src/homeassistant/homeassistant/core.py”, line 900, in get
return self._states.get(entity_id.lower())
AttributeError: ‘list’ object has no attribute ‘lower’

Any advice?

I’m about to be offline, but I can give you two things to try.

1} I think it’s case-sensitive. Make sure you’re matching the case for both the parameter and the value.

  1. Try adding the duration. I think his script uses a default time, but I don’t remember. The way the math works it should still do something (but it’ll be wrong in some way because the calculation is off). Here’s an example from my version. You might want to check it against his though, because I’ve modified it pretty heavily.
color: Green
duration: 10 Minutes
effect: Chase
entity_id: zwave.office_lights
level: 6

The trace looks like entity_id.lower() is being called, but I don’t think the script calls that. Could this be coming from something else?

Added the duration and capitalized everything like the script expects. I don’t believe it’s actually case sensitive since it works from the developer tools service calls without it.

I suspect that the function trying to find the node_id from the entity_id, when it’s called from an automation, has a bug, or the script is out of date.

When I modify the script to just accept a node_id it works fine from automation screen.

my automation action:

# entity_id: zwave.media_room_ls_back_row
node_id: 13
color: green
effect: chase
level: 10

my script:

data_template:
  # node_id: '{{ state_attr(entity_id,''node_id'') }}'
  node_id: '{{ node_id }}'

Is this using two single quotes around node_id, or is it just my eyes?

  # node_id: '{{ state_attr(entity_id,''node_id'') }}'

Try:

node_id: "{{ state_attr(entity_id,'node_id') }}"

That’s how it starts but HA converts it to single quotes. After I save the script it gets changed. I don’t think that should matter though.

Pre-save:

Post-save:

Still, the script works when called from the developer tools. It’s just broken coming from the Automation Actions.

For the script editor, try making the outside quotes single and the inside ones double.

  node_id: '{{ state_attr(entity_id,"node_id") }}'

Damn, that works! Think this is a bug worth reporting?

Please do. Whoever coded this probably does quotes in the order that works, so they haven’t noticed it. :slight_smile:

Done. First bug for this project, hopefully I filled it out right. https://github.com/home-assistant/core/issues/38288

1 Like

Also managed to extend my first script, which is yours, @BrianHanifin :slight_smile: I added a random colour.

Here’s my working script that anyone can paste into the YAML script editor inside the HA UI.

Scripts > New Script > Click the Kebab (three dots) > Edit as YAML
data_template:
  node_id: '{{ state_attr(entity_id,"node_id") }}'
  parameter: >
    {%- set dimmer = dimmer|default('true') %} {{ "16" if dimmer == "true" else
    "8" }}
  size: 4
  value: >
    {# Skip the calculations for effect = "off". #} {% if effect|title == "Off"
    %}
      0
    {% else %}
      {# Set default values if any needed values are missing. #}
      {% set color = color|default("Yellow") %}
      {% set level = level|default(4) %}{# 1-10 #}
      {% set duration = duration|default("Indefinitely") %}
      {% set effect = effect|default("Pulse") %}
      {# Let's make things easy by using descriptive text instead of hard to understand numbers. #}
      {% set colors = {
        "Red": 1,
        "Orange": 21,
        "Green": 85,
        "Blue": 170,
        "Pink": 234,
        "Yellow": 42,
        "Cyan": 127,
        "Purple": 195,
        "Random": range(1, 255) | random
      } %}
      {% set durations = {
        "1 Second": 1,
        "2 Seconds": 2,
        "3 Seconds": 3,
        "4 Seconds": 4,
        "5 Seconds": 5,
        "6 Seconds": 6,
        "7 Seconds": 7,
        "8 Seconds": 8,
        "9 Seconds": 9,
        "10 Seconds": 10,
        "15 Seconds": 15,
        "20 Seconds": 20,
        "25 Seconds": 25,
        "30 Seconds": 30,
        "35 Seconds": 35,
        "40 Seconds": 40,
        "45 Seconds": 45,
        "50 Seconds": 50,
        "55 Seconds": 55,
        "60 Seconds": 60,
        "2 Minutes": 62,
        "3 Minutes": 63,
        "4 Minutes": 64,
        "15 Minutes": 75,
        "30 Minutes": 90,
        "45 Minutes": 105,
        "1 Hour": 120,
        "2 Hours": 122,
        "Indefinitely": 255
      } %}
      {% set effects = {
        "Off": 0,
        "Solid": 1,
        "Chase": 2,
        "Fast Blink": 3,
        "Slow Blink": 4,
        "Blink": 4,
        "Pulse": 5,
        "Breath": 5
      } %}
      {# Preform the Inovelli mind bending mathmatics automatically for us! :) #}
      {{ colors[color|title] + (level * 256) + (durations[duration|title] * 65536) + (effects[effect|title] * 16777216) }}
    {% endif %}
service: zwave.set_config_parameter

1 Like

Heads up this script won’t work with OZW because the model name is not an attribute on the entity, it’s on the device. I’m not sure if there’s a way to get that data from the script.

Did you just do this recently? I think the recent update to 113 moved the folders around. Now it’s python 3.8 but the /site-packages/ folder is no longer there. Also searching using find / -type d -iname ozw_config doesn’t find anything either…
Anyone know where it got moved so i can add the config there?

Are you using docker, or something else? In the 113.3 image I’m running I have:

bash-5.0# ls -lh /usr/local/lib/python3.8/site-packages/python_openzwave/ozw_config/manufacturer_specific.xml 
-rw-r--r--    1 root     root      138.9K Aug  1 06:44 /usr/local/lib/python3.8/site-packages/python_openzwave/ozw_config/manufacturer_specific.xml