Just for fun.
[{"id":"656888cd7308428e","type":"inject","z":"bf0d83d32eec75c2","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":230,"y":860,"wires":[["b7f0df3f43a8dc6e"]]},{"id":"b7f0df3f43a8dc6e","type":"function","z":"bf0d83d32eec75c2","name":"random data","func":"let current = context.get('current') || 1\n\ncurrent += Math.floor(Math.random()*2);\nif(current > 999999){\n current = 1\n}\n\ncontext.set('current',current)\nmsg.payload = current \nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":860,"wires":[["aac264ba0df29e45"]]},{"id":"bf8580a24000c55c","type":"ui_template","z":"bf0d83d32eec75c2","group":"55888982bdd0637f","name":"display","order":5,"width":0,"height":0,"format":"<div id=\"{{'slot_'+$id+'_0'}}\" class=\"slot\" ng-init=\"init()\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_1'}}\" class=\"slot\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_2'}}\" class=\"slot\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_3'}}\" class=\"slot\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_4'}}\" class=\"slot\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_5'}}\" class=\"slot\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div class=\"gap\"></div>\n<div id=\"{{'slot_'+$id+'_6'}}\" class=\"slot last\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_7'}}\" class=\"slot last\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div class=\"unit\">kWh</div>\n\n<script>\n (function(scope) {\n let lock = 0\n const count = 8\n scope.previous = Array(count).fill(-1);\n scope.drawlist = Array(count).fill(true);\n scope.inited = false\n scope.waitingData = null \n scope.init = function(){\n if($(\"#slot_\"+scope.$id+\"_0\".length)){\n actuallyInit()\n }\n else{\n setTimeout(function(){\n actuallyInit()\n },40)\n }\n }\n function actuallyInit(){ \n lock = 0\n if(scope.waitingData){ \n setTimeout(function(){\n callDraw()\n },40)\n }\n scope.inited = true\n }\n\n scope.$watch('msg', function(msg) {\n if (msg) { \n let data = msg.payload.toString().split('')\n if(!scope.inited){\n scope.waitingData = data\n return\n }\n if(checkCollapsed()){\n scope.waitingData = data\n return\n }\n if(locked()){\n scope.waitingData = data\n validateLocks()\n }\n else{\n if(!draw(data)){\n scope.waitingData = data\n setTimeout(function(){\n validateLocks()\n },40)\n }\n \n }\n }\n });\n\n function checkCollapsed(){\n let d = $(\"#slot_\"+scope.$id+\"_0\").closest('.nr-dashboard-cardcontainer').css('display')\n if(d == 'none'){\n lock = 0\n return true\n }\n return false\n }\n\n function locked(){\n return lock > 0 \n }\n\n function pickLock(){\n if(locked()){\n lock --;\n }\n if(lock < 0){\n lock = 0;\n return\n }\n if(!locked()){\n if(scope.waitingData){\n setTimeout(function(){\n callDraw()\n },10) \n }\n }\n }\n\n function validateLocks(){\n let e = 0\n for(let i = 0;i<count;++i){\n e += $(\"#slot_\"+scope.$id+\"_\"+i).find(\".current\").text().length\n } \n if(e == 0){ \n lock = 0\n callDraw()\n }\n }\n\n function callDraw(){ \n if(!scope.waitingData){\n return\n }\n draw(Array.from(scope.waitingData)) \n scope.waitingData = null;\n }\n\n function draw(data){\n if(locked()){ \n return\n }\n let failures = 0 \n while(data.length < count){\n data.unshift(\"0\")\n }\n data.forEach((d,i)=>{\n //d = parseInt(d)\n if(scope.previous[i] == d && $(\"#slot_\"+scope.$id+\"_\"+i).find(\".current\").text().length > 0){\n scope.drawlist[i] = false\n }\n else{\n scope.previous[i] = d\n scope.drawlist[i] = true\n lock ++;\n } \n })\n data.forEach((d,i) => {\n if(scope.drawlist[i] == false){\n return\n }\n let current = $(\"#slot_\"+scope.$id+\"_\"+i).find(\".current\")\n let next = $(\"#slot_\"+scope.$id+\"_\"+i).find(\".next\")\n let old = $(\"#slot_\"+scope.$id+\"_\"+i).find(\".old\") \n \n if(next.length == 0 && old.length > 0){\n old.removeClass(\"old\").addClass(\"next\")\n next = $(\"#slot_\"+scope.$id+\"_\"+i).find(\".next\")\n }\n \n next.text(data[i])\n if(next.length == 0){ \n failures ++\n } \n current.removeClass(\"current\").addClass(\"old\").on('transitionend webkitTransitionEnd oTransitionEnd', function () {\n old = $(\"#slot_\"+scope.$id+\"_\"+i).find(\".old\")\n old.off('transitionend webkitTransitionEnd oTransitionEnd')\n old.removeClass(\"old\").addClass(\"next\")\n pickLock()\n });\n next.removeClass(\"next\").addClass(\"current\")\n \n })\n return failures == 0\n }\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"roll-display","x":560,"y":900,"wires":[[]]},{"id":"f94b5cb02555e740","type":"ui_template","z":"bf0d83d32eec75c2","group":"674712f8d915bcc0","name":"display css","order":8,"width":0,"height":0,"format":"<style id='roll-display-styles'>\n .roll-display {\n --size: 2;\n --speed: .95;\n display: flex;\n align-content: center;\n justify-content: center;\n align-items: center;\n flex-direction: row;\n overflow: hidden;\n }\n\n .roll-display .slot {\n height: calc(var(--size) * 1em);\n width: calc(var(--size) * .8em);\n text-align: center;\n line-height: calc(var(--size) * 1em);\n outline: 1px solid #4d4d4d;\n outline-offset: -3px;\n border: 3px solid #000000a3;\n border-radius: 6px;\n position: relative;\n overflow: hidden;\n background: linear-gradient(0deg, #161616, #7e7e7e, #161616);\n }\n\n .roll-display .last {\n background: linear-gradient(0deg, #5a5a5a, #e5e5e5, #5d5d5d);\n }\n\n .roll-display .num {\n position: absolute;\n left: 0;\n right: 0;\n user-select: none;\n font-size: calc(var(--size) *.7em);\n }\n\n .roll-display .last>.num {\n color: #2f2f2f;\n }\n\n .roll-display .num:after {\n content: \"\";\n position: absolute;\n left: -2%;\n width: 20%;\n top: 0;\n height: 100%;\n background: linear-gradient(to bottom,\n #00000050,\n #00000050 50%,\n transparent 50%,\n transparent);\n background-size: 100% 7%;\n }\n \n .roll-display .num:before {\n content: \"\";\n position: absolute;\n right: -2%;\n width: 20%;\n top: 0;\n height: 100%;\n background: linear-gradient(to bottom,\n #00000050,\n #00000050 50%,\n transparent 50%,\n transparent);\n background-size: 100% 7%;\n }\n\n .roll-display .last>.num:before{\n background: linear-gradient(to bottom,\n #00000030,\n #00000030 50%,\n transparent 50%,\n transparent);\n background-size: 100% 7%;\n }\n \n .roll-display .last>.num:after{\n background: linear-gradient(to bottom,\n #00000030,\n #00000030 50%,\n transparent 50%,\n transparent);\n background-size: 100% 7%;\n }\n\n .roll-display .current {\n top: 0%;\n transition: top calc(var(--speed) *1s) ease-in;\n }\n\n .roll-display .old {\n top: 100%;\n transition: top calc(var(--speed) *1s) ease-in;\n }\n\n .roll-display .next {\n top: -100%;\n transition: none;\n }\n\n .roll-display .gap {\n position: relative;\n width: 8px;\n }\n\n .roll-display .gap:after {\n content: '•';\n position: absolute;\n text-align: center;\n inset: 0;\n color: #b9b9b9;\n }\n\n .roll-display .label {\n display: flex;\n flex-direction: column;\n justify-content: center;\n padding-right: 1em;\n }\n\n .roll-display .unit {\n display: flex;\n flex-direction: column;\n justify-content: center;\n padding-left: 0.5em;\n }\n</style>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"global","className":"","x":570,"y":820,"wires":[[]]},{"id":"aac264ba0df29e45","type":"ui_template","z":"bf0d83d32eec75c2","group":"55888982bdd0637f","name":"display last 2 red","order":5,"width":0,"height":0,"format":"<style>\n .roll-display.red .last>.num {\n color: #bd1818;\n /* text-shadow: 0px 0px 1px black;*/\n }\n</style>\n\n<div id=\"{{'slot_'+$id+'_0'}}\" class=\"slot\" ng-init=\"init()\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_1'}}\" class=\"slot\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_2'}}\" class=\"slot\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_3'}}\" class=\"slot\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_4'}}\" class=\"slot\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_5'}}\" class=\"slot\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div class=\"gap\"></div>\n<div id=\"{{'slot_'+$id+'_6'}}\" class=\"slot last\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div id=\"{{'slot_'+$id+'_7'}}\" class=\"slot last\">\n <div class=\"num current\"></div>\n <div class=\"num next\"></div>\n</div>\n<div class=\"unit\">MWh</div>\n\n<script>\n (function(scope) {\n let lock = 0\n const count = 8\n scope.previous = Array(count).fill(-1);\n scope.drawlist = Array(count).fill(true);\n scope.inited = false\n scope.waitingData = null \n scope.init = function(){\n if($(\"#slot_\"+scope.$id+\"_0\".length)){\n actuallyInit()\n }\n else{\n setTimeout(function(){\n actuallyInit()\n },40)\n }\n }\n function actuallyInit(){ \n lock = 0\n if(scope.waitingData){ \n setTimeout(function(){\n callDraw()\n },40)\n }\n scope.inited = true\n }\n\n scope.$watch('msg', function(msg) {\n if (msg) { \n let data = msg.payload.toString().split('')\n if(!scope.inited){\n scope.waitingData = data\n return\n }\n if(checkCollapsed()){\n scope.waitingData = data\n return\n }\n if(locked()){\n scope.waitingData = data\n validateLocks()\n }\n else{\n if(!draw(data)){\n scope.waitingData = data\n setTimeout(function(){\n validateLocks()\n },40)\n }\n \n }\n }\n });\n\n function checkCollapsed(){\n let d = $(\"#slot_\"+scope.$id+\"_0\").closest('.nr-dashboard-cardcontainer').css('display')\n if(d == 'none'){\n lock = 0\n return true\n }\n return false\n }\n\n function locked(){\n return lock > 0 \n }\n\n function pickLock(){\n if(locked()){\n lock --;\n }\n if(lock < 0){\n lock = 0;\n return\n }\n if(!locked()){\n if(scope.waitingData){\n setTimeout(function(){\n callDraw()\n },10) \n }\n }\n }\n\n function validateLocks(){\n let e = 0\n for(let i = 0;i<count;++i){\n e += $(\"#slot_\"+scope.$id+\"_\"+i).find(\".current\").text().length\n } \n if(e == 0){ \n lock = 0\n callDraw()\n }\n }\n\n function callDraw(){ \n if(!scope.waitingData){\n return\n }\n draw(Array.from(scope.waitingData)) \n scope.waitingData = null;\n }\n\n function draw(data){\n if(locked()){ \n return\n }\n let failures = 0 \n while(data.length < count){\n data.unshift(\"0\")\n }\n data.forEach((d,i)=>{\n //d = parseInt(d)\n if(scope.previous[i] == d && $(\"#slot_\"+scope.$id+\"_\"+i).find(\".current\").text().length > 0){\n scope.drawlist[i] = false\n }\n else{\n scope.previous[i] = d\n scope.drawlist[i] = true\n lock ++;\n } \n })\n data.forEach((d,i) => {\n if(scope.drawlist[i] == false){\n return\n }\n let current = $(\"#slot_\"+scope.$id+\"_\"+i).find(\".current\")\n let next = $(\"#slot_\"+scope.$id+\"_\"+i).find(\".next\")\n let old = $(\"#slot_\"+scope.$id+\"_\"+i).find(\".old\") \n \n if(next.length == 0 && old.length > 0){\n old.removeClass(\"old\").addClass(\"next\")\n next = $(\"#slot_\"+scope.$id+\"_\"+i).find(\".next\")\n }\n \n next.text(data[i])\n if(next.length == 0){ \n failures ++\n } \n current.removeClass(\"current\").addClass(\"old\").on('transitionend webkitTransitionEnd oTransitionEnd', function () {\n old = $(\"#slot_\"+scope.$id+\"_\"+i).find(\".old\")\n old.off('transitionend webkitTransitionEnd oTransitionEnd')\n old.removeClass(\"old\").addClass(\"next\")\n pickLock()\n });\n next.removeClass(\"next\").addClass(\"current\")\n \n })\n return failures == 0\n }\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"roll-display red","x":590,"y":860,"wires":[[]]},{"id":"608b4f75f0cdc9b1","type":"function","z":"bf0d83d32eec75c2","name":"random data","func":"let current = context.get('current') || 1\n\ncurrent += Math.floor(Math.random()*100);\nif(current > 999999){\n current = 1\n}\n\ncontext.set('current',current)\nmsg.payload = current \nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":900,"wires":[["bf8580a24000c55c"]]},{"id":"93a93c8c71124851","type":"inject","z":"bf0d83d32eec75c2","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"2","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":230,"y":900,"wires":[["608b4f75f0cdc9b1"]]},{"id":"55888982bdd0637f","type":"ui_group","name":"1. group","tab":"62083694d0eab7ca","order":1,"disp":true,"width":"6","collapse":false,"className":""},{"id":"674712f8d915bcc0","type":"ui_group","name":"1. group","tab":"8482b305c416b850","order":1,"disp":true,"width":"6","collapse":true,"className":"ordered-1"},{"id":"62083694d0eab7ca","type":"ui_tab","name":"Home","icon":"dashboard","order":1,"disabled":false,"hidden":false},{"id":"8482b305c416b850","type":"ui_tab","name":"Away","icon":"dashboard","disabled":false,"hidden":false}]