Compare commits

...

No commits in common. "main" and "97a787efab0258e040cb45c7656ad4d67a7159e4" have entirely different histories.

26 changed files with 288 additions and 163 deletions

53
.gitignore vendored
View File

@ -1,53 +0,0 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual environments
.venv/
venv/
ENV/
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Testing
.pytest_cache/
.coverage
htmlcov/
.tox/
.nox/
# mypy
.mypy_cache/
# Local config
.env
*.local.yaml
# Documentation symlink (points to project-docs)
docs

16
404.html Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,48 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**Home Hub** - Entertainment system with Home Assistant, smart speakers, and voice control
## Development Commands
```bash
# Install for development
pip install -e ".[dev]"
# Run tests
pytest
# Run a single test
pytest tests/test_file.py::test_name
```
## Architecture
*TODO: Describe the project architecture*
### Key Modules
*TODO: List key modules and their purposes*
### Key Paths
- **Source code**: `src/home-hub/`
- **Tests**: `tests/`
- **Documentation**: `docs/` (symlink to project-docs)
## Documentation
Documentation lives in `docs/` (symlink to centralized docs system).
**Before updating docs, read `docs/updating-documentation.md`** for full details on visibility rules and procedures.
Quick reference:
- Edit files in `docs/` folder
- Use `public: true` frontmatter for public-facing docs
- Use `<!-- PRIVATE_START -->` / `<!-- PRIVATE_END -->` to hide sections
- Deploy: `~/PycharmProjects/project-docs/scripts/build-public-docs.sh home-hub --deploy`
Do NOT create documentation files directly in this repository.

View File

@ -1,39 +0,0 @@
# Home Hub
Entertainment system with Home Assistant, smart speakers, and voice control
## Installation
```bash
pip install -e .
```
## Usage
*TODO: Add usage instructions*
## Documentation
Full documentation is available at: https://pages.brrd.tech//home-hub/
## Development
```bash
# Clone the repository
git clone //home-hub.git
cd home-hub
# Create virtual environment
python -m venv .venv
source .venv/bin/activate
# Install for development
pip install -e ".[dev]"
# Run tests
pytest
```
## License
*TODO: Add license*

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
"use strict";(globalThis.webpackChunkproject_public_docs=globalThis.webpackChunkproject_public_docs||[]).push([[237],{2237(e,t,i){i.r(t),i.d(t,{default:()=>l});i(6540);var o=i(1312),n=i(5500),s=i(1656),r=i(3363),a=i(4848);function l(){const e=(0,o.T)({id:"theme.NotFound.title",message:"Page Not Found"});return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.be,{title:e}),(0,a.jsx)(s.A,{children:(0,a.jsx)(r.A,{})})]})}},3363(e,t,i){i.d(t,{A:()=>a});i(6540);var o=i(4164),n=i(1312),s=i(1107),r=i(4848);function a({className:e}){return(0,r.jsx)("main",{className:(0,o.A)("container margin-vert--xl",e),children:(0,r.jsx)("div",{className:"row",children:(0,r.jsxs)("div",{className:"col col--6 col--offset-3",children:[(0,r.jsx)(s.A,{as:"h1",className:"hero__title",children:(0,r.jsx)(n.A,{id:"theme.NotFound.title",description:"The title of the 404 page",children:"Page Not Found"})}),(0,r.jsx)("p",{children:(0,r.jsx)(n.A,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page",children:"We could not find what you were looking for."})}),(0,r.jsx)("p",{children:(0,r.jsx)(n.A,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page",children:"Please contact the owner of the site that linked you to the original URL and let them know their link is broken."})})]})})})}}}]);

View File

@ -0,0 +1 @@
"use strict";(globalThis.webpackChunkproject_public_docs=globalThis.webpackChunkproject_public_docs||[]).push([[256],{7294(e){e.exports=JSON.parse('{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"docs":[{"type":"link","href":"/rob/home-hub/","label":"Home Hub","docId":"overview","unlisted":false},{"type":"link","href":"/rob/home-hub/todos","label":"Home Hub TODOs","docId":"todos","unlisted":false},{"type":"link","href":"/rob/home-hub/goals","label":"Goals","docId":"goals","unlisted":false},{"type":"link","href":"/rob/home-hub/milestones","label":"Milestones","docId":"milestones","unlisted":false}]},"docs":{"goals":{"id":"goals","title":"Goals","description":"Vision","sidebar":"docs"},"milestones":{"id":"milestones","title":"Milestones","description":"Active","sidebar":"docs"},"overview":{"id":"overview","title":"Home Hub","description":"Entertainment system with Home Assistant, smart speakers, and voice control","sidebar":"docs"},"todos":{"id":"todos","title":"Home Hub TODOs","description":"Active Tasks","sidebar":"docs"}}}}')}}]);

View File

@ -0,0 +1 @@
"use strict";(globalThis.webpackChunkproject_public_docs=globalThis.webpackChunkproject_public_docs||[]).push([[647],{7121(e,c,s){s.r(c),s.d(c,{default:()=>t});s(6540);var r=s(4164),u=s(7559),a=s(5500),l=s(2831),o=s(1656),p=s(4848);function t(e){return(0,p.jsx)(a.e3,{className:(0,r.A)(u.G.wrapper.docsPages),children:(0,p.jsx)(o.A,{children:(0,l.v)(e.route.routes)})})}}}]);

View File

@ -0,0 +1 @@
"use strict";(globalThis.webpackChunkproject_public_docs=globalThis.webpackChunkproject_public_docs||[]).push([[894],{7836(e,s,t){t.r(s),t.d(s,{assets:()=>r,contentTitle:()=>l,default:()=>u,frontMatter:()=>a,metadata:()=>i,toc:()=>c});const i=JSON.parse('{"id":"goals","title":"Goals","description":"Vision","source":"@site/docs/goals.md","sourceDirName":".","slug":"/goals","permalink":"/rob/home-hub/goals","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"type":"goals","project":"home-hub","updated":"2026-02-02T00:00:00.000Z"},"sidebar":"docs","previous":{"title":"Home Hub TODOs","permalink":"/rob/home-hub/todos"},"next":{"title":"Milestones","permalink":"/rob/home-hub/milestones"}}');var n=t(4848),o=t(8453);const a={type:"goals",project:"home-hub",updated:new Date("2026-02-02T00:00:00.000Z")},l="Goals",r={},c=[{value:"Vision",id:"vision",level:2},{value:"Active",id:"active",level:2},{value:"Future",id:"future",level:2},{value:"Non-Goals",id:"non-goals",level:2}];function d(e){const s={h1:"h1",h2:"h2",header:"header",input:"input",li:"li",p:"p",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(s.header,{children:(0,n.jsx)(s.h1,{id:"goals",children:"Goals"})}),"\n",(0,n.jsx)(s.h2,{id:"vision",children:"Vision"}),"\n",(0,n.jsx)(s.p,{children:"Home Hub transforms a Dell Inspiron 14 5420 laptop into a living room entertainment center and smart home controller. It combines media playback (YouTube, Netflix, web browsing) with Home Assistant orchestration and a custom ESP32 WiFi smart speaker for voice control and multi-room audio routing."}),"\n",(0,n.jsx)(s.h2,{id:"active",children:"Active"}),"\n",(0,n.jsxs)(s.ul,{className:"contains-task-list",children:["\n",(0,n.jsxs)(s.li,{className:"task-list-item",children:[(0,n.jsx)(s.input,{type:"checkbox",disabled:!0})," ","Reliable audio playback for media and entertainment use #high"]}),"\n",(0,n.jsxs)(s.li,{className:"task-list-item",children:[(0,n.jsx)(s.input,{type:"checkbox",disabled:!0})," ","Home Assistant running stably alongside desktop applications #high"]}),"\n",(0,n.jsxs)(s.li,{className:"task-list-item",children:[(0,n.jsx)(s.input,{type:"checkbox",disabled:!0})," ","Voice-controlled audio routing between laptop speakers and ESP32 smart speaker #high"]}),"\n",(0,n.jsxs)(s.li,{className:"task-list-item",children:[(0,n.jsx)(s.input,{type:"checkbox",disabled:!0})," ","ESP32 smart speaker with wake word detection and voice assistant #medium"]}),"\n",(0,n.jsxs)(s.li,{className:"task-list-item",children:[(0,n.jsx)(s.input,{type:"checkbox",disabled:!0})," ","Seamless media control via voice commands #medium"]}),"\n"]}),"\n",(0,n.jsx)(s.h2,{id:"future",children:"Future"}),"\n",(0,n.jsxs)(s.ul,{className:"contains-task-list",children:["\n",(0,n.jsxs)(s.li,{className:"task-list-item",children:[(0,n.jsx)(s.input,{type:"checkbox",disabled:!0})," ","Multi-room audio with additional ESP32 speakers #low"]}),"\n",(0,n.jsxs)(s.li,{className:"task-list-item",children:[(0,n.jsx)(s.input,{type:"checkbox",disabled:!0})," ","Whole-home automation routines via voice #low"]}),"\n",(0,n.jsxs)(s.li,{className:"task-list-item",children:[(0,n.jsx)(s.input,{type:"checkbox",disabled:!0})," ","Remote access and monitoring of Home Assistant #low"]}),"\n"]}),"\n",(0,n.jsx)(s.h2,{id:"non-goals",children:"Non-Goals"}),"\n",(0,n.jsxs)(s.ul,{children:["\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.strong,{children:"Replacing dedicated smart speakers"})," - The ESP32 speaker is DIY and experimental, not a polished consumer product"]}),"\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.strong,{children:"Server workloads"})," - This is an entertainment/control hub, not a compute server"]}),"\n",(0,n.jsxs)(s.li,{children:[(0,n.jsx)(s.strong,{children:"Always-on server workloads"})," - No 24/7 services beyond Home Assistant (this is a living room device, not a rack server)"]}),"\n"]})]})}function u(e={}){const{wrapper:s}={...(0,o.R)(),...e.components};return s?(0,n.jsx)(s,{...e,children:(0,n.jsx)(d,{...e})}):d(e)}},8453(e,s,t){t.d(s,{R:()=>a,x:()=>l});var i=t(6540);const n={},o=i.createContext(n);function a(e){const s=i.useContext(o);return i.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:a(e.components),i.createElement(o.Provider,{value:s},e.children)}}}]);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
"use strict";(globalThis.webpackChunkproject_public_docs=globalThis.webpackChunkproject_public_docs||[]).push([[98],{1723(n,e,s){s.r(e),s.d(e,{default:()=>d});s(6540);var r=s(5500);function o(n,e){return`docs-${n}-${e}`}var c=s(3025),t=s(2831),i=s(1463),u=s(4848);function l(n){const{version:e}=n;return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(i.A,{version:e.version,tag:o(e.pluginId,e.version)}),(0,u.jsx)(r.be,{children:e.noIndex&&(0,u.jsx)("meta",{name:"robots",content:"noindex, nofollow"})})]})}function a(n){const{version:e,route:s}=n;return(0,u.jsx)(r.e3,{className:e.className,children:(0,u.jsx)(c.n,{version:e,children:(0,t.v)(s.routes)})})}function d(n){return(0,u.jsxs)(u.Fragment,{children:[(0,u.jsx)(l,{...n}),(0,u.jsx)(a,{...n})]})}}}]);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
"use strict";(globalThis.webpackChunkproject_public_docs=globalThis.webpackChunkproject_public_docs||[]).push([[742],{7093(c){c.exports=JSON.parse('{"name":"docusaurus-plugin-content-docs","id":"default"}')}}]);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,61 @@
/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress
* @license MIT */
/**
* @license React
* react-dom-client.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-dom.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react-jsx-runtime.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.js
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.13.1
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

View File

@ -0,0 +1 @@
(()=>{"use strict";var e,r,t,a,o,n={},i={};function c(e){var r=i[e];if(void 0!==r)return r.exports;var t=i[e]={id:e,loaded:!1,exports:{}};return n[e].call(t.exports,t,t.exports,c),t.loaded=!0,t.exports}c.m=n,c.c=i,e=[],c.O=(r,t,a,o)=>{if(!t){var n=1/0;for(l=0;l<e.length;l++){for(var[t,a,o]=e[l],i=!0,d=0;d<t.length;d++)(!1&o||n>=o)&&Object.keys(c.O).every(e=>c.O[e](t[d]))?t.splice(d--,1):(i=!1,o<n&&(n=o));if(i){e.splice(l--,1);var f=a();void 0!==f&&(r=f)}}return r}o=o||0;for(var l=e.length;l>0&&e[l-1][2]>o;l--)e[l]=e[l-1];e[l]=[t,a,o]},c.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return c.d(r,{a:r}),r},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,c.t=function(e,a){if(1&a&&(e=this(e)),8&a)return e;if("object"==typeof e&&e){if(4&a&&e.__esModule)return e;if(16&a&&"function"==typeof e.then)return e}var o=Object.create(null);c.r(o);var n={};r=r||[null,t({}),t([]),t(t)];for(var i=2&a&&e;("object"==typeof i||"function"==typeof i)&&!~r.indexOf(i);i=t(i))Object.getOwnPropertyNames(i).forEach(r=>n[r]=()=>e[r]);return n.default=()=>e,c.d(o,n),o},c.d=(e,r)=>{for(var t in r)c.o(r,t)&&!c.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},c.f={},c.e=e=>Promise.all(Object.keys(c.f).reduce((r,t)=>(c.f[t](e,r),r),[])),c.u=e=>"assets/js/"+({48:"a94703ab",98:"a7bd4aaa",256:"41905f00",393:"1db78e9f",401:"17896441",413:"1db64337",574:"817f7194",647:"5e95c892",742:"aba21aa0",894:"5eebbccf"}[e]||e)+"."+{48:"b8c77466",98:"3ba34601",237:"447ba118",256:"1c336e56",393:"3a321e78",401:"a2525508",413:"f698cce6",574:"b988fcde",647:"a3b66919",742:"4a552a5c",894:"88d75018"}[e]+".js",c.miniCssF=e=>{},c.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),a={},o="project-public-docs:",c.l=(e,r,t,n)=>{if(a[e])a[e].push(r);else{var i,d;if(void 0!==t)for(var f=document.getElementsByTagName("script"),l=0;l<f.length;l++){var u=f[l];if(u.getAttribute("src")==e||u.getAttribute("data-webpack")==o+t){i=u;break}}i||(d=!0,(i=document.createElement("script")).charset="utf-8",c.nc&&i.setAttribute("nonce",c.nc),i.setAttribute("data-webpack",o+t),i.src=e),a[e]=[r];var b=(r,t)=>{i.onerror=i.onload=null,clearTimeout(s);var o=a[e];if(delete a[e],i.parentNode&&i.parentNode.removeChild(i),o&&o.forEach(e=>e(t)),r)return r(t)},s=setTimeout(b.bind(null,void 0,{type:"timeout",target:i}),12e4);i.onerror=b.bind(null,i.onerror),i.onload=b.bind(null,i.onload),d&&document.head.appendChild(i)}},c.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.p="/rob/home-hub/",c.gca=function(e){return e={17896441:"401",a94703ab:"48",a7bd4aaa:"98","41905f00":"256","1db78e9f":"393","1db64337":"413","817f7194":"574","5e95c892":"647",aba21aa0:"742","5eebbccf":"894"}[e]||e,c.p+c.u(e)},(()=>{var e={354:0,869:0};c.f.j=(r,t)=>{var a=c.o(e,r)?e[r]:void 0;if(0!==a)if(a)t.push(a[2]);else if(/^(354|869)$/.test(r))e[r]=0;else{var o=new Promise((t,o)=>a=e[r]=[t,o]);t.push(a[2]=o);var n=c.p+c.u(r),i=new Error;c.l(n,t=>{if(c.o(e,r)&&(0!==(a=e[r])&&(e[r]=void 0),a)){var o=t&&("load"===t.type?"missing":t.type),n=t&&t.target&&t.target.src;i.message="Loading chunk "+r+" failed.\n("+o+": "+n+")",i.name="ChunkLoadError",i.type=o,i.request=n,a[1](i)}},"chunk-"+r,r)}},c.O.j=r=>0===e[r];var r=(r,t)=>{var a,o,[n,i,d]=t,f=0;if(n.some(r=>0!==e[r])){for(a in i)c.o(i,a)&&(c.m[a]=i[a]);if(d)var l=d(c)}for(r&&r(t);f<n.length;f++)o=n[f],c.o(e,o)&&e[o]&&e[o][0](),e[o]=0;return c.O(l)},t=globalThis.webpackChunkproject_public_docs=globalThis.webpackChunkproject_public_docs||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})()})();

38
goals/index.html Normal file

File diff suppressed because one or more lines are too long

0
img/favicon.ico Normal file
View File

55
index.html Normal file

File diff suppressed because one or more lines are too long

36
milestones/index.html Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,23 +0,0 @@
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "home-hub"
version = "0.1.0"
description = "Entertainment system with Home Assistant, smart speakers, and voice control"
readme = "README.md"
requires-python = ">=3.10"
dependencies = []
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"pytest-cov>=4.0",
]
[tool.setuptools.packages.find]
where = ["src"]
[tool.pytest.ini_options]
testpaths = ["tests"]

1
sitemap.xml Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://pages.brrd.tech/rob/home-hub/goals/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://pages.brrd.tech/rob/home-hub/milestones/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://pages.brrd.tech/rob/home-hub/todos/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url><url><loc>https://pages.brrd.tech/rob/home-hub/</loc><changefreq>weekly</changefreq><priority>0.5</priority></url></urlset>

66
todos/index.html Normal file

File diff suppressed because one or more lines are too long