{"id":261,"date":"2024-12-17T15:01:22","date_gmt":"2024-12-17T15:01:22","guid":{"rendered":"http:\/\/blog.ltzs.us\/?p=261"},"modified":"2024-12-17T15:01:22","modified_gmt":"2024-12-17T15:01:22","slug":"switching-between-tabs-via-node-red","status":"publish","type":"post","link":"http:\/\/blog.ltzs.us\/?p=261","title":{"rendered":"Switching between tabs via Node Red"},"content":{"rendered":"\n<p>I have a raspberry pi zero 2w running a 24&#8243; monitor that is displaying relevant information in an area of the farm. The Raspi isn&#8217;t strong enough to run Chromium or Firefox (only 512MB RAM) so I am VNCing into a server that is displaying a Firefox window with the info I want. Takes much less RAM to VNC, and I have enough left over to run Node Red. <\/p>\n\n\n\n<p>The problem is that the 24&#8243; screen isn&#8217;t big enough to display everything, so I need to open a second tab that shows other stuff. That&#8217;s easy enough to do upon startup with a service file in \/etc\/systemd\/system<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">[Unit]\nDescription=Launches Firefox fullscreen in the :3 X session for the etable screen\nAfter=network-online.target\n\n[Service]\nUser=etable\nType=simple\nRemainAfterExit=yes\nExecStartPre=\/bin\/sleep 10\nEnvironment=DISPLAY=:3\nExecStart=\/usr\/bin\/firefox-esr\n\n[Install]\nWantedBy=graphical.target<\/code><\/pre>\n\n\n\n<p>Firefox has the two tabs I want already defined in defaults so it loads them up when it runs.<\/p>\n\n\n\n<p>To get the two tabs switching back and forth, I use node red with the following flow:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"277\" src=\"http:\/\/blog.ltzs.us\/wp-content\/uploads\/2024\/12\/image-1024x277.png\" alt=\"\" class=\"wp-image-262\" srcset=\"http:\/\/blog.ltzs.us\/wp-content\/uploads\/2024\/12\/image-1024x277.png 1024w, http:\/\/blog.ltzs.us\/wp-content\/uploads\/2024\/12\/image-300x81.png 300w, http:\/\/blog.ltzs.us\/wp-content\/uploads\/2024\/12\/image-768x207.png 768w, http:\/\/blog.ltzs.us\/wp-content\/uploads\/2024\/12\/image-850x230.png 850w, http:\/\/blog.ltzs.us\/wp-content\/uploads\/2024\/12\/image.png 1222w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">[\n    {\n        &quot;id&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;type&quot;: &quot;group&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;style&quot;: {\n            &quot;stroke&quot;: &quot;#999999&quot;,\n            &quot;stroke-opacity&quot;: &quot;1&quot;,\n            &quot;fill&quot;: &quot;none&quot;,\n            &quot;fill-opacity&quot;: &quot;1&quot;,\n            &quot;label&quot;: true,\n            &quot;label-position&quot;: &quot;nw&quot;,\n            &quot;color&quot;: &quot;#a4a4a4&quot;\n        },\n        &quot;nodes&quot;: [\n            &quot;060aaf4b78b14dd5&quot;,\n            &quot;2b7e0ee6d87c7de2&quot;,\n            &quot;c8ba8b78e8999b10&quot;,\n            &quot;6f4a2accaba5a034&quot;,\n            &quot;6d98b5b2fd59f86a&quot;,\n            &quot;1e13380653b2a62d&quot;,\n            &quot;d6778ddedf9f8d53&quot;,\n            &quot;2e73f15f19dc86a4&quot;,\n            &quot;fda8631ae092c027&quot;,\n            &quot;885a08861676cac8&quot;\n        ],\n        &quot;x&quot;: 74,\n        &quot;y&quot;: 359,\n        &quot;w&quot;: 1192,\n        &quot;h&quot;: 302\n    },\n    {\n        &quot;id&quot;: &quot;060aaf4b78b14dd5&quot;,\n        &quot;type&quot;: &quot;rbe&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;g&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;name&quot;: &quot;if button state changes&quot;,\n        &quot;func&quot;: &quot;rbe&quot;,\n        &quot;gap&quot;: &quot;&quot;,\n        &quot;start&quot;: &quot;&quot;,\n        &quot;inout&quot;: &quot;out&quot;,\n        &quot;septopics&quot;: true,\n        &quot;property&quot;: &quot;payload&quot;,\n        &quot;topi&quot;: &quot;topic&quot;,\n        &quot;x&quot;: 460,\n        &quot;y&quot;: 500,\n        &quot;wires&quot;: [\n            [\n                &quot;c8ba8b78e8999b10&quot;\n            ]\n        ]\n    },\n    {\n        &quot;id&quot;: &quot;2b7e0ee6d87c7de2&quot;,\n        &quot;type&quot;: &quot;mqtt in&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;g&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;name&quot;: &quot;&quot;,\n        &quot;topic&quot;: &quot;su900-modbus\/stat\/POWER&quot;,\n        &quot;qos&quot;: &quot;0&quot;,\n        &quot;datatype&quot;: &quot;auto-detect&quot;,\n        &quot;broker&quot;: &quot;9d32dc412883433f&quot;,\n        &quot;nl&quot;: false,\n        &quot;rap&quot;: true,\n        &quot;rh&quot;: 0,\n        &quot;inputs&quot;: 0,\n        &quot;x&quot;: 220,\n        &quot;y&quot;: 500,\n        &quot;wires&quot;: [\n            [\n                &quot;060aaf4b78b14dd5&quot;\n            ]\n        ]\n    },\n    {\n        &quot;id&quot;: &quot;c8ba8b78e8999b10&quot;,\n        &quot;type&quot;: &quot;function&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;g&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;name&quot;: &quot;2 minutes if button, otherwise 30 seconds&quot;,\n        &quot;func&quot;: &quot;var temp = msg.payload;\\nvar button = global.get(\\&quot;button\\&quot;);\\nif ((temp != button) &amp;&amp; ((temp == \\&quot;ON\\&quot;) || (temp == \\&quot;OFF\\&quot;))) {\\n    global.set(\\&quot;button\\&quot;,temp);\\n    global.set(\\&quot;test\\&quot;,temp);\\n    msg.payload = {};\\n    msg.payload = 120;\\n}\\nelse if (temp === false) {\\n    msg.payload = {};\\n    msg.payload = 30;\\n}\\nelse return;\\nreturn msg;&quot;,\n        &quot;outputs&quot;: 1,\n        &quot;timeout&quot;: 0,\n        &quot;noerr&quot;: 0,\n        &quot;initialize&quot;: &quot;&quot;,\n        &quot;finalize&quot;: &quot;&quot;,\n        &quot;libs&quot;: [],\n        &quot;x&quot;: 680,\n        &quot;y&quot;: 400,\n        &quot;wires&quot;: [\n            [\n                &quot;6d98b5b2fd59f86a&quot;,\n                &quot;1e13380653b2a62d&quot;\n            ]\n        ]\n    },\n    {\n        &quot;id&quot;: &quot;6f4a2accaba5a034&quot;,\n        &quot;type&quot;: &quot;rbe&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;g&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;name&quot;: &quot;&quot;,\n        &quot;func&quot;: &quot;rbe&quot;,\n        &quot;gap&quot;: &quot;&quot;,\n        &quot;start&quot;: &quot;&quot;,\n        &quot;inout&quot;: &quot;out&quot;,\n        &quot;septopics&quot;: true,\n        &quot;property&quot;: &quot;payload&quot;,\n        &quot;topi&quot;: &quot;topic&quot;,\n        &quot;x&quot;: 410,\n        &quot;y&quot;: 400,\n        &quot;wires&quot;: [\n            [\n                &quot;c8ba8b78e8999b10&quot;\n            ]\n        ]\n    },\n    {\n        &quot;id&quot;: &quot;6d98b5b2fd59f86a&quot;,\n        &quot;type&quot;: &quot;timer-node&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;g&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;name&quot;: &quot;&quot;,\n        &quot;topic&quot;: &quot;&quot;,\n        &quot;timer&quot;: &quot;&quot;,\n        &quot;payloadOn&quot;: &quot;true&quot;,\n        &quot;payloadOnType&quot;: &quot;bool&quot;,\n        &quot;payloadOff&quot;: &quot;false&quot;,\n        &quot;payloadOffType&quot;: &quot;bool&quot;,\n        &quot;x&quot;: 710,\n        &quot;y&quot;: 520,\n        &quot;wires&quot;: [\n            [\n                &quot;6f4a2accaba5a034&quot;,\n                &quot;2e73f15f19dc86a4&quot;,\n                &quot;fda8631ae092c027&quot;\n            ],\n            []\n        ]\n    },\n    {\n        &quot;id&quot;: &quot;1e13380653b2a62d&quot;,\n        &quot;type&quot;: &quot;change&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;g&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;name&quot;: &quot;change timer set to timer start&quot;,\n        &quot;rules&quot;: [\n            {\n                &quot;t&quot;: &quot;set&quot;,\n                &quot;p&quot;: &quot;payload&quot;,\n                &quot;pt&quot;: &quot;msg&quot;,\n                &quot;to&quot;: &quot;true&quot;,\n                &quot;tot&quot;: &quot;bool&quot;\n            }\n        ],\n        &quot;action&quot;: &quot;&quot;,\n        &quot;property&quot;: &quot;&quot;,\n        &quot;from&quot;: &quot;&quot;,\n        &quot;to&quot;: &quot;&quot;,\n        &quot;reg&quot;: false,\n        &quot;x&quot;: 570,\n        &quot;y&quot;: 600,\n        &quot;wires&quot;: [\n            [\n                &quot;d6778ddedf9f8d53&quot;\n            ]\n        ]\n    },\n    {\n        &quot;id&quot;: &quot;d6778ddedf9f8d53&quot;,\n        &quot;type&quot;: &quot;delay&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;g&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;name&quot;: &quot;&quot;,\n        &quot;pauseType&quot;: &quot;delay&quot;,\n        &quot;timeout&quot;: &quot;100&quot;,\n        &quot;timeoutUnits&quot;: &quot;milliseconds&quot;,\n        &quot;rate&quot;: &quot;1&quot;,\n        &quot;nbRateUnits&quot;: &quot;1&quot;,\n        &quot;rateUnits&quot;: &quot;second&quot;,\n        &quot;randomFirst&quot;: &quot;1&quot;,\n        &quot;randomLast&quot;: &quot;5&quot;,\n        &quot;randomUnits&quot;: &quot;seconds&quot;,\n        &quot;drop&quot;: false,\n        &quot;allowrate&quot;: false,\n        &quot;outputs&quot;: 1,\n        &quot;x&quot;: 790,\n        &quot;y&quot;: 620,\n        &quot;wires&quot;: [\n            [\n                &quot;6d98b5b2fd59f86a&quot;\n            ]\n        ]\n    },\n    {\n        &quot;id&quot;: &quot;2e73f15f19dc86a4&quot;,\n        &quot;type&quot;: &quot;debug&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;g&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;name&quot;: &quot;debug 1&quot;,\n        &quot;active&quot;: true,\n        &quot;tosidebar&quot;: true,\n        &quot;console&quot;: false,\n        &quot;tostatus&quot;: false,\n        &quot;complete&quot;: &quot;false&quot;,\n        &quot;statusVal&quot;: &quot;&quot;,\n        &quot;statusType&quot;: &quot;auto&quot;,\n        &quot;x&quot;: 1100,\n        &quot;y&quot;: 440,\n        &quot;wires&quot;: []\n    },\n    {\n        &quot;id&quot;: &quot;fda8631ae092c027&quot;,\n        &quot;type&quot;: &quot;function&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;g&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;name&quot;: &quot;only send tab switch command once&quot;,\n        &quot;func&quot;: &quot;var temp = msg.payload;\\nif (temp === false) {\\n    return msg;\\n}&quot;,\n        &quot;outputs&quot;: 1,\n        &quot;timeout&quot;: 0,\n        &quot;noerr&quot;: 0,\n        &quot;initialize&quot;: &quot;&quot;,\n        &quot;finalize&quot;: &quot;&quot;,\n        &quot;libs&quot;: [],\n        &quot;x&quot;: 1050,\n        &quot;y&quot;: 520,\n        &quot;wires&quot;: [\n            [\n                &quot;885a08861676cac8&quot;\n            ]\n        ]\n    },\n    {\n        &quot;id&quot;: &quot;885a08861676cac8&quot;,\n        &quot;type&quot;: &quot;exec&quot;,\n        &quot;z&quot;: &quot;64b7c4985fcb7349&quot;,\n        &quot;g&quot;: &quot;1a3d9ae75c833589&quot;,\n        &quot;command&quot;: &quot;\/bin\/bash \/home\/pi\/Desktop\/switchtabs.sh&quot;,\n        &quot;addpay&quot;: &quot;&quot;,\n        &quot;append&quot;: &quot;&quot;,\n        &quot;useSpawn&quot;: &quot;false&quot;,\n        &quot;timer&quot;: &quot;&quot;,\n        &quot;winHide&quot;: false,\n        &quot;oldrc&quot;: false,\n        &quot;name&quot;: &quot;Swap tabs&quot;,\n        &quot;x&quot;: 1170,\n        &quot;y&quot;: 600,\n        &quot;wires&quot;: [\n            [],\n            [],\n            []\n        ]\n    },\n    {\n        &quot;id&quot;: &quot;9d32dc412883433f&quot;,\n        &quot;type&quot;: &quot;mqtt-broker&quot;,\n        &quot;name&quot;: &quot;DebianVM&quot;,\n        &quot;broker&quot;: &quot;192.168.3.98&quot;,\n        &quot;port&quot;: 1883,\n        &quot;clientid&quot;: &quot;&quot;,\n        &quot;autoConnect&quot;: true,\n        &quot;usetls&quot;: false,\n        &quot;protocolVersion&quot;: 4,\n        &quot;keepalive&quot;: 60,\n        &quot;cleansession&quot;: true,\n        &quot;autoUnsubscribe&quot;: true,\n        &quot;birthTopic&quot;: &quot;&quot;,\n        &quot;birthQos&quot;: &quot;0&quot;,\n        &quot;birthRetain&quot;: &quot;false&quot;,\n        &quot;birthPayload&quot;: &quot;&quot;,\n        &quot;birthMsg&quot;: {},\n        &quot;closeTopic&quot;: &quot;&quot;,\n        &quot;closeQos&quot;: &quot;0&quot;,\n        &quot;closeRetain&quot;: &quot;false&quot;,\n        &quot;closePayload&quot;: &quot;&quot;,\n        &quot;closeMsg&quot;: {},\n        &quot;willTopic&quot;: &quot;&quot;,\n        &quot;willQos&quot;: &quot;0&quot;,\n        &quot;willRetain&quot;: &quot;false&quot;,\n        &quot;willPayload&quot;: &quot;&quot;,\n        &quot;willMsg&quot;: {},\n        &quot;userProps&quot;: &quot;&quot;,\n        &quot;sessionExpiry&quot;: &quot;&quot;\n    }\n]<\/code><\/pre>\n\n\n\n<p>MQTT connection to the ESP8266 device running tasmota with the button attached and a dummy relay defined. It sends out a stat message when the button is pressed with ON or OFF as the payload. Homebridge is polling stat every 60 seconds so I put in a filter node to only send on the data when it changes (i.e. when the button is pressed)<\/p>\n\n\n\n<p>Then a function node that checks for a button press vs a loop (the payload is different) and sets the timer depending. The user wants the screen static for 2 minutes if he pushes the button, but otherwise the screen should rotate on a 30 second loop. <\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">var temp = msg.payload;\nvar button = global.get(&quot;button&quot;);\nif ((temp != button) &amp;&amp; ((temp == &quot;ON&quot;) || (temp == &quot;OFF&quot;))) {\n    global.set(&quot;button&quot;,temp);\n    global.set(&quot;test&quot;,temp);\n    msg.payload = {};\n    msg.payload = 120;\n}\nelse if (temp === false) {\n    msg.payload = {};\n    msg.payload = 30;\n}\nelse return;\nreturn msg;<\/code><\/pre>\n\n\n\n<p>The timer node pushes false if the timer runs out (which is when I want to call the exec node) and a true when the timer starts. In the exec node, I call a script that calls xdotool to press Ctrl-Tab. <\/p>\n\n\n\n<p>In the node: <mark style=\"background-color:#fcb900\" class=\"has-inline-color\"><code>\/bin\/bash \/home\/pi\/Desktop\/switchtabs.sh<\/code><\/mark><\/p>\n\n\n\n<p>In switchtabs.sh:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">#!\/bin\/sh\n\nexport DISPLAY=:0\nxdotool key ctrl+Tab\nexit 0<\/code><\/pre>\n\n\n\n<p>Make sure switchtabs.sh is executable with <mark style=\"background-color:#fcb900\" class=\"has-inline-color\"><code>sudo chmod +x switchtabs.sh<\/code><\/mark><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have a raspberry pi zero 2w running a 24&#8243; monitor that is displaying relevant information in an area of the farm. The Raspi isn&#8217;t strong enough to run Chromium or Firefox (only 512MB RAM) so I am VNCing into a server that is displaying a Firefox window with the info I want. Takes much&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-261","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"http:\/\/blog.ltzs.us\/index.php?rest_route=\/wp\/v2\/posts\/261","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/blog.ltzs.us\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.ltzs.us\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.ltzs.us\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.ltzs.us\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=261"}],"version-history":[{"count":1,"href":"http:\/\/blog.ltzs.us\/index.php?rest_route=\/wp\/v2\/posts\/261\/revisions"}],"predecessor-version":[{"id":263,"href":"http:\/\/blog.ltzs.us\/index.php?rest_route=\/wp\/v2\/posts\/261\/revisions\/263"}],"wp:attachment":[{"href":"http:\/\/blog.ltzs.us\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.ltzs.us\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=261"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.ltzs.us\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}