From e660ca3113321220cedd11b07efe070beed7072b Mon Sep 17 00:00:00 2001 From: Tom Hirschberger Date: Thu, 6 Jan 2022 09:41:00 +0100 Subject: [PATCH 1/8] the "forced" option in the payload now either can be set as boolean or string; this change was neccessary because MMM-RemoteControl sends notifications with string content --- MMM-Screen-Powersave-Notification.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MMM-Screen-Powersave-Notification.js b/MMM-Screen-Powersave-Notification.js index d11c87c..3289615 100644 --- a/MMM-Screen-Powersave-Notification.js +++ b/MMM-Screen-Powersave-Notification.js @@ -167,6 +167,19 @@ Module.register('MMM-Screen-Powersave-Notification', { (notification === 'SCREEN_OFF') || (notification === 'SCREEN_POWERSAVE') ) { + if ( + (typeof payload !== 'undefined') && + (typeof payload.forced !== 'undefined') + ){ + if ( + (payload.forced === true) || + (payload.forced.toString().toLowerCase() === "true") + ){ + payload.forced = true + } else { + payload.forced = false + } + } this.sendSocketNotification(notification, payload) } else if (notification === 'CHANGED_PROFILE'){ From 3bbbb32f086d522f9f485bccd33fb8d7d0cf81d7 Mon Sep 17 00:00:00 2001 From: Tom Hirschberger Date: Fri, 7 Jan 2022 16:13:45 +0100 Subject: [PATCH 2/8] will disable screensave mode now if USER_PRESENCE notifcation is received; no need to send SCREEN_ON (forced: false) in addtion to USER_PRESENCE now --- node_helper.js | 1 + 1 file changed, 1 insertion(+) diff --git a/node_helper.js b/node_helper.js index 003172a..53e989e 100644 --- a/node_helper.js +++ b/node_helper.js @@ -188,6 +188,7 @@ module.exports = NodeHelper.create({ this.hiddenModules = payload } else if (notification === 'USER_PRESENCE') { if (payload && ((payload === true) || (payload==="true"))){ + self.turnScreenOn(false) self.clearAndSetScreenTimeout(true) } } else if (notification === 'SCREEN_TOGGLE') { From b58631024ae3e61b55233fb5a56e147204c2c60d Mon Sep 17 00:00:00 2001 From: Tom Hirschberger Date: Sat, 8 Jan 2022 21:51:30 +0100 Subject: [PATCH 3/8] added a script to control the display by tvservice instead of vcgencmd --- exampleScripts/control_display | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 exampleScripts/control_display diff --git a/exampleScripts/control_display b/exampleScripts/control_display new file mode 100755 index 0000000..5dadafd --- /dev/null +++ b/exampleScripts/control_display @@ -0,0 +1,21 @@ +#!/bin/bash + +if [ "$1" == "on" ] +then + tvservice -p > /dev/null 2>&1 + sudo chvt 1 > /dev/null 2>&1 + sudo chvt 7 > /dev/null 2>&1 + echo "display_power=1" +elif [ "$1" == "off" ] +then + tvservice -o > /dev/null 2>&1 + echo "display_power=0" +else + RESULT=`tvservice -s 2>&1 | grep -c "off"` + if [ $RESULT -lt 1 ] + then + echo "display_power=1" + else + echo "display_power=0" + fi +fi From 962f36e4c3470da20ba5d0c6f231c3f1a3b538d0 Mon Sep 17 00:00:00 2001 From: Tom Hirschberger Date: Sat, 8 Jan 2022 22:00:39 +0100 Subject: [PATCH 4/8] rename example script to make the use of tvservice clear --- exampleScripts/tvservice_control | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 exampleScripts/tvservice_control diff --git a/exampleScripts/tvservice_control b/exampleScripts/tvservice_control new file mode 100755 index 0000000..5dadafd --- /dev/null +++ b/exampleScripts/tvservice_control @@ -0,0 +1,21 @@ +#!/bin/bash + +if [ "$1" == "on" ] +then + tvservice -p > /dev/null 2>&1 + sudo chvt 1 > /dev/null 2>&1 + sudo chvt 7 > /dev/null 2>&1 + echo "display_power=1" +elif [ "$1" == "off" ] +then + tvservice -o > /dev/null 2>&1 + echo "display_power=0" +else + RESULT=`tvservice -s 2>&1 | grep -c "off"` + if [ $RESULT -lt 1 ] + then + echo "display_power=1" + else + echo "display_power=0" + fi +fi From 0378a5a0921b532642aaad931a322f316ecf627e Mon Sep 17 00:00:00 2001 From: Tom Hirschberger Date: Wed, 12 Jan 2022 06:52:01 +0100 Subject: [PATCH 5/8] only reset the screensave timeout on USER_PRESENCE if the screen is on now; renamed example script; --- exampleScripts/control_display | 21 --------------------- node_helper.js | 17 ++++++++++------- 2 files changed, 10 insertions(+), 28 deletions(-) delete mode 100755 exampleScripts/control_display diff --git a/exampleScripts/control_display b/exampleScripts/control_display deleted file mode 100755 index 5dadafd..0000000 --- a/exampleScripts/control_display +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -if [ "$1" == "on" ] -then - tvservice -p > /dev/null 2>&1 - sudo chvt 1 > /dev/null 2>&1 - sudo chvt 7 > /dev/null 2>&1 - echo "display_power=1" -elif [ "$1" == "off" ] -then - tvservice -o > /dev/null 2>&1 - echo "display_power=0" -else - RESULT=`tvservice -s 2>&1 | grep -c "off"` - if [ $RESULT -lt 1 ] - then - echo "display_power=1" - else - echo "display_power=0" - fi -fi diff --git a/node_helper.js b/node_helper.js index 53e989e..f7c4576 100644 --- a/node_helper.js +++ b/node_helper.js @@ -49,12 +49,11 @@ module.exports = NodeHelper.create({ turnScreenOff: async function (forced) { const self = this + if (self.config.changeToProfileBeforeAction !== null) { + self.sendSocketNotification("CURRENT_PROFILE", self.config.changeToProfileBeforeAction) + await self.Sleep(500) + } if (self.isScreenOn()){ - if (self.config.changeToProfileBeforeAction !== null) { - self.sendSocketNotification("CURRENT_PROFILE", self.config.changeToProfileBeforeAction) - await self.Sleep(500) - } - if (forced === true) { console.log(self.name + ': Turning screen off (forced)!') self.forcedDown = true @@ -188,8 +187,12 @@ module.exports = NodeHelper.create({ this.hiddenModules = payload } else if (notification === 'USER_PRESENCE') { if (payload && ((payload === true) || (payload==="true"))){ - self.turnScreenOn(false) - self.clearAndSetScreenTimeout(true) + self.turnScreenOn(false) + if (self.isScreenOn()){ + self.clearAndSetScreenTimeout(true) + } else { + self.clearAndSetScreenTimeout(false,false) + } } } else if (notification === 'SCREEN_TOGGLE') { var forced = payload.forced === true ? payload.forced : false From 32bc22160591361632e6bd758fe1169da6a56b61 Mon Sep 17 00:00:00 2001 From: Tom Hirschberger Date: Fri, 14 Jan 2022 16:16:37 +0100 Subject: [PATCH 6/8] fixed reset of screen timeout if in "hideInsteadOfShutoff" mode --- .eslintrc.json | 26 +-- MMM-Screen-Powersave-Notification.js | 251 ++++++++++++++------------- README.md | 64 +++---- node_helper.js | 19 +- screen-powersave.css | 4 +- 5 files changed, 193 insertions(+), 171 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 46c19d4..61ed338 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,14 +1,14 @@ { - "extends": "airbnb-base", - "globals": { - "Module": true, - "Log": true, - "MM": true - }, - "rules": { - "comma-dangle": "off", - "object-shorthand": "off", - "func-names": "off", - "space-before-function-paren": "off" - } -} \ No newline at end of file + "extends": "airbnb-base", + "globals": { + "Module": true, + "Log": true, + "MM": true + }, + "rules": { + "comma-dangle": "off", + "object-shorthand": "off", + "func-names": "off", + "space-before-function-paren": "off" + } +} diff --git a/MMM-Screen-Powersave-Notification.js b/MMM-Screen-Powersave-Notification.js index 3289615..747e6a4 100644 --- a/MMM-Screen-Powersave-Notification.js +++ b/MMM-Screen-Powersave-Notification.js @@ -6,82 +6,90 @@ * By Tom Hirschberger * MIT Licensed. */ -Module.register('MMM-Screen-Powersave-Notification', { - +Module.register("MMM-Screen-Powersave-Notification", { defaults: { delay: 60, profiles: {}, - screenOnCommand: '/usr/bin/vcgencmd display_power 1', - screenOffCommand: '/usr/bin/vcgencmd display_power 0', - screenStatusCommand: '/usr/bin/vcgencmd display_power', + screenOnCommand: "/usr/bin/vcgencmd display_power 1", + screenOffCommand: "/usr/bin/vcgencmd display_power 0", + screenStatusCommand: "/usr/bin/vcgencmd display_power", turnScreenOnIfProfileDelayIsSet: true, - countDownText: 'Display powersave: ', - disabledText: 'disabled', + countDownText: "Display powersave: ", + disabledText: "disabled", countDownUpdateInterval: 5000, displayHours: false, - animationSpeed : 0, + animationSpeed: 0, hideInsteadShutoff: false, - changeToProfile : null, + changeToProfile: null, hideAnimationSpeed: 500, changeToProfileBeforeAction: null }, - getStyles: function() { - return ['screen-powersave.css'] + getStyles: function () { + return ["screen-powersave.css"]; }, - getScripts: function() { - return ['moment.js'] + getScripts: function () { + return ["moment.js"]; }, - getTimeString: function(seconds) { - var remainingSeconds = seconds - if(this.config.displayHours){ - remainingSeconds = seconds % 3600 - hours = Math.round((seconds - remainingSeconds)/ 3600) + getTimeString: function (seconds) { + var remainingSeconds = seconds; + if (this.config.displayHours) { + remainingSeconds = seconds % 3600; + hours = Math.round((seconds - remainingSeconds) / 3600); } - remainingSeconds2 = remainingSeconds % 60 - var minutes = Math.round((remainingSeconds - remainingSeconds2) / 60) - - - if(this.config.displayHours){ - return (""+hours).padStart(2,'0')+":"+(""+minutes).padStart(2,'0')+":"+(""+remainingSeconds2).padStart(2,'0') + remainingSeconds2 = remainingSeconds % 60; + var minutes = Math.round((remainingSeconds - remainingSeconds2) / 60); + + if (this.config.displayHours) { + return ( + ("" + hours).padStart(2, "0") + + ":" + + ("" + minutes).padStart(2, "0") + + ":" + + ("" + remainingSeconds2).padStart(2, "0") + ); } else { - return (""+minutes).padStart(2,'0')+":"+(""+remainingSeconds2).padStart(2,'0') + return ( + ("" + minutes).padStart(2, "0") + + ":" + + ("" + remainingSeconds2).padStart(2, "0") + ); } }, - getDom: function() { - clearTimeout(this.currentDelayTimer) - - const wrapper = document.createElement('div') + getDom: function () { + clearTimeout(this.currentDelayTimer); - if ((!this.delayDisabled) || (this.config.disabledText !== null)){ - const textWrapper = document.createElement('span') - textWrapper.className = 'textWrapper' + const wrapper = document.createElement("div"); - textWrapper.innerHTML=this.config.countDownText - wrapper.appendChild(textWrapper) + if (!this.delayDisabled || this.config.disabledText !== null) { + const textWrapper = document.createElement("span"); + textWrapper.className = "textWrapper"; + textWrapper.innerHTML = this.config.countDownText; + wrapper.appendChild(textWrapper); - const valueWrapper = document.createElement('span') - valueWrapper.className = 'valueWrapper' + const valueWrapper = document.createElement("span"); + valueWrapper.className = "valueWrapper"; - if(this.delayDisabled){ - valueWrapper.innerHTML = this.config.disabledText - } else { - //valueWrapper.innerHTML = moment("1900-01-01 00:00:00").add(this.currentDelay, 'seconds').format(this.config.countDownFormatString) - valueWrapper.innerHTML = this.getTimeString(this.currentDelay) - } + if (this.delayDisabled) { + valueWrapper.innerHTML = this.config.disabledText; + } else { + //valueWrapper.innerHTML = moment("1900-01-01 00:00:00").add(this.currentDelay, 'seconds').format(this.config.countDownFormatString) + valueWrapper.innerHTML = this.getTimeString(this.currentDelay); + } - wrapper.appendChild(valueWrapper) + wrapper.appendChild(valueWrapper); - const self = this - self.currentDelayTimer = setTimeout(function() { - self.currentDelay = self.currentDelay - (self.config.countDownUpdateInterval/1000); - self.updateDom(self.config.animationSpeed) - }, self.config.countDownUpdateInterval); + const self = this; + self.currentDelayTimer = setTimeout(function () { + self.currentDelay = + self.currentDelay - self.config.countDownUpdateInterval / 1000; + self.updateDom(self.config.animationSpeed); + }, self.config.countDownUpdateInterval); } return wrapper; @@ -89,108 +97,111 @@ Module.register('MMM-Screen-Powersave-Notification', { start: function () { Log.info("Starting module: " + this.name); - this.sendSocketNotification('CONFIG', this.config) - this.currentDelay = this.config.delay - this.delayDisabled = false - this.hiddenModules = null - this.profileHistory = [] + this.sendSocketNotification("CONFIG", this.config); + this.currentDelay = this.config.delay; + this.delayDisabled = false; + this.hiddenModules = null; + this.profileHistory = []; }, - hideModules: function(){ - const self = this - self.hiddenModules = [] - var allModules = MM.getModules() - allModules.enumerate(function(curModule){ - if (!curModule.hidden){ - var callback = function(){} - var options = {lockString: self.identifier} - self.hiddenModules.push(curModule) - curModule.hide(self.config.hideAnimationSpeed,callback,options) + hideModules: function () { + const self = this; + self.hiddenModules = []; + var allModules = MM.getModules(); + allModules.enumerate(function (curModule) { + if (!curModule.hidden) { + var callback = function () {}; + var options = { lockString: self.identifier }; + self.hiddenModules.push(curModule); + curModule.hide(self.config.hideAnimationSpeed, callback, options); } - }) + }); }, - showModules: function(){ - const self = this - if(self.hiddenModules){ - for(var curModule in self.hiddenModules){ - var callback = function(){} - var options = {lockString:self.identifier} - self.hiddenModules[curModule].show(self.config.hideAnimationSpeed, callback, options) + showModules: function () { + const self = this; + if (self.hiddenModules) { + for (var curModule in self.hiddenModules) { + var callback = function () {}; + var options = { lockString: self.identifier }; + self.hiddenModules[curModule].show( + self.config.hideAnimationSpeed, + callback, + options + ); } - self.hiddenModules = null + self.hiddenModules = null; } }, socketNotificationReceived: function (notification, payload) { - const self = this - if( notification === 'SCREEN_TIMEOUT_CHANGED'){ - this.currentDelay = payload.delay - if(payload.delay === 0){ - this.delayDisabled = true + const self = this; + if (notification === "SCREEN_TIMEOUT_CHANGED") { + this.currentDelay = payload.delay; + if (payload.delay === 0) { + this.delayDisabled = true; } else { - this.delayDisabled = false + this.delayDisabled = false; } - this.updateDom() - } else if (notification === 'SCREEN_HIDE_MODULES'){ - self.sendNotification("DISABLE_PROFILE_TIMERS") - if (self.config.changeToProfile !== null){ - if(self.profileHistory[1] === self.config.changeToProfile){ - self.profileHistory[0] = self.profileHistory[1] + this.updateDom(); + } else if (notification === "SCREEN_HIDE_MODULES") { + self.sendNotification("DISABLE_PROFILE_TIMERS"); + if (self.config.changeToProfile !== null) { + if (self.profileHistory[1] === self.config.changeToProfile) { + self.profileHistory[0] = self.profileHistory[1]; } - self.sendNotification("CURRENT_PROFILE", self.config.changeToProfile) + self.sendNotification("CURRENT_PROFILE", self.config.changeToProfile); } else { - self.hideModules() + self.hideModules(); } - } else if (notification === 'SCREEN_SHOW_MODULES'){ - self.sendNotification("ENABLE_PROFILE_TIMERS") - if (self.config.changeToProfile !== null){ - self.sendNotification("CURRENT_PROFILE", self.profileHistory[0]) + } else if (notification === "SCREEN_SHOW_MODULES") { + self.sendNotification("ENABLE_PROFILE_TIMERS"); + if (self.config.changeToProfile !== null) { + self.sendNotification("CURRENT_PROFILE", self.profileHistory[0]); } else { - self.showModules() + self.showModules(); } } else if ( - (notification === 'SCREENSAVE_ENABLED') || - (notification === 'SCREENSAVE_DISABLED') || - (notification === 'CURRENT_PROFILE') - ){ - this.sendNotification(notification,payload) + notification === "SCREENSAVE_ENABLED" || + notification === "SCREENSAVE_DISABLED" || + notification === "CURRENT_PROFILE" + ) { + this.sendNotification(notification, payload); } }, notificationReceived: function (notification, payload) { - const self = this + const self = this; if ( - (notification === 'USER_PRESENCE') || - (notification === 'SCREEN_TOGGLE') || - (notification === 'SCREEN_ON') || - (notification === 'SCREEN_OFF') || - (notification === 'SCREEN_POWERSAVE') + notification === "USER_PRESENCE" || + notification === "SCREEN_TOGGLE" || + notification === "SCREEN_ON" || + notification === "SCREEN_OFF" || + notification === "SCREEN_POWERSAVE" ) { if ( - (typeof payload !== 'undefined') && - (typeof payload.forced !== 'undefined') - ){ + typeof payload !== "undefined" && + typeof payload.forced !== "undefined" + ) { if ( - (payload.forced === true) || - (payload.forced.toString().toLowerCase() === "true") - ){ - payload.forced = true + payload.forced === true || + payload.forced.toString().toLowerCase() === "true" + ) { + payload.forced = true; } else { - payload.forced = false + payload.forced = false; } } - this.sendSocketNotification(notification, payload) - } - else if (notification === 'CHANGED_PROFILE'){ - if (self.profileHistory.length > 1){ - self.profileHistory[0] = self.profileHistory[1] - self.profileHistory[1] = payload.to + this.sendSocketNotification(notification, payload); + } else if (notification === "CHANGED_PROFILE") { + if (self.profileHistory.length > 1) { + self.profileHistory[0] = self.profileHistory[1]; + self.profileHistory[1] = payload.to; } else { - self.profileHistory[0] = payload.to - self.profileHistory[1] = payload.to + self.profileHistory[0] = payload.to; + self.profileHistory[1] = payload.to; } - this.sendSocketNotification(notification, payload) + this.sendSocketNotification(notification, payload); } } -}) +}); diff --git a/README.md b/README.md index 7b1a965..f81bfb7 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ # MMM-Screen-Powersave-Notification + MMM-Screen-Powersave-Notification is a module for the [MagicMirror](https://github.com/MichMich/MagicMirror) project by [Michael Teeuw](https://github.com/MichMich) and is based on https://github.com/mboskamp/MMM-PIR and https://github.com/mboskamp/MMM-PIR. It uses notifications to check for user presence or forced on and offs of the screen. After a configurated time without user presence the display will turn off. Additionaly scripts can be run after the screen turned of or on. If you like you can specify different delays for different profiles. The "normal" delay is used if no specific delay for a profile is configured. -As of version 0.0.4 it is also possible to only hide/show the modules for users that use a display that does not support turn off/on commands. +As of version 0.0.4 it is also possible to only hide/show the modules for users that use a display that does not support turn off/on commands. **You need a second module like in example [MMM-GPIO-Notifications](https://github.com/Tom-Hirschberger/MMM-GPIO-Notifications) whichs sends the notifications. I wrote a [english](https://www.github.com/Tom-Hirschberger/MMM-GPIO-Notifications/tree/master/examples%2FHC-SR501%2FHC-SR501-GPIO4-README-EN.md) and [german](https://www.github.com/Tom-Hirschberger/MMM-GPIO-Notifications/tree/master/examples%2FHC-SR501%2FHC-SR501-GPIO4-README-DE.md) tutorial on howto use an HC-SR501 PIR sensor with these modules.** ## Installation + ```sh cd ~/MagicMirror/modules git clone https://github.com/Tom-Hirschberger/MMM-Screen-Powersave-Notification.git @@ -17,7 +19,9 @@ As of version 0.0.4 it is also possible to only hide/show the modules for users ``` ## Configuration + To display the module insert it in the config.js file. Here is an example: + ```js { module: 'MMM-Screen-Powersave-Notification', @@ -32,36 +36,38 @@ To display the module insert it in the config.js file. Here is an example:
-| Option | Description | Type | Default | -| ------- | --- | --- | --- | -| delay | time before the mirror turns off the display if no user activity is detected. (in seconds) | Integer | 60 | -| profiles | it is possible to configure different delays for different profiles (i.e. if you use profiles as pages) | Map | empty | -| screenOnCommand | the command which is used to turn the screen on | String | '/usr/bin/vcgencmd display_power 1' | -| screenOffCommand | the command which is used to turn the screen off | String | '/usr/bin/vcgencmd display_power 0' | -| screenStatusCommand | the command which is used to check if the screen is on (result needs to be 'display_power 1' if on) | String | '/usr/bin/vcgencmd display_power' | -| turnScreenOnIfProfileDelayIsSet | if you do not want the screen to be turned on if the profile changes and a profile specific delay is set set this value to false | boolean | true | -| countDownText | If you specify a position for the module in the config an countdown will be displayed; the countdown starts with an text that you can change with this value | String | 'Display powersave: ' | -| disabledText | If the display powersave is disabled an message instead of the counter will be display | String | 'disabled' | -| displayHours | If you use such long powersave intervals that you need hours you can set this value to true | boolean | false | -| countDownUpdateInterval | How often should the counter be updated | Integer | 5000 | -| animationSpeed | If you like the update of the counter to be animated you can specify an interval with this value | Integer | 0 | -| hideInsteadShutoff | If you use an display that can not be turned off and on with the pi you can hide the modules only instead. Turning this option to true will do so | boolean | false | -| hideAnimationSpeed | The hiding and reappearing off the modules will be animated with this speed | Integer | 500 | -| changeToProfile | If the hiding is enabled and this string is set an change to the new profile is triggered. If the screensave mode is disabled a change to the previous profile will be initiated | String | null | -| changeToProfileBeforeAction | Set a profile string to this variable if you like the module to change the screen to a specific profile (i.e. start page) before the "normal" action is triggerd | String | null | +| Option | Description | Type | Default | +| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------- | +| delay | time before the mirror turns off the display if no user activity is detected. (in seconds) | Integer | 60 | +| profiles | it is possible to configure different delays for different profiles (i.e. if you use profiles as pages) | Map | empty | +| screenOnCommand | the command which is used to turn the screen on | String | '/usr/bin/vcgencmd display_power 1' | +| screenOffCommand | the command which is used to turn the screen off | String | '/usr/bin/vcgencmd display_power 0' | +| screenStatusCommand | the command which is used to check if the screen is on (result needs to be 'display_power 1' if on) | String | '/usr/bin/vcgencmd display_power' | +| turnScreenOnIfProfileDelayIsSet | if you do not want the screen to be turned on if the profile changes and a profile specific delay is set set this value to false | boolean | true | +| countDownText | If you specify a position for the module in the config an countdown will be displayed; the countdown starts with an text that you can change with this value | String | 'Display powersave: ' | +| disabledText | If the display powersave is disabled an message instead of the counter will be display | String | 'disabled' | +| displayHours | If you use such long powersave intervals that you need hours you can set this value to true | boolean | false | +| countDownUpdateInterval | How often should the counter be updated | Integer | 5000 | +| animationSpeed | If you like the update of the counter to be animated you can specify an interval with this value | Integer | 0 | +| hideInsteadShutoff | If you use an display that can not be turned off and on with the pi you can hide the modules only instead. Turning this option to true will do so | boolean | false | +| hideAnimationSpeed | The hiding and reappearing off the modules will be animated with this speed | Integer | 500 | +| changeToProfile | If the hiding is enabled and this string is set an change to the new profile is triggered. If the screensave mode is disabled a change to the previous profile will be initiated | String | null | +| changeToProfileBeforeAction | Set a profile string to this variable if you like the module to change the screen to a specific profile (i.e. start page) before the "normal" action is triggerd | String | null | ## Received Notifications -| Notification | Payload | Default | Result | -| ------------ | ------- | ------- | ------ | -| USER_PRESENCE | true (mandatory) | | the timeout to turn of the screen will be reseted | -| SCREEN_OFF | forced=true or false | false | turns the screen off; if the forced option is set to true also if the screen was turned on forced | -| SCREEN_ON | forced=true or false | false | turns the screen on; if the forced option is set to true also if the screen was turned off forced | -| SCREEN_TOGGLE | forced=true or false | false | switches the state of the screen; the forced option will be used for on off like in SCREEN_ON or SCREEN_OFF | -| SCREEN_POWERSAVE | delay=NUMBER | delay=0 | can be used to set the delay for the timeout to an different value (or deactivate it if 0) during runtime | -| CHANGED_PROFILE | from and to values which are the name of the old and the new profile| | resets the screen timeout to either the default delay or to a specific delay configured with the profiles map | + +| Notification | Payload | Default | Result | +| ---------------- | -------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------- | +| USER_PRESENCE | true (mandatory) | | the timeout to turn of the screen will be reseted | +| SCREEN_OFF | forced=true or false | false | turns the screen off; if the forced option is set to true also if the screen was turned on forced | +| SCREEN_ON | forced=true or false | false | turns the screen on; if the forced option is set to true also if the screen was turned off forced | +| SCREEN_TOGGLE | forced=true or false | false | switches the state of the screen; the forced option will be used for on off like in SCREEN_ON or SCREEN_OFF | +| SCREEN_POWERSAVE | delay=NUMBER | delay=0 | can be used to set the delay for the timeout to an different value (or deactivate it if 0) during runtime | +| CHANGED_PROFILE | from and to values which are the name of the old and the new profile | | resets the screen timeout to either the default delay or to a specific delay configured with the profiles map | ## Send Notifications -| Notification | Payload | Cause | -| ------------ | ------- | ------ | -| SCREENSAVE_ENABLED | nothing | This notification is send if the module sets the display to screensave mode (or hides the modules) | + +| Notification | Payload | Cause | +| ------------------- | ------- | -------------------------------------------------------------------------------------------------- | +| SCREENSAVE_ENABLED | nothing | This notification is send if the module sets the display to screensave mode (or hides the modules) | | SCREENSAVE_DISABLED | nothing | This notification is send if the module sets the display to screensave mode (or shows the modules) | diff --git a/node_helper.js b/node_helper.js index f7c4576..7181040 100644 --- a/node_helper.js +++ b/node_helper.js @@ -50,6 +50,7 @@ module.exports = NodeHelper.create({ turnScreenOff: async function (forced) { const self = this if (self.config.changeToProfileBeforeAction !== null) { + self.skipNextProfileChange = true self.sendSocketNotification("CURRENT_PROFILE", self.config.changeToProfileBeforeAction) await self.Sleep(500) } @@ -190,8 +191,6 @@ module.exports = NodeHelper.create({ self.turnScreenOn(false) if (self.isScreenOn()){ self.clearAndSetScreenTimeout(true) - } else { - self.clearAndSetScreenTimeout(false,false) } } } else if (notification === 'SCREEN_TOGGLE') { @@ -213,13 +212,19 @@ module.exports = NodeHelper.create({ } self.clearAndSetScreenTimeout(true) } else if (notification === 'CHANGED_PROFILE'){ - if(typeof payload.to !== 'undefined'){ - self.currentProfile = payload.to - self.currentProfilePattern = new RegExp('\\b'+payload.to+'\\b') + if (!self.skipNextProfileChange){ + if(typeof payload.to !== 'undefined'){ + self.currentProfile = payload.to + self.currentProfilePattern = new RegExp('\\b'+payload.to+'\\b') - if(self.config.profiles && (Object.keys(self.config.profiles).length > 0)){ - self.clearAndSetScreenTimeout(true, profileChange=true); + if (payload.to !== self.config.changeToProfile){ + if(self.config.profiles && (Object.keys(self.config.profiles).length > 0)){ + self.clearAndSetScreenTimeout(true, profileChange=true); + } + } } + } else { + self.skipNextProfileChange = false } } else { console.log(this.name + ': Received Notification: ' + notification) diff --git a/screen-powersave.css b/screen-powersave.css index 8719add..5ba4416 100644 --- a/screen-powersave.css +++ b/screen-powersave.css @@ -1,3 +1,3 @@ .MMM-Screen-Powersave-Notification { - font-size: 14px; -} \ No newline at end of file + font-size: 14px; +} From 15dc8af0692b480a151ffa2c7933e1191a8c9be4 Mon Sep 17 00:00:00 2001 From: Tom Hirschberger Date: Sat, 22 Jan 2022 14:18:08 +0100 Subject: [PATCH 7/8] only updateDom if the module got an position set now --- MMM-Screen-Powersave-Notification.js | 45 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/MMM-Screen-Powersave-Notification.js b/MMM-Screen-Powersave-Notification.js index 747e6a4..9e1bca7 100644 --- a/MMM-Screen-Powersave-Notification.js +++ b/MMM-Screen-Powersave-Notification.js @@ -61,35 +61,36 @@ Module.register("MMM-Screen-Powersave-Notification", { }, getDom: function () { - clearTimeout(this.currentDelayTimer); - const wrapper = document.createElement("div"); + if (typeof this.data.position !== "undefined"){ + clearTimeout(this.currentDelayTimer); - if (!this.delayDisabled || this.config.disabledText !== null) { - const textWrapper = document.createElement("span"); - textWrapper.className = "textWrapper"; + if (!this.delayDisabled || this.config.disabledText !== null) { + const textWrapper = document.createElement("span"); + textWrapper.className = "textWrapper"; - textWrapper.innerHTML = this.config.countDownText; - wrapper.appendChild(textWrapper); + textWrapper.innerHTML = this.config.countDownText; + wrapper.appendChild(textWrapper); - const valueWrapper = document.createElement("span"); - valueWrapper.className = "valueWrapper"; + const valueWrapper = document.createElement("span"); + valueWrapper.className = "valueWrapper"; - if (this.delayDisabled) { - valueWrapper.innerHTML = this.config.disabledText; - } else { - //valueWrapper.innerHTML = moment("1900-01-01 00:00:00").add(this.currentDelay, 'seconds').format(this.config.countDownFormatString) - valueWrapper.innerHTML = this.getTimeString(this.currentDelay); - } + if (this.delayDisabled) { + valueWrapper.innerHTML = this.config.disabledText; + } else { + //valueWrapper.innerHTML = moment("1900-01-01 00:00:00").add(this.currentDelay, 'seconds').format(this.config.countDownFormatString) + valueWrapper.innerHTML = this.getTimeString(this.currentDelay); + } - wrapper.appendChild(valueWrapper); + wrapper.appendChild(valueWrapper); - const self = this; - self.currentDelayTimer = setTimeout(function () { - self.currentDelay = - self.currentDelay - self.config.countDownUpdateInterval / 1000; - self.updateDom(self.config.animationSpeed); - }, self.config.countDownUpdateInterval); + const self = this; + self.currentDelayTimer = setTimeout(function () { + self.currentDelay = + self.currentDelay - self.config.countDownUpdateInterval / 1000; + self.updateDom(self.config.animationSpeed); + }, self.config.countDownUpdateInterval); + } } return wrapper; From 0a854774a6bb7662a74ef6286ece0e97e5346c3b Mon Sep 17 00:00:00 2001 From: Tom Hirschberger Date: Sat, 7 Jan 2023 21:44:52 +0100 Subject: [PATCH 8/8] added xrandr_control script; renamed exampleScripts to controlScripts; changed version to 0.0.7 --- README.md | 98 ++++++++++++++++++- .../tvservice_control | 0 controlScripts/xrandr_control | 32 ++++++ package.json | 2 +- 4 files changed, 130 insertions(+), 2 deletions(-) rename {exampleScripts => controlScripts}/tvservice_control (100%) create mode 100755 controlScripts/xrandr_control diff --git a/README.md b/README.md index f81bfb7..f10d82e 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,103 @@ To display the module insert it in the config.js file. Here is an example: | hideInsteadShutoff | If you use an display that can not be turned off and on with the pi you can hide the modules only instead. Turning this option to true will do so | boolean | false | | hideAnimationSpeed | The hiding and reappearing off the modules will be animated with this speed | Integer | 500 | | changeToProfile | If the hiding is enabled and this string is set an change to the new profile is triggered. If the screensave mode is disabled a change to the previous profile will be initiated | String | null | -| changeToProfileBeforeAction | Set a profile string to this variable if you like the module to change the screen to a specific profile (i.e. start page) before the "normal" action is triggerd | String | null | +| changeToProfileBeforeAction | Set a profile string to this variable if you like the module to change the screen to a specific profile (i.e. start page) before the "normal" action is triggerd | String | null | + +## Alternative commands + +In default the module uses `vcgencmd` to control the screen. There might be situations where `vcgencmd` fails (for example with newer Raspberry Bullseye versions) and you need to find an alterative way to control the screen. + +You can provide your own scripts and set the paths to them with the configuration options `screenStatusCommand`, `screenOnCommand` and `screenOffCommand`. + +Make sure to let the scripts return/print "display_power=0" if the screen is turned off and "display_power=1" if the screen is turned on! + +The module already provides alternative scripts in the controlScripts directory which will be described in the following sections... + +### xrandr_control + +This solution will work even with Raspberry OS Bullseye which uses the new "vc4-kms-v3d" graphics driver. + +The first option of the script is the action you want the second option is optional and the port you want to use. +My Raspberry 4 provides two HDMI ports which are called "HDMI-1" and "HDMI-2". + +You can check which port is used with the command: + +```bash +xrandr -display :0.0 --current +``` + +The output will look something like: + +```bash +Screen 0: minimum 320 x 200, current 800 x 1280, maximum 7680 x 7680 +HDMI-1 connected primary 800x1280+0+0 right (normal left inverted right x axis y axis) 519mm x 324mm + 1280x800 59.91*+ 60.00 + 1920x1080 60.00 59.94 + 1920x1080i 60.00 59.94 + 1280x1024 60.02 + 1280x960 60.00 + 1152x864 75.00 + 1280x720 60.00 59.94 + 1024x768 75.03 70.07 60.00 + 800x600 72.19 75.00 60.32 56.25 + 720x576i 50.00 + 720x480 60.00 59.94 + 720x480i 60.00 59.94 + 640x480 75.00 72.81 60.00 59.94 +HDMI-2 disconnected (normal left inverted right x axis y axis) +``` + +The output above shows that HDMI-1 is connected and HDMI-2 is disconnected. Depending of your output you need to change the second option in the command configurtions which are following. + +In the following example the output is simply shut off or on and no rotation is provided: + +```json5 + { + module: 'MMM-Screen-Powersave-Notification', + config: { + delay: 60, + screenStatusCommand: "./modules/MMM-Screen-Powersave-Notification/controlScripts/xrandr_control status HDMI-1", + screenOnCommand: "./modules/MMM-Screen-Powersave-Notification/controlScripts/xrandr_control on HDMI-1", + screenOffCommand: "./modules/MMM-Screen-Powersave-Notification/controlScripts/xrandr_control off HDMI-1" + } + } +``` + +In the following example the output is shut off and on but it is rotatet left: + +```json5 + { + module: 'MMM-Screen-Powersave-Notification', + config: { + delay: 60, + screenStatusCommand: "./modules/MMM-Screen-Powersave-Notification/controlScripts/xrandr_control status HDMI-1", + screenOnCommand: "./modules/MMM-Screen-Powersave-Notification/controlScripts/xrandr_control left HDMI-1", + screenOffCommand: "./modules/MMM-Screen-Powersave-Notification/controlScripts/xrandr_control off HDMI-1" + } + } +``` + +Other possible rotations are "normal", "inverted", "right". + +### tvservice_control + +The `tvservice` command was an alternative to `vcgencmd` but it will **NOT** work with current installations of Raspberry OS Bullseye! + +Rotation is **NOT** supported only simple on or off! + +Use the following configuration if you want to use `tvservice` command. + +```json5 + { + module: 'MMM-Screen-Powersave-Notification', + config: { + delay: 60, + screenStatusCommand: "./modules/MMM-Screen-Powersave-Notification/controlScripts/tvservice_control status", + screenOnCommand: "./modules/MMM-Screen-Powersave-Notification/controlScripts/tvservice_control on", + screenOffCommand: "./modules/MMM-Screen-Powersave-Notification/controlScripts/tvservice_control off" + } + } +``` ## Received Notifications diff --git a/exampleScripts/tvservice_control b/controlScripts/tvservice_control similarity index 100% rename from exampleScripts/tvservice_control rename to controlScripts/tvservice_control diff --git a/controlScripts/xrandr_control b/controlScripts/xrandr_control new file mode 100755 index 0000000..ebef040 --- /dev/null +++ b/controlScripts/xrandr_control @@ -0,0 +1,32 @@ +#!/bin/bash + +port="HDMI-1" +if [ "$2" != "" ] +then + port=$2 +fi + +#xrandr -display :0.0 | grep -cE "^HDMI-[0-9][[:space:]]connected[^0-9]*[0-9]+[^\(]+\(.*" +#xrandr -display :0.0 --output "HDMI-1" --auto --rotate right +#xrandr -display :0.0 --output "HDMI-1" --off + +if [ "$1" == "on" ] +then + #normal,inverted,left,right + xrandr -display :0.0 --output $port --auto + echo "display_power=1" +elif [ "$1" == "normal" ] || [ "$1" == "inverted" ] || [ "$1" == "left" ] || [ "$1" == "right" ] +then + xrandr -display :0.0 --output "HDMI-1" --auto --rotate $1 +elif [ "$1" == "off" ] +then + xrandr -display :0.0 --output $port --off + echo "display_power=0" +else + if [ `xrandr -display :0.0 | grep -cE "^${port}[[:space:]]connected[^0-9]*[0-9]+[^\(]+\(.*"` -gt 0 ] + then + echo "display_power=1" + else + echo "display_power=0" + fi +fi diff --git a/package.json b/package.json index 2e0b4a0..e08c9a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MMM-Screen-Powersave-Notification", - "version": "0.0.6", + "version": "0.0.7", "description": "", "main": "MMM-Screen-Powersave-Notification.js", "dependencies": {