{"id":33605874,"url":"https://github.com/weirdtangent/pulse-os","last_synced_at":"2026-04-08T15:01:03.667Z","repository":{"id":324418887,"uuid":"1089765712","full_name":"weirdtangent/pulse-os","owner":"weirdtangent","description":"Pulse Display Assistant is a Raspberry Pi kiosk OS for Home Assistant, delivering a live overlay of timers/alarms/reminders plus news, weather, sports, and an optional OpenAI/Gemini voice assistant.","archived":false,"fork":false,"pushed_at":"2026-04-01T22:51:09.000Z","size":5670,"stargazers_count":13,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-02T05:03:26.146Z","etag":null,"topics":["automation","chromium","display","embedded-linux","home-assistant","home-automation","iot","kiosk","maker-project","mqtt-client","raspberry-pi","raspberry-pi-touchscreen","smart-home","systemd"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/weirdtangent.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","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}},"created_at":"2025-11-04T19:30:01.000Z","updated_at":"2026-04-01T22:50:01.000Z","dependencies_parsed_at":"2026-01-28T17:05:38.136Z","dependency_job_id":null,"html_url":"https://github.com/weirdtangent/pulse-os","commit_stats":null,"previous_names":["weirdtangent/pulse-os"],"tags_count":469,"template":false,"template_full_name":null,"purl":"pkg:github/weirdtangent/pulse-os","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weirdtangent%2Fpulse-os","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weirdtangent%2Fpulse-os/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weirdtangent%2Fpulse-os/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weirdtangent%2Fpulse-os/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/weirdtangent","download_url":"https://codeload.github.com/weirdtangent/pulse-os/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weirdtangent%2Fpulse-os/sbom","scorecard":{"id":1240740,"data":{"date":"2025-12-24T16:31:28Z","repo":{"name":"github.com/weirdtangent/pulse-os","commit":"569d63474bb9850a925b747433554f87167e7257"},"scorecard":{"version":"v5.3.0","commit":"c22063e786c11f9dd714d777a687ff7c4599b600"},"score":7.4,"checks":[{"name":"Maintained","score":0,"reason":"project was created within the last 90 days. Please review its contents carefully","details":["Warn: Repository was created within the last 90 days."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#maintained"}},{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: detected update tool: Dependabot: .github/dependabot.yml:1"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dependency-update-tool"}},{"name":"Code-Review","score":0,"reason":"Found 0/26 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dangerous-workflow"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#security-policy"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":9,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/build.yaml:26","Info: jobLevel 'contents' permission set to 'read': .github/workflows/build.yaml:50","Warn: jobLevel 'contents' permission set to 'write': .github/workflows/build.yaml:157","Info: topLevel 'contents' permission set to 'read': .github/workflows/build.yaml:19","Info: topLevel 'actions' permission set to 'read': .github/workflows/codeql.yaml:12","Info: topLevel 'contents' permission set to 'read': .github/workflows/codeql.yaml:13","Warn: topLevel 'security-events' permission set to 'write': .github/workflows/codeql.yaml:14","Info: topLevel 'contents' permission set to 'read': .github/workflows/fuzz.yaml:16","Info: topLevel permissions set to 'read-all': .github/workflows/scorecard.yaml:15"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yaml:168: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/build.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yaml:173: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/build.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yaml:178: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/build.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yaml:188: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/build.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yaml:31: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/build.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yaml:37: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/build.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yaml:58: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/build.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yaml:64: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/build.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yaml:103: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/build.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yaml:109: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/build.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yaml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/codeql.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yaml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/codeql.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/codeql.yaml:33: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/codeql.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/fuzz.yaml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/fuzz.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/fuzz.yaml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/fuzz.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecard.yaml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/scorecard.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/scorecard.yaml:34: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/scorecard.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecard.yaml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/scorecard.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecard.yaml:55: update your workflow using https://app.stepsecurity.io/secureworkflow/weirdtangent/pulse-os/scorecard.yaml/main?enable=pin","Warn: npmCommand not pinned by hash: .github/workflows/build.yaml:184","Warn: pipCommand not pinned by hash: .github/workflows/build.yaml:71","Warn: pipCommand not pinned by hash: .github/workflows/build.yaml:72","Warn: pipCommand not pinned by hash: .github/workflows/build.yaml:117","Warn: pipCommand not pinned by hash: .github/workflows/build.yaml:118","Warn: pipCommand not pinned by hash: .github/workflows/build.yaml:119","Warn: pipCommand not pinned by hash: .github/workflows/fuzz.yaml:34","Warn: pipCommand not pinned by hash: .github/workflows/fuzz.yaml:35","Warn: pipCommand not pinned by hash: .github/workflows/fuzz.yaml:36","Info:   0 out of  15 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   4 third-party GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned","Info:   0 out of   8 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#pinned-dependencies"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#license"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#vulnerabilities"}},{"name":"CII-Best-Practices","score":5,"reason":"badge detected: Passing","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#cii-best-practices"}},{"name":"SAST","score":10,"reason":"SAST tool is run on all commits","details":["Info: SAST configuration detected: CodeQL","Info: all commits (6) are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#sast"}},{"name":"Signed-Releases","score":8,"reason":"5 out of the last 5 releases have a total of 5 signed artifacts.","details":["Info: signed release artifact: pulse-os.tar.gz.sig: https://github.com/weirdtangent/pulse-os/releases/tag/v0.98.32","Info: signed release artifact: pulse-os.tar.gz.sig: https://github.com/weirdtangent/pulse-os/releases/tag/v0.98.31","Info: signed release artifact: pulse-os.tar.gz.sig: https://github.com/weirdtangent/pulse-os/releases/tag/v0.98.30","Info: signed release artifact: pulse-os.tar.gz.sig: https://github.com/weirdtangent/pulse-os/releases/tag/v0.98.29","Info: signed release artifact: pulse-os.tar.gz.sig: https://github.com/weirdtangent/pulse-os/releases/tag/v0.98.28","Warn: release artifact v0.98.32 does not have provenance: https://api.github.com/repos/weirdtangent/pulse-os/releases/272712454","Warn: release artifact v0.98.31 does not have provenance: https://api.github.com/repos/weirdtangent/pulse-os/releases/272711851","Warn: release artifact v0.98.30 does not have provenance: https://api.github.com/repos/weirdtangent/pulse-os/releases/272711076","Warn: release artifact v0.98.29 does not have provenance: https://api.github.com/repos/weirdtangent/pulse-os/releases/272710621","Warn: release artifact v0.98.28 does not have provenance: https://api.github.com/repos/weirdtangent/pulse-os/releases/272710456"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":8,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Warn: 'stale review dismissal' is disabled on branch 'main'","Warn: required approving review count is 1 on branch 'main'","Info: codeowner review is required on branch 'main'","Info: 'last push approval' is required to merge on branch 'main'","Info: 'up-to-date branches' is required to merge on branch 'main'","Info: status check found to merge onto on branch 'main'","Info: PRs are required in order to make changes on branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#branch-protection"}},{"name":"Fuzzing","score":10,"reason":"project is fuzzed","details":["Info: PythonAtherisFuzzer integration found: fuzz/fuzz_datetime_parsing.py:3","Info: PythonAtherisFuzzer integration found: fuzz/fuzz_overlay_header.py:3","Info: PythonAtherisFuzzer integration found: fuzz/fuzz_utils.py:3"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#fuzzing"}},{"name":"CI-Tests","score":10,"reason":"2 out of 2 merged PRs checked by a CI test -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project runs tests before pull requests are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#ci-tests"}},{"name":"Contributors","score":6,"reason":"project has 2 contributing companies or organizations -- score normalized to 6","details":["Info: found contributions from: semantic-release, ziprecruiter"],"documentation":{"short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies).","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#contributors"}}]},"last_synced_at":"2025-12-25T17:12:12.039Z","repository_id":324418887,"created_at":"2025-12-25T17:12:12.040Z","updated_at":"2025-12-25T17:12:12.040Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31560476,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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":["automation","chromium","display","embedded-linux","home-assistant","home-automation","iot","kiosk","maker-project","mqtt-client","raspberry-pi","raspberry-pi-touchscreen","smart-home","systemd"],"created_at":"2025-11-30T15:05:00.058Z","updated_at":"2026-04-08T15:01:03.660Z","avatar_url":"https://github.com/weirdtangent.png","language":"Python","funding_links":["https://buymeacoffee.com/weirdtangent"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/weirdtangent/pulse-os/main/assets/splash/graystorm-pulse_splash.png\" alt=\"Pulse OS social preview\" width=\"640\" /\u003e\n\u003c/p\u003e\n\n# Pulse Kiosk v1 — Complete Setup Guide\n\n## Raspberry Pi 5 + Pi 7\" Touch Display 2\n\nPulse Display Assistant is a Raspberry Pi kiosk OS purpose-built for Home Assistant dashboards. Each device self-provisions a hardened Chromium display with watchdogs, schedule-aware backlighting, and MQTT telemetry/control. A live overlay surfaces internal timers, alarms, reminders, now-playing info, plus on-demand news, weather, and sports snapshots—with clickable notification badges that stay synced to the backend schedule service. An optional Wyoming voice stack adds wake-word control, STT/TTS, and multi-turn conversations, while the LLM layer can hot-swap between OpenAI, Google Gemini, Anthropic Claude, Groq, Mistral AI, and OpenRouter so follow-up questions and automations route through whichever provider and model you prefer, all without leaving the Pulse display.\n\n**What Pulse can do today**\n\n- Hardened Chromium kiosk with watchdogs, self-healing restarts, and MQTT “home/update/reboot” buttons targeted at photo-frame style Lovelace dashboards.\n- Overlay timeline that keeps alarms, timers, reminders, calendar events, now-playing info, and badge-driven info cards in sync with the backend scheduler.\n- Full alarm/timer/reminder scheduler (manual UI, MQTT, or voice shortcuts) plus remote completion/delay actions from the overlay itself.\n- Local ICS/WebCal polling with RRULE expansion for recurring events, multi-`VALARM` support, “declined” attendee detection, on-screen calendar cards, and auto-suppressed pop-ups for meetings you said “No” to.\n- Optional Wyoming voice stack (wake word, Whisper STT, Piper TTS) with shortcut intents for news/weather/sports and LLM routing between 6 providers: OpenAI, Gemini, Anthropic Claude, Groq, Mistral AI, and OpenRouter.\n- MQTT telemetry, syslog streaming, and safe-reboot guardrails for remote monitoring, plus built-in OTA-style updates triggered from Home Assistant.\n- Sunrise/sunset-aware backlight control, Bluetooth autoconnect for external speakers, and one-touch audio tests to confirm volume changes.\n- Printable hardware accessories (mic stand, speaker cups, Pi 5 case) and ready-made scripts for kiosk recovery, calendar snapshots, and service restarts.\n\n### How to obtain, get help, and contribute\n- Obtain: clone from GitHub (`git clone https://github.com/weirdtangent/pulse-os.git`) and follow the setup steps below (we publish interim commits, not just tagged drops).\n- Bugs \u0026 enhancements: open [issues](https://github.com/weirdtangent/pulse-os/issues) with repro details and expected behavior.\n- Contribute: follow [`CONTRIBUTING.md`](CONTRIBUTING.md) and [`TESTING.md`](TESTING.md) for coding standards (Black 120 cols, Ruff) and required checks (ruff, black, pytest).\n- Security reports: use the process in [`SECURITY.md`](SECURITY.md); please do not open public issues for vulnerabilities.\n- CPE: not published / N/A.\n\n---\n\n## Jump to…\n- [Hardware Guide](#hardware-guide)\n- [Prerequisites](#0-prerequisites)\n- [First-boot Basics](#1-firstboot-basics)\n- [PulseOS Configuration](#pulseos-configuration)\n- [Notes \u0026 Extras](#notes--extras)\n- [Troubleshooting](#troubleshooting-checklist)\n\n### Reference Docs\n- [home-assistant-photo-frame](docs/home-assistant-photo-frame.md) — Nest-style Lovelace photo frame + custom card\n- [mqtt-and-telemetry](docs/mqtt-and-telemetry.md) — MQTT buttons (Home/Update/Reboot) \u0026 diagnostic sensors\n- [voice-assistant](docs/voice-assistant.md) — Wyoming pipelines, LLM providers, and real-time news/weather/sports intents\n- [assistant-commands](docs/assistant-commands.md) — Built-in “no LLM needed” voice shortcuts (alarms, timers, news, etc.)\n- [troubleshooting](docs/troubleshooting.md) — Pi 5 + Touch Display kiosk fixes (black strip, touch, autologin, etc.)\n- [notes-and-extras](docs/notes-and-extras.md) — Voice assistant tips, MQTT knobs, hardware accessories, boot splash notes, and other odds \u0026 ends\n- [public-photo-sources](docs/public-photo-sources.md) — Open-licensed image feeds (NASA, Smithsonian, Met, etc.) for `pulse-photo-card`\n- [config-reference](docs/config-reference.md) — Comprehensive `pulse.conf` option list with defaults and usage notes\n\n### External interfaces (APIs, inputs/outputs)\n- Configuration surface: [`docs/config-reference.md`](docs/config-reference.md) details every `pulse.conf` key and default.\n- MQTT control \u0026 telemetry topics: [`docs/mqtt-and-telemetry.md`](docs/mqtt-and-telemetry.md).\n- Voice assistant intents \u0026 CLI: [`docs/assistant-commands.md`](docs/assistant-commands.md) and [`docs/voice-assistant.md`](docs/voice-assistant.md).\n\n## Hardware Guide\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eSupported hardware, recommended parts, and printable accessories\u003c/strong\u003e\u003c/summary\u003e\n\n#### As of Nov 2025, $317 plus 3d printed parts (or buy/figure out a case) to build a single Pulse. Or choose your own components - anything that will work with Linux.\n\n### Raspberry Pi 5 — 16GB\n* Specs: \u003chttps://www.raspberrypi.com/products/raspberry-pi-5/\u003e\n* Retailer: \u003chttps://www.pishop.us/product/raspberry-pi-5-16gb/?src=raspberrypi\u003e\n* Recommended add-ons:\n  * Pi Active Cooler: \u003chttps://www.pishop.us/product/raspberry-pi-active-cooler/?searchid=0\u0026search_query=374-1\u003e\n* Price (11/2025): $131 with active cooler\n\n### Pi Touch Display 2 — 7”\n* Specs: \u003chttps://www.raspberrypi.com/products/touch-display-2/\u003e\n* Retailer: \u003chttps://www.pishop.us/product/raspberry-pi-touch-display-2/\u003e\n* Price (11/2025): $82\n\n### ReSpeaker Mic Array 3.0 (XVF3000)\n* Specs: \u003chttps://wiki.seeedstudio.com/respeaker_mic_array_v3.0/\u003e\n* Retailer: \u003chttps://www.seeedstudio.com/ReSpeaker-Mic-Array-v3-0.html\u003e\n* Price (11/2025): $64\n\n### BoomPod Zero mini speaker\n* Specs: \u003chttps://boompodsusa.com/products/boompods-zero-mini-wireless-bluetooth-5-pocket-size-speaker\u003e\n* Retailer: same\n* Price (11/2025): $40\n\n### Desktop Case + Pi 5 Stand\n* MakerWorld: \u003chttps://makerworld.com/en/models/789481-desktop-case-for-raspberry-pi-7-touch-display-2\u003e\n\n### 3d Print Models (see models/ directory)\n* ReSpeaker Mic Array Plate (which I glue to the Pi case cover)\n* ReSpeaker Mic Array Cover\n* BoomPod Zero Cup (which I glue to one of the display leg stands - or both for two pods!)\n\u003c/details\u003e\n\n---\n\n## 0) Prerequisites\n\n* Raspberry Pi 5/CM4/CM5 with **Raspberry Pi Touch Display 2** (DSI ribbon in the “closer” socket).\n* Fresh **Raspberry Pi OS Lite (64-bit)** (Trixie, at the moment) written to microSD - I'm using 128GB but it's only 20% full (so far).\n  * Setup \"pulse-\u003clocation\u003e\" hostname and \"pulse\" user when imaging OS\n  * Can also pre-setup networking, make sure SSH is on, enable auto-login\n* Network connectivity (Ethernet or Wi‑Fi).\n* SSH access (run `sudo raspi-config nonint do_ssh 0` or enable via imager advanced options).\n\n---\n\n## 1) First‑boot basics\n\n1. **Update Raspberry Pi OS and install prerequisites.**\n   ```bash\n   sudo apt update \u0026\u0026 sudo apt full-upgrade -y\n   sudo apt install -y git gh neovim\n   sudo chown pulse:pulse /opt\n   ```\n2. **Clone PulseOS under `/opt` and open the repo.**\n   ```bash\n   cd /opt\n   git clone https://github.com/weirdtangent/pulse-os.git\n   cd pulse-os\n   ```\n3. **Create `pulse.conf` from the sample and edit it for this kiosk.**\n   ```bash\n   cp pulse.conf.sample pulse.conf\n   vi pulse.conf\n   ```\n4. **Create a Pulse dashboard in Home Assistant (recommended).**\n   - From the HA sidebar, add a new dashboard named “Pulse” using the dashboard URL slug `dashboard-pulse`.\n   - For the first tab/view, set the title to “Home” with the view URL slug `home` and add the Lovelace cards you want displayed.\n   - Pulse defaults to `http://homeassistant.local:8123/dashboard-pulse/home`, so tweak the hostname if needed or point `PULSE_URL` elsewhere in the next step.\n\n5. **Run the setup script with a location slug (first boot only).**\n   ```bash\n   ./setup.sh \u003clocation-name\u003e\n   ```\n   By default `setup.sh` finishes by calling `bin/tools/restart-services.sh` so every kiosk service reloads without a reboot. Pass `--no-restart` if you’re iterating on a single unit and want to restart things manually (you can always run the helper script yourself later).\n\n   Re-run `./setup.sh` after changing `pulse.conf` or pulling new code (omit the location on repeat runs). The MQTT “Update” button performs the same update + setup flow remotely and inherits the automatic restart step.\n\n---\n\n# PulseOS Configuration\n\nEach kiosk reads `/opt/pulse-os/pulse.conf`. Copy the sample, edit the values that matter for this device, then rerun `./setup.sh`.\n\n```bash\ncp /opt/pulse-os/pulse.conf.sample /opt/pulse-os/pulse.conf\nvi /opt/pulse-os/pulse.conf\n```\n\nAll keys are optional, but filling out the relevant sections keeps boot, kiosk, MQTT, and assistant services aligned with your environment. Refer to [docs/config-reference.md](docs/config-reference.md) for every available variable, its default, and practical usage notes.\n\n\u003e 🔒 **Security tip:** `pulse.conf` often contains API keys, MQTT credentials, and private calendar URLs. `setup.sh` automatically sets the file owner to `PULSE_USER` and applies `chmod 600`, but keep the repo staged under `/opt` (not your home directory) and never commit `pulse.conf` back to git or copy it into cloud backups without similar protections.\n\n### Quick config verification\n\nAfter editing `pulse.conf`, run the connectivity check to confirm the services you referenced are reachable:\n\n```bash\nbin/tools/verify-conf.py --config /opt/pulse-os/pulse.conf\n```\n\nIt loads the config (or the path you pass with `--config`) and:\n- Connects to the MQTT broker using your credentials.\n- Sends a single RFC5424 syslog line if remote logging is enabled.\n- Calls each configured Wyoming endpoint (openWakeWord/Whisper/Piper) and, when possible, performs a short functional probe.\n- Tests `HOME_ASSISTANT_BASE_URL`/`HOME_ASSISTANT_TOKEN` by calling `/api/`.\n\nAny failure is printed with remediation text and the script exits non-zero so you can gate deployments on it if desired.\n\n---\n\n## Notes \u0026 Extras\n\nAll of the voice-assistant tips, MQTT knobs, Home Assistant snippets, printable accessories, and boot splash notes now live in [docs/notes-and-extras.md](docs/notes-and-extras.md). Looking for the built-in wake-word shortcuts? See [docs/assistant-commands.md](docs/assistant-commands.md).\n\n## Troubleshooting checklist\n\nSee the dedicated [troubleshooting guide](docs/troubleshooting.md) for the full Pi 5 + Touch Display checklist (black strip, touch calibration, autologin, etc.).\n\n## Contributors\n\n- [@weirdtangent](https://github.com/weirdtangent)\n\n\u003ca href=\"https://buymeacoffee.com/weirdtangent\"\u003eBuy Me A Coffee\u003c/a\u003e\n\n### Releases, versioning, and release notes\n- Semantic versioning (major.minor.patch); version metadata is managed by `release.config.js` and tagged in git.\n- Interim commits are pushed to `main`; formal releases are tagged and summarized in [`CHANGELOG.md`](CHANGELOG.md).\n- Release artifacts and source are delivered over HTTPS/SSH; verify tags and checksums where applicable.\n\n### Quality gates and analysis\n- CI runs on every PR and main push: `ruff check`, `black --check`, `pytest`, dependency auditing, and CodeQL analysis.\n- Static analysis (Ruff, CodeQL) is required before releases; medium+ findings must be fixed before publishing.\n- Dynamic checks: pytest suite (with assertions enabled) runs per PR and release.\n\n### Build \u0026 Quality Status\n\n![Lint](https://img.shields.io/github/actions/workflow/status/weirdtangent/pulse-os/build.yaml?branch=main\u0026label=lint\u0026logo=python)\n![Tests](https://img.shields.io/github/actions/workflow/status/weirdtangent/pulse-os/build.yaml?branch=main\u0026label=tests\u0026logo=pytest)\n![CI](https://img.shields.io/github/actions/workflow/status/weirdtangent/pulse-os/build.yaml?branch=main\u0026label=ci\u0026logo=githubactions)\n![Release](https://img.shields.io/github/v/release/weirdtangent/pulse-os?sort=semver)\n![Python](https://img.shields.io/badge/python-3.13%20%7C%203.14-blue?logo=python\u0026logoColor=white)\n![License](https://img.shields.io/github/license/weirdtangent/pulse-os)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n\n### Security Status\n\n[![CodeQL](https://img.shields.io/github/actions/workflow/status/weirdtangent/pulse-os/codeql.yaml?branch=main\u0026label=CodeQL\u0026logo=github)](https://github.com/weirdtangent/pulse-os/security/code-scanning)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/weirdtangent/pulse-os/badge)](https://scorecard.dev/viewer/?uri=github.com/weirdtangent/pulse-os)\n[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/11650/badge)](https://www.bestpractices.dev/projects/11650)\n![Dependabot](https://img.shields.io/badge/Dependabot-enabled-brightgreen?logo=dependabot)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fweirdtangent%2Fpulse-os","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fweirdtangent%2Fpulse-os","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fweirdtangent%2Fpulse-os/lists"}