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

Just for info: the only time you will read (GET) bad info from the hub, is if you have written (PUT) bad info to it. (!!) And more specifically the GET will always parrot the last PUT (it too has a cache) until the shade next updates the hub with its actual live data. A GET with refresh=true forces the hub to fetch actual live data from the shade (which is why the GET with refresh=true calls take longer than plain GET calls).


EDIT: another case where you can (if you want to be perverse) PUT bad data to the hub, is by trying to move the upper rail of a TDBU shade below its lower rail (or its lower above its upper). The next hub GET’s will parrot that … for a while. But (obviously) the shade cannot physically move the rails to such a position, so it will move to a position that satisfies its logical physical constraints. And once you do a GET refresh=true the state will sort itself out.

For this reason, in such cases, (and the @TonyInHiro case), in OpenHAB we do the logical physical constraint checks and limitations in code before sending the PUT to the hub, in order to avoid sending something that is physically impossible.

Same applies to NOT sending tilt commands in the 90…180 degree range to shades that only support 0…90 degree tilt…

So, fresh install of HAOS, fresh SD card, and have only installed Samba and the custom integration from the link above (with a read e file date of 21st)

After adding, only one set of controls visible for type 51, only controlling up/down, no tilt.

Yea i just posted a new version in the last 30 - assuming you have that can you add this to your config file

logger:
  default: warning
  logs:
    custom_components.hunterdouglas_powerview_custom: debug

looking in particular for this result of what the class determination is such as

2022-05-21 00:44:36 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Study Duette - Detected as [<class 'custom_components.hunterdouglas_powerview_custom.cover.PowerViewShadeTDBUTop'>, <class 'custom_components.hunterdouglas_powerview_custom.cover.PowerViewShadeTDBUBottom'>]

EDIT: found 1 thing that could have caused tilt to dissapear - updated link again

Thanks - no joy, but here is the result of the log file. Assume it should have been ‘PowerViewShadeTiltAnywhere’ rather than ‘PowerViewShade’.

2022-05-20 16:22:44 WARNING (SyncWorker_0) [homeassistant.loader] We found a custom integration hunterdouglas_powerview_custom which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you experience issues with Home Assistant
2022-05-20 16:23:02 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Blind C - Detected as [<class 'custom_components.hunterdouglas_powerview_custom.cover.PowerViewShade'>]
2022-05-20 16:23:03 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Blind L - Detected as [<class 'custom_components.hunterdouglas_powerview_custom.cover.PowerViewShade'>]
2022-05-20 16:23:04 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Blind R - Detected as [<class 'custom_components.hunterdouglas_powerview_custom.cover.PowerViewShade'>]
2022-05-20 16:23:04 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Raw data update: {'id': 53960, 'type': 51, 'capabilities': 2, 'batteryKind': 1, 'smartPowerSupply': {'status': 0, 'id': 0, 'port': 0}, 'batteryStatus': 4, 'batteryStrength': 180, 'roomId': 7666, 'firmware': {'revision': 2, 'subRevision': 5, 'build': 287, 'index': 280}, 'name': 'QmxpbmQgQw==', 'positions': {'posKind1': 1, 'position1': 65535, 'posKind2': 3, 'position2': 65535}, 'signalStrength': 4, 'groupId': 9209, 'name_unicode': 'Blind C'}
2022-05-20 16:23:04 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Blind C - Update Position: {'posKind1': 1, 'position1': 65535, 'posKind2': 3, 'position2': 65535}
2022-05-20 16:23:04 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Blind C - Update Position: {'posKind1': 1, 'position1': 65535, 'posKind2': 3, 'position2': 65535}
2022-05-20 16:23:04 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Raw data update: {'id': 25921, 'type': 51, 'capabilities': 2, 'batteryKind': 1, 'smartPowerSupply': {'status': 0, 'id': 0, 'port': 0}, 'batteryStatus': 4, 'batteryStrength': 180, 'roomId': 7666, 'firmware': {'revision': 2, 'subRevision': 5, 'build': 287, 'index': 280}, 'name': 'QmxpbmQgTA==', 'groupId': 9209, 'positions': {'posKind1': 1, 'position1': 0, 'posKind2': 3, 'position2': 0}, 'name_unicode': 'Blind L'}
2022-05-20 16:23:04 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Blind L - Update Position: {'posKind1': 1, 'position1': 0, 'posKind2': 3, 'position2': 0}
2022-05-20 16:23:04 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Blind L - Update Position: {'posKind1': 1, 'position1': 0, 'posKind2': 3, 'position2': 0}
2022-05-20 16:23:04 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Raw data update: {'id': 59321, 'type': 51, 'capabilities': 2, 'batteryKind': 1, 'smartPowerSupply': {'status': 0, 'id': 0, 'port': 0}, 'batteryStatus': 4, 'batteryStrength': 180, 'roomId': 7666, 'firmware': {'revision': 2, 'subRevision': 5, 'build': 287, 'index': 280}, 'name': 'QmxpbmQgUg==', 'signalStrength': 4, 'positions': {'posKind1': 1, 'position1': 0, 'posKind2': 3, 'position2': 0}, 'groupId': 9209, 'name_unicode': 'Blind R'}
2022-05-20 16:23:04 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Blind R - Update Position: {'posKind1': 1, 'position1': 0, 'posKind2': 3, 'position2': 0}
2022-05-20 16:23:04 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Blind R - Update Position: {'posKind1': 1, 'position1': 0, 'posKind2': 3, 'position2': 0}

It should indeed - wondering if it was the ordance of my if statement - can you try version just posted now and if that still fails can you post the value of ‘Visit Device’

No joy - sorry :frowning:

Where can I find “Visit Device”? Couldn’t see it in the log file or in /api/shades.

I did blindly change the else clause (line 148) to default to “PowerViewShadeTiltAnywhere”, which was picked up in the logs, but still no sign of the additional tilt control.

Go to integrations - and select the link for ‘x devices’ under the Hunter Douglas integration.

From there select one of the affected shades and you will see the ‘visit link’ on this page

All it does is hit /api/shades with a force refresh - but that will make sure I have the right data to try and work out what’s going on

Gotcha.

{"shade":{"id":53960,"type":51,"capabilities":2,"batteryKind":1,"smartPowerSupply":{"status":0,"id":0,"port":0},"batteryStatus":4,"batteryStrength":180,"roomId":7666,"firmware":{"revision":2,"subRevision":5,"build":287,"index":280},"name":"QmxpbmQgQw==","positions":{"posKind1":1,"position1":0,"posKind2":3,"position2":0},"signalStrength":4,"groupId":9209,"timedOut":false}}

Its not making a lot of sense - what type of device was your new install on? It shouldnt make any difference but i am currently spinning up a pi3 to test on, all my tests are docker or esxi atm.

Could you update line 134 to the below to test maybe

shade_type = int(shade.shade_type.shade_type)

I can spoof my blind type to test each model using the same line that is currently including type 51 (this is just a temp thing for testing - the api will be updated to include these)
Below is a Type 42 that is top only, but it successfully gets identified as a Tilt shade by using

elif shade.can_tilt or shade_type in [42, 51]:

It doesnt make any sense why 51 is not being picked up th same way (its just an integer)

image

My new pi install worked fine with the spoofing too
Can you post the info of your hub maybe - you can get htere using ‘via device’ on the screen we used visit device from
image

EDIT: while still wanting that - just confirming how long you have had the hub - there is a new gen3 hub and wanting to make sure it isnt in play here - hopefully someone else here with type 51 can chime in too

Kitchen Roller (42) - Detected as [<class 'custom_components.hunterdouglas_powerview_custom.cover.PowerViewShadeTiltAnywhere'>]

Type 51 / Capabilities 2 is (AFAIK) a “tilt anywhere” shade (rather than a “tilt on closed”).

This means that it uses position1/posKind1 for its primary rail position and additionally / independently uses position2/posKind2 for the vane position (unlike “tilt on closed” shades which pack interlocked values of primary rail and vane position into a single position1/posKind1 JSON element).

Furthermore AFAIK this type has a vane position range of 180 degrees rather than 90 degrees, so the maximum vane position value is 64k instead of 32k…

Yea that should be coded to work that way

Because they code api doesn’t support them from a class perspective they should currently be recognised based on shade type number (see the elif statements above). I am working on a pull for the api and would have those both committed at the same time so this is just temporary

This type recognition based on integer seems to be not working here, but if I change it from type 51 to type 42 for a test my 42 gets recognised as a tilt anywhere as per the code

Can’t make sense of why it would act different for someone else, his json appears to be formatted how we would want it

it would indeed be great if you can post the result of opening http://[powerviewip]/api/shades in your browser.

@csamulski to be more specific can you please…

  1. Use your remote to tilt the vanes fully UP, and post the result of opening http://[powerviewip]/api/shades in your browser, and also,
  2. Use your remote to tilt the vanes fully DOWN, and post the result of opening http://[powerviewip]/api/shades in your browser

Many thanks in advance.


EDIT: from what I have seen elsewhere, I am guessing that the shutters will have Capabilities 5 :slight_smile:

Couldn’t see the hub in HA, but can confirm its a v2 on the same FW (based on what’s in the official HD app)

Change to line 134 was inconclusive. Log still showing them as being “PowerViewShade”

It works!!!

I had a bit of a blind hack about with the elif statements, so it now reads like this - starting line 127.

@Kingy444 - much like you, not immediately obvious why this has fixed it, but it has. Just changing the default without adding nonsense into the earlier elif clause for 51 didn’t work in isolation.

Also seems to read the tilt position really reliably, which is fantastic.

def create_powerview_shade_entity(
    coordinator, device_info, room_name, shade, name_before_refresh
):
    """Determing the class required for the PowerViewShade entity."""
    classes = []
    # order here is important as both ShadeTDBU are listed in aiovapi as can_tilt
    # and both require their own class here to work
    shade_type = int(shade.shade_type.shade_type)
    # shade_type = shade.shade_type.shade_type
    if isinstance(shade, ShadeTdbu) or shade_type in [9]:
        classes.extend([PowerViewShadeTDBUTop, PowerViewShadeTDBUBottom])
    elif isinstance(shade, Silhouette) or shade_type in [18, 43]:
        classes.append(PowerViewShadeSilhouette)
    # elif isinstance(shade, ShadeTiltOnly) or shade_type in [70, 71, 55, 56]:
    # elif shade_type in [42]:
    #    classes.append(PowerViewShadeTiltOnly)
    elif isinstance(shade, ShadeBottomUpTilt) or shade_type in [70, 71, 55, 56]:
        classes.append(PowerViewShadeTiltOnClosed)
        # ShadeVerticalTilt [70, 71, 55, 56]:
    elif shade_type in [69, 54]:  # or isinstance(shade, ShadeVerticalTiltInvert):
        classes.append(PowerViewShadeTiltOnClosedInvert)
    elif shade.can_tilt or shade_type in [51]:
        classes.append(dddsda)
    elif shade_type in [7]:  # or isinstance(shade, ShadeTiltOnly):
        classes.append(PowerViewShadeTopDown)
    else:
        classes.append(PowerViewShadeTiltAnywhere)
    _LOGGER.debug("%s (%s) - Detected as %a", shade.name, shade_type, classes)
    return [
        cls(coordinator, device_info, room_name, shade, name_before_refresh)
        for cls in classes
    ]```\\

Also seems to read the tilt position really reliably, which is fantastic.

Thats the most important thing to know - hopefully once we have this in core the API recognizes them properly (we wont be using shade_type in core HA

out of curiousity could you try this block

def create_powerview_shade_entity(
    coordinator, device_info, room_name, shade, name_before_refresh
):
    """Determing the class required for the PowerViewShade entity."""
    classes = []
    # order here is important as both ShadeTDBU are listed in aiovapi as can_tilt
    # and both require their own class here to work
    shade_type = int(shade.shade_type.shade_type)
    if isinstance(shade, ShadeTdbu) or shade_type in [9]:
        classes.extend([PowerViewShadeTDBUTop, PowerViewShadeTDBUBottom])
    elif isinstance(shade, Silhouette) or shade_type in [18, 43]:
        classes.append(PowerViewShadeSilhouette)
    elif isinstance(shade, ShadeBottomUpTilt) or shade_type in [70, 71, 55, 56]:
        classes.append(PowerViewShadeTiltOnClosed)
    elif shade_type in [69, 54]:
        classes.append(PowerViewShadeTiltOnClosedInvert)
    elif shade_type in [51]:
        classes.append(PowerViewShadeTiltAnywhere)
    elif shade.can_tilt:
        classes.append(PowerViewShadeTiltAnywhere)
    elif shade_type in [7]: 
        classes.append(PowerViewShadeTopDown)
    else:
        classes.append(PowerViewShadeBase)
    _LOGGER.debug("%s (%s) - Detected as %a", shade.name, shade_type, classes)
    return [
        cls(coordinator, device_info, room_name, shade, name_before_refresh)
        for cls in classes
    ]

Can confirm that new block works as well.

As this reaches maturity, can other shade types be requested? I’ve got five type 38’s (top down, dual motors - one for blackout and one for the tilting vanes that sit in front of the blackout and two type 23’s (single motor, top down tilting) that don’t appear to have native code like some of the others in the covers.py.

If you’d be open to that, would you be willing to create some sort of submission form of all the necessary JSON data/datasets needed for integration? Not knowing any better, it always seemed efficient to fill out GitHub templates.

Sorry I’ve been AWOL, had a baby! Will test my bits this week hopefully

My only concern is as noted far above is that my type 23’s seems to behave differently to @TonyInHiro’s.

The code for 23’s should be back to how you had it coded. Don’t expect any issues there

I assume you are referring to the comment I tagged where his type23 had poskind2 - that was testing that failed. We are only sending 1 poskind in code again now

Once my tdbu merge is committed to core I’m almost complete on this one to submit to core as well (this is an extension of that commit so need it done first)

I have an extension written for the upstream api to support all the other shades which may be the only hold up

Just wanted to make sure you know so we weren’t submitting the same work - I will have all shades supported hopefully

Not that you will have much time with a newborn - congrats!