Custom Component: Cover Time Based

I can control and monitor only the relays.
If I move the cover by the wall buttons, from HA I can see the state of the relay.
So I suppose that if I ALWAYS monitor the states of the relay (by counting the seconds of movement) the position will be always updated…

I suppose that “cover time based” works like this:
-when I call cover.close_cover/cover.open_cover it starts counting and stops the cover when the position (time) is reached

My idea is start counting when the relay open/close… in this way the position is updated when I use the wall buttons also…

Hi,

Is there a way to avoid creating switches to be used with that custom component to control cover.
I already have working cover object.
In order to integrate that component, I would have to create via script the open/close/stop switches via script but there are already cover object. Might be that there is a direct command I not aware of to avoid this scripting.

I do have native cover from RFXTRX integration that provide native cover objects from RTS somfy shutters.

Let me know,
thanks for your help,
Pierre

The custom component documentation explains it this way as far as I remember : considering the variety of devices and behavior, the most adaptive way to do is to let the configuration be done by the end-user in a script.

It is a one-time job ; as I posted months ago, I managed to do it with my configuration which seems as yours whereas it was my first sript in HA and my making it up.

When this is done, original cover objects are not to be used, and even they should be put apart to avoid the lost of the position by the time counting in the custom component. It is not a problem when you totally forget them in the UI and automations.

The Custom Component works very well in Google Home by voice commands without further setup. It naturally integrates in my Nodered automations in parallel. I can even use this custom card should I want a slider command in the UI : slider-entity-row. So I am more than happy with it.

1 Like

Hi all
I have some tuya switches for my covers with 3 buttons eash. up-down-stop.
They don’t report position so I would like to try this component, because I need to know approximately their position.

since I have only one switch for each cover should I use the same entity in
the open_switch_entity_id: and close_switch_entity_id ?

cover:
  - platform: cover_time_based
	devices:
	  room_rolling_shutter:
	   name: Room Rolling Shutter
	   travelling_time_down: 23
	   travelling_time_up: 25
	   open_switch_entity_id: switch.wall_switch_right
	   close_switch_entity_id: switch.wall_switch_left
	   aliases:
	    - room_rolling_shutter

Hello, @robi & @davidramosweb . Thank you both for this integration, got it “almost” working with the @robi RF variant.
But I need a little help here, not sure if possible: My case, and what I have:

  • Control by Broadlink RF - the SAME command for OPEN / CLOSE / STOP.
  • Door sensor that only monitors CLOSED state.
  • Gate funtionality:
    • if gate is CLOSED - RF command OPENs it.
    • if gate is OPEN - RF command CLOSEs it.
    • if gate is OPENING - RF command STOPs, and next command will be to CLOSE.
    • if gate is CLOSING - RF command STOPs, and next command will be to OPEN.

How should I do this logic? In the button created by the integration, if gate is OPENING and I press it again, it starts CLOSING, does not STOP.
Thanks again, both, and in advance to anyone for the help!

To use the door sensor, already have this automation:

alias: Gate - Status
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.gate_contact
    from: 'off'
    to: 'on'
    id: gate_opening
  - platform: state
    entity_id: binary_sensor.gate_contact
    id: gate_closed
    from: 'on'
    to: 'off'
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id: gate_opening
        sequence:
          - service: cover_rf_time_based.set_known_action
            data:
              entity_id: cover.gate
              action: open
      - conditions:
          - condition: trigger
            id: gate_closed
        sequence:
          - service: cover_rf_time_based.set_known_position
            data:
              entity_id: cover.gate
              position: 0
              confident: true
    default: []
mode: single

Well, maybe replying to my own question, although it’s not ideal because it overrides the cover functionality, maybe I must change the “toggle” action of the button for a custom script that follows this logic. It will only work in Lovelace where I set it up, not directly with Google assistant, for example.
Is there other way? Maybe customize the code? Not a programmer, though…

My logic for the script that will replace the button “toggle” action:

### Support variable:
   input_select.gate_next_action = ['Open','Close']
### Logic (NOT programmning language):
   if <gate = closed > then (call-service.open_cover)
   if <gate = opening > then (call-service.stop_cover + define input_select.gate_next_action = 'Close')
   if <gate = closing > then (call-service.stop_cover + define input_select.gate_next_action = 'Open')
   if <gate = open AND position = 100 > then (call-service.close_cover)
   if <gate = open AND position < 100 > then 
         - if input_select.gate_next_action = 'Open' then (call-service.open_cover)
         - if input_select.gate_next_action = 'Close' then (call-service.close_cover)

Any other suggestions on how to do this? It would be better if this funciontalitty could be in the cover itself. Is it possible to add new service and next_action data to the custom cover that reacts like this? Maybe call it “selective_action” instead of “toggle”?

Well, I got my gate working with some work, but now it respondes as it should:

Hardware and functionallity(in my case):

  1. Control by Broadlink RF - the SAME command for OPEN / CLOSE / STOP.
  2. Door sensor that only monitors CLOSED state.
  3. Gate funtionality:
  • if gate is CLOSED - RF command OPENs it.
  • if gate is OPEN - RF command CLOSEs it.
  • if gate is OPENING - RF command STOPs, and next command will be to CLOSE.
  • if gate is CLOSING - RF command STOPs, and next command will be to OPEN.

image

Integrations / custom cards used:

  1. Lovelace:

    • custom:button-card
    • custom:vertical-stack-in-card
    • custom:bar-card
    • custom:custom:multiple-entity-row
  2. Custom Component (HACS)

  3. Helpers defined in HA:

input_select.portao_do_quintal_proxima_accao (options="Abrir","Fechar","Parar")
  1. Working single command for activating the gate (RF) defined within Broadlink: This must work:
  - service: remote.send_command
    data:
      entity_id: remote.broadlink_ir_rf_rm4
      device: portao-quintal
      command: ativar_on_off
  1. Code
configuration.yaml
cover: !include covers.yaml
script: !include scripts.yaml
automation: !include automations.yaml

covers.yaml
- platform: cover_rf_time_based
  devices:
    portao_quintal:
      name: Portão do Quintal
      travelling_time_up: 24
      travelling_time_down: 23
      close_script_entity_id: script.ativar_portao_do_quintal
      stop_script_entity_id: script.ativar_portao_do_quintal
      open_script_entity_id: script.ativar_portao_do_quintal
      send_stop_at_ends: false #optional
      aliases: #optional
        - portao_quintal
scripts.yaml
ativar_portao_do_quintal:
  alias: Portão do Quintal - Activar
  sequence:
  - service: remote.send_command
    data:
      entity_id: remote.broadlink_ir_rf_rm4
      device: portao-quintal
      command: ativar_on_off
      hold_secs: 1.5
    entity_id: remote.broadlink_ir_rf_rm4
  mode: single
  icon: mdi:gate

portao_do_quintal_controlo:
  alias: Portão do Quintal - Controlo
  sequence:
  - choose:
    - conditions:
      - condition: state
        entity_id: cover.portao_quintal
        state: closed
      sequence:
      - service: cover.open_cover
        target:
          entity_id: cover.portao_quintal
      - service: input_select.select_option
        target:
          entity_id: input_select.portao_do_quintal_proxima_accao
        data:
          option: Parar
    - conditions:
      - condition: state
        entity_id: cover.portao_quintal
        state: open
      - condition: numeric_state
        entity_id: cover.portao_quintal
        attribute: current_position
        above: '99'
      sequence:
      - service: cover.close_cover
        target:
          entity_id: cover.portao_quintal
      - service: input_select.select_option
        target:
          entity_id: input_select.portao_do_quintal_proxima_accao
        data:
          option: Parar
    - conditions:
      - condition: state
        entity_id: cover.portao_quintal
        state: opening
      sequence:
      - service: cover.stop_cover
        target:
          entity_id: cover.portao_quintal
      - service: input_select.select_option
        target:
          entity_id: input_select.portao_do_quintal_proxima_accao
        data:
          option: Fechar
    - conditions:
      - condition: state
        entity_id: cover.portao_quintal
        state: closing
      sequence:
      - service: cover.stop_cover
        target:
          entity_id: cover.portao_quintal
      - service: input_select.select_option
        target:
          entity_id: input_select.portao_do_quintal_proxima_accao
        data:
          option: Abrir
    - conditions:
      - condition: and
        conditions:
        - condition: state
          entity_id: input_select.portao_do_quintal_proxima_accao
          state: Abrir
        - condition: numeric_state
          entity_id: cover.portao_quintal
          attribute: current_position
          above: '0'
          below: '100'
      sequence:
      - service: cover.open_cover
        target:
          entity_id: cover.portao_quintal
      - service: input_select.select_option
        data:
          option: Parar
        target:
          entity_id: input_select.portao_do_quintal_proxima_accao
    - conditions:
      - condition: and
        conditions:
        - condition: state
          entity_id: input_select.portao_do_quintal_proxima_accao
          state: Fechar
        - condition: numeric_state
          entity_id: cover.portao_quintal
          attribute: current_position
          above: '0'
          below: '100'
      sequence:
      - service: cover.close_cover
        target:
          entity_id: cover.portao_quintal
      - service: input_select.select_option
        data:
          option: Parar
        target:
          entity_id: input_select.portao_do_quintal_proxima_accao
    default: []
  mode: single
automations.yaml
- id: '1634052087263'
 alias: Portão do Quintal - Estado actual e próximo
  description: ''
  trigger:
  - platform: state
    entity_id: binary_sensor.portao_quintal_contact
    from: 'off'
    to: 'on'
    id: portao_abriu
  - platform: state
    entity_id: binary_sensor.portao_quintal_contact
    id: portao_fechou
    from: 'on'
    to: 'off'
  - platform: numeric_state
    entity_id: cover.portao_do_quintal
    id: abriu_tudo
    attribute: current_position
    above: '99'
  condition: []
  action:
  - choose:
    - conditions:
      - condition: trigger
        id: portao_abriu
      sequence:
      - service: cover_rf_time_based.set_known_action
        data:
          entity_id: cover.portao_quintal
          action: open
    - conditions:
      - condition: trigger
        id: portao_fechou
      sequence:
      - service: cover_rf_time_based.set_known_position
        data:
          entity_id: cover.portao_quintal
          position: 0
          confident: true
      - service: input_select.select_option
        data:
          option: Abrir
        target:
          entity_id: input_select.portao_do_quintal_proxima_accao
    - conditions:
      - condition: trigger
        id: abriu_tudo
      sequence:
      - service: input_select.select_option
        data:
          option: Fechar
        target:
          entity_id: input_select.portao_do_quintal_proxima_accao
    default: []

customize.yaml
cover.portao_quintal:
  device_class: gate
input_select.portao_do_quintal_proxima_accao:
  templates:
    icon: if (state == "Abrir") return 'mdi:arrow-expand-horizontal'; if (state ==
      "Fechar") return 'mdi:arrow-collapse-horizontal'; return 'mdi:stop';
script.portao_do_quintal_controlo:
  icon: mdi:gate
Custom Button in Lovelace
type: custom:vertical-stack-in-card
cards:
  - type: custom:button-card
    show_state: true
    show_name: false
    tap_action:
      action: call-service
      service: script.portao_do_quintal_controlo
    icon: mdi:gate
    entity: cover.portao_quintal
    size: 180px
    lock:
      enabled: '[[[ return entity.state === ''closed''; ]]]'
      duration: 30
      unlock: tap
    styles:
      grid:
        - grid-template-areas: '"i s" "i s"'
        - grid-template-columns: 2.5fr 1fr
        - grid-template-rows: min-content min-content
  - type: custom:bar-card
    entities:
      - entity: cover.portao_quintal
        attribute: current_position
    unit_of_measurement: '%'
    icon: mdi:gate
    direction: right
    positions:
      icon: outside
      indicator: outside
      name: inside
    severity:
      - from: '0'
        to: '0'
        color: green
        icon: mdi:gate
      - from: '1'
        to: '10'
        color: yellow
        icon: mdi:gate
      - from: '11'
        to: '20'
        color: orange
        icon: mdi:gate-open
      - from: '21'
        to: '100'
        color: red
        icon: mdi:gate-open
    attribute: cover.portao_quintal
  - type: entities
    entities:
      - type: custom:multiple-entity-row
        entity: input_select.portao_do_quintal_proxima_accao

Sorry for the portuguese variables, will translate it as I can. Maybe a little incomplete, but maybe can help others! Works as a CHARM! :slight_smile:

2 Likes

Since ESPHome v1.15.0 (September 13, 2020) it’s possible to create native Time-Based covers, where timing is intelligently handled by the ESP hardware itself. Home Assistant just gets standard covers with position setting.

Possible with RF codes on Sonoff RF Bridge too. You can have as many such covers as you want all built-in the same RF Bridge, even with Open/Close commands appearing on the web interface of it:

substitutions:
  device_name: sonoff-rf-bridge
  friendly_name: "RF Bridge"
  device_ip: 192.168.81.22

esphome:
  name: ${device_name}
  platform: ESP8266
  board: esp01_1m
  esp8266_restore_from_flash: true

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    static_ip: ${device_ip}
    gateway: 192.168.81.254
    subnet: 255.255.255.0
  use_address: ${device_ip}

logger:
  baud_rate: 0
  level: INFO

uart:
  tx_pin: GPIO01
  rx_pin: GPIO03
  baud_rate: 19200

rf_bridge:

api:
  reboot_timeout: 15min
  password: !secret api_password
  encryption:
    key: !secret encryption_key
  services:
    - service: send_rf_raw
      variables:
        raw: string
      then:
        - rf_bridge.send_raw:
            raw: !lambda 'return raw;'

ota:
  password: !secret ota_password

web_server:
  version: 2
  local: true
  port: 80
  auth:
    username: !secret web_server_adminuser
    password: !secret web_server_password

sensor:
  - platform: wifi_signal
    name: ${friendly_name} WiFi signal
    update_interval: 60s
  - platform: uptime
    name: ${friendly_name} Uptime

status_led:
  pin:
    number: GPIO13
    inverted: true

binary_sensor:
- platform: status
  name: ${friendly_name} Status

button:
- platform: restart
  name: ${friendly_name} Restart
  
cover:
- platform: time_based
  name: 'My Room Cover'
  device_class: shutter
  assumed_state: true
  has_built_in_endstop: true

  close_action:
    - rf_bridge.send_raw:
        raw: 'AAB0XXXXX....XXXXXXXXXX'
  close_duration: 34

  stop_action:
    - rf_bridge.send_raw:
        raw: 'AAB0XXXXX....XXXXXXXXXX'

  open_action:
    - rf_bridge.send_raw:
        raw: 'AAB0XXXXX....XXXXXXXXXX'
  open_duration: 36s

With this, you don’t need this component anymore (at least for RF), all is done in hardware, also the states are restored from flash.

1 Like

Wow, thank you for the component, it’s working well!
Also ‘set_known_action’ it’s working fine.

A question: is it possible to create a service to reach a position value?

many thks

Use standard service of HA, cover.set_cover_position.

Thank you for your reply.

I tried it, but cover goes over the target, I think it depends my shutters work: one pulse to open, another pulse to stop (same for close button).
May be I will implement a stop comand when shutter reach the target value.

You should adjust the scripts with an extra stop, I guess…

yes, it’s not so easy for me :expressionless: :smiley:
some suggestion?

this is my configuration:

    devices:
      tapparella_studio:
        name: Tapparella Studio
        travelling_time_up: 28
        travelling_time_down: 28
        close_script_entity_id: script.chiudi_tapparella_studio 
        stop_script_entity_id: script.ferma_tapparella_studio 
        open_script_entity_id: script.apri_tapparella_studio 
        send_stop_at_ends: True #optional
        always_confident: True #optional
script:
  apri_tapparella_studio:
    sequence:
      - service: rest_command.tapparella_studio_su
 
  chiudi_tapparella_studio:
    sequence:
      - service: rest_command.tapparella_studio_giu
      
  ferma_tapparella_studio:
    sequence:
      - service: >  
            {% if is_state('sensor.stato_tapparella_studio', 'isgoingup') %} {{'rest_command.tapparella_studio_su'}} 
            {% elif is_state('sensor.stato_tapparella_studio', 'isgoingdown') %} {{'rest_command.tapparella_studio_giu'}} 
            {% endif %}

I have enabled debugging with logs, this is the result:

2021-12-14 14:45:51 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: async_set_cover_position: 75
2021-12-14 14:45:51 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: set_position
2021-12-14 14:45:51 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: set_position :: current_position: 31, new_position: 75
2021-12-14 14:45:51 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: start_auto_updater
2021-12-14 14:45:51 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: init _unsubscribe_auto_updater
2021-12-14 14:45:51 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: set_position :: command open_cover

...
2021-12-14 14:45:53 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: start_auto_updater
2021-12-14 14:45:53 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: auto_updater_hook
...
2021-12-14 14:46:11 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: auto_updater_hook
2021-12-14 14:46:11 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: auto_updater_hook
2021-12-14 14:46:11 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: auto_updater_hook :: position_reached
2021-12-14 14:46:11 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: stop_auto_updater
2021-12-14 14:46:11 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: auto_stop_if_necessary :: send_stop_at_ends :: calling stop command
2021-12-14 14:46:11 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: _async_handle_command :: STOP

but the cover is at position 100.

You should add some magnetic sensors sense the real positions…

i think there is something wrong if debug give position_reached not at the input value, but and 100 or 0 only.

there is a problem with the target position inside TravelCalculator set_known_action, in my case it goes from 50 to 0…

021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: position_reached False
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: auto_updater_hook
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: current_position 94
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: target_position 50
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: position_reached False
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: current_position 94
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: target_position 50
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: position_reached False
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: start_auto_updater
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: auto_updater_hook
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: current_position 93
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: target_position 0
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: position_reached False
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: current_position 93
2021-12-15 09:16:15 DEBUG (MainThread) [custom_components.cover_rf_time_based.cover] Tapparella Studio: target_position 0

the problem is set_known_action service that overwrite target position.
I solved it with two code changes:

    def auto_updater_hook(self, now):
        """Call for the autoupdater."""
        self.async_schedule_update_ha_state()
        if self.position_reached():
            _LOGGER.debug(self._name + ': ' + 'auto_updater_hook :: position_reached')
            self.stop_auto_updater()
            self._target_position = 0    #jump396
        self.hass.async_create_task(self.auto_stop_if_necessary())

and

    async def set_known_action(self, **kwargs):
        """We want to do a few things when we get a position"""
        if (self._target_position == 0 or self._target_position == 100):   #jump396
          action = kwargs[ATTR_ACTION] 
          if action not in ["open","close","stop"]:
            raise ValueError("action must be one of open, close or cover.")
          if action == "stop":
            self._handle_stop()
            return
          if action == "open":
            self.tc.start_travel_up()
            self._target_position = 100
          if action == "close":
            self.tc.start_travel_down()
            self._target_position = 0 
          self.start_auto_updater()

Hello !!!
how to fix the warning appears 1 time when the HA server boots
Using Cover Time Based Component

Logger: homeassistant.helpers.entity
Source: helpers/entity.py:549
First occurred: 23:20:28 (4 occurrences)
Last logged: 23:20:28

Entity cover.roleta_detskaia (<class 'custom_components.cover_time_based.cover.CoverTimeBased'>) implements device_state_attributes. Please report it to the custom component author.
Entity cover.roleta_gostinnaia (<class 'custom_components.cover_time_based.cover.CoverTimeBased'>) implements device_state_attributes. Please report it to the custom component author.
Entity cover.roleta_kukhnia (<class 'custom_components.cover_time_based.cover.CoverTimeBased'>) implements device_state_attributes. Please report it to the custom component author.
Entity cover.shtora_spalnia (<class 'custom_components.cover_time_based.cover.CoverTimeBased'>) implements device_state_attributes. Please report it to the custom component author.

configuration.yaml :

cover:
  - platform: cover_time_based
    devices:
      shtora_detskaya:
       name: "Ролета Детская"
       travelling_time_down: 35
       travelling_time_up: 37
       open_switch_entity_id: light.mega2_23
       close_switch_entity_id: light.mega2_22
       aliases:
        - shtora_detskaya
  - platform: cover_time_based
    devices:
      shtora_gostinaya:
       name: "Ролета Гостинная"
       travelling_time_down: 35
       travelling_time_up: 39
       open_switch_entity_id: light.mega1_22
       close_switch_entity_id: light.mega1_23
       aliases:
        - shtora_gostinaya
  - platform: cover_time_based
    devices:
      shtora_kuhnya:
       name: "Ролета Кухня"
       travelling_time_down: 36
       travelling_time_up: 41
       open_switch_entity_id: light.mega_23
       close_switch_entity_id: light.mega_22
       aliases:
        - shtora_kuhnya
  - platform: cover_time_based
    devices:
      shtora_spalna:
       name: "Штора Спальня"
       travelling_time_down: 16
       travelling_time_up: 16
       open_switch_entity_id: light.mega2_09
       close_switch_entity_id: light.mega2_08
       aliases:
        - shtora_spalna

There is a issue here :

The fix is easy : rename “device_state_attributes into extra_state_attributes”

Like that one :