esphome / feature-requests

ESPHome Feature Request Tracker
https://esphome.io/
412 stars 26 forks source link

Get cover position when triggered from Home Assistant front end #566

Closed slarti42 closed 4 years ago

slarti42 commented 4 years ago

Describe the problem you have/What new integration you would like

Drive a servo to a position that is requested by Home Assistant.

Please describe your use case for this integration and alternatives you've tried:

Hi. I'm new to esphome and Home Assistant but looking to move away from mysensors/domoticz/mqtt because using Wemos D1 minis is proving to be a headache with them.

One thing I've always had is servo controlled mini-blinds. My problem is that I would like for the position (not just open/closed) be in sync with local control and the front end. I have almost accomplished this in Esphome but can't seem to get the final part right which is setting the position from Home Assistant.

I have a global variable and a script which drive the servo:

globals:
  - id: my_pos
    type: float

script:
  - id: move_servo
    then:
      - servo.write:
          id: my_servo
          level: !lambda "return id(my_pos);"
      - delay: 1s
      - servo.detach: my_servo
      - cover.template.publish:
          id: pub_pos
          position: !lambda "return id(my_pos) / 2 + 0.5;"
      - logger.log:
          format: "position: %.2f"
          args: ['id(my_pos)']

I also have a rotary encoder which I use to control the servo locally. This updates the global variable and calls the script on change (this works, and the position is updated in HA):

sensor:
  - platform: rotary_encoder
    name: "Rotary Encoder"
    id: knob
    pin_a: D8
    pin_b: D6
    min_value: -1
    max_value: 1
    on_value:
      then:
          - lambda: !lambda |-
                if (id(my_pos) > -0.9 && id(my_pos) < 0.9) {
                id(my_pos) += id(knob).state * 0.1;
                }
                else if (id(my_pos) <= -0.9 && id(knob).state == -1 && id(my_pos) > -1) {
                id(my_pos) = -1;
                }
                else if (id(my_pos) >= 0.9 && id(knob).state == 1 && id(my_pos) < 1) {
                id(my_pos) = 1;
                }
                else if (id(my_pos) == 1 && id(knob).state == -1) {
                id(my_pos) += id(knob).state * 0.1;
                }
                else if (id(my_pos) == -1 && id(knob).state == 1) {
                id(my_pos) += id(knob).state * 0.1;
                }            
          - script.execute: move_servo
          - sensor.rotary_encoder.set_value:
              id: knob
              value: 0

And in template cover component I have:

cover:
  - platform: template
    name: "Blinds"
    id: pub_pos

    open_action:
      - lambda: |-
          id(my_pos) = 1;
      - script.execute: move_servo

    close_action:    
      - lambda: |-
          id(my_pos) = -1;
      - script.execute: move_servo

    stop_action:
      - servo.detach: my_servo

    position_action:
      - logger.log:
          format: "position_action_position: %.2f"
          args: ['id(pub_pos).position']      
      - lambda: |-
          id(my_pos) = 2 * (id(pub_pos).position) - 1.0;
      - script.execute: move_servo

This where it fails. *"id(my_pos) = 2 (id(pub_pos).position) - 1.0;"**

id(pub_pos) isn't the new value from HA but the previous position and the servo doesn't budge. Is there any way to get that new position? The cover log reports it nicely (cover.cpp: line 80)

Additional context

glmnet commented 4 years ago

I guess you don’t need the lambda in position action

glmnet commented 4 years ago

Also sensor lambda should call cover set position (remove the call to servo)

slarti42 commented 4 years ago

I use that to map the value 0.0... 1.0 to - 1.0...1.0 needed by the servo

slarti42 commented 4 years ago

I'll try to map it elsewhere. Thanks for the suggestion!

glmnet commented 4 years ago

you have to use cover.control when controlling the cover from the rotary encoder, do not write the servo on the rotary encoder sensor action, use the cover control call instead which in turn should call the servo.

Im confused by the lambda now

slarti42 commented 4 years ago

The rotary encoder part works. A little convoluted but it basically increments the global variable my_pos by - 1 0.1 or 1 0.1 and then calls the script and resets the counter back to 0 so the next increment is easy. The lambda is there to make sure my_pos is never outside - 1...1. Everything is nicely in sync internally with my_pos because that is the only thing that gets updated and the servo is moved with the script using that variable.

glmnet commented 4 years ago

I see, this is not documented... hmmm try x instead of id(pub_pos).position

glmnet commented 4 years ago

sorry, variable name seems to be pos

slarti42 commented 4 years ago

It compiles but the result is the same.

Log:

[22:32:39][D][cover:072]: 'Säleverhot' - Setting
[22:32:39][D][cover:080]:   Position: 64%
[22:32:39][D][main:656]: position_action_position: 1.00
[22:32:39][D][cover:152]: 'Säleverhot' - Publishing:
[22:32:39][D][cover:155]:   Position: 100%
[22:32:39][D][cover:168]:   Current Operation: IDLE
[22:32:40][D][cover:152]: 'Säleverhot' - Publishing:
[22:32:40][D][cover:155]:   Position: 100%
[22:32:40][D][cover:168]:   Current Operation: IDLE
[22:32:40][D][main:637]: positio: 1.00
glmnet commented 4 years ago

did you modify the log statement?

    position_action:
      - lambda: |-
          ESP_LOGD("main", "position_action_position: %.2f", pos);
          id(my_pos) = 2 * pos - 1.0;
glmnet commented 4 years ago

ahh yeah pos should work in logger.log too

slarti42 commented 4 years ago

I changed it and it seems to be the right variable but for some reason it's not updating my_pos

[22:40:10][D][cover:072]: 'Säleverhot' - Setting
[22:40:10][D][cover:080]:   Position: 46%
[22:40:10][D][main:648]: position_action_position: 0.46
[22:40:10][D][cover:152]: 'Säleverhot' - Publishing:
[22:40:10][D][cover:155]:   Position: 100%
[22:40:10][D][cover:168]:   Current Operation: IDLE
[22:40:11][D][cover:152]: 'Säleverhot' - Publishing:
[22:40:11][D][cover:155]:   Position: 100%
[22:40:11][D][cover:168]:   Current Operation: IDLE
[22:40:11][D][main:629]: positio: 1.00
slarti42 commented 4 years ago

Noo, stupid typo, it works! Thank you! Been banging head against a wall for hours...

I realise I'm on dev branch but maybe this should be in the docs?

sentiment-bot[bot] commented 4 years ago

Please be sure to review the code of conduct and be respectful of other users. Keep in mind, this repository uses the Contributor Covenant.

glmnet commented 4 years ago

Yeah, sorry for trying to confuse you, fortunately I failed.

The docs are here https://deploy-preview-419--esphome.netlify.com/components/cover/template.html but not merged yet as we are going to add a config validation first, but sure the variable names are missing there too (also for tilt if you use tilt_action

Question, did you just imagine there will be a position_action option?

glmnet commented 4 years ago

Should be fixed by https://github.com/esphome/esphome-docs/pull/419

slarti42 commented 4 years ago

No, I did read it somewhere here in github and confirmed by reading the source but stupidly couldn't read the variable name pos which is now quite evident. Thanks again!

glmnet commented 4 years ago

I realise I'm on dev branch but maybe this should be in the docs?

Ahh yes this is only available in dev so you'll not find it in the 1.4 docs, the dev docs are here http://next.esphome.io however as I said before the docs for this specific feature got out of sync, let's keep this issue open to track it.

magiva commented 4 years ago

i too was struggling with this and stumbled by complete accident to this post, glad i did. when does this move from dev to prod ?

glmnet commented 4 years ago

i too was struggling with this and stumbled by complete accident to this post, glad i did. when does this move from dev to prod ?

https://github.com/esphome/feature-requests/issues/658