Skip to content

Conversation

@raman325
Copy link
Collaborator

@raman325 raman325 commented Jan 4, 2026

Summary

Adds Lovelace dashboard, view, and section strategies for Keymaster to dynamically generate the UI, replacing the current copy/paste YAML workflow.

Proposed change

Lovelace strategies allow custom frontend modules to auto-generate dashboard views. This PR extracts lovelace generation code and adds WebSocket commands that return configs to the frontend. A TypeScript module registers the keymaster strategy at dashboard, view, and section levels.

Key features:

  • Dashboard strategy - Fetches all Keymaster config entries and creates a view per lock (sorted alphabetically)
  • View strategy - Generates complete view with badges and section strategies for code slots
  • Section strategy - Generates individual code slot sections (enables lazy loading)
  • Two identifiers supported at view/section level:
    • lock_name - User-friendly option for manual configuration
    • config_entry_id - Internal use by dashboard/view strategies (efficient, no lookup)
  • View-level overrides: title, icon, path, theme, visible
  • Default paths prefixed with keymaster- to avoid conflicts

WebSocket APIs:

  • keymaster/get_view_metadata - Returns lock metadata, badges, slot info
  • keymaster/get_section_config - Returns single code slot section config

Architecture changes:

  • Refactored lovelace.py to use sync functions with @callback decorator
  • generate_view_config, generate_badges_config, generate_section_config as public API
  • External entity IDs (lock, door sensor) pass through unchanged during entity mapping

Benefits:

  • UI changes remain in Python and apply to both YAML and strategy paths
  • YAML file generation can be cleanly deprecated
  • Section-level strategies enable lazy loading for better performance
  • Integration auto-registers the JavaScript resource

Usage

Full Dashboard (all locks)

strategy:
  type: custom:keymaster

Single Lock View (by lock name)

views:
  - strategy:
      type: custom:keymaster
      lock_name: Front Door

With view overrides

views:
  - strategy:
      type: custom:keymaster
      lock_name: Front Door
      title: My Custom Title
      icon: mdi:door-closed-lock
      path: my-lock

Section strategy (advanced)

sections:
  - strategy:
      type: custom:keymaster
      lock_name: Front Door
      slot_num: 1

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (which adds functionality)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

  • This PR is related to deprecating the YAML lovelace generation workflow
  • Adds GitHub Actions for TypeScript linting, building, and testing with Codecov support
  • Includes test infrastructure improvements and cleanup

🤖 Generated with Claude Code

@raman325 raman325 force-pushed the feature/dashboard-strategy branch from 83da353 to 227620f Compare January 5, 2026 00:37
@codecov-commenter
Copy link

codecov-commenter commented Jan 5, 2026

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 88.92989% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.81%. Comparing base (a9a30ef) to head (9bdeb78).

Files with missing lines Patch % Lines
lovelace_strategy/section-strategy.ts 0.00% 16 Missing ⚠️
custom_components/keymaster/__init__.py 82.14% 5 Missing ⚠️
custom_components/keymaster/lovelace.py 93.61% 3 Missing ⚠️
lovelace_strategy/main.ts 0.00% 3 Missing ⚠️
lovelace_strategy/slugify.ts 75.00% 3 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             beta     #536      +/-   ##
==========================================
+ Coverage   80.86%   81.81%   +0.95%     
==========================================
  Files          19       28       +9     
  Lines        2341     2563     +222     
  Branches        0       24      +24     
==========================================
+ Hits         1893     2097     +204     
- Misses        448      466      +18     
Flag Coverage Δ
python 82.13% <95.85%> (?)
typescript 71.79% <71.79%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@raman325 raman325 marked this pull request as ready for review January 5, 2026 01:28
Copilot AI review requested due to automatic review settings January 5, 2026 01:28
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a Lovelace dashboard and view strategy feature to dynamically generate the Keymaster UI, providing an alternative to the existing manual copy/paste YAML workflow. The implementation includes both frontend TypeScript modules and backend Python websocket APIs to support the strategy system.

Key Changes:

  • Adds TypeScript/JavaScript frontend strategy modules that register custom Lovelace strategies for dashboard and view generation
  • Implements a websocket API that returns view configurations dynamically based on lock names or config entry IDs
  • Refactors the existing lovelace generation code to separate view config generation from file writing, enabling reuse for both strategies
  • Adds resource registration/cleanup logic to automatically manage the frontend module in Home Assistant's lovelace resources

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
tsconfig.json TypeScript configuration for the frontend strategy module
tsconfig.build.json Build-specific TypeScript configuration that excludes test files
package.json Node.js package configuration with build scripts and frontend dependencies
rollup.config.js Rollup bundler configuration to compile TypeScript into a single JavaScript file
lovelace_strategy/*.ts TypeScript source files implementing dashboard and view strategies
custom_components/keymaster/www/keymaster.js Compiled JavaScript output from TypeScript sources
custom_components/keymaster/const.py Adds constants for strategy file paths and URLs
custom_components/keymaster/lovelace.py Refactors to extract view config generation logic for reuse
custom_components/keymaster/websocket.py New websocket API handler for retrieving view configurations
custom_components/keymaster/resources.py Helper functions for registering/unregistering lovelace resources
custom_components/keymaster/init.py Updates to register websocket API, expose strategy files, and manage resources
custom_components/keymaster/manifest.json Adds "http" to after_dependencies
tests/conftest.py Adds mock for hass.http to support static path registration in tests
tests/test_lovelace.py Comprehensive tests for view generation and file operations
tests/test_resources.py Tests for resource registration and cleanup logic
tests/test_websocket.py Tests for websocket API endpoint

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

raman325 and others added 5 commits January 4, 2026 21:01
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@raman325 raman325 added the enhancement New feature or request label Jan 5, 2026
@tykeal
Copy link
Contributor

tykeal commented Jan 5, 2026

@raman325 this is one of the features that I love about LCM! It's working for the first case, but if I do it in a view I get the following error:

ERROR: Either config_entry_title or config_entry_id must be provided in the view config, but not both!

I'm adding

  - strategy:
      type: custom:keymaster
      lock_name: ZWaveTestLock

I've tried it In my views section of an existing dashboard and I tried the entire code block in a fresh dashboard which got me the same error.

@tykeal
Copy link
Contributor

tykeal commented Jan 5, 2026

I'm going to try this on one of my more complex HA setups with 7 locks to see how it does 😆

@raman325
Copy link
Collaborator Author

raman325 commented Jan 5, 2026

Thx for testing I can fix that bug shortly, holdover from before I changed the configuration approach. Will post and tag you again once it's fixed. I tested the dashboard strategy but forgot to test the view strategy so glad you did and nice catch

@firstof9
Copy link
Collaborator

firstof9 commented Jan 5, 2026

Give me a tag when you update that as well, I prefer doing views vs whole dashboards for keymaster.

- Dashboard strategy uses config_entry_id (efficient, no lookup)
- Manual view config uses lock_name (user-friendly)
- View strategy validates exactly one identifier is provided
- Views sorted alphabetically by lock name

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@raman325
Copy link
Collaborator Author

raman325 commented Jan 5, 2026

@firstof9 fixed

raman325 and others added 4 commits January 5, 2026 09:20
- Set up vitest for TypeScript testing
- Add view-strategy tests covering:
  - Validation of exactly one identifier (config_entry_id XOR lock_name)
  - Error handling when neither or both provided
  - WebSocket call with correct identifier
  - Error view on failed WebSocket calls
  - Starting view when HA not running
- Add dashboard-strategy tests covering:
  - Alphabetical sorting of views by lock name
  - Single view placeholder hack
  - Error handling for failed individual lock fetch
  - Parallel fetching of view configs
  - Using config_entry_id for efficiency
- Add GitHub workflow for vitest (only runs on lovelace_strategy changes)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update vitest workflow to match lock_code_manager pattern
  - Add branch filtering (beta, main)
  - Add codecov integration for TypeScript coverage
- Add yarn.yaml workflow for lint and build validation
  - Runs lint:fix and fails if repo becomes dirty
  - Runs build and fails if generated files change
  - Only triggers on TypeScript/JavaScript file changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add codecov.yml with flag management for both languages
- Update pytest workflow to use python flag
- Enable carryforward for flags (maintains coverage when only one language changes)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@tykeal
Copy link
Contributor

tykeal commented Jan 5, 2026

Tab view strategy isn't currently generating a view name so it's getting set as Unnamed View, but the fix for the view solved the problem with rendering.

raman325 and others added 8 commits January 5, 2026 09:27
Only run Python tests when Python-related files change:
- **.py files
- custom_components/** and tests/**
- pyproject.toml, requirements*.txt
- The workflow file itself

Scheduled runs still execute regardless of paths.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Configure paths for each flag so status checks only appear when
relevant files change:
- Python checks: custom_components/, tests/
- TypeScript checks: lovelace_strategy/

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Users can now customize the view tab title by adding a `title` parameter
to the view strategy config:

```yaml
views:
  - strategy:
      type: custom:keymaster
      lock_name: Front Door
      title: My Custom Title
```

This addresses the "Unnamed View" issue where HA's UI editor shows a
placeholder before the strategy generates content. Users can now set
their preferred title directly in the strategy config.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add support for standard Lovelace view parameters in the strategy config:
- icon: tab icon (e.g., mdi:door)
- path: URL path for the view
- theme: theme override
- title: tab title (moved from previous commit)
- visible: visibility conditions

These are exposed at the strategy config level for convenience since
strategy-generated views cannot be edited through the UI.

Also:
- Add slugify utility for consistent path generation
- Dashboard strategy ensures title fallback from config entry
- View strategy sets default path from slugified title if backend doesn't provide one
- Reuse types from LovelaceViewConfig via Pick<>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add .eslintrc.cjs with TypeScript ESLint configuration
- Fix lint script to point to lovelace_strategy/ instead of ts/

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The backend's websocket response always includes the title derived from
CONF_LOCK_NAME in config entry data. Using configEntry.title as a fallback
was both unnecessary and potentially incorrect since that value is set
once at setup and may become stale.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Backend always provides title and never provides path, so remove
unnecessary conditional checks. Path is always generated from title.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Dashboard now returns view strategy configs instead of resolved views.
HA calls KeymasterViewStrategy for each view, centralizing all view
generation logic (path, overrides, error handling) in one place.

- Dashboard only fetches config entries (single WS call)
- Returns strategy configs with title for tab display
- View strategy handles WS call, path generation, and overrides

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@raman325 raman325 marked this pull request as draft January 5, 2026 15:11
raman325 and others added 3 commits January 5, 2026 10:12
configEntry.title is set once at setup and may become stale.
configEntry.data.lock_name is the current authoritative value.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Title now explicitly defaults to backend's response, with strategy
config able to override. This ensures the backend's authoritative
title is used while still allowing user overrides.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Path generation now:
- Default (backend title): keymaster-{slugified-title}
- Custom title override: {slugified-custom-title}

Also adds strategy property to LovelaceViewConfig type stub.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@firstof9
Copy link
Collaborator

firstof9 commented Jan 6, 2026

Seeing this when trying to add the strategy to a section view:

image

@firstof9
Copy link
Collaborator

firstof9 commented Jan 6, 2026

Works fine in masonry view however.

@raman325
Copy link
Collaborator Author

raman325 commented Jan 9, 2026

@firstof9 Sections are within a view, what do you expect the frontend logic to generate at that level? We could do the exact same thing we do at the view level (as in all the code slot cards for the lock), I just don't know if it would look good

@firstof9
Copy link
Collaborator

firstof9 commented Jan 9, 2026

I wasn't sure if there was an easy way to just clone the entities from the masonry view into a sections view.
That's basically what I do now, everything in sections views.

- Convert lovelace.py functions from async to sync with @callback
- Add ws_get_view_metadata and ws_get_section_config websocket handlers
- Update tests for new sync API and websocket endpoints
- Add section-strategy.ts for section-level generation
- Update TypeScript types and strategy utilities

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@raman325
Copy link
Collaborator Author

raman325 commented Jan 10, 2026

OK so now we do sections, and the section strategy requires the code slot number since that emulates what we do in the view. So basically @firstof9 , what you tried the first time will now work as long as you provide a slot num. Everything looks good to me so I am marking this as ready for review shortly.

Also worth noting that I checked the lovelace before and after this PR and they are the exact same. I have a refactor coming after this one to clean up the lovelace code and get rid of some extra parts of the payload that are redundant but one step at a time

Clarify that the file-writing function is async while the config
generation functions are sync.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@raman325
Copy link
Collaborator Author

raman325 commented Jan 10, 2026

Here's the documentation I will push to the wiki with this PR so you can test all of the functionality:

Lovelace View

Keymaster provides two ways to add a Lovelace dashboard for managing your locks:

  1. Lovelace Strategy (Recommended) - Automatically generates and updates the dashboard
  2. Manual YAML (Legacy) - Copy and paste YAML configuration

Option 1: Lovelace Strategy (Recommended)

The Lovelace Strategy dynamically generates your dashboard and keeps it in sync with your lock configuration. No manual updates needed when you add or remove locks.

Prerequisites

Install the Keymaster frontend component. This is typically done automatically when you install Keymaster, but you can verify it's loaded by checking your browser's developer console for the ll-strategy-dashboard-keymaster custom element.

Dashboard Strategy (Easiest)

Create a dedicated Keymaster dashboard that automatically shows all your locks:

  1. Open Home Assistant Settings → Dashboards
  2. Click Add Dashboard
  3. Set a title (e.g., "Keymaster") and icon
  4. Click Create
  5. Open the new dashboard, click the three-dot menu → Edit dashboard
  6. Click the three-dot menu → Raw configuration editor
  7. Replace the entire content with:
strategy:
  type: custom:keymaster
  1. Click Save and exit the editor

The dashboard will automatically create a tab for each configured lock.

View Strategy (Single Lock)

Add a Keymaster view to an existing dashboard:

  1. Open your dashboard's raw configuration editor
  2. Add a new view using the view strategy:
views:
  # ... your existing views ...
  - strategy:
      type: custom:keymaster
      lock_name: Front Door

Configuration Options:

Option Required Description
lock_name One of these The name of the lock (as configured in Keymaster) - recommended
config_entry_id is required The config entry ID - primarily for internal use
title No Override the view title
icon No Override the view icon
path No Override the view URL path

Tip: Use lock_name for easier configuration. The config_entry_id option exists primarily for internal use by the dashboard strategy.

Example with overrides:

- strategy:
    type: custom:keymaster
    lock_name: Front Door
    title: Front Door Codes
    icon: mdi:door

Section Strategy (Advanced)

For advanced users who want to embed individual code slot sections into custom views:

sections:
  - strategy:
      type: custom:keymaster
      lock_name: Front Door
      slot_num: 1

Configuration Options:

Option Required Description
lock_name One of these The name of the lock (as configured in Keymaster) - recommended
config_entry_id is required The config entry ID - primarily for internal use
slot_num Yes Which code slot to generate (starting from your configured start slot)

This generates the section for a single code slot, allowing you to compose custom layouts.


Option 2: Manual YAML (Legacy)

If you prefer manual control or the strategy doesn't work for your setup, you can copy the generated YAML.

Copy the Lovelace YAML

Keymaster generates a lovelace YAML file for each lock. Find it at:

<config directory>/custom_components/keymaster/lovelace/<lock_name>.yaml
  1. Open the file in a text editor
  2. Select all and copy (Ctrl-A, Ctrl-C)
  3. Open your Home Assistant dashboard
  4. Click the three-dot menu → Edit Dashboard → three-dot menu → Raw configuration editor
  5. Scroll to the bottom and paste the clipboard contents
  6. Click Save

Required Frontend Components

For the manual YAML to work, install these frontend cards via HACS:

  1. lovelace-auto-entities
  2. lovelace-card-tools
  3. lovelace-fold-entity-row
  4. numberbox-card

Note: The Lovelace Strategy method does not require these additional components.

raman325 and others added 5 commits January 10, 2026 03:07
Section strategy now accepts lock_name as an alternative to
config_entry_id, matching the view strategy pattern. View strategy
passes through whichever identifier it received.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update _generate_lock_badges to accept lock_entity and door_sensor params
- Update _get_entity_id to return original value when registry lookup fails
- Remove _add_lock_and_door_to_badges (no longer needed)
- Fix test mock to properly simulate real registry behavior

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@raman325 raman325 changed the title Add dashboard and view strategies to deprecate lovelace YAML Add Lovelace strategies (dashboard, view, section) for dynamic UI generation Jan 10, 2026
@raman325
Copy link
Collaborator Author

raman325 commented Jan 10, 2026

here's the before and after JSON for the view for this PR (on beta branch and with this PR), they both match 1 for 1
lovelace-view-beta.json
lovelace-view-pr536.json

@raman325 raman325 marked this pull request as ready for review January 10, 2026 16:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants