Merge pull request #2517 from home-assistant/dev

20190120.0
This commit is contained in:
Paulus Schoutsen 2019-01-20 16:20:37 -08:00 committed by GitHub
commit 6bf954ccb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
179 changed files with 42029 additions and 1559 deletions

View File

@ -18,6 +18,7 @@
}, },
"globals": { "globals": {
"__DEV__": false, "__DEV__": false,
"__DEMO__": false,
"__BUILD__": false, "__BUILD__": false,
"__VERSION__": false, "__VERSION__": false,
"__STATIC_PATH__": false, "__STATIC_PATH__": false,

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

23
demo/public/index.html Normal file
View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#2157BC" />
<title>Home Assistant Demo</title>
<script src="./custom-elements-es5-adapter.js"></script>
<script src="./compatibility.js"></script>
<script src="./main.js" async></script>
<style>
body {
font-family: Roboto, Noto, sans-serif;
margin: 0;
padding: 0;
}
</style>
</head>
<body></body>
</html>

17
demo/script/build_demo Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
# Build the demo
# Stop on errors
set -e
cd "$(dirname "$0")/.."
OUTPUT_DIR=dist
rm -rf $OUTPUT_DIR
cd ..
DEMO=1 ./node_modules/.bin/gulp build-translations gen-icons
cd demo
NODE_ENV=production ../node_modules/.bin/webpack -p --config webpack.config.js

13
demo/script/develop_demo Executable file
View File

@ -0,0 +1,13 @@
#!/bin/sh
# Develop the demo
# Stop on errors
set -e
cd "$(dirname "$0")/.."
cd ..
DEMO=1 ./node_modules/.bin/gulp build-translations gen-icons
cd demo
../node_modules/.bin/webpack-dev-server

View File

@ -0,0 +1,26 @@
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import { Lovelace } from "../../../src/panels/lovelace/types";
import { DemoConfig } from "./types";
export const demoConfigs: Array<() => Promise<DemoConfig>> = [
() => import("./teachingbirds").then((mod) => mod.demoTeachingbirds),
() => import("./kernehed").then((mod) => mod.demoKernehed),
() => import("./jimpower").then((mod) => mod.demoJimpower),
];
export let selectedDemoConfigIndex: number = 0;
export let selectedDemoConfig: Promise<DemoConfig> = demoConfigs[
selectedDemoConfigIndex
]();
export const setDemoConfig = async (
hass: MockHomeAssistant,
lovelace: Lovelace,
index: number
) => {
selectedDemoConfigIndex = index;
selectedDemoConfig = demoConfigs[index]();
const config = await selectedDemoConfig;
hass.addEntities(config.entities(), true);
lovelace.saveConfig(config.lovelace());
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
import { DemoConfig } from "../types";
import { demoLovelaceJimpower } from "./lovelace";
import { demoEntitiesJimpower } from "./entities";
export const demoJimpower: DemoConfig = {
authorName: "Jimpower",
authorUrl: "https://github.com/JamesMcCarthy79/Home-Assistant-Config",
name: "Kingia Castle",
lovelace: demoLovelaceJimpower,
entities: demoEntitiesJimpower,
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
import { DemoConfig } from "../types";
import { demoLovelaceKernehed } from "./lovelace";
import { demoEntitiesKernehed } from "./entities";
export const demoKernehed: DemoConfig = {
authorName: "Kernehed",
authorUrl: "https://github.com/kernehed",
name: "Hem",
lovelace: demoLovelaceKernehed,
entities: demoEntitiesKernehed,
};

View File

@ -0,0 +1,476 @@
import { LovelaceConfig } from "../../../../src/data/lovelace";
export const demoLovelaceKernehed: () => LovelaceConfig = () => ({
name: "Hem",
resources: [
// {
// url: "/local/custom-lovelace/monster-card.js",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/mini-media-player-bundle.js?v=0.9.8",
// type: "module",
// },
// {
// url: "/local/custom-lovelace/slideshow-card.js?=1.1.0",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/fold-entity-row.js?v=3ae2c4",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/swipe-card/swipe-card.js?v=2.0.0",
// type: "module",
// },
// {
// url: "/local/custom-lovelace/upcoming-media-card/upcoming-media-card.js",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/tracker-card.js?v=0.1.5",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/card-tools.js?v=6ce5d0",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/krisinfo.js?=0.0.1",
// type: "js",
// },
],
views: [
{
cards: [
{ type: "custom:ha-demo-card" },
{
cards: [
{
cards: [
{
image: "/assets/kernehed/oscar.jpg",
elements: [
{
style: {
color: "white",
top: "93%",
left: "20%",
},
type: "state-label",
entity: "sensor.oskar_devices",
},
{
style: {
color: "white",
top: "93%",
left: "90%",
},
type: "state-label",
entity: "sensor.battery_oskar",
},
{
style: {
color: "white",
top: "93%",
left: "55%",
},
type: "state-label",
entity: "sensor.oskar_tid_till_hem",
},
],
type: "picture-elements",
},
{
image: "/assets/kernehed/bella.jpg",
elements: [
{
style: {
color: "white",
top: "92%",
left: "20%",
},
type: "state-label",
entity: "sensor.bella_devices",
},
{
style: {
color: "white",
top: "92%",
left: "90%",
},
type: "state-label",
entity: "sensor.battery_bella",
},
{
style: {
color: "white",
top: "92%",
left: "55%",
},
type: "state-label",
entity: "sensor.bella_tid_till_hem",
},
],
type: "picture-elements",
},
],
type: "horizontal-stack",
},
],
type: "vertical-stack",
},
{
entities: [
"lock.polycontrol_danalock_v3_btze_locked",
"sensor.zwave_battery_front_door",
"alarm_control_panel.kernehed_manison",
"binary_sensor.dorrklockan",
],
show_header_toggle: false,
type: "entities",
title: "Lock",
},
// {
// filter: {
// exclude: [
// {
// state: "not_home",
// },
// ],
// include: [
// {
// entity_id: "device_tracker.annasiphone",
// },
// {
// entity_id: "device_tracker.iphone_2",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "G\u00e4ster",
// },
// show_empty: false,
// },
// {
// filter: {
// exclude: [
// {
// state: "Inget",
// },
// {
// state: "i.u.",
// },
// ],
// include: [
// {
// entity_id: "sensor.pollen_al",
// },
// {
// entity_id: "sensor.pollen_alm",
// },
// {
// entity_id: "sensor.pollen_salg_vide",
// },
// {
// entity_id: "sensor.pollen_bjork",
// },
// {
// entity_id: "sensor.pollen_bok",
// },
// {
// entity_id: "sensor.pollen_ek",
// },
// {
// entity_id: "sensor.pollen_grabo",
// },
// {
// entity_id: "sensor.pollen_gras",
// },
// {
// entity_id: "sensor.pollen_hassel",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "Pollenniv\u00e5er",
// },
// show_empty: false,
// },
{
cards: [
{
entities: [
"switch.rest_julbelysning",
"binary_sensor.front_door_sensor",
"binary_sensor.unifi_camera",
"binary_sensor.back_door_sensor",
],
image: "/assets/kernehed/camera.entre.jpg",
type: "picture-glance",
title: "Entrance camera",
},
{
entities: [
"input_select.christmas_pattern",
"input_select.christmas_palette",
],
type: "entities",
},
],
type: "vertical-stack",
},
// {
// url: "https://embed.windy.com/embed2.html",
// type: "iframe",
// },
{
entities: [
{
name: "Laundry sensor",
entity: "binary_sensor.tvattstugan_motion_sensor",
},
{
name: "Pantry sensor",
entity: "binary_sensor.skafferiet_motion_sensor",
},
{
name: "Basement sensor",
entity: "binary_sensor.kallaren_motion_sensor",
},
{
name: "Stair sensor",
entity: "binary_sensor.trapp_motion_sensor",
},
{
name: "Bench sensor",
entity: "binary_sensor.banksensor",
},
{
name: "Porch sensor",
entity: "binary_sensor.altan_motion_sensor",
},
{
name: "Bathroom sensor",
entity: "binary_sensor.badrumssensor",
},
],
type: "glance",
show_state: false,
},
{
entities: ["sensor.oskar_bluetooth"],
show_header_toggle: false,
type: "entities",
title: "Occupancy",
},
// {
// filter: {
// exclude: [
// {
// state: false,
// },
// ],
// include: [
// {
// entity_id:
// "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_2",
// },
// {
// entity_id:
// "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_3",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "Brandvarnare",
// },
// show_empty: false,
// },
{
type: "weather-forecast",
entity: "weather.smhi_vader",
},
// {
// cards: [
// {
// max: 50,
// min: -50,
// type: "gauge",
// title: "\u00d6verv\u00e5ning",
// entity:
// "sensor.fibaro_system_unknown_type0c02_id1003_temperature",
// },
// {
// max: 50,
// min: -50,
// type: "gauge",
// title: "Entr\u00e9n",
// entity:
// "sensor.fibaro_system_unknown_type0c02_id1003_temperature_2",
// },
// {
// max: 50,
// min: -50,
// type: "gauge",
// title: "K\u00e4llaren",
// entity:
// "sensor.philio_technology_corporation_phpat02beu_multisensor_2in1_temperature",
// },
// ],
// type: "custom:slideshow-card",
// arrow_color: "var(--primary-text-color)",
// arrow_opacity: 0.7,
// },
],
title: "Home",
path: "home",
icon: "mdi:home",
},
{
cards: [
{
entities: [
"sensor.processor_use",
"sensor.memory_free",
"sensor.disk_free_home",
"sensor.last_boot",
"sensor.db_size",
],
show_header_toggle: false,
type: "entities",
title: "System",
},
{
entities: [
"sensor.pi_hole_dns_queries_today",
"sensor.pi_hole_ads_blocked_today",
"sensor.pi_hole_dns_unique_clients",
],
show_header_toggle: false,
type: "entities",
title: "Pi-Hole",
},
{
entities: [
"sensor.plex",
"binary_sensor.gaming_pc",
"binary_sensor.server_1",
"binary_sensor.server_2",
"binary_sensor.windows_server",
"binary_sensor.teamspeak",
"binary_sensor.harmony_hub",
{
style: {
height: "1px",
width: "85%",
"margin-left": "auto",
background: "#62717b",
"margin-right": "auto",
},
type: "divider",
},
// {
// items: ["sensor.uptime_router", "sensor.installerad_routeros"],
// head: {
// entity: "binary_sensor.router",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "mdi:router",
// },
// },
// {
// items: [
// "sensor.uptime_router_server",
// "sensor.installerad_routeros_server",
// ],
// head: {
// entity: "binary_sensor.router_server",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "mdi:router",
// },
// },
],
show_header_toggle: false,
type: "entities",
title: "Network",
},
{
entities: [
"binary_sensor.ubiquiti_controller",
"binary_sensor.ubiquiti_switch",
"binary_sensor.ubiquiti_nvr",
"binary_sensor.entre_kamera",
// {
// items: ["sensor.uptime_ap_1"],
// head: {
// entity: "binary_sensor.accesspunkt_1",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "router-wireless",
// },
// },
// {
// items: ["sensor.uptime_ap_2"],
// head: {
// entity: "binary_sensor.accesspunkt_2",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "router-wireless",
// },
// },
"sensor.total_clients_wireless",
],
show_header_toggle: false,
type: "entities",
title: "Ubiquiti",
},
{
entities: [
"sensor.qbittorrent_up_speed",
"sensor.qbittorrent_down_speed",
"sensor.qbittorrent_status",
],
show_header_toggle: false,
type: "entities",
title: "Bittorrent",
},
{
entities: [
"sensor.speedtest_download",
"sensor.speedtest_upload",
"sensor.speedtest_ping",
],
show_header_toggle: false,
type: "entities",
title: "Bandbredd",
},
// {
// title: "Updater",
// type: "custom:tracker-card",
// trackers: [
// "sensor.custom_card_tracker",
// "sensor.custom_component_tracker",
// ],
// },
],
title: "System & Network",
path: "system_network",
icon: "mdi:server-network",
},
],
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
import { DemoConfig } from "../types";
import { demoLovelaceTeachingbirds } from "./lovelace";
import { demoEntitiesTeachingbirds } from "./entities";
export const demoTeachingbirds: DemoConfig = {
authorName: "Isabella Gross Alström",
authorUrl: "https://github.com/isabellaalstrom/",
name: "Isa's mobile friendly LL",
lovelace: demoLovelaceTeachingbirds,
entities: demoEntitiesTeachingbirds,
};

File diff suppressed because it is too large Load Diff

11
demo/src/configs/types.ts Normal file
View File

@ -0,0 +1,11 @@
import { LovelaceConfig } from "../../../src/data/lovelace";
import { Entity } from "../../../src/fake_data/entity";
export interface DemoConfig {
index?: number;
name: string;
authorName: string;
authorUrl: string;
lovelace: () => LovelaceConfig;
entities: () => Entity[];
}

View File

@ -0,0 +1,86 @@
import { LitElement } from "lit-element";
import "./card-tools";
class CardModder extends LitElement {
setConfig(config) {
if (!window.cardTools)
throw new Error(
`Can't find card-tools. See https://github.com/thomasloven/lovelace-card-tools`
);
window.cardTools.checkVersion(0.2);
if (!config || !config.card) {
throw new Error("Card config incorrect");
}
if (Array.isArray(config.card)) {
throw new Error("It says 'card', not 'cardS'. Remove the dash.");
}
this._config = config;
this.card = window.cardTools.createCard(config.card);
this.templated = [];
this.attempts = 0;
}
render() {
return window.cardTools.litHtml`
<div id="root">${this.card}</div>
`;
}
firstUpdated() {
this._cardMod();
}
_cardMod() {
if (!this._config.style) return;
let target = null;
target = target || this.card.querySelector("ha-card");
target =
target ||
(this.card.shadowRoot && this.card.shadowRoot.querySelector("ha-card"));
target =
target ||
(this.card.firstChild &&
this.card.firstChild.shadowRoot &&
this.card.firstChild.shadowRoot.querySelector("ha-card"));
if (!target && !this.attempts)
// Try twice
setTimeout(() => this._cardMod(), 100);
this.attempts++;
target = target || this.card;
for (var k in this._config.style) {
if (window.cardTools.hasTemplate(this._config.style[k]))
this.templated.push(k);
target.style.setProperty(
k,
window.cardTools.parseTemplate(this._config.style[k])
);
}
this.target = target;
}
set hass(hass) {
if (this.card) this.card.hass = hass;
if (this.templated)
this.templated.forEach((k) => {
this.target.style.setProperty(
k,
window.cardTools.parseTemplate(this._config.style[k], "")
);
});
}
getCardSize() {
if (this._config && this._config.report_size)
return this._config.report_size;
if (this.card)
return typeof this.card.getCardSize === "function"
? this.card.getCardSize()
: 1;
return 1;
}
}
customElements.define("card-modder", CardModder);

View File

@ -0,0 +1,197 @@
import { LitElement, html } from "lit-element";
if (!window.cardTools) {
const version = 0.2;
const CUSTOM_TYPE_PREFIX = "custom:";
let cardTools = {};
cardTools.v = version;
cardTools.checkVersion = (v) => {
if (version < v) {
throw new Error(
`Old version of card-tools found. Get the latest version of card-tools.js from https://github.com/thomasloven/lovelace-card-tools`
);
}
};
cardTools.LitElement = LitElement;
cardTools.litHtml = html;
cardTools.hass = () => {
return document.querySelector("home-assistant").hass;
};
cardTools.fireEvent = (ev, detail) => {
ev = new Event(ev, {
bubbles: true,
cancelable: false,
composed: true,
});
ev.detail = detail || {};
document.querySelector("ha-demo").dispatchEvent(ev);
};
cardTools.createThing = (thing, config) => {
const _createThing = (tag, config) => {
const element = document.createElement(tag);
try {
element.setConfig(config);
} catch (err) {
console.error(tag, err);
return _createError(err.message, config);
}
return element;
};
const _createError = (error, config) => {
return _createThing("hui-error-card", {
type: "error",
error,
config,
});
};
if (!config || typeof config !== "object" || !config.type)
return _createError(`No ${thing} type configured`, config);
let tag = config.type;
if (config.error) {
const err = config.error;
delete config.error;
return _createError(err, config);
}
if (tag.startsWith(CUSTOM_TYPE_PREFIX))
tag = tag.substr(CUSTOM_TYPE_PREFIX.length);
else tag = `hui-${tag}-${thing}`;
if (customElements.get(tag)) return _createThing(tag, config);
// If element doesn't exist (yet) create an error
const element = _createError(
`Custom element doesn't exist: ${tag}.`,
config
);
element.style.display = "None";
const time = setTimeout(() => {
element.style.display = "";
}, 2000);
// Remove error if element is defined later
customElements.whenDefined(tag).then(() => {
clearTimeout(timer);
cardTools.fireEvent("rebuild-view");
});
return element;
};
cardTools.createCard = (config) => {
return cardTools.createThing("card", config);
};
cardTools.createElement = (config) => {
return cardTools.createThing("element", config);
};
cardTools.createEntityRow = (config) => {
const SPECIAL_TYPES = new Set([
"call-service",
"divider",
"section",
"weblink",
]);
const DEFAULT_ROWS = {
alert: "toggle",
automation: "toggle",
climate: "toggle",
cover: "cover",
fan: "toggle",
group: "group",
input_boolean: "toggle",
input_number: "input-number",
input_select: "input-select",
input_text: "input-text",
light: "toggle",
media_player: "media-player",
lock: "lock",
scene: "scene",
script: "script",
sensor: "sensor",
timer: "timer",
switch: "toggle",
vacuum: "toggle",
};
if (
!config ||
typeof config !== "object" ||
(!config.entity && !config.type)
) {
Object.assign(config, { error: "Invalid config given" });
return cardTools.createThing("", config);
}
const type = config.type || "default";
if (SPECIAL_TYPES.has(type) || type.startsWith(CUSTOM_TYPE_PREFIX))
return cardTools.createThing("row", config);
const domain = config.entity.split(".", 1)[0];
Object.assign(config, { type: DEFAULT_ROWS[domain] || "text" });
return cardTools.createThing("entity-row", config);
};
cardTools.deviceID = (() => {
const ID_STORAGE_KEY = "lovelace-player-device-id";
if (window["fully"] && typeof fully.getDeviceId === "function")
return fully.getDeviceId();
if (!localStorage[ID_STORAGE_KEY]) {
const s4 = () => {
return Math.floor((1 + Math.random()) * 100000)
.toString(16)
.substring(1);
};
localStorage[ID_STORAGE_KEY] = `${s4()}${s4()}-${s4()}${s4()}`;
}
return localStorage[ID_STORAGE_KEY];
})();
cardTools.moreInfo = (entity) => {
cardTools.fireEvent("hass-more-info", { entityId: entity });
};
cardTools.longpress = (element) => {
customElements.whenDefined("long-press").then(() => {
const longpress = document.body.querySelector("long-press");
longpress.bind(element);
});
return element;
};
cardTools.hasTemplate = (text) => {
return /\[\[\s+.*\s+\]\]/.test(text);
};
cardTools.parseTemplate = (text, error) => {
const _parse = (str) => {
try {
str = str.replace(/^\[\[\s+|\s+\]\]$/g, "");
const parts = str.split(".");
let v = cardTools.hass().states[`${parts[0]}.${parts[1]}`];
parts.shift();
parts.shift();
parts.forEach((item) => (v = v[item]));
return v;
} catch (err) {
return error || `[[ Template matching failed ${str} ]]`;
}
};
text = text.replace(/(\[\[\s.*?\s\]\])/g, (str, p1, offset, s) =>
_parse(str)
);
return text;
};
window.cardTools = cardTools;
cardTools.fireEvent("rebuild-view");
}

View File

@ -0,0 +1,139 @@
import { LitElement, html, CSSResult, css } from "lit-element";
import { until } from "lit-html/directives/until";
import "@polymer/paper-icon-button";
import "../../../src/components/ha-card";
import { LovelaceCard, Lovelace } from "../../../src/panels/lovelace/types";
import { LovelaceCardConfig } from "../../../src/data/lovelace";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import {
demoConfigs,
selectedDemoConfig,
setDemoConfig,
selectedDemoConfigIndex,
} from "../configs/demo-configs";
export class HADemoCard extends LitElement implements LovelaceCard {
public lovelace?: Lovelace;
public hass?: MockHomeAssistant;
public getCardSize() {
return 2;
}
public setConfig(
// @ts-ignore
config: LovelaceCardConfig
// tslint:disable-next-line:no-empty
) {}
protected render() {
return html`
<ha-card header="Home Assistant Demo Switcher">
<div class="picker">
<paper-icon-button
@click=${this._prevConfig}
icon="hass:chevron-right"
style="transform: rotate(180deg)"
></paper-icon-button>
<div>
${
until(
selectedDemoConfig.then(
(conf) => html`
${conf.name}
<small>
by
<a target="_blank" href="${conf.authorUrl}">
${conf.authorName}
</a>
</small>
`
),
""
)
}
</div>
<paper-icon-button
@click=${this._nextConfig}
icon="hass:chevron-right"
></paper-icon-button>
</div>
</ha-card>
`;
}
private _prevConfig() {
this._updateConfig(
selectedDemoConfigIndex > 0
? selectedDemoConfigIndex - 1
: demoConfigs.length - 1
);
}
private _nextConfig() {
this._updateConfig(
selectedDemoConfigIndex < demoConfigs.length - 1
? selectedDemoConfigIndex + 1
: 0
);
}
private _updateConfig(index: number) {
setDemoConfig(this.hass!, this.lovelace!, index);
}
static get styles(): CSSResult[] {
return [
css`
.content {
padding: 0 16px;
}
ul {
margin-top: 0;
margin-bottom: 0;
padding: 16px 16px 16px 38px;
}
li {
padding: 8px 0;
}
li:first-child {
margin-top: -8px;
}
li:last-child {
margin-bottom: -8px;
}
a {
color: var(--primary-color);
}
.picker {
display: flex;
justify-content: space-between;
align-items: center;
height: 60px;
}
.picker div {
text-align: center;
}
.picker small {
display: block;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-demo-card": HADemoCard;
}
}
customElements.define("ha-demo-card", HADemoCard);

19
demo/src/entrypoint.ts Normal file
View File

@ -0,0 +1,19 @@
import "@polymer/paper-styles/typography";
import "@polymer/polymer/lib/elements/dom-if";
import "@polymer/polymer/lib/elements/dom-repeat";
import "../../src/resources/hass-icons";
import "../../src/resources/ha-style";
import "../../src/resources/roboto";
import "../../src/components/ha-iconset-svg";
import "./ha-demo";
/* polyfill for paper-dropdown */
setTimeout(
() =>
import(/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"),
1000
);
document.body.appendChild(document.createElement("ha-demo"));

80
demo/src/ha-demo.ts Normal file
View File

@ -0,0 +1,80 @@
import { HomeAssistant } from "../../src/layouts/app/home-assistant";
import { provideHass } from "../../src/fake_data/provide_hass";
import { navigate } from "../../src/common/navigate";
import { mockLovelace } from "./stubs/lovelace";
import { mockAuth } from "./stubs/auth";
import { selectedDemoConfig } from "./configs/demo-configs";
import { mockTranslations } from "./stubs/translations";
import { mockHistory } from "./stubs/history";
import { mockShoppingList } from "./stubs/shopping_list";
class HaDemo extends HomeAssistant {
protected async _handleConnProm() {
const initial: Partial<HomeAssistant> = {
panelUrl: (this as any).panelUrl,
};
const hass = provideHass(this, initial);
mockLovelace(hass);
mockAuth(hass);
mockTranslations(hass);
mockHistory(hass);
mockShoppingList(hass);
selectedDemoConfig.then((conf) => hass.addEntities(conf.entities()));
// Taken from polymer/pwa-helpers. BSD-3 licensed
document.body.addEventListener(
"click",
(e) => {
if (
e.defaultPrevented ||
e.button !== 0 ||
e.metaKey ||
e.ctrlKey ||
e.shiftKey
) {
return;
}
const anchor = e
.composedPath()
.filter((n) => (n as HTMLElement).tagName === "A")[0] as
| HTMLAnchorElement
| undefined;
if (
!anchor ||
anchor.target ||
anchor.hasAttribute("download") ||
anchor.getAttribute("rel") === "external"
) {
return;
}
let href = anchor.href;
if (!href || href.indexOf("mailto:") !== -1) {
return;
}
const location = window.location;
const origin =
location.origin || location.protocol + "//" + location.host;
if (href.indexOf(origin) !== 0) {
return;
}
href = href.substr(origin.length);
if (href === "#") {
return;
}
e.preventDefault();
navigate(this as any, href);
},
{ capture: true }
);
(this as any).hassConnected();
}
}
customElements.define("ha-demo", HaDemo);

6
demo/src/stubs/auth.ts Normal file
View File

@ -0,0 +1,6 @@
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockAuth = (hass: MockHomeAssistant) => {
hass.mockWS("config/auth/list", () => []);
hass.mockWS("auth/refresh_tokens", () => []);
};

View File

@ -0,0 +1,5 @@
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockHistory = (hass: MockHomeAssistant) => {
hass.mockAPI(new RegExp("history/period/.+"), () => []);
};

View File

@ -0,0 +1,28 @@
import "../custom-cards/ha-demo-card";
// Not duplicate, one is for typing.
// tslint:disable-next-line
import { HADemoCard } from "../custom-cards/ha-demo-card";
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import { HUIView } from "../../../src/panels/lovelace/hui-view";
import { selectedDemoConfig } from "../configs/demo-configs";
export const mockLovelace = (hass: MockHomeAssistant) => {
selectedDemoConfig.then((config) => hass.addEntities(config.entities()));
hass.mockWS("lovelace/config", () =>
selectedDemoConfig.then((config) => config.lovelace())
);
hass.mockWS("lovelace/config/save", () => Promise.resolve());
};
// Patch HUI-VIEW to make the lovelace object available to the demo card
const oldCreateCard = HUIView.prototype.createCardElement;
HUIView.prototype.createCardElement = function(config) {
const el = oldCreateCard.call(this, config);
if (el.tagName === "HA-DEMO-CARD") {
(el as HADemoCard).lovelace = this.lovelace;
}
return el;
};

View File

@ -0,0 +1,44 @@
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
import { ShoppingListItem } from "../../../src/data/shopping-list";
let items: ShoppingListItem[] = [
{
id: 12,
name: "Milk",
complete: false,
},
{
id: 13,
name: "Eggs",
complete: false,
},
{
id: 14,
name: "Oranges",
complete: true,
},
];
export const mockShoppingList = (hass: MockHomeAssistant) => {
hass.mockWS("shopping_list/items", () => items);
hass.mockWS("shopping_list/items/add", (msg) => {
const item: ShoppingListItem = {
id: new Date().getTime(),
complete: false,
name: msg.name,
};
items.push(item);
hass.mockEvent("shopping_list_updated");
return item;
});
hass.mockWS("shopping_list/items/update", ({ type, item_id, ...updates }) => {
items = items.map((item) =>
item.id === item_id ? { ...item, ...updates } : item
);
hass.mockEvent("shopping_list_updated");
});
hass.mockWS("shopping_list/items/clear", () => {
items = items.filter((item) => !item.complete);
hass.mockEvent("shopping_list_updated");
});
};

View File

@ -0,0 +1,474 @@
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockTranslations = (hass: MockHomeAssistant) => {
hass.mockWS("frontend/get_translations", () => ({
resources: {
"component.lifx.config.abort.no_devices_found":
"No LIFX devices found on the network.",
"component.lifx.config.abort.single_instance_allowed":
"Only a single configuration of LIFX is possible.",
"component.lifx.config.step.confirm.description":
"Do you want to set up LIFX?",
"component.lifx.config.step.confirm.title": "LIFX",
"component.lifx.config.title": "LIFX",
"component.hangouts.config.abort.already_configured":
"Google Hangouts is already configured",
"component.hangouts.config.abort.unknown": "Unknown error occurred.",
"component.hangouts.config.error.invalid_2fa":
"Invalid 2 Factor Authentication, please try again.",
"component.hangouts.config.error.invalid_2fa_method":
"Invalid 2FA Method (Verify on Phone).",
"component.hangouts.config.error.invalid_login":
"Invalid Login, please try again.",
"component.hangouts.config.step.2fa.data.2fa": "2FA Pin",
"component.hangouts.config.step.2fa.title": "2-Factor-Authentication",
"component.hangouts.config.step.user.data.email": "E-Mail Address",
"component.hangouts.config.step.user.data.password": "Password",
"component.hangouts.config.step.user.title": "Google Hangouts Login",
"component.hangouts.config.title": "Google Hangouts",
"component.rainmachine.config.error.identifier_exists":
"Account already registered",
"component.rainmachine.config.error.invalid_credentials":
"Invalid credentials",
"component.rainmachine.config.step.user.data.ip_address":
"Hostname or IP Address",
"component.rainmachine.config.step.user.data.password": "Password",
"component.rainmachine.config.step.user.data.port": "Port",
"component.rainmachine.config.step.user.title":
"Fill in your information",
"component.rainmachine.config.title": "RainMachine",
"component.homematicip_cloud.config.abort.already_configured":
"Access point is already configured",
"component.homematicip_cloud.config.abort.connection_aborted":
"Could not connect to HMIP server",
"component.homematicip_cloud.config.abort.unknown":
"Unknown error occurred.",
"component.homematicip_cloud.config.error.invalid_pin":
"Invalid PIN, please try again.",
"component.homematicip_cloud.config.error.press_the_button":
"Please press the blue button.",
"component.homematicip_cloud.config.error.register_failed":
"Failed to register, please try again.",
"component.homematicip_cloud.config.error.timeout_button":
"Blue button press timeout, please try again.",
"component.homematicip_cloud.config.step.init.data.hapid":
"Access point ID (SGTIN)",
"component.homematicip_cloud.config.step.init.data.name":
"Name (optional, used as name prefix for all devices)",
"component.homematicip_cloud.config.step.init.data.pin":
"Pin Code (optional)",
"component.homematicip_cloud.config.step.init.title":
"Pick HomematicIP Access point",
"component.homematicip_cloud.config.step.link.description":
"Press the blue button on the access point and the submit button to register HomematicIP with Home Assistant.\n\n![Location of button on bridge](/static/images/config_flows/config_homematicip_cloud.png)",
"component.homematicip_cloud.config.step.link.title": "Link Access point",
"component.homematicip_cloud.config.title": "HomematicIP Cloud",
"component.daikin.config.abort.already_configured":
"Device is already configured",
"component.daikin.config.abort.device_fail":
"Unexpected error creating device.",
"component.daikin.config.abort.device_timeout":
"Timeout connecting to the device.",
"component.daikin.config.step.user.data.host": "Host",
"component.daikin.config.step.user.description":
"Enter IP address of your Daikin AC.",
"component.daikin.config.step.user.title": "Configure Daikin AC",
"component.daikin.config.title": "Daikin AC",
"component.unifi.config.abort.already_configured":
"Controller site is already configured",
"component.unifi.config.abort.user_privilege":
"User needs to be administrator",
"component.unifi.config.error.faulty_credentials": "Bad user credentials",
"component.unifi.config.error.service_unavailable":
"No service available",
"component.unifi.config.step.user.data.host": "Host",
"component.unifi.config.step.user.data.password": "Password",
"component.unifi.config.step.user.data.port": "Port",
"component.unifi.config.step.user.data.site": "Site ID",
"component.unifi.config.step.user.data.username": "User name",
"component.unifi.config.step.user.data.verify_ssl":
"Controller using proper certificate",
"component.unifi.config.step.user.title": "Set up UniFi Controller",
"component.unifi.config.title": "UniFi Controller",
"component.nest.config.abort.already_setup":
"You can only configure a single Nest account.",
"component.nest.config.abort.authorize_url_fail":
"Unknown error generating an authorize url.",
"component.nest.config.abort.authorize_url_timeout":
"Timeout generating authorize url.",
"component.nest.config.abort.no_flows":
"You need to configure Nest before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/nest/).",
"component.nest.config.error.internal_error":
"Internal error validating code",
"component.nest.config.error.invalid_code": "Invalid code",
"component.nest.config.error.timeout": "Timeout validating code",
"component.nest.config.error.unknown": "Unknown error validating code",
"component.nest.config.step.init.data.flow_impl": "Provider",
"component.nest.config.step.init.description":
"Pick via which authentication provider you want to authenticate with Nest.",
"component.nest.config.step.init.title": "Authentication Provider",
"component.nest.config.step.link.data.code": "Pin code",
"component.nest.config.step.link.description":
"To link your Nest account, [authorize your account]({url}).\n\nAfter authorization, copy-paste the provided pin code below.",
"component.nest.config.step.link.title": "Link Nest Account",
"component.nest.config.title": "Nest",
"component.mailgun.config.abort.not_internet_accessible":
"Your Home Assistant instance needs to be accessible from the internet to receive Mailgun messages.",
"component.mailgun.config.abort.one_instance_allowed":
"Only a single instance is necessary.",
"component.mailgun.config.create_entry.default":
"To send events to Home Assistant, you will need to setup [Webhooks with Mailgun]({mailgun_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) on how to configure automations to handle incoming data.",
"component.mailgun.config.step.user.description":
"Are you sure you want to set up Mailgun?",
"component.mailgun.config.step.user.title": "Set up the Mailgun Webhook",
"component.mailgun.config.title": "Mailgun",
"component.tellduslive.config.abort.already_setup":
"TelldusLive is already configured",
"component.tellduslive.config.abort.authorize_url_fail":
"Unknown error generating an authorize url.",
"component.tellduslive.config.abort.authorize_url_timeout":
"Timeout generating authorize url.",
"component.tellduslive.config.abort.unknown": "Unknown error occurred",
"component.tellduslive.config.error.auth_error":
"Authentication error, please try again",
"component.tellduslive.config.step.auth.description":
"To link your TelldusLive account:\n 1. Click the link below\n 2. Login to Telldus Live\n 3. Authorize **{app_name}** (click **Yes**).\n 4. Come back here and click **SUBMIT**.\n\n [Link TelldusLive account]({auth_url})",
"component.tellduslive.config.step.auth.title":
"Authenticate against TelldusLive",
"component.tellduslive.config.step.user.data.host": "Host",
"component.tellduslive.config.step.user.title": "Pick endpoint.",
"component.tellduslive.config.title": "Telldus Live",
"component.esphome.config.abort.already_configured":
"ESP is already configured",
"component.esphome.config.error.connection_error":
"Can't connect to ESP. Please make sure your YAML file contains an 'api:' line.",
"component.esphome.config.error.invalid_password": "Invalid password!",
"component.esphome.config.error.resolve_error":
"Can't resolve address of the ESP. If this error persists, please set a static IP address: https://esphomelib.com/esphomeyaml/components/wifi.html#manual-ips",
"component.esphome.config.step.authenticate.data.password": "Password",
"component.esphome.config.step.authenticate.description":
"Please enter the password you set in your configuration.",
"component.esphome.config.step.authenticate.title": "Enter Password",
"component.esphome.config.step.user.data.host": "Host",
"component.esphome.config.step.user.data.port": "Port",
"component.esphome.config.step.user.description":
"Please enter connection settings of your [ESPHome](https://esphomelib.com/) node.",
"component.esphome.config.step.user.title": "ESPHome",
"component.esphome.config.title": "ESPHome",
"component.luftdaten.config.error.communication_error":
"Unable to communicate with the Luftdaten API",
"component.luftdaten.config.error.invalid_sensor":
"Sensor not available or invalid",
"component.luftdaten.config.error.sensor_exists":
"Sensor already registered",
"component.luftdaten.config.step.user.data.show_on_map": "Show on map",
"component.luftdaten.config.step.user.data.station_id":
"Luftdaten Sensor ID",
"component.luftdaten.config.step.user.title": "Define Luftdaten",
"component.luftdaten.config.title": "Luftdaten",
"component.upnp.config.abort.already_configured":
"UPnP/IGD is already configured",
"component.upnp.config.abort.incomplete_device":
"Ignoring incomplete UPnP device",
"component.upnp.config.abort.no_devices_discovered":
"No UPnP/IGDs discovered",
"component.upnp.config.abort.no_devices_found":
"No UPnP/IGD devices found on the network.",
"component.upnp.config.abort.no_sensors_or_port_mapping":
"Enable at least sensors or port mapping",
"component.upnp.config.abort.single_instance_allowed":
"Only a single configuration of UPnP/IGD is necessary.",
"component.upnp.config.step.confirm.description":
"Do you want to set up UPnP/IGD?",
"component.upnp.config.step.confirm.title": "UPnP/IGD",
"component.upnp.config.step.init.title": "UPnP/IGD",
"component.upnp.config.step.user.data.enable_port_mapping":
"Enable port mapping for Home Assistant",
"component.upnp.config.step.user.data.enable_sensors":
"Add traffic sensors",
"component.upnp.config.step.user.data.igd": "UPnP/IGD",
"component.upnp.config.step.user.title":
"Configuration options for the UPnP/IGD",
"component.upnp.config.title": "UPnP/IGD",
"component.point.config.abort.already_setup":
"You can only configure a Point account.",
"component.point.config.abort.authorize_url_fail":
"Unknown error generating an authorize url.",
"component.point.config.abort.authorize_url_timeout":
"Timeout generating authorize url.",
"component.point.config.abort.external_setup":
"Point successfully configured from another flow.",
"component.point.config.abort.no_flows":
"You need to configure Point before being able to authenticate with it. [Please read the instructions](https://www.home-assistant.io/components/point/).",
"component.point.config.create_entry.default":
"Successfully authenticated with Minut for your Point device(s)",
"component.point.config.error.follow_link":
"Please follow the link and authenticate before pressing Submit",
"component.point.config.error.no_token": "Not authenticated with Minut",
"component.point.config.step.auth.description":
"Please follow the link below and <b>Accept</b> access to your Minut account, then come back and press <b>Submit</b> below.\n\n[Link]({authorization_url})",
"component.point.config.step.auth.title": "Authenticate Point",
"component.point.config.step.user.data.flow_impl": "Provider",
"component.point.config.step.user.description":
"Pick via which authentication provider you want to authenticate with Point.",
"component.point.config.step.user.title": "Authentication Provider",
"component.point.config.title": "Minut Point",
"component.auth.mfa_setup.notify.abort.no_available_service":
"No notification services available.",
"component.auth.mfa_setup.notify.error.invalid_code":
"Invalid code, please try again.",
"component.auth.mfa_setup.notify.step.init.description":
"Please select one of the notification services:",
"component.auth.mfa_setup.notify.step.init.title":
"Set up one-time password delivered by notify component",
"component.auth.mfa_setup.notify.step.setup.description":
"A one-time password has been sent via **notify.{notify_service}**. Please enter it below:",
"component.auth.mfa_setup.notify.step.setup.title": "Verify setup",
"component.auth.mfa_setup.notify.title": "Notify One-Time Password",
"component.auth.mfa_setup.totp.error.invalid_code":
"Invalid code, please try again. If you get this error consistently, please make sure the clock of your Home Assistant system is accurate.",
"component.auth.mfa_setup.totp.step.init.description":
"To activate two factor authentication using time-based one-time passwords, scan the QR code with your authentication app. If you don't have one, we recommend either [Google Authenticator](https://support.google.com/accounts/answer/1066447) or [Authy](https://authy.com/).\n\n{qr_code}\n\nAfter scanning the code, enter the six digit code from your app to verify the setup. If you have problems scanning the QR code, do a manual setup with code **`{code}`**.",
"component.auth.mfa_setup.totp.step.init.title":
"Set up two-factor authentication using TOTP",
"component.auth.mfa_setup.totp.title": "TOTP",
"component.emulated_roku.config.abort.name_exists": "Name already exists",
"component.emulated_roku.config.step.user.data.advertise_ip":
"Advertise IP",
"component.emulated_roku.config.step.user.data.advertise_port":
"Advertise port",
"component.emulated_roku.config.step.user.data.host_ip": "Host IP",
"component.emulated_roku.config.step.user.data.listen_port":
"Listen port",
"component.emulated_roku.config.step.user.data.name": "Name",
"component.emulated_roku.config.step.user.data.upnp_bind_multicast":
"Bind multicast (True/False)",
"component.emulated_roku.config.step.user.title":
"Define server configuration",
"component.emulated_roku.config.title": "EmulatedRoku",
"component.owntracks.config.abort.one_instance_allowed":
"Only a single instance is necessary.",
"component.owntracks.config.create_entry.default":
"\n\nOn Android, open [the OwnTracks app]({android_url}), go to preferences -> connection. Change the following settings:\n - Mode: Private HTTP\n - Host: {webhook_url}\n - Identification:\n - Username: `<Your name>`\n - Device ID: `<Your device name>`\n\nOn iOS, open [the OwnTracks app]({ios_url}), tap (i) icon in top left -> settings. Change the following settings:\n - Mode: HTTP\n - URL: {webhook_url}\n - Turn on authentication\n - UserID: `<Your name>`\n\n{secret}\n\nSee [the documentation]({docs_url}) for more information.",
"component.owntracks.config.step.user.description":
"Are you sure you want to set up OwnTracks?",
"component.owntracks.config.step.user.title": "Set up OwnTracks",
"component.owntracks.config.title": "OwnTracks",
"component.zone.config.error.name_exists": "Name already exists",
"component.zone.config.step.init.data.icon": "Icon",
"component.zone.config.step.init.data.latitude": "Latitude",
"component.zone.config.step.init.data.longitude": "Longitude",
"component.zone.config.step.init.data.name": "Name",
"component.zone.config.step.init.data.passive": "Passive",
"component.zone.config.step.init.data.radius": "Radius",
"component.zone.config.step.init.title": "Define zone parameters",
"component.zone.config.title": "Zone",
"component.hue.config.abort.all_configured":
"All Philips Hue bridges are already configured",
"component.hue.config.abort.already_configured":
"Bridge is already configured",
"component.hue.config.abort.cannot_connect":
"Unable to connect to the bridge",
"component.hue.config.abort.discover_timeout":
"Unable to discover Hue bridges",
"component.hue.config.abort.no_bridges":
"No Philips Hue bridges discovered",
"component.hue.config.abort.unknown": "Unknown error occurred",
"component.hue.config.error.linking": "Unknown linking error occurred.",
"component.hue.config.error.register_failed":
"Failed to register, please try again",
"component.hue.config.step.init.data.host": "Host",
"component.hue.config.step.init.title": "Pick Hue bridge",
"component.hue.config.step.link.description":
"Press the button on the bridge to register Philips Hue with Home Assistant.\n\n![Location of button on bridge](/static/images/config_philips_hue.jpg)",
"component.hue.config.step.link.title": "Link Hub",
"component.hue.config.title": "Philips Hue",
"component.tradfri.config.abort.already_configured":
"Bridge is already configured",
"component.tradfri.config.error.cannot_connect":
"Unable to connect to the gateway.",
"component.tradfri.config.error.invalid_key":
"Failed to register with provided key. If this keeps happening, try restarting the gateway.",
"component.tradfri.config.error.timeout": "Timeout validating the code.",
"component.tradfri.config.step.auth.data.host": "Host",
"component.tradfri.config.step.auth.data.security_code": "Security Code",
"component.tradfri.config.step.auth.description":
"You can find the security code on the back of your gateway.",
"component.tradfri.config.step.auth.title": "Enter security code",
"component.tradfri.config.title": "IKEA TRÅDFRI",
"component.mqtt.config.abort.single_instance_allowed":
"Only a single configuration of MQTT is allowed.",
"component.mqtt.config.error.cannot_connect":
"Unable to connect to the broker.",
"component.mqtt.config.step.broker.data.broker": "Broker",
"component.mqtt.config.step.broker.data.discovery": "Enable discovery",
"component.mqtt.config.step.broker.data.password": "Password",
"component.mqtt.config.step.broker.data.port": "Port",
"component.mqtt.config.step.broker.data.username": "Username",
"component.mqtt.config.step.broker.description":
"Please enter the connection information of your MQTT broker.",
"component.mqtt.config.step.broker.title": "MQTT",
"component.mqtt.config.step.hassio_confirm.data.discovery":
"Enable discovery",
"component.mqtt.config.step.hassio_confirm.description":
"Do you want to configure Home Assistant to connect to the MQTT broker provided by the hass.io add-on {addon}?",
"component.mqtt.config.step.hassio_confirm.title":
"MQTT Broker via Hass.io add-on",
"component.mqtt.config.title": "MQTT",
"component.geofency.config.abort.not_internet_accessible":
"Your Home Assistant instance needs to be accessible from the internet to receive messages from Geofency.",
"component.geofency.config.abort.one_instance_allowed":
"Only a single instance is necessary.",
"component.geofency.config.create_entry.default":
"To send events to Home Assistant, you will need to setup the webhook feature in Geofency.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details.",
"component.geofency.config.step.user.description":
"Are you sure you want to set up the Geofency Webhook?",
"component.geofency.config.step.user.title":
"Set up the Geofency Webhook",
"component.geofency.config.title": "Geofency Webhook",
"component.simplisafe.config.error.identifier_exists":
"Account already registered",
"component.simplisafe.config.error.invalid_credentials":
"Invalid credentials",
"component.simplisafe.config.step.user.data.code":
"Code (for Home Assistant)",
"component.simplisafe.config.step.user.data.password": "Password",
"component.simplisafe.config.step.user.data.username": "Email Address",
"component.simplisafe.config.step.user.title": "Fill in your information",
"component.simplisafe.config.title": "SimpliSafe",
"component.dialogflow.config.abort.not_internet_accessible":
"Your Home Assistant instance needs to be accessible from the internet to receive Dialogflow messages.",
"component.dialogflow.config.abort.one_instance_allowed":
"Only a single instance is necessary.",
"component.dialogflow.config.create_entry.default":
"To send events to Home Assistant, you will need to setup [webhook integration of Dialogflow]({dialogflow_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) for further details.",
"component.dialogflow.config.step.user.description":
"Are you sure you want to set up Dialogflow?",
"component.dialogflow.config.step.user.title":
"Set up the Dialogflow Webhook",
"component.dialogflow.config.title": "Dialogflow",
"component.deconz.config.abort.already_configured":
"Bridge is already configured",
"component.deconz.config.abort.no_bridges":
"No deCONZ bridges discovered",
"component.deconz.config.abort.one_instance_only":
"Component only supports one deCONZ instance",
"component.deconz.config.error.no_key": "Couldn't get an API key",
"component.deconz.config.step.init.data.host": "Host",
"component.deconz.config.step.init.data.port": "Port",
"component.deconz.config.step.init.title": "Define deCONZ gateway",
"component.deconz.config.step.link.description":
'Unlock your deCONZ gateway to register with Home Assistant.\n\n1. Go to deCONZ system settings\n2. Press "Unlock Gateway" button',
"component.deconz.config.step.link.title": "Link with deCONZ",
"component.deconz.config.step.options.data.allow_clip_sensor":
"Allow importing virtual sensors",
"component.deconz.config.step.options.data.allow_deconz_groups":
"Allow importing deCONZ groups",
"component.deconz.config.step.options.title":
"Extra configuration options for deCONZ",
"component.deconz.config.title": "deCONZ Zigbee gateway",
"component.openuv.config.error.identifier_exists":
"Coordinates already registered",
"component.openuv.config.error.invalid_api_key": "Invalid API key",
"component.openuv.config.step.user.data.api_key": "OpenUV API Key",
"component.openuv.config.step.user.data.elevation": "Elevation",
"component.openuv.config.step.user.data.latitude": "Latitude",
"component.openuv.config.step.user.data.longitude": "Longitude",
"component.openuv.config.step.user.title": "Fill in your information",
"component.openuv.config.title": "OpenUV",
"component.locative.config.title": "Locative Webhook",
"component.locative.config.step.user.title":
"Set up the Locative Webhook",
"component.locative.config.step.user.description":
"Are you sure you want to set up the Locative Webhook?",
"component.locative.config.abort.one_instance_allowed":
"Only a single instance is necessary.",
"component.locative.config.abort.not_internet_accessible":
"Your Home Assistant instance needs to be accessible from the internet to receive messages from Geofency.",
"component.locative.config.create_entry.default":
"To send locations to Home Assistant, you will need to setup the webhook feature in the Locative app.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details.",
"component.ios.config.abort.single_instance_allowed":
"Only a single configuration of Home Assistant iOS is necessary.",
"component.ios.config.step.confirm.description":
"Do you want to set up the Home Assistant iOS component?",
"component.ios.config.step.confirm.title": "Home Assistant iOS",
"component.ios.config.title": "Home Assistant iOS",
"component.smhi.config.error.name_exists": "Name already exists",
"component.smhi.config.error.wrong_location": "Location Sweden only",
"component.smhi.config.step.user.data.latitude": "Latitude",
"component.smhi.config.step.user.data.longitude": "Longitude",
"component.smhi.config.step.user.data.name": "Name",
"component.smhi.config.step.user.title": "Location in Sweden",
"component.smhi.config.title": "Swedish weather service (SMHI)",
"component.sonos.config.abort.no_devices_found":
"No Sonos devices found on the network.",
"component.sonos.config.abort.single_instance_allowed":
"Only a single configuration of Sonos is necessary.",
"component.sonos.config.step.confirm.description":
"Do you want to set up Sonos?",
"component.sonos.config.step.confirm.title": "Sonos",
"component.sonos.config.title": "Sonos",
"component.ifttt.config.abort.not_internet_accessible":
"Your Home Assistant instance needs to be accessible from the internet to receive IFTTT messages.",
"component.ifttt.config.abort.one_instance_allowed":
"Only a single instance is necessary.",
"component.ifttt.config.create_entry.default":
'To send events to Home Assistant, you will need to use the "Make a web request" action from the [IFTTT Webhook applet]({applet_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/json\n\nSee [the documentation]({docs_url}) on how to configure automations to handle incoming data.',
"component.ifttt.config.step.user.description":
"Are you sure you want to set up IFTTT?",
"component.ifttt.config.step.user.title":
"Set up the IFTTT Webhook Applet",
"component.ifttt.config.title": "IFTTT",
"component.twilio.config.abort.not_internet_accessible":
"Your Home Assistant instance needs to be accessible from the internet to receive Twilio messages.",
"component.twilio.config.abort.one_instance_allowed":
"Only a single instance is necessary.",
"component.twilio.config.create_entry.default":
"To send events to Home Assistant, you will need to setup [Webhooks with Twilio]({twilio_url}).\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n- Content Type: application/x-www-form-urlencoded\n\nSee [the documentation]({docs_url}) on how to configure automations to handle incoming data.",
"component.twilio.config.step.user.description":
"Are you sure you want to set up Twilio?",
"component.twilio.config.step.user.title": "Set up the Twilio Webhook",
"component.twilio.config.title": "Twilio",
"component.zha.config.abort.single_instance_allowed":
"Only a single configuration of ZHA is allowed.",
"component.zha.config.error.cannot_connect":
"Unable to connect to ZHA device.",
"component.zha.config.step.user.data.radio_type": "Radio Type",
"component.zha.config.step.user.data.usb_path": "USB Device Path",
"component.zha.config.step.user.title": "ZHA",
"component.zha.config.title": "ZHA",
"component.gpslogger.config.title": "GPSLogger Webhook",
"component.gpslogger.config.step.user.title":
"Set up the GPSLogger Webhook",
"component.gpslogger.config.step.user.description":
"Are you sure you want to set up the GPSLogger Webhook?",
"component.gpslogger.config.abort.one_instance_allowed":
"Only a single instance is necessary.",
"component.gpslogger.config.abort.not_internet_accessible":
"Your Home Assistant instance needs to be accessible from the internet to receive messages from GPSLogger.",
"component.gpslogger.config.create_entry.default":
"To send events to Home Assistant, you will need to setup the webhook feature in GPSLogger.\n\nFill in the following info:\n\n- URL: `{webhook_url}`\n- Method: POST\n\nSee [the documentation]({docs_url}) for further details.",
"component.zwave.config.abort.already_configured":
"Z-Wave is already configured",
"component.zwave.config.abort.one_instance_only":
"Component only supports one Z-Wave instance",
"component.zwave.config.error.option_error":
"Z-Wave validation failed. Is the path to the USB stick correct?",
"component.zwave.config.step.user.data.network_key":
"Network Key (leave blank to auto-generate)",
"component.zwave.config.step.user.data.usb_path": "USB Path",
"component.zwave.config.step.user.description":
"See https://www.home-assistant.io/docs/z-wave/installation/ for information on the configuration variables",
"component.zwave.config.step.user.title": "Set up Z-Wave",
"component.zwave.config.title": "Z-Wave",
"component.cast.config.abort.no_devices_found":
"No Google Cast devices found on the network.",
"component.cast.config.abort.single_instance_allowed":
"Only a single configuration of Google Cast is necessary.",
"component.cast.config.step.confirm.description":
"Do you want to set up Google Cast?",
"component.cast.config.step.confirm.title": "Google Cast",
"component.cast.config.title": "Google Cast",
},
}));
};

100
demo/webpack.config.js Normal file
View File

@ -0,0 +1,100 @@
const path = require("path");
const webpack = require("webpack");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const { babelLoaderConfig } = require("../config/babel.js");
const isProd = process.env.NODE_ENV === "production";
const chunkFilename = isProd ? "chunk.[chunkhash].js" : "[name].chunk.js";
const buildPath = path.resolve(__dirname, "dist");
const publicPath = isProd ? "./" : "http://localhost:8080/";
const latestBuild = false;
module.exports = {
mode: isProd ? "production" : "development",
// Disabled in prod while we make Home Assistant able to serve the right files.
// Was source-map
devtool: isProd ? "none" : "inline-source-map",
entry: {
main: "./src/entrypoint.ts",
compatibility: "../src/entrypoints/compatibility.js",
},
module: {
rules: [
babelLoaderConfig({ latestBuild }),
{
test: /\.css$/,
use: "raw-loader",
},
{
test: /\.(html)$/,
use: {
loader: "html-loader",
options: {
exportAsEs6Default: true,
},
},
},
],
},
plugins: [
new webpack.DefinePlugin({
__DEV__: false,
__BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
__VERSION__: JSON.stringify("DEMO"),
__DEMO__: true,
__STATIC_PATH__: "/static/",
"process.env.NODE_ENV": JSON.stringify(
isProd ? "production" : "development"
),
}),
new CopyWebpackPlugin([
"public",
"../node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js",
{ from: "../public", to: "static" },
{ from: "../build-translations/output", to: "static/translations" },
{
from: "../node_modules/leaflet/dist/leaflet.css",
to: "static/images/leaflet/",
},
{
from: "../node_modules/@polymer/font-roboto-local/fonts",
to: "static/fonts",
},
{
from: "../node_modules/leaflet/dist/images",
to: "static/images/leaflet/",
},
]),
isProd &&
new UglifyJsPlugin({
extractComments: true,
sourceMap: true,
uglifyOptions: {
// Disabling because it broke output
mangle: false,
},
}),
].filter(Boolean),
resolve: {
extensions: [".ts", ".js", ".json"],
alias: {
react: "preact-compat",
"react-dom": "preact-compat",
// Not necessary unless you consume a module using `createClass`
"create-react-class": "preact-compat/lib/create-react-class",
// Not necessary unless you consume a module requiring `react-dom-factories`
"react-dom-factories": "preact-compat/lib/react-dom-factories",
},
},
output: {
filename: "[name].js",
chunkFilename: chunkFilename,
path: buildPath,
publicPath,
},
devServer: {
contentBase: "./public",
},
};

View File

@ -1,13 +0,0 @@
import config from './config_data';
import events from './event_data';
import services from './service_data';
import states from './state_data';
import panels from './panel_data';
export default {
config,
events,
panels,
services,
states,
};

View File

@ -1,18 +0,0 @@
export default {
components: [
'configurator',
'http',
'api',
'frontend',
'history',
'conversation',
'logbook',
'introduction',
],
latitude: 32.87336,
location_name: 'Home',
longitude: -117.22743,
temperature_unit: '\u00b0F',
time_zone: 'America/Los_Angeles',
version: '0.26',
};

View File

@ -1,5 +0,0 @@
import bootstrap from './bootstrap_data';
import logbook from './logbook_data';
import stateHistory from './state_history_data';
window.hassDemoData = { bootstrap, logbook, stateHistory };

View File

@ -1,18 +0,0 @@
export default [
{
event: 'call_service',
listener_count: 1,
},
{
event: 'time_changed',
listener_count: 1,
},
{
event: 'state_changed',
listener_count: 3,
},
{
event: 'homeassistant_stop',
listener_count: 2,
},
];

View File

@ -1,93 +0,0 @@
export default [
{
domain: 'sun',
entity_id: 'sun.sun',
message: 'has risen',
name: 'sun',
when: '2015-04-24T06:08:47.000Z',
},
{
domain: 'device_tracker',
entity_id: 'device_tracker.paulus',
message: 'left home',
name: 'Paulus',
when: '2015-04-24T08:54:47.000Z',
},
{
domain: 'device_tracker',
entity_id: 'device_tracker.anne_therese',
message: 'left home',
name: 'Anne Therese',
when: '2015-04-24T09:08:47.000Z',
},
{
domain: 'group',
entity_id: 'group.all_devices',
message: 'left home',
name: 'All devices',
when: '2015-04-24T09:08:47.000Z',
},
{
domain: 'thermostat',
entity_id: 'thermostat.nest',
message: 'changed to 17 \u00b0C',
name: 'Nest',
when: '2015-04-24T09:08:47.000Z',
},
{
domain: 'thermostat',
entity_id: 'thermostat.nest',
message: 'changed to 21 \u00b0C',
name: 'Nest',
when: '2015-04-24T16:00:47.000Z',
},
{
domain: 'device_tracker',
entity_id: 'device_tracker.anne_therese',
message: 'came home',
name: 'Anne Therese',
when: '2015-04-24T16:24:47.000Z',
},
{
domain: 'group',
entity_id: 'group.all_devices',
message: 'came home',
name: 'All devices',
when: '2015-04-24T16:24:47.000Z',
},
{
domain: 'light',
entity_id: 'light.bowl',
message: 'turned on',
name: 'Bowl',
when: '2015-04-24T18:01:47.000Z',
},
{
domain: 'light',
entity_id: 'light.ceiling',
message: 'turned on',
name: 'Ceiling',
when: '2015-04-24T18:16:47.000Z',
},
{
domain: 'light',
entity_id: 'light.tv_back_light',
message: 'turned on',
name: 'TV Back Light',
when: '2015-04-24T18:31:47.000Z',
},
{
domain: 'sun',
entity_id: 'sun.sun',
message: 'has set',
name: 'sun',
when: '2015-04-24T18:46:47.000Z',
},
{
domain: 'media_player',
entity_id: 'media_player.living_room',
message: 'changed to Plex',
name: 'Media Player',
when: '2015-04-24T19:12:47.000Z',
},
];

View File

@ -1,48 +0,0 @@
export default {
'dev-event': {
component_name: 'dev-event',
url: '/demo/panels/ha-panel-dev-event.html',
url_name: 'dev-event',
},
'dev-info': {
component_name: 'dev-info',
url: '/demo/panels/ha-panel-dev-info.html',
url_name: 'dev-info',
},
'dev-service': {
component_name: 'dev-service',
url: '/demo/panels/ha-panel-dev-service.html',
url_name: 'dev-service',
},
'dev-state': {
component_name: 'dev-state',
url: '/demo/panels/ha-panel-dev-state.html',
url_name: 'dev-state',
},
'dev-template': {
component_name: 'dev-template',
url: '/demo/panels/ha-panel-dev-template.html',
url_name: 'dev-template',
},
history: {
component_name: 'history',
icon: 'mdi:poll-box',
title: 'History',
url: '/demo/panels/ha-panel-history.html',
url_name: 'history',
},
logbook: {
component_name: 'logbook',
icon: 'mdi:format-list-bulleted-type',
title: 'Logbook',
url: '/demo/panels/ha-panel-logbook.html',
url_name: 'logbook',
},
map: {
component_name: 'map',
icon: 'mdi:account-location',
title: 'Map',
url: '/demo/panels/ha-panel-map.html',
url_name: 'map',
},
};

View File

@ -1,37 +0,0 @@
export default [
{
domain: 'homeassistant',
services: {
stop: { description: '', fields: {} },
turn_off: { description: '', fields: {} },
turn_on: { description: '', fields: {} },
},
},
{
domain: 'light',
services: {
turn_off: { description: '', fields: {} },
turn_on: { description: '', fields: {} },
},
},
{
domain: 'switch',
services: {
turn_off: { description: '', fields: {} },
turn_on: { description: '', fields: {} },
},
},
{
domain: 'input_boolean',
services: {
turn_off: { description: '', fields: {} },
turn_on: { description: '', fields: {} },
},
},
{
domain: 'configurator',
services: {
configure: { description: '', fields: {} },
},
},
];

View File

@ -1,279 +0,0 @@
function getRandomTime() {
const ts = new Date(new Date().getTime() - (Math.random() * 80 * 60 * 1000));
return ts.toISOString();
}
const entities = [];
function addEntity(entityId, state, attributes = {}) {
entities.push({
state,
attributes,
entity_id: entityId,
last_changed: getRandomTime(),
last_updated: getRandomTime(),
});
}
let groupOrder = 0;
function addGroup(objectId, state, entityIds, name, view) {
groupOrder++;
const attributes = {
entity_id: entityIds,
order: groupOrder,
};
if (name) {
attributes.friendly_name = name;
}
if (view) {
attributes.view = view;
attributes.hidden = true;
}
addEntity(`group.${objectId}`, state, attributes);
}
// ---------------------------------------------------
// HOME ASSISTANT
// ---------------------------------------------------
addEntity('a.demo_mode', 'enabled');
addEntity('configurator.philips_hue', 'configure', {
configure_id: '4415244496-1',
description: 'Press the button on the bridge to register Philips Hue with Home Assistant.',
description_image: '/demo/images/config_philips_hue.jpg',
fields: [],
submit_caption: 'I have pressed the button',
friendly_name: 'Philips Hue',
});
// ---------------------------------------------------
// VIEWS
// ---------------------------------------------------
addGroup(
'default_view', 'on', [
'a.demo_mode',
'sensor.humidity',
'sensor.temperature',
'device_tracker.paulus',
'device_tracker.anne_therese',
'configurator.philips_hue',
'group.cooking',
'group.general',
'group.rooms',
'camera.living_room',
'media_player.living_room',
'scene.romantic',
'scene.good_morning',
'script.water_lawn',
], 'Main', true);
addGroup(
'rooms_view', 'on', [
'group.living_room',
'group.bedroom',
], 'Rooms', true);
addGroup('rooms', 'on', ['group.living_room', 'group.bedroom'], 'Rooms');
// ---------------------------------------------------
// DEVICE TRACKER + ZONES
// ---------------------------------------------------
addEntity('device_tracker.anne_therese', 'school', {
entity_picture: 'https://graph.facebook.com/621994601/picture',
friendly_name: 'Anne Therese',
latitude: 32.879898,
longitude: -117.236776,
gps_accuracy: 250,
battery: 76,
});
addEntity('device_tracker.paulus', 'not_home', {
entity_picture: 'https://graph.facebook.com/297400035/picture',
friendly_name: 'Paulus',
gps_accuracy: 75,
latitude: 32.892950,
longitude: -117.203431,
battery: 56,
});
addEntity('zone.school', 'zoning', {
radius: 250,
latitude: 32.880834,
longitude: -117.237556,
icon: 'mdi:library',
hidden: true,
});
addEntity('zone.work', 'zoning', {
radius: 250,
latitude: 32.896844,
longitude: -117.202204,
icon: 'mdi:worker',
hidden: true,
});
addEntity('zone.home', 'zoning', {
radius: 100,
latitude: 32.873708,
longitude: -117.226590,
icon: 'mdi:home',
hidden: true,
});
// ---------------------------------------------------
// GENERAL
// ---------------------------------------------------
addGroup('general', 'on', [
'alarm_control_panel.home',
'garage_door.garage_door',
'lock.kitchen_door',
'thermostat.nest',
'camera.living_room',
]);
addEntity('camera.living_room', 'idle', {
entity_picture: '/demo/webcam.jpg?',
});
addEntity('garage_door.garage_door', 'open', {
friendly_name: 'Garage Door',
});
addEntity('alarm_control_panel.home', 'armed_home', {
friendly_name: 'Alarm',
code_format: '^\\d{4}',
});
addEntity('lock.kitchen_door', 'open', {
friendly_name: 'Kitchen Door',
});
// ---------------------------------------------------
// PRESETS
// ---------------------------------------------------
addEntity('script.water_lawn', 'off', {
friendly_name: 'Water Lawn',
});
addEntity('scene.romantic', 'scening', {
friendly_name: 'Romantic',
});
// addEntity('scene.good_morning', 'scening', {
// friendly_name: 'Good Morning',
// });
// ---------------------------------------------------
// LIVING ROOM
// ---------------------------------------------------
addGroup(
'living_room', 'on',
[
'light.table_lamp',
'light.ceiling',
'light.tv_back_light',
'switch.ac',
'media_player.living_room',
],
'Living Room'
);
addEntity('light.tv_back_light', 'off', {
friendly_name: 'TV Back Light',
});
addEntity('light.ceiling', 'on', {
friendly_name: 'Ceiling Lights',
brightness: 200,
rgb_color: [255, 116, 155],
});
addEntity('light.table_lamp', 'on', {
brightness: 200,
rgb_color: [150, 212, 94],
friendly_name: 'Table Lamp',
});
addEntity('switch.ac', 'on', {
friendly_name: 'AC',
icon: 'mdi:air-conditioner',
});
addEntity('media_player.living_room', 'playing', {
entity_picture: '/demo/images/thrones.jpg',
friendly_name: 'Chromecast',
supported_features: 509,
media_content_type: 'tvshow',
media_title: 'The Dance of Dragons',
media_series_title: 'Game of Thrones',
media_season: 5,
media_episode: '09',
app_name: 'HBO Now',
});
// ---------------------------------------------------
// BEDROOM
// ---------------------------------------------------
addGroup(
'bedroom', 'off',
[
'light.bed_light',
'switch.decorative_lights',
'rollershutter.bedroom_window',
],
'Bedroom'
);
addEntity('switch.decorative_lights', 'off', {
friendly_name: 'Decorative Lights',
});
addEntity('light.bed_light', 'off', {
friendly_name: 'Bed Light',
});
addEntity('rollershutter.bedroom_window', 'closed', {
friendly_name: 'Window',
current_position: 0,
});
// ---------------------------------------------------
// SENSORS
// ---------------------------------------------------
addEntity('sensor.temperature', '15.6', {
unit_of_measurement: '\u00b0C',
friendly_name: 'Temperature',
});
addEntity('sensor.humidity', '54', {
unit_of_measurement: '%',
friendly_name: 'Humidity',
});
addEntity('thermostat.nest', '23', {
away_mode: 'off',
temperature: '21',
current_temperature: '18',
unit_of_measurement: '\u00b0C',
friendly_name: 'Nest',
});
// ---------------------------------------------------
// COOKING AUTOMATION
// ---------------------------------------------------
addEntity('input_select.cook_today', 'Paulus', {
options: ['Paulus', 'Anne Therese'],
icon: 'mdi:panda',
});
addEntity('input_boolean.notify_cook', 'on', {
icon: 'mdi:alarm',
friendly_name: 'Notify Cook',
});
addGroup(
'cooking', 'unknown',
['input_select.cook_today', 'input_boolean.notify_cook']
);
export default entities;

View File

@ -1,255 +0,0 @@
import stateData from './state_data';
function getTime(minutesAgo) {
const ts = new Date(Date.now() - (minutesAgo * 60 * 1000));
return ts.toISOString();
}
// prefill with entities we do not want to track
const seen = {
'a.demo_mode': true,
'configurator.philips_hue': true,
'group.default_view': true,
'group.rooms_view': true,
'group.rooms': true,
'zone.school': true,
'zone.work': true,
'zone.home': true,
'group.general': true,
'camera.roundabout': true,
'script.water_lawn': true,
'scene.romantic': true,
'scene.good_morning': true,
'group.cooking': true,
};
const history = [];
function randomTimeAdjustment(diff) {
return Math.random() * diff - (diff / 2);
}
const maxTime = 1440;
function addEntity(state, deltas) {
seen[state.entity_id] = true;
let changes;
if (typeof deltas[0] === 'string') {
changes = deltas.map(state_ => ({ state: state_ }));
} else {
changes = deltas;
}
const timeDiff = (900 / changes.length);
history.push(changes.map(
(change, index) => {
let attributes;
if (!change.attributes && !state.attributes) {
attributes = {};
} else if (!change.attributes) {
attributes = state.attributes;
} else if (!state.attributes) {
attributes = change.attributes;
} else {
attributes = Object.assign({}, state.attributes, change.attributes);
}
const time = index === 0 ? getTime(maxTime) : getTime(maxTime - index * timeDiff +
randomTimeAdjustment(timeDiff));
return {
attributes,
entity_id: state.entity_id,
state: change.state || state.state,
last_changed: time,
last_updated: time,
};
}));
}
addEntity(
{
entity_id: 'sensor.humidity',
attributes: {
unit_of_measurement: '%',
},
}, ['45', '49', '52', '49', '52', '49', '45', '42']
);
addEntity(
{
entity_id: 'sensor.temperature',
attributes: {
unit_of_measurement: '\u00b0C',
},
}, ['23', '27', '25', '23', '24']
);
addEntity(
{
entity_id: 'thermostat.nest',
attributes: {
unit_of_measurement: '\u00b0C',
},
}, [
{
state: '23',
attributes: {
current_temperature: 20,
temperature: 23,
},
},
{
state: '23',
attributes: {
current_temperature: 22,
temperature: 23,
},
},
{
state: '20',
attributes: {
current_temperature: 21,
temperature: 20,
},
},
{
state: '20',
attributes: {
current_temperature: 20,
temperature: 20,
},
},
{
state: '20',
attributes: {
current_temperature: 19,
temperature: 20,
},
},
]
);
addEntity(
{
entity_id: 'media_player.living_room',
attributes: {
friendly_name: 'Chromecast',
},
}, ['Plex', 'idle', 'YouTube', 'Netflix', 'idle', 'Plex']
);
addEntity(
{
entity_id: 'group.all_devices',
}, ['home', 'not_home', 'home']
);
addEntity(
{
entity_id: 'device_tracker.paulus',
}, ['home', 'not_home', 'work', 'not_home']
);
addEntity(
{
entity_id: 'device_tracker.anne_therese',
}, ['home', 'not_home', 'home', 'not_home', 'school']
);
addEntity(
{
entity_id: 'garage_door.garage_door',
}, ['open', 'closed', 'open']
);
addEntity(
{
entity_id: 'alarm_control_panel.home',
}, ['disarmed', 'pending', 'armed_home', 'pending', 'disarmed', 'pending', 'armed_home']
);
addEntity(
{
entity_id: 'lock.kitchen_door',
}, ['unlocked', 'locked', 'unlocked', 'locked']
);
addEntity(
{
entity_id: 'light.tv_back_light',
}, ['on', 'off', 'on', 'off']
);
addEntity(
{
entity_id: 'light.ceiling',
}, ['on', 'off', 'on']
);
addEntity(
{
entity_id: 'light.table_lamp',
}, ['on', 'off', 'on']
);
addEntity(
{
entity_id: 'switch.ac',
}, ['on', 'off', 'on']
);
addEntity(
{
entity_id: 'group.bedroom',
}, ['on', 'off', 'on', 'off']
);
addEntity(
{
entity_id: 'group.living_room',
}, ['on', 'off', 'on']
);
addEntity(
{
entity_id: 'switch.decorative_lights',
}, ['on', 'off', 'on', 'off']
);
addEntity(
{
entity_id: 'light.bed_light',
}, ['on', 'off', 'on', 'off']
);
addEntity(
{
entity_id: 'rollershutter.bedroom_window',
}, ['open', 'closed', 'open', 'closed']
);
addEntity(
{
entity_id: 'input_select.cook_today',
}, ['Anne Therese', 'Paulus']
);
addEntity(
{
entity_id: 'input_boolean.notify_cook',
}, ['off', 'on']
);
if (__DEV__) {
for (let i = 0; i < stateData.length; i++) {
const entity = stateData[i];
if (!(entity.entity_id in seen)) {
/* eslint-disable no-console */
console.warn(`Missing history for ${entity.entity_id}`);
/* eslint-enable no-console */
}
}
}
export default history;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

View File

@ -2,11 +2,6 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import JsYaml from "js-yaml"; import JsYaml from "js-yaml";
import HomeAssistant from "../data/hass";
import { demoConfig } from "../data/demo_config";
import { demoServices } from "../data/demo_services";
import demoResources from "../data/demo_resources";
import demoStates from "../data/demo_states";
import { createCardElement } from "../../../src/panels/lovelace/common/create-card-element"; import { createCardElement } from "../../../src/panels/lovelace/common/create-card-element";
class DemoCard extends PolymerElement { class DemoCard extends PolymerElement {
@ -68,23 +63,7 @@ class DemoCard extends PolymerElement {
} }
const el = createCardElement(JsYaml.safeLoad(config.config)[0]); const el = createCardElement(JsYaml.safeLoad(config.config)[0]);
el.hass = this.hass;
if (this.hass) {
el.hass = this.hass;
} else {
const hass = new HomeAssistant(demoStates);
hass.config = demoConfig;
hass.services = demoServices;
hass.resources = demoResources;
hass.language = "en";
hass.states = demoStates;
hass.themes = {
default_theme: "default",
themes: {},
};
el.hass = hass;
}
card.appendChild(el); card.appendChild(el);
} }

View File

@ -1,11 +0,0 @@
export const demoConfig = {
elevation: 300,
latitude: 51.5287352,
longitude: -0.381773,
unit_system: {
length: "km",
mass: "kg",
temperature: "°C",
volume: "L",
},
};

View File

@ -1,264 +0,0 @@
export default {
en: {
"state.default.off": "Off",
"state.default.on": "On",
"state.default.unknown": "Unknown",
"state.default.unavailable": "Unavailable",
"state.alarm_control_panel.armed": "Armed",
"state.alarm_control_panel.disarmed": "Disarmed",
"state.alarm_control_panel.armed_home": "Armed home",
"state.alarm_control_panel.armed_away": "Armed away",
"state.alarm_control_panel.armed_night": "Armed night",
"state.alarm_control_panel.armed_custom_bypass": "Armed custom bypass",
"state.alarm_control_panel.pending": "Pending",
"state.alarm_control_panel.arming": "Arming",
"state.alarm_control_panel.disarming": "Disarming",
"state.alarm_control_panel.triggered": "Triggered",
"state.automation.off": "Off",
"state.automation.on": "On",
"state.binary_sensor.default.off": "Off",
"state.binary_sensor.default.on": "On",
"state.binary_sensor.battery.off": "Normal",
"state.binary_sensor.battery.on": "Low",
"state.binary_sensor.cold.off": "Normal",
"state.binary_sensor.cold.on": "Cold",
"state.binary_sensor.connectivity.off": "Disconnected",
"state.binary_sensor.connectivity.on": "Connected",
"state.binary_sensor.door.off": "Closed",
"state.binary_sensor.door.on": "Open",
"state.binary_sensor.garage_door.off": "Closed",
"state.binary_sensor.garage_door.on": "Open",
"state.binary_sensor.gas.off": "Clear",
"state.binary_sensor.gas.on": "Detected",
"state.binary_sensor.heat.off": "Normal",
"state.binary_sensor.heat.on": "Hot",
"state.binary_sensor.lock.off": "Locked",
"state.binary_sensor.lock.on": "Unlocked",
"state.binary_sensor.moisture.off": "Dry",
"state.binary_sensor.moisture.on": "Wet",
"state.binary_sensor.motion.off": "Clear",
"state.binary_sensor.motion.on": "Detected",
"state.binary_sensor.occupancy.off": "Clear",
"state.binary_sensor.occupancy.on": "Detected",
"state.binary_sensor.opening.off": "Closed",
"state.binary_sensor.opening.on": "Open",
"state.binary_sensor.presence.off": "Away",
"state.binary_sensor.presence.on": "Home",
"state.binary_sensor.problem.off": "OK",
"state.binary_sensor.problem.on": "Problem",
"state.binary_sensor.safety.off": "Safe",
"state.binary_sensor.safety.on": "Unsafe",
"state.binary_sensor.smoke.off": "Clear",
"state.binary_sensor.smoke.on": "Detected",
"state.binary_sensor.sound.off": "Clear",
"state.binary_sensor.sound.on": "Detected",
"state.binary_sensor.vibration.off": "Clear",
"state.binary_sensor.vibration.on": "Detected",
"state.binary_sensor.window.off": "Closed",
"state.binary_sensor.window.on": "Open",
"state.calendar.off": "Off",
"state.calendar.on": "On",
"state.camera.recording": "Recording",
"state.camera.streaming": "Streaming",
"state.camera.idle": "Idle",
"state.climate.off": "Off",
"state.climate.on": "On",
"state.climate.heat": "Heat",
"state.climate.cool": "Cool",
"state.climate.idle": "Idle",
"state.climate.auto": "Auto",
"state.climate.dry": "Dry",
"state.climate.fan_only": "Fan only",
"state.climate.eco": "Eco",
"state.climate.electric": "Electric",
"state.climate.performance": "Performance",
"state.climate.high_demand": "High demand",
"state.climate.heat_pump": "Heat pump",
"state.climate.gas": "Gas",
"state.configurator.configure": "Configure",
"state.configurator.configured": "Configured",
"state.cover.open": "Open",
"state.cover.opening": "Opening",
"state.cover.closed": "Closed",
"state.cover.closing": "Closing",
"state.cover.stopped": "Stopped",
"state.device_tracker.home": "Home",
"state.device_tracker.not_home": "Away",
"state.fan.off": "Off",
"state.fan.on": "On",
"state.group.off": "Off",
"state.group.on": "On",
"state.group.home": "Home",
"state.group.not_home": "Away",
"state.group.open": "Open",
"state.group.opening": "Opening",
"state.group.closed": "Closed",
"state.group.closing": "Closing",
"state.group.stopped": "Stopped",
"state.group.locked": "Locked",
"state.group.unlocked": "Unlocked",
"state.group.ok": "OK",
"state.group.problem": "Problem",
"state.input_boolean.off": "Off",
"state.input_boolean.on": "On",
"state.light.off": "Off",
"state.light.on": "On",
"state.lock.locked": "Locked",
"state.lock.unlocked": "Unlocked",
"state.media_player.off": "Off",
"state.media_player.on": "On",
"state.media_player.playing": "Playing",
"state.media_player.paused": "Paused",
"state.media_player.idle": "Idle",
"state.media_player.standby": "Standby",
"state.plant.ok": "OK",
"state.plant.problem": "Problem",
"state.remote.off": "Off",
"state.remote.on": "On",
"state.scene.scening": "Scening",
"state.script.off": "Off",
"state.script.on": "On",
"state.sensor.off": "Off",
"state.sensor.on": "On",
"state.sun.above_horizon": "Above horizon",
"state.sun.below_horizon": "Below horizon",
"state.switch.off": "Off",
"state.switch.on": "On",
"state.weather.clear-night": "Clear, night",
"state.weather.cloudy": "Cloudy",
"state.weather.fog": "Fog",
"state.weather.hail": "Hail",
"state.weather.lightning": "Lightning",
"state.weather.lightning-rainy": "Lightning, rainy",
"state.weather.partlycloudy": "Partly cloudy",
"state.weather.pouring": "Pouring",
"state.weather.rainy": "Rainy",
"state.weather.snowy": "Snowy",
"state.weather.snowy-rainy": "Snowy, rainy",
"state.weather.sunny": "Sunny",
"state.weather.windy": "Windy",
"state.weather.windy-variant": "Windy",
"state.zwave.default.initializing": "Initializing",
"state.zwave.default.dead": "Dead",
"state.zwave.default.sleeping": "Sleeping",
"state.zwave.default.ready": "Ready",
"state.zwave.query_stage.initializing": "Initializing ({query_stage})",
"state.zwave.query_stage.dead": "Dead ({query_stage})",
"state_badge.default.unknown": "Unk",
"state_badge.default.unavailable": "Unavai",
"state_badge.alarm_control_panel.armed": "Armed",
"state_badge.alarm_control_panel.disarmed": "Disarm",
"state_badge.alarm_control_panel.armed_home": "Armed",
"state_badge.alarm_control_panel.armed_away": "Armed",
"state_badge.alarm_control_panel.armed_night": "Armed",
"state_badge.alarm_control_panel.armed_custom_bypass": "Armed",
"state_badge.alarm_control_panel.pending": "Pend",
"state_badge.alarm_control_panel.arming": "Arming",
"state_badge.alarm_control_panel.disarming": "Disarm",
"state_badge.alarm_control_panel.triggered": "Trig",
"state_badge.device_tracker.home": "Home",
"state_badge.device_tracker.not_home": "Away",
"ui.card.alarm_control_panel.code": "Code",
"ui.card.alarm_control_panel.clear_code": "Clear",
"ui.card.alarm_control_panel.disarm": "Disarm",
"ui.card.alarm_control_panel.arm_home": "Arm home",
"ui.card.alarm_control_panel.arm_away": "Arm away",
"ui.card.automation.last_triggered": "Last triggered",
"ui.card.automation.trigger": "Trigger",
"ui.card.camera.not_available": "Image not available",
"ui.card.climate.currently": "Currently",
"ui.card.climate.on_off": "On / off",
"ui.card.climate.target_temperature": "Target temperature",
"ui.card.climate.target_humidity": "Target humidity",
"ui.card.climate.operation": "Operation",
"ui.card.climate.fan_mode": "Fan mode",
"ui.card.climate.swing_mode": "Swing mode",
"ui.card.climate.away_mode": "Away mode",
"ui.card.climate.aux_heat": "Aux heat",
"ui.card.cover.position": "Position",
"ui.card.cover.tilt_position": "Tilt position",
"ui.card.fan.speed": "Speed",
"ui.card.fan.oscillate": "Oscillate",
"ui.card.fan.direction": "Direction",
"ui.card.light.brightness": "Brightness",
"ui.card.light.color_temperature": "Color temperature",
"ui.card.light.white_value": "White value",
"ui.card.light.effect": "Effect",
"ui.card.lock.code": "Code",
"ui.card.lock.lock": "Lock",
"ui.card.lock.unlock": "Unlock",
"ui.card.media_player.source": "Source",
"ui.card.media_player.sound_mode": "Sound mode",
"ui.card.media_player.text_to_speak": "Text to speak",
"ui.card.persistent_notification.dismiss": "Dismiss",
"ui.card.scene.activate": "Activate",
"ui.card.script.execute": "Execute",
"ui.card.weather.attributes.air_pressure": "Air pressure",
"ui.card.weather.attributes.humidity": "Humidity",
"ui.card.weather.attributes.temperature": "Temperature",
"ui.card.weather.attributes.visibility": "Visibility",
"ui.card.weather.attributes.wind_speed": "Wind speed",
"ui.card.weather.cardinal_direction.e": "E",
"ui.card.weather.cardinal_direction.ene": "ENE",
"ui.card.weather.cardinal_direction.ese": "ESE",
"ui.card.weather.cardinal_direction.n": "N",
"ui.card.weather.cardinal_direction.ne": "NE",
"ui.card.weather.cardinal_direction.nne": "NNE",
"ui.card.weather.cardinal_direction.nw": "NW",
"ui.card.weather.cardinal_direction.nnw": "NNW",
"ui.card.weather.cardinal_direction.s": "S",
"ui.card.weather.cardinal_direction.se": "SE",
"ui.card.weather.cardinal_direction.sse": "SSE",
"ui.card.weather.cardinal_direction.ssw": "SSW",
"ui.card.weather.cardinal_direction.sw": "SW",
"ui.card.weather.cardinal_direction.w": "W",
"ui.card.weather.cardinal_direction.wnw": "WNW",
"ui.card.weather.cardinal_direction.wsw": "WSW",
"ui.card.weather.forecast": "Forecast",
"ui.common.loading": "Loading",
"ui.common.cancel": "Cancel",
"ui.components.entity.entity-picker.entity": "Entity",
"ui.components.relative_time.past": "{time} ago",
"ui.components.relative_time.future": "In {time}",
"ui.components.relative_time.never": "Never",
"ui.components.relative_time.duration.second":
"{count} {count, plural,\n one {second}\n other {seconds}\n}",
"ui.components.relative_time.duration.minute":
"{count} {count, plural,\n one {minute}\n other {minutes}\n}",
"ui.components.relative_time.duration.hour":
"{count} {count, plural,\n one {hour}\n other {hours}\n}",
"ui.components.relative_time.duration.day":
"{count} {count, plural,\n one {day}\n other {days}\n}",
"ui.components.relative_time.duration.week":
"{count} {count, plural,\n one {week}\n other {weeks}\n}",
"ui.components.history_charts.loading_history": "Loading state history...",
"ui.components.history_charts.no_history_found": "No state history found.",
"ui.components.service-picker.service": "Service",
"ui.dialogs.more_info_settings.save": "Save",
"ui.dialogs.more_info_settings.name": "Name",
"ui.duration.second":
"{count} {count, plural,\n one {second}\n other {seconds}\n}",
"ui.duration.minute":
"{count} {count, plural,\n one {minute}\n other {minutes}\n}",
"ui.duration.hour":
"{count} {count, plural,\n one {hour}\n other {hours}\n}",
"ui.duration.day":
"{count} {count, plural,\n one {day}\n other {days}\n}",
"ui.duration.week":
"{count} {count, plural,\n one {week}\n other {weeks}\n}",
"ui.login-form.password": "Password",
"ui.login-form.remember": "Remember",
"ui.login-form.log_in": "Log in",
"ui.notification_toast.entity_turned_on": "Turned on {entity}.",
"ui.notification_toast.entity_turned_off": "Turned off {entity}.",
"ui.notification_toast.service_called": "Service {service} called.",
"ui.notification_toast.service_call_failed":
"Failed to call service {service}.",
"ui.notification_toast.connection_lost": "Connection lost. Reconnecting…",
"ui.sidebar.developer_tools": "Developer tools",
"ui.sidebar.log_out": "Log out",
"attribute.weather.humidity": "Humidity",
"attribute.weather.visibility": "Visibility",
"attribute.weather.wind_speed": "Wind speed",
},
};

View File

@ -1,96 +0,0 @@
export const demoServices = {
configurator: ["configure"],
tts: ["demo_say", "clear_cache"],
cover: [
"open_cover",
"close_cover",
"open_cover_tilt",
"close_cover_tilt",
"set_cover_tilt_position",
"set_cover_position",
"stop_cover_tilt",
"stop_cover",
],
group: ["set", "reload", "remove", "set_visibility"],
alarm_control_panel: [
"alarm_arm_night",
"alarm_disarm",
"alarm_trigger",
"alarm_arm_home",
"alarm_arm_away",
"alarm_arm_custom_bypass",
],
conversation: ["process"],
notify: ["demo_test_target_name", "notify"],
lock: ["open", "lock", "unlock"],
input_select: [
"select_previous",
"set_options",
"select_next",
"select_option",
],
recorder: ["purge"],
persistent_notification: ["create", "dismiss"],
timer: ["pause", "cancel", "finish", "start"],
input_boolean: ["turn_off", "toggle", "turn_on"],
fan: [
"set_speed",
"turn_on",
"turn_off",
"set_direction",
"oscillate",
"toggle",
],
climate: [
"set_humidity",
"set_operation_mode",
"set_aux_heat",
"turn_on",
"set_hold_mode",
"set_away_mode",
"turn_off",
"set_fan_mode",
"set_temperature",
"set_swing_mode",
],
switch: ["turn_off", "toggle", "turn_on"],
script: ["turn_off", "demo", "reload", "toggle", "turn_on"],
scene: ["turn_on"],
system_log: ["clear", "write"],
camera: ["disable_motion_detection", "enable_motion_detection", "snapshot"],
image_processing: ["scan"],
media_player: [
"media_previous_track",
"clear_playlist",
"shuffle_set",
"media_seek",
"turn_on",
"media_play_pause",
"media_next_track",
"media_pause",
"volume_down",
"volume_set",
"media_stop",
"toggle",
"media_play",
"play_media",
"volume_mute",
"turn_off",
"select_sound_mode",
"select_source",
"volume_up",
],
input_number: ["set_value", "increment", "decrement"],
device_tracker: ["see"],
homeassistant: [
"stop",
"check_config",
"reload_core_config",
"turn_on",
"turn_off",
"restart",
"toggle",
],
light: ["turn_off", "toggle", "turn_on"],
input_text: ["set_value"],
};

View File

@ -1,112 +0,0 @@
import { fireEvent } from "../../../src/common/dom/fire_event";
import { demoConfig } from "./demo_config";
import { demoServices } from "./demo_services";
import demoResources from "./demo_resources";
const ensureArray = (val) => (Array.isArray(val) ? val : [val]);
export default (elements, { initialStates = {} } = {}) => {
elements = ensureArray(elements);
const wsCommands = {};
const restResponses = {};
let hass;
const entities = {};
function updateHass(obj) {
hass = Object.assign({}, hass, obj);
elements.forEach((el) => {
el.hass = hass;
});
}
updateHass({
// Home Assistant properties
config: demoConfig,
services: demoServices,
language: "en",
resources: demoResources,
states: initialStates,
themes: {},
connection: {
subscribeEvents: async (callback, event) => {
console.log("subscribeEvents", event);
return () => console.log("unsubscribeEvents", event);
},
},
// Mock properties
mockEntities: entities,
// Home Assistant functions
async callService(domain, service, data) {
fireEvent(elements[0], "show-notification", {
message: `Called service ${domain}/${service}`,
});
if (data.entity_id) {
await Promise.all(
ensureArray(data.entity_id).map((ent) =>
entities[ent].handleService(domain, service, data)
)
);
} else {
console.log("unmocked callService", domain, service, data);
}
},
async callWS(msg) {
const callback = wsCommands[msg.type];
return callback
? callback(msg)
: Promise.reject({
code: "command_not_mocked",
message: "This command is not implemented in the gallery.",
});
},
async sendWS(msg) {
const callback = wsCommands[msg.type];
if (callback) {
callback(msg);
} else {
console.error(`Unknown command: ${msg.type}`);
}
console.log("sendWS", msg);
},
async callApi(method, path, parameters) {
const callback = restResponses[path];
return callback
? callback(method, path, parameters)
: Promise.reject(`Mock for {path} is not implemented`);
},
// Mock functions
updateHass,
updateStates(newStates) {
updateHass({
states: Object.assign({}, hass.states, newStates),
});
},
addEntities(newEntities) {
const states = {};
ensureArray(newEntities).forEach((ent) => {
ent.hass = hass;
entities[ent.entityId] = ent;
states[ent.entityId] = ent.toState();
});
this.updateStates(states);
},
mockWS(type, callback) {
wsCommands[type] = callback;
},
mockAPI(path, callback) {
restResponses[path] = callback;
},
});
return hass;
};

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [
@ -48,7 +48,8 @@ const CONFIGS = [
config: ` config: `
- type: entity-button - type: entity-button
entity: light.bed_light entity: light.bed_light
tap_action: toggle tap_action:
action: toggle
`, `,
}, },
{ {

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -1,7 +1,7 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const CONFIGS = [ const CONFIGS = [

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -1,8 +1,8 @@
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
const ENTITIES = [ const ENTITIES = [

View File

@ -4,8 +4,8 @@ import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../src/dialogs/more-info/controls/more-info-content"; import "../../../src/dialogs/more-info/controls/more-info-content";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import getEntity from "../data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import provideHass from "../data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-more-infos"; import "../components/demo-more-infos";
import { SUPPORT_BRIGHTNESS } from "../../../src/data/light"; import { SUPPORT_BRIGHTNESS } from "../../../src/data/light";

View File

@ -2,12 +2,13 @@ const path = require("path");
const gulp = require("gulp"); const gulp = require("gulp");
const foreach = require("gulp-foreach"); const foreach = require("gulp-foreach");
const hash = require("gulp-hash"); const hash = require("gulp-hash");
const insert = require("gulp-insert");
const merge = require("gulp-merge-json"); const merge = require("gulp-merge-json");
const minify = require("gulp-jsonminify"); const minify = require("gulp-jsonminify");
const rename = require("gulp-rename"); const rename = require("gulp-rename");
const transform = require("gulp-json-transform"); const transform = require("gulp-json-transform");
const isDemo = process.env.DEMO === "1";
const inDir = "translations"; const inDir = "translations";
const workDir = "build-translations"; const workDir = "build-translations";
const fullDir = workDir + "/full"; const fullDir = workDir + "/full";
@ -230,7 +231,7 @@ gulp.task(taskName, ["build-flattened-translations"], function() {
hash({ hash({
algorithm: "md5", algorithm: "md5",
hashLength: 32, hashLength: 32,
template: "<%= name %>-<%= hash %>.json", template: isDemo ? "<%= name %>.json" : "<%= name %>-<%= hash %>.json",
}) })
) )
.pipe(hash.manifest("translationFingerprints.json")) .pipe(hash.manifest("translationFingerprints.json"))

Some files were not shown because too many files have changed in this diff Show More