From f03b59ac97ca7501fb33d5db2b654ab0974b88b5 Mon Sep 17 00:00:00 2001 From: Sam Detweiler Date: Fri, 20 May 2022 16:00:43 -0500 Subject: [PATCH 01/10] upgrade to electron 17.2, handle errors from unconfigured plugins gravefully --- .gitignore | 2 + .gitkeep | 32 +++++++++++++++ app/js/controller.js | 9 ----- main.js | 38 ++++++++++++++++- package.json | 5 ++- plugins/geolocation/service.js | 74 +++++++++++++++++----------------- plugins/reminder/controller.js | 7 ++++ plugins/speech/service.js | 21 ++++++++-- plugins/spotify/service.js | 73 +++++++++++++++++---------------- plugins/stock/controller.js | 32 +++++++++------ plugins/weather/controller.js | 21 +++++----- scripts/installcblas | 3 +- 12 files changed, 208 insertions(+), 109 deletions(-) create mode 100644 .gitkeep diff --git a/.gitignore b/.gitignore index 87c29726d..a9d17a7d9 100755 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ app/css/local.css .gitignore .prettierrc .editorconfig +plugins/ +!.gitkeep diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 000000000..b0dbeab4a --- /dev/null +++ b/.gitkeep @@ -0,0 +1,32 @@ +plugins/autosleep +plugins/calendar +plugins/commands +plugins/commitstrip +plugins/datetime +plugins/dilbert +plugins/fitbit +plugins/_general +plugins/geolocation +plugins/giphy +plugins/greeting +plugins/ha-display +plugins/light +plugins/maker +plugins/map +plugins/pluginconfig +plugins/reminder +plugins/remote +plugins/rss +plugins/scrobbler +plugins/search +plugins/soundcloud +plugins/speech +plugins/spotify +plugins/stock +plugins/timebox +plugins/timer +plugins/todoist +plugins/traffic +plugins/tvshows +plugins/weather +plugins/xkcd diff --git a/app/js/controller.js b/app/js/controller.js index afd3ecf92..5f0e9d0b8 100644 --- a/app/js/controller.js +++ b/app/js/controller.js @@ -5,7 +5,6 @@ Focus, SpeechService, AutoSleepService, - // LightService, $rootScope, $scope, $timeout, @@ -183,14 +182,6 @@ console.debug("It is", moment().format("h:mm:ss a")); }); - // Control light - /* SpeechService.addCommand("light_action", function ( - state, - target, - action - ) { - LightService.performUpdate([state, target, action].join(" ")); - }); */ }; _this.init(); diff --git a/main.js b/main.js index 9acfe2953..14da9d05d 100755 --- a/main.js +++ b/main.js @@ -7,6 +7,12 @@ const remote = require("./remote.js"); const app = electron.app; // Module to create native browser window. const BrowserWindow = electron.BrowserWindow; +// Replace with: +const debug = true; +//const { BrowserWindow } = require('@electron/remote/main') + +// In the main process: +require('@electron/remote/main').initialize() // Prevent the monitor from going to sleep. const powerSaveBlocker = electron.powerSaveBlocker; powerSaveBlocker.start("prevent-display-sleep"); @@ -17,7 +23,29 @@ const getPort = require("get-port"); // Launching the mirror in dev mode const DevelopmentMode = process.argv.includes("dev"); -const usepm2 = process.argv.includes("usepm2"); +let usepm2 = false; // process.argv.includes("usepm2"); + +//if (debug) console.log("getting pm2 process list"); + exec("pm2 jlist", (error, stdout, stderr) => { + if (!error) { + let output = JSON.parse(stdout); + if (debug) + console.log( + "processing pm2 jlist output, " + output.length + " entries" + ); + output.forEach((managed_process) => { + if(debug) + console.log("comparing "+__dirname +" with "+ managed_process.pm2_env.pm_cwd ) + if (managed_process.pm2_env.pm_cwd.startsWith(__dirname)) { + if (debug) + console.log( + "found our pm2 entry, id=" + managed_process.pm_id + ); + usepm2 = true; + } + }); + } + }); //var atomScreen = null; // Load the smart mirror config let config; @@ -67,6 +95,7 @@ function createWindow() { break; } } + const { width, height } = atomScreen.getPrimaryDisplay().workAreaSize; var browserWindowOptions = { width: width, @@ -80,6 +109,7 @@ function createWindow() { nodeIntegration: true, enableRemoteModule: true, contextIsolation: false, + additionalArguments:["sonusPort:"+global.sonusSocket] }, }; if (externalDisplay) { @@ -148,6 +178,8 @@ function startSonus(port) { if (config && config.speech && !firstRun) { // get the sonus communications socket port getPort({ port: getPort.makeRange(9000, 9500) }).then((port) => { + console.log("found port="+port) + //console.log("global="+JSON.stringify(global,null,2)) global.sonusSocket = port; config.communications_port = port; startSonus(config.communications_port); @@ -216,10 +248,14 @@ if ((config.remote && config.remote.enabled) || firstRun) { remote.on("relaunch", function (newConfig) { console.log("Relaunching..."); // rebuild the html file plugin position info, from the NEW config data + // if pm2 is not being used for this process control if (!usepm2) { + // recalc plugin info since something changed loader.loadPluginInfo(__dirname + "/index.html", newConfig); + // force reload app.relaunch(); } + // else die and pm2 will restart us app.quit(); }); } diff --git a/package.json b/package.json index 1a5c568ca..af3c5ccdc 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "smart-mirror", - "version": "0.0.30", + "version": "0.0.31", "description": "The fairest of them all", "main": "main.js", "scripts": { @@ -36,11 +36,12 @@ "devDependencies": { "angular-i18n": "^1.8.2", "bower": "^1.8.12", - "electron": "11.3.0", + "electron": "17.2.0", "eslint": "^6.8.0", "wiredep-cli": "^0.1.0" }, "dependencies": { + "@electron/remote": "^2.0.8", "@google-cloud/speech": "^4.2.0", "@grpc/grpc-js": "^1.2.11", "alphavantage": "1.2.6", diff --git a/plugins/geolocation/service.js b/plugins/geolocation/service.js index 76030d1fb..f60f27729 100644 --- a/plugins/geolocation/service.js +++ b/plugins/geolocation/service.js @@ -13,43 +13,45 @@ var geoloc = null; service.getLocation = function (parms) { - var deferred = $q.defer(); - // Use geo postion from config file if it is defined - if( config.geoPosition - && typeof config.geoPosition.latitude != 'undefined' - && typeof config.geoPosition.longitude != 'undefined') { - deferred.resolve({ - coords: { - latitude: config.geoPosition.latitude, - longitude: config.geoPosition.longitude, - }, - }); - geoloc = deferred.promise; - } else { - // if we haven't requested info yet - if (geoloc == null) { - var body={}; - if(parms!=null) - body = parms - // if we have a key - if(config.geoPosition && config.geoPosition.key) { - geoloc = deferred.promise; - body.considerIp=true; - $http.post("https://www.googleapis.com/geolocation/v1/geolocate?key="+config.geoPosition.key, body).then( - function (result) { - var location = angular.fromJson(result).data.location - deferred.resolve({ 'coords': { 'latitude': location.lat, 'longitude': location.lng } }) - }, - function (err) { - deferred.reject("Failed to retrieve geolocation.eeror ="+ err) - } - ); + //var deferred = $q.defer(); + return new Promise((resolve,reject)=>{ + // Use geo postion from config file if it is defined + if( config.geoPosition + && typeof config.geoPosition.latitude != 'undefined' + && typeof config.geoPosition.longitude != 'undefined') { + /*deferred.*/resolve({ + coords: { + latitude: config.geoPosition.latitude, + longitude: config.geoPosition.longitude, + }, + }); + //geoloc = deferred.promise; + } else { + // if we haven't requested info yet + //if (geoloc == null) { + var body={}; + if(parms!=null) + body = parms + // if we have a key + if(config.geoPosition && config.geoPosition.key) { + //geoloc = deferred.promise; + body.considerIp=true; + $http.post("https://www.googleapis.com/geolocation/v1/geolocate?key="+config.geoPosition.key, body).then( + function (result) { + var location = angular.fromJson(result).data.location + /*deferred.*/resolve({ 'coords': { 'latitude': location.lat, 'longitude': location.lng } }) + }, + function (err) { + /*deferred.*/ reject("Failed to retrieve geolocation.eeror ="+ err) + } + ); + } + else + /*deferred.*/ reject("Failed to retrieve geolocation.eeror ="+ "no key provided"); } - else - deferred.reject("Failed to retrieve geolocation.eeror ="+ "no key provided"); - } - } - return geoloc; + //} + }) + //return geoloc?geoloc:deferred.promise; } return service; } diff --git a/plugins/reminder/controller.js b/plugins/reminder/controller.js index c2b3eb44c..2a2a02470 100644 --- a/plugins/reminder/controller.js +++ b/plugins/reminder/controller.js @@ -1,5 +1,12 @@ +const __rm = require("path"); +let __rmpath = document.currentScript.src.substring( + 7, + document.currentScript.src.lastIndexOf(__sp.sep) +); + function Reminder($scope, SpeechService, $translate, Focus) { const storage = require("electron-json-storage"); + storage.setDataPath(__rmpath); // Service variable var remind = {}; remind.reminders = []; diff --git a/plugins/speech/service.js b/plugins/speech/service.js index 6d4143377..ea4b321ba 100755 --- a/plugins/speech/service.js +++ b/plugins/speech/service.js @@ -1,16 +1,31 @@ -const { ipcRenderer, remote } = require("electron"); +const { ipcRenderer } = require("electron"); +//const remote = require("@electron/remote") //const remote = require("electron").remote; (function () { "use strict"; + try { + //console.log("args="+JSON.stringify(window.process.argv,null,2)) + } + catch(ex){ + } + finally { + //config.communications_port = 5200 + for(let p of window.process.argv){ + if(p.startsWith("sonusPort")){ + config.communications_port=p.split(":")[1] + break; + } + } + } + //console.log("SpeechService sonusPort="+config.communications_port) function SpeechService($rootScope, $translate) { var service = {}; var callbacks = {}; var commandList = []; var commandPage = []; - config.communications_port = remote.getGlobal("sonusSocket"); service.init = function (cb) { // workaround so we can trigger requests at any time annyang.isListening = () => { @@ -139,7 +154,7 @@ const { ipcRenderer, remote } = require("electron"); // // the recorder library asks sm sonus to disconnect from mic, then start up our own version without // hotword (assistant) and maybe reco all together (alexa) - // after each recording we have no idea is more prompts are coming + // after each recording we have no idea if more prompts are coming // so the library has to disconnect and tell sm sonus.js to reconnect // the plugin needing this service needs to call startVoiceRecognition // to start each sequence diff --git a/plugins/spotify/service.js b/plugins/spotify/service.js index b5f2dd7ef..feb25d031 100644 --- a/plugins/spotify/service.js +++ b/plugins/spotify/service.js @@ -39,6 +39,7 @@ let _spotpath = document.currentScript.src.substring( if ( typeof config.spotify !== "undefined" && + typeof config.spotify.creds !== "undefined" && typeof config.spotify.creds.clientSecret !== "undefined" && config.spotify.creds.clientSecret !== "" ) { @@ -101,40 +102,44 @@ let _spotpath = document.currentScript.src.substring( service.init = function (cb) { var port = process.env.PORT || 4100; - console.debug("Express is listening on port: " + port); - app.listen(port); - - // Read the persisted token, initially captured by a webapp. - fs.stat(tokenFile, function (err) { - if (err == null) { - persist.read(tokenFile, function (err, token) { - if (err) { - console.error( - "Spotify authentication invalid format, please see the config screen for the authorization instructions.", - err - ); - } else { - var access_token = token["access_token"]; - var refresh_token = token["refresh_token"]; - - if (!default_device) - console.debug("no default spotify device chosen"); - - spotify.setAccessToken(access_token); // Set the client token - spotify.setRefreshToken(refresh_token); // Set the client token - // if (authorized_session) cb(); - cb(); - } - }); - } else if (err.code == "ENOENT") { - console.error( - "Spotify authentication required, please see the config screen for the authorization instructions.", - err - ); - } else { - console.error(err); - } - }); + if(app){ + console.debug("Express is listening on port: " + port); + app.listen(port); + + // Read the persisted token, initially captured by a webapp. + fs.stat(tokenFile, function (err) { + if (err == null) { + persist.read(tokenFile, function (err, token) { + if (err) { + console.error( + "Spotify authentication invalid format, please see the config screen for the authorization instructions.", + err + ); + } else { + var access_token = token["access_token"]; + var refresh_token = token["refresh_token"]; + + if (!default_device) + console.debug("no default spotify device chosen"); + + spotify.setAccessToken(access_token); // Set the client token + spotify.setRefreshToken(refresh_token); // Set the client token + // if (authorized_session) cb(); + cb(); + } + }); + } else if (err.code == "ENOENT") { + console.error( + "Spotify authentication required, please see the config screen for the authorization instructions.", + err + ); + } else { + console.error(err); + } + }); + } else { + console.error("Spotify plugin not configured") + } }; service.refreshToken = function () { diff --git a/plugins/stock/controller.js b/plugins/stock/controller.js index b5b223091..63ad5b4ee 100644 --- a/plugins/stock/controller.js +++ b/plugins/stock/controller.js @@ -1,6 +1,8 @@ function Stock($scope, $http, $q, $interval) { - - var Stocks= require('alphavantage')({ key: config.stock.key }); + var Stocks=null + if(config.stock && config.stock.key){ + Stocks= require('alphavantage')({ key: config.stock.key }); + } // request info on each stock, 1 at a time var getStockQuotes = function () { var promises = []; @@ -11,17 +13,21 @@ function Stock($scope, $http, $q, $interval) { } var getStocks = function () { - getStockQuotes().then(function (result) { - var stock = []; - if (result instanceof Array) { - stock = stock.concat(result); - } else { - stock.push(result); - } - $scope.stock = stock; - }, function (error) { - console.log(error); - }); + if(Stocks){ + getStockQuotes().then(function (result) { + var stock = []; + if (result instanceof Array) { + stock = stock.concat(result); + } else { + stock.push(result); + } + $scope.stock = stock; + }, function (error) { + console.log(error); + }); + } else { + console.error("stocks not configured") + } } getStocks(); diff --git a/plugins/weather/controller.js b/plugins/weather/controller.js index f48586880..1d2825900 100644 --- a/plugins/weather/controller.js +++ b/plugins/weather/controller.js @@ -374,16 +374,17 @@ function Weather($scope, $interval, $http, $translate, GeolocationService) { return weather.forecast.data.hourly } - GeolocationService.getLocation({ enableHighAccuracy: true }).then(function ( - geopo - ) { - geoposition = geopo - refreshWeatherData(geoposition) - $interval( - refreshWeatherData, - config.forecast.refreshInterval * 60000 || 7200000 - ) - }) + GeolocationService.getLocation({ enableHighAccuracy: true }) + .then((geopo)=> + { + geoposition = geopo + refreshWeatherData(geoposition) + $interval( + refreshWeatherData, + config.forecast.refreshInterval * 60000 || 7200000 + ) + }) + .catch((error)=>{if(!geoposition) console.error("Weather, no location configured")}) function refreshWeatherData() { config.forecast.keytype = (config.forecast.keytype + " ").split(" ")[0] diff --git a/scripts/installcblas b/scripts/installcblas index 47c5ec810..43d0fb773 100755 --- a/scripts/installcblas +++ b/scripts/installcblas @@ -5,7 +5,8 @@ if [ $mac == 'Darwin' ]; then brew install o2scl else v=$(apt -qq list libatlas3-base 2>/dev/null) - if [ "$v". == "." ]; then + d=$(apt -qq list libatlas-base-dev 2>/dev/null) + if [ "$v". == "." -o "$d". == "." ]; then sudo apt-get install libatlas-base-dev libatlas3-base fi fi From 4db9f468d22421141d6a9eb25b1b6d472fafcb2a Mon Sep 17 00:00:00 2001 From: Sam Detweiler Date: Fri, 20 May 2022 16:54:33 -0500 Subject: [PATCH 02/10] fix package.json for jsonform url --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index af3c5ccdc..547b17444 100755 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "fitbit-oauth2": "0.0.1", "humanize-duration": "^3.25.1", "hyperion-client": "1.0.3", - "jsonform": "git://github.com/jsonform/jsonform.git", + "jsonform": "git+https://git@github.com/jsonform/jsonform.git", "moment": "^2.29.1", "pretty-ms": "^7.0.1", "recorder": "git+https://git@github.com/sdetweil/recorder.git", From 1821ac56ecc92ce0d3ca74725153dc1a1e002d2b Mon Sep 17 00:00:00 2001 From: Sam Detweiler Date: Fri, 20 May 2022 17:02:36 -0500 Subject: [PATCH 03/10] fix formatting of new pm2 check code --- main.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.js b/main.js index 14da9d05d..a4007925c 100755 --- a/main.js +++ b/main.js @@ -26,7 +26,7 @@ const DevelopmentMode = process.argv.includes("dev"); let usepm2 = false; // process.argv.includes("usepm2"); //if (debug) console.log("getting pm2 process list"); - exec("pm2 jlist", (error, stdout, stderr) => { + exec("pm2 jlist", (error, stdout) => { if (!error) { let output = JSON.parse(stdout); if (debug) @@ -34,8 +34,8 @@ let usepm2 = false; // process.argv.includes("usepm2"); "processing pm2 jlist output, " + output.length + " entries" ); output.forEach((managed_process) => { - if(debug) - console.log("comparing "+__dirname +" with "+ managed_process.pm2_env.pm_cwd ) + if(debug) + console.log("comparing "+__dirname +" with "+ managed_process.pm2_env.pm_cwd ) if (managed_process.pm2_env.pm_cwd.startsWith(__dirname)) { if (debug) console.log( From 06615b21a59c0e11fceddda7fa1ad9ee6047c445 Mon Sep 17 00:00:00 2001 From: Sam Detweiler Date: Sat, 21 May 2022 08:53:16 -0500 Subject: [PATCH 04/10] add test for online status for PM2 process detection --- main.js | 56 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/main.js b/main.js index a4007925c..03e8a0e5f 100755 --- a/main.js +++ b/main.js @@ -8,7 +8,7 @@ const app = electron.app; // Module to create native browser window. const BrowserWindow = electron.BrowserWindow; // Replace with: -const debug = true; +const debug =false; //const { BrowserWindow } = require('@electron/remote/main') // In the main process: @@ -23,29 +23,31 @@ const getPort = require("get-port"); // Launching the mirror in dev mode const DevelopmentMode = process.argv.includes("dev"); -let usepm2 = false; // process.argv.includes("usepm2"); +let usepm2 = false; //if (debug) console.log("getting pm2 process list"); - exec("pm2 jlist", (error, stdout) => { - if (!error) { - let output = JSON.parse(stdout); - if (debug) - console.log( - "processing pm2 jlist output, " + output.length + " entries" - ); - output.forEach((managed_process) => { - if(debug) - console.log("comparing "+__dirname +" with "+ managed_process.pm2_env.pm_cwd ) - if (managed_process.pm2_env.pm_cwd.startsWith(__dirname)) { - if (debug) - console.log( - "found our pm2 entry, id=" + managed_process.pm_id - ); - usepm2 = true; - } - }); - } - }); +exec("pm2 jlist", (error, stdout) => { + if (!error) { + let output = JSON.parse(stdout); + if (debug) + console.log( + "processing pm2 jlist output, " + output.length + " entries" + ); + output.forEach((managed_process) => { + if(debug) + console.log("comparing "+__dirname +" with "+ managed_process.pm2_env.pm_cwd ) + // if we find a pm2 process matching our location + // and that process is online, then it is us + if (managed_process.pm2_env.pm_cwd.startsWith(__dirname) && managed_process.pm2_env.status ==="online") { + if (debug) + console.log( + "found our pm2 entry, id=" + managed_process.pm_id + ); + usepm2 = true; + } + }); + } +}); //var atomScreen = null; // Load the smart mirror config let config; @@ -115,9 +117,10 @@ function createWindow() { if (externalDisplay) { browserWindowOptions.x = width; //+ 2; //externalDisplay.bounds.x + 50 browserWindowOptions.y = height; //externalDisplay.bounds.y + 50 - console.log( - "display size=" + browserWindowOptions.x + "+" + browserWindowOptions.y - ); + if(debug) + console.log( + "display size=" + browserWindowOptions.x + "+" + browserWindowOptions.y + ); } // Create the browser window. @@ -178,7 +181,8 @@ function startSonus(port) { if (config && config.speech && !firstRun) { // get the sonus communications socket port getPort({ port: getPort.makeRange(9000, 9500) }).then((port) => { - console.log("found port="+port) + if(debug) + console.log("found port="+port) //console.log("global="+JSON.stringify(global,null,2)) global.sonusSocket = port; config.communications_port = port; From d9e6915ab0580a55d07907a0badb32bc0265d88d Mon Sep 17 00:00:00 2001 From: Sam Detweiler Date: Tue, 24 May 2022 09:36:44 -0500 Subject: [PATCH 05/10] fix giphy and xkcd for api changes --- plugins/giphy/controller.js | 3 ++- plugins/xkcd/controller.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/giphy/controller.js b/plugins/giphy/controller.js index 594f21a76..fa91ff386 100644 --- a/plugins/giphy/controller.js +++ b/plugins/giphy/controller.js @@ -4,7 +4,8 @@ function Giphy($scope, $http, SpeechService, Focus) { SpeechService.addCommand('image_giphy', function (img) { $http.get("http://api.giphy.com/v1/gifs/random?api_key=" + config.giphy.key + "&tag=" + img) .then(function (response) { - $scope.gifimg = response.data.data.image_url; + $scope.gifimg = response.data.data.images.original.url; + //console.log(JSON.stringify(response.data.data,null,2)) Focus.change("gif"); }) }); diff --git a/plugins/xkcd/controller.js b/plugins/xkcd/controller.js index 401cb5bf6..c9355a7e6 100644 --- a/plugins/xkcd/controller.js +++ b/plugins/xkcd/controller.js @@ -2,7 +2,7 @@ function Xkcd($scope, $http, SpeechService, Focus) { // Show xkcd comic SpeechService.addCommand('image_comic', function () { - $http.jsonp("http://dynamic.xkcd.com/api-0/jsonp/comic?callback=JSON_CALLBACK") + $http.get("https://xkcd.com/info.0.json") .then(function (response) { $scope.xkcd = response.data.img; Focus.change("xkcd"); From 3d0dd2917d9f3fdcf8a096d6f5227245b6465470 Mon Sep 17 00:00:00 2001 From: Sam Detweiler Date: Wed, 28 Sep 2022 21:18:19 -0500 Subject: [PATCH 06/10] add services and notifications from calednar, weather, clock --- app/css/newfile.css | 19 ++- app/js/controller.js | 1 + plugins/autosleep/service.js | 4 +- plugins/calendar/config.schema.json | 14 +- plugins/calendar/controller.js | 5 +- plugins/calendar/locales/de.json | 5 +- plugins/calendar/locales/en.json | 3 +- plugins/calendar/locales/es.json | 7 +- plugins/calendar/locales/fr.json | 7 +- plugins/calendar/locales/it.json | 7 +- plugins/calendar/locales/ko.json | 9 +- plugins/calendar/locales/pt.json | 9 +- plugins/weather/controller.js | 206 +++++---------------------- plugins/weather/service.js | 207 ++++++++++++++++++++++++++++ 14 files changed, 299 insertions(+), 204 deletions(-) create mode 100644 plugins/weather/service.js diff --git a/app/css/newfile.css b/app/css/newfile.css index 1af3da500..b8827f857 100644 --- a/app/css/newfile.css +++ b/app/css/newfile.css @@ -1,24 +1,31 @@ :root{ --scale-factor: 1; /* set default scaling in case we have partial window, debug or in vm terminal window */ + --design-width: 1920px; + --design-height: 1080px; } -@media screen and (width:1920px) and (orientation: landscape) { +@media screen and (orientation: landscape) { :root{ - --scale-factor: 1920/1920; + --scale-factor: var(width) / var(--design-width); + }; + } + @media screen and (orientation: portrait) { + :root{ + --scale-factor: var(width) / var(--design-height); }; } -@media screen and (width:3840px) and (orientation: landscape) { +/*@media screen and (width:3840px) and (orientation: landscape) { :root{ - --scale-factor: 3840/1920; + --scale-factor: width/var(--design-width); }; } @media screen and (width:1080px) { :root{ - --scale-factor: 1920/1920; + --scale-factor: width/var(--design-width); }; -} +}*/ diff --git a/app/js/controller.js b/app/js/controller.js index afd3ecf92..3f5a830ec 100644 --- a/app/js/controller.js +++ b/app/js/controller.js @@ -93,6 +93,7 @@ $scope.focus = AutoSleepService.scope; AutoSleepService.startAutoSleepTimer(); } + $rootScope.$broadcast("clock-tick",$scope.date) } var clearListening = function(){ $scope.listening = false; diff --git a/plugins/autosleep/service.js b/plugins/autosleep/service.js index a85cb988c..db54ee4e0 100755 --- a/plugins/autosleep/service.js +++ b/plugins/autosleep/service.js @@ -2,7 +2,7 @@ (function () { "use strict"; - function AutoSleepService($interval, Focus) { + function AutoSleepService($interval, $rootScope, Focus) { var service = {}; var autoSleepTimer; service.woke = true; @@ -55,6 +55,7 @@ // only wake up if sleeping if (Focus.get() === "sleep") { service.woke = true; + $rootScope.$broadcast("userPresence", "true") switch (config.autoTimer.mode) { case "monitor": case "tv": @@ -106,6 +107,7 @@ service.sleep = function () { if (config.autoTimer.mode !== "disabled") { service.woke = false; + $rootScope.$broadcast("userPresence", "false") switch (config.autoTimer.mode) { case "monitor": case "tv": diff --git a/plugins/calendar/config.schema.json b/plugins/calendar/config.schema.json index a810d8d0d..adeee4ca0 100644 --- a/plugins/calendar/config.schema.json +++ b/plugins/calendar/config.schema.json @@ -25,6 +25,17 @@ "type": "boolean", "title": "{{showCalendarNames}}", "default": false + }, + "refreshInterval":{ + "type":"integer", + "title":"{{refeshInterval}}", + "default":30, + "enum":[ + 15, + 30, + 45, + 60 + ] } } } @@ -54,7 +65,8 @@ }, "calendar.maxResults", "calendar.maxDays", - "calendar.showCalendarNames" + "calendar.showCalendarNames", + "calendar.refreshInterval" ] } ], diff --git a/plugins/calendar/controller.js b/plugins/calendar/controller.js index 87978f142..81de06181 100644 --- a/plugins/calendar/controller.js +++ b/plugins/calendar/controller.js @@ -1,8 +1,9 @@ -function Calendar($scope, $http, $interval, CalendarService) { +function Calendar($scope, $rootScope, $http, $interval, CalendarService) { var getCalendar = function(){ CalendarService.getCalendarEvents().then(function () { - $scope.calendar = CalendarService.getFutureEvents(); + $scope.calendar=CalendarService.getFutureEvents() + $rootScope.$broadcast('calendar',$scope.calendar ); }, function (error) { console.log(error); }); diff --git a/plugins/calendar/locales/de.json b/plugins/calendar/locales/de.json index 9c13a128a..9dd9ac827 100644 --- a/plugins/calendar/locales/de.json +++ b/plugins/calendar/locales/de.json @@ -1,11 +1,12 @@ { "calendar": { "config": { - "title": "Calendar Settings", + "title": "Kalendereinstellungen", "URLs": "iCal-URLs", "maxevents": "Maximale Anzahl von Ereignissen für alle iCals", "maxdays": "Maximale Anzahl der anzuzeigenden Tage", - "showCalendarNames": "Aktivieren Sie diese Option, um Kalendernamen anzuzeigen" + "showCalendarNames": "Aktivieren Sie diese Option, um Kalendernamen anzuzeigen", + "refeshInterval": "Wie oft in Minuten aktualisiert werden soll, standardmäßig 30" } } } \ No newline at end of file diff --git a/plugins/calendar/locales/en.json b/plugins/calendar/locales/en.json index cfdb75689..6e4c36544 100644 --- a/plugins/calendar/locales/en.json +++ b/plugins/calendar/locales/en.json @@ -5,7 +5,8 @@ "URLs":"iCal URLs", "maxevents":"Max Number of Events for all iCals", "maxdays":"Max Number of Days to display", - "showCalendarNames":"Check to display Calendar Names" + "showCalendarNames":"Check to display Calendar Names", + "refeshInterval":"How often to refresh in minutes, default 30" } } } \ No newline at end of file diff --git a/plugins/calendar/locales/es.json b/plugins/calendar/locales/es.json index 9da412844..9a50a30d7 100644 --- a/plugins/calendar/locales/es.json +++ b/plugins/calendar/locales/es.json @@ -1,11 +1,12 @@ { "calendar": { "config": { - "title": "Calendar Settings", - "URLs": "URL ICal", + "title": "Configuración del calendario", + "URLs": "URL de iCal", "maxevents": "Número máximo de eventos para todos los iCals", "maxdays": "Número máximo de días para mostrar", - "showCalendarNames": "Marque para mostrar los nombres de calendario" + "showCalendarNames": "Marque para mostrar los nombres de los calendarios", + "refeshInterval": "Frecuencia de actualización en minutos, por defecto 30" } } } \ No newline at end of file diff --git a/plugins/calendar/locales/fr.json b/plugins/calendar/locales/fr.json index 7832825a2..879fc1753 100644 --- a/plugins/calendar/locales/fr.json +++ b/plugins/calendar/locales/fr.json @@ -1,11 +1,12 @@ { "calendar": { "config": { - "title": "Calendar Settings", + "title": "Paramètres du calendrier", "URLs": "URL iCal", "maxevents": "Nombre maximum d'événements pour tous les iCals", "maxdays": "Nombre maximum de jours à afficher", - "showCalendarNames": "Cochez pour afficher les noms des calendriers" + "showCalendarNames": "Cochez pour afficher les noms de calendrier", + "refeshInterval": "Fréquence d'actualisation en minutes, par défaut 30" } } -} +} \ No newline at end of file diff --git a/plugins/calendar/locales/it.json b/plugins/calendar/locales/it.json index d080d9d6a..c7062a45b 100644 --- a/plugins/calendar/locales/it.json +++ b/plugins/calendar/locales/it.json @@ -1,11 +1,12 @@ { "calendar": { "config": { - "title": "Calendar Settings", + "title": "Impostazioni del calendario", "URLs": "URL iCal", - "maxevents": "Numero massimo di eventi per tutti gli iCals", + "maxevents": "Numero massimo di eventi per tutti iCal", "maxdays": "Numero massimo di giorni da visualizzare", - "showCalendarNames": "Selezionare per visualizzare i nomi dei calendari" + "showCalendarNames": "Selezionare per visualizzare i nomi del calendario", + "refeshInterval": "Frequenza di aggiornamento in minuti, valore predefinito 30" } } } \ No newline at end of file diff --git a/plugins/calendar/locales/ko.json b/plugins/calendar/locales/ko.json index 770e97e2c..369879db3 100644 --- a/plugins/calendar/locales/ko.json +++ b/plugins/calendar/locales/ko.json @@ -1,11 +1,12 @@ { "calendar": { "config": { - "title": "Calendar Settings", + "title": "캘린더 설정", "URLs": "iCal URL", - "maxevents": "모든 iCals의 최대 이벤트 수", - "maxdays": "표시 할 최대 일수", - "showCalendarNames": "캘린더 이름 표시를 확인하십시오." + "maxevents": "모든 iCal의 최대 이벤트 수", + "maxdays": "표시할 최대 일수", + "showCalendarNames": "캘린더 이름을 표시하려면 확인하십시오.", + "refeshInterval": "새로 고침 빈도(분), 기본값 30" } } } \ No newline at end of file diff --git a/plugins/calendar/locales/pt.json b/plugins/calendar/locales/pt.json index af781f963..68c89c670 100644 --- a/plugins/calendar/locales/pt.json +++ b/plugins/calendar/locales/pt.json @@ -1,11 +1,12 @@ { "calendar": { "config": { - "title": "Calendar Settings", - "URLs": "URLs do iCal", + "title": "Configurações do calendário", + "URLs": "URLs iCal", "maxevents": "Número máximo de eventos para todos os iCals", - "maxdays": "Número máximo de dias para exibir", - "showCalendarNames": "Marque para exibir os nomes do calendário" + "maxdays": "Número máximo de dias a serem exibidos", + "showCalendarNames": "Marque para exibir nomes de calendário", + "refeshInterval": "Com que frequência atualizar em minutos, padrão 30" } } } \ No newline at end of file diff --git a/plugins/weather/controller.js b/plugins/weather/controller.js index f48586880..7557a9fa5 100644 --- a/plugins/weather/controller.js +++ b/plugins/weather/controller.js @@ -1,4 +1,4 @@ -function Weather($scope, $interval, $http, $translate, GeolocationService) { +function Weather($scope, $rootScope, $interval, $http, $translate, GeolocationService, WeatherService) { var language = typeof config.general.language !== "undefined" ? config.general.language.substr(0, 2) @@ -7,166 +7,7 @@ function Weather($scope, $interval, $http, $translate, GeolocationService) { var weather = {} weather.get = {} - weather.getCountry = function () { - return new Promise((resolve, reject) => { - if (config.forecast.keytype != "Darksky") { - // forecast untis will be changed from auto to resolved type, - // so this api call is ececuted only once per sm execution - // if units is auto, try to discover if this is USA or not - if (config.forecast.units === "auto") { - // if the geoposition api key is set - if(config.geoPosition.key){ - let url= "https://maps.googleapis.com/maps/api/geocode/json?latlng="+ - geoposition.coords.latitude.toString().substring(0, 10) + - "," + - geoposition.coords.longitude.toString().substring(0, 11) + - "&key="+config.geoPosition.key - $http - .get( - url - ) - .then((results) => { - // point to the first data entry - let addresses=results.data.results[0].address_components - // get just the country entry - let info = addresses.filter((entry) =>{ - return JSON.stringify(entry.types) === JSON.stringify(['country','political']) - }) - if(info.length){ - if(info[0].short_name === 'US') - config.forecast.units = "us" - else config.forecast.units = "si" - resolve() - } else { - console.error("weather unable to determine country from geolocation") - reject() - } - }) - .catch((error) => { - console.error( - "weather google geocode country from geolocation failed =" + - JSON.stringify(error) - ) - reject() - }) - } - else { - console.error("geoposition apikey not set, needed by weather"); - reject() // - } - } else resolve() - } else { - // darksky, nothign to do here - resolve() - } - }) - } - weather.get.Openweather = function () { - return new Promise((resolve, reject) => { - //$http.get("https://api.openweathermap.org/data/2.5/onecall?lat=30.4548443&lon=-97.6222674&appid=a6bf9feaa86bc2677df1e5f46bd79d55") - $http - .get( - "https://api.openweathermap.org/data/2.5/onecall?lat=" + - geoposition.coords.latitude.toString().substring(0, 10) + - "&lon=" + - geoposition.coords.longitude.toString().substring(0, 11) + - "&units=" + - (config.forecast.units == "us" ? "imperial" : "metric") + - "&appid=" + - config.forecast.key - ) - .then(function (response) { - //console.log("json="+JSON.stringify(response.data)); - resolve((weather.forecast = response)) - }) - .catch(() => { - reject() - }) - }) - } - weather.get.Darksky = function () { - return new Promise((resolve, reject) => { - $http - .jsonp( - "https://api.darksky.net/forecast/" + - config.forecast.key + - "/" + - geoposition.coords.latitude + - "," + - geoposition.coords.longitude + - "?units=" + - config.forecast.units + - "&lang=" + - language + - "&callback=JSON_CALLBACK" - ) - .then(function (response) { - //console.log("json="+JSON.stringify(response.data)); - resolve((weather.forecast = response)) - }) - .catch(() => { - reject() - }) - }) - } - weather.get.Climacell = function () { - // return a promise, so the caller can wait - return new Promise((resolve, reject) => { - // list of concurrent requests - var plist = [] - var forecast = null - var currently = null - // "https://data.climacell.co/v4/timelines?timesteps=1h&units=" + this.config.tempUnits + "&location=" + this.config.lat + "," + this.config.lon + "&fields=temperature,temperatureApparent,precipitationType,humidity,windSpeed,windDirection,weatherCode&apikey=" + this.config.apiKey - - plist.push( $http.get( - /* 'https://api.climacell.co/v3/weather/realtime?lat=' + - geoposition.coords.latitude.toString().substring(0,10) + '&lon=' + geoposition.coords.longitude.toString().substring(0,11) + '&unit_system=' + - config.forecast.units + '&fields=temp%2Cprecipitation%2Cweather_code%2Ccloud_cover%2Csunrise%2Csunset%2Cvisibility%2Cwind_gust%2Cwind_speed&apikey='+config.forecast.key */ - "https://data.climacell.co/v4/timelines?timesteps=1h"+ - "&units=" + 'metric' + //config.forecast.units + - "&location=" + geoposition.coords.latitude.toString().substring(0,10) + "," + geoposition.coords.longitude.toString().substring(0,11) + - "&fields=temperature,temperatureApparent,precipitationType,humidity,windSpeed,windDirection,weatherCode"+ - "&apikey="+config.forecast.key - ).then( - (response)=>{ - currently=response.data.data; - } - ).catch((error)=>{ - console.log("climacell realltime failed ="+JSON.stringify(error)) - reject(); - }) - ) - // get the 10 day forecast info - plist.push($http.get( - "https://data.climacell.co/v4/timelines?timesteps=1d"+ - '&location=' + geoposition.coords.latitude.toString().substring(0,10) + ',' + geoposition.coords.longitude.toString().substring(0,11) + - "&units=" + 'metric' + // config.forecast.units + - '&fields=temperatureMax,temperatureMin,precipitationType,weatherCode'+ - '&apikey='+config.forecast.key - ).then( - (response)=>{ - forecast=response.data.data; - } - ).catch( (error)=>{ - console.log("climacell forecast failed ="+JSON.stringify(error)) - reject(); - }) - ) - - // wait for both above apis to complete - Promise.all(plist).then(() => { - // save the total data - weather["forecast"] = forecast - weather.forecast.data = {} - weather.forecast.data["currently"] = [] - // set the currently - weather.forecast.data.currently["data"] = currently - resolve(weather) - }) - }) - } - - weather.minutelyForecast = function () { + weather.minutelyForecast = function (weather) { if (weather.forecast === null) { return null } @@ -185,7 +26,7 @@ function Weather($scope, $interval, $http, $translate, GeolocationService) { return r } //Returns the current forecast along with high and low tempratures for the current day - weather.currentForecast = function () { + weather.currentForecast = function (weather) { if (weather.forecast === null) { return null } @@ -248,7 +89,7 @@ function Weather($scope, $interval, $http, $translate, GeolocationService) { return weather.forecast.data.currently } - weather.weeklyForecast = function () { + weather.weeklyForecast = function (weather) { if (weather.forecast === null) { return null } @@ -264,6 +105,7 @@ function Weather($scope, $interval, $http, $translate, GeolocationService) { .unix(weather.forecast.data.daily.data[i].time) .format("ddd") : $translate.instant("forecast.today") + weather.forecast.data.daily.data[i].dt=weather.forecast.data.daily.data[i].time weather.forecast.data.daily.data[i].temperatureMin = parseFloat( weather.forecast.data.daily.data[i].temperatureMin ).toFixed(0) @@ -287,6 +129,11 @@ function Weather($scope, $interval, $http, $translate, GeolocationService) { weather.forecast.data.daily.data = [] for (i = 0; i < datalength; i++) { weather.forecast.data.daily.data[i] = {} + weather.forecast.data.daily.data[i].dt=moment + .utc( + weather.forecast.timelines[0].intervals[i].startTime, + "YYYY-MM-DD" + ).valueOf()/1000 weather.forecast.data.daily.data[i].day = i > 0 ? moment @@ -329,6 +176,8 @@ function Weather($scope, $interval, $http, $translate, GeolocationService) { weather.forecast.data.daily.data = [] for (i = 0; i < datalength; i++) { weather.forecast.data.daily.data[i] = {} + weather.forecast.data.daily.data[i].dt=weather.forecast.data.daily[i].dt + weather.forecast.data.daily.data[i].day = i > 0 ? moment.unix(weather.forecast.data.daily[i].dt).format("ddd") @@ -356,7 +205,7 @@ function Weather($scope, $interval, $http, $translate, GeolocationService) { return weather.forecast.data.daily } - weather.hourlyForecast = function () { + weather.hourlyForecast = function (weather) { if (weather.forecast === null) { return null } @@ -379,32 +228,41 @@ function Weather($scope, $interval, $http, $translate, GeolocationService) { ) { geoposition = geopo refreshWeatherData(geoposition) - $interval( - refreshWeatherData, + $interval( ()=>{ + refreshWeatherData(geoposition) + }, config.forecast.refreshInterval * 60000 || 7200000 ) }) - function refreshWeatherData() { + function refreshWeatherData(geoposition) { config.forecast.keytype = (config.forecast.keytype + " ").split(" ")[0] config.forecast.key = config.forecast.key.trim() // map location to country for auto weather units (if needed) - weather.getCountry().then(() => { + WeatherService.getCountry().then(() => { // get the weather info - weather.get[config.forecast.keytype]().then( - () => { + WeatherService.get[config.forecast.keytype](geoposition).then( + (weather_data) => { // set the current forecast info for index.html usage - $scope.currentForecast = weather.currentForecast() + $scope.currentForecast = weather.currentForecast(weather_data) // set the weekely forecast info for index.html usage - $scope.weeklyForecast = weather.weeklyForecast() + $scope.hourlyForecast=null + $scope.minutelyForecast=null + $scope.weeklyForecast = weather.weeklyForecast(weather_data) if (config.forecast.keytype != "Climacell") { // we don't have hourly - $scope.hourlyForecast = weather.hourlyForecast() + $scope.hourlyForecast = weather.hourlyForecast(weather_data) if (config.forecast.keytype == "Darksky") { // or minutely anymore - $scope.minutelyForecast = weather.minutelyForecast() + $scope.minutelyForecast = weather.minutelyForecast(weather_data) } } + $rootScope.$broadcast('weather', { + current:$scope.currentForecast, + weekly:$scope.weeklyForecast, + hourly:$scope.hourlyForecast, + minutely:$scope.minutelyForecast + }); }, function (err) { console.log(err) diff --git a/plugins/weather/service.js b/plugins/weather/service.js new file mode 100644 index 000000000..90a51ae39 --- /dev/null +++ b/plugins/weather/service.js @@ -0,0 +1,207 @@ +(function () { + 'use strict'; + + function WeatherService($window, $http, $q) { + + var language = + typeof config.general.language !== "undefined" + ? config.general.language.substr(0, 2) + : "en" + var geoposition = {} + var service = {}; + + service.events = []; + service.get= {} + service.getCountry = function () { + return new Promise((resolve, reject) => { + if (config.forecast.keytype != "Darksky") { + // forecast untis will be changed from auto to resolved type, + // so this api call is ececuted only once per sm execution + // if units is auto, try to discover if this is USA or not + if (config.forecast.units === "auto") { + // if the geoposition api key is set + if(config.geoPosition.key){ + let url= "https://maps.googleapis.com/maps/api/geocode/json?latlng="+ + geoposition.coords.latitude.toString().substring(0, 10) + + "," + + geoposition.coords.longitude.toString().substring(0, 11) + + "&key="+config.geoPosition.key + $http + .get( + url + ) + .then((results) => { + // point to the first data entry + let addresses=results.data.results[0].address_components + // get just the country entry + let info = addresses.filter((entry) =>{ + return JSON.stringify(entry.types) === JSON.stringify(['country','political']) + }) + if(info.length){ + if(info[0].short_name === 'US') + config.forecast.units = "us" + else config.forecast.units = "si" + service.weather={} + service.weather["forecast"] = info + if(service.weather["forecast"]===null) + service.weather["forecast"] = {} + service.weather.forecast.data = {} + service.weather.forecast.data["currently"] = [] + // set the currently + service.weather.forecast.data.currently["data"] = null + resolve((service.weather)) + } else { + console.error("weather unable to determine country from geolocation") + reject() + } + }) + .catch((error) => { + console.error( + "weather google geocode country from geolocation failed =" + + JSON.stringify(error) + ) + reject() + }) + } + else { + console.error("geoposition apikey not set, needed by weather"); + reject() // + } + } else + resolve(null) + } else { + // darksky, nothign to do here + resolve(null) + } + }) + } + service.get.Openweather = function (geoposition) { + return new Promise((resolve, reject) => { + //$http.get("https://api.openweathermap.org/data/2.5/onecall?lat=30.4548443&lon=-97.6222674&appid=a6bf9feaa86bc2677df1e5f46bd79d55") + $http + .get( + "https://api.openweathermap.org/data/2.5/onecall?lat=" + + geoposition.coords.latitude.toString().substring(0, 10) + + "&lon=" + + geoposition.coords.longitude.toString().substring(0, 11) + + "&units=" + + (config.forecast.units == "us" ? "imperial" : "metric") + + "&appid=" + + config.forecast.key + ) + .then(function (response) { + //console.log("json="+JSON.stringify(response.data)); + service.weather={} + //service.weather["forecast"] = response.data + //if(service.weather["forecast"]===null || service.weather["forecast"]=== undefined) + service.weather["forecast"] = response.data + // set the currently + service.weather.forecast.data = response.data + resolve((service.weather)) + }) + .catch(() => { + reject() + }) + }) + } + service.get.Darksky = function (geoposition) { + return new Promise((resolve, reject) => { + $http + .jsonp( + "https://api.darksky.net/forecast/" + + config.forecast.key + + "/" + + geoposition.coords.latitude + + "," + + geoposition.coords.longitude + + "?units=" + + config.forecast.units + + "&lang=" + + language + + "&callback=JSON_CALLBACK" + ) + .then(function (response) { + //console.log("json="+JSON.stringify(response.data)); + service.weather={} + service.weather.forecast={} + service.weather.forecast.data = response.data + resolve((service.weather)) + }) + .catch(() => { + reject() + }) + }) + } + service.get.Climacell = function (geoposition) { + // return a promise, so the caller can wait + return new Promise((resolve, reject) => { + // list of concurrent requests + var plist = [] + var forecast = null + var currently = null + // "https://data.climacell.co/v4/timelines?timesteps=1h&units=" + this.config.tempUnits + "&location=" + this.config.lat + "," + this.config.lon + "&fields=temperature,temperatureApparent,precipitationType,humidity,windSpeed,windDirection,weatherCode&apikey=" + this.config.apiKey + + plist.push( $http.get( + /* 'https://api.climacell.co/v3/weather/realtime?lat=' + + geoposition.coords.latitude.toString().substring(0,10) + '&lon=' + geoposition.coords.longitude.toString().substring(0,11) + '&unit_system=' + + config.forecast.units + '&fields=temp%2Cprecipitation%2Cweather_code%2Ccloud_cover%2Csunrise%2Csunset%2Cvisibility%2Cwind_gust%2Cwind_speed&apikey='+config.forecast.key */ + "https://data.climacell.co/v4/timelines?timesteps=1h"+ + "&units=" + 'metric' + //config.forecast.units + + "&location=" + geoposition.coords.latitude.toString().substring(0,10) + "," + geoposition.coords.longitude.toString().substring(0,11) + + "&fields=temperature,temperatureApparent,precipitationType,humidity,windSpeed,windDirection,weatherCode"+ + "&apikey="+config.forecast.key + ).then( + (response)=>{ + currently=response.data.data; + } + ).catch((error)=>{ + console.log("climacell realltime failed ="+JSON.stringify(error)) + reject(); + }) + ) + // get the 10 day forecast info + plist.push($http.get( + "https://data.climacell.co/v4/timelines?timesteps=1d"+ + '&location=' + geoposition.coords.latitude.toString().substring(0,10) + ',' + geoposition.coords.longitude.toString().substring(0,11) + + "&units=" + 'metric' + // config.forecast.units + + '&fields=temperatureMax,temperatureMin,precipitationType,weatherCode'+ + '&apikey='+config.forecast.key + ).then( + (response)=>{ + forecast=response.data.data; + } + ).catch( (error)=>{ + console.log("climacell forecast failed ="+JSON.stringify(error)) + reject(); + }) + ) + + // wait for both above apis to complete + Promise.all(plist).then(() => { + // save the total data + service.weather={} + service.weather["forecast"] = forecast + if(service.weather["forecast"]===null) + service.weather["forecast"] = {} + service.weather.forecast.data = {} + service.weather.forecast.data["currently"] = [] + // set the currently + service.weather.forecast.data.currently["data"] = currently + resolve(service.weather) + }) + }) + } + service.setCurrent=function(currentdata){service.weatherdata=currentdata} + service.getWeatherData=function(){return service.weatherdata} + return service; + } + + angular.module('SmartMirror') + .factory('WeatherService', WeatherService); +} ()); + + + + + + From 06e9c8cb53fb13eec15d10139f8a2dd0adc71a26 Mon Sep 17 00:00:00 2001 From: Sam Detweiler Date: Fri, 14 Oct 2022 21:31:35 -0500 Subject: [PATCH 07/10] fixup services for broadcast status or data --- package.json | 1 + plugins/speech/service.js | 2 +- plugins/timer/controller.js | 53 +++++++++++++++++++++++------------ plugins/timer/service.js | 18 +++++++++--- plugins/weather/controller.js | 2 +- plugins/weather/service.js | 3 +- scripts/raspi-monitor.sh | 35 +++++++++++++++++++---- 7 files changed, 83 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index 1a5c568ca..93a95ff13 100755 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "wiredep-cli": "^0.1.0" }, "dependencies": { + "@fortawesome/fontawesome-free": "^6.2.0", "@google-cloud/speech": "^4.2.0", "@grpc/grpc-js": "^1.2.11", "alphavantage": "1.2.6", diff --git a/plugins/speech/service.js b/plugins/speech/service.js index 6d4143377..26a52bb2f 100755 --- a/plugins/speech/service.js +++ b/plugins/speech/service.js @@ -10,7 +10,7 @@ const { ipcRenderer, remote } = require("electron"); var commandList = []; var commandPage = []; - config.communications_port = remote.getGlobal("sonusSocket"); + config.communications_port = 5200 || remote.getGlobal("sonusSocket"); service.init = function (cb) { // workaround so we can trigger requests at any time annyang.isListening = () => { diff --git a/plugins/timer/controller.js b/plugins/timer/controller.js index 71d7a14c4..6fe296858 100644 --- a/plugins/timer/controller.js +++ b/plugins/timer/controller.js @@ -1,50 +1,67 @@ function Timer($scope, TimerService, SpeechService, Focus) { + const operationTypes={ + "start":startTimer,"stop":stopTimer,"show":showTimer,"resume":resumeTimer} // Start timer - SpeechService.addCommand('timer_start', function (duration) { + function startTimer(params) { + if(typeof params !== 'object'){ + params={name:'default',duration:params, type:"start"} + } console.debug("Starting timer"); Focus.change("timer"); $scope.timer = TimerService; - TimerService.start(duration); + TimerService.start(params); $scope.$watch('timer.countdown', function (countdown) { if (countdown === 0) { - TimerService.stop(); + TimerService.stop(params); // defaultView(); } }); - }); + } + SpeechService.addCommand('timer_start',startTimer ); - // Show timer - SpeechService.addCommand('timer_show', function () { + function showTimer(params={name:'default', type:"show"}) { if (TimerService.running) { // Update animation if (TimerService.paused) { - TimerService.start(); - TimerService.stop(); + TimerService.start(params); + TimerService.stop(params); } else { - TimerService.start(); + TimerService.start(params); } Focus.change("timer"); } - }); + } + // Show timer + SpeechService.addCommand('timer_show', showTimer ); - // Stop timer - SpeechService.addCommand('timer_stop', function () { + function stopTimer(params={name:'default', type:"stop"}) { if (TimerService.running && !TimerService.paused) { - TimerService.stop(); + TimerService.stop(params); } - }); + } - // Resume timer - SpeechService.addCommand('timer_resume', function () { + // Stop timer + SpeechService.addCommand('timer_stop', stopTimer); + + function resumeTimer(params={name:'default', type:"resume"}) { if (TimerService.running && TimerService.paused) { - TimerService.start(); + TimerService.start(params); Focus.change("timer"); } - }); + } + // Resume timer + SpeechService.addCommand('timer_resume',resumeTimer ); + $scope.$on('TimerService', (events,params)=>{ + if(params && params.type){ + if (Object.keys(operationTypes).contains(params.type.toLowerCase())){ + operationTypes[params.type.toLowerCase()](params) + } + } + }) } diff --git a/plugins/timer/service.js b/plugins/timer/service.js index 2b177a23e..72ce8f24b 100644 --- a/plugins/timer/service.js +++ b/plugins/timer/service.js @@ -10,6 +10,7 @@ service.paused = true; service.duration = 0; service.countdown = -1; + //service.timerlist={} /** * Parse spoken duration into seconds @@ -44,10 +45,13 @@ }, 1000); }; - service.start = function (duration) { - if (angular.isDefined(duration)) { - if (isNaN(duration)) { - duration = parseDuration(duration); + service.start = function (params) { + //if (angular.isDefined(duration)) { + // get seconds of timer duration + let duration=params.duration + if( params.duration != undefined){ + if ( isNaN(params.duration)) { + duration = parseDuration(params.duration); } if (service.running) { service.reset(); @@ -64,6 +68,7 @@ intervalId = startTimer(); service.paused = false; + $rootScope.$broadcast("timer:start", service.countdown); } }; @@ -152,6 +157,11 @@ }); } }; + function signalForTimer(params){ + console.log("signaling timer="+JSON.stringify(params)) + $rootScope.$broadcast('TimerService',params) + } + service.startTimer=service.stopTimer=service.resumeTimer=service.showTimer=signalForTimer }; angular.module('SmartMirror') diff --git a/plugins/weather/controller.js b/plugins/weather/controller.js index 7557a9fa5..3bb0d989b 100644 --- a/plugins/weather/controller.js +++ b/plugins/weather/controller.js @@ -239,7 +239,7 @@ function Weather($scope, $rootScope, $interval, $http, $translate, GeolocationSe config.forecast.keytype = (config.forecast.keytype + " ").split(" ")[0] config.forecast.key = config.forecast.key.trim() // map location to country for auto weather units (if needed) - WeatherService.getCountry().then(() => { + WeatherService.getCountry(geoposition).then(() => { // get the weather info WeatherService.get[config.forecast.keytype](geoposition).then( (weather_data) => { diff --git a/plugins/weather/service.js b/plugins/weather/service.js index 90a51ae39..9f36a8b35 100644 --- a/plugins/weather/service.js +++ b/plugins/weather/service.js @@ -7,12 +7,11 @@ typeof config.general.language !== "undefined" ? config.general.language.substr(0, 2) : "en" - var geoposition = {} var service = {}; service.events = []; service.get= {} - service.getCountry = function () { + service.getCountry = function (geoposition) { return new Promise((resolve, reject) => { if (config.forecast.keytype != "Darksky") { // forecast untis will be changed from auto to resolved type, diff --git a/scripts/raspi-monitor.sh b/scripts/raspi-monitor.sh index dc7a27e06..cf85bffe2 100755 --- a/scripts/raspi-monitor.sh +++ b/scripts/raspi-monitor.sh @@ -2,12 +2,20 @@ # Script to enable and disable the HDMI signal of the Raspberry PI CMD="$1" CMD=${CMD,,} -type=vcgencmd +# check if type supplied +if [ "$2." != "." ]; then + type=$2 + type=${type,,} + t=type +else + type=vcgencmd +fi + function on { case $type in "tvservice") - (tvservice -p && sudo chvt 6 && sudo chvt 7) >/dev/null + tvservice -p && sudo chvt 6 && sudo chvt 7 >/dev/null ;; "dpms") # this one doesn't work @@ -18,11 +26,18 @@ function on { xset dpms force on >/dev/null exit ;; + "vcgencmd") (/usr/bin/vcgencmd display_power 1) >/dev/null ;; "cec-utils") - (echo 'on 0' | cec-client -s) >/dev/null + echo "on 0" | cec-client -s >/dev/null + ;; + "xrandr") + xrandr --output HDMI-1 --rotate $3 --auto + ;; + "xset") + xset dpms force on >/dev/null ;; esac } @@ -34,7 +49,11 @@ function off { ;; "dpms") export DISPLAY=:0 - export LOGIN_USER="$2" + if [ "$3." !="." $t == "dpms"]; then + export LOGIN_USER="$3" + else + export LOGIN_USER="$2" + fi su - $LOGIN_USER sudo xhost local:$LOGIN_USER &>/dev/null xset dpms force off >/dev/null @@ -44,7 +63,13 @@ function off { (/usr/bin/vcgencmd display_power 0) >/dev/null ;; "cec-utils") - (echo 'standby 0' | cec-client -s) >/dev/null + echo standby 0 | cec-client -s >/dev/null + ;; + "xrandr") + xrandr --output HDMI-1 --off >/dev/null + ;; + "xset") + xset dpms force off >/dev/null ;; esac } From 6ee7004f45280ecb48915efe10768ce7d6f5a43f Mon Sep 17 00:00:00 2001 From: Sam Detweiler Date: Fri, 14 Oct 2022 23:01:35 -0500 Subject: [PATCH 08/10] fix for server broadcasts --- plugins/calendar/controller.js | 5 +++++ plugins/calendar/service.js | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/calendar/controller.js b/plugins/calendar/controller.js index 81de06181..b1288d392 100644 --- a/plugins/calendar/controller.js +++ b/plugins/calendar/controller.js @@ -3,7 +3,12 @@ function Calendar($scope, $rootScope, $http, $interval, CalendarService) { var getCalendar = function(){ CalendarService.getCalendarEvents().then(function () { $scope.calendar=CalendarService.getFutureEvents() +<<<<<<< Updated upstream $rootScope.$broadcast('calendar',$scope.calendar ); +======= + if($scope.calendar) + $rootScope.$broadcast('calendar',$scope.calendar ); +>>>>>>> Stashed changes }, function (error) { console.log(error); }); diff --git a/plugins/calendar/service.js b/plugins/calendar/service.js index ea2e5291a..8e7946559 100644 --- a/plugins/calendar/service.js +++ b/plugins/calendar/service.js @@ -195,10 +195,10 @@ return service.events; } - service.getFutureEvents = function () { + service.getFutureEvents = function (days,count) { var future_events = [], current_date = new moment(), - end_date = new moment().add(config.calendar.maxDays, 'days'); + end_date = new moment().add(days || config.calendar.maxDays, 'days'); service.events.forEach(function (itm) { //If the event started before current time but ends after the current time or @@ -208,7 +208,7 @@ } }); future_events = sortAscending(future_events); - return future_events.slice(0, config.calendar.maxResults); + return future_events.slice(0, count || config.calendar.maxResults); } var sortAscending = function (events) { From 6710123f3694c8924d2b37bc213bf5763903e552 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Fri, 5 Jan 2024 18:07:44 -0600 Subject: [PATCH 09/10] bring up to node 18 --- app/js/app.js | 2 +- main.js | 9 +++++---- package.json | 10 ++++++---- plugins/calendar/controller.js | 6 +----- plugins/weather/config.schema.json | 7 ++++--- plugins/weather/controller.js | 4 ++++ plugins/weather/service.js | 30 ++++++++++++++++++++++++++++++ scripts/pi-install.sh | 8 ++++---- scripts/pm2_smart-mirror.json | 4 ++-- 9 files changed, 57 insertions(+), 23 deletions(-) diff --git a/app/js/app.js b/app/js/app.js index a71f89941..ecf0671f2 100755 --- a/app/js/app.js +++ b/app/js/app.js @@ -16,7 +16,7 @@ .config(function (tmhDynamicLocaleProvider) { console.log(config); tmhDynamicLocaleProvider.localeLocationPattern( - "bower_components/angular-i18n/angular-locale_" + + "node_modules/angular-i18n/angular-locale_" + language + ".js" ); diff --git a/main.js b/main.js index 03e8a0e5f..b11217de9 100755 --- a/main.js +++ b/main.js @@ -281,17 +281,18 @@ if (config.motion && config.motion.enabled) { mtnProcess.stdout.on("data", function (data) { var message = data.toString(); + //console.log("motion message="+message) if (message.startsWith("!s:")) { - console.log(message.substring(3)); + //console.log(message.substring(3)); mainWindow.webContents.send("motionstart", true); } else if (message.startsWith("!e:")) { - console.log(message.substring(3)); + //console.log(message.substring(3)); mainWindow.webContents.send("motionend", true); } else if (message.startsWith("!c:")) { - console.log(message.substring(3)); + //console.log(message.substring(3)); mainWindow.webContents.send("calibrated", true); } else if (message.startsWith("!E:")) { - console.log(message.substring(3)); + //console.log(message.substring(3)); mainWindow.webContents.send("Error", message.substring(3)); mtnProcess.kill(); } else { diff --git a/package.json b/package.json index b76859d57..5aa278814 100755 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "main.js", "scripts": { "install": "bower install", - "start": "DISPLAY=\"${DISPLAY:=:0}\" electron main.js ", + "start": "DISPLAY=\"${DISPLAY:=:0}\" electron --disable_gpu main.js ", "train-model": "electron scripts/train-model.js", "sonus": "node sonus.js", "motion": "sudo node motion.js", @@ -36,27 +36,29 @@ "devDependencies": { "angular-i18n": "^1.8.2", "bower": "^1.8.12", - "electron": "17.2.0", + "electron": "^27.0.2", "eslint": "^6.8.0", "wiredep-cli": "^0.1.0" }, "dependencies": { + "@electron/rebuild": "github:electron/rebuild", + "@electron/remote": "^2.0.12", "@fortawesome/fontawesome-free": "^6.2.0", "@google-cloud/speech": "^4.2.0", "@grpc/grpc-js": "^1.2.11", - "alphavantage": "1.2.6", + "alphavantage": "^2.5.0", "angular-moment": "^1.3.0", "angular-sanitize": "1.5.11", "annyang": "^2.6.1", "cheerio": "^1.0.0-rc.5", "electron-json-storage": "^4.4.0", - "electron-rebuild": "^1.11.0", "express": "^4.17.1", "fitbit-oauth2": "0.0.1", "humanize-duration": "^3.25.1", "hyperion-client": "1.0.3", "jsonform": "git+https://git@github.com/jsonform/jsonform.git", "moment": "^2.29.1", + "mqtt": "^5.3.3", "pretty-ms": "^7.0.1", "recorder": "git+https://git@github.com/sdetweil/recorder.git", "rss-parser": "^3.12.0", diff --git a/plugins/calendar/controller.js b/plugins/calendar/controller.js index b1288d392..fc552b74b 100644 --- a/plugins/calendar/controller.js +++ b/plugins/calendar/controller.js @@ -3,12 +3,8 @@ function Calendar($scope, $rootScope, $http, $interval, CalendarService) { var getCalendar = function(){ CalendarService.getCalendarEvents().then(function () { $scope.calendar=CalendarService.getFutureEvents() -<<<<<<< Updated upstream - $rootScope.$broadcast('calendar',$scope.calendar ); -======= if($scope.calendar) $rootScope.$broadcast('calendar',$scope.calendar ); ->>>>>>> Stashed changes }, function (error) { console.log(error); }); @@ -19,4 +15,4 @@ function Calendar($scope, $rootScope, $http, $interval, CalendarService) { } angular.module('SmartMirror') - .controller('Calendar', Calendar); \ No newline at end of file + .controller('Calendar', Calendar); diff --git a/plugins/weather/config.schema.json b/plugins/weather/config.schema.json index 807dbc76a..b616aec1b 100644 --- a/plugins/weather/config.schema.json +++ b/plugins/weather/config.schema.json @@ -11,9 +11,10 @@ "keytype": { "type": "string", "title": "{{API Key source}}", - "enum": ["Darksky is no longer giving out keys, and the current ones will expire at the end of 2021, but can still be used if you have one", + "enum": ["PirateWeather see pirateweather.net and ApiKey to obtain the weather api key", "Climacell see climacell.co documentation to obtain the weather API Key", - "Openweather see openweathermap.org documentation to obtain the weather API Key for One Call API"] + "Openweather see openweathermap.org documentation to obtain the weather API Key for One Call API", + "Darksky is no longer giving out keys, and the current ones will expire at the end of 2021, but can still be used if you have one"] }, "units": { "type": "string", @@ -36,4 +37,4 @@ "order":0 } ] -} \ No newline at end of file +} diff --git a/plugins/weather/controller.js b/plugins/weather/controller.js index b84375ad2..8cf9f9ddb 100644 --- a/plugins/weather/controller.js +++ b/plugins/weather/controller.js @@ -33,6 +33,7 @@ function Weather($scope, $rootScope, $interval, $http, $translate, GeolocationSe let ctemp = 0 switch (config.forecast.keytype) { case "Darksky": + case "PirateWeather": weather.forecast.data.currently.day = moment .unix(weather.forecast.data.currently.time) .format("ddd") @@ -97,6 +98,7 @@ function Weather($scope, $rootScope, $interval, $http, $translate, GeolocationSe var datalength = 0 switch (config.forecast.keytype) { case "Darksky": + case "PirateWeather": // Add human readable info to info for (i = 0; i < weather.forecast.data.daily.data.length; i++) { weather.forecast.data.daily.data[i].day = @@ -211,6 +213,7 @@ function Weather($scope, $rootScope, $interval, $http, $translate, GeolocationSe } switch (config.forecast.keytype) { case "Darksky": + case "PirateWeather": weather.forecast.data.hourly.day = moment .unix(weather.forecast.data.hourly.data[0].time) .format("ddd") @@ -353,6 +356,7 @@ function Weather($scope, $rootScope, $interval, $http, $translate, GeolocationSe case "mostly_clear": case 1100: case "clear sky": + case "clear day": case "few clouds": case "clear": case 1000: diff --git a/plugins/weather/service.js b/plugins/weather/service.js index 9f36a8b35..fa4f7824f 100644 --- a/plugins/weather/service.js +++ b/plugins/weather/service.js @@ -131,6 +131,36 @@ }) }) } + service.get.PirateWeather = function (geoposition) { + return new Promise((resolve, reject) => { + $http + .get( //.jsonp( + "https://api.pirateweather.net/forecast/" + + config.forecast.key + + "/" + + geoposition.coords.latitude + + "," + + geoposition.coords.longitude + + "?units=" + + config.forecast.units + // + + // "&lang=" + + // language + + // "&callback=JSON_CALLBACK" + ) + .then(function (response) { + //console.log("json="+JSON.stringify(response.data)); + service.weather={} + service.weather.forecast={} + service.weather.forecast.data = response.data + resolve((service.weather)) + }) + .catch((error) => { + console.log("error="+JSON.stringify(error)) + reject() + }) + }) + } service.get.Climacell = function (geoposition) { // return a promise, so the caller can wait return new Promise((resolve, reject) => { diff --git a/scripts/pi-install.sh b/scripts/pi-install.sh index ce3f6c450..b5c5af215 100755 --- a/scripts/pi-install.sh +++ b/scripts/pi-install.sh @@ -3,10 +3,10 @@ #set -e # Supported versions of node: v4.x, v5.x, v6.x, v7.x, 8.x, 10.x, 14.x -NODE_MINIMUM_VERSION="v14.0.0" -NODE_STABLE_VERSION="14.x" -NPM_TESTED="V6.0.0" -NODE_TESTED="V14.0.0" +NODE_MINIMUM_VERSION="v18.0.0" +NODE_STABLE_VERSION="20.x" +NPM_TESTED="V10.0.0" +NODE_TESTED="V18.0.0" PM2_FILE=pm2_smart_mirror.json # Compare node versions. diff --git a/scripts/pm2_smart-mirror.json b/scripts/pm2_smart-mirror.json index 75b094405..19f395ac9 100644 --- a/scripts/pm2_smart-mirror.json +++ b/scripts/pm2_smart-mirror.json @@ -1,8 +1,8 @@ { "apps" : [{ "name" : "smart-mirror", - "script" : "/home/pi/smart-mirror.evan/scripts/bash-start.sh", - "watch" : ["/home/pi/smart-mirror.evan/config.json"], + "script" : "/home/sam/temp/sssss/smart-mirror/scripts/bash-start.sh", + "watch" : ["/home/sam/temp/sssss/smart-mirror/config.json"], "autorestart" : true }] } From 120fc2dbd7e28574f8bd0b53e2d35f2ddf65dc90 Mon Sep 17 00:00:00 2001 From: sam detweiler Date: Fri, 5 Jan 2024 18:15:07 -0600 Subject: [PATCH 10/10] move some dev dependencies to regular dependencies --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5aa278814..85980c55d 100755 --- a/package.json +++ b/package.json @@ -33,14 +33,12 @@ "url": "https://github.com/evancohen/smart-mirror/issues" }, "homepage": "https://github.com/evancohen/smart-mirror", - "devDependencies": { - "angular-i18n": "^1.8.2", - "bower": "^1.8.12", - "electron": "^27.0.2", - "eslint": "^6.8.0", - "wiredep-cli": "^0.1.0" + "devDependencies": { + "eslint": "^6.8.0" }, "dependencies": { + "bower": "^1.8.12", + "electron": "^27.0.2", "@electron/rebuild": "github:electron/rebuild", "@electron/remote": "^2.0.12", "@fortawesome/fontawesome-free": "^6.2.0", @@ -49,6 +47,7 @@ "alphavantage": "^2.5.0", "angular-moment": "^1.3.0", "angular-sanitize": "1.5.11", + "angular-i18n": "^1.8.2", "annyang": "^2.6.1", "cheerio": "^1.0.0-rc.5", "electron-json-storage": "^4.4.0", @@ -67,6 +66,7 @@ "spotify-web-api-node": "^5.0.0", "waitprocess": "git+https://github.com/sdetweil/waitprocess.git", "winston": "^3.3.3", + "wiredep-cli": "^0.1.0", "youtube-api": "^3.0.1" }, "eslintConfig": {