Custom Component - Adaptive Cover

First off thanks @basbrus for the cool integration!

For anybody else banging their head a bit trying to get your system to behave as you’d like, perhaps this template sensor code can help to show what the integration is doing. I basically report my intended state under the given conditions and can compare this with actual cover control to debug my configuration. Some things in it are tailored to my own setup, and a few things are hard-coded there since I couldn’t find all necessary configuration parameteres exposed by the integration. For example:

  • I did not set an end time during adaptive cover setup, so there’s no difference between end and sunset behaviour
  • I switch the adaptive cover control toggle on and off with presence via an extra automation in order to force the blind closed instead of the adaptive cover open behaviour
  • start time and a few other things from integration set up were not accessible, so I’ve hard coded them below

Temperature behaviour is still a little confusing for me. I think this is what happens:

Outside temp switch on and climate mode switch on:

  • Outside temp > min summer temp = summer mode (blind forced close when time is between start and stop time)
  • Outside temp < min summer temp and inside temp > max comfort temp = summer mode (unintuitive mode name, but blinds don’t close now because the outside temp switch is on and outside temp is not high enough)
  • Outside temp < min summer temp and inside temp between max and min comfort temp = intermediate mode
  • Inside temp < min comfort temp and outdoor temp < min summer temp = winter mode (blind forced open when time is between start and stop time)
- platform: template
  sensors:
    adaptive_cover_front_window_diagnostics:
      friendly_name: Diagnostics front window adaptive cover
      value_template: |
        {% set position_actual = state_attr('cover.front_window_blinds', 'current_position') %}
        {% set reached_target = is_state('sensor.adaptive_cover_cover_position_front_window', position_actual | string) %}
        {% set adaptive_cover_enabled = is_state('switch.adaptive_cover_toggle_control_front_window', 'on') %}
        {% set moving = is_state('binary_sensor.adaptive_cover_manual_override_front_window', 'on') %}
        {% set override = is_state('binary_sensor.adaptive_cover_manual_override_front_window', 'on') %}
        {% set mode = states('sensor.adaptive_cover_control_method_front_window') %}
        {% set sunlight = is_state('binary_sensor.adaptive_cover_sun_infront_front_window', 'on') %}
        {% set before_start = now() < today_at('06:00:00') %}
        {% set presence = is_state('input_boolean.presence_expected', 'on') %}
        {% set before_sunrise = is_state('sun.sun', 'below_horizon') and now().time().hour < 12 %}
        {% set after_sunset = is_state('sun.sun', 'below_horizon') and now().time().hour > 12 %}
        {% set weather = states('weather.buienradar') in ['sunny', 'clear', 'windy', 'partlycloudy', 'cloudy'] %}
        {% set outside_enabled = is_state('switch.adaptive_cover_outside_temperature_front_window', 'on') %}
        {% set temp_indoor = states('sensor.ground_floor_temperature') | float(0) %}
        {% set temp_outdoor = states('sensor.outdoor_temperature_average') | float(0) %}
        {% set temp_criteria = [20, 25, 30] %}
        {% set hot_outside = outside_enabled and (temp_outdoor > temp_criteria[2]) %} 
        {% set hot_inside = not outside_enabled and (temp_indoor > temp_criteria[1]) %}
        
        {% if not adaptive_cover_enabled %}
          adaptive cover off
        {% elif override %}
          manual override
        {% elif not presence %}
          closed (nobody home)
        {% elif before_start %}
          closed (before early start)
        {% elif reached_target %}
          {% if before_sunrise %}
            closed (before sunrise)
          {% elif after_sunset %}
            closed (after sunset)
          {% elif mode == 'winter' %}
            open (winter low temp)
          {% elif mode == 'summer' and (hot_outside or hot_inside) %}
            {% if hot_outside %}
              closed (summer outdoor high temp)
            {% elif hot_inside %}
              closed (summer indoor high temp)
            {% endif %}
          {% elif mode == 'intermediate' %}
            {% if sunlight and weather %}
              controlling (sun in view)
            {% elif not sunlight and weather %}
              open (sun out of view)
            {% elif not weather %}
              open (invalid weather)
            {% else %}
              unknown control state 1
            {% endif %}
          {% endif %}
        {% elif moving %}
          moving
        {% else %}
          unknown control state 2
        {% endif %}
      icon_template: mdi:magnify

Putting some cover entities and their respective diagnostic sensors next to eachother in a dashboard worked well for me.

@basbrus how can I tell from the integration if a cover has been manually overridden in order to use that for other automations? It seems that the manual override sensor from your integration doesn’t differentiate between a move made by adaptive control or via some other source (pushing buttons on the blind, etc.), at least not in any of the states or attributes of the integration’s entities that I’ve found.

Are you sure you have correctly setup the threshold before the integration interprets something as a manual intervention? Because the state is not immediately pushed to the cover in most cases the integration interprets it as a manual intervention. For what I’ve played around with it the only way around this is playing around with this threshold value

I’ll try playing around with the override threshold. Thanks for the tip.

Update: have not been able to get this to work. Integration doesn’t seem to differentiate outwardly between a move action initiated by the integration or by some other manual overriding source.

Anybody understand what is going on here? Control method is in summer mode as I would expect (in this case because outside temperature exceeds min summer temperature). Weather condition is valid for control, and presence is detected. The blinds should close as far as I can tell, but they’re being controlled to block the sun as if the control method was intermediate. :man_shrugging:

Update: Had too much difficulty understanding what Adaptive Cover was doing, so went my own route using Adaptive Cover as a basis but focused on reporting the reason behind the state in my own template sensors and in nice buttons in the front end. It’s hard/impossible to make a one-fits-all solution that is still usable and I recognize that I wanted something more specific than any blueprint or integration I’ve come across. Some contextual buttons like this could go a long way to help users configure adaptive cover as desired, but more info needs to be available (e.g. via sensor attributes).

Hello,
Don’t ask me why, but for the shutters to close completely when in summer mode, the Transparent Blind in Climate mode must be set to ON.
I also noticed that this function doesn’t work on the slats of an adjustable sunshade.

I have a switchbot blind tilt control on the blinds in my office. I had the same problem when the “type of blind” was set to “tilted blind.” It never moved. While troubleshooting, tried setting it up as a “vertical blind” and adaptive cover started moving the blinds. I don’t know why this is the case. My speculation is that the SwitchBot blind tilt devices present themselves to Home Assistant as roller blinds (what adaptive cover seems to call a “vertical blind”) somehow.

Anyway, try setting your up as a “vertical blind” and see if it works for you too.

Dears,

I want to have overrides for my covers, for example, I have a personal rain gauge, and I want to close the covers when rains, and I don’t want to use the weather entity, because is not exact than i want. Also I have a awning, and I want to use the adaptive cover integration to manage this awning, but I want to close also when rains and when the wind is very high and I also have a personal anemometer. Is not possible to add some personal overrides to close the covers according with personal alarms?

Thanks,

Fantastic cards design, can you share the yaml files for the cards?

The integration provides a switch to enable or disable the adaptive control. With this you can use automations or scripts to toggle the adaptive control and do what you want with the covers while adaptive cover is off.

Here is the yaml for one of my cards. This is made to run with my own custom template entities (not provided by the adaptive cover integration, but based on adaptive cover and its earlier source), so you’ll have to rewrite it a bit to work with your own setup. I use a datetime helper to override cover control until the future timestamp in the helper is reached, and my own template sensor has attributes that explain the state (sunset, morning_hold, closed: hot_outside, manual_override, etc.) and if the sun is in direct view or not.

The button makes use of the custom button-card installable via HACS.

type: custom:button-card
variables:
  name: Master Bedroom
  cover_entity: cover.master_bedroom_window
  override_entity: input_datetime.smart_blind_master_bedroom_window_override_until
  sensor_entity: sensor.smart_blind_master_bedroom_window_adaptive_position
entity: "[[[ return variables.cover_entity; ]]]"
triggers_update: all
layout: custom
show_icon: true
show_name: false
show_state: false
icon: |
  [[[
    const pos = Number(states[variables.cover_entity]?.attributes?.current_position ?? -1);
    const reason = states[variables.sensor_entity].attributes?.reason ?? '';
    if (reason == 'cover_disabled') return 'mdi:lock';
    if (pos <= 5)      return 'mdi:roller-shade-closed';
    if (pos >= 90)     return 'mdi:checkbox-blank-outline';
    return              'mdi:roller-shade';
  ]]]
custom_fields:
  text_block: |
    [[[
      const stateObj   = states[variables.cover_entity];
      const sensorObj  = states[variables.sensor_entity];
      const override   = states[variables.override_entity]?.state;

      const state  = stateObj?.state ?? '';
      const pos    = stateObj?.attributes?.current_position ?? '?';
      const reason = sensorObj?.attributes?.reason ?? '';

      let label = reason;
      if (reason === 'manual_override' && override) {
        const timePart = override.includes(' ') ? override.split(' ')[1] : override;
        const [hour = '', minute = ''] = timePart.split(':');
        label = `${reason} until ${hour.padStart(2,'0')}:${minute.padStart(2,'0')}`;
      }

      return `
        <div style="display:flex;flex-direction:column;align-items:flex-start;">
          <div style="font-weight:bold;font-size:14px;">${variables.name}</div>
          <div style="font-size:12px;font-style:italic;color:var(--secondary-text-color);">${label}</div>
          <div style="font-size:12px;color:var(--secondary-text-color);">
            ${state.charAt(0).toUpperCase() + state.slice(1)} · ${pos}%
          </div>
        </div>
      `;
    ]]]
  sun_icon: |
    [[[
      return states[variables.sensor_entity]?.attributes?.sun_in_view
        ? '<ha-icon icon="mdi:sun-angle" style="--mdc-icon-size:20px;color:darkorange;"></ha-icon>'
        : '';
    ]]]
styles:
  grid:
    - grid-template-areas: "\"i text_block\""
    - grid-template-columns: 1fr 3fr
  card:
    - align-items: center
    - padding: 6px
    - border-radius: 12px
    - background-color: var(--card-background-color)
    - box-shadow: 0 2px 6px rgba(0,0,0,0.2)
    - position: relative
  icon:
    - width: 100%
    - max-width: 35px
    - justify-self: center
    - color: |
        [[[
          const reason = states[variables.sensor_entity]?.attributes?.reason ?? '';
          if (reason === 'control_off' || reason === 'cover_disabled')       return 'grey';
          if (reason === 'manual_override')                                  return 'orangered';
          const coverState = states[variables.cover_entity]?.state ?? '';
          if (coverState === 'closed'  || coverState === 'closing')          return 'mediumpurple';
          if (coverState === 'open'    || coverState === 'opening')          return 'orange';
          return 'var(--paper-item-icon-color)';
        ]]]
  custom_fields:
    text_block:
      - justify-self: start
      - padding: 4px
    sun_icon:
      - position: absolute
      - top: 1px
      - left: 3px
      - pointer-events: none
      - width: 20px
      - height: 20px
tap_action:
  action: more-info
double_tap_action:
  action: more-info
  entity: "[[[ return variables.sensor_entity; ]]]"
hold_action:
  action: call-service
  service: input_datetime.set_datetime
  service_data:
    entity_id: "[[[ return variables.override_entity; ]]]"
    time: "00:00:00"
    date: "[[[ return new Date().toISOString().slice(0,10); ]]]"
grid_options:
  columns: 6

Thanks a lot cjborchert is working well with my configuration also.

For some odd reason, my adaptive cover control seems to be stuck in Winter mode on every one of my blinds.

I’ve tried using a sensor for outside temp

Sensor:

And also removed the sensor so it should use the climate entity.

While it’s a cold and rainy day here today, it’s still well above 14C and should be in summer mode, or potentially intermediate mode since inside temp is between thresholds. But certainly not winter.

Also made sure the inside temp is not below the minimum comfort threshold.

Inside temp sensor:

I understand, thanks, I solved too with this information.

Dear cjborchert, I don’t have the date time helper, but I don’t understand how you can show the text with the cover is disable or not (in the second line text) if the cover is dissable or not. Only with th datetime help you show all the information in the second line text?. Thanks.

I created my own version of adaptive cover using template sensors, and in that template sensor I have an attribute that reports this state information used for the custom button (the state defines the cover position, one attribute explains the state, and another evaluates to true if the sun is in direct view of the window).

You could make your own state info sensor to work with your setup. I tried to make such a sensor here at the end of this post but I struggled to find a way to get it to work in all cases because I couldn’t fully understand how adaptive cover works. If you don’t use all the capabilities of adaptive cover (indoor and outdoor temperature thresholds, weather conditions, etc.) it might be more straightforward than what I was working with.

Maybe the flow chart from @patrickp78 can help here? I had similar unexpected behaviour that I was unable to resolve myself…

Thanks Chris.

Hello everyone

I’ve been using this amazing custom integration with my vertical blinds. Now I’m trying to set two awnings that are not horizontal just 45º I’d say.

The thing is not working properly, all day is set to 100% which is up (not protecting from sun). I think is the length, height and angle problem.

So for lenght I just measured the actual cloth or fabric from the top to the end around 1,6m
For height I measured from the floor to the anchor position (2,6~)
Lastly for the angle I set to 45º, more or less the awning unrolls diagonally.

The other parameters I am pretty confident I got it right. What am I missing?

Hi, I came across Adaptive Cover in a video and was totally excited. Unfortunately, I already have a problem during the initial setup. I cannot select any of my 5 covers under Cover Entities. It always says ‘no matching entities found’ there. In the developer tools, all 5 are displayed under the term cover. What am I doing wrong?