DIY circadian rhythm based lighting

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

Yes you can, using it now for the current moon fase:
Won’t be updating the entire script for circadian, but I guess this tip will at least help getting it along:

await Homey.logic.updateVariable({id: 'VARIABLE_ID', variable: { value: VALUE } })

You can retrieve the Variable ID(s) by pre-running a small command in Homey Script:
Homey.logic.getVariables().then(r => log(r))
Value should be the type the variable is, so do make sure the value is a number if the type is of the logic is also a number, I don’t think it will auto change

1 Like

Thx to @Peter_Kawa and @Caseda.

@Peter_Kawa, in my example-flowcard-list.js there is no homey:manager:logic. Maybe because I’m still on v7.3.0. Btw., I don’t see anything nearly similar in the examples to the current script with the Better Logic app, so I’m completely out of my depth with it.

This is the relevant part (I guess) of the actually script:

// 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}`);

@Caseda, this looks much better for me… :wink:
So I guess that I had to create two numeric variables with equal names.
And the I have to change the line

let BLApp = await Homey.apps.getApp({ id: 'net.i-dev.betterlogic' });

into

await Homey.logic.updateVariable({id: 'VARIABLE_ID', variable: { value: VALUE } });

right?
Info: Actually I’m not able to get the right Variable ID because I’m still on v7.3.0 as already mentioned.

But what about the rest of the entries that relate to the BL app?

YW. That’s odd, Dirk. I’m on v7.3.0 as well.
Ahh, I think you didn’t run the script and searched for it in the results.
That’s what I meant with

Ahh, okay, now I understand. But this is too complicated for me to understand. I understand that these are the possible commands, but I don’t know how to use them.

This returns the ID (and the value) when you input your var name :upside_down_face:

// Replace MyVar with your var name
let varName = "MyVar"
const logicVars = await Homey.logic.getVariables();
for (var iLogicVar in logicVars){
  if (logicVars[iLogicVar].name == varName)
log("Variable: " + logicVars[iLogicVar].name + "\nValue:\t  " + logicVars[iLogicVar].value + "\nID:\t  " + logicVars[iLogicVar].id)
}

Screenshot from 2022-04-22 03-19-53

.

This is possible on 7.3.0 also @fantross
just like Homey.logic.getVariables().then(r => log(r))
which returns all of your variables & ID’s

It should be something like this with Logics variables. (First create the variables, and then go find the variable ID’s )

// This is not needed anylonger, so comment out or remove
//let BLApp = await Homey.apps.getApp({ id: 'net.i-dev.betterlogic' });

//BLApp.apiPut(`${VARIABLE_PREFIX}temperature/${temperature}`);
await Homey.logic.updateVariable({id: '1a164078-63fc-470c-ac76-9da7f113445e', variable: { value: temperature } })
// The example ID should be the ID from var '${VARIABLE_PREFIX}_temperature'

//BLApp.apiPut(`${VARIABLE_PREFIX}brightness/${brightness}`);
await Homey.logic.updateVariable({id: '1a164078-63fc-470c-ac76-9da7f113445e', variable: { value: brightness } })
// The example ID should be the ID from var '${VARIABLE_PREFIX}_brightness'

Just installed rc.22 and adjusted the script accordingly your (@Peter_Kawa and @Caseda) suggestion.
First try seems to look good. The (new) Homey logic variables are filled with the correct values.
Tomorrow I will let you know, if it worked correct.

Thank you very much for your efforts!

EDIT
It worked like it should.

So thank you very much for helping out @Caseda and @Peter_Kawa! And @iotnerd of course for the grate script! :+1:t3:

1 Like

Hi!

Just got my Homey and have been eager to set up circadian lighting (amongst a lot of other things, of course…).

May I ask for the entire updated script and some guidance on which variables I should create and where to put their ID’s in the script?

Best regards.

You wrote “updated script”, but it depends on Homey firmware. So for which Homey firmware? v7.3.0 (or lower) or v7.4.x (experimental)?
“Tutorial” for firmware v7.3.0 (or lower) please see post #25.

Btw, it is not recommended to install an experimental firmware on production systems.

I was thinking for 7.4.x. But I have 7.3.0 installed.

I’m in no rush to get the circadian lighting in place, hence I can wait for stable 7.4.x before I try anything.

Seems easier to skip the 7.3.0 method and go straight for the 7.4.0 method when it’s available. Right?

For the script in relation with the Homey firmware version v7.3.0 an additional app (Better Logic) is necessary. This app is not necessary with firmware version v7.4.x.

I just finished the process for 7.3.0 with the Better Logic app and it seems to be working. Thank you!

Looking forward to an updated tutorial on 7.4.0 without the Better Logic app :slight_smile:

Looking backward is maybe better by starting to read the posts :upside_down_face:

This is the entire updated script for Homey firmware v7.4.x, based on the script in post #16, which I modified a little bit in comparison to the original script:

/** 
 * 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 = 'Circadian';
const MIN_BRIGHTNESS = 0.40;
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)}%`);

// 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}_Temperatur:\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}_Helligkeit:\t`, brightness);

await Homey.logic.updateVariable({id: 'b042b591-48f9-4803-a9af-3d115ac2a95b', variable: { value: temperature } });
await Homey.logic.updateVariable({id: '2074ddcf-92b2-42eb-a4f2-2c32de90a96a', variable: { value: brightness } });

return true;

The Homey logic variables I use are called:
– Circadian_Helligkeit
– Circadian_Temperatur

If you want to rename the variables, then the following red marked words must be changed:

const VARIABLE_PREFIX = 'Circadian';
console.log(`${VARIABLE_PREFIX}_Temperatur:\t`, temperature);
console.log(`${VARIABLE_PREFIX}_Helligkeit:\t`, brightness);

In these two lines the IDs of the variable must be entered:

await Homey.logic.updateVariable({id: 'b042b591-48f9-4803-a9af-3d115ac2a95b', variable: { value: temperature } });
await Homey.logic.updateVariable({id: '2074ddcf-92b2-42eb-a4f2-2c32de90a96a', variable: { value: brightness } });

The variable IDs are received by running the following script:

Homey.logic.getVariables().then(r => log(r))
2 Likes

Thanks, but that wasn’t what I was asking for. I was asking for one post with the complete instructions, as @fantross just served me in post #45. Being new to Homey, HomeyScript and scripting in general - I can’t add previous posts together to find the solution.

Much appreciated :heart_eyes:

Will test as soon as I recieve 7.4.x. on my Homey!

EDIT 220511: Just tested with 7.4.1 - working great. Thank you!