A lightweight web app to track electronics parts (category, subcategory, description, package, container, quantity, notes) with a simple HTML UI โ including optional links to datasheets and pinouts.
Note
If youโre looking for a more professional-grade (and free) inventory system with broader features, you may want to check out InvenTree (it might be a better fit depending on your needs).
- Add, inline-edit, and delete parts
- Duplicate parts (pre-fills add form for quick variations)
- AI Auto-Fill (optional): automatically populate part data from a description using OpenAI + Tavily web search
- Part detail page: click any part description to view a dedicated page with all metadata, images, and datasheet in an organized layout
- Maintenance page: manage lookup values (categories, subcategories, containers, packages) with add/rename/delete operations and usage tracking
- Search + filter by category, subcategory, and container (free-text search also matches container codes like
BX-50) - Click a container chip in the table to open the container page
- CSV export
- Optional links per part: datasheet + pinout (quick-open buttons in the table)
- Optional images per part: device photo + pinout image (hover preview in the table)
- Optional stock levels (low-stock warning colors) for quantity
- Container pages and printable container labels with QR codes
- Uses a local SQLite database file (no server required)
The maintenance page (/maintenance) lets you manage the lookup values used for suggestions and dropdowns throughout the app.
- Accordion interface: Collapsible sections for Categories, Subcategories, Containers, and Packages โ remembers which section you last opened
- Usage tracking: Each section header shows the total count and how many items are unused (e.g., "9 (2 unused)")
- Visual distinction: Unused items appear with a dashed orange border so you can quickly identify candidates for cleanup
- Interactive tags: Click any existing item tag to auto-select it in the rename and delete dropdowns
- Inline feedback: Success/error messages appear within the relevant section, not at the top of the page
- Add: Create a new category, subcategory, container, or package
- Rename: Rename an existing value โ all parts using the old value are automatically updated (useful for merging duplicates)
- Delete: Remove an unused value โ blocked if any parts still reference it
The Qty chip can be color-coded using optional stock levels:
- Enter levels as
hi:lo(example:10:5)-
Green: quantity
$\ge$ hi -
Yellow:
lo$\le$ quantity$<$ hi -
Red: quantity
$<$ lo
-
Green: quantity
- Enter a single number like
5to use the same value for both (5:5). - Leave empty to disable levels (chip stays neutral).
Click any part description in the main table to open its detail page, which shows:
- All metadata (description, package, category, subcategory, container, notes, timestamps)
- Quantity controls with +/โ buttons and inline editing (same as main page)
- Device image and pinout image side-by-side (with placeholders if missing)
- Datasheet/documentation link for quick access
This provides a focused view of a single part with all its information organized in one place. Use the Back to Inventory button to return to the main table.
The detail page allows you to add or update images and datasheets for existing parts:
- Click the โ๏ธ edit icon next to "Device Image", "Pinout", or "Datasheet / Documentation"
- An edit form appears with:
- Text input: enter a filename (for local images) or URL manually
- ๐ AI Search button: search for images or datasheets using AI (requires API keys)
- For images, the AI search opens a modal with image results โ click one to download and save it locally
- For datasheets, the AI search shows a list of PDF links from electronics distributors โ click one to use that URL
- Click Save to update the part, or Cancel to discard changes
This is especially useful for parts that were added without images or datasheets โ you can easily populate them later.
Fig. 1: Main inventory screen with search, filters, and inline editing.
Fig. 2 & 3: Printable container labels โ combined QR + text.
Fig. 4: Contents of box BX-41 in Gridfinity bins with part labels.
More screenshots:
- Main screen (board popup)
- Label selection
- Printable Avery labels (with offset support for partially-used label sheets)
- Restore parts (trash)
- FastAPI (serves HTML)
- Jinja2 templates
- SQLite (stored in
inventory.db) - OpenAI API (optional, for AI Auto-Fill)
- Tavily API (optional, for web/image search)
The โจ Auto-Fill button uses AI to automatically populate part data from a description. It searches the web for datasheets and specifications, then extracts structured data.
- Enter a part description (e.g., "LM358 DIP" or "ADS1115 I2C ADC module")
- Click โจ Auto-Fill
- The system uses Tavily to search for datasheets and technical information
- OpenAI extracts and structures the data (package, category, subcategory, notes, datasheet URL)
- Form fields are populated automatically โ review and adjust as needed
The ๐ท (part image) and ๐ (pinout image) buttons let you search for images:
- Click the camera or pinout button next to the image field
- A modal displays image results from Tavily image search
- Click an image to select it โ the image is downloaded and saved locally:
- Part images are saved to
static/images/ - Pinout images are saved to
static/pinouts/ - The filename (based on the part description) is inserted into the field
- Part images are saved to
This ensures images are stored permanently and won't break if external sources change or go offline.
Note: Image search is also available on the Part Detail page for updating existing parts โ click the edit icon next to the image section and use the ๐ search button.
To enable AI features, set these environment variables:
export OPENAI_API_KEY="sk-..."
export TAVILY_API_KEY="tvly-..."Both keys are required. If either is missing, the Auto-Fill and image search buttons will be disabled.
Get API keys:
- OpenAI: https://platform.openai.com/api-keys
- Tavily: https://tavily.com/ (free tier available)
The AI features require additional Python packages (already in requirements.txt):
pip install openai tavily-pythonIf these packages are not installed, AI features will be gracefully disabled.
python3 -m venv .venv
source .venv/bin/activatepip install -r requirements.txtQR code support is included via the qrcode dependency in requirements.txt.
This app uses a login page + server-side sessions. You must configure credentials via environment variables.
Local-only (no authentication):
INVENTORY_DISABLE_AUTH=1 \
INVENTORY_BASE_URL="http://127.0.0.1:8001" \
uvicorn app:app --reload --host 127.0.0.1 --port 8001Do not use INVENTORY_DISABLE_AUTH on an internet-exposed instance.
If you run behind a reverse proxy (recommended for HTTPS), also set INVENTORY_BASE_URL so label QR codes point to the correct external hostname.
Example (behind reverse proxy + HTTPS):
export INVENTORY_BASE_URL="https://inventory.reverseproxy.com"Dev (auto-reload on code changes):
INVENTORY_USER="andreas" \
INVENTORY_PASS_HASH='$pbkdf2-sha256$...$...$...' \
INVENTORY_BASE_URL="http://127.0.0.1:8001" \
uvicorn app:app --reload --host 0.0.0.0 --port 8001 --proxy-headersProd (no reload):
INVENTORY_USER="andreas" \
INVENTORY_PASS_HASH='$pbkdf2-sha256$...$...$...' \
INVENTORY_BASE_URL="http://127.0.0.1:8001" \
uvicorn app:app --host 0.0.0.0 --port 8001 --proxy-headersOpen:
On startup, the app creates/uses a SQLite database at inventory.db (in the repo directory).
- To reset all data: stop the server and delete
inventory.db.
/โ main inventory table/parts/{uuid}โ detail page for a specific part/helpโ help page/export.csvโ download CSV export (respects current filters via query params)/containers/{code}โ show parts in a specific container/containers/labelsโ printable labels with QR codes
Each part can store optional image URLs:
image_urlโ photo of the IC/module/device
Pinouts are stored in pinout_url. If pinout_url points directly to an image, the table shows a hover preview.
Store images under the repo's static/ folder (for example static/images/ and static/pinouts/).
Then reference them with a URL starting with /static/...:
image_url:/static/images/MP2307_HW133ABC_board.jpgpinout_url:/static/pinouts/MP2307_pinout.jpg
Shortcut: when editing image_url / pinout_url, you can paste just the filename (for example MP2307_HW133ABC_board.jpg or MP2307_pinout.jpg).
The app will automatically resolve it under /static/images/ or /static/pinouts/.
Note: static/images/ and static/pinouts/ are present in the repo (via .gitkeep), but the actual image files are intentionally ignored by git.
If the URL ends with an image extension (.png, .jpg, .jpeg, .gif, .webp, .svg), the table shows a hover preview.
Clicking the preview opens the image in a new tab.
On touch devices (mobile/tablet), previews are shown by tapping the icon (instead of hovering). Use the ร button to close the preview.
Container label QR codes are generated using BASE_URL in app.py.
- Configure the external base URL via
INVENTORY_BASE_URLso QR codes point to a reachable URL (LAN IP or public HTTPS hostname).
The app supports two types of labels:
- QR only (1 label) โ container code + QR code
- Text only (1 label) โ container code + descriptive text
- QR + text (1 label) โ container code + QR code + optional text on one label
- QR + text (2 labels) โ creates two labels per container
Container label selection also supports:
- Quantity per container โ set how many copies to print for each selected container.
- Multiple containers per sheet โ when selecting multiple containers, labels fill the sheet leftโright and wrap to the next row based on the chosen preset.
Part labels are designed for small compartments inside containers, e.g, Gridfinity bins or other. Each label shows:
- Container โ which container the part is in (top, small)
- Description โ part name/description (large, prominent)
- Notes โ additional details (smaller)
- Subcategory โ part classification (bottom, italic)
To print part labels:
- Go to Labels in the navigation
- Select Part Labels (mutually exclusive with container labels)
- Filter parts by search text, category, or container
- Check the parts you want to label
- Select a preset and print
Note: Part label text sizes automatically scale down for smaller presets (like Avery 3666) via CSS custom properties.
If your Avery sheet has some labels already used, set "Start at position" to skip them. Positions are counted row-wise (left to right, top to bottom). The number of columns depends on your label preset.
Example for a 2-column layout (like Avery 3425):
โโโโโโโฌโโโโโโ
โ 1 โ 2 โ
โโโโโโโผโโโโโโค
โ 3 โ 4 โ
โโโโโโโผโโโโโโค
โ 5 โ 6 โ
โโโโโโโดโโโโโโ
For example, if positions 1โ2 are used, set start position to 3 to begin on the second row.
Label printing is implemented as:
- Shared label UI/print styles:
static/labels.css - One layout file per Avery preset:
static/avery_<preset>.css(e.g.static/avery_3425.css)
Trademark notice: Averyยฎ is a registered trademark of its respective owner (commonly Avery Products Corporation and/or affiliated entities). This project is not affiliated with, sponsored, or endorsed by Avery. The Avery name and label numbers are used only to indicate intended compatibility with commonly available label sheets.
To add a new layout:
- Create
static/avery_<new-id>.css - Add a metadata comment near the top of the file:
This enables the UI to show grid info (e.g., "2ร8 = 16 labels") and position hints.
/* Meta: columns=2, rows=8, label_size=105x57mm */ - Reload the Labels page โ the preset list is discovered automatically from
static/avery_*.css
Printers often introduce small feed/margin offsets even with โMargins: noneโ. Each preset CSS supports knobs:
--label_offset_x/--label_offset_y(shift the whole sheet; negative moves left/up)--label_gap_x/--label_gap_y(space between labels)
Example (move 5mm left and 1mm up):
--label_offset_x: -5mm;
--label_offset_y: -1mm;By default, label borders are not printed (best for real Avery sheets where tiny alignment offsets can make printed outlines look off).
For calibration or printing on plain paper (where you want cut guides), enable Print cut outlines on the Labels page before printing (always shown on screen; printed only when enabled).
This app uses a login page + server-side sessions (stored in SQLite) for all routes.
Configure it with:
INVENTORY_USERโ usernameINVENTORY_PASS_HASHโ password hash in Passlibpbkdf2_sha256formatINVENTORY_BASE_URLโ optional; external base URL used for container label QR codes
Optional (local-only):
INVENTORY_DISABLE_AUTHโ when set to1/true, disables authentication entirely. Use only for fully local deployments (e.g. bound to127.0.0.1). Do not enable this on an internet-exposed instance.
Note: the session cookie is configured as secure, so you should access the app via HTTPS (directly or via reverse proxy).
Generate a hash (example):
python3 -c "from passlib.hash import pbkdf2_sha256; print(pbkdf2_sha256.hash('change-me'))"Run with credentials (example):
export INVENTORY_USER="andreas"
export INVENTORY_PASS_HASH='<paste-hash-here>'
uvicorn app:app --host 0.0.0.0 --port 8001 --proxy-headersIf INVENTORY_USER / INVENTORY_PASS_HASH are not set, the login page will show an error because auth is not configured.
Note: the Passlib hash contains $ characters. Use single quotes around INVENTORY_PASS_HASH (or escape $) to avoid shell expansion.
- Security: even with Basic Auth, treat this as a trusted-LAN tool. Do not expose it directly to the public internet.
- If you need remote access, prefer a VPN and restrict access.
MIT โ see LICENSE.



