[{"data":1,"prerenderedAt":322},["ShallowReactive",2],{"i-lucide:menu":3,"i-lucide:arrow-up-right":8,"i-lucide:moon":10,"i-lucide:sun":12,"i-lucide:rss":14,"i-simple-icons:github":16,"i-simple-icons:linkedin":18,"post-\u002F2017\u002Fscreen":21,"surround-\u002F2017\u002Fscreen":309,"i-lucide:arrow-left":318,"i-lucide:arrow-right":320},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":7},0,24,false,"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 5h16M4 12h16M4 19h16\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":9},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M7 7h10v10M7 17L17 7\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":11},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":13},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Ccircle cx=\"12\" cy=\"12\" r=\"4\"\u002F>\u003Cpath d=\"M12 2v2m0 16v2M4.93 4.93l1.41 1.41m11.32 11.32l1.41 1.41M2 12h2m16 0h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":15},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"M4 11a9 9 0 0 1 9 9M4 4a16 16 0 0 1 16 16\"\u002F>\u003Ccircle cx=\"5\" cy=\"19\" r=\"1\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":17},"\u003Cpath fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":19,"hidden":20},"\u003Cpath fill=\"currentColor\" d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037c-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85c3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.06 2.06 0 0 1-2.063-2.065a2.064 2.064 0 1 1 2.063 2.065m1.782 13.019H3.555V9h3.564zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0z\"\u002F>",true,{"id":22,"title":23,"body":24,"category":297,"comments":298,"date":299,"description":30,"excerpt":298,"extension":300,"image":228,"meta":301,"navigation":20,"path":302,"readingTime":303,"seo":304,"stem":305,"subtitle":306,"tags":298,"wordCount":307,"__hash__":308},"posts\u002F2017\u002Fscreen.md","Repurposing an Old Phone: Personal HUD Screen",{"type":25,"value":26,"toc":284},"minimark",[27,31,36,44,47,52,86,93,96,116,122,125,129,132,141,162,165,168,177,188,197,200,223,229,233,236,245,249,270],[28,29,30],"p",{},"I was running with my buddy Priit, when I accidentally dropped my trusty Nexus 5 phone and cracked its screen.\nThe screen had fractures, but was still perfectly legible; and the hardware still functioned.\nSo, instead of throwing it away, I gave it a new life on my wall, as a personal \"HUD\" screen, showing relevant info\nsuch as weather, time and bus schedules.",[32,33,35],"h2",{"id":34},"revision-1","Revision #1",[28,37,38,39,43],{},"The first revision was just a hacked-together ",[40,41,42],"code",{},".html"," page with some inline Javascript string concatenation.\nIt fetched relevant data from publicly available API-s and displayed it out. The page took as little work as possible,\nto get the prototype usefulness validated.",[28,45,46],{},"As I was already running a in-home Openshift3 cluster, deploying it into a Nginx stateless web container\nwas a non-issue and after two nights of hacking I had a functional prototype.",[48,49,51],"h3",{"id":50},"features","Features",[53,54,55,66,75,83],"ul",{},[56,57,58,59],"li",{},"Display current time using ",[60,61,65],"a",{"href":62,"rel":63},"https:\u002F\u002Fmomentjs.com",[64],"nofollow","moment.js",[56,67,68,69,74],{},"Show current weather and the forecast for the next three hours using data from ",[60,70,73],{"href":71,"rel":72},"https:\u002F\u002Fopenweathermap.org\u002Fapi",[64],"OpenWeatherMap"," API",[56,76,77,78],{},"Show the departure times (using real-time GPS predictions) of busses from my nearest stop using the undocumented and quite horrible CSV \"API\" from ",[60,79,82],{"href":80,"rel":81},"https:\u002F\u002Fsoiduplaan.tallinn.ee",[64],"Tallinn city",[56,84,85],{},"Always-on screen on dark background",[28,87,88],{},[89,90],"img",{"alt":91,"src":92},"Revision 1","\u002Fcontent\u002F2017\u002Fscreen\u002Frev1.jpg",[28,94,95],{},"The solution was quick to implement and worked; providing useful information when I was heading out.\nHowever, as quick PoC-s often do, it had its problems:",[53,97,98,101,104,107],{},[56,99,100],{},"The codebase was a mess - literally hacked together into a couple of files. It needed refactoring and \"beautification\" before I dared show it to anyone",[56,102,103],{},"All config was hardcoded",[56,105,106],{},"It was not very modular nor expandable (with new features)",[56,108,109,110,115],{},"After a couple of months, I learned what ",[60,111,114],{"href":112,"rel":113},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FScreen_burn-in",[64],"screen burn-in"," means",[28,117,118],{},[89,119],{"alt":120,"src":121},"Screen burn","\u002Fcontent\u002F2017\u002Fscreen\u002Fscreen-burn.jpg",[28,123,124],{},"Screen burn-in: the outlines of previous content are visibly \"burned in\".",[32,126,128],{"id":127},"revision-2","Revision #2",[28,130,131],{},"I was satisfied that the concept worked and provided value; time to refactor.",[28,133,134,135,140],{},"I ordered a cheap-ish, but still good quality (excellent, large screen!) tablet from China -",[60,136,139],{"href":137,"rel":138},"https:\u002F\u002Fwww.gearbest.com\u002Ftablet-pcs\u002Fpp_690462.html",[64],"Teclast T10",". This gave me larger screen real-estate and also a better quality (and not broken) touch-screen.",[28,142,143,144,149,150,155,156,161],{},"I rewrote the code from zero and split the project into two parts: ",[60,145,148],{"href":146,"rel":147},"https:\u002F\u002Fgithub.com\u002Fsqroot-eu\u002Fscreen-frontend",[64],"screen-frontend"," (",[60,151,154],{"href":152,"rel":153},"https:\u002F\u002Fvuejs.org",[64],"Vue.js",") and ",[60,157,160],{"href":158,"rel":159},"https:\u002F\u002Fgithub.com\u002Fsqroot-eu\u002Fscreen-backend",[64],"screen-backend"," (Python \u002F Flask). This allowed me to do it in a API-first manner and also keep everything nice and apart.",[28,163,164],{},"Getting to know Vue and Webpack without prior major JavaScript experience was a challenge, especially the asset build process (I insist in using CoffeeScript and Sass over their 'plainer' cousins), but finally, I succeeded.",[28,166,167],{},"I removed most of the hardcoded values (weather station to use, bus stop ID-s) from the code, which makes it more portable and - in theory - allows others to benefit from the project, although this is not a goal in itself.",[28,169,170,171,176],{},"I switched to weather data provider to the ",[60,172,175],{"href":173,"rel":174},"http:\u002F\u002Filmateenistus.ee",[64],"national weather service",",\nbecause they have local data (although in XML...) and audio reports.",[28,178,179,180,183,184,187],{},"Why would ",[40,181,182],{},"http:\u002F\u002Fwww.ilmateenistus.ee\u002Filma_andmed\u002Fxml\u002Fhoiatus.php"," API respond with ",[40,185,186],{},"HTTP 404"," for valid queries is beyond me.\nWhoever developed that endpoint owes me 30 minutes of lost time.",[28,189,190,191,196],{},"I solved the screen burn-in problem by keeping the screen OFF most of the time. The tablet turns the screen ON automatically\nfor five minutes when the front-facing camera detects motion. This saves the screen, some power and also keeps the ambient\nlight low at night. This can be done with the ",[60,192,195],{"href":193,"rel":194},"https:\u002F\u002Fwww.fully-kiosk.com\u002F",[64],"Fully kiosk browser",".",[48,198,51],{"id":199},"features-1",[53,201,202,205,208,211,214,217,220],{},[56,203,204],{},"Show current time",[56,206,207],{},"Show current weather + forecast for the next couple of days",[56,209,210],{},"Play weather forecast in audio form (audio provided by the national weather service)",[56,212,213],{},"Highlight and display weather alerts for my area, if there are any",[56,215,216],{},"Show bus schedule in real-time",[56,218,219],{},"Track investments",[56,221,222],{},"Turn screen ON only if motion is detected",[28,224,225],{},[89,226],{"alt":227,"src":228},"Revision 2","\u002Fcontent\u002F2017\u002Fscreen\u002Frev2.jpg",[32,230,232],{"id":231},"future","Future?",[28,234,235],{},"Tentative future plans include refactoring the codebase some more (don't look at my variable names...)\nand adding more features, for example a tab that shows readings from home IoT sensors.",[28,237,238,239,244],{},"As the tablet is running its original Chinese firmware, I don't consider it a \"trusted\" device: it could\nbe doing ",[60,240,243],{"href":241,"rel":242},"https:\u002F\u002Fwww.cyberscoop.com\u002Fandroid-malware-china-huawei-zte-kryptowire-blu-products",[64],"much more than only detect motion","\nwith its camera. Hence, I will either try to flash\nstock Android to it (tried once, failed, any links to tutorials for this specific model are appreciated)\nor more likely, use my PFSense & IoT WiFi to block its access to the outside Internet.",[32,246,248],{"id":247},"skill-level-ups","Skill Level-Ups",[53,250,251,254,257],{},[56,252,253],{},"First contact with Vue.js, Webpack and with Javascript frontend frameworks in general",[56,255,256],{},"Python \u002F Flask experience",[56,258,259,260,264,265,269],{},"Open source credz (both the ",[60,261,263],{"href":158,"rel":262},[64],"backend"," and ",[60,266,268],{"href":146,"rel":267},[64],"frontend"," are in GitHub)",[28,271,272,273,278,279,196],{},"The project can be seen and used from ",[60,274,277],{"href":275,"rel":276},"https:\u002F\u002Fscreen.sqroot.eu",[64],"screen.sqroot.eu"," and the backend API is at ",[60,280,283],{"href":281,"rel":282},"https:\u002F\u002Fapi.screen.sqroot.eu",[64],"api.screen.sqroot.eu",{"title":285,"searchDepth":286,"depth":286,"links":287},"",2,[288,292,295,296],{"id":34,"depth":286,"text":35,"children":289},[290],{"id":50,"depth":291,"text":51},3,{"id":127,"depth":286,"text":128,"children":293},[294],{"id":199,"depth":291,"text":51},{"id":231,"depth":286,"text":232},{"id":247,"depth":286,"text":248},"Projects",null,"2017-12-06","md",{},"\u002F2017\u002Fscreen","about 4 minutes",{"title":23,"description":30},"2017\u002Fscreen","Instead of throwing away old hardware, why not put it to good use as a digital \"picture frame\" on your wall?",770,"VGao30Kf47W2Z-fRqeQ-nIe6SeDop0owuZjRRkjC9Tg",[310,314],{"title":311,"path":312,"stem":313,"children":-1},"Snarky Doorbell","\u002F2017\u002Fsnarky-doorbell","2017\u002Fsnarky-doorbell",{"title":315,"path":316,"stem":317,"children":-1},"\"Shut It!\" - An Arduino Door Alarm","\u002F2018\u002Fshut-it-an-arduino-door-alarm","2018\u002Fshut-it-an-arduino-door-alarm",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":319},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"m12 19l-7-7l7-7m7 7H5\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":321},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7l7 7l-7 7\"\u002F>",1790228552549]