Skip to content

Latest commit

 

History

History
210 lines (161 loc) · 7.13 KB

File metadata and controls

210 lines (161 loc) · 7.13 KB

MealWish

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.

Meal Wish

How it works

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.

Status and known limitations

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-server exposes 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 /upload endpoint 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-scripts 5.0.1 (no longer maintained upstream), multer 1.x, json-server 0.17. npm audit will 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_custom mechanism 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.

Installation

Preconditions

Clone the repository:

git clone https://github.com/mschmicking/MealWish.git

Point 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 build

The backend build emits JavaScript into backend/dist/, with the entry point at backend/dist/server.js.

Home Assistant add-on

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: 3000

The 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.

Frontend

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>.js

Restart Home Assistant and the MealWish panel should appear in the sidebar.

Development

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 server

Outside 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.

License

Licensed under the GNU Affero General Public License v3.0.