DIY circadian rhythm based lighting

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

Does anyone know if it is possible to use the build-in logic functions instead of the BetterLogic app.
I use the BetterLogic app only for this use case and would like to delete the app.

Advantage is, they can be easy viewed in the flow editor in the HomeyWeb App, handy when testing flow.

Yes, this is an advantage from BL app, but I don’t need it.

Hi @DirkG

Do you mind sharing what you worked out?
What is the ? in the flows??
“Circadian 75% dimmed” and “Circadian 75% dimmed” are required to be created in Homey Logic as number variable, In take it?

I have created number variables for “CircadianRhythm__brightness” and “CircadianRhythm__brightness” in better logic and executed the scrip. But the values of the variables are stuck on 0 and not updating on executing script…

Thanks

Hi @GaryOz,

sorry, but I can’ t explain everything because I didn’ t program the script and I’ m not a programmer. Also, my process is a bit different from the one described by the author.

Don’t know, sorry. And I don’t know why they calculate to 2 digits after the decimal point, because the script does it automatically.

Yes

Please change the name of the variables as follows (only with one underscore):
CircadianRhythm_brightness
CircadianRhythm_temperature

This is how my solution looks like:

Script
Found in post #16

Numeric variables in better logic app
CircadianRhythm_brightness
CircadianRhythm_temperature

Numeric variables in Homey
(more → logic; you can name the variables as you like)
CircadianDim
CircadianTemp

Flow 1
(to set the BL variables into Homey variables)

Flow 2
(turn on lamp with the current logic values)

Flow 3
(change of temperature when the lamp is on. The same for brightness)

The result
(Insights graph, 24 h)

Info:
You can change these parameters in the script according to your own needs.

const MIN_BRIGHTNESS = 0.35;
const MAX_BRIGHTNESS = 0.75;
const MIN_TEMPERATURE = 0.3;
const MAX_TEMPERATURE = 0.8;

I hope it is a little bit helpful.

2 Likes

Thanks mate!
That helps.

To answer your question
“I don’t know why they calculate to 2 digits after the decimal point, because the script does it automatically.”
They do this, as Homey calculates brightness by decimal and not percentage. (0.50 = 50%, 0.14 =14%)

I’m glad to hear that.

In generally I know why it has to be calculated to 2 digits.
But I don’t know why they calculate in this case, because the output of the script is calculated to 2 digits already.

1 Like

It should be relatively simple to turn this in to an app, just wondering if anyone has done that yet?

2 Likes

The Sun Events app takes “Sunlight Change” into account. With this value it’s possible to adjust the light temperature and brightness depending on the current position of the sun as explained in this post.
So it’s not exact the same behavior of the light as done with the script, because “Sun Events method” takes into account the seasons.

Circadian Rhythm Light as @iotnerd wanted could probably have been realized with the app Splines, but the app is no longer available.

Otherwise, I don’t know of any other app.

With the new beta Homey firmware release v7.4.0.-rc22 logic and notification permission for HomeyScript is added. According to this, the script should now be modifiable so that the Better Logik app is no longer needed, right?
Does anyone have a hint how the script should be changed?

As mentioned before, I’m not a software programmer and I don’t know how to write a HomeyScript, so I would appreciate for any hints.

Info: I haven’t installed the beta firmware myself yet, so I couldn’t try a modified script at the moment.

Hi Dirk,
Yes, I also understood we can finally write to logics variables on the v7.4.0RC version
In theory this should work:
In the example-flowcard-list.js script output, you can search for homey:manager:logic

Example

await Homey.flow.runFlowCardAction({  
  "uri": "homey:manager:logic",
  "id": "variable_set_string",
  "args": [
    {
      "name": "MyStringVariable",
      "type": "autocomplete",
      "title": "Text Variable"
    },
    {
      "name": "value",
      "type": "Hello World",
      "title": "Text"
    }
  ]});

This results in a Rejection error on pre-v7.4.x firmware

  • all THEN cards:
Set [[variable]] to [[value]]

{
  "uri": "homey:manager:logic",
  "id": "variable_set_string",
  "args": [
    {
      "name": "variable",
      "type": "autocomplete",
      "title": "Text Variable"
    },
    {
      "name": "value",
      "type": "text",
      "title": "Text"
    }
  ]
}

-----------

Set [[variable]] to [[value]]

{
  "uri": "homey:manager:logic",
  "id": "variable_set_number",
  "args": [
    {
      "name": "variable",
      "type": "autocomplete",
      "title": "Number Variable"
    },
    {
      "name": "value",
      "type": "number",
      "title": "Number"
    }
  ]
}

-----------

Calculate [[variable]] as [[value]]

{
  "uri": "homey:manager:logic",
  "id": "variable_set_number_math",
  "args": [
    {
      "name": "variable",
      "type": "autocomplete",
      "title": "Number Variable"
    },
    {
      "name": "value",
      "type": "text",
      "placeholder": "Number"
    }
  ]
}

-----------

Set [[variable]] to [[value]]

{
  "uri": "homey:manager:logic",
  "id": "variable_set_boolean",
  "args": [
    {
      "name": "variable",
      "type": "autocomplete",
      "title": "Yes/No Variable"
    },
    {
      "name": "value",
      "type": "checkbox",
      "value": true,
      "title": "Yes/No"
    }
  ]
}

-----------

Flip [[variable]]

{
  "uri": "homey:manager:logic",
  "id": "variable_toggle_boolean",
  "args": [
    {
      "name": "variable",
      "type": "autocomplete",
      "title": "Yes/No Variable"
    }
  ]
}

-----------

Make a HTTP [[method]] request to [[url]] with headers [[headers]] and body [[body]]

{
  "uri": "homey:manager:logic",
  "id": "http",
  "args": [
    {
      "name": "method",
      "type": "dropdown",
      "values": [
        {
          "id": "get",
          "title": "GET"
        },
        {
          "id": "post",
          "title": "POST"
        },
        {
          "id": "put",
          "title": "PUT"
        },
        {
          "id": "delete",
          "title": "DELETE"
        }
      ],
      "title": "Method"
    },
    {
      "name": "url",
      "type": "text",
      "title": "URL",
      "placeholder": "https://www.athom.com"
    },
    {
      "name": "headers",
      "type": "text",
      "required": false,
      "placeholder": "Content-Type: application/json",
      "title": "Headers"
    },
    {
      "name": "body",
      "type": "text",
      "required": false,
      "placeholder": "{ \"your\": \"value\" }",
      "title": "Body"
    }
  ]
}
  • all AND cards:
[[droptoken]] is !{{|not}} exactly [[comparator]]

{
  "uri": "homey:manager:logic",
  "id": "equal",
  "args": [
    {
      "name": "comparator",
      "type": "text",
      "placeholder": "Value"
    }
  ],
  "droptoken": [
    "string",
    "number"
  ]
}

-----------

[[droptoken]] is equal to !{{`yes`|`no`}}

{
  "uri": "homey:manager:logic",
  "id": "equal_boolean",
  "droptoken": [
    "boolean"
  ]
}

-----------

[[droptoken]] !{{contains|doet not contain}} [[comparator]]

{
  "uri": "homey:manager:logic",
  "id": "contains",
  "args": [
    {
      "name": "comparator",
      "type": "text"
    }
  ],
  "droptoken": "string"
}

-----------

[[droptoken]] is !{{|not}} less than [[comparator]]

{
  "uri": "homey:manager:logic",
  "id": "lt",
  "args": [
    {
      "name": "comparator",
      "type": "number"
    }
  ],
  "droptoken": "number"
}

-----------

[[droptoken]] is !{{|not}} greater than [[comparator]]

{
  "uri": "homey:manager:logic",
  "id": "gt",
  "args": [
    {
      "name": "comparator",
      "type": "number"
    }
  ],
  "droptoken": "number"
}

-----------

Continue with a chance of [[chance]]

{
  "uri": "homey:manager:logic",
  "id": "random",
  "args": [
    {
      "name": "chance",
      "type": "number",
      "min": 0,
      "max": 100,
      "placeholder": "50%"
    }
  ]
}

-----------

[[variable]] is !{{|not}} equal to [[value]]

{
  "uri": "homey:manager:logic",
  "id": "var_equal",
  "args": [
    {
      "name": "variable",
      "type": "autocomplete",
      "title": "Variable"
    },
    {
      "name": "value",
      "type": "text",
      "placeholder": "value to compare to"
    }
  ]
}

-----------

[[value1]] [[operator]] [[value2]] is !{{true|false}}

{
  "uri": "homey:manager:logic",
  "id": "equation",
  "args": [
    {
      "name": "value1",
      "type": "text",
      "placeholder": "Value 1"
    },
    {
      "name": "operator",
      "type": "dropdown",
      "values": [
        {
          "id": "eq",
          "title": "is equal to"
        },
        {
          "id": "neq",
          "title": "is not"
        },
        {
          "id": "lt",
          "title": "is less than"
        },
        {
          "id": "gt",
          "title": "is more than"
        },
        {
          "id": "lte",
          "title": "is less than or equal to"
        },
        {
          "id": "gte",
          "title": "is more than or equal to"
        }
      ],
      "title": "Operator"
    },
    {
      "name": "value2",
      "type": "text",
      "placeholder": "Value 2"
    }
  ]
}

-----------

HTTP [[method]] to [[url]] with headers [[headers]] and body [[body]] is !{{|not}} equal to status code [[statusCode]]

{
  "uri": "homey:manager:logic",
  "id": "http_status_code_equals",
  "args": [
    {
      "name": "method",
      "type": "dropdown",
      "values": [
        {
          "id": "get",
          "title": "GET"
        },
        {
          "id": "post",
          "title": "POST"
        },
        {
          "id": "put",
          "title": "PUT"
        },
        {
          "id": "delete",
          "title": "DELETE"
        }
      ],
      "title": "Method"
    },
    {
      "name": "url",
      "type": "text",
      "title": "URL",
      "placeholder": "https://www.athom.com"
    },
    {
      "name": "headers",
      "type": "text",
      "required": false,
      "placeholder": "Content-Type: application/json",
      "title": "Headers"
    },
    {
      "name": "body",
      "type": "text",
      "required": false,
      "placeholder": "{ \"your\": \"value\" }",
      "title": "Body"
    },
    {
      "name": "statusCode",
      "type": "number",
      "min": 100,
      "max": 599,
      "title": "Response status code"
    }
  ]
}
1 Like