{"id":47091920,"url":"https://github.com/GeiserX/PiSpot-Watch","last_synced_at":"2026-03-26T15:00:59.850Z","repository":{"id":292583831,"uuid":"981326461","full_name":"GeiserX/PiSpot-Watch","owner":"GeiserX","description":"Software for running a PiSpot Watch (composed of a Raspberry Pi Zero and a PaPiRus Zero) for the company GPConnect","archived":false,"fork":false,"pushed_at":"2026-03-10T13:22:17.000Z","size":25471,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-10T15:56:55.654Z","etag":null,"topics":["captive-portal","e-ink","e-paper","embedded","epd","hardware","hospitality","hotel","hotspot","iot","linux","papirus","pispot","portable","python","raspberry-pi-zero","smartwatch","voucher","wearable","wifi"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GeiserX.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"geiserx","patreon":"geiser","buy_me_a_coffee":"geiser","thanks_dev":"u/gh/geiserx"}},"created_at":"2025-05-10T21:11:18.000Z","updated_at":"2026-03-10T13:22:21.000Z","dependencies_parsed_at":null,"dependency_job_id":"3b2f0aa6-b3e2-4f7e-a993-b49cd3f5b702","html_url":"https://github.com/GeiserX/PiSpot-Watch","commit_stats":null,"previous_names":["geiserx/pispot-watch"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/GeiserX/PiSpot-Watch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GeiserX%2FPiSpot-Watch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GeiserX%2FPiSpot-Watch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GeiserX%2FPiSpot-Watch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GeiserX%2FPiSpot-Watch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GeiserX","download_url":"https://codeload.github.com/GeiserX/PiSpot-Watch/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GeiserX%2FPiSpot-Watch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30972995,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-26T14:13:19.610Z","status":"ssl_error","status_checked_at":"2026-03-26T14:12:43.279Z","response_time":114,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["captive-portal","e-ink","e-paper","embedded","epd","hardware","hospitality","hotel","hotspot","iot","linux","papirus","pispot","portable","python","raspberry-pi-zero","smartwatch","voucher","wearable","wifi"],"created_at":"2026-03-12T11:00:28.636Z","updated_at":"2026-03-26T15:00:59.844Z","avatar_url":"https://github.com/GeiserX.png","language":"Python","readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"docs/images/banner.svg\" alt=\"PiSpot Watch banner\" width=\"900\"/\u003e\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003cimg src=\"https://github.com/GeiserX/PiSpot-Watch/blob/main/extra/logo.jpg?raw=true\" width=\"128\" height=\"128\" alt=\"PiSpot Watch logo\"/\u003e\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003ePiSpot Watch\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eA wrist-wearable Raspberry Pi Zero smartwatch with an e-ink display that generates Wi-Fi voucher codes on demand.\u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/GeiserX/PiSpot-Watch/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/GeiserX/PiSpot-Watch\" alt=\"License: GPL-3.0\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.raspberrypi.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/platform-Raspberry%20Pi%20Zero-C51A4A?logo=raspberrypi\u0026logoColor=white\" alt=\"Raspberry Pi Zero\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.python.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/python-3.x-3776AB?logo=python\u0026logoColor=white\" alt=\"Python 3\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.ansible.com/\"\u003e\u003cimg src=\"https://img.shields.io/badge/deploy-Ansible-EE0000?logo=ansible\u0026logoColor=white\" alt=\"Ansible\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.vaultproject.io/\"\u003e\u003cimg src=\"https://img.shields.io/badge/secrets-Vault-FFEC6E?logo=vault\u0026logoColor=black\" alt=\"HashiCorp Vault\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Overview\n\nPiSpot Watch is a custom-built wearable IoT device designed for hotel and venue staff. Press a button on your wrist and instantly generate a time-limited Wi-Fi voucher code, displayed on a low-power e-ink screen. The device communicates with the [Spotipo](https://www.spotipo.com/) captive-portal API and retrieves per-device configuration securely from [HashiCorp Vault](https://www.vaultproject.io/).\n\nOriginally developed and deployed in 2018 for the company GPConnect, this project has been released as open-source. It is part of the **PiSpot ecosystem**:\n\n| Project | Description |\n|---|---|\n| **PiSpot Watch** (this repo) | Wrist-wearable e-ink voucher device |\n| [PiSpot Show](https://github.com/GeiserX/PiSpot-Show) | HDMI kiosk display for lobby TVs |\n| [PiSpot Deployment](https://github.com/GeiserX/PiSpot-Deployment) | Fleet provisioning and Vault configuration |\n\n---\n\n## Features\n\n- **Wearable form factor** -- fully 3D-printed wristwatch case housing a Raspberry Pi Zero, PaPiRus e-ink HAT, and battery.\n- **One-press voucher generation** -- each of the 4 configurable buttons creates a voucher with different duration presets via the Spotipo API.\n- **E-ink display** -- 2.0\" PaPiRus Zero electronic paper screen; always readable in sunlight, ultra-low power draw, holds image with no power.\n- **HashiCorp Vault integration** -- per-device secrets (API keys, button mappings, speed limits) stored centrally and fetched at runtime via AppRole authentication.\n- **Low-battery shutdown** -- JuiceBox battery monitor triggers graceful poweroff before the battery dies.\n- **Centralized logging** -- FluentBit ships rotating logs to a remote aggregator.\n- **Ansible deployment** -- single playbook provisions the entire device: drivers, services, Vault credentials, WiFi, power management.\n- **Power-optimized** -- GPU memory reduced to 16 MB, HDMI disabled, ACT LED off, event-driven GPIO (no polling).\n\n---\n\n## Videos\n\n### In Action\n\n[![PiSpot Watch In Action](http://img.youtube.com/vi/wfsdekCCtP0/0.jpg)](http://www.youtube.com/watch?v=wfsdekCCtP0 \"PiSpot Watch In Action\")\n\n[![PiSpot Watch In Action 2](http://img.youtube.com/vi/Iu0-R4fZw-Q/0.jpg)](http://www.youtube.com/watch?v=Iu0-R4fZw-Q \"PiSpot Watch In Action 2\")\n\n### Assembly\n\n[![PiSpot Watch Assembly](http://img.youtube.com/vi/riw7c_wJmEY/0.jpg)](http://www.youtube.com/watch?v=riw7c_wJmEY \"PiSpot Watch Assembly\")\n\n### Disassembly\n\n[![PiSpot Watch Disassembly](http://img.youtube.com/vi/lueef-ptVpc/0.jpg)](http://www.youtube.com/watch?v=lueef-ptVpc \"PiSpot Watch Disassembly\")\n\n---\n\n## Photos\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/photos/watch-worn-on-wrist.jpg\" width=\"300\" alt=\"PiSpot Watch worn on wrist\"/\u003e\n  \u003cimg src=\"docs/photos/watch-in-action-voucher-code.jpg\" width=\"300\" alt=\"PiSpot Watch showing voucher code\"/\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/photos/watch-in-black-case-assembly.jpg\" width=\"300\" alt=\"Watch in black 3D-printed case\"/\u003e\n  \u003cimg src=\"docs/photos/device-in-white-case-base.jpg\" width=\"300\" alt=\"Device in white case base\"/\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/photos/eink-display-gpconnect-logo-closeup.jpg\" width=\"300\" alt=\"E-ink display with GPConnect logo\"/\u003e\n  \u003cimg src=\"docs/photos/weight-measurement-60g.jpg\" width=\"300\" alt=\"Device weighing 60g on scale\"/\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/photos/three-watch-cases-batch.jpg\" width=\"300\" alt=\"Batch of three watch cases\"/\u003e\n  \u003cimg src=\"docs/photos/development-desk-overview.jpg\" width=\"300\" alt=\"Development desk with PiSpot devices\"/\u003e\n\u003c/p\u003e\n\nSee all photos in [`docs/photos/`](docs/photos/).\n\n---\n\n## Hardware\n\n| Component | Purpose |\n|---|---|\n| Raspberry Pi Zero | ARM processor, Wi-Fi connectivity |\n| PaPiRus Zero (2.0\") | E-ink display via SPI/I2C (V231_G2 panel) |\n| JuiceBox battery | Wearable power supply with low-voltage shutdown |\n| 5 tactile buttons | GPIO inputs: reboot (SW1) + 4 voucher presets (SW3-SW5 + optional SW2) |\n| 3D-printed case | Custom enclosure with button cutouts and strap mounts |\n\n---\n\n## How It Works\n\n```\n  Button press (GPIO interrupt)\n         |\n         v\n  +------+------+\n  | HashiCorp   |   Fetches device config:\n  | Vault       |   API key, button mappings,\n  | (AppRole)   |   speed limits, duration presets\n  +------+------+\n         |\n         v\n  +------+------+\n  | Spotipo     |   POST /api/voucher/create/\n  | WiFi API    |   --\u003e returns voucher code\n  +------+------+\n         |\n         v\n  +------+------+\n  | PaPiRus     |   Renders duration + code\n  | e-ink       |   on 2.0\" e-paper display\n  | display     |   (holds for 30 seconds)\n  +-------------+\n```\n\n1. The device idles in a low-power loop, waiting for a GPIO falling-edge interrupt.\n2. On button press, it connects to Vault and retrieves the device's secret configuration by hostname.\n3. It POSTs to the Spotipo API with the voucher parameters mapped to the pressed button.\n4. The voucher code and duration are rendered on the e-ink display for 30 seconds, then the screen returns to the logo.\n\n---\n\n## 3D-Printable Case\n\nThe `Case/` directory contains everything needed to print the wristwatch enclosure:\n\n| File | Description |\n|---|---|\n| `Case.stl` | Main watch body |\n| `Buttons.stl` | Physical button caps (set of 5) |\n| `Cover.stl` | Protective back cover |\n| `PiSpot_Voucher.fcstd` | FreeCAD parametric source (editable) |\n| `*.gx` | Goxel voxel models (case, buttons, cover, assembly views) |\n\nMultiple case variants are included (`3 Cases.gx`, `7 Buttons.gx`) for different button configurations.\n\n---\n\n## Getting Started\n\n### 1. Clone\n\n```bash\ngit clone https://github.com/GeiserX/PiSpot-Watch.git\n```\n\n### 2. Set up Vault secrets\n\nUse [PiSpot Deployment](https://github.com/GeiserX/PiSpot-Deployment) to configure per-device secrets in Vault, or manually create a KV secret at `pispot_voucher/\u003chostname\u003e` containing the Spotipo API key, button duration mappings, speed limits, and site number.\n\n### 3. Deploy with Ansible\n\n```bash\nansible-playbook -i inventory deployment-files/main.yml\n```\n\nThe playbook handles:\n- PaPiRus driver compilation (gratis V231_G2) and SPI/I2C enablement\n- Python dependencies (`RPi.GPIO`, `requests`, `hvac`)\n- `pispot.service` systemd unit (auto-starts after `epd-fuse`)\n- JuiceBox low-battery shutdown service\n- FluentBit log aggregation\n- Vault AppRole token bootstrap and weekly renewal cron job\n- WiFi configuration and power optimizations\n\n---\n\n## Project Structure\n\n```\nPiSpot-Watch/\n  main.py                          # Main application (GPIO events + API calls + display)\n  deployment-files/\n    main.yml                       # Ansible provisioning playbook (288 lines)\n    get-approle-token.py           # Unwraps Vault AppRole wrapped secret ID\n    vault-renew-token-pi.py        # Weekly Vault token renewal (cron)\n    pispot.service                 # Main app systemd unit\n    low-battery-shutdown.service   # JuiceBox battery monitor unit\n    papirus-clear.service          # Clears e-ink on shutdown\n    td-agent-bit.conf              # FluentBit log shipping config\n    wpa_supplicant.conf            # WiFi network config\n  Case/                            # 3D enclosure (FreeCAD + Goxel + STL)\n  docs/images/                     # Documentation assets\n  extra/                           # Logo\n  LICENSE                          # GPL-3.0\n```\n\n---\n\n## License\n\n[GNU General Public License v3.0](LICENSE)\n\n## Maintainers\n\n[@GeiserX](https://github.com/GeiserX)\n\n## Contributing\n\nContributions are welcome. [Open an issue](https://github.com/GeiserX/PiSpot-Watch/issues/new) or submit a pull request.\n\nThis project follows the [Contributor Covenant v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) Code of Conduct.\n","funding_links":["https://github.com/sponsors/geiserx","https://patreon.com/geiser","https://buymeacoffee.com/geiser","https://thanks.dev/u/gh/geiserx"],"categories":["wearable","Projects","Hardware","Table of Contents"],"sub_categories":["dev","Android Things","Hardware"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGeiserX%2FPiSpot-Watch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGeiserX%2FPiSpot-Watch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGeiserX%2FPiSpot-Watch/lists"}