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

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…

Not needed!! See my prior post.

See this Palm Beach™ Polysatin™ Shutters | Official Hunter Douglas

From the picture my guess is that these would be a new Type / Capabilities entry that does report a tilt position but not a primary position.

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

Type 23 blinds here.

The new update is no longer causing errors, and using the sliders in HA sets the blind to the correct position, and also reports the correct position.

One bug I found is that if the blinds are open and you set a tilt position from the hunterdouglas app, HA will update the tilt position, but the blind position will stay at whatever it previously was. Inversely if you have the blind closed and tilted, and you open the blinds in the hunterdouglas app, HA will update the blind position to open, but the tilt position will also remain open. I’m guessing you have only implemented the logic for the unreported poskind in the POST section of the code, but I think it also needs to be anytime a position is reported.

Latest update no longer shows the tilt slider for Type 51. (raise/lower response seems much faster!)

I am curious if there is something off with my HA so will try a clean install on a fresh SD card tomorrow to rule that out.

Hello @andrewfg - appreciate your feedback and assistance to date - did not realise you were on the HA forum too

Guess/Assume was just some poor wording on my part - you’re obviously correct around it being a fact. It has more to do with how we are accessing the data with HA. Which also leads into the cached value situation.

We are essentially processing the json directly now rather than storing it into a property of each entity which I believe has made things quicker and more consistent - could be the placebo effect but it works.

The side effect of this is that when a shade only reports 1 poskind back (type 18/23) we won’t get the pos of the other - in that case we will then use the cached value. I could have went with class overrides here but I had another edge case this fixed so this was a cleaner solution. As we are using a function now to return the value that works out simple enough.
The other edge case it fixes is when you send a bad API command to a shade that needs two posKind and it only has 1 the shade won’t return the true value of the one you didn’t send until a refresh=true is sent

We never did before but this release does now support either poskind1 or posKind2 in either order and values for all 3 shade types too.

Thanks again for your help mate

@TonyInHiro new one up that should work wth type 23 as before - has the same logic @trullock implemented so should be fine

appreciate the confirmation though

I hear what you say. But you don’t need to cache any values. If HA asks for (say) a primary rail position, and the hub responds with a tilt position, the you can hard code it that the primary position is zero. And vice versa.

Yea I could definitely hard code or use a sub class override as I mentioned.
I just went the cache route to address that other edge case I mentioned of the api info returned being bad (that should never happen but if we can account for it why not)
In the case the return is bad you would get the issue described by @TonyInHiro above where the attr doesn’t exist

In reality - The cache is probably never even going to be accessed for anything other than silhouette but it won’t hurt :wink: and for silhouette we are pushing the value so it will be correct

The Dev team have wanted me to keep it as simple as I can so trying to avoid overrides if I don’t need them

EDIT: while I refer to it as a cache it’s really just a property on the class and the way it was done prior to this version - new way is an enhancement and the old way is now a fallback