Skip to content

Commit

Permalink
Support entities[].attribute for displaying an attribute instead of s…
Browse files Browse the repository at this point in the history
…tate. Refactorings
  • Loading branch information
nervetattoo committed May 20, 2019
1 parent 01a6c11 commit 037ae5b
Show file tree
Hide file tree
Showing 6 changed files with 1,445 additions and 62 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ resources:
| entities[].unit | _string_ or _false_ | Override the automatic unit | `unit: My unit` |
| entities[].name | _string_ | Override the automatic usage of friendly_name | `name: A sensor` |
| entities[].map_state | _object_ | Map state values to resulting text or icons. A string prefixed with mdi: or hass: will yield a rendered icon. | `map_state: { home: mdi:home-account, not_home: mdi:walk }` |
| entities[].attribute | _string_ | Display an attribute instead of the state | |

## Example configuration for results as seen in screenshot

Expand Down
14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
"description": "A fluffy banner card for Home Assistant",
"license": "MIT",
"scripts": {
"test": "ava",
"build": "NODE_ENV=production rollup -c"
},
"dependencies": {
"lit-element": "^2.1.0"
},
"devDependencies": {
"@babel/core": "^7.4.4",
"@babel/register": "^7.4.4",
"ava": "^1.4.1",
"husky": "^2.3.0",
"prettier": "^1.17.1",
"pretty-quick": "^1.10.0",
Expand Down Expand Up @@ -47,5 +51,15 @@
"beforeStart": "yarn build",
"afterRelease": "rm banner-card.js"
}
},
"babel": {
"presets": [
"@ava/stage-4"
]
},
"ava": {
"require": [
"@babel/register"
]
}
}
50 changes: 20 additions & 30 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LitElement, html, css } from "lit-element";
import styles from "./styles";
import { parseEntity, getAttributeOrState } from "./utils";

//
// type: custom:banner
Expand All @@ -8,21 +9,22 @@ import styles from "./styles";
// entities:
// - entity: light.fibaro_system_fgd212_dimmer_2_level

function mapObject(data, fn) {
return Object.entries(data).reduce((result, [key, value]) => {
return {
...result,
[key]: fn(value, key)
};
}, {});
function renderError(heading, error) {
return html`
<ha-card class="not-found">
<h2 class="heading">
${heading}
</h2>
<div class="overlay-strip">
<div class="error">${error}</div>
</div>
</ha-card>
`;
}
function parseEntity(entity) {
if (typeof entity === "object") {
return mapObject(entity, value => {
return value === false ? null : value;
});
}
return { entity };

const ICON_REGEXP = /^(mdi|hass):/;
function isIcon(value) {
return typeof value === "string" && value.match(ICON_REGEXP);
}

class BannerCard extends LitElement {
Expand Down Expand Up @@ -67,10 +69,11 @@ class BannerCard extends LitElement {
return config;
}
const state = hass.states[config.entity];

const data = {
name: state.attributes.friendly_name,
state: state.state,
value: state.state,
value: getAttributeOrState(state, config.attribute),
unit: state.attributes.unit_of_measurement,
domain: config.entity.split(".")[0]
};
Expand All @@ -89,22 +92,9 @@ class BannerCard extends LitElement {
});
}

renderError(error) {
return html`
<ha-card class="not-found">
<h2 class="heading">
${this.config.heading}
</h2>
<div class="overlay-strip">
<div class="error">${error}</div>
</div>
</ha-card>
`;
}

render() {
if (this.error) {
return this.renderError(this.error);
return renderError(this.config.heading, this.error);
}

const onClick = () => this.config.link && this.navigate(this.config.link);
Expand Down Expand Up @@ -137,7 +127,7 @@ class BannerCard extends LitElement {
</span>
</div>
`;
} else if (value.match(/^(mdi|hass):/)) {
} else if (isIcon(value)) {
htmlContent = html`
<ha-icon icon="${value}"></ha-icon>
`;
Expand Down
26 changes: 26 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Helper to return an attribute if specified and present,
// if not the value of state
export function getAttributeOrState({ state, attributes }, attribute = false) {
if (typeof attribute === "string" && attributes.hasOwnProperty(attribute)) {
return attributes[attribute];
}
return state;
}

export function mapObject(data, fn) {
return Object.entries(data).reduce((result, [key, value]) => {
return {
...result,
[key]: fn(value, key)
};
}, {});
}

export function parseEntity(entity) {
if (typeof entity === "object") {
return mapObject(entity, value => {
return value === false ? null : value;
});
}
return { entity };
}
38 changes: 38 additions & 0 deletions src/utils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import test from "ava";
import { parseEntity, mapObject, getAttributeOrState } from "./utils";

// Test that entities both can be specified as a string or as an object
test("parseEntity supports string + object", t => {
const entity = "my.entity";
t.deepEqual(parseEntity(entity), { entity });
t.deepEqual(parseEntity({ entity }), { entity });
});

test("parseEntity nulls false values", t => {
const fixture = {
entity: "a",
name: false
};
t.is(parseEntity(fixture).name, null);
});

test("parseEntity passes through all truthy values", t => {
const fixture = {
entity: "a",
name: "B",
foo: true
};
t.deepEqual(parseEntity(fixture), fixture);
});

test("getAttributeOrState returns only valid attribute", t => {
const fixture = {
state: "on",
attributes: {
active: true
}
};

t.is(getAttributeOrState(fixture, "active"), true);
t.is(getAttributeOrState(fixture, "nope"), "on");
});
Loading

0 comments on commit 037ae5b

Please sign in to comment.