uid: absorb-it:blockly:nspanel_callback tags: [] props: parameters: [] parameterGroups: [] timestamp: Nov 30, 2023, 8:47:32 PM component: BlockLibrary config: name: NSPanel Callback slots: blocks: - component: BlockType config: args0: - type: input_dummy align: CENTER - check: oh_item name: TARGET required: true type: input_value - check: Number name: ACTIVE_BRIGHTNESS type: input_value - type: input_dummy args1: - type: input_dummy align: CENTER - check: Number name: TIMEOUT type: input_value - check: Number name: SCREENSAVER_BRIGHTNESS type: input_value - name: SCREENSAVER2 type: field_checkbox - type: input_dummy - type: input_dummy - check: String name: SCREENSAVER_SCRIPT_ID type: input_value - type: input_dummy args2: - align: CENTER type: input_dummy - check: String name: STARTUP_SCRIPT_ID type: input_value - type: input_dummy - name: STARTUP_ACTION type: input_statement - name: STARTUP_REDO type: field_checkbox - type: input_dummy - type: input_dummy colour: 90 helpUrl: https://github.com/absorb-it/openhab-blockly-nspanel/blob/main/docs/blockLibrary_nspanel_callback_callback.md message0: > NSPANEL CALLBACK %1 Display on %2 Initial Display Brightness (0 - 100) %3 %4 message1: > INITIAL SCREENSAVER SETTINGS %1 Timeout (0 = disabled) %2 Brightness (0 - 100) %3 Use Complex Screensaver %4 %5 Call Script after Screensaver shown %6 (i.e. for WeatherUpdate, enter Id) %7 %8 message2: > STARTUP %1 Show Page / Call Script (enter Id) %2 %5 run this Script always when leaving Screensaver %6 %7 Run Startup Statement %3 %4 nextStatement: "" previousStatement: "" tooltip: "Registers and maintains all external and internal requests to some NSPanel Item. Stores basic setings and forwards information like the NSPanel Item as context to called scripts. Some settings like screensaver brightness and timing are only used during startup and can only be modified trough other helpers." type: absorb_it_nspanel_callback slots: code: - component: BlockCodeTemplate config: template: >- {{utility:absorb_it_nspanel_callback}}({{input:TARGET}}, {{input:TIMEOUT}}, {{input:SCREENSAVER_BRIGHTNESS}}, {{input:ACTIVE_BRIGHTNESS}}, '{{field:SCREENSAVER2}}', {{input:SCREENSAVER_SCRIPT_ID}}, {{input:STARTUP_SCRIPT_ID}}, `{{statements:STARTUP_ACTION}}`, '{{field:STARTUP_REDO}}'); toolbox: - component: PresetInput config: name: TARGET shadow: true type: oh_item - component: PresetInput config: fields: NUM: 20 name: TIMEOUT required: false shadow: true type: math_number - component: PresetInput config: fields: NUM: 50 name: SCREENSAVER_BRIGHTNESS required: false shadow: true type: math_number - component: PresetInput config: fields: NUM: 100 name: ACTIVE_BRIGHTNESS required: false shadow: true type: math_number - component: PresetInput config: fields: TEXT: Weather/IconUpdate Script Id name: SCREENSAVER_SCRIPT_ID required: false shadow: true type: text - component: PresetInput config: fields: TEXT: Script Id name: STARTUP_SCRIPT_ID required: false shadow: true type: text - component: BlockType config: args0: - name: TARGET required: true type: input_value check: oh_item args1: - name: TIME_FORMAT type: input_value check: String - name: DATE_FORMAT type: input_value check: String colour: 90 helpUrl: "https://github.com/absorb-it/openhab-blockly-nspanel/blob/main/docs/blockLibrary_nspanel_callback_timeUpdate.md" message0: > NSPANEL TIME UPDATE for %1 message1: > Time Format %1 Date Format %2 nextStatement: "" previousStatement: "" tooltip: "This updates the time on some NSPanel - there is no local clock in these devices." type: absorb_it_nspanel_timeUpdate inputsInline: false slots: code: - component: BlockCodeTemplate config: template: >- {{utility:absorb_it_nspanel_timeUpdate}}({{input:TARGET}}, {{input:TIME_FORMAT}}, {{input:DATE_FORMAT}}); toolbox: - component: PresetInput config: name: TARGET shadow: true type: oh_item - component: PresetInput config: fields: TEXT: HH:mm name: TIME_FORMAT required: false shadow: true type: text - component: PresetInput config: fields: TEXT: dd-MM-yyyy name: DATE_FORMAT required: false shadow: true type: text utilities: - component: UtilityFunction config: code: >- function {{name}}() { return 'absorb_it_nspanel_refreshTimer_' + ctx.ruleUID; } name: getTimerName - component: UtilityFunction config: code: >- function {{name}}() { if (cache.shared.exists({{getTimerName}}())) { console.debug("remove existing refresh timer " + {{getTimerName}}()); cache.shared.remove({{getTimerName}}()).cancel(); } } name: absorb_it_nspanel_callback_killRefreshTimer - component: UtilityFunction config: code: >- function {{name}}(scriptName, context, updateCacheLink) { const RuleManager = osgi.getService('org.openhab.core.automation.RuleManager'); var thread = Java.type('java.lang.Thread'); if (!scriptName || scriptName === "Script Id") return 0; context["timerName"] = {{getTimerName}}(); try { let timer = 10; while (RuleManager.getStatus(scriptName) && RuleManager.getStatus(scriptName).toString() === "RUNNING") { if (!timer--) { console.log("finished waiting for rule " + scriptName); break; } console.log("waiting 1 sec. for running rule " + scriptName); thread.sleep(1000); } rules.runRule(scriptName, context); cache.private.put('lastPage', scriptName); // any last page if (updateCacheLink) cache.private.put('currentCard', scriptName); // only some last pages (excluding popupNotify, Screensaver) return 1; } catch (e) { console.error("Error", e.stack); console.log("Error", e.toString()); console.log("can't start/find rule with id '" + scriptName + "'"); } return 0; } name: absorb_it_nspanel_callback_startScript - component: UtilityFunction config: code: >- function {{name}}(command, returnValue = '') { // evaluate command, use returnValue as context let result = ""; if (command) { try { result = eval(command); } catch (e) { console.error("Error", e.stack); console.log("Error", e.toString()); console.log("eval failed: '" + command + "'"); } } return result; } name: absorb_it_nspanel_callback_safeEval - component: UtilityFunction config: code: >- function {{name}}(param, noOfItems) { let result = String(param); if (result.charAt(0) != '~') result = '~' + result; while ((result.match(/~/g) || []).length < noOfItems) result += '~' while ((result.match(/~/g) || []).length > noOfItems) result = result.replace(/~/, ''); return result; } name: absorb_it_nspanel_callback_checkParam - component: UtilityFunction config: code: >- function {{name}}(target, timeout, screenSaverBrightness, activeBrightness, screensaver2, screensaverStartupScript, startupScript, startupAction, startupRedo) { // on first run only... if (!cache.private.exists('initialized')) { // allow refreshing of dimming and timeout when saving configured callback items.getItem(target).sendCommand( "timeout" + {{absorb_it_nspanel_callback_checkParam}}(timeout, 1) ); items.getItem(target).sendCommand( "dimmode" + {{absorb_it_nspanel_callback_checkParam}}(screenSaverBrightness, 1) + {{absorb_it_nspanel_callback_checkParam}}(activeBrightness, 1) ); // store settings from blockly interface to be able to change them on-the-fly cache.private.put('complexScreenSaver', screensaver2); cache.private.put('enterScriptName', screensaverStartupScript); cache.private.put('leaveScriptName', startupScript); cache.private.put('startupRedo', startupRedo); // done cache.private.put('initialized', 1); }; try { contextValues = (JSON.parse(event.itemState))["CustomRecv"].split(','); } catch { if (event.itemState) { // if callback received non-JSON string check for local requests let callbackParam = event.itemState.toString().split('?'); switch (callbackParam[0]) { case "refresh": case "ON": // triggered by Hardware Button press (if rule configured to fire) case "OFF": // triggered by Hardware Button press (if rule configured to fire) if (cache.private.get('currentCard')) { let context = { "previousPage": cache.private.get('lastPage'), "target": target }; if (cache.private.exists('DetailPage')) { context["request"] = "pageOpenDetail"; context["item"] = cache.private.get('DetailPage'); }; {{absorb_it_nspanel_callback_startScript}}( cache.private.get('currentCard'), context, 0 ); } return; case "refreshTimer": if (cache.private.get('currentCard') && (cache.private.get('lastPage') !== "screensaver")) {{absorb_it_nspanel_callback_startScript}}( cache.private.get('currentCard'), { "previousPage": cache.private.get('lastPage'), "target": target }, 0 ); return; case "loadPage": if (callbackParam[1]) contextValues = [ , "buttonPress2", callbackParam[1] ]; break; case "loadScreensaver": contextValues = [ , "sleepReached" ]; break; case "newTimeout": if (callbackParam[1]) items.getItem(target).sendCommand( "timeout" + {{absorb_it_nspanel_callback_checkParam}}(callbackParam[1], 1) ); return; case "newBrightness": case "newBrigthness": // keep old value, type of parameter was fixed in 0.6 if (callbackParam[1] && callbackParam[2]) items.getItem(target).sendCommand( "dimmode" + {{absorb_it_nspanel_callback_checkParam}}(callbackParam[1], 1) + {{absorb_it_nspanel_callback_checkParam}}(callbackParam[2], 1) ); return; case "complexScreenSaver": { cache.private.put('complexScreenSaver', callbackParam[1]); return; } case "enterScriptName": { cache.private.put('enterScriptName', callbackParam[1]); return; } case "leaveScriptName": { cache.private.put('leaveScriptName', callbackParam[1]); if (callbackParam[1]) cache.private.put('startupRedo', "TRUE"); else cache.private.put('startupRedo', "FALSE"); return; } default: return; } } } switch(contextValues[1]) { case "startup": cache.private.put('screensaverActive', "FALSE"); items.getItem(target).sendCommand( "timeout" + {{absorb_it_nspanel_callback_checkParam}}(timeout, 1) ); items.getItem(target).sendCommand( "dimmode" + {{absorb_it_nspanel_callback_checkParam}}(screenSaverBrightness, 1) + {{absorb_it_nspanel_callback_checkParam}}(activeBrightness, 1) ); let success = {{absorb_it_nspanel_callback_startScript}}( cache.private.get('leaveScriptName'), { "target": target }, 1 ); {{absorb_it_nspanel_callback_safeEval}}(startupAction); if (success || startupAction != '') break; case "sleepReached": {{absorb_it_nspanel_callback_killRefreshTimer}}; cache.private.put('screensaverActive', "TRUE"); if (cache.private.get('complexScreenSaver') == "TRUE") items.getItem(target).sendCommand("pageType~screensaver2"); else items.getItem(target).sendCommand("pageType~screensaver"); {{absorb_it_nspanel_callback_startScript}}( cache.private.get('enterScriptName'), { "target": target }, 0 ); cache.private.put('lastPage', 'screensaver'); // therefore ignore timer updates during screensaver break; case "buttonPress2": if (cache.private.get('screensaverActive') == "TRUE") { cache.private.put('screensaverActive', "FALSE"); if (cache.private.get('startupRedo') == "TRUE") cache.private.remove('currentCard'); } switch(contextValues[3]) { case "bExit": cache.private.remove('DetailPage'); if (cache.private.get('currentCard')) {{absorb_it_nspanel_callback_startScript}}( cache.private.get('currentCard'), { "target": target }, 0 ); else {{absorb_it_nspanel_callback_startScript}}( cache.private.get('leaveScriptName'), { "target": target }, 1 ); break; default: if (contextValues[2].split('?')[0] !== cache.private.get('lastPage')) { // new page called, probably clicked on navigation buttons {{absorb_it_nspanel_callback_startScript}}( contextValues[2].split('?')[0], { "target": target }, (contextValues[2] !== "popupNotify" && contextValues[3] !== "notifyAction") ); } // find the clicked item, if there hade been multiple on the page let item = ""; // alarmButtons: third context field contains 'cb' and item id, reparated by '?' // {"CustomRecv":"event,buttonPress2,*cardAlarmScriptName*,item?1,1"} if (contextValues[3] && contextValues[3].split('?')[0] === "item") item = contextValues[3].split('?')[1]; // HVACButtons: third context field contains script name and item id, reparated by '?' // {"CustomRecv":"event,buttonPress2,*cardThermoScriptName*,hvac_action,item?1"} if (contextValues[4] && contextValues[4].split('?')[0] === "item") item = contextValues[4].split('?')[1]; // entities: third context field contains both script name and item id, reparated by '?' // {"CustomRecv":"event,buttonPress2,*cardEntitiesScriptName*?2,OnOff,0"} else item = contextValues[2].split('?')[1]; {{absorb_it_nspanel_callback_startScript}}( contextValues[2].split('?')[0], { "request": cache.private.exists('DetailPage')?"pageOpenDetail":"pageUpdate", "item": item, "trigger": contextValues[3], "newState": contextValues[4], "previousPage": cache.private.get('lastPage'), "target": target }, (contextValues[2] !== "popupNotify" && contextValues[3] !== "notifyAction") ); } break; case "pageOpenDetail": let item = contextValues[3].split('?')[1]; cache.private.put('DetailPage', item?item:0); {{absorb_it_nspanel_callback_startScript}}( contextValues[3].split('?')[0], { "request": "pageOpenDetail", "item": item, "trigger": contextValues[2], "target": target }, 1 ); break; default: } } name: absorb_it_nspanel_callback - component: UtilityFunction config: code: >- function {{name}}(target, patternTime = 'HH:mm', patternDate = 'dd-MM-yyyy') { let timeString = String((time.ZonedDateTime.now()).format(time.DateTimeFormatter.ofPattern(patternTime))); let dateString = String((time.ZonedDateTime.now()).format(time.DateTimeFormatter.ofPattern(patternDate))); items.getItem(target).sendCommand('time~' + timeString); items.getItem(target).sendCommand('date~' + dateString); } name: absorb_it_nspanel_timeUpdate