KitchenIO is a small self-hosted grocery list service. Version 1 is intentionally focused on two simple lists: products at home and the shopping list.
It is similar in concept to Grocy, but intentionally simpler for daily use.
- Accessible web UI built with semantic HTML.
- Products tab with product name, amount, and plus/minus controls.
- Shopping List tab with checkbox, item name, amount, and plus/minus controls.
- Floating
+button with tab-specific accessible label. - Light and dark themes.
- English and Norwegian UI language.
- Settings page for default theme, default language, and API key creation.
- Optional API key protection for integrations: once a key exists, API calls require it.
- SQLite storage.
- REST API for Home Assistant, Hermes Agent, scripts, and automations.
- Home Assistant custom integration under
custom_components/kitchenio. - Shopping-list items sync both ways with the default Home Assistant
todo.shopping_list. - Accessible modal add screen opened from a
+button. - Docker and Docker Compose support.
- Basic automated tests.
KitchenIO is intentionally simple for home hosting:
- Backend: FastAPI
- Database: SQLite
- Frontend: server-rendered HTML with minimal CSS
- Container: Docker image exposing port
8000
No Node.js build step is required.
git clone https://github.com/turbothomas01/KitchenIO.git
cd KitchenIO
docker compose up -d --buildOpen:
http://localhost:8000
Data is stored in the Docker volume kitchenio-data.
git clone https://github.com/turbothomas01/KitchenIO.git
cd KitchenIO
python3 -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r requirements-dev.txt
python -m uvicorn kitchenio.app:app --reloadOpen:
http://localhost:8000
By default, the SQLite database is created at data/kitchenio.db. Override it with:
KITCHENIO_DB=/path/to/kitchenio.db python -m uvicorn kitchenio.app:app --reload. .venv/bin/activate
python -m pytest -qOpen the settings page from the KitchenIO header, or go directly to:
http://localhost:8000/settings
The settings page lets you choose the default UI language and theme. It also lets you create API keys for trusted integrations such as Home Assistant and Hermes Agent.
API key behavior is intentionally simple for home setup:
- If no API key exists yet, the API is open on the local server so you can bootstrap KitchenIO.
- After you create the first API key, API endpoints require a valid key.
- The UI remains available locally for managing KitchenIO.
- Newly generated keys are shown once. Copy the key immediately.
- After the first key exists, creating additional keys from settings requires entering an existing API key.
Use either header format:
X-API-Key: kio_your_key_hereor:
Authorization: Bearer kio_your_key_hereThe repository includes a HACS-compatible custom integration in custom_components/kitchenio.
Recommended display:
- Use
sensor.kitchenio_stockas a single stock summary sensor. - Use the sensor's
items,low_stock_items, andstock_tableattributes for dashboard cards and automations. - Keep shopping in Home Assistant's built-in list,
todo.shopping_list, instead of managing a second list. - Open shopping-list items sync both ways between Home Assistant and the KitchenIO dashboard, with
kitchenio.sync_shopping_listavailable for immediate syncs. - Home Assistant shopping items use the
Name x1,Name x2quantity format.
Full setup instructions are in docs/home-assistant.md.
KitchenIO is designed accessibility-first:
- Semantic headings, forms, labels, lists, buttons, and landmarks.
- The main navigation uses accessible tab semantics.
- Every input has an associated
<label>. - Buttons use visible text.
- Keyboard users can reach every control with normal tab navigation.
- Focus indicators are visible in both themes.
- Completion state is communicated with text, not color alone.
- Light and dark themes maintain strong contrast.
- The language selector updates the document
langattribute.
Interactive OpenAPI documentation is available at:
http://localhost:8000/docs
Raw OpenAPI JSON is available at:
http://localhost:8000/openapi.json
GET /healthResponse:
{"status":"ok"}GET /api/stockExample response:
[
{
"id": 1,
"name": "Coffee",
"description": "Whole beans",
"amount": "1"
}
]POST /api/stock
Content-Type: application/json
{
"name": "Coffee",
"description": "Whole beans",
"amount": "1"
}PUT /api/stock/1
Content-Type: application/json
{
"name": "Coffee",
"description": "Whole beans",
"amount": "2"
}DELETE /api/stock/1POST /api/stock/1/add-to-shopping-listThis creates a shopping list item using the stock item's name and amount.
GET /api/shopping-listExample response:
[
{
"id": 1,
"item": "Milk",
"amount": "2",
"completed": false,
"stock_item_id": null
}
]Plain text item:
POST /api/shopping-list
Content-Type: application/json
{
"item": "Milk",
"amount": "2"
}Item linked to stock:
POST /api/shopping-list
Content-Type: application/json
{
"item": "Coffee",
"amount": "1",
"stock_item_id": 1
}PUT /api/shopping-list/1
Content-Type: application/json
{
"item": "Milk",
"amount": "3",
"completed": false,
"stock_item_id": null
}DELETE /api/shopping-list/1POST /api/shopping-list/1/completeReplace http://kitchenio.local:8000 with your KitchenIO URL. If you have created an API key in KitchenIO settings, include it in each REST call.
Example header:
headers:
X-API-Key: !secret kitchenio_api_keysensor:
- platform: rest
name: KitchenIO Stock
resource: http://kitchenio.local:8000/api/stock
headers:
X-API-Key: !secret kitchenio_api_key
value_template: "{{ value_json | count }}"
json_attributes:
- id
- name
- description
- amountrest_command:
kitchenio_add_shopping_item:
url: http://kitchenio.local:8000/api/shopping-list
method: POST
headers:
content-type: application/json
X-API-Key: !secret kitchenio_api_key
payload: >
{
"item": "{{ item }}",
"amount": "{{ amount }}"
}Call it from an automation or script:
action: rest_command.kitchenio_add_shopping_item
data:
item: Milk
amount: "2"alias: Add milk to KitchenIO shopping list from helper
trigger:
- platform: state
entity_id: input_button.add_milk_to_shopping_list
action:
- service: rest_command.kitchenio_add_shopping_item
data:
item: Milk
amount: "1"KitchenIO's API uses simple JSON, predictable endpoints, and a single API key header, so Hermes Agent can call it directly. Create a key on /settings, store it securely, and send it as X-API-Key.
Examples of commands Hermes Agent can perform:
- “Add milk to the shopping list”
POST /api/shopping-listwith{"item":"milk","amount":"1"}
- “Add 2 bread to the shopping list”
POST /api/shopping-listwith{"item":"bread","amount":"2"}
- “Show what is in stock”
GET /api/stock
- “Add coffee to stock with amount 1”
POST /api/stockwith{"name":"coffee","description":"","amount":"1"}
- “Move eggs from stock to shopping list”
GET /api/stock, findeggs, thenPOST /api/stock/{id}/add-to-shopping-list
Example curl commands:
curl http://localhost:8000/api/stock \
-H "X-API-Key: $KITCHENIO_API_KEY"
curl -X POST http://localhost:8000/api/stock \
-H 'Content-Type: application/json' \
-H "X-API-Key: $KITCHENIO_API_KEY" \
-d '{"name":"Coffee","description":"Whole beans","amount":"1"}'
curl -X POST http://localhost:8000/api/shopping-list \
-H 'Content-Type: application/json' \
-H "X-API-Key: $KITCHENIO_API_KEY" \
-d '{"item":"Milk","amount":"2"}'- Missing or invalid fields return
422 Unprocessable Entitywith validation details. - Unknown stock items return
404withStock item not found. - Unknown shopping list items return
404withShopping list item not found.
KitchenIO is intentionally focused. Please do not add Grocy-like extras unless explicitly requested later, such as:
- recipes
- chores
- batteries
- calendars
- meal planning
- barcode scanning
