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
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better 💗
Register Login