My first Python script, syntax check needed

Hi,

I have written a short Python script but I am not sure about the correctness of that all. I have never ever written a Python script before and also the HA API is new to me. So I’d highle appreciate, if someone could have a look at my script and tell me, where it needs corrections.

Basically, the script will be called by an automation. This automation is triggered by either an open- or a close-event of any of my windows. The automation should give two parameters to the script: the partial name of the entity that triggered the automation (name of the window) as well as the trigger state (on/off).

The script should now control the heating and the covers of the corresponding room/window:

  • an open-event should lower the heating and open the cover in case it is lower than a threshold
  • a close-event should set the heating to auto and close the cover again to its previous position.

I have already tested this with “normal” automations and only one window. So this generally works.
I just need some assistance in merging all this into one script.

Now here it is:

The automation:

- id: '303427509327'
  alias: "Call window automation script"
  trigger:
    - platform: state
      entity_id: binary_sensor.dinning_room
    - platform: state
      entity_id: binary_sensor.living_room
    - platform: state
      entity_id: binary_sensor.kitchen
    (... six more triggers here... )
  action:
    - service: python_script.window_automation
      data:
        entity_id: '{{ trigger.entity_id }}'
        sensor_state: '{{ trigger.to_state.state }}'

My window_automation.py contains:

sensor_state = data.get('sensor_state')
entity_id = (data.get('entity_id')).split('.')[1]
current_position = hass.states.get(('cover.' + entity_id).attributes.current_position)
saved_position = (hass.states.get(('input_number.' + entity_id).state) | int)
fresh_air_position = (hass.states.get('input_number.fresh_air').state) | int)

# Window got opened, do the following
# - set heating to "lower"
# - save current cover position
# - open cover until "fresh air"-position is reached
if sensor_state == 'on':
	hass.services.call('climate', 'set_operation_mode', { 'entity_id': ('climate.' + entity_id), 'operation_mode': 'lowering'})
	if current_position < fresh_air_position:
		hass.services.call('input_number', 'set_value', { 'entity_id': ('input_number.' + entity_id), 'value': (current_position | float) })
		hass.services.call('cover', 'set_cover_position', {'entity_id': ('cover.' + entity_id), 'position': fresh_air_position })

# Window got closed, do the following
# - set heating to "auto"
# - move cover back to previous position
elif sensor_state == 'off':
	hass.services.call('climate', 'set_operation_mode', { 'entity_id': ('climate.' + entity_id), 'operation_mode': 'auto'})
	if current_position > saved_position:
		hass.services.call('cover', 'set_cover_position', {'entity_id': ('cover.' + entity_id), 'position': saved_position })

Is that something to work with?!

Thanks and greetings!

It looks fine, why do you think it needs ‘corrections’.?

Hey, thanks for the reply!

To be honest: this script was just my first shot. I don’t know anything about Python. That’s why the chance of errors is actually high :slight_smile: .

In addition, it just does not work. The automation got triggered, but then nothing happens. And I don’t know how to trace down the problem, because there are no messages, logs or something like that.

How can I investigate that?

Thanks and greetings

Here is a rough pass at what I propose, using python dictionaries.

I have literally no idea how well a service call like hass.services.call('climate', 'set_operation_mode', { 'entity_id': sensor_to_room["entity_id"] }, 'operation_mode': 'lowering'}) will be handled, or if my parenthesis are even remotely right. But, if this approach does work, I think it will be less subject to issues current and future with entity names changing and tricky string parsing, because you can just update the dictionaries.

#define dictionaries. Now you can name sensors, heaters, and covers however you want and they don't have to match perfectly
sensor_to_room = {"binary_sensor.living_room" : "climate.living_room", "binary_sensor.bedroom" : "climate.bedroom", "binary_sensor.office" : "climate.office"}
sensor_to_input = {"binary_sensor.living_room" : "input_number.living_room", "binary_sensor.bedroom" : "input_number.bedroom", "binary_sensor.office" : "input_number.office"}
sensor_to_cover = {"binary_sensor.living_room" : "cover.living_room", "binary_sensor.bedroom" : "cover.bedroom", "binary_sensor.office" : "cover.office"}

sensor_state = data.get('sensor_state')
entity_id = data.get('entity_id')
current_position = hass.states.get((sensor_to_cover["entity_id"]).attributes.current_position)
saved_position = (hass.states.get((sensor_to_input["entity_id"]).state) | int)
fresh_air_position = (hass.states.get('input_number.fresh_air').state) | int)

# Window got opened, do the following
# - set heating to "lower"
# - save current cover position
# - open cover until "fresh air"-position is reached
if sensor_state == 'on':
	hass.services.call('climate', 'set_operation_mode', { 'entity_id': sensor_to_room["entity_id"] }, 'operation_mode': 'lowering'})
	if current_position < fresh_air_position:
		hass.services.call('input_number', 'set_value', { 'entity_id': sensor_to_input["entity_id"] }, 'value': (current_position | float) })
		hass.services.call('cover', 'set_cover_position', { 'entity_id': sensor_to_cover["entity_id"] }, 'position': fresh_air_position })

# Window got closed, do the following
# - set heating to "auto"
# - move cover back to previous position
elif sensor_state == 'off':
	hass.services.call('climate', 'set_operation_mode', { 'entity_id': sensor_to_room["entity_id"] }, 'operation_mode': 'auto'})
	if current_position > saved_position:
		hass.services.call('cover', 'set_cover_position', { 'entity_id': sensor_to_cover["entity_id"] }, 'position': saved_position })
1 Like

You can write to the HA logs from within the script.
Cheers

1 Like

@Dolores
Alright, I think I’ve got your point. This could be a step to improve the overall-system in the future. At the moment, I just want to get it running SOMEhow. I am relatively new to HA, never had any experiences with Python and I am not in the 20’s anymore. So my mental capacity has a limited bandwidth :wink: . Give me so time…

@robmarkcole
Thanks, dude! There it is again: the steep HA learning curve (I don’t use the abbreviation “HASS”, because it means “HATE” in German).
I think you are referring to this logger.info("Hello {}".format(name)), right? If I get it right, this would require to adjust the loglevel accordingly, right? And do I have to look into the HA logfile or into the logs on the UI (the UI appears to only log “errors”)?

I usually use the warning level. You might find helpful the following. Cheers

1 Like

@robmarkcole
Hey man, thanks a ton for your help and the hint to just use the warning level. With this (and a little bit of additional reading), I was able to trace down all (known) errors :slight_smile: :+1:.

Basically, there were just a few type conversion errors and an error in the syntax to get an entity’s attribute.

Now, this is the script:

sensor_state = data.get('sensor_state')
entity_id = data.get('entity_id')
entity_id = entity_id.split('.')[1]
current_position = hass.states.get('cover.' + entity_id).attributes['current_position']
saved_position = hass.states.get('input_number.' + entity_id).state
fresh_air_position = hass.states.get('input_number.fresh_air').state

# for debugging only:
#logger.warning("trigger entity ID was {}".format(entity_id))
#logger.warning("sensor state was {}".format(sensor_state))
#logger.warning("current position was {}".format(current_position))
#logger.warning("saved position was {}".format(saved_position))
#logger.warning("fresh air position was {}".format(fresh_air_position))

# Window got opened, do the following
# - set heating to "lower"
# - save current cover position
# - open cover until "fresh air"-position is reached
if sensor_state == 'on':
	#logger.warning("on state detected")
	hass.services.call('climate', 'set_operation_mode', { 'entity_id': ('climate.' + entity_id), 'operation_mode': 'lowering'})
	if float(current_position) < float(fresh_air_position):
		#logger.warning("current position < fresh air position")
		hass.services.call('input_number', 'set_value', { 'entity_id': ('input_number.' + entity_id), 'value': float(current_position) })
		hass.services.call('cover', 'set_cover_position', {'entity_id': ('cover.' + entity_id), 'position': int(float(fresh_air_position)) })

# Window got closed, do the following
# - set heating to "auto"
# - move cover back to previous position
elif sensor_state == 'off':
	#logger.warning("off state detected")
	hass.services.call('climate', 'set_operation_mode', { 'entity_id': ('climate.' + entity_id), 'operation_mode': 'auto'})
	if float(current_position) > float(saved_position):
		#logger.warning("current position > saved position")
		hass.services.call('cover', 'set_cover_position', {'entity_id': ('cover.' + entity_id), 'position': int(float(saved_position)) })

With this little piece of code, I only need one single automation, that will be triggered if any of my windows is opened OR closed. The rest is handled by the script.

Thanks again! :beers:

1 Like