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] 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();