Holiday lights scheduler and demo mode for WLED

Questions and Discussion

Post questions and follow the discussion about this recipe hereopen in new window

Here's a flow to control some outdoor house LEDs hooked up to an ESP8266 running WLEDopen in new window.

The holiday lights turn on each day at sunset and then off at midnight. It uses WLEDopen in new window presets based on the day or season. It has a demo mode that will cycle through all the defined presets or all the included effects.

screenshot of flow

Version 1

[{"id":"886a465.91a0ab8","type":"switch","z":"894a6b3077af9604","name":"dark?","property":"dark","propertyType":"flow","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":450,"y":240,"wires":[["5e38e1ee8665f5e7"],["90579358.a3dce"]]},{"id":"34cb332b.f8228c","type":"server-state-changed","z":"894a6b3077af9604","name":"Demo Mode","server":"","version":3,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_select.holiday_lights_demo_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"off","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"for":"","forType":"num","forUnits":"minutes","ignorePrevStateNull":true,"ignorePrevStateUnknown":true,"ignorePrevStateUnavailable":true,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":294,"y":384,"wires":[["df0da821.aa74b8"],["91131276.4f322"]]},{"id":"90579358.a3dce","type":"api-call-service","z":"894a6b3077af9604","name":"turn off holiday lights","server":"","version":3,"debugenabled":false,"service_domain":"light","service":"turn_off","entityId":"{{flow.wledHolidayLightsEntityId}}","data":"","dataType":"json","mergecontext":"","mustacheAltTags":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"data"}],"queue":"last","x":692,"y":288,"wires":[[]]},{"id":"dc4b2af1.d366a8","type":"ha-wait-until","z":"894a6b3077af9604","name":"loop","server":"","version":0,"outputs":2,"entityId":"{{flow.wledHolidayLightsEntityId}}","entityIdFilterType":"exact","property":"state","comparator":"is","value":"off","valueType":"str","timeout":"60","timeoutType":"num","timeoutUnits":"minutes","entityLocation":"","entityLocationType":"none","checkCurrentState":false,"blockInputOverrides":false,"x":850,"y":240,"wires":[[],["5e38e1ee8665f5e7"]]},{"id":"59e25c2c.a3a514","type":"eztimer","z":"894a6b3077af9604","name":"","debug":false,"autoname":"sunset - 00:00","tag":"test1","suspended":false,"sendEventsOnSuspend":false,"latLongSource":"manual","latLongHaZone":"","lat":"","lon":"","timerType":"1","startupMessage":true,"ontype":"1","ontimesun":"sunset","ontimetod":"17:00","onpropertytype":"flow","onproperty":"dark","onvaluetype":"bool","onvalue":"true","onoffset":"","onrandomoffset":0,"onsuppressrepeats":false,"offtype":"2","offtimesun":"dusk","offtimetod":"00:00","offduration":0,"offpropertytype":"flow","offproperty":"dark","offvaluetype":"bool","offvalue":"false","offoffset":0,"offrandomoffset":0,"offsuppressrepeats":false,"resend":false,"resendInterval":"1m","mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"sun":true,"x":304,"y":240,"wires":[["886a465.91a0ab8"]]},{"id":"f4ab62e2.10029","type":"api-call-service","z":"894a6b3077af9604","name":"turn on holiday lights","server":"","version":3,"debugenabled":false,"service_domain":"select","service":"select_option","entityId":"{{flow.wledHolidayLightsPresetEntityId}}","data":"{\"option\": payload}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"last","x":900,"y":192,"wires":[[]]},{"id":"98efb1f6.c85b8","type":"ha-wait-until","z":"894a6b3077af9604","name":"loop","server":"","version":0,"outputs":2,"entityId":"{{flow.wledHolidayLightsEntityId}}","entityIdFilterType":"exact","property":"state","comparator":"is","value":"off","valueType":"str","timeout":"$number($entities(\"input_number.holiday_lights_demo_mode_delay\").state)","timeoutType":"jsonata","timeoutUnits":"seconds","entityLocation":"","entityLocationType":"none","checkCurrentState":false,"blockInputOverrides":true,"x":1058,"y":432,"wires":[[],["91131276.4f322"]]},{"id":"91131276.4f322","type":"switch","z":"894a6b3077af9604","name":"which mode?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"cycle presets","vt":"str"},{"t":"eq","v":"cycle effects","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":470,"y":432,"wires":[["5adba3095728cdab"],["28fd29dc.96d306"]]},{"id":"333ed476.51a77c","type":"api-call-service","z":"894a6b3077af9604","name":"turn on preset","server":"","version":3,"debugenabled":false,"service_domain":"select","service":"select_option","entityId":"{{flow.wledHolidayLightsPresetEntityId}}","data":"{\"option\": name}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":880,"y":432,"wires":[["98efb1f6.c85b8","d0b9f667.79a8c8"]]},{"id":"fcf7dabd.b7a368","type":"api-call-service","z":"894a6b3077af9604","name":"turn on effect","server":"","version":3,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"{{flow.wledHolidayLightsEntityId}}","data":"{\"effect\": name}","dataType":"jsonata","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":870,"y":480,"wires":[["98efb1f6.c85b8","d0b9f667.79a8c8"]]},{"id":"df0da821.aa74b8","type":"change","z":"894a6b3077af9604","name":"reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"true","tot":"bool"}],"action":"","property":"","from":"","to":"","reg":false,"x":450,"y":384,"wires":[["886a465.91a0ab8","98efb1f6.c85b8"]]},{"id":"998c4f75.52cbf","type":"api-call-service","z":"894a6b3077af9604","name":"turn off demo mode","server":"","version":3,"debugenabled":false,"service_domain":"input_select","service":"select_option","entityId":"input_select.holiday_lights_demo_mode","data":"{\"option\": \"off\"}","dataType":"json","mergecontext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":570,"y":528,"wires":[[]]},{"id":"5caeec35.01a114","type":"server-state-changed","z":"894a6b3077af9604","name":"Holiday Lights turned off?","server":"","version":3,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"light.wled","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"off","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":false,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":332,"y":528,"wires":[["998c4f75.52cbf"],[]]},{"id":"28fd29dc.96d306","type":"function","z":"894a6b3077af9604","name":"get next effect","func":"const states = global.get(\"homeassistant\").homeAssistant.states;\nconst entityId = flow.get(\"wledHolidayLightsEntityId\");\nconst effects = states[entityId].attributes.effect_list;\nconst activeEffect = states[entityId].attributes.effect;\nlet index = effects.findIndex(ele => ele === activeEffect);\nindex = (index === -1 || index >= effects.length - 1) ? 0 : index + 1;\n\nnode.status({text: effects[index]});\n\nmsg.name = effects[index];\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":672,"y":480,"wires":[["fcf7dabd.b7a368"]]},{"id":"d0b9f667.79a8c8","type":"ha-entity","z":"894a6b3077af9604","name":"current demo","server":"","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"Holiday Lights Demo Current"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"name","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"","outputPayloadType":"str","x":1078,"y":480,"wires":[[]]},{"id":"9168ce00.dc7de","type":"comment","z":"894a6b3077af9604","name":"Scheduler","info":"","x":284,"y":192,"wires":[]},{"id":"241d097a.a3fe76","type":"comment","z":"894a6b3077af9604","name":"Demo Mode","info":"","x":294,"y":336,"wires":[]},{"id":"22f2928a.e2ae9e","type":"inject","z":"894a6b3077af9604","name":"Manual Start","props":[{"p":"payload"},{"p":"topic","vt":"str"},{"p":"dark","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":294,"y":288,"wires":[["5e38e1ee8665f5e7"]]},{"id":"9f8ca90550d41489","type":"inject","z":"894a6b3077af9604","name":"Setup","props":[{"p":"entityId","v":"light.holiday_lights","vt":"str"}],"repeat":"3600","crontab":"","once":true,"onceDelay":0.1,"topic":"","x":284,"y":144,"wires":[["1047f2356f1e5203"]]},{"id":"1047f2356f1e5203","type":"change","z":"894a6b3077af9604","name":"set entity ids here","rules":[{"t":"set","p":"wledHolidayLightsEntityId","pt":"flow","to":"light.wled","tot":"str"},{"t":"set","p":"wledHolidayLightsPresetEntityId","pt":"flow","to":"select.wled_preset","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":490,"y":144,"wires":[[]]},{"id":"5e38e1ee8665f5e7","type":"function","z":"894a6b3077af9604","name":"date ranges","func":"const now = new Date();\nconst monthDay = getMonthDay(now);\nconst states = global.get(\"homeassistant\").homeAssistant.states;\nconst entityId = flow.get(\"wledHolidayLightsEntityId\");\nconst presetId = flow.get(\"wledHolidayLightsPresetEntityId\");\nconst christmasPresets = [\n  \"Christmas\",\n  \"Christmas Candy Cane\",\n  \"Christmas Classic\",\n  \"Christmas Holly Jolly\"\n];\n\nlet updateInterval = 0; // in minutes\nlet preset;\n\nswitch(true) {\n// Winter \n    case isToday(214):\n      preset = \"Valentine\";\n      break;\n// President's Day changes every year, Veteran's Day & Flag Day always the same date\n    case isToday(614): // 6/14 Flag Day\n    case isToday(1111): // 11/11 Veterans Day\n    case isToday(\"President's Day\"):\n    case isToday(\"Memorial Day\"):\n      preset = \"Red White Blue\";\n      break;\n    case isToday(317):\n      preset = \"St Patty\";\n      break;\n    case isToday(\"Easter\"):\n      preset = \"Easter\";\n      break;\n    case isToday(\"Ash Wednesday\", \"Easter\"):\n      preset = \"Spring\";\n      break;\n// Spring\n    case isToday(504):\n      preset = \"Star Wars\";\n      break;\n    case isToday(505):\n      preset = \"Cinco de Mayo\";\n      break;\n// Summer\n    case isToday(704):\n      preset = \"Fireworks\";\n      break;\n// Autumn\n    case isToday(1014, 1031):\n      preset = \"Halloween\";\n      break;\n    case isToday(\"Thanksgiving Day\"):\n      preset = \"Autumn\";\n      break;\n    case isToday(1231):\n      preset = \"Fireworks White\";\n      break;\n    case isToday(\"Thanksgiving Day\", 1231):\n    case isToday(101, 106):\n      preset = getRandomPreset(christmasPresets);\n      updateInterval = 60;\n      break;\n    default:\n      const seasons = {\n        \"winter\": \"Winter\",\n        \"spring\": \"Spring\",\n        \"summer\": \"Summer\",\n        \"autumn\": \"Autumn\"\n      }\n      const currentSeason = states[\"sensor.season\"].state;\n      preset = seasons[currentSeason];\n      break;\n}\n\nnode.status({ text: preset});\nmsg.payload = preset;\nconst secondOutput = updateInterval ?  { payload: { timeout: updateInterval } } : { reset: true };\n\nreturn [msg, secondOutput];\n\nfunction isToday(start, end) {\n  if (typeof start === \"string\") {\n    start = getHolidayDate(now, start)\n  }\n  if (typeof end === \"string\") {\n    end = getHolidayDate(now, end)\n  }\n  if (end === undefined) return monthDay === start;\n\n  return monthDay >= start && monthDay <= end;\n}\n\nfunction getHolidayDate(date, holiday) {\n    holiday = holiday.toLowerCase();\n    // dates are formatted as month, week, day of the week\n    // names should be lowercase\n    const holidays = {\n        \"martin luther king day\": [0,2,1],\n        \"president's day\": [1,2,1],\n        \"daylight savings time begins\": [2,1,0],\n        \"mother's day\": [4,1,0],\n        \"memorial day\": [4,-1,1],\n        \"father's day\": [5,2,0],\n        \"labor day\": [8,0,1],\n        \"columbus day\": [9,1,1],\n        \"daylight savings time ends\": [10,0,0],\n        \"thanksgiving day\": [10,3,4]\n    };\n    let holidayDate;\n    if(holiday === \"easter\") {\n        holidayDate = getEasterDate(date);\n    } else if (holiday === \"ash wednesday\") {\n        const easter = getEasterDate(date);\n        holidayDate = new Date(easter.getFullYear(), easter.getMonth(), (easter.getDate()-46));\n    } else {\n        const [month, week, day] = holidays[holiday];\n        holidayDate = getDate(date.getFullYear(), month, week, day);\n    }\n    \n    const monthDay = getMonthDay(holidayDate);\n    \n    return monthDay;\n}\n\nfunction getMonthDay(date) {\n    const monthString = String(date.getMonth() + 1).padStart(2, \"0\");\n    const dayString = String(date.getDate()).padStart(2, \"0\");\n    const monthDay = Number(`${monthString}${dayString}`);\n    \n    return monthDay;\n}\n\nfunction getDate(year, month, week, day) {\n    let firstDay = 1;\n    if (week < 0) {\n        month++;\n        firstDay--;\n    }\n    const date = new Date(year, month, (week * 7) + firstDay);\n    if (day < date.getDay()) {\n        day += 7;\n    }\n    date.setDate(date.getDate() - date.getDay() + day);\n    return date;\n}\n\n/**\n * Calculates Easter in the Gregorian/Western (Catholic and Protestant) calendar\n * based on the algorithm by Oudin (1940) from http://www.tondering.dk/claus/cal/easter.php\n * @returns {array} [int month, int day]\n */\nfunction getEasterDate(date) {\n  const year = date.getFullYear();\n  const f = Math.floor,\n    // Golden Number - 1\n    G = year % 19,\n    C = f(year / 100),\n    // related to Epact\n    H = (C - f(C / 4) - f((8 * C + 13) / 25) + 19 * G + 15) % 30,\n    // number of days from 21 March to the Paschal full moon\n    I = H - f(H / 28) * (1 - f(29 / (H + 1)) * f((21 - G) / 11)),\n    // weekday for the Paschal full moon\n    J = (year + f(year / 4) + I + 2 - C + f(C / 4)) % 7,\n    // number of days from 21 March to the Sunday on or before the Paschal full moon\n    L = I - J,\n    month = 3 + f((L + 40) / 44),\n    day = L + 28 - 31 * f(month / 4);\n\n  return new Date(`${month}/${day}/${year}`);\n}\n\nfunction getRandomPreset(list) {\n  const activePreset = states[presetId].state;\n  const filteredList = list.filter((e) => e !== activePreset);\n  \n  return filteredList[Math.floor(Math.random() * filteredList.length)];\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":662,"y":240,"wires":[["f4ab62e2.10029"],["dc4b2af1.d366a8"]]},{"id":"e01d1bd31bd9e634","type":"comment","z":"894a6b3077af9604","name":"Change holiday dates in the \"date ranges\" node","info":"","x":404,"y":96,"wires":[]},{"id":"5adba3095728cdab","type":"function","z":"894a6b3077af9604","name":"get next preset","func":"const states = global.get(\"homeassistant\").homeAssistant.states;\nconst presetId = flow.get(\"wledHolidayLightsPresetEntityId\");\nconst presets = states[presetId].attributes.options;\nconst activePreset = states[presetId].state;\nlet index = presets.findIndex(ele => ele === activePreset);\nindex = index === -1 || index >= presets.length - 1 ? 0 : index +1;\nconst name = presets[index];\n\nnode.status({text: name });\n\nmsg.name = name;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":672,"y":432,"wires":[["333ed476.51a77c"]]}]
version 0
[{"id":"eb756b3f.d770f8","type":"subflow","name":"Create HA Helpers","info":"","category":"","in":[{"x":84,"y":96,"wires":[{"id":"7f1493fc.1300fc"}]}],"out":[],"env":[{"name":"serverName","type":"str","value":"Home Assistant","ui":{"label":{"en-US":"HA Server Name"},"type":"input","opts":{"types":["str"]}}},{"name":"helpers","type":"json","value":"[]","ui":{"label":{"en-US":"Helpers"},"type":"input","opts":{"types":["json"]}}}],"color":"#DDAA99","status":{"x":246,"y":48,"wires":[{"id":"a66d5d93.8a5f","port":0}]}},{"id":"7f1493fc.1300fc","type":"function","z":"eb756b3f.d770f8","name":"process helpers","func":"const serverName = toCamelCase(env.get('serverName'));\nconst haServer = global.get(\"homeassistant\")[serverName];\nif(!haServer) {\n    node.error(\"Invalid HA server name\");\n    return;\n}\nconst states = haServer.states;\nconst helpers = env.get(\"helpers\");\n\nhelpers.forEach(h => {\n    const entityId = `${h.type}.${h.id}`;\n    if(!states[entityId]) {\n        const {id, type, ...data} = h;\n        const apiData = {\n            entity: h,\n            payload: { \n                data \n            }\n        };\n        apiData.payload.data.type = `${type}/create`;\n    \n        node.send(apiData);\n        node.status({text: `Creating ${entityId}`});\n    }\n});\n\nnode.done();\n\nfunction toCamelCase(str) {\n    return str.replace(/(?:^\\w|[A-Z]|\\b\\w|\\s+)/g, (match, index) => {\n        if (+match === 0) return '';\n        return index === 0 ? match.toLowerCase() : match.toUpperCase();\n    });\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":224,"y":96,"wires":[["a4a0abd9.7576d8"]]},{"id":"a4a0abd9.7576d8","type":"ha-api","z":"eb756b3f.d770f8","name":"create helper","server":"","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"","dataType":"json","location":"payload","locationType":"msg","responseType":"json","x":390,"y":96,"wires":[["b76d0c56.d54b9"]]},{"id":"388bb5c7.47346a","type":"ha-api","z":"eb756b3f.d770f8","name":"rename helper entity id","server":"","debugenabled":false,"protocol":"websocket","method":"get","path":"","data":"{\t   \"type\":\"config/entity_registry/update\",\t   \"entity_id\": entity.type & \".\" & payload.id,\t   \"new_entity_id\": entity.type & \".\" & entity.id\t}","dataType":"jsonata","location":"payload","locationType":"msg","responseType":"json","x":788,"y":96,"wires":[[]]},{"id":"b76d0c56.d54b9","type":"switch","z":"eb756b3f.d770f8","name":"need to rename?","property":"payload.id","propertyType":"msg","rules":[{"t":"neq","v":"entity.id","vt":"msg"}],"checkall":"true","repair":false,"outputs":1,"x":570,"y":96,"wires":[["388bb5c7.47346a","3e47440a.eee2cc"]]},{"id":"a66d5d93.8a5f","type":"status","z":"eb756b3f.d770f8","name":"","scope":["7f1493fc.1300fc","858d7015.518f4","3e47440a.eee2cc"],"x":124,"y":48,"wires":[[]]},{"id":"3e47440a.eee2cc","type":"function","z":"eb756b3f.d770f8","name":"rename status","func":"const oldEntityId = `${msg.entity.type}.${msg.payload.id}`;\nconst newEntityId = `${msg.entity.type}.${msg.entity.id}`;\n\nnode.status({text: `Renaming ${oldEntityId} to ${newEntityId}`});","outputs":1,"noerr":0,"initialize":"","finalize":"","x":768,"y":144,"wires":[[]]},{"id":"4762791a.4c5408","type":"function","z":"daf25abf.0203b8","name":"date ranges","func":"const now = new Date();\nconst monthDay = getMonthDay(now);\nconst { idLookup, nameLookup } = flow.get(\"wledHolidayLightsPresets\");\nconst christmasPresets = [\n  \"Christmas\",\n  \"Christmas Classic\",\n  \"Christmas Holly Jolly\",\n  \"Christmas Candy Cane\"\n];\nconst states = global.get(\"homeassistant\").homeAssistant.states;\nconst entityId = flow.get(\"wledHolidayLightsEntityId\");\n\nlet updateInterval = 0; // in minutes\nlet preset;\n\nswitch(true) {\n// Winter \n    case isToday(214):\n      preset = \"Valentine\";\n      break;\n// President's Day changes every year, Veteran's Day & Flag Day always the same date\n    case isToday(614): // 6/14 Flag Day\n    case isToday(1111): // 11/11 Veterans Day\n    case isToday(\"President's Day\"):\n    case isToday(\"Memorial Day\"):\n      preset = \"Red, White, and Blue\";\n      break;\n    case isToday(317):\n      preset = \"St Patty\";\n      break;\n    case isToday(\"Easter\"):\n      preset = \"Easter\";\n      break;\n// Spring\n    case isToday(504):\n      preset = \"Starwars\";\n      break;\n    case isToday(505):\n      preset = \"Cinco de Mayo\";\n      break;\n// Summer\n    case isToday(704):\n      preset = \"Independence Day\";\n      break;\n// Autumn\n    case isToday(1017, 1031):\n      preset = \"Halloween\";\n      break;\n    case isToday(\"Thanksgiving Day\"):\n      preset = \"Autumn\";\n      break;\n    case isToday(1231):\n      preset = \"New Years\";\n      break;\n    case isToday(\"Thanksgiving Day\", 1231):\n    case isToday(101, 106):\n      preset = getRandomPreset(christmasPresets);\n      updateInterval = 60;\n      break;\n    default:\n      const seasons = {\n        \"winter\": \"Winter\",\n        \"spring\": \"Spring\",\n        \"summer\": \"Summer\",\n        \"autumn\": \"Autumn\"\n      }\n      const currentSeason = states[\"sensor.season\"].state;\n      preset = seasons[currentSeason];\n      break;\n}\n\nnode.status({ text: preset});\nmsg.payload = nameLookup[preset];\nconst secondOutput = updateInterval ?  { payload: { timeout: updateInterval } } : { reset: true };\n\nreturn [msg, secondOutput];\n\nfunction isToday(start, end) {\n  if (typeof start === \"string\") {\n    start = getHolidayDate(now, start)\n  }\n  if (typeof end === \"string\") {\n    end = getHolidayDate(now, end)\n  }\n  if (end === undefined) return monthDay === start;\n\n  return monthDay >= start && monthDay <= end;\n}\n\nfunction getHolidayDate(date, holiday) {\n    holiday = holiday.toLowerCase();\n    // dates are formatted as month, week, day of the week\n    // names should be lowercase\n    const holidays = {\n        \"martin luther king day\": [0,2,1],\n        \"president's day\": [1,2,1],\n        \"daylight savings time begins\": [2,1,0],\n        \"mother's day\": [4,1,0],\n        \"memorial day\": [4,-1,1],\n        \"father's day\": [5,2,0],\n        \"labor day\": [8,0,1],\n        \"columbus day\": [9,1,1],\n        \"daylight savings time ends\": [10,0,0],\n        \"thanksgiving day\": [10,3,4]\n    };\n    let holidayDate;\n    if(holiday === \"easter\") {\n        holidayDate = getEasterDate(date); \n    } else {\n        const [month, week, day] = holidays[holiday];\n        holidayDate = getDate(date.getFullYear(), month, week, day);\n    }\n    \n    const monthDay = getMonthDay(holidayDate);\n    \n    return monthDay;\n}\n\nfunction getMonthDay(date) {\n    const monthString = String(date.getMonth() + 1).padStart(2, \"0\");\n    const dayString = String(date.getDate()).padStart(2, \"0\");\n    const monthDay = Number(`${monthString}${dayString}`);\n    \n    return monthDay;\n}\n\nfunction getDate(year, month, week, day) {\n    let firstDay = 1;\n    if (week < 0) {\n        month++;\n        firstDay--;\n    }\n    const date = new Date(year, month, (week * 7) + firstDay);\n    if (day < date.getDay()) {\n        day += 7;\n    }\n    date.setDate(date.getDate() - date.getDay() + day);\n    return date;\n}\n\n/**\n * Calculates Easter in the Gregorian/Western (Catholic and Protestant) calendar\n * based on the algorithm by Oudin (1940) from http://www.tondering.dk/claus/cal/easter.php\n * @returns {array} [int month, int day]\n */\nfunction getEasterDate(date) {\n  const year = date.getFullYear();\n  const f = Math.floor,\n    // Golden Number - 1\n    G = year % 19,\n    C = f(year / 100),\n    // related to Epact\n    H = (C - f(C / 4) - f((8 * C + 13) / 25) + 19 * G + 15) % 30,\n    // number of days from 21 March to the Paschal full moon\n    I = H - f(H / 28) * (1 - f(29 / (H + 1)) * f((21 - G) / 11)),\n    // weekday for the Paschal full moon\n    J = (year + f(year / 4) + I + 2 - C + f(C / 4)) % 7,\n    // number of days from 21 March to the Sunday on or before the Paschal full moon\n    L = I - J,\n    month = 3 + f((L + 40) / 44),\n    day = L + 28 - 31 * f(month / 4);\n\n  return new Date(`${month}/${day}/${year}`);\n}\n\nfunction getRandomPreset(list) {\n  const activePreset = states[entityId].attributes.preset;\n  const presetName = idLookup[activePreset];\n  const filteredList = list.filter((e) => e !== presetName);\n  \n  return filteredList[Math.floor(Math.random() * filteredList.length)];\n}\n","outputs":2,"noerr":0,"initialize":"","finalize":"","x":470,"y":272,"wires":[["91b62e9f.fe736","dece0bc.3c215f8"],["dc570e50.22a3a"]]},{"id":"c46d93f8.1fe18","type":"switch","z":"daf25abf.0203b8","name":"dark?","property":"dark","propertyType":"flow","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":306,"y":272,"wires":[["4762791a.4c5408"],["73710f8.1582bf"]]},{"id":"fe4f63d8.5312c","type":"server-state-changed","z":"daf25abf.0203b8","name":"Demo Mode","server":"","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"input_select.holiday_lights_demo_mode","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"off","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"for":"","forType":"num","forUnits":"minutes","ignorePrevStateNull":true,"ignorePrevStateUnknown":true,"ignorePrevStateUnavailable":true,"ignoreCurrentStateUnknown":true,"ignoreCurrentStateUnavailable":true,"x":102,"y":464,"wires":[["1a546dd7.2d8ef2"],["92c007de.c4a168"]]},{"id":"73710f8.1582bf","type":"api-call-service","z":"daf25abf.0203b8","name":"turn off holiday lights","server":"","version":1,"debugenabled":false,"service_domain":"light","service":"turn_off","entityId":"{{flow.wledHolidayLightsEntityId}}","data":"","dataType":"json","mergecontext":"","output_location":"payload","output_location_type":"msg","mustacheAltTags":false,"x":500,"y":320,"wires":[[]]},{"id":"dc570e50.22a3a","type":"ha-wait-until","z":"daf25abf.0203b8","name":"loop","server":"","outputs":2,"entityId":"{{flow.wledHolidayLightsEntityId}}","entityIdFilterType":"exact","property":"state","comparator":"is","value":"off","valueType":"str","timeout":"60","timeoutType":"num","timeoutUnits":"minutes","entityLocation":"","entityLocationType":"none","checkCurrentState":false,"blockInputOverrides":false,"x":658,"y":272,"wires":[[],["4762791a.4c5408"]]},{"id":"f5422b5c.762798","type":"eztimer","z":"daf25abf.0203b8","name":"","debug":false,"autoname":"sunset - 00:00","tag":"test1","suspended":false,"sendEventsOnSuspend":false,"latLongSource":"manual","latLongHaZone":"zone.home","lat":"","lon":"","timerType":"1","startupMessage":true,"ontype":"1","ontimesun":"sunset","ontimetod":"17:00","onpropertytype":"msg","onproperty":"dark","onvaluetype":"bool","onvalue":"true","onoffset":"","onrandomoffset":0,"onsuppressrepeats":false,"offtype":"2","offtimesun":"dusk","offtimetod":"00:00","offduration":0,"offpropertytype":"msg","offproperty":"dark","offvaluetype":"bool","offvalue":"false","offoffset":0,"offrandomoffset":0,"offsuppressrepeats":false,"resend":false,"resendInterval":"1m","mon":true,"tue":true,"wed":true,"thu":true,"fri":true,"sat":true,"sun":true,"x":112,"y":272,"wires":[["c800976d.89af98"]]},{"id":"91b62e9f.fe736","type":"api-call-service","z":"daf25abf.0203b8","name":"turn on holiday lights","server":"","version":1,"debugenabled":false,"service_domain":"wled","service":"preset","entityId":"{{flow.wledHolidayLightsEntityId}}","data":"{\"preset\": payload}","dataType":"jsonata","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":708,"y":224,"wires":[[]]},{"id":"78b7479f.e79dd8","type":"function","z":"daf25abf.0203b8","name":"get preset","func":"// WLED Presets\nconst { nameLookup } = flow.get(\"wledHolidayLightsPresets\");\n\nmsg.payload = nameLookup[msg.payload];\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":668,"y":176,"wires":[["cc0fc459.d7f3a8"]]},{"id":"6a3ef206.84c5fc","type":"api-call-service","z":"daf25abf.0203b8","name":"populate HA dropdown with presets","server":"","version":1,"debugenabled":false,"service_domain":"input_select","service":"set_options","entityId":"input_select.holiday_lights_presets","data":"{\"options\": payload}","dataType":"jsonata","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":748,"y":128,"wires":[["d7d3f0e8.9971d"]]},{"id":"a6b79bf.256d268","type":"inject","z":"daf25abf.0203b8","name":"Setup","props":[{"p":"entityId","v":"light.holiday_lights","vt":"str"},{"p":"WLED_IP","v":"","vt":"str"}],"repeat":"3600","crontab":"","once":true,"onceDelay":0.1,"topic":"","x":108,"y":128,"wires":[["4b308915.771578"]]},{"id":"1538c003.e770a","type":"ha-wait-until","z":"daf25abf.0203b8","name":"loop","server":"","outputs":2,"entityId":"{{flow.wledHolidayLightsEntityId}}","entityIdFilterType":"exact","property":"state","comparator":"is","value":"off","valueType":"str","timeout":"$number($entities(\"input_number.holiday_lights_demo_mode_delay\").state)","timeoutType":"jsonata","timeoutUnits":"seconds","entityLocation":"","entityLocationType":"none","checkCurrentState":false,"blockInputOverrides":true,"x":866,"y":512,"wires":[[],["92c007de.c4a168"]]},{"id":"92c007de.c4a168","type":"switch","z":"daf25abf.0203b8","name":"which mode?","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"cycle presets","vt":"str"},{"t":"eq","v":"cycle effects","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":278,"y":512,"wires":[["29bbe4ad.4de71c"],["7f2dc75.a04cf38"]]},{"id":"422a9fe2.d974c","type":"api-call-service","z":"daf25abf.0203b8","name":"turn on preset","server":"","version":1,"debugenabled":false,"service_domain":"wled","service":"preset","entityId":"{{flow.wledHolidayLightsEntityId}}","data":"{\"preset\": preset}","dataType":"jsonata","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":688,"y":512,"wires":[["1538c003.e770a","e132c6fa.e3da18"]]},{"id":"9755a99c.54ba18","type":"api-call-service","z":"daf25abf.0203b8","name":"turn on effect","server":"","version":1,"debugenabled":false,"service_domain":"light","service":"turn_on","entityId":"{{flow.wledHolidayLightsEntityId}}","data":"{\"effect\": effect}","dataType":"jsonata","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":678,"y":560,"wires":[["1538c003.e770a","e132c6fa.e3da18"]]},{"id":"1a546dd7.2d8ef2","type":"change","z":"daf25abf.0203b8","name":"reset","rules":[{"t":"set","p":"reset","pt":"msg","to":"true","tot":"bool"},{"t":"set","p":"payload","pt":"msg","to":"info","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":258,"y":464,"wires":[["1538c003.e770a","c46d93f8.1fe18"]]},{"id":"71da85fd.de44fc","type":"api-call-service","z":"daf25abf.0203b8","name":"turn off demo mode","server":"","version":1,"debugenabled":false,"service_domain":"input_select","service":"select_option","entityId":"input_select.holiday_lights_demo_mode","data":"{\"option\": \"off\"}","dataType":"json","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":378,"y":608,"wires":[[]]},{"id":"320d2444.2aa86c","type":"server-state-changed","z":"daf25abf.0203b8","name":"Holiday Lights turned off?","server":"","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"light.holiday_lights","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"off","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":false,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":140,"y":608,"wires":[["71da85fd.de44fc"],[]]},{"id":"7f2dc75.a04cf38","type":"function","z":"daf25abf.0203b8","name":"get next effect","func":"const states = global.get(\"homeassistant\").homeAssistant.states;\nconst entityId = flow.get(\"wledHolidayLightsEntityId\");\nconst effects = states[entityId].attributes.effect_list;\nconst activeEffect = states[entityId].attributes.effect;\nlet index = effects.findIndex(ele => ele === activeEffect);\nindex = (index === -1 || index >= effects.length - 1) ? 0 : index + 1;\n\nnode.status({text: effects[index]});\n\nmsg.effect = msg.name = effects[index];\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":480,"y":560,"wires":[["9755a99c.54ba18"]]},{"id":"29bbe4ad.4de71c","type":"function","z":"daf25abf.0203b8","name":"get next preset","func":"const states = global.get(\"homeassistant\").homeAssistant.states;\nconst entityId = flow.get(\"wledHolidayLightsEntityId\");\nconst { idLookup, nameLookup } = flow.get(\"wledHolidayLightsPresets\");\nconst keys = Object.keys(idLookup);\nconst activePreset = states[entityId].attributes.preset;\nlet index = keys.findIndex(ele => ele == activePreset);\nindex = index === -1 || index >= keys.length - 1 ? 0 : index +1;\nconst name = idLookup[keys[index]];\n\nnode.status({text: name });\n\nmsg.preset = keys[index];\nmsg.name = name;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":480,"y":512,"wires":[["422a9fe2.d974c"]]},{"id":"d89db901.9fd588","type":"function","z":"daf25abf.0203b8","name":"process data","func":"if(msg.statusCode !== 200) {\n    node.status({fill: 'red', shape: 'dot', text: `Status Code: ${msg.statusCode}`})\n    return;\n}\n\nconst wledPresets = msg.payload;\nconst idLookup = {};\nconst nameLookup = {};\n\nfor(const key in wledPresets) {\n    if(Object.keys(wledPresets[key]).length > 0) {\n        const name = wledPresets[key].n;\n        idLookup[key] = name;\n        nameLookup[name] = key;\n    }\n}\n\nconst presets = {\n    idLookup,\n    nameLookup,\n    names: Object.values(idLookup).sort()\n};\n\nflow.set(\"wledHolidayLightsPresets\", presets);\nflow.set(\"wledHolidayLightsEntityId\", msg.entityId);\nnode.status({text: `${presets.names.length} Presets Processed`});\n\nmsg.payload = presets.names;\n\nreturn [msg, { payload: \"disable\" }];","outputs":2,"noerr":0,"initialize":"","finalize":"","x":470,"y":128,"wires":[["6a3ef206.84c5fc"],["539eea44.9b6524"]]},{"id":"d7d3f0e8.9971d","type":"change","z":"daf25abf.0203b8","name":"enable","rules":[{"t":"set","p":"payload","pt":"msg","to":"enable","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":962,"y":128,"wires":[["7c6317af.370198"]]},{"id":"539eea44.9b6524","type":"trigger-state","z":"daf25abf.0203b8","name":"Preset Selector","server":"","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityid":"input_select.holiday_lights_presets","entityidfiltertype":"exact","debugenabled":false,"constraints":[{"targetType":"this_entity","targetValue":"","propertyType":"current_state","comparatorType":"is_not","comparatorValueDatatype":"prevEntity","comparatorValue":"state","propertyValue":"new_state.state"}],"outputs":2,"customoutputs":[],"outputinitially":false,"state_type":"str","x":480,"y":176,"wires":[["78b7479f.e79dd8"],[]]},{"id":"e132c6fa.e3da18","type":"ha-entity","z":"daf25abf.0203b8","name":"current demo","server":"","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"Holiday Lights Demo Current"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"name","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","x":886,"y":560,"wires":[[]]},{"id":"a3f0ee9.6fd551","type":"server-events","z":"daf25abf.0203b8","name":"HA Client Events","server":"","event_type":"home_assistant_client","exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"waitForRunning":false,"x":112,"y":176,"wires":[["6c13e445.ca929c"]]},{"id":"6c13e445.ca929c","type":"function","z":"daf25abf.0203b8","name":"HA running?","func":"if(msg.payload !== \"running\") return;\n\nconst { names } = flow.get(\"wledHolidayLightsPresets\");\n\nmsg.payload = names;\n\nreturn [msg, { payload: \"disable\" }];","outputs":2,"noerr":0,"initialize":"","finalize":"","x":278,"y":176,"wires":[["6a3ef206.84c5fc"],["539eea44.9b6524"]]},{"id":"afc3f7e9.88ac98","type":"comment","z":"daf25abf.0203b8","name":"Setup","info":"","x":82,"y":32,"wires":[]},{"id":"5b75e78b.d9d608","type":"comment","z":"daf25abf.0203b8","name":"Scheduler","info":"","x":92,"y":224,"wires":[]},{"id":"cc0fc459.d7f3a8","type":"link out","z":"daf25abf.0203b8","name":"turn on holiday lights","links":["d830a00d.1d744"],"x":852,"y":176,"wires":[],"l":true},{"id":"d830a00d.1d744","type":"link in","z":"daf25abf.0203b8","name":"Holiday Lights On","links":["cc0fc459.d7f3a8"],"x":527,"y":224,"wires":[["91b62e9f.fe736"]]},{"id":"215b93f8.a1883c","type":"comment","z":"daf25abf.0203b8","name":"Demo Mode","info":"","x":102,"y":416,"wires":[]},{"id":"4b308915.771578","type":"http request","z":"daf25abf.0203b8","name":"get presets","method":"GET","ret":"obj","paytoqs":"ignore","url":"http://{{WLED_IP}}/presets.json","tls":"","persist":false,"proxy":"","authType":"","x":278,"y":128,"wires":[["d89db901.9fd588"]]},{"id":"7c6317af.370198","type":"delay","z":"daf25abf.0203b8","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":1100,"y":128,"wires":[["539eea44.9b6524"]]},{"id":"aea9da7f.af56a8","type":"ha-entity","z":"daf25abf.0203b8","name":"current preset","server":"","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":"Holiday Lights Current Preset"},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"","outputLocationType":"none","inputOverride":"allow","x":480,"y":368,"wires":[[]]},{"id":"4b02330f.bc08fc","type":"function","z":"daf25abf.0203b8","name":"format","func":"const { idLookup } = flow.get(\"wledHolidayLightsPresets\");\nconst id = msg.data.new_state.attributes.preset || 0;\nconst presetName = idLookup[id] || \"none\";\n\nmsg.payload = msg.payload === \"on\" ? presetName : \"none\";\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":258,"y":368,"wires":[["aea9da7f.af56a8"]]},{"id":"a3f37acd.911a88","type":"server-state-changed","z":"daf25abf.0203b8","name":"Preset Updated","server":"","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"light.holiday_lights","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":false,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":112,"y":368,"wires":[["4b02330f.bc08fc"]]},{"id":"dece0bc.3c215f8","type":"debug","z":"daf25abf.0203b8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":752,"y":336,"wires":[]},{"id":"c800976d.89af98","type":"change","z":"daf25abf.0203b8","name":"","rules":[{"t":"set","p":"dark","pt":"flow","to":"dark","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":223,"y":272,"wires":[["c46d93f8.1fe18"]],"l":false},{"id":"b1ec00ad.8c8f7","type":"subflow:eb756b3f.d770f8","z":"daf25abf.0203b8","name":"","env":[{"name":"helpers","value":"[{\"id\":\"holiday_lights_demo_mode_delay\",\"type\":\"input_number\",\"name\":\"Cycle Delay\",\"initial\":30,\"min\":-5,\"max\":300,\"step\":5,\"icon\":\"mdi:timer-sand\"},{\"id\":\"holiday_lights_demo_mode\",\"type\":\"input_select\",\"name\":\"Mode\",\"options\":[\"off\",\"cycle effects\",\"cycle presets\"],\"initial\":\"off\",\"icon\":\"mdi:animation\"},{\"id\":\"holiday_lights_presets\",\"type\":\"input_select\",\"name\":\"Presets\",\"options\":[\"none\"],\"icon\":\"mdi:animation\"}]","type":"json"}],"x":346,"y":80,"wires":[]},{"id":"297d2a12.388f86","type":"inject","z":"daf25abf.0203b8","name":"Create HA elements","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":138,"y":80,"wires":[["b1ec00ad.8c8f7"]]}]

Requirements

Integrations

The season integration is used to determine the current season and set a WLEDopen in new window preset if no other holiday range is defined.

The two entity nodes will require the Node-RED custom integration but the flow will function fine without it. The UI just won't have the updated current preset and demo effect displayed.

Nodes

Setup

The flow has been modified so that it should run pretty much out of the box after changing the entity id for the light and IP for the WLED controller.

  1. Setting up WLED variables
  2. Modifing holiday date ranges
  3. Creating Home Assistant entities

WLED variables

screenshot of a node that needs to be editted

Edit the change node, set entity ids here, to edit the Home Assistant entity id of the holiday lights and the select entity for presets. The entity of your WLED light needs to be changed in two places.

Setting date ranges up

Holidays can be either a single day or a range of dates. If no holiday is defined for the current day it will default to the season preset WINTER, SPRING, SUMMER, and AUTUMN.

screenshot of a node that needs to be editted

In the date ranges node, a lot is going on but the main thing is the switch statement that determines which preset to use.

The isToday function can take two arguments but only the first one is required which is the start date of the holiday. The second argument is the end date of the holiday. Both of the arguments can take a number in the format of MONTHDAY or a holiday name defined in the getHolidayDate function on line #92.

Lines 16 - 72

switch (true) {
  // Winter
  case isToday(214):
    preset = "Valentine";
    break;
  // President's Day changes every year, Veteran's Day & Flag Day always the same date
  case isToday(614): // 6/14 Flag Day
  case isToday(1111): // 11/11 Veterans Day
  case isToday("President's Day"):
  case isToday("Memorial Day"):
    preset = "Red, White, and Blue";
    break;
  case isToday(317):
    preset = "St Patty";
    break;
  case isToday("Easter"):
    preset = "Easter";
    break;
  // Spring
  case isToday(504):
    preset = "Starwars";
    break;
  case isToday(505):
    preset = "Cinco de Mayo";
    break;
  // Summer
  case isToday(704):
    preset = "Independence Day";
    break;
  // Autumn
  case isToday(1017, 1031):
    preset = "Halloween";
    break;
  case isToday("Thanksgiving Day"):
    preset = "Autumn";
    break;
  case isToday(1231):
    preset = "New Years";
    break;
  case isToday("Thanksgiving Day", 1231):
  case isToday(101, 106):
    const activePreset = states[entityId].attributes.preset;
    const presetName = idLookup[activePreset];
    preset = getRandomPreset(christmasPresets.filter((e) => e !== presetName));
    updateInterval = 60;
    break;
  default:
    const seasons = {
      winter: "Winter",
      spring: "Spring",
      summer: "Summer",
      autumn: "Autumn",
    };
    const currentSeason = states["sensor.season"].state;
    preset = seasons[currentSeason];
    break;
}

Home Assistant Entities

Three entities will need to be created in Home Assistant two input_selectopen in new window and an input_numberopen in new window. The helpers configuration, Configuration > Helpers, can be used or writing them in YAML.

input_number.holiday_lights_demo_mode_delay

input_number:
  holiday_lights_demo_mode_delay:
    name: Cycle Delay
    initial: 30
    min: -5
    max: 300
    step: 5
    icon: "mdi:timer-sand"

screenshot of lovelace ui

input_select.holiday_lights_demo_mode

input_select:
  holiday_lights_demo_mode:
    name: Mode
    options:
      - off
      - cycle effects
      - cycle presets
    initial: off
    icon: "mdi:animation"

screenshot of lovelace ui

Lovelace UI

screenshot of lovelace ui

title: Holiday Lights
type: entities
entities:
  - entity: light.holiday_lights
  - entity: select.holiday_lights_presets
  - type: section
    label: Demo Mode
  - type: conditional
    conditions:
      - entity: input_select.holiday_lights_demo_mode
        state_not: "off"
    row:
      entity: sensor.holiday_lights_demo_current
  - entity: input_select.holiday_lights_demo_mode
  - entity: input_number.holiday_lights_demo_mode_delay

Things that could have been added but never got around to it

  • Previous and Next button in the demo UI