From 74394f10f3564a64171ed3170a1c0502026381c4 Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:04:57 +0100 Subject: [PATCH 01/14] Create tvremove.app.js added inital code --- apps/tvremote/tvremove.app.js | 631 ++++++++++++++++++++++++++++++++++ 1 file changed, 631 insertions(+) create mode 100644 apps/tvremote/tvremove.app.js diff --git a/apps/tvremote/tvremove.app.js b/apps/tvremote/tvremove.app.js new file mode 100644 index 000000000..c115fc970 --- /dev/null +++ b/apps/tvremote/tvremove.app.js @@ -0,0 +1,631 @@ +require("Font7x11Numeric7Seg").add(Graphics); + +let deviceFileName = "tvdevicelist.json"; +let serverDataFile = "tvremote.settings.json"; +let serverData = require("Storage").readJSON(serverDataFile, true); +let devicefile = require("Storage").readJSON(deviceFileName, true); + +//console.log(require("Storage").list()); +//console.log(devicefile); + + +let serverDns = serverData.webServerDns; +let serverPort = serverData.port; +let tvIp = serverData.tvIp; +let username = serverData.username; +let password = serverData.password; + +let panaIp = "tvIp" in serverData ? serverData.tvIp : 'undefined'; +let settingsPort = "port" in serverData ? serverData.port : 'undefined'; + +let counter; +let countdownTimer = null; +let drawTimeout; +let currNumber = null; +let prevNumber = null; +let selected = "NONE"; +let currentScreen = "power"; +let font = 'Vector'; +let RESULT_HEIGHT = 24; +let RIGHT_MARGIN = 15; +let midpoint = (g.getWidth() / 2); +let IP_AREA = [0, RESULT_HEIGHT, g.getWidth(), g.getHeight()]; // values used for key buttons +let KEY_AREA = [0, 24, g.getWidth(), g.getHeight()]; +let COLORS = {DEFAULT: ['#FF0000'], BLACK: ['#000000'], WHITE: ['#FFFFFF'], GREY: ['#808080', '#222222']}; // background + +let sourceApps = { + "selection": { + '!': { + grid: [0, 1], + globalGrid: [0, 0], + key: 'down' + }, + '^': { + grid: [0, 0], + globalGrid: [0, 0], + key: 'up' + }, + '<': { + grid: [1, 0], + globalGrid: [1, 0], + key: 'left' + }, + '>': { + grid: [1, 1], + globalGrid: [1, 0], + key: 'right' + } + }, + "volume": { + 'Vol Up': { + grid: [0, 0], + globalGrid: [0, 0], + key: 'volume_up' + }, + 'Vol Dwn': { + grid: [0, 1], + globalGrid: [1, 0], + key: 'volume_down' + }, + 'Mute': { + grid: [1, 0], + globalGrid: [2, 0], + key: 'mute' + }, + 'Options': { + grid: [1, 1], + globalGrid: [2, 0], + key: 'option' + } + }, + "numbers": { + '<': { + grid: [0, 3], + globalGrid: [1, 4] + }, + '0': { + grid: [1, 3], + globalGrid: [1, 4] + }, + 'ok': { + grid: [2, 3], + globalGrid: [2, 4] + }, + '1': { + grid: [0, 2], + globalGrid: [0, 3] + }, + '2': { + grid: [1, 2], + globalGrid: [1, 3] + }, + '3': { + grid: [2, 2], + globalGrid: [2, 3] + }, + '4': { + grid: [0, 1], + globalGrid: [0, 2] + }, + '5': { + grid: [1, 1], + globalGrid: [1, 2] + }, + '6': { + grid: [2, 1], + globalGrid: [2, 2] + }, + '7': { + grid: [0, 0], + globalGrid: [0, 1] + }, + '8': { + grid: [1, 0], + globalGrid: [1, 1] + }, + '9': { + grid: [2, 0], + globalGrid: [2, 1] + } + }, + "apps": [{ + "name": "Disney +", + "key": "disney" + }, + { + "name": "Netflix", + "key": "netflix" + }, + { + "name": "Amazon Prime", + "key": "prime" + }, + { + "name": "Youtube", + "key": "youtube" + }, + { + "name": "Home", + "key": "home" + }, + { + "name": "TV", + "key": "tv" + }, + { + "name": "HDMI1", + "key": "hdmi1" + }, + { + "name": "HDMI2", + "key": "hdmi2" + }, + { + "name": "HDMI3", + "key": "hdmi3" + }, + { + "name": "HDMI4", + "key": "hdmi4" + } + ] +}; +let numbersGrid = [3, 4]; +let selectionGrid = [2, 2]; +let volumeGrid = [2, 2]; +let appData = sourceApps.apps; +let volume = sourceApps.volume; +let selection = sourceApps.selection; +let numbers = sourceApps.numbers; + +function assignScreen (screen) { + currentScreen = screen; + console.log(currentScreen); +} + +function sendPost(keyPress) { + let serverPort = settingsPort; + let tvIp = panaIp; + let credentials = btoa(`${username}:${password}`); + let serverUrl = `https://${serverDns}:${serverPort}`; + + let keyJson = { + "command": keyPress, + "tvip": tvIp, + }; + + Bangle.http( + serverUrl, + { + method: 'POST', + headers: { + 'Authorization': `Basic ${credentials}`, + }, + body: JSON.stringify(keyJson) + }) + .then(response => { + console.log("Response received:", response); + }).catch(error => { + console.error("Error sending data:", error); + }); +} + +function receiveDevices() { + let serverPort = settingsPort; + let credentials = btoa(`${username}:${password}`); + let serverUrl = `https://${serverDns}:${serverPort}/ssdp-devices.json`; + return Bangle.http( + serverUrl, + { + method: 'GET', + headers: { + 'Authorization': `Basic ${credentials}` + }, + }).then(data => { + require("Storage").write(deviceFileName, data); + devicefile = require("Storage").readJSON(deviceFileName, true); + }); +} + +function prepareScreen(screen, grid, defaultColor, area) { // grid, [3, 4], colour, grid area size + for (let k in screen) { + if (screen.hasOwnProperty(k)) { + screen[k].color = screen[k].color || defaultColor; + let position = []; + let xGrid = (area[2] - area[0]) / grid[0]; + let yGrid = (area[3] - area[1]) / grid[1]; + + //console.log(xGrid + " " + yGrid); + position[0] = area[0] + xGrid * screen[k].grid[0]; + position[1] = area[1] + yGrid * screen[k].grid[1]; + + position[2] = position[0] + xGrid - 1; + position[3] = position[1] + yGrid - 1; + + screen[k].xy = position; + //console.log("prepared " + screen+"\n"); + } + } + Bangle.setUI({ + mode: "custom", + back: function() { + appMenu(); + } + }); +} + +function drawKey(name, k, selected) { // number, number data, NONE + g.setColor(COLORS.DEFAULT[0]); // set color for rectangles + g.setFont('Vector', 20).setFontAlign(0, 0); + g.fillRect(k.xy[0], k.xy[1], k.xy[2], k.xy[3]); // create rectangles based on letters xy areas + + g.setColor(COLORS.BLACK[0]).drawRect(k.xy[0], k.xy[1], k.xy[2], k.xy[3]); + + g.setColor(COLORS.WHITE[0]); // color for numbers + g.drawString(name, (k.xy[0] + k.xy[2]) / 2, (k.xy[1] + k.xy[3]) / 2); // center the keys to the rectangle that is drawn +} + +function drawKeys(area, buttons) { + g.setColor(COLORS.DEFAULT[0]); // background colour + g.fillRect(area[0], area[1], area[2], area[3]); // number grid area + for (let k in buttons) { + if (buttons.hasOwnProperty(k)) { + drawKey(k, buttons[k], k == selected); + } + } +} + +function displayOutput(num, screenValue) { // top block + num = num.toString(); + g.setFont('Vector', 18); //'7x11Numeric7Seg' + g.setFontAlign(1, 0); + g.setBgColor(0).clearRect(0, 0, g.getWidth(), RESULT_HEIGHT - 1); + g.setColor(-1); // value + + g.drawString(num, g.getWidth() - RIGHT_MARGIN, RESULT_HEIGHT / 2); +} + +function buttonPress(val, screenValue) { + + if (screenValue === "ip") { + if (val === "<") currNumber = currNumber.slice(0,-1); + else if (val === ".") currNumber = currNumber + "."; + else currNumber = currNumber == null ? val : currNumber + val; // currNumber is null if no value pressed + + let ipcount = (currNumber.match(/\./g) || []).length; + if (ipcount > 3 || currNumber.length > 15) currNumber = currNumber.slice(0,-1); + + displayOutput(currNumber, screenValue); + } + + checkValue = appData.some(app => app.name === screenValue); // check app data + + if (checkValue) sendPost(val); // app values + + if (screenValue === "numbers") { + if (val === '<') sendPost('back'); + else if (val === 'ok') sendPost('enter'); + else sendPost("num_"+val); + } + + else if (screenValue === "selection") sendPost(selection[val].key); + else if (screenValue === "volume") sendPost(volume[val].key); +} + +const powerScreen = () => { + currentScreen = "power"; + g.setColor(COLORS.GREY[0]).fillRect(0, 24, g.getWidth(), g.getWidth()); // outer circ + g.setColor(COLORS.WHITE[0]).fillCircle(midpoint, 76 + 24, 50); // inner circ + g.setColor(COLORS.BLACK[0]).setFont('Vector', 25).setFontAlign(0, 0).drawString("On/Off", 88, 76 + 24); // circ text + + Bangle.setUI({ + mode: "custom", + back: function() { + mainMenu(); + } + }); +}; + +const appMenu = () => { + + assignScreen("apps"); + E.showScroller({ + h: 54, + c: appData.length, + draw: (i, r) => { + let sourceOption = appData[i]; + g.setColor(COLORS.DEFAULT[0]).fillRect((r.x), (r.y), (r.x + r.w), (r.y + r.h)); + g.setColor(COLORS.BLACK[0]).drawRect((r.x), (r.y), (r.x + r.w), (r.y + r.h)); + g.setColor(COLORS.WHITE[0]).setFont(font, 20).setFontAlign(-1, 1).drawString(sourceOption.name, 15, r.y + 32); + }, + + select: i => { + let sourceOption = appData[i]; + let appPressed = sourceOption.name; + let appKey = sourceOption.key; + buttonPress(appKey, appPressed); + }, + + back: main => { + E.showScroller(); + mainMenu(); + }, + }); + g.flip(); // force a render before widgets have finished drawing +}; + +function ipScreen() { + //require("widget_utils").hide(); + assignScreen("ip"); + currNumber = ""; + prepareScreen(numbers, numbersGrid, COLORS.DEFAULT, IP_AREA); + drawKeys(IP_AREA, numbers); + displayOutput(0); +} + +let tvSelector = { + "": { + title: "TV Selector", + back: function() { + load();//E.showMenu(tvSelector); + } + }, + "Panasonic": function() { + assignScreen("power"); + powerScreen(); + }, + "Samsung": function() { + assignScreen("power"); + powerScreen(); + }, + "Settings": function() { + subMenu(); + } +}; + +function samsungmenu() { + //E.showMessage("It's on my list to do", "Error"); +} + +function mainMenu() { + assignScreen("mainmenu"); + E.showMenu(tvSelector); +} + +function clearCountdown() { + if (countdownTimer) { + clearTimeout(countdownTimer); + countdownTimer = null; + } +} + +function countDown(callback) { + require("widget_utils").show(); + if (counter === 0) { + callback(); // Call the callback function when countdown reaches 0 + return; + } + E.showMessage(`Searching for devices...\n${counter}`, "Device Search"); + counter--; + countdownTimer = setTimeout(() => countDown(callback), 1000); +} + +function clearDisplayOutput() { + g.clear(); + Bangle.loadWidgets(); + Bangle.drawWidgets(); +} + +function subMenu() { + + if (typeof IPASSIGN !== 'undefined' && currNumber !== "") { + if (IPASSIGN === "pana") { + console.log("current numeber = " + currNumber); + panaIp = currNumber; + console.log("pana ip " + panaIp); + console.log("default ip " + serverData.tvIp); + serverData.tvIp = panaIp; + require("Storage").write(serverDataFile, serverData); + console.log("tv ip is now " + serverData.tvIp); + + } else if (IPASSIGN === "sams") { + samsIp = currNumber; + + } else if (IPASSIGN === "port") { + settingsPort = currNumber; + console.log("setting port " + settingsPort); + console.log("server port " + serverData.port); + serverData.port = settingsPort; + require("Storage").write(serverDataFile, serverData); + console.log("port is now " + serverData.port); + } + } + + require("widget_utils").show(); + assignScreen("settingssub"); + clearDisplayOutput(); + + let settingssub = { + "": { + title: "Settings", + back: function() { + E.showMenu(tvSelector); + clearCountdown(); + } + }, + }; + + if (typeof settingsPort !== 'undefined' && settingsPort !== 'undefined') { + let portHeader = `Port: ${settingsPort}`; + settingssub[portHeader] = function() { IPASSIGN = "port"; ipScreen();}; + } else { + settingssub["Set DNS Port"] = function() { IPASSIGN = "port"; ipScreen();}; + } + + if (typeof panaIp !== 'undefined' && panaIp !== 'undefined') { + let panaheader = `Pana IP: ${panaIp}`; + settingssub[panaheader] = function() { + IPASSIGN = "pana"; + E.showMenu(deviceSelect); + deviceFile = require("Storage").readJSON("tvdevicelist.json", true); +console.log(devicefile); + }; + } else { + settingssub["Set Pana IP"] = function() { IPASSIGN = "pana"; ipScreen();}; + } + + if (typeof samsIp !== 'undefined' && panaIp !== 'undefined') { + let samsheader = `Sams IP: ${samsIp}`; + settingssub[samsheader] = function() { IPASSIGN = "sams"; ipScreen();}; + } else { + settingssub["Set Sams IP"] = function() { IPASSIGN = "sams"; ipScreen();}; + } + + E.showMenu(settingssub); +} + +const deviceMenu = () => { +let parsedResp = JSON.parse(devicefile.resp); + E.showScroller({ + h: 54, + c: parsedResp.length, + draw: (i, r) => { + let sourceOption = parsedResp[i]; + g.setColor(COLORS.DEFAULT[0]).fillRect((r.x), (r.y), (r.x + r.w), (r.y + r.h)); + g.setColor(COLORS.BLACK[0]).drawRect((r.x), (r.y), (r.x + r.w), (r.y + r.h)); + g.setColor(COLORS.WHITE[0]).setFont(font, 15).setFontAlign(-1, 1).drawString(sourceOption.name, 15, r.y + 32); + }, + + select: i => { + let sourceOption = parsedResp[i]; + let devicePressed = sourceOption.name; + let deviceIp = sourceOption.ip; + + assignScreen("deviceSearch"); + serverData.tvIp = deviceIp.replace('http://', ''); + currNumber = serverData.tvIp; + require("Storage").write(serverDataFile, serverData); + console.log("tv ip is now " + serverData.tvIp); + subMenu(); + }, + + back: main => { + E.showScroller(); + E.showMenu(deviceSelect); + }, + }); + g.flip(); // force a render before widgets have finished drawing +}; + + +let deviceSelect = { + "" : { title : "Device Select", + back : function() { subMenu(); } + }, + "Manual IP Assign" : function() { + ipScreen(); + }, + "Device Select" : function() { + receiveDevices(); + counter = 5; + countDown(deviceMenu); + } +}; + + +function swipeHandler(LR, UD) { + if (LR == -1) { // swipe left + if (currentScreen === "power") { + assignScreen("apps"); + appMenu(); + + } else if (currentScreen === "apps") { + //require("widget_utils").hide(); + assignScreen("selection"); + E.showScroller(); + prepareScreen(selection, selectionGrid, COLORS.DEFAULT, KEY_AREA); + drawKeys(KEY_AREA, selection); + + } else if (currentScreen === "volume") { + sendPost("fast_forward"); + + } else if (currentScreen === "selection") { + sendPost("enter"); + } + } + if (LR == 1) { // swipe right + if (currentScreen === "apps") { + assignScreen("power"); + E.showScroller(); + powerScreen(); + } else if (currentScreen === "volume") { + sendPost("rewind"); + } else if (currentScreen === "selection") { + sendPost("back"); + } + } + if (UD == -1) { // swipe up + if (currentScreen === "selection") { + assignScreen("volume"); + prepareScreen(volume, volumeGrid, COLORS.DEFAULT, KEY_AREA); + drawKeys(KEY_AREA, volume); + } else if (currentScreen === "volume") { + sendPost("enter"); + } else if (currentScreen === "ip") { + buttonPress(".", "ip"); + } else if (currentScreen == "numbers") { + assignScreen("selection"); + prepareScreen(selection, selectionGrid, COLORS.DEFAULT, KEY_AREA); + drawKeys(KEY_AREA, selection); + } + } + if (UD == 1) { // swipe down + if (currentScreen === "volume") { + assignScreen("selection"); + prepareScreen(selection, selectionGrid, COLORS.DEFAULT, KEY_AREA); + drawKeys(KEY_AREA, selection); + } else if (currentScreen === "selection") { + assignScreen("numbers"); + prepareScreen(numbers, numbersGrid, COLORS.DEFAULT, KEY_AREA); + drawKeys(KEY_AREA, numbers); + } + } +} +Bangle.on('swipe', swipeHandler); + + +function touchHandler(button, e) { + const screenActions = { + ip: () => checkButtons(numbers), + volume: () => checkButtons(volume), + numbers: () => checkButtons(numbers), + selection: () => checkButtons(selection), + power: () => { + if (Math.pow(e.x - 88, 2) + Math.pow(e.y - 88, 2) < 2500) { + sendPost("power"); + } + } + }; + + function checkButtons(buttonMap) { + for (let key in buttonMap) { + if (typeof buttonMap[key] === "undefined") continue; + let r = buttonMap[key].xy; + + if (e.x >= r[0] && e.y >= r[1] && e.x < r[2] && e.y < r[3]) { + if (currentScreen === "ip" && key === "ok") { + subMenu(); + } else { + buttonPress("" + key, currentScreen); + } + } + } + } + + if (currentScreen in screenActions) screenActions[currentScreen](); +} +Bangle.on('touch', touchHandler); + +Bangle.loadWidgets(); +Bangle.drawWidgets(); +mainMenu(); From d6a506ac9ad8d3abd553d130d0dafc952f672100 Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:07:09 +0100 Subject: [PATCH 02/14] Rename tvremove.app.js to app.js --- apps/tvremote/{tvremove.app.js => app.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename apps/tvremote/{tvremove.app.js => app.js} (100%) diff --git a/apps/tvremote/tvremove.app.js b/apps/tvremote/app.js similarity index 100% rename from apps/tvremote/tvremove.app.js rename to apps/tvremote/app.js From 55bdf8cff73d0e833e7fd2fab3d15c74280db219 Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:08:26 +0100 Subject: [PATCH 03/14] added app image app image --- apps/tvremote/app.png | Bin 0 -> 607 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/tvremote/app.png diff --git a/apps/tvremote/app.png b/apps/tvremote/app.png new file mode 100644 index 0000000000000000000000000000000000000000..2d661af88aad420fed5bff452fb1bf216b938bb9 GIT binary patch literal 607 zcmV-l0-*hgP)F6XU=)mkAqj%=tK38D>)9BFA_&Q=oEVZ<^*=PP zvVvhKf}osg7mx=+@+!XuWmU^F3`G!}SG5}KiQLLngwzIR)Qg4YR%S8`CJ>lgCKH@j z`5By)aw|M=s>`Ss3(2jz42iL-EQY}ZLh~vsps{evBR7{FLq4}sG&ryF64de6L$gXG z7zPsvt*>VTISk~$;M}V6A?0{{8G6V#9$SVUa*jvO(80Nt_XphZ=WpEkKNvB?s0VHg z@fbLC`ojO^o3{V&ojLD+MQhLh^wR48VW~O)eWDZpI|oJmxAP1AZ|?37ra^p=I7l9( z4x}EW52PPt4qZL4`{=3v(U}Ec$59K1r{@0OcHk(6Bk#^%|Noax$p2qDq5tnL+&~=< zM5N_W)$uSKoRo>-0g%K0i@SjFFWpeeJ+SB4DVjJQhW8vhMOzO{TeO^}9+Vb-uZrWzz_`Fh@dLSXMjJ6)|iB6`e2YjNFY3l*!;3%4U zz$GM_wjQwd4yLIGti6Nbc1>Ne>HmZ!>oD-h_JH$HPeMLJJrl>%u^637izGzPifk$^ zJV5WJ#Cd8bA$m9F&Qse1ARL*IPZP&SWfq{=Lz5%~iv^Hn)O37IcH#fM$IsBm14F(` td47;!hEWgP7$Pw+3P!;w7zGUU0s!Tn)#ptDBbERF002ovPDHLkV1oOB5wQRO literal 0 HcmV?d00001 From 99b09b56174e8aec82aecda7541703be345063da Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:18:42 +0100 Subject: [PATCH 04/14] Create app-icon.js adding app-icon --- apps/tvremote/app-icon.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/tvremote/app-icon.js diff --git a/apps/tvremote/app-icon.js b/apps/tvremote/app-icon.js new file mode 100644 index 000000000..00903b468 --- /dev/null +++ b/apps/tvremote/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4kA///7fOlMytX5muVt9T5lK6eMy2l1H4nO+i3d19jq2VxdCltTMf4A/AFsFqAYWqoXXGCxIEJqZIDGiYXCgpkTIYRjUFgIuUJAQuPgMRACEQC+0aw98+3ul3m/l3xQXMjVSkQAH/QUB2c7C48fCxEil4XBnOTC4+iC5Mi0IXKoQXKoJHKuQXKuJ3K4QXK4IXKsQXKsIXK8QXK8IXXjvd7vRC9Z3iU5hHKa5gXKoQXKoJHK0QXK0IXKj4WJksRI5UaqQXI5QXLDAOHvn290u838u+KIoZHIACAX0AH4A/ABo=")) From 1906e8a8b9a844e72cf7ee6d0cf79925b03c1033 Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Mon, 26 Aug 2024 09:26:39 +0100 Subject: [PATCH 05/14] Create metadata.json added metadata --- apps/tvremote/metadata.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 apps/tvremote/metadata.json diff --git a/apps/tvremote/metadata.json b/apps/tvremote/metadata.json new file mode 100644 index 000000000..a624b625f --- /dev/null +++ b/apps/tvremote/metadata.json @@ -0,0 +1,15 @@ +{ "id": "tvremote", + "name": "TV Remote", + "shortName":"TV Remote", + "icon": "app.png", + "version":"0.01", + "description": "remote for controlling your tv, using a webserver and the bangle Gadget Bridge (https://www.espruino.com/Gadgetbridge).", + "icon": "app.png", + "tags": "remote", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"tvremote.app.js","url":"app.js"}, + {"name":"tvremote.img","url":"app-icon.js","evaluate":true} + ] +} From 022181a3e577643e82d811dd30f2004de96df6ea Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Wed, 28 Aug 2024 12:36:17 +0100 Subject: [PATCH 06/14] Update app.js amended app back button --- apps/tvremote/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tvremote/app.js b/apps/tvremote/app.js index c115fc970..9fb88fd2d 100644 --- a/apps/tvremote/app.js +++ b/apps/tvremote/app.js @@ -348,7 +348,7 @@ const appMenu = () => { back: main => { E.showScroller(); - mainMenu(); + powerScreen(); }, }); g.flip(); // force a render before widgets have finished drawing From abec3f7701a95c377c246351ee70954e2b7572ed Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:02:22 +0100 Subject: [PATCH 07/14] Create README.md --- apps/tvremote/README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/tvremote/README.md diff --git a/apps/tvremote/README.md b/apps/tvremote/README.md new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/apps/tvremote/README.md @@ -0,0 +1 @@ + From f3bbaeb7f0b7d7c7bc7ed66d1f28516a35e49e8e Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:20:56 +0100 Subject: [PATCH 08/14] Update README.md --- apps/tvremote/README.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/apps/tvremote/README.md b/apps/tvremote/README.md index 8b1378917..a18c7a81b 100644 --- a/apps/tvremote/README.md +++ b/apps/tvremote/README.md @@ -1 +1,25 @@ +# TV Remote +A [BangleJS 2](https://shop.espruino.com/banglejs2) app that allows the user to send TV input signals from their watch to their TV. +Currenly there is only support for Panasonic viera TV's however support for other brands may be considered in interest is there. +# Requirements +1. The [Bangle GadgetBridge App](https://www.espruino.com/Gadgetbridge) with permissions allowed for `http requests`. +2. A domain name and DNS created. +3. A webserver that the DNS points to, that is set up to receive and process the watch http requests. [Here](https://github.com/Guptilious/banglejs-tvremote-webserver) is one I have created that should complete the full set up for users - provided they have their own domain name and DNS created. + +# Set Up +You will need to upload the below JSON file to your BangleJS, which will be used for config settings. At minimum you must provide: +* `webServerDNS` address, which points to your webserver. +* `username` which should mirror what is included in your webservers auth config. If using my webserver it would be `config.json`. +* `password` which should mirror what is included in your webservers auth config. If using my webserver it would be `config.json`. + +`port` and `tvIp` are optional as they can be manually assigned and updated via the tvremote watch app settings. + + ## Tv remote config example + require("Storage").write("tvremote.settings.json", { + "webServerDns": "", + "tvIp": "", + "port": "", + "username": "", + "password": "" + }); From 4d63ddf134abc3fe070401e69a505724a9194853 Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:31:24 +0100 Subject: [PATCH 09/14] Update README.md --- apps/tvremote/README.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/apps/tvremote/README.md b/apps/tvremote/README.md index a18c7a81b..7eb612245 100644 --- a/apps/tvremote/README.md +++ b/apps/tvremote/README.md @@ -23,3 +23,40 @@ You will need to upload the below JSON file to your BangleJS, which will be used "username": "", "password": "" }); + +# Usage +Main Menu +* Select TV type (panasonic is currently the only one supported) +* Settings takes you to the settings menu, that allows you to manually assign ports and IP's. + +Settings Menu +* Device Select sends a http request to the webserver for a scrollable list devices to select. +* Manual IP takes standard number inputs and swipping up will provide a `.` for IP's. + +Power Screen +* Press button - on/off. +* Swipe left - `App Menu`. +* Swipe Right - Main Menu + +App Menu +* Scroll and select to send App menu input. +* Swipe left - Selection menu. +* Swipe right - Power Screen. + +Selection Menu +* ^ - up +* ! - down +* < - left +* > - right +* Swipe right - back +* Swipe left - select +* Swipe Down - Number Menu ( used for inputting key passwords). +* Swipe Up - Vol Commands + +Vol Commands +* Swipe Down - Selection Menu +* Swipe right - rewind +* Swipe left - fast forward +* Swipe Up - Play/Pause + +Back Button - Should take you back to the previous menu screen. From 6ad651e27390c5d27afdde0719a2c18ff0b31a59 Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Wed, 28 Aug 2024 13:32:12 +0100 Subject: [PATCH 10/14] Update README.md --- apps/tvremote/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tvremote/README.md b/apps/tvremote/README.md index 7eb612245..77ee3681f 100644 --- a/apps/tvremote/README.md +++ b/apps/tvremote/README.md @@ -47,7 +47,7 @@ Selection Menu * ^ - up * ! - down * < - left -* > - right +* `>` - right * Swipe right - back * Swipe left - select * Swipe Down - Number Menu ( used for inputting key passwords). From f49a9341a502d25b3e00a3d6885b085d6fdc1829 Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Thu, 29 Aug 2024 09:38:30 +0100 Subject: [PATCH 11/14] Update app.js --- apps/tvremote/app.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/tvremote/app.js b/apps/tvremote/app.js index 9fb88fd2d..5911d5c5b 100644 --- a/apps/tvremote/app.js +++ b/apps/tvremote/app.js @@ -19,10 +19,10 @@ let panaIp = "tvIp" in serverData ? serverData.tvIp : 'undefined'; let settingsPort = "port" in serverData ? serverData.port : 'undefined'; let counter; +let IPASSIGN; +let samsIp; let countdownTimer = null; -let drawTimeout; let currNumber = null; -let prevNumber = null; let selected = "NONE"; let currentScreen = "power"; let font = 'Vector'; @@ -298,7 +298,7 @@ function buttonPress(val, screenValue) { displayOutput(currNumber, screenValue); } - checkValue = appData.some(app => app.name === screenValue); // check app data + let checkValue = appData.some(app => app.name === screenValue); // check app data if (checkValue) sendPost(val); // app values @@ -383,10 +383,6 @@ let tvSelector = { } }; -function samsungmenu() { - //E.showMessage("It's on my list to do", "Error"); -} - function mainMenu() { assignScreen("mainmenu"); E.showMenu(tvSelector); @@ -467,7 +463,7 @@ function subMenu() { settingssub[panaheader] = function() { IPASSIGN = "pana"; E.showMenu(deviceSelect); - deviceFile = require("Storage").readJSON("tvdevicelist.json", true); + devicefile = require("Storage").readJSON("tvdevicelist.json", true); console.log(devicefile); }; } else { @@ -498,7 +494,7 @@ let parsedResp = JSON.parse(devicefile.resp); select: i => { let sourceOption = parsedResp[i]; - let devicePressed = sourceOption.name; + //let devicePressed = sourceOption.name; let deviceIp = sourceOption.ip; assignScreen("deviceSearch"); From 1a1d8cf1dd2c839bbad9e996d09304d7f265f3c4 Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Thu, 29 Aug 2024 10:09:28 +0100 Subject: [PATCH 12/14] Update app.js --- apps/tvremote/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/tvremote/app.js b/apps/tvremote/app.js index 5911d5c5b..628ccd81c 100644 --- a/apps/tvremote/app.js +++ b/apps/tvremote/app.js @@ -184,8 +184,8 @@ function assignScreen (screen) { } function sendPost(keyPress) { - let serverPort = settingsPort; - let tvIp = panaIp; + serverPort = settingsPort; + tvIp = panaIp; let credentials = btoa(`${username}:${password}`); let serverUrl = `https://${serverDns}:${serverPort}`; @@ -211,7 +211,7 @@ function sendPost(keyPress) { } function receiveDevices() { - let serverPort = settingsPort; + serverPort = settingsPort; let credentials = btoa(`${username}:${password}`); let serverUrl = `https://${serverDns}:${serverPort}/ssdp-devices.json`; return Bangle.http( From ca004b27001a81dec99892c3b6a919e3fd181509 Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Thu, 12 Sep 2024 07:56:31 +0100 Subject: [PATCH 13/14] Update app.js added an if statement to display an alert message if the tv config file is not present. --- apps/tvremote/app.js | 150 +++++++++++++++++++++++++------------------ 1 file changed, 89 insertions(+), 61 deletions(-) diff --git a/apps/tvremote/app.js b/apps/tvremote/app.js index 628ccd81c..1564f1857 100644 --- a/apps/tvremote/app.js +++ b/apps/tvremote/app.js @@ -9,13 +9,15 @@ let devicefile = require("Storage").readJSON(deviceFileName, true); //console.log(devicefile); -let serverDns = serverData.webServerDns; -let serverPort = serverData.port; -let tvIp = serverData.tvIp; -let username = serverData.username; -let password = serverData.password; +let serverDns = "webServerDns" in serverData ? serverData.webServerDns : 'undefined'; -let panaIp = "tvIp" in serverData ? serverData.tvIp : 'undefined'; +let serverPort = "port" in serverData ? serverData.port : 'undefined'; +let tvIp = "tvIp" in serverData ? serverData.tvIp : 'undefined'; +let username = "username" in serverData ? serverData.username : 'undefined'; +let password = "password" in serverData ? serverData.password : 'undefined'; + +let panaIp = tvIp; +//"tvIp" in serverData ? serverData.tvIp : 'undefined'; let settingsPort = "port" in serverData ? serverData.port : 'undefined'; let counter; @@ -31,7 +33,12 @@ let RIGHT_MARGIN = 15; let midpoint = (g.getWidth() / 2); let IP_AREA = [0, RESULT_HEIGHT, g.getWidth(), g.getHeight()]; // values used for key buttons let KEY_AREA = [0, 24, g.getWidth(), g.getHeight()]; -let COLORS = {DEFAULT: ['#FF0000'], BLACK: ['#000000'], WHITE: ['#FFFFFF'], GREY: ['#808080', '#222222']}; // background +let COLORS = { + DEFAULT: ['#FF0000'], + BLACK: ['#000000'], + WHITE: ['#FFFFFF'], + GREY: ['#808080', '#222222'] +}; // background let sourceApps = { "selection": { @@ -178,7 +185,7 @@ let volume = sourceApps.volume; let selection = sourceApps.selection; let numbers = sourceApps.numbers; -function assignScreen (screen) { +function assignScreen(screen) { currentScreen = screen; console.log(currentScreen); } @@ -195,14 +202,13 @@ function sendPost(keyPress) { }; Bangle.http( - serverUrl, - { - method: 'POST', - headers: { - 'Authorization': `Basic ${credentials}`, - }, - body: JSON.stringify(keyJson) - }) + serverUrl, { + method: 'POST', + headers: { + 'Authorization': `Basic ${credentials}`, + }, + body: JSON.stringify(keyJson) + }) .then(response => { console.log("Response received:", response); }).catch(error => { @@ -211,19 +217,18 @@ function sendPost(keyPress) { } function receiveDevices() { - serverPort = settingsPort; + let serverPort = settingsPort; let credentials = btoa(`${username}:${password}`); let serverUrl = `https://${serverDns}:${serverPort}/ssdp-devices.json`; return Bangle.http( - serverUrl, - { + serverUrl, { method: 'GET', headers: { 'Authorization': `Basic ${credentials}` }, }).then(data => { - require("Storage").write(deviceFileName, data); - devicefile = require("Storage").readJSON(deviceFileName, true); + require("Storage").write(deviceFileName, data); + devicefile = require("Storage").readJSON(deviceFileName, true); }); } @@ -277,7 +282,7 @@ function drawKeys(area, buttons) { function displayOutput(num, screenValue) { // top block num = num.toString(); - g.setFont('Vector', 18); //'7x11Numeric7Seg' + g.setFont('Vector', 18); //'7x11Numeric7Seg' g.setFontAlign(1, 0); g.setBgColor(0).clearRect(0, 0, g.getWidth(), RESULT_HEIGHT - 1); g.setColor(-1); // value @@ -288,27 +293,25 @@ function displayOutput(num, screenValue) { // top block function buttonPress(val, screenValue) { if (screenValue === "ip") { - if (val === "<") currNumber = currNumber.slice(0,-1); + if (val === "<") currNumber = currNumber.slice(0, -1); else if (val === ".") currNumber = currNumber + "."; else currNumber = currNumber == null ? val : currNumber + val; // currNumber is null if no value pressed let ipcount = (currNumber.match(/\./g) || []).length; - if (ipcount > 3 || currNumber.length > 15) currNumber = currNumber.slice(0,-1); + if (ipcount > 3 || currNumber.length > 15) currNumber = currNumber.slice(0, -1); displayOutput(currNumber, screenValue); } - let checkValue = appData.some(app => app.name === screenValue); // check app data + let checkValue = appData.some(app => app.name === screenValue); // check app data - if (checkValue) sendPost(val); // app values + if (checkValue) sendPost(val); // app values if (screenValue === "numbers") { if (val === '<') sendPost('back'); else if (val === 'ok') sendPost('enter'); - else sendPost("num_"+val); - } - - else if (screenValue === "selection") sendPost(selection[val].key); + else sendPost("num_" + val); + } else if (screenValue === "selection") sendPost(selection[val].key); else if (screenValue === "volume") sendPost(volume[val].key); } @@ -327,7 +330,7 @@ const powerScreen = () => { }; const appMenu = () => { - + assignScreen("apps"); E.showScroller({ h: 54, @@ -367,7 +370,7 @@ let tvSelector = { "": { title: "TV Selector", back: function() { - load();//E.showMenu(tvSelector); + load(); //E.showMenu(tvSelector); } }, "Panasonic": function() { @@ -398,7 +401,7 @@ function clearCountdown() { function countDown(callback) { require("widget_utils").show(); if (counter === 0) { - callback(); // Call the callback function when countdown reaches 0 + callback(); // Call the callback function when countdown reaches 0 return; } E.showMessage(`Searching for devices...\n${counter}`, "Device Search"); @@ -453,36 +456,51 @@ function subMenu() { if (typeof settingsPort !== 'undefined' && settingsPort !== 'undefined') { let portHeader = `Port: ${settingsPort}`; - settingssub[portHeader] = function() { IPASSIGN = "port"; ipScreen();}; + settingssub[portHeader] = function() { + IPASSIGN = "port"; + ipScreen(); + }; } else { - settingssub["Set DNS Port"] = function() { IPASSIGN = "port"; ipScreen();}; + settingssub["Set DNS Port"] = function() { + IPASSIGN = "port"; + ipScreen(); + }; } if (typeof panaIp !== 'undefined' && panaIp !== 'undefined') { let panaheader = `Pana IP: ${panaIp}`; - settingssub[panaheader] = function() { - IPASSIGN = "pana"; + settingssub[panaheader] = function() { + IPASSIGN = "pana"; E.showMenu(deviceSelect); devicefile = require("Storage").readJSON("tvdevicelist.json", true); -console.log(devicefile); + console.log(devicefile); }; } else { - settingssub["Set Pana IP"] = function() { IPASSIGN = "pana"; ipScreen();}; + settingssub["Set Pana IP"] = function() { + IPASSIGN = "pana"; + ipScreen(); + }; } if (typeof samsIp !== 'undefined' && panaIp !== 'undefined') { let samsheader = `Sams IP: ${samsIp}`; - settingssub[samsheader] = function() { IPASSIGN = "sams"; ipScreen();}; + settingssub[samsheader] = function() { + IPASSIGN = "sams"; + ipScreen(); + }; } else { - settingssub["Set Sams IP"] = function() { IPASSIGN = "sams"; ipScreen();}; + settingssub["Set Sams IP"] = function() { + IPASSIGN = "sams"; + ipScreen(); + }; } E.showMenu(settingssub); } const deviceMenu = () => { -let parsedResp = JSON.parse(devicefile.resp); - E.showScroller({ + let parsedResp = JSON.parse(devicefile.resp); + E.showScroller({ h: 54, c: parsedResp.length, draw: (i, r) => { @@ -515,13 +533,16 @@ let parsedResp = JSON.parse(devicefile.resp); let deviceSelect = { - "" : { title : "Device Select", - back : function() { subMenu(); } - }, - "Manual IP Assign" : function() { + "": { + title: "Device Select", + back: function() { + subMenu(); + } + }, + "Manual IP Assign": function() { ipScreen(); }, - "Device Select" : function() { + "Device Select": function() { receiveDevices(); counter = 5; countDown(deviceMenu); @@ -549,7 +570,7 @@ function swipeHandler(LR, UD) { sendPost("enter"); } } - if (LR == 1) { // swipe right + if (LR == 1) { // swipe right if (currentScreen === "apps") { assignScreen("power"); E.showScroller(); @@ -560,13 +581,13 @@ function swipeHandler(LR, UD) { sendPost("back"); } } - if (UD == -1) { // swipe up + if (UD == -1) { // swipe up if (currentScreen === "selection") { assignScreen("volume"); prepareScreen(volume, volumeGrid, COLORS.DEFAULT, KEY_AREA); drawKeys(KEY_AREA, volume); } else if (currentScreen === "volume") { - sendPost("enter"); + sendPost("enter"); } else if (currentScreen === "ip") { buttonPress(".", "ip"); } else if (currentScreen == "numbers") { @@ -575,15 +596,15 @@ function swipeHandler(LR, UD) { drawKeys(KEY_AREA, selection); } } - if (UD == 1) { // swipe down - if (currentScreen === "volume") { - assignScreen("selection"); - prepareScreen(selection, selectionGrid, COLORS.DEFAULT, KEY_AREA); - drawKeys(KEY_AREA, selection); - } else if (currentScreen === "selection") { - assignScreen("numbers"); - prepareScreen(numbers, numbersGrid, COLORS.DEFAULT, KEY_AREA); - drawKeys(KEY_AREA, numbers); + if (UD == 1) { // swipe down + if (currentScreen === "volume") { + assignScreen("selection"); + prepareScreen(selection, selectionGrid, COLORS.DEFAULT, KEY_AREA); + drawKeys(KEY_AREA, selection); + } else if (currentScreen === "selection") { + assignScreen("numbers"); + prepareScreen(numbers, numbersGrid, COLORS.DEFAULT, KEY_AREA); + drawKeys(KEY_AREA, numbers); } } } @@ -624,4 +645,11 @@ Bangle.on('touch', touchHandler); Bangle.loadWidgets(); Bangle.drawWidgets(); -mainMenu(); + +if (serverData === undefined) { + E.showAlert(`${serverDataFile}.json missing.\nSee READ.me`, "Config Error").then(function() { + mainMenu(); + }); +} else { + mainMenu(); +} From beaa23da2db455695953e818c00707e4f6717684 Mon Sep 17 00:00:00 2001 From: Guptilious <42465910+Guptilious@users.noreply.github.com> Date: Thu, 12 Sep 2024 10:05:40 +0100 Subject: [PATCH 14/14] Update apps/tvremote/app.js Co-authored-by: thyttan <97237430+thyttan@users.noreply.github.com> --- apps/tvremote/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/tvremote/app.js b/apps/tvremote/app.js index 1564f1857..55d9fe9ad 100644 --- a/apps/tvremote/app.js +++ b/apps/tvremote/app.js @@ -647,7 +647,7 @@ Bangle.loadWidgets(); Bangle.drawWidgets(); if (serverData === undefined) { - E.showAlert(`${serverDataFile}.json missing.\nSee READ.me`, "Config Error").then(function() { + E.showAlert(`No settings.\nSee READ.me`, "Config Error").then(function() { mainMenu(); }); } else {