Meal planning and shopping helper for Home Assistant households.
Important
This project is no longer maintained. It is published as a reference and a portfolio piece. Issues and pull requests will not be answered, dependencies will not be updated, and no further releases are planned. The code is frozen in the state it reached in early 2024, which is the state it ran in for a while in my own household. You are welcome to fork it — see License.
MealWish helps households collect meal wishes, plan upcoming meals, and organize shopping.
Each member can choose meals they would like to eat in the coming weeks. The person cooking, or the household as a group, can then decide which dishes will be prepared on specific days. The meal plan is visible in an overview, and the people responsible for shopping can use it to plan what they need to buy.
The interesting part of MealWish is how a normal React app gets to live inside Home Assistant's UI.
The frontend is a Create React App build that registers itself as a custom
element, <meal-wish>, and attaches its own Shadow DOM on
connectedCallback. Because MUI renders through Emotion, a dedicated Emotion
cache is created with its container pointed at a <style> element inside that
shadow root — so all component styles are injected into the shadow tree instead
of the host document, and nothing leaks in either direction. MUI's portalling
components (Popover, Popper, Modal) render into document.body by default,
which would escape the shadow root and lose their styling, so they are
re-targeted at the shadow container via theme defaultProps.
Home Assistant then mounts the element as a sidebar page through panel_custom
and assigns the hass object onto the element as a property. A context provider
watches that property and exposes it to the React tree, which is how the app
knows which household member is currently logged in.
The backend is deliberately trivial: json-server over a flat db.json, plus one
multer upload route for meal photos, packaged as a Home Assistant add-on so the
data lands in the add-on's config volume.
These are conscious scope decisions for a household LAN project, not an oversight list — but you should know about them before running it anywhere:
- The backend has no authentication or authorization at all.
json-serverexposes full CRUD on every collection on port 3000. Anyone who can reach that port can read, modify, and delete everything. It is designed to sit on a trusted home network. Do not expose it to the internet. - The
/uploadendpoint accepts anything. No file type filter, no size limit (backend/src/server.ts). Uploaded files are then served back over/static. Same LAN-only caveat applies, more so. - Dependencies are frozen at their early-2024 versions — Create React App /
react-scripts5.0.1 (no longer maintained upstream),multer1.x,json-server0.17.npm auditwill have plenty to say. It will not be addressed. - Test coverage is one unit test covering the ISO week helpers, and there is no CI. The project was never built with a test-first workflow.
- The Home Assistant integration was developed against the 2024.x panel API
and has not been verified against current Home Assistant releases. The
panel_custommechanism has been stable, but no promises. - The frontend is a single-user-per-browser experience with no realtime sync; changes made by one household member appear for others on their next page load.
Clone the repository:
git clone https://github.com/mschmicking/MealWish.gitPoint the frontend at your own backend by editing the API URL in
frontend/src/config.tsx. This value is baked into the build, so it has to be set
before you build.
Then build the frontend and the backend:
cd MealWish/frontend
npm install
npm run build
cd ../backend
npm install
npm run buildThe backend build emits JavaScript into backend/dist/, with the entry point at
backend/dist/server.js.
Note
The config.yaml and Dockerfile below are reference instructions, not files
in this repository. This repo is not an installable Home Assistant add-on
repository — there is no repository.yaml and no add-on directory. You create
these two files yourself in your Home Assistant installation.
Create the following structure in your Home Assistant installation:
/
/addon_configs
/local_mealwish-api
/uploads
/db.json
/addons
/meal-wish-api
/config.yaml
/Dockerfile
/package.json
/package-lock.json
/dist
/server.js
Copy backend/package.json, backend/package-lock.json, the built
backend/dist/server.js, and a starting db.json into place. db.json and the
uploads folder live under /addon_configs so that your data survives add-on
restarts and updates.
The content of the config.yaml file:
name: "MealWishAPI"
description: "API for the MealWish Webcomponent"
version: "1.0.0"
slug: "mealwish-api"
init: false
arch:
- aarch64
- amd64
- armhf
- armv7
- i386
startup: services
map:
- addon_config:rw
ports:
3000/tcp: 3000The content of the Dockerfile file:
ARG BUILD_FROM
FROM $BUILD_FROM
RUN apk add --no-cache nodejs npm
WORKDIR /usr/src/app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm install --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "dist/server.js"]ENV NODE_ENV=production matters. The container runs node directly rather than
through an npm script, and the server uses NODE_ENV to decide whether to read and
write its data under /config (the mapped add-on config volume) or in the working
directory. Without it, the add-on writes db.json inside the container and your
data is discarded on every restart.
Restart Home Assistant and install the add-on from the Supervisor panel. For more information, see step 2 of the Home Assistant add-on tutorial.
Copy the main.[hash].js file from frontend/build/static/js/ into your Home
Assistant /config/www/ folder. The hash is generated per build, so check the
actual filename and use it below rather than copying the example verbatim.
In your Home Assistant configuration.yaml, add:
panel_custom:
- name: meal-wish
sidebar_title: Meal Wish
sidebar_icon: mdi:silverware-variant
url_path: mealwish
module_url: /local/main.<hash>.jsRestart Home Assistant and the MealWish panel should appear in the sidebar.
Run the backend and the frontend separately:
cd backend && npm run start:dev # json-server on port 3000, data from ./db.json
cd frontend && npm start # CRA dev serverOutside Home Assistant there is no hass object, so the development build injects
a placeholder user after a short delay. That code is excluded from production
builds.
Licensed under the GNU Affero General Public License v3.0.
