Skip to content

Commit

Permalink
Merge pull request #5 from kizza/ha-target-selector
Browse files Browse the repository at this point in the history
Introduce target selector for lights
  • Loading branch information
kizza authored Mar 2, 2024
2 parents dcd8118 + c6c1f1f commit 5c709f2
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 158 deletions.
79 changes: 77 additions & 2 deletions cypress/e2e/card.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ describe('magic home party', () => {
const lime = [0, 255, 0];
const yellow = [255, 255, 0];

const hassAreas = () => ({
areas: {
kitchen: { name: "Kitchen" },
bathroom: { name: "Bathroom" },
}
})

const hassDevices = () => ({
devices: {
123456: { name: "***", name_by_user: "Dishwasher" },
ABCDEF: { name: "Fan" },
}
})

const hassStates = () => ({
states: {
foo: {
Expand All @@ -29,6 +43,65 @@ describe('magic home party', () => {
.waitForCustomElement('magic-home-party-card')
);

describe('cofiguration', () => {
const hass = {
...hassAreas(),
...hassDevices(),
...hassStates(),
};

const withConfig = (config) =>
cy.setupCustomCard('magic-home-party-card', {
colours: [red, lime],
...config,
}, hass).get('.targets')

it('resolves legacy entities', () => {
withConfig({ one: "one", entities: ['foo', 'bar']})
.should('contain', 'Foo light')
.and('contain', 'Bar light')
});

describe("entity_id targets", () => {
it('resolves a single entity_id', () => {
withConfig({ two: "two", targets: {entity_id: 'foo'}})
.should('contain', 'Foo light')
});

it('resolves a multiple entity_ids', () => {
withConfig({ targets: {entity_id: ['foo', 'bar']}})
.should('contain', 'Foo light')
.and('contain', 'Bar light')
});
})

describe("device_id targets", () => {
it('resolves a single device_id', () => {
withConfig({ two: "two", targets: {device_id: '123456'}})
.should('contain', 'Dishwasher')
});

it('resolves a multiple device_ids', () => {
withConfig({ two: "two", targets: {device_id: ['123456', 'ABCDEF']}})
.should('contain', 'Dishwasher')
.and('contain', 'Fan')
});
})

describe("area_id targets", () => {
it('resolves a single area_id', () => {
withConfig({ two: "two", targets: {area_id: 'kitchen'}})
.should('contain', 'Kitchen')
});

it('resolves a multiple area_id', () => {
withConfig({ two: "two", targets: {area_id: ['kitchen', 'bathroom']}})
.should('contain', 'Kitchen')
.and('contain', 'Bathroom')
});
})
});

describe('card', () => {
it('renders a gradient', () => {
const colours = [red, lime];
Expand All @@ -43,12 +116,14 @@ describe('magic home party', () => {

it('plays the effect on click', () => {
const hass = {
...hassAreas(),
...hassStates(),
callService: cy.stub(),
};

const cardConfig = {
entities: Object.keys(hass.states),
targets: { area_id: 'kitchen', entity_id: ['foo', 'bar'] },
colours: [red, lime],
};

Expand All @@ -57,7 +132,7 @@ describe('magic home party', () => {
.click()
.then(() => {
expect(hass.callService).to.be.calledWith('flux_led', 'set_custom_effect', {
entity_id: cardConfig.entities,
...cardConfig.targets,
colors: cardConfig.colours,
speed_pct: 20,
transition: 'gradual',
Expand Down Expand Up @@ -96,7 +171,7 @@ describe('magic home party', () => {
.should(
() => {
expect(hass.callService).to.be.calledWith('light', 'turn_on', {
entity_id: cardConfig.entities,
target: {entity_id: cardConfig.entities},
rgb_color: yellow,
});
},
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"typescript": "^4.9.4"
},
"dependencies": {
"@mdi/js": "^7.4.47",
"custom-card-helpers": "^1.9.0",
"lit": "^2.6.0"
}
Expand Down
35 changes: 35 additions & 0 deletions src/config/lookup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { HomeAssistant } from "custom-card-helpers"

type Area = {
name: string
}

type Device = {
name: string;
name_by_user: string
}

type HomeAssistantWithExtras = HomeAssistant & {
areas: Record<string, Area>
devices: Record<string, Device>
}

export const toFriendlyName = (
identifier: 'area_id' | 'device_id' | 'entity_id',
_hass: HomeAssistant,
) => (value: string): string => {
const hass = _hass as HomeAssistantWithExtras
let name: string | undefined = undefined

if (identifier == 'area_id') {
name = hass.areas[value].name
} else if (identifier == 'device_id') {
name = hass.devices[value].name_by_user || hass.devices[value].name
} else if (identifier == 'entity_id') {
name = toState(hass)(value).attributes.friendly_name
}

return name || `Unknown (${value})`
}

export const toState = (hass: HomeAssistant) => (value: string) => hass.states[value]
35 changes: 35 additions & 0 deletions src/config/parse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { BaseConfig, Colour, HassServiceTarget, MagicHomePartyConfig } from "../types"

export const parseConfig = (input: BaseConfig): MagicHomePartyConfig =>
withTargets(withSpeed(withColours(input)))

const withColours = <T>(config: T): T & {colours: Colour[]} => ({
colours: [],
...config,
})

const withSpeed = <T>(config: T): T & {speed: number} => ({
speed: 20,
...config,
})

const withTargets = <T>(config: T): T & {targets: HassServiceTarget} => {
// Has targets already
if ((config as any).targets !== undefined) {
return config as T & {entities: [], targets: HassServiceTarget}
// Migrate entities to targets
} else if ((config as any).entities !== undefined) {
return {
...config,
targets: {
entity_id: (config as any).entities
}
}
// Defaults
} else {
return {
...config,
targets: {}
}
}
}
8 changes: 2 additions & 6 deletions src/const.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { css } from 'lit';
import { Colours, MagicHomePartyConfig } from './types';
import { Colours } from './types';

export const DEFAULT_CONFIG: Partial<MagicHomePartyConfig> = {
speed: 20,
}

export const CARD_VERSION = '0.1.3';
export const CARD_VERSION = '0.1.4';

export const RADIUS = css`0.8em`;

Expand Down
40 changes: 16 additions & 24 deletions src/editor.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { fireEvent, HomeAssistant } from 'custom-card-helpers';
import { css, html, LitElement } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { colours, DEFAULT_CONFIG } from './const';
import { colours } from './const';
import './elements/chip';
import './elements/entities-picker';
import './elements/targets-picker';
import './elements/palette';
import { ChipEvent, Colour, MagicHomePartyConfig } from './types';
import { BaseConfig, ChipEvent, Colour, MagicHomePartyConfig } from './types';
import { copyToClipboard } from './clipboard';
import { parseConfig } from './config/parse';

const { values } = Object;

Expand All @@ -16,19 +17,11 @@ export class MagicHomePartyEditor extends LitElement {

@state() private config!: MagicHomePartyConfig;
@state() private selectedColours: Colour[] = [];
@state() private selectedEntities: string[] = [];
@state() private speed: number = 50;

public setConfig(config: MagicHomePartyConfig) {
this.config = {
...DEFAULT_CONFIG,
...config,
colours: config.colours || [],
entities: config.entities || [],
};
@state() private speed: number | undefined = undefined;

public setConfig(config: BaseConfig) {
this.config = parseConfig(config)
this.selectedColours = this.config.colours;
this.selectedEntities = this.config.entities;
this.speed = this.config.speed;
}

Expand All @@ -41,7 +34,7 @@ export class MagicHomePartyEditor extends LitElement {
Single click to preview a color. Double click to add-or-remove a color.
<div style="display: flex; align-items: center;">
<h3>Selected Colours</h3>
<h3>Selected colours</h3>
<span class="copyButton" @click=${this._copyToClipboard}> Copy to clipboard </span>
</div>
<magic-home-party-palette
Expand All @@ -60,14 +53,14 @@ export class MagicHomePartyEditor extends LitElement {
@doubleClick="${(e: ChipEvent) => this._addChip(e.detail.colour)}"
></magic-home-party-palette>
<h3>Selected Lights</h3>
<magic-home-party-entities-picker
<h3>Selected lights</h3>
<magic-home-party-targets-picker
.
.hass=${this.hass}
.value=${this.config.entities}
@value-changed=${this._entitiesChanged}
.value=${this.config.targets}
@value-changed=${this._targetsChanged}
>
</magic-home-party-entities-picker>
</magic-home-party-targets-picker>
<h3>Transition speed</h3>
<ha-selector
Expand All @@ -86,13 +79,13 @@ export class MagicHomePartyEditor extends LitElement {

private _setLight = (colour: Colour) =>
this.hass.callService('light', 'turn_on', {
entity_id: this.selectedEntities,
target: this.config.targets,
rgb_color: colour,
});

private _entitiesChanged(event: any) {
private _targetsChanged(event: any) {
event.stopPropagation();
this.selectedEntities = [...event.detail.value];
this.config.targets = {...event.detail.value};
this._updateConfig();
}

Expand All @@ -116,7 +109,6 @@ export class MagicHomePartyEditor extends LitElement {
const newConfig = {
...this.config,
colours: this.selectedColours,
entities: this.selectedEntities,
speed: this.speed,
};

Expand Down
88 changes: 0 additions & 88 deletions src/elements/entities-picker.ts

This file was deleted.

Loading

0 comments on commit 5c709f2

Please sign in to comment.