DIY circadian rhythm based lighting

I’m basically looking to recreate BrainLit’s circadian rhythm matching lighting, which they call BioCentric Lighting. I’ve been to their office here in Lund and essentially it adjusts color temperature and brightness to match the sun as if every day was April 16th in southern Sweden.


I would like to be able to use wall switches to turn on sets of lights in a room in different modes:

  1. Following the sun – as above – as long as they are left on.
  2. Fully bright and white
  3. Some scenes for specific cases.

I’m new to Homey, so I don’t know the best approach to develop this without reinventing the wheel. I’ve come as far as to create a HomeyScript that adjusts a color temperate and brightness variables in Better Logic, and have a Flow setting these properties on certain lights when they are turned on.
Left to do:

  1. Continuous updating of turned on lights in ‘circadian’ mode.
  2. Grouping of lights in each room for different modes.
  3. Handling of different modes in which updating is inactive.
  4. Handle bulbs, LED strips with different capabilities.

Is this the best way with HomeyScripts and Flows? Or should I instead build a custom app?

Does this help with your idea?

Thanks! I’ve gotten this far as to calculate both color temperature and brightness to a certain degree for a single light.
It’s the way forward, scaling it to every room of the apartment that I’m uncertain about. :slight_smile:

Can you share how you calculated the light temperature?

It’s much simplified and because I never want the lights to be completely dark, there is also a minimum, but it does serve its purpose well. :slight_smile:

:point_right: gist

3 Likes

Hi. How is this going? Can you share the calulations?

Yes, follow the finger :point_right: in my previous post. :slight_smile:

Hi,
Just discovered your script and immediately packed in homeyscript!
But I still have some questions:
which flows have you made? Do you let homey do the
script every second?
Which flow did you create so that the temperature etc. are retrieved? Are they stored in a logic?
Definitely want to have this :slight_smile:
Maybe you could share your flows on this?
I would be mega grateful to you :wink:

Updating the brightness and color temperature values every minute seemed sufficient to make it transition smoothly throughout the day. They are stored in the Better Logic app.
Here’s the update flow, which also creates dimmed variants for certain lights.

Then I use the values with Hue lights. This flow is called repeatedly as long as the light is turned on.

Let me know if there’s anything else you’re wondering.

thank you for the answer, since yesterday I already did it though :slight_smile:

However, I noticed something and that is when I test the script, the shows me a different system time.
Now, for example, the script says:
At 15:55:05
Day progress: 66%
CircadianRhythm__temperature: 0.04
CircadianRhythm__brightness: 1

But for me it is 17:55 o’clock, where can I define this in the script? or add that it adds 2 hours?

and what is the second flow for? is it just to show the light a bit more dimmed (75% and 50%)?
And last question, the “dimming” from the second flow you do for both values right?

  1. Oh, yes. Completely forgot about the timezone issue in scripts since Homey v5. I’ve updated my gist with the hack to fix it. Now, this script should use the correct local time.

  2. In the first Flow I create different dimmed versions of the current brightness because sometimes it is too bright, while I still want it to change during the day.

  3. In the second Flow I repeatedly set temperature to the CircadianRhythm_temperature and dim to the CircadianRhythm_brightness with the Dining room lights.

1 Like

Great work @iotnerd.

Because I am not familiar with JSON and HomeyScript, I have one more question.
How can I write the values of “CircadianRhythm__temperature” and “CircadianRhythm__brightness” of the script in a variable?
So I don’t understand your first flow because there are question marks in the flows:

@fantross
des ist ganz einfach, leg in der better logic App eine Variable mit Nummer an.
Wichtig ist das du die auch genauso benennst wie im Script also:

CircadianRhythm__brightness
CircadianRhythm__temperature

und dann starte mal das Script, dann trägt der automatisch die Werte ein.

1 Like

@iotnerd
I have one last question, who I want to do exactly the opposite with the calculation. So let’s say on the weekend the light is too yellow for me and I want to achieve that it is 25% stronger than currently. Iwas I would then have to apply a formula? I come with these formulas topics not quite so to cope.

btw thanks for the customization of the script, the time is now displayed correctly :slight_smile:
Especially during the week the light is so really nice

When I wish to have something other than circadian lighting, I just disable flows and set whatever custom values I want. You can have a physical or virtual switch that turns on (1) circadian mode and another that turns on (2) custom mode.

  1. Sets lights to circadian, and enables a flow that repeatedly sets the values again every x minutes.
  2. Sets lights to some other preferred values, and disables the repeated update Flow.

Something like this?

Great script and thanks for sharing it!
In the meantime, I got it working with the help of @clipse_M.

The marked lines in the screenshot are probably intended to limit the dimming level to the upper and lower range, right?

Is this also possible with the temperature?

Because I have absolutely no programming knowledge, I would be happy if someone could change the script.

EDIT

I did it myself. If anyone is interested here is the code:

/**
* Adjust Color temperature and Brightness based on time of day
* as if it were April 16th, a nice long spring day in southern Sweden
*/
const VARIABLE_PREFIX = 'CircadianRhythm_';
const MIN_BRIGHTNESS = 0.35;
const MAX_BRIGHTNESS = 0.75;
const MIN_TEMPERATURE = 0.3;
const MAX_TEMPERATURE = 0.8;

const sys = await Homey.system.getInfo();

// hack because sys date no longer returns local time, but rather UTC :(
const regex = /(\d\d):(\d\d):(\d\d)/;
const timeMatches = sys.dateHuman.match(regex)

const solarNoon = 0.546 // on April 16 is 13:06

// defaults
let temperature = MIN_TEMPERATURE; // Homey color temperature: 0 (coolest, midday) to 1 (warmest, sunset/sunrise)
let brightness = MIN_BRIGHTNESS; // [0-1]

// used standard normal distribution curve to derive equation [https://en.wikipedia.org/wiki/Normal_distribution#Standard_normal_distribution]
// equations adjusted for color temperature and brightness [https://codepen.io/suhajdab/pen/wvzpqdQ?editors=0010]

getTemperaturePoint = x => {
const r = 0.20; // adjust curve width
return Math.pow(Math.E, -0.3 * Math.pow((x - solarNoon) / r, 4));
}
getBrightnessPoint = x => {
const r = 0.28; // adjust curve width
return Math.min(1 * Math.pow(Math.E, -0.3 * Math.pow((x - solarNoon) / r, 8)), 1);
}

// round to 2 decimal places to avoid 'invalid token error' when reading in a flow
const roundDecimals = (num, places = 2) => {
return Math.round(num * Math.pow(10, places)) / Math.pow(10, places);
}

const dayStart = new Date(sys.date).setHours(0, 0);
const dayEnd = new Date(sys.date).setHours(23, 59);

const now = new Date(sys.date).setHours(timeMatches[1]); // adjust for borked timezones in Homey v5
const dayProgress = roundDecimals((now - dayStart) / (dayEnd - dayStart), 4);

console.log('At', new Date(now).toLocaleTimeString([], { hour12: false }));
console.log('Day progress:\t\t', `${Math.round(dayProgress * 100)}%`);

// use Better Logic app to store values and use later in Flows
let BLApp = await Homey.apps.getApp({ id: 'net.i-dev.betterlogic' });

// color temperature transitions from 1 to 0 to 1 during the day
temperature = roundDecimals((MAX_TEMPERATURE - (MAX_TEMPERATURE - MIN_TEMPERATURE) * getTemperaturePoint(dayProgress)));
console.log(`${VARIABLE_PREFIX}_temperature:\t`, temperature);

// brightness transitions from MIN to MAX to MIN during day
brightness = roundDecimals((MIN_BRIGHTNESS + (MAX_BRIGHTNESS - MIN_BRIGHTNESS) * getBrightnessPoint(dayProgress)));
console.log(`${VARIABLE_PREFIX}_brightness:\t`, brightness);

BLApp.apiPut(`${VARIABLE_PREFIX}temperature/${temperature}`);
BLApp.apiPut(`${VARIABLE_PREFIX}brightness/${brightness}`);

return true;
1 Like

I did it much easier… Sun Events app has a color temperature tag which changes during the day the right way.

The only issue was to solve, this value is in Kelvin, however I need to transform it to percentage because Homey can set percentage.

Here you are:

Works flawless, but you need CCT light source or RGB-CCT because RGBW solutions are too red or too blue at 100% and 0%, while CCT gives the best result.

2 Likes

Better Logic card in the first flow is only for tracking the daily kelvin states in Insights.

Question mark in first flow, first “then” card is the mentioned Sun Events Kelvin tag.

That’s a good solution too if you want to just follow the sun. I want to replay the “ideal” day every day. So the lights are vibrant and activate me in the morning at the same time every day, while letting me calm down to get ready to sleep each night regardless of where the real sun is in the sky. Because our work schedule does not adjust to the sun, so neither should my lights. :slight_smile:

1 Like