Create a Home Assistant Lovelace press-and-hold button card with visual feedback, smart domain-aware actions (button.press, toggle, custom services), and extensive testing.
Creates a custom Home Assistant Lovelace card with press-and-hold functionality, visual feedback, and intelligent action handling.
Guides you through building a production-ready Home Assistant custom card featuring:
Initialize a LitElement-based project with TypeScript and Rollup:
```bash
mkdir -p src dist
pnpm init
pnpm add lit custom-card-helpers
pnpm add -D typescript rollup @rollup/plugin-node-resolve @rollup/plugin-typescript @rollup/plugin-terser
```
**Key architecture decisions:**
Create `src/press-and-hold-button-card.ts`:
**Action System Design:**
- Button entities → `button.press` service (not toggle)
- Light/Switch/Cover → Toggle action
- Other entities → More info dialog
**Press-Hold Detection:**
**Visual Feedback:**
**Implementation pattern:**
```typescript
class PressAndHoldButtonCard extends LitElement {
@property({ attribute: false }) hass!: HomeAssistant;
@property({ attribute: false }) config!: CardConfig;
private holdTimer?: number;
private pressStartTime?: number;
private pressStartPos?: { x: number; y: number };
private handlePointerDown(e: PointerEvent) {
// Capture pointer, start timer, begin animation
}
private handlePointerMove(e: PointerEvent) {
// Check movement tolerance, cancel if exceeded
}
private handlePointerUp(e: PointerEvent) {
// If held long enough, execute action
}
private executeAction() {
// Smart domain detection or custom action execution
}
}
```
Create `src/press-and-hold-button-card-editor.ts`:
**Dynamic Schema Pattern:**
**Schema structure:**
```typescript
private _schema = computed(() => [
{ name: 'entity', selector: { entity: {} } },
{ name: 'hold_duration', selector: { number: { min: 100 } } },
{ name: 'action', selector: { select: { options: [...] } } },
// Conditional service fields when action === 'call-service'
...(this._config?.action === 'call-service' ? [
{ name: 'service', selector: { text: {} } },
{ name: 'service_data', selector: { object: {} } }
] : [])
]);
```
Create `test.html` with complete Home Assistant mock:
**Mock Components:**
**Shadow DOM Testing:**
```javascript
// Access button through shadow root
const card = document.getElementById('testCard');
const button = card.shadowRoot.querySelector('.button');
// Simulate press-hold
button.dispatchEvent(new PointerEvent('pointerdown', {
pointerId: 1, clientX: 947, clientY: 888, bubbles: true
}));
setTimeout(() => {
button.dispatchEvent(new PointerEvent('pointerup', {
pointerId: 1, clientX: 947, clientY: 888, bubbles: true
}));
}, 1200); // Wait longer than hold duration
```
**Mock Entity Types:**
```javascript
states: {
'button.ratgdo_door': { state: 'idle', attributes: { friendly_name: 'Door Button' } },
'switch.test': { state: 'off', attributes: { friendly_name: 'Test Switch' } },
'light.living_room': { state: 'on', attributes: { brightness: 255 } }
}
```
Create `rollup.config.mjs`:
**Build requirements:**
**Output:**
Create HACS configuration files:
**hacs.json:**
```json
{
"name": "Nerdo UX Cards",
"render_readme": true,
"filename": "hacs-nerdo-ux.js"
}
```
**info.md:** Repository description for HACS store
**README.md:** Installation instructions and configuration examples
**Manual Testing (test.html):**
1. Open in browser and use dev tools
2. Test all action types with different entities
3. Verify console shows clean execution
4. Check visual feedback and timing
**Automated Testing (Playwright):**
```javascript
await page.evaluate(() => {
const card = document.getElementById('testCard');
const button = card.shadowRoot.querySelector('.button');
button.dispatchEvent(new PointerEvent('pointerdown', {
pointerId: 1, clientX: 947, clientY: 888, bubbles: true
}));
setTimeout(() => {
button.dispatchEvent(new PointerEvent('pointerup', {
pointerId: 1, clientX: 947, clientY: 888, bubbles: true
}));
}, 1200);
});
```
**Error Scenarios:**
```
src/
├── press-and-hold-button-card.ts # Main card component
├── press-and-hold-button-card-editor.ts # Configuration UI
├── constants.ts # Default values
└── build-info.ts # Build timestamp
dist/
└── hacs-nerdo-ux.js # Built output
test.html # Test environment
rollup.config.mjs # Build config
hacs.json # HACS integration
```
**Smart Default (Button Entity):**
```yaml
type: custom:press-and-hold-button-card
entity: button.garage_door
hold_duration: 1000
```
**Toggle Action:**
```yaml
type: custom:press-and-hold-button-card
entity: light.living_room
action: toggle
hold_duration: 500
```
**Custom Service:**
```yaml
type: custom:press-and-hold-button-card
entity: cover.garage
action: call-service
service: cover.set_cover_position
service_data:
position: 50
```
```bash
pnpm install
pnpm run build
pnpm run watch
open test.html
```
1. Update TypeScript interfaces
2. Add option to editor schema
3. Implement execution logic in `executeAction()`
4. Update documentation
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/home-assistant-press-hold-card/raw