Option to set a delay before and after sunrise and sunset

Feature description
For switching ambient lighting on and off, sunrise and sunset times don’t exactly match needs depending on the exposure of the rooms in a house. A delay on either side would be very useful.

I thought there was already a request about that and that there was a GitHub issue, but I couldn’t find anything.

+1 for the lights and shutters, it’s essential.

Yes I agree!

For those who come across this topic, an « after » delay as such is already possible — you just need to add a « wait » action in the scene. For a « before » delay, indeed it’s not possible at the moment :slight_smile:

1 Like

I am currently using « At each sunrise » to open the roller shutters.

But it is already daylight for some time when they open.

There are additional astronomical parameters: Lever du Soleil et Coucher du Soleil | Augusta

Wouldn’t there be an API that could give us more options?

1 Like

I think I’ve found something usable:

https://sunrise-sunset.org/api

Not all of the Heliotrope options are available in Jeedom, but it does include concepts like astronomical dawn, etc…

1 Like

I’m not sure we need to go through an API; at the moment we’re doing the calculations locally :slight_smile:

Why not add other calculations if needed — could you create a specific feature request with the other calculations you’d like?

Ok! Great… I don’t realize everything that’s behind Gladys!

I’ll look more closely at the different concepts (astronomical/civil/nautical dawn/twilight…) to formulate something coherent.

2 Likes

Hello @b3n.0,

In general the calculations are performed using the civil ephemerides (sun 6° below the horizon).

From a display point of view, an interesting representation for example is a timeline on which you can use a slider to lengthen or shorten day and night.

But the risk is that, if the calculation is taken on a 0–24h UTC date, and the app display uses the browser’s GMT time, the slider ends up straddling two days.

Also, note that the calculation code often uses simplified algorithms that become less accurate above the 60th parallel. It would be worth verifying the calculations if someone uses Gladys on polar stations?

Gladys uses the « suncalc » library which performs the calculation in this way:

In the same library, from what I understand, it seems to include the concepts that allow for the « first light of day »:

// sun times configuration (angle, morning name, evening name)

var times = SunCalc.times = [
    [-0.833, 'sunrise',       'sunset'      ],
    [  -0.3, 'sunriseEnd',    'sunsetStart' ],
    [    -6, 'dawn',          'dusk'        ],
    [   -12, 'nauticalDawn',  'nauticalDusk'],
    [   -18, 'nightEnd',      'night'       ],
    [     6, 'goldenHourEnd', 'goldenHour'  ]
];

@b3n.0
il y a 2 solutions, passer par node-red Mini-tuto Sun-Position Nore-red Mqtt pour piloter les volets en fonction de la position du soleil et des mois

en modifiant le flow pour avoir sunrise et sunset comme ci-dessous et en ajoutant
dans gladys dans l’appareil créé pour l’azimuth et l’altitude 2 commutateurs
correspondants qui recoit du flow node-red
gladys/master/device/mqtt:soleil/feature/mqtt:sunrise/state
gladys/master/device/mqtt:soleil/feature/mqtt:sunset/state
ensuite il y a plus qu’à le gérer dans une scène en récupérant l’état de l’appareil
dans le code de la fonction minutedepuis minuit il faut passer l’état à 1 dans le payload pour commuter et ajuster le node « delay »

Code du flow
t[
    {
        "id": "dbd46647b51191ed",
        "type": "tab",
        "label": "Sun-Position",
        "disabled": false,
        "info": "",
        "env": []
    },
    {
        "id": "a0d0e562.7ad1d8",
        "type": "debug",
        "z": "dbd46647b51191ed",
        "name": "debug 1",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 720,
        "y": 180,
        "wires": []
    },
    {
        "id": "9cc2d51.4ac0828",
        "type": "change",
        "z": "dbd46647b51191ed",
        "name": "azimuth",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.azimuth",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "gladys/master/device/mqtt:soleil/feature/mqtt:azimuth/state",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 720,
        "y": 240,
        "wires": [
            [
                "0a17f9d94c9dae35",
                "ddf8b5e66052a4dc"
            ]
        ]
    },
    {
        "id": "28e91.9d63d16f6",
        "type": "change",
        "z": "dbd46647b51191ed",
        "name": "altitude",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.altitude",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "gladys/master/device/mqtt:soleil/feature/mqtt:altitude/state",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 720,
        "y": 320,
        "wires": [
            [
                "ae57174554627c71",
                "58367a9aedab25dc"
            ]
        ]
    },
    {
        "id": "e921e01a.a0fa3",
        "type": "debug",
        "z": "dbd46647b51191ed",
        "name": "debug 4",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 720,
        "y": 380,
        "wires": []
    },
    {
        "id": "e866e950.a7f798",
        "type": "debug",
        "z": "dbd46647b51191ed",
        "name": "debug 3",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "true",
        "targetType": "full",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 1160,
        "y": 180,
        "wires": []
    },
    {
        "id": "5b085e1b.4ec8a",
        "type": "debug",
        "z": "dbd46647b51191ed",
        "name": "debug 6",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "true",
        "targetType": "full",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 1160,
        "y": 380,
        "wires": []
    },
    {
        "id": "8eee70dd77c1369c",
        "type": "mqtt out",
        "z": "dbd46647b51191ed",
        "name": "",
        "topic": "",
        "qos": "",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "3c3eb52d.9fb296",
        "x": 1150,
        "y": 280,
        "wires": []
    },
    {
        "id": "0a17f9d94c9dae35",
        "type": "function",
        "z": "dbd46647b51191ed",
        "name": "arrondi entier",
        "func": "msg.payload = Math.round(msg.payload)\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 930,
        "y": 240,
        "wires": [
            [
                "8eee70dd77c1369c",
                "e866e950.a7f798"
            ]
        ]
    },
    {
        "id": "ddf8b5e66052a4dc",
        "type": "debug",
        "z": "dbd46647b51191ed",
        "name": "debug 2",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 920,
        "y": 180,
        "wires": []
    },
    {
        "id": "ae57174554627c71",
        "type": "function",
        "z": "dbd46647b51191ed",
        "name": "arrondi entier",
        "func": "msg.payload = Math.round(msg.payload)\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 930,
        "y": 320,
        "wires": [
            [
                "5b085e1b.4ec8a",
                "8eee70dd77c1369c"
            ]
        ]
    },
    {
        "id": "58367a9aedab25dc",
        "type": "debug",
        "z": "dbd46647b51191ed",
        "name": "debug 5",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 920,
        "y": 380,
        "wires": []
    },
    {
        "id": "56265aeb.99f034",
        "type": "sun-position",
        "z": "dbd46647b51191ed",
        "name": "",
        "positionConfig": "2831ba70.55a636",
        "rules": [
            {
                "valueLow": "0",
                "valueLowType": "num",
                "valueHigh": "360",
                "valueHighType": "num"
            }
        ],
        "onlyOnChange": "true",
        "topic": "",
        "outputs": 2,
        "start": "sunsetStart",
        "startType": "pdsTime",
        "startOffset": 0,
        "startOffsetType": "num",
        "startOffsetMultiplier": 60000,
        "end": "sunriseEnd",
        "endType": "pdsTime",
        "endOffset": 0,
        "endOffsetType": "num",
        "endOffsetMultiplier": 60000,
        "x": 470,
        "y": 280,
        "wires": [
            [
                "a0d0e562.7ad1d8",
                "9cc2d51.4ac0828",
                "28e91.9d63d16f6",
                "6b1f97b4cc4c5e53",
                "b10eedb2bb5656c1"
            ],
            [
                "e921e01a.a0fa3"
            ]
        ]
    },
    {
        "id": "664b1852987e09be",
        "type": "time-inject",
        "z": "dbd46647b51191ed",
        "name": "Période et Intervalle d'injection .",
        "nameInt": "¹sunrise start - sunset end = timestamp ↻10min",
        "positionConfig": "2831ba70.55a636",
        "props": [
            {
                "p": "",
                "pt": "msgPayload",
                "v": "",
                "vt": "date",
                "o": "",
                "oT": "none",
                "oM": "60000",
                "f": 0,
                "fS": 0,
                "fT": "UNIX timestamp (ms)",
                "fI": "0",
                "next": true,
                "days": "*",
                "months": "*",
                "onlyOddDays": false,
                "onlyEvenDays": false,
                "onlyOddWeeks": false,
                "onlyEvenWeeks": false
            },
            {
                "p": "",
                "pt": "msgTopic",
                "v": "",
                "vt": "str",
                "o": "",
                "oT": "none",
                "oM": "60000",
                "f": 0,
                "fS": 0,
                "fT": "UNIX timestamp (ms)",
                "fI": "0",
                "next": false,
                "days": "*",
                "months": "*",
                "onlyOddDays": false,
                "onlyEvenDays": false,
                "onlyOddWeeks": false,
                "onlyEvenWeeks": false
            }
        ],
        "injectTypeSelect": "interval-time",
        "intervalCount": "10",
        "intervalCountType": "num",
        "intervalCountMultiplier": 60000,
        "cron": "",
        "cronType": "cronexpr",
        "time": "sunriseStart",
        "timeType": "pdsTime",
        "offset": 0,
        "offsetType": "none",
        "offsetMultiplier": 60000,
        "timeEnd": "sunsetEnd",
        "timeEndType": "pdsTime",
        "timeEndOffset": 0,
        "timeEndOffsetType": "none",
        "timeEndOffsetMultiplier": 60000,
        "timeDays": "*",
        "timeOnlyOddDays": false,
        "timeOnlyEvenDays": false,
        "timeOnlyOddWeeks": false,
        "timeOnlyEvenWeeks": false,
        "timeMonths": "*",
        "timedatestart": "",
        "timedateend": "",
        "property": "",
        "propertyType": "none",
        "propertyCompare": "true",
        "propertyThreshold": "",
        "propertyThresholdType": "num",
        "timeAlt": "",
        "timeAltType": "entered",
        "timeAltDays": "*",
        "timeAltOnlyOddDays": false,
        "timeAltOnlyEvenDays": false,
        "timeAltOnlyOddWeeks": false,
        "timeAltOnlyEvenWeeks": false,
        "timeAltMonths": "*",
        "timeAltOffset": 0,
        "timeAltOffsetType": "none",
        "timeAltOffsetMultiplier": 60000,
        "once": true,
        "onceDelay": 0.1,
        "recalcTime": 2,
        "x": 220,
        "y": 280,
        "wires": [
            [
                "56265aeb.99f034"
            ]
        ]
    },
    {
        "id": "6b1f97b4cc4c5e53",
        "type": "change",
        "z": "dbd46647b51191ed",
        "name": "sunset",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.times.sunsetStart",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "gladys/master/device/mqtt:soleil/feature/mqtt:sunset/state",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 710,
        "y": 660,
        "wires": [
            [
                "8c99bc6da0a4d5b0",
                "3e97e9902da57988"
            ]
        ]
    },
    {
        "id": "b10eedb2bb5656c1",
        "type": "change",
        "z": "dbd46647b51191ed",
        "name": "sunrise",
        "rules": [
            {
                "t": "set",
                "p": "payload",
                "pt": "msg",
                "to": "payload.times.sunriseStart",
                "tot": "msg"
            },
            {
                "t": "set",
                "p": "topic",
                "pt": "msg",
                "to": "gladys/master/device/mqtt:soleil/feature/mqtt:sunrise/state",
                "tot": "str"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 720,
        "y": 500,
        "wires": [
            [
                "ac45aa2966444fcf",
                "bb2297c4afea4845"
            ]
        ]
    },
    {
        "id": "3e97e9902da57988",
        "type": "debug",
        "z": "dbd46647b51191ed",
        "name": "debug 10",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "true",
        "targetType": "full",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 920,
        "y": 720,
        "wires": []
    },
    {
        "id": "e9dd4a4e8fcb034c",
        "type": "debug",
        "z": "dbd46647b51191ed",
        "name": "debug 11",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "true",
        "targetType": "full",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 1160,
        "y": 440,
        "wires": []
    },
    {
        "id": "870a1a0c309ea346",
        "type": "mqtt out",
        "z": "dbd46647b51191ed",
        "name": "",
        "topic": "",
        "qos": "",
        "retain": "",
        "respTopic": "",
        "contentType": "",
        "userProps": "",
        "correl": "",
        "expiry": "",
        "broker": "3c3eb52d.9fb296",
        "x": 1170,
        "y": 580,
        "wires": []
    },
    {
        "id": "8c99bc6da0a4d5b0",
        "type": "function",
        "z": "dbd46647b51191ed",
        "name": "minutes depuis 00h00",
        "func": "// Entrée : msg.payload = \"2023-11-23T16:23:18.121Z\"\n\n// Convertir la chaîne de caractères en objet Date\nvar dateObj = new Date(msg.payload.value);\n\n// Extraire l'heure et les minutes\nvar heures = dateObj.getUTCHours();\nvar minutes = dateObj.getUTCMinutes();\n\n// Calculer le nombre total de minutes depuis 00:00\nvar minutesDepuisMinuit = heures * 60 + minutes;\n\nmsg.topic = heures;\nmsg.payload = minutesDepuisMinuit;\n\n// Renvoyer le message\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 940,
        "y": 660,
        "wires": [
            [
                "7134c8c9865f611d",
                "5efa49d45f363336"
            ]
        ]
    },
    {
        "id": "ac45aa2966444fcf",
        "type": "function",
        "z": "dbd46647b51191ed",
        "name": "minutes depuis 00h00",
        "func": "// Entrée : msg.payload = \"2023-11-23T16:23:18.121Z\"\n\n// Convertir la chaîne de caractères en objet Date\nvar dateObj = new Date(msg.payload.value);\n\n// Extraire l'heure et les minutes\nvar heures = dateObj.getUTCHours();\nvar minutes = dateObj.getUTCMinutes();\n\n// Calculer le nombre total de minutes depuis 00:00\nvar minutesDepuisMinuit = heures * 60 + minutes;\n\nmsg.payload = minutesDepuisMinuit;\n\n// Renvoyer le message\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 940,
        "y": 500,
        "wires": [
            [
                "e9dd4a4e8fcb034c",
                "9de494cb30ca99c2"
            ]
        ]
    },
    {
        "id": "bb2297c4afea4845",
        "type": "debug",
        "z": "dbd46647b51191ed",
        "name": "debug 12",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "true",
        "targetType": "full",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 920,
        "y": 440,
        "wires": []
    },
    {
        "id": "7134c8c9865f611d",
        "type": "debug",
        "z": "dbd46647b51191ed",
        "name": "debug 13",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": true,
        "complete": "true",
        "targetType": "full",
        "statusVal": "payload",
        "statusType": "auto",
        "x": 1160,
        "y": 720,
        "wires": []
    },
    {
        "id": "9de494cb30ca99c2",
        "type": "delay",
        "z": "dbd46647b51191ed",
        "name": "",
        "pauseType": "delay",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 1160,
        "y": 500,
        "wires": [
            [
                "870a1a0c309ea346"
            ]
        ]
    },
    {
        "id": "5efa49d45f363336",
        "type": "delay",
        "z": "dbd46647b51191ed",
        "name": "",
        "pauseType": "delay",
        "timeout": "5",
        "timeoutUnits": "seconds",
        "rate": "1",
        "nbRateUnits": "1",
        "rateUnits": "second",
        "randomFirst": "1",
        "randomLast": "5",
        "randomUnits": "seconds",
        "drop": false,
        "allowrate": false,
        "outputs": 1,
        "x": 1160,
        "y": 660,
        "wires": [
            [
                "870a1a0c309ea346"
            ]
        ]
    },
    {
        "id": "3c3eb52d.9fb296",
        "type": "mqtt-broker",
        "name": "MQTT Broker",
        "broker": "localhost",
        "port": "1883",
        "clientid": "",
        "autoConnect": true,
        "usetls": false,
        "compatmode": false,
        "protocolVersion": "4",
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "birthMsg": {},
        "closeTopic": "",
        "closeQos": "0",
        "closePayload": "",
        "closeMsg": {},
        "willTopic": "",
        "willQos": "0",
        "willPayload": "",
        "willMsg": {},
        "userProps": "",
        "sessionExpiry": ""
    },
    {
        "id": "2831ba70.55a636",
        "type": "position-config",
        "name": "Maison",
        "isValide": "true",
        "angleType": "deg",
        "timeZoneOffset": "99",
        "timeZoneDST": "0",
        "stateTimeFormat": "3",
        "stateDateFormat": "12",
        "contextStore": ""
    }
]

ou par une scène (en attendant un dev pour cela avec sunrise, sunset, l’altitude et la longitude) :

pour choisir les jours concernés avec J -1 ici pour que cela fonctionne de L à V

combien de temps avant le lever du soleil = 24h - le temps désiré ici pour 1 h avant le lever du soleil

Il y a plus qu’à actionner

Thanks @cce66 for your reply.

I’m still not very comfortable with Node-RED! I do intend to take a closer look but I’m having trouble finding the time.

I had indeed thought of that solution… having realized that it doesn’t work on two Sundays of the year (time change).

That’s what prompted me to look into the other notions of dawn!

This article is quite interesting: Comprendre - Concepts fondamentaux - Lever et coucher d'un astre
But maybe a bit too technical (at least for me).

@b3n.0

excellent tutorials and funny to boot…

You just need to adjust the function node of the « flow » but normally the code adjusts to UTC so it should be fine otherwise twice a year…
// Extract hours and minutes
var heures = dateObj.getUTCHours();
var minutes = dateObj.getUTCMinutes();

That’s why I made that tutorial on Sun-position — it lets you control the shutters, for example, to compensate for sunrise/sunset; there’s the notion of altitude: you can trigger opening as soon as the altitude exceeds 10 and close them when it falls below 10

1 Like

hello @cce66 ,
iodyme the master in Node-RED ! a reference !
present on YouTube from time to time but especially on Twitch !

Thanks for sharing! I’ll try to set myself the challenge

you’ll see it’s not that complicated, especially after watching Neodyme’s tutorials (he’s a professor at the University of Liège) and Node-RED is great for interacting with everything if you check the documentation or look for flows on the web (there are quite a few ready-made ones that help you get familiar with it). Having an interaction between Node-RED and Gladys currently fills in what isn’t yet implemented in Gladys or what won’t be if the need is too specific.

I’ve added a mini-tutorial that should help you :blush:

1 Like

I have a very simple solution that allows me to configure triggers in scenes in advance of sunrises and sunsets.
I tested the « attendre » function with values in whole hours or directly in minutes, for delay values beyond around 8:00 PM, and it allows managing the trigger ahead of sunrise or sunset.


However, the accuracy depends on the amount the sunrise and sunset times varied from the previous day :wink:
If that works, it frees up votes.

That’s what I mentioned earlier in the post (with the constraint for the sunrise of having to do it as follows HeureLeverSoleil + Attendre24h - TempsActionAvantLeverSoleil )

[quote=« pierre-gilles, post:3, topic:7850 »]
For those who come across this thread, an « after » delay in itself is already possible — you just need to add a « wait » action in the scene.

Oops, sorry, I wasn’t paying attention :face_with_hand_over_mouth:

That has happened to me :wink: sometimes! :joy: