Python_script to save and restore switches and lights


#1

I needed a way to save the current state of some switches and lights so I could change them temporarily, and then restore them back to the way they were. Somewhat surprisingly, HA doesn’t seem to have a native feature for this. (If I’m wrong, by all means, enlighten me! :slight_smile:) So I searched the forum and found that the common technique was to create various input_boolean, etc. entities and then write various automations and scripts to get the job done. That seemed a bit cumbersome at best.

So, since the State Machine is basically a big, global variable, and can pretty much save whatever, I wrote a python_script that can save, and later restore, the states of switches and lights.

You need to add the following to your configuration.yaml file if you don’t have it already:

python_script:

See https://www.home-assistant.io/components/python_script/ for more details.

The script accepts the following service data (i.e., parameters):

store_name - (Optional) The “domain” name to use for the entities created in the State Machine to hold the saved switch/light states and attributes. The default is light_store.

entity_id - (Optional) The entity_id(s) of the switch/light entities to save/restore. (Currently only entities of the switch, light or group components are supported. Groups will be expanded recursively into the individual entities they contain.) Can be a single entity_id, a list of entity_id’s, or a string containing a comma separated list of entity_id’s. When saving, the default is to save all existing switch and light entities. When restoring, the default is to restore all previously saved entities.

operation - (Optional) If supplied, must be save or restore. The default is save.

When saving, the script will create an entity in the State Machine named <store_name>.<component>_<entity> for each saved entity. The state will be the state of the entity, and if the entity is a light, and if the light has brightness and/or hs_color attributes, those will be saved as well. (Note that xy_color and rgb_color are translated to hs_color inside the light component, so only hs_color needs to be saved/restored.)

So, e.g., if store_name is light_store, and switch.abc and light.def are saved, and switch.abc is currently off, and light.def is currently on with a brightness of 128, the following (temporary) entities would be created in the State Machine:

light_store.switch_abc, state: off
light_store.light_def, state: on, attributes: brightness: 128

After restoring the temporary entities will be removed from the State Machine.

Here is an example of how you might use it:

script:
  lights_on:
    alias: Turn on a given selection of lights, saving current state
    sequence:
      - service: python_script.light_store
        data:
          store_name: flash_store
          entity_id:
            - switch.kitchen_light
            - light.family_room_lamp
            - group.upstairs_lights
      - service: homeassistant.turn_on
      ...

  restore_lights:
    alias: Restore saved lights to the way they were
    sequence:
      - service: python_script.light_store
        data:
          store_name: flash_store
          operation: restore

One caveat: If you have any light groups (i.e., an entity of the light component that is actually a group of other lights), then you should not use the default for entity_id (when saving.) Rather, you should specify the list of individual light entity_id’s. Unfortunately, a python_script cannot tell the difference between a light entity that is an actual light and a light entity that is a group of other lights. If both are saved & later restored, the result will not be what you want.

I would appreciate any comments, criticisms, suggestions or feedback of any kind.

You can obtain the code here:


Place it here: <config>/python_scripts/light_store.py, where <config> is your Home Assistant configuration directory.


Light Snapshot (similar to media_player.sonos_snapshot)
Garage Door Hue Notification Light
Save and restore state of lights
Is it possible to change an attribute via a script/service?
Temporary RGB Lights
Brighten front porch lights, then return to recorded dim level
Smart bulbs power outages
Yeelight rgb bulb flash notify
#2

Fantastic, I was about to hack my way through an input boolean/mqtt scheme. Looking forward to trying this.

Could I create multiples of this script by changing the file name and associated automation calls? It would be nice to separate some of my groups of lights.


#3

I hope it works for you. I’ve been using it for a little while and a few others have, too. But if you have any feedback, I’m all ears.

You shouldn’t have to make copies of the script. Just use different store_name's. E.g.,

script1:
  sequence:
    - service: python_script.light_store
      data:
        store_name: light_store_1
        entity_id: ...

script2:
  sequence:
    - service: python_script.light_store
      data:
        store_name: light_store_2
        entity_id: ...

#4

Perfect. I’ll try to give it a call tonight. I’ll be sure to give feedback either way.


#5

Added support for groups (i.e., from the group component, not from the light component.)


#6

Moved code to github.


#7

Thanks for the script.

I tested this out… was scratching my head as to why HA kept complaining that it couldn’t find the script, then I realised I had to add python_script: to my configuration.yaml :relieved:

I’ve setup the following:

  1. Backup specific light.
  2. Change color and brightness (green)
  3. Change color and brightness (red)
  4. Restore light from step 1.

This works fine when the light is already on.

If the light is off and the script is ran, the light turns on, green then red then powers off. But the next time the light is turned it still seems to be red.

Have I configure something wrong?


#8

The problem probably is due to the fact the light entity has no color (our brightness) attribute (in HA) when it is off, so there’s no way for the script to save any color or brightness values. Check out the light entity on the states page when it’s off and you’ll see. So the light is probably just turning on the next time to whatever color it was the last time it was on. You may want to add a step to change the color and/or brightness before the last step of restoring.


#9

Awesome, thanks for confirming. I wanted to make sure it wasn’t a misconfiguration on my end. I did check the states and thought this was the case.

I was going to add a check if the light is off, to turn it on first, then store the on state, change color, restore then switch it off.

Thanks a lot and cheers for the script.


#10

I did further testing on this and found the following:
I started with a LIFX preset (2750K Incandescent) using the LIFX app and this was the states attributes in hass.

brightness: 77
color_temp: 363
hs_color: 0,0
rgb_color: 255,255,255
xy_color: 0.323,0.329

SAVE: I then used the script to save the state of the LIFX and then alter the color. At this stage I checked the states/attributes.
entity: light_store_light_living_room.light_living_room
state: on
brightness: 77
hs_color: 0,0

RESTORE: I then restored the state and checked the states again and got the following:
brightness: 77
color_temp: 285
hs_color: 0,0
rgb_color: 255,255,255
xy_color: 0.323,0.329

For some reason when it’s restored the color_temp is different from the save and restore state.

This also happens when not using the LIFX app and just hass. I’ll set the color_temp in hass, save, restore and the color_temp value changes.


#11

I browsed the light component and lifx platform code a bit and I’ll admit I don’t fully understand how they use all these different representations of color.

It looks like when using the light.turn_on service that you can only use one of profile, color_name, hs_color, xy_color, rgb_color, color_temp or kelvin. Then the code converts as necessary. E.g., in the light component code, profile, color_name, xy_color and rgb_color all seem to ultimately get converted to hs_color. Also, kelvin gets converted to color_temp. So ultimately, only hs_color or color_temp is used (again, after possibly being converted from another input value), but not both. Then in the lifx code it further converts either hs_color or color_temp (depending on which was provided) to hue/saturation/kelvin, and that is apparently what it uses to set the color of the bulb.

Then when setting the entity’s attributes, the lifx platform sets hs_color and color_temp (based on the color it set), and the light component adds xy_color and rgb_color (converted from hs_color.)

Now I’m no color expert, but from the little I researched, I don’t see how a color_temp of 363 is equivalent to an hs_color of 0,0.

In any case, I think the bottom line is you’re setting the color via color_temp (or possibly via kelvin that gets converted to color_temp), and the lifx platform is representing that with an hs_color that is not equivalent. Then my script saves and restores (the invalid) hs_color, resulting in an incorrect color_temp. Of course, I could still be misreading things. If someone knows more about this, maybe they can help.

But, at least for now, assuming I’m correct that you’re using color_temp or kelvin when turning the light on, then I would suggest making a minor change to my script. I.e., change the following:

# Select light attributes to save/restore.
ATTR_BRIGHTNESS = "brightness"
ATTR_HS_COLOR = "hs_color"
LIGHT_ATTRS = [ATTR_BRIGHTNESS, ATTR_HS_COLOR]

to:

# Select light attributes to save/restore.
ATTR_BRIGHTNESS = "brightness"
ATTR_COLOR_TEMP = "color_temp"
LIGHT_ATTRS = [ATTR_BRIGHTNESS, ATTR_COLOR_TEMP]

If that works, and someone can fill in a bit more about the relationship between hs_color and color_temp, then there might be a more elegant way for my script to handle this situation.


#12

Thanks for looking into this. I can confirm that in my script/automation I do in fact use color_name to set green and red and yellow. I’ll try and use hass to set the color using the hs_color, but from what I found hs_color = 0,0 only represents white and does not contain the color_temp or kelvin.

I did have a quick look at your script and attempted to guess the code and updated this:
ATTR_BRIGHTNESS = "brightness"
ATTR_HS_COLOR = "hs_color"
ATTR_HS_COLOR_TEMP = "hs_color"
LIGHT_ATTRS = [ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_HS_COLOR_TEMP]

It saves the values, but I get an error when it attempts to restore.


#13

As I mentioned, the light component will convert color_name to hs_color itself, so you shouldn’t need to try using hs_color.

I agree that hs_color == (0,0) would represent white (or some shade of grey depending on brightness.) However, I disagree that hs_color does not “contain” (or can’t be converted to) color temperature. In fact, homeassistant.util.color contains several color conversion routines, and using them, a color_temp of 363 (the color_temp you got when you first turned on your bulb) equates to 2754 kelvin (which is about what your preset of 2750K was), which equates to an hs_color of (28.281, 64.025). Here is how I achieved those numbers:

>>> import homeassistant.util.color as uc
>>> uc.color_temperature_mired_to_kelvin(363)
2754
>>> uc.color_temperature_to_hs(2754)
(28.281, 64.025)

But, from what you showed above, your LIFX bulb represented its hs_color instead as (0,0). This is the problem: it’s setting its color_temp attribute correctly, but not its hs_color attribute.

This is why I said to change my script to save & restore color_temp instead of hs_color. If you did exactly that, I’m pretty sure it would work for you, at least with this bulb.


#14

LIFX describes colors with [hue, saturation, brightness, kelvin].

The saturation is the balance between hue and kelvin. To get a white light, we set saturation to 0 (full white) and then the hue does not matter.

Setting a non-zero saturation (i.e. a colored light) always returns kelvin to 3500 so the desaturation happens with a neutral white.

This should explain what you see but let me know if it is still not clear :slight_smile:


#15

I’m not the one with the LIFX bulb, so I’m not seeing anything other than what @q00dee is reporting when trying to use a script I wrote.

My script saves a light entity’s hs_color attribute (if the light is currently on, and its entity currently has that attribute), and when restoring, if the light was on and had the hs_color attribute, uses the saved hs_color value.

However, the hs_color value his LIFX entity is reporting (0,0) doesn’t seem to be valid for the color_temp (363), so when restoring the color gets set incorrectly. It seems to me that if the light’s color_temp is 363, then the light entity’s hs_color attribute should be reported as (28.281, 64.025), not (0,0). Everything I’ve read while researching this situation seems to indicate (0,0) is wrong for a color_temp of 363 (mireds, or 2754K.) But again, I’m not an expert.

Regardless, it would seem if the script is changed to save/restore color_temp instead of hs_color, it might work in this case.


#16

Thanks @pnbruckner ! I have altered the script from hs_color to color_temp and that seems to work. :+1:

I have also observed in my testing that initially there is definitely a hs_color value that corresponds to the correct value, but somehow is ends up being 0,0 and eventually the hs_color reverts to the correct value.


#17

Glad to hear it. I’ll think about a way to make the script more generic, like seeing if hs_color, color_temp or both are present. If one of the other, then it’s pretty clear to save (& restore) that attribute. But if both are present, and worse, like in this case they disagree, then I’m not sure what would be best to do. Maybe chose color_temp because we know at least LIFX seems to have a bug in its reporting of hs_color???

Yeah, that definitely sounds like a bug. You might want to consider looking into that, or at least somehow informing the author. Maybe it’s just a transient issue. I.e., when you change the light, hs_color is only temporarily wrong, so if you delay a bit before saving???


#18

Just ignore hs_color (hue/saturation) and use color_temp if the current saturation is zero.

I guess you can argue that light platforms should return just one or the other so I made a PR for LIFX to do that: https://github.com/home-assistant/home-assistant/pull/15234

However, this issue will affect other light types as well.


#19

Probably a dumb question - I was looking at this to cover off when I’m working on HA and my wife is home; when I restart HA the lights stay on until it restarts and then they all turn off (usually, I seem to pick the most inopportune time to do this) – Could this be used to save the light states when they’re turned on and then on HA start trigger a restore? The idea being that when I restart HA, the lights may blink out momentarily but are restored to last known state?

PS… I just posted this as I recently just had an inopportune moment while my wife was cooking :stuck_out_tongue:


#20

I don’t think so. I don’t think HA restores states in the state machine, which is what this uses. I think HA only restores certain entities (like input_xxx’s, automations, scripts, and maybe a few more) to their previous state during startup (which, of course, causes state changes from none to something that repopulates those states into the state machine.)

What type of lights do you have? I use Z-Wave exclusively interacting directly with HA via a Z-Wave controller stick. They don’t change due to a HA restart. (HA just updates their current states into the state machine.) Maybe it’s something about how you have your light entities configured that needs adjusting.