- TypeScript 63.1%
- Vue 32.5%
- CSS 4.2%
- JavaScript 0.1%
| .devcontainer | ||
| .vscode | ||
| public | ||
| src | ||
| .editorconfig | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc.json | ||
| AGENTS.md | ||
| AGENTS_STYLE.md | ||
| Brainstorming.config.yaml | ||
| Brainstorming.md | ||
| env.d.ts | ||
| eslint.config.js | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.app.json | ||
| tsconfig.json | ||
| tsconfig.node.json | ||
| tsconfig.vitest.json | ||
| vite.config.ts | ||
| vitest.config.ts | ||
Home Automation Dashboard
A Vue 3 + TypeScript application for smart-home control, telemetry, and visualization.
This README is written for developers who need to understand the current architecture quickly and continue development safely.
Table of Contents
- 1. Architecture at a Glance
- 2. Repository Structure
- 3. Runtime Flow
- 4. Routing and Feature Modules
- 5. Configuration Model
- 6. Authorization (OIDC)
- 7. MQTT Communication Model
- 8. Service Layer Reference
- 9. Store Layer Reference
- 10. UI Layer and Component Patterns
- 11. Utilities
- 12. Development Workflow
- 13. Testing and Quality Status
- 14. Known Gaps and Extension Points
1. Architecture at a Glance
The app follows a layered architecture:
- UI/Feature Layer (
src/*/views,src/app/components) renders screens and dispatches user intents. - Store Layer (
src/app/stores) holds reactive state and adapts service events to UI-friendly data. - Service Layer (
src/app/services) implements integration logic (OIDC auth, config loading, MQTT, storage, telemetry). - Infrastructure Utilities (
src/app/utilities) provide generic helpers (promises, result wrappers, object merge, topic handling).
Primary runtime context:
- Vue app (
createApp) - Pinia store container
- Vue Router
- Services container (provided/injected)
2. Repository Structure
src/
main.ts # App entrypoint and bootstrap execution
App.vue # Root shell (RouterView + Screensaver)
routes.ts # Route composition
auth/ # OIDC callback routes/views
home/ # Home module
rooms/ # Main smart-home module (areas, rooms, groups)
map/ # Map module (MapLibre marker/track visualization)
settings/ # Settings module
testarea/ # Manual integration playground
floorplan/ # Floorplan module (large inline SVG)
app/
bootstrap/ # App bootstrap orchestration
services/ # Service layer + service bootstrap + MQTT client
stores/ # Pinia stores + store bootstrap
components/ # Reusable UI controls/indicators/elements
composables/ # Shared composition logic (e.g. TOC navigation)
types/ # Domain config types
assets/css/ # Layered global styling and theme variables
utilities/ # Current utility helpers
utilities.old/ # Legacy utility helpers still used by indicators
public/
config/ # Static config examples / fixtures
map.json # Map style
img/ # UI assets
Additional folders:
trash/: archive/legacy snapshots, not active runtime code.
3. Runtime Flow
3.1 Startup and Bootstrap
Main entrypoint: src/main.ts.
Sequence:
- Create Vue app (
createApp(App)). - Install Pinia.
- Build router from merged route table.
- Execute
bootstrap(config, { app, pinia, router }). - Install router.
- Log successful bootstrap.
- Mount to
#App.
Core bootstrap files:
src/app/bootstrap/bootstrap.tssrc/app/services/services.bootstrap.tssrc/app/stores/stores.bootstrap.ts
3.2 Dependency Injection
Service DI is intentionally centralized:
- Services are created in
bootstrapServices(...). - Services are provided globally via
app.provide(SERVICES, services). - Pinia gets
$servicesvia plugin:pinia.use(() => ({ $services: services })).
Usage patterns:
- Stores call
useXStore().$services. - Views/components can inject the container via
inject(SERVICES)(used in auth callback views).
This keeps feature modules independent from service constructors.
3.3 Cross-Cutting Watchers
bootstrap(...) creates an effectScope with 3 important watchers:
- Entities connection watcher
RebuildsGatewayServiceMQTT connection when app/site MQTT config changes. - Device service watcher
Starts/stopsDeviceServicetelemetry and MQTT depending on runtime config. - Role-to-config watcher
Loads config as soon as auth status is ready; role fallback chain:- unauthenticated ->
guest - authenticated without roles ->
default - authenticated with roles -> user roles
- unauthenticated ->
bootstrap(...) returns stopWatchers() for cleanup.
4. Routing and Feature Modules
Route composition happens in src/routes.ts by combining each feature module route array.
Defined routes:
/->home/rooms/,/rooms/:areaId/,/rooms/:areaId/:roomId/,/rooms/:areaId/:roomId/:groupId//map//settings//testarea//floorplan//auth/callbacks/login(public)/auth/callbacks/logout(public)- catch-all -> redirect to
/
Notes:
settingsroute hasmeta.requiresAuth, but a global auth guard is currently commented out inmain.ts.
4.1 Feature Module Responsibilities
home/
MainHome.vue: auth-aware switch between guest and authenticated home view.HomeGuest.vue/HomeAuthenticated.vue: user/status rendering and welcome header.
rooms/ (primary business module)
MainSite.vue: renders site area overview and room cards.MainRoom.vue: renders room-level groups/modes/sensors.MainGroup.vue: type-specific group detail (lights,shutters,climate).Group*.vue: group-specific composition (scenes/entities/climate control).- Uses
tocNavigation(...)for synchronized scroll-section navigation.
map/
Main.vue: map screen usingIndicators.Map.- Combines static demo markers with entity-driven track data (
gatewayStore.getData(...)).
settings/
- Settings sections + logout action (
authStore.signout()). - Uses TOC-style section navigation.
testarea/
- Manual integration playground for auth/config/entities interactions.
- Useful for smoke testing without full UI navigation flows.
auth/
- Callback-only module for OIDC redirects.
MainLogInCallback.vueandMainLogOutCallback.vueprocess provider callbacks and redirect home.
floorplan/
Main.vuecurrently contains a very large inline SVG floorplan representation.
5. Configuration Model
Two config layers exist:
- Bootstrap config (
src/config.ts)
Static startup config for logger/auth/config endpoints/storage/l10n. - Runtime app config (loaded remotely by
ConfigService)
Typed insrc/app/types/types.config*.ts.
Runtime config root shape:
app: global connections/device settingssites[]: site/area/room/group/entity treeusers[]: user metadata mapping (OIDC subject, symbols/images)
Key MQTT-related config nodes:
app.connections.mqtt.defaultsapp.connections.mqtt.general.root_topicapp.device.connections.mqttsite.connections.mqtt
6. Authorization (OIDC)
AuthService wraps oidc-client-ts.
Flow:
signin()redirects to OIDC provider.- Provider returns to
/auth/callbacks/login. MainLogInCallback.vuecallsauth.handleLogInCallback()then redirects to/.- Auth events update
AuthStoreand downstream config loading.
Sign-out flow mirrors login via /auth/callbacks/logout.
Important behavior:
- Service readiness is promise-based (
whenReady()), accounting for redirect callbacks. getToken(renew=true)can trigger silent renew.
7. MQTT Communication Model
See also 8. Service Layer Reference.
7.1 MqttClient (low-level)
src/app/services/clients/mqtt/mqtt.ts wraps mqtt library and exposes:
- connection lifecycle events (
connected,disconnected,reconnected) - topic subscribe/unsubscribe
- publish
- message event dispatch with optional topic-filtered listener objects
7.2 Entities pipeline
GatewayServicemanages one MQTT client for site/entity states.- Incoming messages become
gatewayStore.data[topic]. gatewayStore.publish(topic, value)writes totopic + '/write'.
7.3 Device telemetry pipeline
DeviceService publishes telemetry to a device-scoped root topic:
- user auth state
- browser metadata
- geolocation updates
Remote control channel is scaffolded but not implemented yet.
8. Service Layer Reference
8.1 LoggerService
File: src/app/services/logger/logger.ts
Purpose:
- centralized logging/event bus
- leveled logging (
trace,debug,info,success,warn,error,fatal) - optional deduplication (
dedupeMs) - pluggable sink (console sink in dev by default)
Core methods:
init(config)scope(defaults)/withCorrelation(correlationId)emit(event)addEventListener,removeEventListener
Bootstrap hooks (logger.bootstrap.ts):
- registers
windowerror/unhandledrejection handlers - wires Vue/Pinia/Router error reporting
8.2 StorageService
File: src/app/services/storage/storage.ts
Purpose:
- namespaced persistence abstraction (
namespace::key) - backend selection: localStorage / sessionStorage / in-memory fallback
Core methods:
set,get,delete,has,keys,clearaddEventListenerforset|delete|clear|backend_changed
8.3 AuthService
File: src/app/services/auth/auth.ts
Purpose:
- OIDC login/logout/token handling
- auth lifecycle event dispatch
Core methods:
signin,signout,refreshhandleLogInCallback,handleLogOutCallbackgetUser,getToken,isTokenExpired,whenReady
Events:
user_loaded,user_unloaded,token_expiring,token_expired,ready
8.4 GeolocationService
File: src/app/services/geolocation/geolocation.ts
Purpose:
- wraps browser geolocation API in interval/watch modes
- emits normalized positions
Core methods:
getPositionstartIntervall(spelling kept from code),startWatchstopInterval,stopWatch,stopAllgetPermissionState
Events:
position,error,started,stopped
8.5 ConfigService
File: src/app/services/config/config.ts
Purpose:
- role-aware config loading from endpoint lists
- optional auth header injection
- reload support
Core methods:
load(scopes, options?)reload()addEventListener('loaded', ...)
Load strategy:
- selects first matching configured scope
- tries endpoints for that scope in order until one succeeds
- emits
loadedwith parsed configuration
8.6 MqttClient
File: src/app/services/clients/mqtt/mqtt.ts
Purpose:
- MQTT connectivity abstraction for higher-level services.
Core methods:
init,endsubscribe,unsubscribepublishaddEventListener,removeEventListener
8.7 DeviceService
File: src/app/services/device/device.ts
Purpose:
- device identity + telemetry publishing over MQTT
- auth/geolocation integration
Core methods:
start(runtimeConfig),stop()getDeviceId()(stored underdevice.id)getBrowser()- telemetry handlers:
eventlistenerUser,eventlistenerBrowser,eventlistenerGeolocation
8.8 GatewayService
File: src/app/services/entities/entities.ts
Purpose:
- entity-state MQTT transport
- publish helpers with root topic handling
Core methods:
connect(connectionRequest)publish(topic, message, options?)addEventListener,removeEventListener
9. Store Layer Reference
9.1 bootstrapStores
File: src/app/stores/stores.bootstrap.ts
Initialization order:
authStore.init()configStore.init()gatewayStore.init()preferencesStore.init()
9.2 AuthStore (useAuthStore)
Purpose:
- reactive user/status facade over
AuthService
Public methods:
init,signin,signout,refresh,getToken,$reset
State:
userstatus.ready,status.authenticated,status.expired
9.3 ConfigStore (useConfigStore)
Purpose:
- active runtime config projection for UI
Public methods:
init,reload,setActiveSite,$reset
State/computed:
app,sites,usersactiveSite,activeAreas,activeUser
9.4 gatewayStore (useGatewayStore)
Purpose:
- reactive MQTT value cache and publish adapter
Public methods:
init,connect,disconnectgetData,getValue,getValueAsNumber,getValueAsBoolean,getUnitpublish,$reset
Behavior:
- parses incoming MQTT payload as JSON
- ignores topics ending in
/write
9.5 PreferencesStore (usePreferencesStore)
Purpose:
- user preference persistence via
StorageService
Public methods:
init,setPreferences,$reset
Default key:
preferences
10. UI Layer and Component Patterns
10.1 App-Level Views
src/app/views/Navigation.vue: primary navigation + sidebar + site switching.src/app/composables/toc-navigation.ts: section/button sync for in-page navigation.
10.2 UI Component Families
controls/*: low-level interactive controls (Panel,Icon,Slider,Switch)ui-elements/*: domain widgets (Room,Groups,Modes,Entities,Glance)indicators/*: data visualizations (Chart,Map,Value)
Interaction pattern:
- UI components call
gatewayStore.publish(...)for MQTT actions. - display state derives from
gatewayStore.getValue*.
10.3 Styling System
src/app/assets/css/app.css uses CSS layers:
- reset
- defaults
- layout
- components
- media
- theme
Theme variables are defined in theme.definitions.css and applied in theme.default.css.
11. Utilities
Current utility layer (src/app/utilities):
promises.createPromise()for externally resolvable readiness promisesresultsmodule (ok/error/fromPromise) for explicit async error handlingobjectsmodule for deep merge/clone/compare operationsmqtt.mergeTopics(...)for robust topic concatenationparsers/typecheckshelpers
Legacy utility layer (src/app/utilities.old) is still used by chart/value/list sensor components.
12. Development Workflow
12.1 Commands
npm install-> install dependenciesnpm run dev-> local dev server (host enabled)npm run build-> type-check + production buildnpm run preview-> preview production buildnpm run type-check->vue-tsc --buildnpm run lint-> ESLint with auto-fixnpm run format/npm run format:check-> Prettier onsrc/npm run test:unit-> Vitest runner
12.2 Conventions
- indentation: tabs, size 4 (
.editorconfig) - Prettier: no semicolons, single quotes, print width 100
- alias:
@/*->src/*
13. Testing and Quality Status
- Vitest is configured (
vitest.config.ts, jsdom environment). - ESLint includes Vitest rules for
src/**/__tests__/*. - No active test files are currently present in this snapshot.
14. Known Gaps and Extension Points
- Auth route guard is prepared but commented out in
main.ts. - Device remote control path in
DeviceServiceis marked TODO. - Config examples in
public/configare mostly YAML fixtures, while runtime loader currently expects JSON from configured endpoints. utilities.olddependencies remain in indicator/UI code and can be migrated tosrc/app/utilitiesincrementally.- Floorplan view (
src/floorplan/views/Main.vue) is a very large static SVG and should likely be decomposed for maintainability.
If you add new behavior, update this README in the same PR with:
- affected service/store/module,
- new events and public methods,
- required config schema changes,
- routing or MQTT topic contract changes.