Skip to content

Commit

Permalink
feat: add support for mealie v2 (households)
Browse files Browse the repository at this point in the history
Fixes #69
  • Loading branch information
zanix committed Oct 23, 2024
1 parent 0a64fa2 commit 54f83ae
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 21 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ These are the possible options:
| `apiKey` | <p>An API key generated from a user profile in Mealie.</p><p>**REQUIRED** When not using username and password<br>**Type:** `string`<br>**Example:** `"eyhJbcG..."`<br>**Default value:** none</p><p>**Note:** You can generate a key by going to your user profile in Mealie then to API Tokens link (or using this path `/user/profile/api-tokens`).</p>|
| `username` | <p>The username/email for your Mealie account.</p><p>**REQUIRED** When not using `apiKey`<br>**Type:** `string`<br>**Example:** `"[email protected]"`<br>**Default value:** none</p>|
| `password` | <p>The password for your for Mealie account.</p><p>**REQUIRED** When not using `apiKey`<br>**Type:** `string`<br>**Example:** `"Secret!"`<br>**Default value:** none</p>|
| `groupId` | <p>The Group ID (as a UUID) to use when fetching the meal plan.</p><p>**Type:** `string`<br>**Example:** `"c0aa0c1c-bdbb-4948-823b-2a725fb05ce1"`<br>**Default value:** none ("Home" group)</p><p>**Note 1:** You can get the UUID of a group from Settings > Groups.</p><p>**Note 2:** The default "Home" group is used when this is blank.</p>|
| `groupId` | <p>The Household ID (as a UUID) to use when fetching the meal plan.</p><p>**Type:** `string`<br>**Example:** `"c0aa0c1c-bdbb-4948-823b-2a725fb05ce1"`<br>**Default value:** none ("Family" household)</p><p>**Note 1:** You can get the UUID of a household from Settings > Households.</p><p>**Note 2:** The default "Family" household is used when this is blank.</p><p>**Note 3:** For Mealie v1, Households were called Groups, v2 changed this to Households and Groups moved within Households.|
| `currentWeek` | <p>Only show meals for the current week. Set to `true` to display meals up-to the end of the week. The beginning of the week is set using `weekStartsOnMonday`. The number of meals displayed is limited by `dayLimit`, `entryLimit`, `priorDayLimit`, and `priorEntryLimit`.</p><p>**Type:** `boolean`<br>**Default value:** `true`<br>**Possible values:** `true` and `false`|
| `weekStartsOnMonday` | <p>Show Monday as the first day of the week. Set to `true` to show Monday as the first day of the week.</p><p>**Type:** `boolean`<br>**Default value:** `false`<br>**Possible values:** `true` and `false`</p><p>**Note:** Only valid when `currentWeek` is set to `true`.</p>|
| `dayLimit` | <p>How many days will be displayed after today.</p><p>**Type:** `integer`<br>**Example:** `5`<br>**Default value:** `7`<br>**Unit:** `days`</p><p>**Note:** If `0`, only today will be shown.</p><p>**Note 2:** If `currentWeek` is `true`, the max number of days is until the end of the week.|
Expand Down
97 changes: 77 additions & 20 deletions node_helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,13 @@ module.exports = NodeHelper.create({
this.token = null;
this.tokenExpiration = null;
this.outstandingRequest = false;
this.ApiVersion = "1.0.0";
},

socketNotificationReceived (notification, payload) {
switch (notification) {
case "MEALIE_INIT":
// Use API Key or fetch a token.
if (payload.apiKey) {
this.token = payload.apiKey;
this.initComplete(payload);
} else {
this.getToken(payload)
.then(() => {
this.initComplete(payload);
})
.catch((error) => {
Log.error(`[${this.name}] Auth error:`, JSON.stringify(error.toString()));

this.sendSocketNotification("MEALIE_ERROR", {
error: "AUTH_ERROR",
details: error,
identifier: payload.identifier
});
});
}
this.initApi(payload);
break;

case "MEALIE_MENU_GET":
Expand All @@ -42,12 +25,60 @@ module.exports = NodeHelper.create({
}
},

async initApi (payload) {
try {
// Get API version
await this.getApiVersion(payload);

// Use API key or fetch token
if (payload.apiKey) {
this.token = payload.apiKey;
} else {
await this.getToken(payload);
}

this.initComplete(payload, this.token);
} catch (error) {
Log.error(`[${this.name}] Initialization error:`, JSON.stringify(error.toString()));

this.sendSocketNotification("MEALIE_ERROR", {
error: "INIT_ERROR",
details: error,
identifier: payload.identifier
});
}
},

initComplete (payload) {
this.sendSocketNotification("MEALIE_INITIALIZED", {
identifier: payload.identifier
});
},

getApiVersion (payload) {
const url = new URL(`${payload.host}/api/app/about`);

return fetch(url, {
method: "GET",
headers: {
Accept: "application/json"
}
})
.then((response) => {
if (response.ok) {
return response;
}
throw response.statusText;
})
.then((response) => response.json())
.then((data) => {
this.ApiVersion = data.version;
})
.catch((error) => {
throw error;
});
},

getToken (payload) {
const url = new URL(`${payload.host}/api/auth/token`);
const params = new URLSearchParams();
Expand Down Expand Up @@ -134,7 +165,11 @@ module.exports = NodeHelper.create({

Log.info(`[${this.name}] Fetching meals: Start Date ${startDate.format("YYYY-MM-DD")} - End date ${endDate.format("YYYY-MM-DD")}`);

const url = new URL(`${payload.host}/api/groups/mealplans`);
let url = new URL(`${payload.host}/api/groups/mealplans`);
// Check for new API endpoint.
if (this.versionCheck(this.ApiVersion, "2.0.0")) {
url = new URL(`${payload.host}/api/households/mealplans`);
}

const params = new URLSearchParams();
params.append("start_date", startDate.format("YYYY-MM-DD"));
Expand Down Expand Up @@ -193,6 +228,28 @@ module.exports = NodeHelper.create({
});
},

versionCheck (version, required) {
const minParts = required
.replace(/[^0-9.]/ug, "")
.trim()
.split(".");
const newParts = version
.replace(/[^0-9.]/ug, "")
.trim()
.split(".");
for (let part = 0; part < newParts.length; part += 1) {
const verA = parseInt(newParts[part], 10);
const verB = parseInt(minParts[part], 10);
if (verA > verB) {
return true;
}
if (verA < verB) {
return false;
}
}
return true;
},

getFirstDayOfWeek (weekStartsOnMonday) {
const today = moment();
let firstDayOfWeek = moment();
Expand Down
1 change: 1 addition & 0 deletions translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"ERROR_NO_USER": "You have not provided a username! Username is required when not using the apiKey.<br>Please set 'username' in config.js!",
"ERROR_NO_PASS": "You have not provided a password! Password is required when not using the apiKey.<br>Please set 'username' in config.js!",
"ERROR_MEAL_SORT_ORDER": "mealSortOrder should be an array of strings continaing: ['breakfast', 'lunch', 'dinner', 'side']. They should appear exactly once in the desired sort order.",
"INIT_ERROR": "Initialization Error: There was an error setting up Mealie.",
"AUTH_ERROR": "Authentication Error: There was an error fetching a user token. Check your username and password.",
"FETCH_ERROR": "Fetch Error: There was an error fetching data from Mealie.",
"MEALIE_SUSPEND": "Function suspend - module: {name} with identifier: {identifier}",
Expand Down

0 comments on commit 54f83ae

Please sign in to comment.