Adding Tilt to the Hunter Douglas PowerView Cover integration (Luxaflex)

Hi all –

Separate sub-topic here, but, anyone have a way to delete an old device from this integration? Something happened where I needed to remove and add a shade, now I can’t seem to get rid of the old ghost device(!)

I have 3 hardware type 23 blinds named Left, Middle, and Right in PowerView. After adding the integration zip file to my HA, Left and Middle behave perfectly, including tilt. However, Right is somehow only partially recognized. It has no available actions and in the entity list (3 devices, 8 entities), no cover.right is in the list. I realize this may or may not be related to an integration bug, but I’m not sure how to sort it out or what other information I can post that would be helpful in deciding. Thanks for your thoughts and advice.

@StonyCourt Does the built in HA integration find the blinds? If not its probably not related to the changes going on here

@trullock Yes, the built-in integration with HA sees all blinds. I deleted the custom integration and installed the built-in one. It recognized all 3 blinds (3 devices, 6 entities). I deleted the built-in integration and installed the custom integration. This time it sees all 3 blinds (3 devices and 9 entities). Shrug. I’ll move forward with automation to make sure it works. Thanks for advice.

Update: Yup, everything works perfectly. Thanks

I’ve made a branch and PR which fixes tilting on Type 23 blinds, if you want to give it a test its here: GitHub - trullock/core at hunterdouglas and Add support for Silhouette Type 23 Tilting by trullock · Pull Request #70775 · home-assistant/core · GitHub

Just spin up the devcontainer and add the standard HD integration, your blinds should now be tiltable.

Likely bugs are that the UI doesn’t update properly after a movement, can you try and find any such cases?

Hi there,

I don’t see that you have any feedback for vertical blinds here (type 54 & 55). I’ve been using the native integration up until now just to apply scenes to the blinds. I’d like to implement some automations based on the state of the blinds which lead me here.
I’ve added your custom component to my test instance and can see tilt controls in the UI and they operate perfectly. I do however have a similar issue to @Massamino where my blinds have 2 closed positions on the tilt axis.

0 position is closed with vanes turned fully anticlockwise
50 position is “open” with vanes at a 90 degree angle letting in max light
100 position is closed with vanes turned fully clockwise

This is the result of a http://POWERVIEWIP/api/shades with all blinds in the “blind” closed “tilt” around 50% position in case its useful

{ {
        "shadeIds": [3433, 58444, 58722, 731],
        "shadeData": [{
                "id": 3433,
                "type": 55,
                "capabilities": 3,
                "batteryKind": 1,
                "smartPowerSupply": {
                    "status": 0,
                    "id": 0,
                    "port": 0
                },
                "batteryStatus": 0,
                "batteryStrength": 0,
                "roomId": 49704,
                "firmware": {
                    "revision": 1,
                    "subRevision": 8,
                    "build": 1944
                },
                "name": "QmxpbmQgUmlnaHQgQmxpbmQ=",
                "signalStrength": 4,
                "groupId": 46896,
                "motor": {
                    "revision": 50,
                    "subRevision": 52,
                    "build": 11825
                },
                "positions": {
                    "posKind1": 1,
                    "position1": 0,
                    "posKind2": 3,
                    "position2": 31856
                }
            }, {
                "id": 58444,
                "type": 54,
                "capabilities": 3,
                "batteryKind": 1,
                "smartPowerSupply": {
                    "status": 0,
                    "id": 0,
                    "port": 0
                },
                "batteryStatus": 0,
                "batteryStrength": 0,
                "roomId": 49704,
                "firmware": {
                    "revision": 1,
                    "subRevision": 8,
                    "build": 1944
                },
                "name": "QmxpbmQgTGVmdCBXaW5kb3c=",
                "signalStrength": 4,
                "groupId": 27689,
                "motor": {
                    "revision": 49,
                    "subRevision": 50,
                    "build": 11825
                },
                "positions": {
                    "posKind1": 1,
                    "position1": 0,
                    "posKind2": 3,
                    "position2": 31546
                }
            }, {
                "id": 58722,
                "type": 55,
                "capabilities": 4,
                "batteryKind": 1,
                "smartPowerSupply": {
                    "status": 0,
                    "id": 0,
                    "port": 0
                },
                "batteryStatus": 0,
                "batteryStrength": 0,
                "roomId": 49704,
                "firmware": {
                    "revision": 1,
                    "subRevision": 8,
                    "build": 1944
                },
                "name": "U2xpZGluZyBEb29y",
                "groupId": 46896,
                "positions": {
                    "posKind1": 1,
                    "position1": 0,
                    "posKind2": 3,
                    "position2": 36554
                },
                "signalStrength": 4,
                "motor": {
                    "revision": 50,
                    "subRevision": 48,
                    "build": 11826
                }
            }, {
                "id": 731,
                "type": 54,
                "capabilities": 4,
                "batteryKind": 1,
                "smartPowerSupply": {
                    "status": 0,
                    "id": 0,
                    "port": 0
                },
                "batteryStatus": 0,
                "batteryStrength": 0,
                "roomId": 49704,
                "firmware": {
                    "revision": 1,
                    "subRevision": 8,
                    "build": 1944
                },
                "motor": {
                    "revision": 50,
                    "subRevision": 48,
                    "build": 11826
                },
                "name": "Qmlmb2xk",
                "groupId": 27689,
                "signalStrength": 4,
                "positions": {
                    "posKind1": 1,
                    "position1": 0,
                    "posKind2": 3,
                    "position2": 30849
                }
            }
        ]
    }

Let me know if you need any more info

Can I please have some people test their different Tilt capable blinds please before i submit the work to core.

@trullock this also has considerable changes to the way your Silhoutte commit worked to try and ensure that coordinator information remains up to date with the changes made to core in support of TDBU blinds (main purpose of my commit aio-powerview-api/shade.py at 89711e2a0cb4640eb458767d289dcfa3acafb10f · sander76/aio-powerview-api · GitHub)

In particular we need to test these 3 groups (1 of each type) but appreciate retesting of type 21 as the comands sent now also send some redundant commands (so that it is in the returned JSON to update both objects) Based on info from @TonyInHiro (see Adding Tilt to the Hunter Douglas PowerView Cover integration (Luxaflex) - #81 by TonyInHiro) i dont think this will be an issue with the moving of the shade but will allow consistency with core BaseShade type

class ShadeBottomUpTiltAnywhere(BaseShade):
    """A shade with move and tilt anywhere capabilities."""
    shade_types = (
        shade_type(62, "Venetian, tilt anywhere"),
        shade_type(54, "Vertical blind, Left stack"),
        shade_type(55, "Vertical blind, Right stack"),
        shade_type(56, "Vertical blind, Split stack"),
    )
class Silhouette(ShadeBottomUpTilt):
    shade_types = (shade_type(23, "Silhouette"),)

class ShadeBottomUpTilt(BaseShade):
    """A shade with move and tilt at bottom capabilities."""

    shade_types = (shade_type(44, "Twist"),)

Type 18, 38 (@Karson) and 51 (@Massamino) are currently hardcoded as they arent supported in base api at the moment but this should still work- I will update the base api for that one before this moves to core - if there are other types not listed here we will need to test those too so please let me know.
I dont expect Type 38 to work entirely - but these are not currently well documented and will take a little work

New version of the custom_component here hunterdouglas_powerview_custom.zip - Google Drive

Hi,

Thanks so much for your work.

I can confirm that tilting works perfectly for type 55 (vertical blinds).
Tilting also works for type 54 (vertical blinds), but when using the slider, the blinds tilt close completely before returning to the correct tilt.
Using the tilt arrows does work fine for both types.
I did notice that when using the tilt arrows, the tilt slider is lagging and often not updated to display the actual tilt.
I also noted that the left tilt arrow, which points to the right, tilts the blinds to the left, which to me at least, is a bit of a visual contradiction :slight_smile:
The position slider is mostly not updated at all when opening or closing the blinds when using the position arrows.
I also noted that after setting the position slider to 100%, the blinds open and after setting the position slider to 0%, the blinds close.
Lastly, I noticed that after the changing the position the current tilt is not always preserved/restored.

It might well be that some of these comments are already known.
If you need any more information please let me know.

I’ll test it out when I get home from work today!

If you need a tester for type 51, I’m happy to help out.

go ahead mate - feedback will be the only way to get these right as i dont have any

Working with the openhab developer to update the core api as while they work currently they are not implemented correctly

Hopefully we dont implement any bugs there - but feedback here will help drive the updates there (api not HA)

@gjdoornink can you confirm if your left tilt arrow comment is only related to the type 54 ? Noting from what i can see they appear to be ‘Left stack’ where as type 44 are ‘Right Stack’

Wondering if i need to invert the open/close for this type - Does the slider work in the same way for these ? moving to the left on open ? If you opened them using the remote or app which way would they move

Your comment I also noted that after setting the position slider to 100%, the blinds open and after setting the position slider to 0%, the blinds close. makes me think that these need to be inverted for open close position

This plugin is using a new way of dealing with positioning (at least for those with capability that is not just up/down)
This currently writes where we intend to move for and then does a sync call to the HD hub 60 seconds after the move.

I did some testing with my tdbu (tricking code that the up was a tilt) and did notice some lag here in the UI, but this didnt exist if i hit the cross and reopened immediatly (before any sync occurs) so i was workign on the assumption this was a HA core issue

edit: could i actually get you to send me the result of http://powerviewip/api/shades - im not confident on the capability listed for type 54/55. One with the shade fully closed, one fully open, one with the ‘tilt’ in place - pm me is fine so tonot clog here

I also noted that after setting the position slider to 100%, the blinds open and after setting the position slider to 0%, the blinds close.

IS this suggesting the move command is working correctly and it is only tilt not working for type 54?

I’m currently getting the following errors when trying to load the custom_component.

2022-05-19 06:07:22 ERROR (MainThread) [homeassistant.helpers.integration_platform] Unexpected error importing hunterdouglas_powerview_custom/group.py
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/integration_platform.py", line 40, in _async_process_single_integration_platform_component
    platform = integration.get_platform(platform_name)
  File "/usr/src/homeassistant/homeassistant/loader.py", line 603, in get_platform
    cache[full_name] = self._import_platform(platform_name)
  File "/usr/src/homeassistant/homeassistant/loader.py", line 620, in _import_platform
    return importlib.import_module(f"{self.pkg_path}.{platform_name}")
  File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'custom_components.hunterdouglas_custom_new.group'
2022-05-19 06:07:22 ERROR (MainThread) [homeassistant.helpers.integration_platform] Unexpected error importing hunterdouglas_powerview_custom/media_source.py
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/integration_platform.py", line 40, in _async_process_single_integration_platform_component
    platform = integration.get_platform(platform_name)
  File "/usr/src/homeassistant/homeassistant/loader.py", line 603, in get_platform
    cache[full_name] = self._import_platform(platform_name)
  File "/usr/src/homeassistant/homeassistant/loader.py", line 620, in _import_platform
    return importlib.import_module(f"{self.pkg_path}.{platform_name}")
  File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'custom_components.hunterdouglas_custom_new.media_source'
2022-05-19 06:07:22 ERROR (MainThread) [homeassistant.helpers.integration_platform] Unexpected error importing hunterdouglas_powerview_custom/system_health.py
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/integration_platform.py", line 40, in _async_process_single_integration_platform_component
    platform = integration.get_platform(platform_name)
  File "/usr/src/homeassistant/homeassistant/loader.py", line 603, in get_platform
    cache[full_name] = self._import_platform(platform_name)
  File "/usr/src/homeassistant/homeassistant/loader.py", line 620, in _import_platform
    return importlib.import_module(f"{self.pkg_path}.{platform_name}")
  File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'custom_components.hunterdouglas_custom_new.system_health'
2022-05-19 06:07:22 ERROR (MainThread) [homeassistant.helpers.integration_platform] Unexpected error importing hunterdouglas_powerview_custom/logbook.py
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/integration_platform.py", line 40, in _async_process_single_integration_platform_component
    platform = integration.get_platform(platform_name)
  File "/usr/src/homeassistant/homeassistant/loader.py", line 603, in get_platform
    cache[full_name] = self._import_platform(platform_name)
  File "/usr/src/homeassistant/homeassistant/loader.py", line 620, in _import_platform
    return importlib.import_module(f"{self.pkg_path}.{platform_name}")
  File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 984, in _find_and_load_unlocked
ModuleNotFoundError: No module named 'custom_components.hunterdouglas_custom_new.logbook'
2022-05-19 06:07:24 ERROR (MainThread) [homeassistant.components.cover] Error adding entities for domain cover with platform hunterdouglas_powerview_custom
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 382, in async_add_entities
    await asyncio.gather(*tasks)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 619, in _async_add_entity
    await entity.add_to_platform_finish()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 810, in add_to_platform_finish
    self.async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 533, in async_write_ha_state
    self._async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 573, in _async_write_ha_state
    attr.update(self.state_attributes or {})
  File "/usr/src/homeassistant/homeassistant/components/cover/__init__.py", line 283, in state_attributes
    if (current_tilt := self.current_cover_tilt_position) is not None:
  File "/config/custom_components/hunterdouglas_custom_new/cover.py", line 592, in current_cover_tilt_position
    return hd_position_to_hass(self.get_position_secondary, self._max_tilt)
  File "/config/custom_components/hunterdouglas_custom_new/cover.py", line 186, in get_position_secondary
    return self.position_data[ATTR_POSITION2]
KeyError: 'position2'
2022-05-19 06:07:24 ERROR (MainThread) [homeassistant.components.cover] Error while setting up hunterdouglas_powerview_custom platform for cover
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 257, in _async_setup_platform
    await asyncio.gather(*pending)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 382, in async_add_entities
    await asyncio.gather(*tasks)
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 619, in _async_add_entity
    await entity.add_to_platform_finish()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 810, in add_to_platform_finish
    self.async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 533, in async_write_ha_state
    self._async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 573, in _async_write_ha_state
    attr.update(self.state_attributes or {})
  File "/usr/src/homeassistant/homeassistant/components/cover/__init__.py", line 283, in state_attributes
    if (current_tilt := self.current_cover_tilt_position) is not None:
  File "/config/custom_components/hunterdouglas_custom_new/cover.py", line 592, in current_cover_tilt_position
    return hd_position_to_hass(self.get_position_secondary, self._max_tilt)
  File "/config/custom_components/hunterdouglas_custom_new/cover.py", line 186, in get_position_secondary
    return self.position_data[ATTR_POSITION2]
KeyError: 'position2'

This is for Type 51.

Custom plugin installed, and blinds detected.

raise and lower works as expected

Tilt slider visable in HA front end but using it does not cause blinds to move. It does trigger the light to flash on the PowerView hub, - so seemingly confirming it’s getting/sending some kind of command.

This is on a Pi3, with the latest prod build of HA. Let me know if I should be using the custom plugin on a dev environment for these purposes.

Thanks I was just thinking about this maybe being an issue - you are running type 18 or 23 right ?

I need to fix up the logic there - I’ll post a new version tonight

Some shades reuse the same position and don’t return the value of the other shade all the time - we can return the correct value for the tilt but will have to ‘guess/assume’ position of the blind I think

Yeah, type 23. It only ever returns one poskind so yeah, if tilted poskind2 is reported, so assume poskind1 is 0, if blind is open to any value only poskind1 is reported, assume poskind2 is 0 logic should cover it.

New Version uploaded.

If you have the custom component installed you can browse to the device through the integrations page and click the new ‘Visit Device’ link - this also does a force refresh of the shade position as part of the call
image

@csamulski I havent heard of these and they are undocumented at least by that name - could you please post result of either the above or http://powerviewip/api/shades so we can add them in.

@ApriliaEdd could you please test type 54/55 - you mentioned they were workinf before but the new integration has changed the method of integration. @gjdoornink can you please test too

@Massamino could you please check for type 51 - im not sure why they would not be sending commands as i didnt think i changed these ones but @Bobtm doesnt appear to be working

@TonyInHiro @trullock new version should handle the fact that type 23/18 dont always provide the data in the json by keeping a cache of values and returning live data when avaialble - just keep this in mind if you see slow data updates - Because with these we can assume if one motor is non-zero the other is closed i am hoping you dont have issues

In away from home at the moment but will update and try it out when I get back in around 2 weeks.

Your are not guessing anything. They are facts that depend on the mechanical physics of ‘Tilt on Closed’ shades…

  • If posKind1 is for tilt position, the actual primary position is de-facto 0
  • If posKind1 is for primary position, the tilt position is de-facto ‘undefined’

It is different for ‘Tilt Anywhere’ shades because they will always return both ‘position1/posKind1’ and ‘position2/posKind2’ values, one for the primary position and the other for the tilt position. Note: do not assume that ‘position1/posKind1’ is the primary and ‘position2/posKind2’ the tilt: they can be reversed, so you need to check the value of each respective posKind…