Setting Hue defaults – part I

Until recently I hadn’t been making best use of my Hue lights. I had basic automations to turn them on for added security when I’m out and I had a few of them linked up to motion sensors, but that was all.  I hadn’t been making good use of their colour features.

I finally decided to rectify that by creating some automations that would change the colour temperature of the lights according to the time of day. In the evening I want a warmer light, especially in certain rooms.

I experimented with the flux component that gradually changes the temperature of a light between certain values. I decided against it because I wanted a bit more control over brightness and the exact colour temperature – or even the colour itself. I have some colour lights, and an orange tone can work even better than a warm white.

I decided that what I wanted was to have a set of default light profiles for different times of day, such as daytime and evening, and set the lights accordingly when I turn a light on. If a light was already on it should change to match the profile as the time changes.

I also wanted some rules around when to change the colour. The automation has to respect user overrides. I didn’t want the light to automatically dim or change colour if I’d turned it up for a specific reason. A warm and quite dim light is great in the evenings if I’m just watching TV but not so great if I’m trying to read, so the light should only change if it’s set to the previous default – so, for example, if I had a daytime and evening profile it should only change to the evening profile if it was currently set to the daytime profile:

Once again I chose to use AppDaemon for the automation. To start off, I set up a configuration file for the automation in defaultlights.yaml:

defaultlights: 
  module: defaultlights 
  class: DefaultLights 
  default_transition: 3 
  debug: False 
  lights: 
    light.study: 
      - name: "Evening" 
        start_time: "sunset" 
        end_time: "sunrise" 
        settings: 
          color_temp: 400 
          brightness: 153 
      - name: "Morning" 
        start_time: "sunrise" 
        end_time: "12:00:00" 
        settings: 
          color_temp: 300 
          brightness: 178 
      - name: "Afternoon" 
        start_time: "12:00:00" 
        end_time: "sunset" 
        settings: 
          color_temp: 340 
          brightness: 178

This defines a dictionary of lights with a list of time based profiles for each light. In this case I have one light – my study – and three time profiles – morning,  afternoon and evening. The settings aren’t limited to color_temp and brightness – for my lounge light I use rgb colours:

    light.lounge:
      - name: "Overnight"
        start_time: "22:00:00"
        end_time: "sunrise"
        settings:
            rgb_color: [255, 164, 76]
            brightness: 102

Any attribute can be used as a setting as long as it can be read back from the attributes of the light, so that it’s possible to compare the settings in a profile with the current state of the light. It’s worth checking the attributes of the light when it’s switched on to confirm the available attributes:

The order of the profiles is important. It doesn’t matter which is first, but they must follow the clock, with the first following on from the last. The valid orders are “morning, afternoon, evening”, “afternoon, evening, morning”, “evening, morning, afternoon” but not “morning, evening, afternoon”.

The next step was to set up the automation in defaultlights.py.

First of all, I initialised the app:

import appdaemon.plugins.hass.hassapi as hass

class DefaultLights(hass.Hass):
    def initialize(self):
        self.transition = self.args.get('default_transition', 5)

        for light in self.args['lights']:
            self.listen_state(self.trap_light_on, light, attribute='all')

        self.listen_event(self.light_on, 'LIGHT_ON')

This just creates the callback that is triggered when a light changes state. I also added an event listener called “LIGHT_ON” to call a function that sets the lights to their current profile. That event is triggered by a standard Home Assistant automation that runs on an interval.

The first callback function calls the “set_light”  method when the light state changes from “off” to “on” :

    def trap_light_on(self, light, attribute, old, new, kwargs):
        """ Run when light on event received """

        if old['state'] == 'off' and new['state'] == 'on':
            self.set_light(light, True)

The second callback runs when the event is fired and calls the same “set_light” method for either a single light or all lights defined in the configuration file. If the force_change parameter is supplied, the light will be set to the current profile regardless of its state:

    def light_on(self, event_name, data, kwargs):
        """ Set lights currently switched on to the default.
            Supply entity ID to set a single light """

        light = data.get('entity_id', 'all')
        force_change = data.get('force_change', False)

        if light == 'all':
            for light in self.args['lights']:   
                if self.get_state(light) == 'on':
                    self.set_light(light, force_change)
        else:
            if self.get_state(light) == 'off':
                force_change = True
            self.set_light(light, force_change)

The set_light method does the actual work of setting the light. It identifies the current profile for the light and changes the light if either the force_change parameter is set or the light settings match the previous previous profile:

    def set_light(self, light, force_change=False): 
        """ Set a single light to the current profile 
            If force_change is true, set regardless of current state. """ 

        for index, profile in enumerate(self.args['lights'][light]): 
            if self.now_is_between(profile['start_time'], profile['end_time']): 
                if not force_change: #and self.get_state(light, attribute='brightness', default=1) < 5: 
                    set_light = self.on_prev(light, index) 
                break 

        if force_change: 
            light_args = profile['settings'].copy() 
            light_args['transition'] = profile.get('transition', self.transition)                 
            self.log('Setting {} to profile: {}'.format(light, profile['name'])) 
            self.turn_on(light, **light_args)

Finally the on_prev method is a helper function to check whether the light settings match the previous profile:

    def on_prev(self, light, index):
        """ Checks to see if light is on previous profile """

        profile_count = len(self.args['lights'][light])

        if index == profile_count:
            prev = 0
        elif index == 0:
            prev = profile_count - 1
        else:
            prev = index - 1

        current_state = self.get_state(light, attribute = 'all')['attributes']

        for attribute, value in self.args['lights'][light][prev]['settings'].items():
            if current_state.get(attribute) != value:
                return False

        return True   

I could have implemented the interval check within AppDaemon, but I chose to do it as a standard automation so that I could easily enable it, disable it, and set the interval within the Home Assistant UI. The automation simply uses a time based trigger and an action that fires the event:

It works quite nicely but there are a couple of issues that I’m still working on and I’ll come back to those in part two.

in Home Automation

Add a Comment

Your email address will not be published. All comments will be reviewed.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Related Posts