Read the statement by Michael Teeuw here.
Requesting assistance with GroceryApp module
-
Hello!
I am still pretty new to MagicMirror and coding, but decided it’d be worth it to attempt to build a custom module since I couldn’t find exactly what I was looking for elsewhere. Which brings me to MMM-GroceryApp; which has been built (to the best of my ability) using Kroger Developer documentation.
I am at the point where I have the main module config.js, node_helper_js, authorize,js, and the CSS file functioning in a way that doesn’t break the MagicMirror. Unfortunately, thats all that is happening - I can’t see this module at all when MagicMirror runs, the location I put it remains blank. So obviously I am missing something, and because of that I can’t login to authorize the application.
I changed my repository to public so the code can be viewed there, but I will also post the main and node_helper code below. My MagicMirror config.js for this module is pretty basic:
{ module: "MMM-GroceryApp", position: "bottom_right" },
NOTE: The app is set to be on the bottom_right of my 3rd page.
MMM-Kroger.js
Module.register("MMM-GroceryApp", { defaults: { refreshInterval: 1000 * 60 * 5, // refresh every 5 minutes updateInterval: 1000 * 60 * 5 // update every 5 minutes }, requiresVersion: "2.1.0", // Required version of MagicMirror start: function() { var self = this; Log.info("Starting module: " + this.name); //Flag for check if module is loaded this.loaded = false; this.sendSocketNotification("CONFIG", this.config); }, getStyles: function () { return [ "MMM-GroceryApp.css" ]; }, getScripts: function () { return [ "moment.js" ]; }, getStats: function () { this.sendSocketNotification("UPDATE", this.config); }, processData: function(data) { var self = this; this.dataRequest = data; if (this.loaded === false) { self.updateDom(self.config.animationSpeed) ; } this.loaded = true; // the data if load // send notification to node_helper.js this.sendSocketNotification("MMM-GroceryApp-NOTIFICATION_TEST", data); }, // socketNotificationReceived from node_helper.js socketNotificationReceived: function(notification, payload) { console.log("socketNotificationReceived"); if (notification === "STARTED") { this.updateDom(); } else if (notification === "CART_DATA") { this.loaded = true; this.processChargeData(JSON.parse(payload).response); this.updateDom(); } else if (notification === "PRODUCT_SEARCH") { this.loaded = true; this.processDrivestateData(JSON.parse(payload).response); this.updateDom(); } else if (notification === "PRODUCT_DETAILS") { this.loaded = true; this.processVehicleData(JSON.parse(payload).response); this.updateDom(); } else if (notification === "LOCATION_DATA") { this.loaded = true; this.processVehicleData(JSON.parse(payload).response); this.updateDom(); } }, getDom: function() { // create element wrapper for show into the module var self = this; // create element wrapper for show into the module var wrapper = document.createElement("div"); wrapper.style.cssText = "float: right;"; // Data from helper if (this.dataNotification) { var wrapperDataNotification = document.createElement("div"); // translations + datanotification wrapperDataNotification.innerHTML = this.translate("UPDATE") + ": " + this.dataNotification.date; wrapper.appendChild(wrapperDataNotification); } return wrapper; } });
node_helper.js
const NodeHelper = require("node_helper"); var request = require("request"); module.exports = NodeHelper.create({ start() { console.log("Starting node helper for: " + this.name); }, getToken(config) { try { const token = getTokenInternal(config); return token; } catch (err) { console.log(err); return "abc"; } }, getTokenInternal(config) { // Set the configuration settings const credentials = { client: { id: config.client_id, secret: config.client_secret }, auth: { tokenHost: "https://api.kroger.com", tokenPath: "/v1/connect/oauth2/token" }, http: { headers: { "User-Agent": "MMM-GroceryApp" } } }; const oauth2 = require("Basic").create(credentials); const tokenConfig = { grant_type: config.client_credentials, client_secret: config.client_secret, client_id: config.client_id }; try { var tokenObject = oauth2.client_secret.getToken(tokenConfig); return tokenObject.access_token; } catch (error) { console.log("Access Token Error", error.message); } }, getData: function() { var self = this; function getCartData(token) { request.get({ url: baseUrl + "/v1/cart/add", headers: { "Accept": "application/json", "Authorization": "Bearer {{TOKEN}}" }, "processData": false, "data": "{\n \"items\": [\n {\n \"upc\": \"0001200016268\",\n \"quantity\": \2\\n }\n ]\n }" }, function (error, response) { if (!error && response.statusCode == 400) { self.sendSocketNotification("CART_DATA", response); } }); } function getProductSearch(token) { request.get({ url: baseUrl + "/v1/products?filter.brand={{BRAND}}&filter.term={{TERM}}&filter.locationId={{LOCATION_ID}}", headers: { "Accept": "application/json", "Authorization": "Bearer {{TOKEN}}" }, function (error, response) { if (!error && response.statusCode == 400) { self.sendSocketNotification("PRODUCT_SEARCH", response); } }} ); } function getProductDetails(token) { request.get({ url: baseUrl + "/v1/products/{{ID}}?filter.locationId={{LOCATION_ID}}", headers: { "Accept": "application/json", "Authorization": "Bearer {{TOKEN}}" }, function (error, response) { if (!error && response.statusCode == 400) { self.sendSocketNotification("PRODUCT_DETAILS", response); } }} ); } function getLocationData(token) { request.get({ url: baseUrl + "/v1/locations", headers: { "Accept": "application/json", "Authorization": "Bearer {{TOKEN}}" }, function (error, response) { if (!error && response.statusCode == 400) { self.sendSocketNotification("LOCATION_DATA", response); } }} ); } if (accessToken === null ) { var tempToken = getToken(this.config); var localToken = tempToken.then(function(accessToken){ return accessToken; }); localToken.then(function(token) { accessToken = token; getCartData(token); getProductSearch(token); getProductDetails(token); getLocationData(token); }); } else { getCartData(accessToken); getProductSearch(accessToken); getProductDetails(accessToken); getLocationData(token); } }, socketNotificationReceived: function(notification, payload) { if (notification === "MMM-GroceryApp") { this.sendSocketNotification("MMM-GroceryApp"); self.config = payload; self.sendSocketNotification("STARTED", true); self.getData(); self.started = true; } else if (notification == "CONFIG") { self.sendSocketNotification("CART_DATA", self.cart_data); self.sendSocketNotification("PRODUCT_SEARCH", self.product_search); self.sendSocketNotification("PRODUCT_DETAILS", self.product_details); self.sendSocketNotification("LOCATION_DATA", self.location_data); } } });
I really appreciate any assistance and/or tips you are able to provide :)
-
@CurlyQ12391 if u have no notification , then getDom () returns a div with no visible content
this is why most modules put up a dummy message “Loading…” until data arrives
if you add console.log() calls in your node helper
then u can look at the messages in the mm startup messagesuse npm start
-
@CurlyQ12391 also, many of the calls u are making, are not synchronous
(they return with data only when done)but async, they return now(immediately) , and also later when the actual work completes.
but you assumed u got the answer,you could add debugging to the ok (not error) path to trace the calls happening
-
@CurlyQ12391 i made a git repo of this…
https://github.com/sdetweil/MMM-GroceryAppa few problems
the functions at the end of node_helper use self, but it wasn’t defined
the socket notification “config” sends back results, but you haven’t got them yetbut getData() has a flow error… accessToken not defined…
don’t know what you were trying to do
I also added the dummy ‘Loading…’ at the end of getDom() (else {'…})
-
@sdetweil, firstly I’d like to apologize for the gap in my response! I broke my main config shortly after posting this and ended up having to re-flashing my SD card and rebuilding my mirror back up the way I had it from scratch.
I really appreciate you taking the time to answer my question.
My intent with this module was to basically house my grocery cart, similar to the interface I have on my phone or visiting the website. I think that an easier approach might be to use an iFrame module for the website, but I was really wanting to utilize the authorization token process to prevent having to login each time - since the end goal is to have the Mirror up on the wall without a keyboard/mouse.
-
@CurlyQ12391 i understood the intent of the module…
but looking at the specific code in node_helper trying to setup the call to get the data is where I don’t understand your plan
-
@sdetweil my apologies.
The code within the node_helper is mostly from the Kroger Developer documentation. However, I am very new to all this and there is a very good chance that I transferred the documentation over incorrectly or in a way that wont work. I may have incorrectly assumed that by using the Kroger Docs/code it would result in getting similar data to what I’d see upon logging into the mobile/desktop site.
-
@CurlyQ12391 np. I was just trying to help you make progress. I know nothing about the API. but couldnt find anything similar to the code in the helper