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

There is a delay usually about 60 seconds or there is a force refresh called when you hit the stop button - if you can play with that there shouldnt be any issue reporting in HA - check out a manual browse to http://POWERVIEWIP/api/shades and that should tell you what is being reported after the press from the physical remote

i am surprised that
“{‘shade’: { ‘positions’: { ‘posKind1’: 1, ‘position1’: 0, ‘posKind2’: 3, ‘position2’: 15420 } } }”
doesnt close and tilt - this is the exact command i would have been sending (i was just lazy when i sent you posKind1 as 3 the first time. From my testing the api hadnt seemed to care which poskind was defined in which position as long as they were defined correctly 1 being the main motor, 2 being the top down and 3 being the tilt

Looking at the base API perhaps this is a shortfalling in the Powerview side as they are only sending either poskind 1 or 3 at a time and never both - but they only do this methon on blinds of type ShadeBottomUpTilt and Silhoutte which both appear to be similiar in design

If you can test scenarios in which posting to the blind works for you (as you have above) and find something that consistently works i can create a class specific for these that treats them special to the other tilt shades - really hard to try and test this without some silhoutte shades of my own sorry :slight_smile:

edit: unfortunately the shades reporting their position post a physical press has always been a bit slow and unreliable - appears i dont have the service calls implemented in this branch yet but i have made a custom service that you can then script to do a refresh of data at a specified time. depending on your use case you could call hourly, against just some of the them etc - just remember that everytime that is implemented it wakes the blind and interrogates the motor so will use (ever so little) power. but it will use some and your batteries will be used as a result. All we normally get is the cache from the hub

An alternate way to prove HA isnt the issue is to open the PowerView App and browse to the shade - the app calls a refresh in the background, updating cache and then HA is up to date. There must be something in the PowerView app that does this in the background as i have had good results keeping position up to date with no intervention post a physical press just by having used the app recently

Thanks - I’ll try to contact them. Just curious - what firmware do you have on your hubs? I have 2 and they’re both on firmware 3.1.379 (ModelID = Standard, Radio = 3.0.15). Are you having success with this firmware?

@Kingy444 remind me of how the API looks for posting the data and I can test it

Invoke-WebRequest -Method PUT -ContentType "application/json" -UseBasicParsing -Uri http://POWERVIEWIP/api/shades/SHADEID -Body '{ "shade": { "positions": { "posKind1": 1, "position1": 0,  "posKind2": 2, "position2": 0 } } }'

posKind 1 is standard motor poskind 2 is top motor poskind 3 is tilt - put the value between 0 and 65535 for the position attribute

check above some examples for tilting from @TonyInHiro

My hub is Firmware 2.0.1056 and Radio 2.0.2610

So trying a bunch of different stuff, which ever motor is in posKind1 takes priority. So if both positions are positive integers, which is an impossible positions for my blind as the veins can only tilt when the blind is fully closed, whichever motor is in posKind1 is the one that is activated ie

“{‘shade’: { ‘positions’: { ‘posKind1’: 1, ‘position1’: 15420, ‘posKind2’: 3, ‘position2’: 25420 } } }”

will raise the blind to the correct position, and obviously not tilt will occur, but the inverse

“{‘shade’: { ‘positions’: { ‘posKind1’: 3, ‘position1’: 15420, ‘posKind2’: 1, ‘position2’: 25420 } } }”

will close the blind fully and tilt the veins to the correct position. If the blind is raised to any position and the following command is sent

“{‘shade’: { ‘positions’: { ‘posKind1’: 1, ‘position1’: 0, ‘posKind2’: 3, ‘position2’: 25420 } } }”

then the blind will close and the veins will not tilt. However if the blind is already fully lowered and the same command is sent again, then the veins will be tilted to the correct position. The same goes for the inverse. If the veins are opened and the tilt motor is in posKind1 the following command will close the veins but not raise the blind

“{‘shade’: { ‘positions’: { ‘posKind1’: 3, ‘position1’: 0, ‘posKind2’: 3, ‘position2’: 25420 } } }”

However once the veins are closed and the command is sent again then the blinds will raise to the correct position. So sending 2 commands in the same message will not activate both motors unless the position in position1 is already true. I can’t really say what the best way of doing this is, but I would suggest for type 23 blinds to send a single position command and use logic in the integration to set the variable of the other motor, eg. if posKind1 = 3 & position1 > 0 then motor 1 position = 0, inversely if posKind1 = 1 & position > 0 then motor 3 position = 0. Including this logic would also catch blind positions that have been set via the app/physical remote as the api only reports a single posKind and position for those.

Also setting any position using my physical remote does not show an update on the api even after 30 minutes or so, so I’m guessing requesting a refresh request is necessary in my case … although I’m having trouble finding how to do that in the api documentation.

Edit: in a slightly more readable format

Original Position Command Result
motor 1 position = 24520 motor 3 position = 0 posKind1’: 1, ‘position1’: 15420, ‘posKind2’: 3, ‘position2’: 25420 motor 1 position = 15420 motor 3 position = 0
motor 1 position = 24520 motor 3 position = 0 posKind1’: 1, ‘position1’: 0, ‘posKind2’: 3, ‘position2’: 25420 motor 1 position = 0 motor 3 position = 0
motor 1 position = 0 motor 3 position = 0 posKind1’: 1, ‘position1’: 0, ‘posKind2’: 3, ‘position2’: 25420 motor 1 position = 0 motor 3 position = 25420
motor 1 position = 24520 motor 3 position = 0 posKind1’: 3, ‘position1’: 15420, ‘posKind2’: 1, ‘position2’: 25420 motor 1 position = 0 motor 3 position = 15420
motor 1 position = 0 motor 3 position = 25420 posKind1’: 1, ‘position1’: 15420, ‘posKind2’: 3, ‘position2’: 0 motor 1 position = 15420 motor 3 position = 0
motor 1 position = 0 motor 3 position = 25420 posKind1’: 3, ‘position1’: 0, ‘posKind2’: 1, ‘position2’: 25420 motor 1 position = 0 motor 3 position = 0
motor 1 position = 0 motor 3 position = 0 posKind1’: 3, ‘position1’: 0, ‘posKind2’: 1, ‘position2’: 25420 motor 1 position = 25420 motor 3 position = 0
motor 1 position = 0 motor 3 position = 25420 posKind1’: 1, ‘position1’: 15420, ‘posKind2’: 3, ‘position2’: 0 motor 1 position = 15420 motor 2 position = 0

Edit 2: whipped that table up quickly from memory before heading out, might be some mistakes.

thanks for all that mate - ill take some time to try and ingest it all :wink:

Just a thought - could you try moving to blind to a position (both via HA and via remote) and once moved you dont need to post you can just browse to the below

http://POWERVIEWIP/api/shades/SHADEID?refresh=true

I am hoping that this command will cause both the poskind1 and poskind 3 to be included - that way i can just post the one command to move the tilt and ask the blind to report back both positions after the move

sorry for the delayed reply. Test results for type 23 using @TonyInHiro’s first test suite

This command:

{"shade": { "positions": { "posKind1": 3, "position1": 15420, "posKind2": 1, "position2": 0 } } }

This closes the blind and sets the tilt position to “closed”, the API the reports this:

{"shade":{"id":15111,"type":23,"batteryStatus":1,"batteryStrength":138,"roomId":41851,"firmware":{"revision":1,"subRevision":8,"build":1944},"motor":{"revision":0,"subRevision":0,"build":279},"name":"QmxpbmQgMQ==","groupId":63751,"capabilities":1,"batteryKind":"unassigned","smartPowerSupply":{"status":0,"id":0,"port":0},"positions":{"posKind1":3,"position1":15420,"posKind2":1,"position2":0},"signalStrength":4}}

This command:

{"shade": { "positions": {"posKind1": 3, "position1": 0, "posKind2": 1, "position2": 15420 } } }

closes the veins and raises the blind by ~25%. API reports:

{"shade":{"id":15111,"type":23,"batteryStatus":1,"batteryStrength":138,"roomId":41851,"firmware":{"revision":1,"subRevision":8,"build":1944},"motor":{"revision":0,"subRevision":0,"build":279},"name":"QmxpbmQgMQ==","groupId":63751,"capabilities":1,"batteryKind":"unassigned","smartPowerSupply":{"status":0,"id":0,"port":0},"positions":{"posKind1":3,"position1":0,"posKind2":1,"position2":15420},"signalStrength":4}}

This command:

{"shade": { "positions": { "posKind1": 1, "position1": 15420, "posKind2": 3, "position2": 0 } } }

Closes the blinds and the veins. The API reports:

{"shade":{"id":15111,"type":23,"batteryStatus":1,"batteryStrength":138,"roomId":41851,"firmware":{"revision":1,"subRevision":8,"build":1944},"motor":{"revision":0,"subRevision":0,"build":279},"name":"QmxpbmQgMQ==","groupId":63751,"capabilities":1,"batteryKind":"unassigned","smartPowerSupply":{"status":0,"id":0,"port":0},"positions":{"posKind1":1,"position1":15420,"posKind2":3,"position2":0},"signalStrength":4}}

This command:

{"shade": { "positions": { "posKind1": 1, "position1": 0, "posKind2": 3, "position2": 15420 } } }

Closes the blind and opens the veins by ~30% and the API reports:

{"shade":{"id":15111,"type":23,"batteryStatus":1,"batteryStrength":138,"roomId":41851,"firmware":{"revision":1,"subRevision":8,"build":1944},"motor":{"revision":0,"subRevision":0,"build":279},"name":"QmxpbmQgMQ==","groupId":63751,"capabilities":1,"batteryKind":"unassigned","smartPowerSupply":{"status":0,"id":0,"port":0},"positions":{"posKind1":1,"position1":0,"posKind2":3,"position2":15420},"signalStrength":4}}

Note that these results are completely different to @TonyInHiro’s experience :confused:

And test results from @TonyInHiro second set of tests:

{"shade": { "positions": { "posKind1": 1, "position1": 15420, "posKind2": 3, "position2": 25420 } } }

Tony: will raise the blind to the correct position, and obviously not tilt will occur, but the inverse

This clsoes my blind and sets the tilt to ~80% open

{"shade": { "positions": { "posKind1": 3, "position1": 15420, "posKind2": 1, "position2": 25420 } } }

Tony: will close the blind fully and tilt the veins to the correct position. If the blind is raised to any position and the following command is sent

This sets my blind to ~ 40% open (veins closed)

{"shade": { "positions": { "posKind1": 1, "position1": 0, "posKind2": 3, "position2": 25420 } } }

Tony: then the blind will close and the veins will not tilt.

This closes my blind and sets the tilt to ~ 80% open

{"shade": { "positions": { "posKind1": 3, "position1": 0, "posKind2": 3, "position2": 25420 } } }

This closes my blind and veins

My questions for @TonyInHiro are,

  1. are you using correctly formatted JSON? the code in your examples doesnt use quotes properly, is that just this forums formatting? If not try my properly formatted JOSN above.

  2. What firmware are your devices running? Im on 1.8.1944. Are you on an old version?

@Kingy444 sorry I haven’t got a chance to test your latest post yet, work has swamped me. I should have time this weekend to check it out.

@trullock just a quick answer of your questions.

  1. The code in the examples I posted is just what I modified from Todd King’s post (he’s using powershell so I don’t know what the escaping special characters is like and possibly the reason for the different quotation marks), I was too lazy to change tabs and copy/paste. My JSON is formatted correctly as follows

{“shade”:{“positions”:{“posKind1”:3,“position1”:0,“posKind2”:1,“position2”:25420}}}

  1. My hub firmware is 2.0.1.056
    My hub radio firmware is 2.0.2610
    My blind firmware is 2.1.2690

As I said earlier, I should have some time this weekend to do more testing of the api and how it responds.

Can you manually update blind firmware? My app seems not and says it happens automatically

Yeah, I can’t manually update the firmware. The only option I could find in the app was to opt-in for beta firmwares, which I’m not sure about.

Hi, (type 51)

I set up a VM with ha. Put my log level to debug.
Now I don’t get an error message anymore, but the tilt function is not working. The up and down does work.

The logs show this:

2022-04-08 20:18:24 DEBUG (MainThread) [aiopvapi.helpers.aiorequest] url: http://<ip>/api/shades/42081
2022-04-08 20:18:24 DEBUG (MainThread) [aiopvapi.helpers.aiorequest] data: {'shade': {'id': 42081, 'positions': {'position1': 17694, 'posKind1': 3}}}
2022-04-08 20:18:25 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Raw data update: {'id': 42081, 'type': 51, 'capabilities': 2, 'batteryKind': 1, 'smartPowerSupply': {'status': 0, 'id': 0, 'port': 0}, 'batteryStatus': 4, 'batteryStrength': 180, 'roomId': 14640, 'firmware': {'revision': 2, 'subRevision': 5, 'build': 287, 'index': 280}, 'name': 'T2ZmaWNlIFdpbmRvdw==', 'groupId': 52387, 'positions': {'position1': 17694, 'posKind1': 3}, 'signalStrength': 4}
2022-04-08 20:18:25 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: entity_id=cover.office_window, old_state=<state cover.office_window=open; current_position=1, current_tilt_position=0, roomName=Office, device_class=shade, friendly_name=Office Window, supported_features=255 @ 2022-04-08T20:17:02.282238+02:00>, new_state=<state cover.office_window=opening; current_position=1, current_tilt_position=27, roomName=Office, device_class=shade, friendly_name=Office Window, supported_features=255 @ 2022-04-08T20:18:25.927899+02:00>>
2022-04-08 20:18:25 DEBUG (MainThread) [homeassistant.components.websocket_api.http.connection] [140034874143360] Sending {"id":2,"type":"event","event":{"c":{"cover.office_window":{"+":{"s":"opening","lc":1649441905.927899,"c":{"user_id":"<userid>","id":"09450680988b347516d06e3ba3312d83"},"a":{"current_tilt_position":27}}}}}}
2022-04-08 20:18:25 DEBUG (MainThread) [homeassistant.components.websocket_api.http.connection] [140034780458576] Sending {"id":2,"type":"event","event":{"c":{"cover.office_window":{"+":{"s":"opening","lc":1649441905.927899,"c":{"user_id":"<userid>","id":"09450680988b347516d06e3ba3312d83"},"a":{"current_tilt_position":27}}}}}}
2022-04-08 20:18:25 DEBUG (MainThread) [homeassistant.components.websocket_api.http.connection] [140034780458576] Sending {"id":44,"type":"result","success":true,"result":{"context":{"id":"09450680988b347516d06e3ba3312d83","parent_id":null,"user_id":"<userid>"}}}
2022-04-08 20:18:33 DEBUG (MainThread) [aiopvapi.helpers.aiorequest] Sending a get request
2022-04-08 20:18:33 DEBUG (MainThread) [aiopvapi.helpers.aiorequest] Sending GET request to: http://<ip>/api/shades/42081
2022-04-08 20:18:36 DEBUG (MainThread) [custom_components.hunterdouglas_powerview_custom.cover] Raw data update: {'id': 42081, 'type': 51, 'capabilities': 2, 'batteryKind': 1, 'smartPowerSupply': {'status': 0, 'id': 0, 'port': 0}, 'batteryStatus': 4, 'batteryStrength': 180, 'roomId': 14640, 'firmware': {'revision': 2, 'subRevision': 5, 'build': 287, 'index': 280}, 'name': 'T2ZmaWNlIFdpbmRvdw==', 'groupId': 52387, 'positions': {'posKind1': 1, 'position1': 898, 'posKind2': 3, 'position2': 0}, 'signalStrength': 4, 'timedOut': False}
2022-04-08 20:18:36 DEBUG (MainThread) [homeassistant.core] Bus:Handling <Event state_changed[L]: entity_id=cover.office_window, old_state=<state cover.office_window=opening; current_position=1, current_tilt_position=27, roomName=Office, device_class=shade, friendly_name=Office Window, supported_features=255 @ 2022-04-08T20:18:25.927899+02:00>, new_state=<state cover.office_window=open; current_position=1, current_tilt_position=0, roomName=Office, device_class=shade, friendly_name=Office Window, supported_features=255 @ 2022-04-08T20:18:36.402120+02:00>>
2022-04-08 20:18:36 DEBUG (MainThread) [homeassistant.components.websocket_api.http.connection] [140034874143360] Sending {"id":2,"type":"event","event":{"c":{"cover.office_window":{"+":{"s":"open","lc":1649441916.40212,"c":{"user_id":null,"id":"4d56ca7111f04222568b4fd9cfae4578"},"a":{"current_tilt_position":0}}}}}}
2022-04-08 20:18:36 DEBUG (MainThread) [homeassistant.components.websocket_api.http.connection] [140034780458576] Sending {"id":2,"type":"event","event":{"c":{"cover.office_window":{"+":{"s":"open","lc":1649441916.40212,"c":{"user_id":null,"id":"4d56ca7111f04222568b4fd9cfae4578"},"a":{"current_tilt_position":0}}}}}}

Edit:

I tested the raw requests and the following command seems to function (based on V2 from the API manual)

Fully closed with tilt “down”

 Invoke-WebRequest -Method PUT -ContentType "application/json" -UseBasicParsing -Uri http://IP/api/shades/ID -Body '{ "shade": { "positions": { "posKind2": 3, "position2": 65535, "posKind1": 1, "position1": 0 } } }'

Partly open with tilt “down”

 Invoke-WebRequest -Method PUT -ContentType "application/json" -UseBasicParsing -Uri http://IP/api/shades/ID -Body '{ "shade": { "positions": { "posKind2": 3, "position2": 65535, "posKind1": 1, "position1": 10000 } } }'

I think it works the same as the ShadeBottomUpTiltAnywhere in the source of aio-powerview-api.

Edit 2:

I altered your custom component and now it’s working:

class PowerViewShadeWithTilt(PowerViewShade):
...
    async def _async_tilt(self, target_hass_position):
        """Move the shade to a position."""

        current_hass_position = self.current_cover_tilt_position
*       current_position = self.current_cover_position
        steps_to_move = abs(current_hass_position - target_hass_position)

        self._async_schedule_update_for_transition(steps_to_move)

        self._async_update_from_command(
            await self._shade.move(
                {
 *                  ATTR_POSITION1: hass_position_to_hd(current_position),
 *                  ATTR_POSKIND1: 1,
 *                  ATTR_POSITION2: hass_position_to_hd(target_hass_position),
 *                  ATTR_POSKIND2: 3,
                }
            )
        )

        self._is_opening = False
        self._is_closing = False
        if target_hass_position > current_hass_position:
            self._is_opening = True
        elif target_hass_position < current_hass_position:
            self._is_closing = True
        self.async_write_ha_state()

When opening or closing the blinds (up/down) , the tilt position is not updated in the gui (tilt slider in ha). It updates sometimes when the blinds are still in motion and then the data is wrong. The blinds tilt one way when the blinds are moving up and the other way when moving down. I sometimes see the slider for the tilt command change to the position it is when going up/down.

The _async_move command should also be different for my shades. It has to include the tilt position, otherwise it won’t tilt to the last known position when opening/closing. So it should call the same function as _async_tilt, but then with tilt position requested and position value as parameter to the function.
I added an override for it in the PowerViewShadeWithTilt, but I think it needs it’s own class.

class PowerViewShadeWithTilt(PowerViewShade):
...
    async def _async_move(self, target_hass_position):
        """Move the shade to a position."""

*       current_tilt_position = self.current_cover_tilt_position
        current_hass_position = self.current_cover_position
        steps_to_move = abs(current_hass_position - target_hass_position)

        self._async_schedule_update_for_transition(steps_to_move)

        self._async_update_from_command(
            await self._shade.move(
                {
                    ATTR_POSITION1: hass_position_to_hd(target_hass_position),
                    ATTR_POSKIND1: 1,
*                   ATTR_POSITION2: hass_position_to_hd(current_tilt_position),
*                   ATTR_POSKIND2: 3,
                }
            )
        )

        self._is_opening = False
        self._is_closing = False
        if target_hass_position > current_hass_position:
            self._is_opening = True
        elif target_hass_position < current_hass_position:
            self._is_closing = True
        self.async_write_ha_state()

Thanks that’s what I expected them to need after the comments from others above but was being hopeful/lazy the basics would work

Could you confirm a couple things for me

I thought I saw someone mention type 51 was tdbu and also had tilt ? Could be wrong though as that is completely from memory and haven’t investigated
If they do The issue I see from your debug is postype 2 is never returned so position of topMotor is unknown

Now that you had the tilt slider working for your shade did it function as expected?
Ie 0 is tilt closed and 100 is shade open (others reported some funkyness here on type 23 - and I think I know why - but need someone who has a full tilt to confirm)

Type 51 is not a tdbu. It is fixed at the top:

Example top down

It worked as expected for the slider: (slash, backslash and hyphen is a single vane)
left : /
right: \
middle: -

The buttons go from left (closed) to right (closed). I think there is no other/good solution to fix this without adding a button (with centered) in the ui for a 51 who can tilt anywhere. Some people would prefer the vanes to go left on close and some the other way around. My left is value 0 and my right is value 65535, Horizontal is half :slight_smile:.

Hi everyone, thanks for working on this! I also have Palm Beach shutters. I really would love the tilt capability. How can we get all this hard work into the main integration?

1 Like

I’ve been following this development for a couple months. Seems like there’s some talented folks (some with blinds with tilt, and others relying on those that do for api help. I think the Google Drive link above is where the custom integration is published, and it feels like it’s close. Fingers crossed!

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