mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 19:56:42 +00:00
commit
d7e7798a55
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -41,7 +41,32 @@ Provide details about what browser (and version) you are seeing the issue in. An
|
|||||||
**Description of problem:**
|
**Description of problem:**
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Explain what the issue is, and how things should look/behave. If possible provide a screenshot with a description.
|
Explain what the issue is, and what is the current behaviour. If possible provide a screenshot with a description.
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Expected behaviour:**
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Explain how things should look/behave. If possible provide a screenshot with a description.
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Relevant config:**
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Give the config of both the integration that is used, the Lovelace config, scene, automation or otherwise relevant configuration.
|
||||||
|
-->
|
||||||
|
|
||||||
|
**Steps to reproduce this problem:**
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Sum up all steps that are necesarry to reproduce this bug.
|
||||||
|
For example:
|
||||||
|
1. Add a climate integration
|
||||||
|
2. Navigate to Lovelace
|
||||||
|
3. Click more info of the climate entity
|
||||||
|
4. Set the hvac action to heat
|
||||||
|
5. Set the temperature higher than the current temperature
|
||||||
|
6. Set the hvac action to cool
|
||||||
-->
|
-->
|
||||||
|
|
||||||
**Javascript errors shown in the web inspector (if applicable):**
|
**Javascript errors shown in the web inspector (if applicable):**
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
This is the repository for the official [Home Assistant](https://home-assistant.io) frontend.
|
This is the repository for the official [Home Assistant](https://home-assistant.io) frontend.
|
||||||
|
|
||||||
[](https://home-assistant.io/demo/)
|
[](https://demo.home-assistant.io/)
|
||||||
|
|
||||||
- [View demo of the Polymer frontend](https://home-assistant.io/demo/)
|
- [View demo of Home Assistant](https://demo.home-assistant.io/)
|
||||||
- [More information about Home Assistant](https://home-assistant.io)
|
- [More information about Home Assistant](https://home-assistant.io)
|
||||||
- [Frontend development instructions](https://developers.home-assistant.io/docs/en/frontend_index.html)
|
- [Frontend development instructions](https://developers.home-assistant.io/docs/en/frontend_index.html)
|
||||||
|
|
||||||
@ -31,3 +31,5 @@ It is possible to compile the project and/or run commands in the development env
|
|||||||
## License
|
## License
|
||||||
|
|
||||||
Home Assistant is open-source and Apache 2 licensed. Feel free to browse the repository, learn and reuse parts in your own projects.
|
Home Assistant is open-source and Apache 2 licensed. Feel free to browse the repository, learn and reuse parts in your own projects.
|
||||||
|
|
||||||
|
We use [BrowserStack](https://www.browserstack.com) to test Home Assistant on a large variation of devices.
|
||||||
|
@ -33,6 +33,7 @@ module.exports.babelLoaderConfig = ({ latestBuild }) => {
|
|||||||
pragma: "h",
|
pragma: "h",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
"@babel/plugin-proposal-optional-chaining",
|
||||||
[
|
[
|
||||||
require("@babel/plugin-proposal-decorators").default,
|
require("@babel/plugin-proposal-decorators").default,
|
||||||
{ decoratorsBeforeExport: true },
|
{ decoratorsBeforeExport: true },
|
||||||
|
@ -175,9 +175,9 @@ export class HcMain extends HassElement {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Generate a Lovelace config.
|
// Generate a Lovelace config.
|
||||||
this._unsubLovelace = () => undefined;
|
this._unsubLovelace = () => undefined;
|
||||||
const {
|
const { generateLovelaceConfigFromHass } = await import(
|
||||||
generateLovelaceConfigFromHass,
|
"../../../../src/panels/lovelace/common/generate-lovelace-config"
|
||||||
} = await import("../../../../src/panels/lovelace/common/generate-lovelace-config");
|
);
|
||||||
this._handleNewLovelaceConfig(
|
this._handleNewLovelaceConfig(
|
||||||
await generateLovelaceConfigFromHass(this.hass!)
|
await generateLovelaceConfigFromHass(this.hass!)
|
||||||
);
|
);
|
||||||
|
@ -53,7 +53,7 @@ class CardModder extends LitElement {
|
|||||||
for (var k in this._config.style) {
|
for (var k in this._config.style) {
|
||||||
if (window.cardTools.hasTemplate(this._config.style[k]))
|
if (window.cardTools.hasTemplate(this._config.style[k]))
|
||||||
this.templated.push(k);
|
this.templated.push(k);
|
||||||
this.card.style.setProperty(k, '');
|
this.card.style.setProperty(k, "");
|
||||||
target.style.setProperty(
|
target.style.setProperty(
|
||||||
k,
|
k,
|
||||||
window.cardTools.parseTemplate(this._config.style[k])
|
window.cardTools.parseTemplate(this._config.style[k])
|
||||||
|
@ -12,5 +12,7 @@ import "./resources/hademo-icons";
|
|||||||
|
|
||||||
/* polyfill for paper-dropdown */
|
/* polyfill for paper-dropdown */
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
import(/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min");
|
import(
|
||||||
|
/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"
|
||||||
|
);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
@ -65,74 +65,79 @@ const generateHistory = (state, deltas) => {
|
|||||||
const incrementalUnits = ["clients", "queries", "ads"];
|
const incrementalUnits = ["clients", "queries", "ads"];
|
||||||
|
|
||||||
export const mockHistory = (mockHass: MockHomeAssistant) => {
|
export const mockHistory = (mockHass: MockHomeAssistant) => {
|
||||||
mockHass.mockAPI(new RegExp("history/period/.+"), (
|
mockHass.mockAPI(
|
||||||
hass,
|
new RegExp("history/period/.+"),
|
||||||
// @ts-ignore
|
(
|
||||||
method,
|
hass,
|
||||||
path,
|
// @ts-ignore
|
||||||
// @ts-ignore
|
method,
|
||||||
parameters
|
path,
|
||||||
) => {
|
// @ts-ignore
|
||||||
const params = parseQuery<HistoryQueryParams>(path.split("?")[1]);
|
parameters
|
||||||
const entities = params.filter_entity_id.split(",");
|
) => {
|
||||||
|
const params = parseQuery<HistoryQueryParams>(path.split("?")[1]);
|
||||||
|
const entities = params.filter_entity_id.split(",");
|
||||||
|
|
||||||
const results: HassEntity[][] = [];
|
const results: HassEntity[][] = [];
|
||||||
|
|
||||||
for (const entityId of entities) {
|
for (const entityId of entities) {
|
||||||
const state = hass.states[entityId];
|
const state = hass.states[entityId];
|
||||||
|
|
||||||
if (!state) {
|
if (!state) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!state.attributes.unit_of_measurement) {
|
if (!state.attributes.unit_of_measurement) {
|
||||||
results.push(generateHistory(state, [state.state]));
|
results.push(generateHistory(state, [state.state]));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const numberState = Number(state.state);
|
const numberState = Number(state.state);
|
||||||
|
|
||||||
if (isNaN(numberState)) {
|
if (isNaN(numberState)) {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
console.log(
|
console.log(
|
||||||
"Ignoring state with unparsable state but with a unit",
|
"Ignoring state with unparsable state but with a unit",
|
||||||
entityId,
|
entityId,
|
||||||
state
|
state
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statesToGenerate = 15;
|
||||||
|
let genFunc;
|
||||||
|
|
||||||
|
if (incrementalUnits.includes(state.attributes.unit_of_measurement)) {
|
||||||
|
let initial = Math.floor(
|
||||||
|
numberState * 0.4 + numberState * Math.random() * 0.2
|
||||||
|
);
|
||||||
|
const diff = Math.max(
|
||||||
|
1,
|
||||||
|
Math.floor((numberState - initial) / statesToGenerate)
|
||||||
|
);
|
||||||
|
genFunc = () => {
|
||||||
|
initial += diff;
|
||||||
|
return Math.min(numberState, initial);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const diff = Math.floor(
|
||||||
|
numberState * (numberState > 80 ? 0.05 : 0.5)
|
||||||
|
);
|
||||||
|
genFunc = () =>
|
||||||
|
numberState - diff + Math.floor(Math.random() * 2 * diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push(
|
||||||
|
generateHistory(
|
||||||
|
{
|
||||||
|
entity_id: state.entity_id,
|
||||||
|
attributes: state.attributes,
|
||||||
|
},
|
||||||
|
Array.from({ length: statesToGenerate }, genFunc)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
return results;
|
||||||
const statesToGenerate = 15;
|
|
||||||
let genFunc;
|
|
||||||
|
|
||||||
if (incrementalUnits.includes(state.attributes.unit_of_measurement)) {
|
|
||||||
let initial = Math.floor(
|
|
||||||
numberState * 0.4 + numberState * Math.random() * 0.2
|
|
||||||
);
|
|
||||||
const diff = Math.max(
|
|
||||||
1,
|
|
||||||
Math.floor((numberState - initial) / statesToGenerate)
|
|
||||||
);
|
|
||||||
genFunc = () => {
|
|
||||||
initial += diff;
|
|
||||||
return Math.min(numberState, initial);
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const diff = Math.floor(numberState * (numberState > 80 ? 0.05 : 0.5));
|
|
||||||
genFunc = () =>
|
|
||||||
numberState - diff + Math.floor(Math.random() * 2 * diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
results.push(
|
|
||||||
generateHistory(
|
|
||||||
{
|
|
||||||
entity_id: state.entity_id,
|
|
||||||
attributes: state.attributes,
|
|
||||||
},
|
|
||||||
Array.from({ length: statesToGenerate }, genFunc)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return results;
|
);
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
@ -12,9 +12,10 @@ export const mockLovelace = (
|
|||||||
localizePromise: Promise<LocalizeFunc>
|
localizePromise: Promise<LocalizeFunc>
|
||||||
) => {
|
) => {
|
||||||
hass.mockWS("lovelace/config", () =>
|
hass.mockWS("lovelace/config", () =>
|
||||||
Promise.all([selectedDemoConfig, localizePromise]).then(
|
Promise.all([
|
||||||
([config, localize]) => config.lovelace(localize)
|
selectedDemoConfig,
|
||||||
)
|
localizePromise,
|
||||||
|
]).then(([config, localize]) => config.lovelace(localize))
|
||||||
);
|
);
|
||||||
|
|
||||||
hass.mockWS("lovelace/config/save", () => Promise.resolve());
|
hass.mockWS("lovelace/config/save", () => Promise.resolve());
|
||||||
|
@ -44,9 +44,7 @@ class HassioAddonAudio extends EventsMixin(PolymerElement) {
|
|||||||
selected="{{selectedInput}}"
|
selected="{{selectedInput}}"
|
||||||
>
|
>
|
||||||
<template is="dom-repeat" items="[[inputDevices]]">
|
<template is="dom-repeat" items="[[inputDevices]]">
|
||||||
<paper-item device\$="[[item.device]]"
|
<paper-item device$="[[item.device]]">[[item.name]]</paper-item>
|
||||||
>[[item.name]]</paper-item
|
|
||||||
>
|
|
||||||
</template>
|
</template>
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
@ -57,9 +55,7 @@ class HassioAddonAudio extends EventsMixin(PolymerElement) {
|
|||||||
selected="{{selectedOutput}}"
|
selected="{{selectedOutput}}"
|
||||||
>
|
>
|
||||||
<template is="dom-repeat" items="[[outputDevices]]">
|
<template is="dom-repeat" items="[[outputDevices]]">
|
||||||
<paper-item device\$="[[item.device]]"
|
<paper-item device$="[[item.device]]">[[item.name]]</paper-item>
|
||||||
>[[item.name]]</paper-item
|
|
||||||
>
|
|
||||||
</template>
|
</template>
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
|
@ -569,7 +569,10 @@ class HassioAddonInfo extends EventsMixin(PolymerElement) {
|
|||||||
openChangelog() {
|
openChangelog() {
|
||||||
this.hass
|
this.hass
|
||||||
.callApi("get", `hassio/addons/${this.addonSlug}/changelog`)
|
.callApi("get", `hassio/addons/${this.addonSlug}/changelog`)
|
||||||
.then((resp) => resp, () => "Error getting changelog")
|
.then(
|
||||||
|
(resp) => resp,
|
||||||
|
() => "Error getting changelog"
|
||||||
|
)
|
||||||
.then((content) => {
|
.then((content) => {
|
||||||
showHassioMarkdownDialog(this, {
|
showHassioMarkdownDialog(this, {
|
||||||
title: "Changelog",
|
title: "Changelog",
|
||||||
|
@ -74,9 +74,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
this.supervisorInfo.version,
|
this.supervisorInfo.version,
|
||||||
this.supervisorInfo.last_version,
|
this.supervisorInfo.last_version,
|
||||||
"hassio/supervisor/update",
|
"hassio/supervisor/update",
|
||||||
`https://github.com//home-assistant/hassio/releases/tag/${
|
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisorInfo.last_version}`
|
||||||
this.supervisorInfo.last_version
|
|
||||||
}`
|
|
||||||
)}
|
)}
|
||||||
${this.hassOsInfo
|
${this.hassOsInfo
|
||||||
? this._renderUpdateCard(
|
? this._renderUpdateCard(
|
||||||
@ -84,9 +82,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
this.hassOsInfo.version,
|
this.hassOsInfo.version,
|
||||||
this.hassOsInfo.version_latest,
|
this.hassOsInfo.version_latest,
|
||||||
"hassio/hassos/update",
|
"hassio/hassos/update",
|
||||||
`https://github.com//home-assistant/hassos/releases/tag/${
|
`https://github.com//home-assistant/hassos/releases/tag/${this.hassOsInfo.version_latest}`
|
||||||
this.hassOsInfo.version_latest
|
|
||||||
}`
|
|
||||||
)
|
)
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,7 +12,9 @@ export const showHassioMarkdownDialog = (
|
|||||||
fireEvent(element, "show-dialog", {
|
fireEvent(element, "show-dialog", {
|
||||||
dialogTag: "dialog-hassio-markdown",
|
dialogTag: "dialog-hassio-markdown",
|
||||||
dialogImport: () =>
|
dialogImport: () =>
|
||||||
import(/* webpackChunkName: "dialog-hassio-markdown" */ "./dialog-hassio-markdown"),
|
import(
|
||||||
|
/* webpackChunkName: "dialog-hassio-markdown" */ "./dialog-hassio-markdown"
|
||||||
|
),
|
||||||
dialogParams,
|
dialogParams,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,9 @@ export const showHassioSnapshotDialog = (
|
|||||||
fireEvent(element, "show-dialog", {
|
fireEvent(element, "show-dialog", {
|
||||||
dialogTag: "dialog-hassio-snapshot",
|
dialogTag: "dialog-hassio-snapshot",
|
||||||
dialogImport: () =>
|
dialogImport: () =>
|
||||||
import(/* webpackChunkName: "dialog-hassio-snapshot" */ "./dialog-hassio-snapshot"),
|
import(
|
||||||
|
/* webpackChunkName: "dialog-hassio-snapshot" */ "./dialog-hassio-snapshot"
|
||||||
|
),
|
||||||
dialogParams,
|
dialogParams,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -56,12 +56,16 @@ class HassioMain extends ProvideHassLitMixin(HassRouterPage) {
|
|||||||
addon: {
|
addon: {
|
||||||
tag: "hassio-addon-view",
|
tag: "hassio-addon-view",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "hassio-addon-view" */ "./addon-view/hassio-addon-view"),
|
import(
|
||||||
|
/* webpackChunkName: "hassio-addon-view" */ "./addon-view/hassio-addon-view"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
ingress: {
|
ingress: {
|
||||||
tag: "hassio-ingress-view",
|
tag: "hassio-ingress-view",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "hassio-ingress-view" */ "./ingress-view/hassio-ingress-view"),
|
import(
|
||||||
|
/* webpackChunkName: "hassio-ingress-view" */ "./ingress-view/hassio-ingress-view"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
46
package.json
46
package.json
@ -19,13 +19,14 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material/chips": "^3.2.0",
|
"@material/chips": "^3.2.0",
|
||||||
"@material/data-table": "^3.2.0",
|
"@material/data-table": "^3.2.0",
|
||||||
"@material/mwc-base": "^0.8.0",
|
"@material/mwc-base": "^0.10.0",
|
||||||
"@material/mwc-button": "^0.8.0",
|
"@material/mwc-button": "^0.10.0",
|
||||||
"@material/mwc-checkbox": "^0.8.0",
|
"@material/mwc-checkbox": "^0.10.0",
|
||||||
"@material/mwc-fab": "^0.8.0",
|
"@material/mwc-dialog": "^0.10.0",
|
||||||
"@material/mwc-ripple": "^0.8.0",
|
"@material/mwc-fab": "^0.10.0",
|
||||||
"@material/mwc-switch": "^0.8.0",
|
"@material/mwc-ripple": "^0.10.0",
|
||||||
"@mdi/svg": "4.5.95",
|
"@material/mwc-switch": "^0.10.0",
|
||||||
|
"@mdi/svg": "4.6.95",
|
||||||
"@polymer/app-layout": "^3.0.2",
|
"@polymer/app-layout": "^3.0.2",
|
||||||
"@polymer/app-localize-behavior": "^3.0.1",
|
"@polymer/app-localize-behavior": "^3.0.1",
|
||||||
"@polymer/app-route": "^3.0.2",
|
"@polymer/app-route": "^3.0.2",
|
||||||
@ -68,8 +69,8 @@
|
|||||||
"@polymer/paper-tooltip": "^3.0.1",
|
"@polymer/paper-tooltip": "^3.0.1",
|
||||||
"@polymer/polymer": "3.1.0",
|
"@polymer/polymer": "3.1.0",
|
||||||
"@thomasloven/round-slider": "0.3.7",
|
"@thomasloven/round-slider": "0.3.7",
|
||||||
"@vaadin/vaadin-combo-box": "^4.2.8",
|
"@vaadin/vaadin-combo-box": "^5.0.6",
|
||||||
"@vaadin/vaadin-date-picker": "^3.3.3",
|
"@vaadin/vaadin-date-picker": "^4.0.3",
|
||||||
"@webcomponents/shadycss": "^1.9.0",
|
"@webcomponents/shadycss": "^1.9.0",
|
||||||
"@webcomponents/webcomponentsjs": "^2.2.7",
|
"@webcomponents/webcomponentsjs": "^2.2.7",
|
||||||
"chart.js": "~2.8.0",
|
"chart.js": "~2.8.0",
|
||||||
@ -98,21 +99,23 @@
|
|||||||
"regenerator-runtime": "^0.13.2",
|
"regenerator-runtime": "^0.13.2",
|
||||||
"roboto-fontface": "^0.10.0",
|
"roboto-fontface": "^0.10.0",
|
||||||
"superstruct": "^0.6.1",
|
"superstruct": "^0.6.1",
|
||||||
|
"copy-to-clipboard": "^1.0.9",
|
||||||
"tslib": "^1.10.0",
|
"tslib": "^1.10.0",
|
||||||
"unfetch": "^4.1.0",
|
"unfetch": "^4.1.0",
|
||||||
"web-animations-js": "^2.3.1",
|
"web-animations-js": "^2.3.1",
|
||||||
"xss": "^1.0.6"
|
"xss": "^1.0.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.4.0",
|
"@babel/core": "^7.7.4",
|
||||||
"@babel/plugin-external-helpers": "^7.2.0",
|
"@babel/plugin-external-helpers": "^7.7.4",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.4.0",
|
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
||||||
"@babel/plugin-proposal-decorators": "^7.4.0",
|
"@babel/plugin-proposal-decorators": "^7.7.4",
|
||||||
"@babel/plugin-proposal-object-rest-spread": "^7.4.0",
|
"@babel/plugin-proposal-object-rest-spread": "^7.7.4",
|
||||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
"@babel/plugin-proposal-optional-chaining": "^7.7.4",
|
||||||
"@babel/plugin-transform-react-jsx": "^7.3.0",
|
"@babel/plugin-syntax-dynamic-import": "^7.7.4",
|
||||||
"@babel/preset-env": "^7.4.2",
|
"@babel/plugin-transform-react-jsx": "^7.7.4",
|
||||||
"@babel/preset-typescript": "^7.4.0",
|
"@babel/preset-env": "^7.7.4",
|
||||||
|
"@babel/preset-typescript": "^7.7.4",
|
||||||
"@types/chai": "^4.1.7",
|
"@types/chai": "^4.1.7",
|
||||||
"@types/chromecast-caf-receiver": "^3.0.12",
|
"@types/chromecast-caf-receiver": "^3.0.12",
|
||||||
"@types/chromecast-caf-sender": "^1.0.1",
|
"@types/chromecast-caf-sender": "^1.0.1",
|
||||||
@ -154,18 +157,18 @@
|
|||||||
"merge-stream": "^1.0.1",
|
"merge-stream": "^1.0.1",
|
||||||
"mocha": "^6.0.2",
|
"mocha": "^6.0.2",
|
||||||
"parse5": "^5.1.0",
|
"parse5": "^5.1.0",
|
||||||
"prettier": "^1.16.4",
|
"prettier": "^1.19.1",
|
||||||
"raw-loader": "^2.0.0",
|
"raw-loader": "^2.0.0",
|
||||||
"reify": "^0.18.1",
|
"reify": "^0.18.1",
|
||||||
"require-dir": "^1.2.0",
|
"require-dir": "^1.2.0",
|
||||||
"sinon": "^7.3.1",
|
"sinon": "^7.3.1",
|
||||||
"terser-webpack-plugin": "^1.2.3",
|
"terser-webpack-plugin": "^1.2.3",
|
||||||
"ts-mocha": "^6.0.0",
|
"ts-mocha": "^6.0.0",
|
||||||
"tslint": "^5.14.0",
|
"tslint": "^5.20.1",
|
||||||
"tslint-config-prettier": "^1.18.0",
|
"tslint-config-prettier": "^1.18.0",
|
||||||
"tslint-eslint-rules": "^5.4.0",
|
"tslint-eslint-rules": "^5.4.0",
|
||||||
"tslint-plugin-prettier": "^2.0.1",
|
"tslint-plugin-prettier": "^2.0.1",
|
||||||
"typescript": "^3.6.3",
|
"typescript": "^3.7.2",
|
||||||
"web-component-tester": "^6.9.2",
|
"web-component-tester": "^6.9.2",
|
||||||
"webpack": "^4.40.2",
|
"webpack": "^4.40.2",
|
||||||
"webpack-cli": "^3.3.9",
|
"webpack-cli": "^3.3.9",
|
||||||
@ -178,7 +181,6 @@
|
|||||||
"_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569",
|
"_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569",
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@webcomponents/webcomponentsjs": "^2.2.10",
|
"@webcomponents/webcomponentsjs": "^2.2.10",
|
||||||
"@vaadin/vaadin-lumo-styles": "^1.4.2",
|
|
||||||
"@polymer/polymer": "3.1.0",
|
"@polymer/polymer": "3.1.0",
|
||||||
"lit-html": "^1.1.2"
|
"lit-html": "^1.1.2"
|
||||||
},
|
},
|
||||||
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="home-assistant-frontend",
|
name="home-assistant-frontend",
|
||||||
version="20191119.6",
|
version="20191204.0",
|
||||||
description="The Home Assistant frontend",
|
description="The Home Assistant frontend",
|
||||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||||
author="The Home Assistant Authors",
|
author="The Home Assistant Authors",
|
||||||
|
@ -98,9 +98,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
<ha-markdown
|
<ha-markdown
|
||||||
allowsvg
|
allowsvg
|
||||||
.content=${this.localize(
|
.content=${this.localize(
|
||||||
`ui.panel.page-authorize.form.providers.${
|
`ui.panel.page-authorize.form.providers.${step.handler[0]}.abort.${step.reason}`
|
||||||
step.handler[0]
|
|
||||||
}.abort.${step.reason}`
|
|
||||||
)}
|
)}
|
||||||
></ha-markdown>
|
></ha-markdown>
|
||||||
`;
|
`;
|
||||||
@ -229,9 +227,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _computeStepDescription(step: DataEntryFlowStepForm) {
|
private _computeStepDescription(step: DataEntryFlowStepForm) {
|
||||||
const resourceKey = `ui.panel.page-authorize.form.providers.${
|
const resourceKey = `ui.panel.page-authorize.form.providers.${step.handler[0]}.step.${step.step_id}.description`;
|
||||||
step.handler[0]
|
|
||||||
}.step.${step.step_id}.description`;
|
|
||||||
const args: string[] = [];
|
const args: string[] = [];
|
||||||
const placeholders = step.description_placeholders || {};
|
const placeholders = step.description_placeholders || {};
|
||||||
Object.keys(placeholders).forEach((key) => {
|
Object.keys(placeholders).forEach((key) => {
|
||||||
@ -245,9 +241,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
// Returns a callback for ha-form to calculate labels per schema object
|
// Returns a callback for ha-form to calculate labels per schema object
|
||||||
return (schema) =>
|
return (schema) =>
|
||||||
this.localize(
|
this.localize(
|
||||||
`ui.panel.page-authorize.form.providers.${step.handler[0]}.step.${
|
`ui.panel.page-authorize.form.providers.${step.handler[0]}.step.${step.step_id}.data.${schema.name}`
|
||||||
step.step_id
|
|
||||||
}.data.${schema.name}`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,9 +249,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
|||||||
// Returns a callback for ha-form to calculate error messages
|
// Returns a callback for ha-form to calculate error messages
|
||||||
return (error) =>
|
return (error) =>
|
||||||
this.localize(
|
this.localize(
|
||||||
`ui.panel.page-authorize.form.providers.${
|
`ui.panel.page-authorize.form.providers.${step.handler[0]}.error.${error}`
|
||||||
step.handler[0]
|
|
||||||
}.error.${error}`
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,9 @@ import "./ha-auth-flow";
|
|||||||
import { AuthProvider, fetchAuthProviders } from "../data/auth";
|
import { AuthProvider, fetchAuthProviders } from "../data/auth";
|
||||||
import { registerServiceWorker } from "../util/register-service-worker";
|
import { registerServiceWorker } from "../util/register-service-worker";
|
||||||
|
|
||||||
import(/* webpackChunkName: "pick-auth-provider" */ "../auth/ha-pick-auth-provider");
|
import(
|
||||||
|
/* webpackChunkName: "pick-auth-provider" */ "../auth/ha-pick-auth-provider"
|
||||||
|
);
|
||||||
|
|
||||||
interface QueryParams {
|
interface QueryParams {
|
||||||
client_id?: string;
|
client_id?: string;
|
||||||
|
@ -10,11 +10,11 @@ function toLocaleDateStringSupportsOptions() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (toLocaleDateStringSupportsOptions()
|
export default toLocaleDateStringSupportsOptions()
|
||||||
? (dateObj: Date, locales: string) =>
|
? (dateObj: Date, locales: string) =>
|
||||||
dateObj.toLocaleDateString(locales, {
|
dateObj.toLocaleDateString(locales, {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "long",
|
month: "long",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
})
|
})
|
||||||
: (dateObj: Date) => fecha.format(dateObj, "mediumDate"));
|
: (dateObj: Date) => fecha.format(dateObj, "mediumDate");
|
||||||
|
@ -10,7 +10,7 @@ function toLocaleStringSupportsOptions() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (toLocaleStringSupportsOptions()
|
export default toLocaleStringSupportsOptions()
|
||||||
? (dateObj: Date, locales: string) =>
|
? (dateObj: Date, locales: string) =>
|
||||||
dateObj.toLocaleString(locales, {
|
dateObj.toLocaleString(locales, {
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
@ -19,4 +19,4 @@ export default (toLocaleStringSupportsOptions()
|
|||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
})
|
})
|
||||||
: (dateObj: Date) => fecha.format(dateObj, "haDateTime"));
|
: (dateObj: Date) => fecha.format(dateObj, "haDateTime");
|
||||||
|
@ -10,10 +10,10 @@ function toLocaleTimeStringSupportsOptions() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (toLocaleTimeStringSupportsOptions()
|
export default toLocaleTimeStringSupportsOptions()
|
||||||
? (dateObj: Date, locales: string) =>
|
? (dateObj: Date, locales: string) =>
|
||||||
dateObj.toLocaleTimeString(locales, {
|
dateObj.toLocaleTimeString(locales, {
|
||||||
hour: "numeric",
|
hour: "numeric",
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
})
|
})
|
||||||
: (dateObj: Date) => fecha.format(dateObj, "shortTime"));
|
: (dateObj: Date) => fecha.format(dateObj, "shortTime");
|
||||||
|
@ -60,7 +60,7 @@ export const applyThemesOnElement = (
|
|||||||
element.updateStyles(styles);
|
element.updateStyles(styles);
|
||||||
} else if (window.ShadyCSS) {
|
} else if (window.ShadyCSS) {
|
||||||
// implement updateStyles() method of Polymer elements
|
// implement updateStyles() method of Polymer elements
|
||||||
window.ShadyCSS.styleSubtree(/** @type {!HTMLElement} */ (element), styles);
|
window.ShadyCSS.styleSubtree(/** @type {!HTMLElement} */ element, styles);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!updateMeta) {
|
if (!updateMeta) {
|
||||||
|
29
src/common/dom/dynamic-content-directive.ts
Normal file
29
src/common/dom/dynamic-content-directive.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { directive, Part, NodePart } from "lit-html";
|
||||||
|
|
||||||
|
export const dynamicContentDirective = directive(
|
||||||
|
(tag: string, properties: { [key: string]: any }) => (part: Part): void => {
|
||||||
|
if (!(part instanceof NodePart)) {
|
||||||
|
throw new Error(
|
||||||
|
"dynamicContentDirective can only be used in content bindings"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let element = part.value as HTMLElement | undefined;
|
||||||
|
|
||||||
|
if (
|
||||||
|
element !== undefined &&
|
||||||
|
tag.toUpperCase() === (element as HTMLElement).tagName
|
||||||
|
) {
|
||||||
|
Object.entries(properties).forEach(([key, value]) => {
|
||||||
|
element![key] = value;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
element = document.createElement(tag);
|
||||||
|
Object.entries(properties).forEach(([key, value]) => {
|
||||||
|
element![key] = value;
|
||||||
|
});
|
||||||
|
part.setValue(element);
|
||||||
|
}
|
||||||
|
);
|
@ -11,7 +11,9 @@ export const setupLeafletMap = async (
|
|||||||
throw new Error("Cannot setup Leaflet map on disconnected element");
|
throw new Error("Cannot setup Leaflet map on disconnected element");
|
||||||
}
|
}
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
const Leaflet = (await import(/* webpackChunkName: "leaflet" */ "leaflet")) as LeafletModuleType;
|
const Leaflet = (await import(
|
||||||
|
/* webpackChunkName: "leaflet" */ "leaflet"
|
||||||
|
)) as LeafletModuleType;
|
||||||
Leaflet.Icon.Default.imagePath = "/static/images/leaflet/images/";
|
Leaflet.Icon.Default.imagePath = "/static/images/leaflet/images/";
|
||||||
|
|
||||||
const map = Leaflet.map(mapElement);
|
const map = Leaflet.map(mapElement);
|
||||||
|
@ -7,8 +7,11 @@
|
|||||||
|
|
||||||
// export function onChangeEvent(this: OnChangeComponent, prop, ev) {
|
// export function onChangeEvent(this: OnChangeComponent, prop, ev) {
|
||||||
export function onChangeEvent(this: any, prop, ev) {
|
export function onChangeEvent(this: any, prop, ev) {
|
||||||
const origData = this.props[prop];
|
if (!this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const origData = this.props[prop];
|
||||||
if (ev.target.value === origData[ev.target.name]) {
|
if (ev.target.value === origData[ev.target.name]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,9 @@ import {
|
|||||||
MDCDataTableFoundation,
|
MDCDataTableFoundation,
|
||||||
} from "@material/data-table";
|
} from "@material/data-table";
|
||||||
|
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BaseElement,
|
|
||||||
html,
|
html,
|
||||||
query,
|
query,
|
||||||
queryAll,
|
queryAll,
|
||||||
@ -15,10 +16,11 @@ import {
|
|||||||
css,
|
css,
|
||||||
customElement,
|
customElement,
|
||||||
property,
|
property,
|
||||||
classMap,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
} from "@material/mwc-base/base-element";
|
} from "lit-element";
|
||||||
|
|
||||||
|
import { BaseElement } from "@material/mwc-base/base-element";
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-webpack-loader-syntax
|
// eslint-disable-next-line import/no-webpack-loader-syntax
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
412
src/components/device/ha-area-devices-picker.ts
Normal file
412
src/components/device/ha-area-devices-picker.ts
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-item/paper-item-body";
|
||||||
|
import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
property,
|
||||||
|
PropertyValues,
|
||||||
|
} from "lit-element";
|
||||||
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
|
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||||
|
import "./ha-devices-picker";
|
||||||
|
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
} from "../../data/device_registry";
|
||||||
|
import { compare } from "../../common/string/compare";
|
||||||
|
import { PolymerChangedEvent } from "../../polymer-types";
|
||||||
|
import {
|
||||||
|
AreaRegistryEntry,
|
||||||
|
subscribeAreaRegistry,
|
||||||
|
} from "../../data/area_registry";
|
||||||
|
import { DeviceEntityLookup } from "../../panels/config/devices/ha-devices-data-table";
|
||||||
|
import {
|
||||||
|
EntityRegistryEntry,
|
||||||
|
subscribeEntityRegistry,
|
||||||
|
} from "../../data/entity_registry";
|
||||||
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
|
|
||||||
|
interface DevicesByArea {
|
||||||
|
[areaId: string]: AreaDevices;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AreaDevices {
|
||||||
|
id?: string;
|
||||||
|
name: string;
|
||||||
|
devices: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowRenderer = (
|
||||||
|
root: HTMLElement,
|
||||||
|
_owner,
|
||||||
|
model: { item: AreaDevices }
|
||||||
|
) => {
|
||||||
|
if (!root.firstElementChild) {
|
||||||
|
root.innerHTML = `
|
||||||
|
<style>
|
||||||
|
paper-item {
|
||||||
|
width: 100%;
|
||||||
|
margin: -10px 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
paper-icon-button {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.devices {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.devices.visible {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-item>
|
||||||
|
<paper-item-body two-line="">
|
||||||
|
<div class='name'>[[item.name]]</div>
|
||||||
|
<div secondary>[[item.devices.length]] devices</div>
|
||||||
|
</paper-item-body>
|
||||||
|
</paper-item>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
root.querySelector(".name")!.textContent = model.item.name!;
|
||||||
|
root.querySelector(
|
||||||
|
"[secondary]"
|
||||||
|
)!.textContent = `${model.item.devices.length.toString()} devices`;
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("ha-area-devices-picker")
|
||||||
|
export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public label?: string;
|
||||||
|
@property() public value?: string;
|
||||||
|
@property() public area?: string;
|
||||||
|
@property() public devices?: string[];
|
||||||
|
/**
|
||||||
|
* Show only devices with entities from specific domains.
|
||||||
|
* @type {Array}
|
||||||
|
* @attr include-domains
|
||||||
|
*/
|
||||||
|
@property({ type: Array, attribute: "include-domains" })
|
||||||
|
public includeDomains?: string[];
|
||||||
|
/**
|
||||||
|
* Show no devices with entities of these domains.
|
||||||
|
* @type {Array}
|
||||||
|
* @attr exclude-domains
|
||||||
|
*/
|
||||||
|
@property({ type: Array, attribute: "exclude-domains" })
|
||||||
|
public excludeDomains?: string[];
|
||||||
|
/**
|
||||||
|
* Show only deviced with entities of these device classes.
|
||||||
|
* @type {Array}
|
||||||
|
* @attr include-device-classes
|
||||||
|
*/
|
||||||
|
@property({ type: Array, attribute: "include-device-classes" })
|
||||||
|
public includeDeviceClasses?: string[];
|
||||||
|
@property({ type: Boolean })
|
||||||
|
private _opened?: boolean;
|
||||||
|
@property() private _areaPicker = true;
|
||||||
|
@property() private _devices?: DeviceRegistryEntry[];
|
||||||
|
@property() private _areas?: AreaRegistryEntry[];
|
||||||
|
@property() private _entities?: EntityRegistryEntry[];
|
||||||
|
private _selectedDevices: string[] = [];
|
||||||
|
private _filteredDevices: DeviceRegistryEntry[] = [];
|
||||||
|
|
||||||
|
private _getDevices = memoizeOne(
|
||||||
|
(
|
||||||
|
devices: DeviceRegistryEntry[],
|
||||||
|
areas: AreaRegistryEntry[],
|
||||||
|
entities: EntityRegistryEntry[],
|
||||||
|
includeDomains: this["includeDomains"],
|
||||||
|
excludeDomains: this["excludeDomains"],
|
||||||
|
includeDeviceClasses: this["includeDeviceClasses"]
|
||||||
|
): AreaDevices[] => {
|
||||||
|
if (!devices.length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const deviceEntityLookup: DeviceEntityLookup = {};
|
||||||
|
for (const entity of entities) {
|
||||||
|
if (!entity.device_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(entity.device_id in deviceEntityLookup)) {
|
||||||
|
deviceEntityLookup[entity.device_id] = [];
|
||||||
|
}
|
||||||
|
deviceEntityLookup[entity.device_id].push(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputDevices = [...devices];
|
||||||
|
|
||||||
|
if (includeDomains) {
|
||||||
|
inputDevices = inputDevices.filter((device) => {
|
||||||
|
const devEntities = deviceEntityLookup[device.id];
|
||||||
|
if (!devEntities || !devEntities.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return deviceEntityLookup[device.id].some((entity) =>
|
||||||
|
includeDomains.includes(computeDomain(entity.entity_id))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludeDomains) {
|
||||||
|
inputDevices = inputDevices.filter((device) => {
|
||||||
|
const devEntities = deviceEntityLookup[device.id];
|
||||||
|
if (!devEntities || !devEntities.length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return entities.every(
|
||||||
|
(entity) =>
|
||||||
|
!excludeDomains.includes(computeDomain(entity.entity_id))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeDeviceClasses) {
|
||||||
|
inputDevices = inputDevices.filter((device) => {
|
||||||
|
const devEntities = deviceEntityLookup[device.id];
|
||||||
|
if (!devEntities || !devEntities.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return deviceEntityLookup[device.id].some((entity) => {
|
||||||
|
const stateObj = this.hass.states[entity.entity_id];
|
||||||
|
if (!stateObj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
stateObj.attributes.device_class &&
|
||||||
|
includeDeviceClasses.includes(stateObj.attributes.device_class)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this._filteredDevices = inputDevices;
|
||||||
|
|
||||||
|
const areaLookup: { [areaId: string]: AreaRegistryEntry } = {};
|
||||||
|
for (const area of areas) {
|
||||||
|
areaLookup[area.area_id] = area;
|
||||||
|
}
|
||||||
|
|
||||||
|
const devicesByArea: DevicesByArea = {};
|
||||||
|
|
||||||
|
for (const device of inputDevices) {
|
||||||
|
const areaId = device.area_id;
|
||||||
|
if (areaId) {
|
||||||
|
if (!(areaId in devicesByArea)) {
|
||||||
|
devicesByArea[areaId] = {
|
||||||
|
id: areaId,
|
||||||
|
name: areaLookup[areaId].name,
|
||||||
|
devices: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
devicesByArea[areaId].devices.push(device.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sorted = Object.keys(devicesByArea)
|
||||||
|
.sort((a, b) =>
|
||||||
|
compare(devicesByArea[a].name || "", devicesByArea[b].name || "")
|
||||||
|
)
|
||||||
|
.map((key) => devicesByArea[key]);
|
||||||
|
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
|
return [
|
||||||
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
||||||
|
this._devices = devices;
|
||||||
|
}),
|
||||||
|
subscribeAreaRegistry(this.hass.connection!, (areas) => {
|
||||||
|
this._areas = areas;
|
||||||
|
}),
|
||||||
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||||
|
this._entities = entities;
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
if (changedProps.has("area") && this.area) {
|
||||||
|
this._areaPicker = true;
|
||||||
|
this.value = this.area;
|
||||||
|
} else if (changedProps.has("devices") && this.devices) {
|
||||||
|
this._areaPicker = false;
|
||||||
|
const filteredDeviceIds = this._filteredDevices.map(
|
||||||
|
(device) => device.id
|
||||||
|
);
|
||||||
|
const selectedDevices = this.devices.filter((device) =>
|
||||||
|
filteredDeviceIds.includes(device)
|
||||||
|
);
|
||||||
|
this._setValue(selectedDevices);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
if (!this._devices || !this._areas || !this._entities) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const areas = this._getDevices(
|
||||||
|
this._devices,
|
||||||
|
this._areas,
|
||||||
|
this._entities,
|
||||||
|
this.includeDomains,
|
||||||
|
this.excludeDomains,
|
||||||
|
this.includeDeviceClasses
|
||||||
|
);
|
||||||
|
if (!this._areaPicker || areas.length === 0) {
|
||||||
|
return html`
|
||||||
|
<ha-devices-picker
|
||||||
|
@value-changed=${this._devicesPicked}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.includeDomains=${this.includeDomains}
|
||||||
|
.includeDeviceClasses=${this.includeDeviceClasses}
|
||||||
|
.value=${this._selectedDevices}
|
||||||
|
.pickDeviceLabel=${`Add ${this.label} device`}
|
||||||
|
.pickedDeviceLabel=${`${this.label} device`}
|
||||||
|
></ha-devices-picker>
|
||||||
|
${areas.length > 0
|
||||||
|
? html`
|
||||||
|
<mwc-button @click=${this._switchPicker}
|
||||||
|
>Choose an area</mwc-button
|
||||||
|
>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<vaadin-combo-box-light
|
||||||
|
item-value-path="id"
|
||||||
|
item-id-path="id"
|
||||||
|
item-label-path="name"
|
||||||
|
.items=${areas}
|
||||||
|
.value=${this._value}
|
||||||
|
.renderer=${rowRenderer}
|
||||||
|
@opened-changed=${this._openedChanged}
|
||||||
|
@value-changed=${this._areaPicked}
|
||||||
|
>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.label === undefined && this.hass
|
||||||
|
? this.hass.localize("ui.components.device-picker.device")
|
||||||
|
: `${this.label} in area`}
|
||||||
|
class="input"
|
||||||
|
autocapitalize="none"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
spellcheck="false"
|
||||||
|
>
|
||||||
|
${this.value
|
||||||
|
? html`
|
||||||
|
<paper-icon-button
|
||||||
|
aria-label=${this.hass.localize(
|
||||||
|
"ui.components.device-picker.clear"
|
||||||
|
)}
|
||||||
|
slot="suffix"
|
||||||
|
class="clear-button"
|
||||||
|
icon="hass:close"
|
||||||
|
@click=${this._clearValue}
|
||||||
|
no-ripple
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</paper-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${areas.length > 0
|
||||||
|
? html`
|
||||||
|
<paper-icon-button
|
||||||
|
aria-label=${this.hass.localize(
|
||||||
|
"ui.components.device-picker.show_devices"
|
||||||
|
)}
|
||||||
|
slot="suffix"
|
||||||
|
class="toggle-button"
|
||||||
|
.icon=${this._opened ? "hass:menu-up" : "hass:menu-down"}
|
||||||
|
>
|
||||||
|
Toggle
|
||||||
|
</paper-icon-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</paper-input>
|
||||||
|
</vaadin-combo-box-light>
|
||||||
|
<mwc-button @click=${this._switchPicker}
|
||||||
|
>Choose individual devices</mwc-button
|
||||||
|
>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearValue(ev: Event) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._setValue([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _value() {
|
||||||
|
return this.value || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openedChanged(ev: PolymerChangedEvent<boolean>) {
|
||||||
|
this._opened = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _switchPicker() {
|
||||||
|
this._areaPicker = !this._areaPicker;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _areaPicked(ev: PolymerChangedEvent<string>) {
|
||||||
|
const value = ev.detail.value;
|
||||||
|
let selectedDevices = [];
|
||||||
|
const target = ev.target as any;
|
||||||
|
if (target.selectedItem) {
|
||||||
|
selectedDevices = target.selectedItem.devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value !== this._value || this._selectedDevices !== selectedDevices) {
|
||||||
|
this._setValue(selectedDevices, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _devicesPicked(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const selectedDevices = ev.detail.value;
|
||||||
|
this._setValue(selectedDevices);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setValue(selectedDevices: string[], value = "") {
|
||||||
|
this.value = value;
|
||||||
|
this._selectedDevices = selectedDevices;
|
||||||
|
setTimeout(() => {
|
||||||
|
fireEvent(this, "value-changed", { value: selectedDevices });
|
||||||
|
fireEvent(this, "change");
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
paper-input > paper-icon-button {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
padding: 2px;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-area-devices-picker": HaAreaDevicesPicker;
|
||||||
|
}
|
||||||
|
}
|
@ -176,6 +176,7 @@ export abstract class HaDeviceAutomationPicker<
|
|||||||
this.value = automation;
|
this.value = automation;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
fireEvent(this, "change");
|
fireEvent(this, "change");
|
||||||
|
fireEvent(this, "value-changed", { value: automation });
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
import "@vaadin/vaadin-combo-box/vaadin-combo-box-light";
|
import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import {
|
import {
|
||||||
|
127
src/components/device/ha-devices-picker.ts
Normal file
127
src/components/device/ha-devices-picker.ts
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
property,
|
||||||
|
html,
|
||||||
|
customElement,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@polymer/paper-icon-button/paper-icon-button-light";
|
||||||
|
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { PolymerChangedEvent } from "../../polymer-types";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
|
||||||
|
import "./ha-device-picker";
|
||||||
|
|
||||||
|
@customElement("ha-devices-picker")
|
||||||
|
class HaDevicesPicker extends LitElement {
|
||||||
|
@property() public hass?: HomeAssistant;
|
||||||
|
@property() public value?: string[];
|
||||||
|
/**
|
||||||
|
* Show entities from specific domains.
|
||||||
|
* @type {string}
|
||||||
|
* @attr include-domains
|
||||||
|
*/
|
||||||
|
@property({ type: Array, attribute: "include-domains" })
|
||||||
|
public includeDomains?: string[];
|
||||||
|
/**
|
||||||
|
* Show no entities of these domains.
|
||||||
|
* @type {Array}
|
||||||
|
* @attr exclude-domains
|
||||||
|
*/
|
||||||
|
@property({ type: Array, attribute: "exclude-domains" })
|
||||||
|
public excludeDomains?: string[];
|
||||||
|
@property({ attribute: "picked-device-label" })
|
||||||
|
@property({ type: Array, attribute: "include-device-classes" })
|
||||||
|
public includeDeviceClasses?: string[];
|
||||||
|
public pickedDeviceLabel?: string;
|
||||||
|
@property({ attribute: "pick-device-label" }) public pickDeviceLabel?: string;
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
if (!this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDevices = this._currentDevices;
|
||||||
|
return html`
|
||||||
|
${currentDevices.map(
|
||||||
|
(entityId) => html`
|
||||||
|
<div>
|
||||||
|
<ha-device-picker
|
||||||
|
allow-custom-entity
|
||||||
|
.curValue=${entityId}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.includeDomains=${this.includeDomains}
|
||||||
|
.excludeDomains=${this.excludeDomains}
|
||||||
|
.includeDeviceClasses=${this.includeDeviceClasses}
|
||||||
|
.value=${entityId}
|
||||||
|
.label=${this.pickedDeviceLabel}
|
||||||
|
@value-changed=${this._deviceChanged}
|
||||||
|
></ha-device-picker>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<ha-device-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.includeDomains=${this.includeDomains}
|
||||||
|
.excludeDomains=${this.excludeDomains}
|
||||||
|
.includeDeviceClasses=${this.includeDeviceClasses}
|
||||||
|
.label=${this.pickDeviceLabel}
|
||||||
|
@value-changed=${this._addDevice}
|
||||||
|
></ha-device-picker>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get _currentDevices() {
|
||||||
|
return this.value || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateDevices(devices) {
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: devices,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.value = devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deviceChanged(event: PolymerChangedEvent<string>) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const curValue = (event.currentTarget as any).curValue;
|
||||||
|
const newValue = event.detail.value;
|
||||||
|
if (newValue === curValue || newValue !== "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (newValue === "") {
|
||||||
|
this._updateDevices(
|
||||||
|
this._currentDevices.filter((dev) => dev !== curValue)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this._updateDevices(
|
||||||
|
this._currentDevices.map((dev) => (dev === curValue ? newValue : dev))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _addDevice(event: PolymerChangedEvent<string>) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const toAdd = event.detail.value;
|
||||||
|
(event.currentTarget as any).value = "";
|
||||||
|
if (!toAdd) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentDevices = this._currentDevices;
|
||||||
|
if (currentDevices.includes(toAdd)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._updateDevices([...currentDevices, toAdd]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-devices-picker": HaDevicesPicker;
|
||||||
|
}
|
||||||
|
}
|
@ -215,7 +215,9 @@ class HaChartBase extends mixinBehaviors(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (scriptsLoaded === null) {
|
if (scriptsLoaded === null) {
|
||||||
scriptsLoaded = import(/* webpackChunkName: "load_chart" */ "../../resources/ha-chart-scripts.js");
|
scriptsLoaded = import(
|
||||||
|
/* webpackChunkName: "load_chart" */ "../../resources/ha-chart-scripts.js"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
scriptsLoaded.then((ChartModule) => {
|
scriptsLoaded.then((ChartModule) => {
|
||||||
this.ChartClass = ChartModule.default;
|
this.ChartClass = ChartModule.default;
|
||||||
|
@ -2,7 +2,7 @@ import "@polymer/paper-icon-button/paper-icon-button";
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
import "@vaadin/vaadin-combo-box/vaadin-combo-box-light";
|
import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
|
|
||||||
import "./state-badge";
|
import "./state-badge";
|
||||||
|
@ -122,8 +122,9 @@ class HaCameraStream extends LitElement {
|
|||||||
|
|
||||||
private async _startHls(): Promise<void> {
|
private async _startHls(): Promise<void> {
|
||||||
// tslint:disable-next-line
|
// tslint:disable-next-line
|
||||||
const Hls = ((await import(/* webpackChunkName: "hls.js" */ "hls.js")) as any)
|
const Hls = ((await import(
|
||||||
.default as HLSModule;
|
/* webpackChunkName: "hls.js" */ "hls.js"
|
||||||
|
)) as any).default as HLSModule;
|
||||||
let hlsSupported = Hls.isSupported();
|
let hlsSupported = Hls.isSupported();
|
||||||
const videoEl = this._videoEl;
|
const videoEl = this._videoEl;
|
||||||
|
|
||||||
|
@ -72,9 +72,7 @@ class HaClimateState extends LocalizeMixin(PolymerElement) {
|
|||||||
computeCurrentStatus(hass, stateObj) {
|
computeCurrentStatus(hass, stateObj) {
|
||||||
if (!hass || !stateObj) return null;
|
if (!hass || !stateObj) return null;
|
||||||
if (stateObj.attributes.current_temperature != null) {
|
if (stateObj.attributes.current_temperature != null) {
|
||||||
return `${stateObj.attributes.current_temperature} ${
|
return `${stateObj.attributes.current_temperature} ${hass.config.unit_system.temperature}`;
|
||||||
hass.config.unit_system.temperature
|
|
||||||
}`;
|
|
||||||
}
|
}
|
||||||
if (stateObj.attributes.current_humidity != null) {
|
if (stateObj.attributes.current_humidity != null) {
|
||||||
return `${stateObj.attributes.current_humidity} %`;
|
return `${stateObj.attributes.current_humidity} %`;
|
||||||
@ -89,22 +87,16 @@ class HaClimateState extends LocalizeMixin(PolymerElement) {
|
|||||||
stateObj.attributes.target_temp_low != null &&
|
stateObj.attributes.target_temp_low != null &&
|
||||||
stateObj.attributes.target_temp_high != null
|
stateObj.attributes.target_temp_high != null
|
||||||
) {
|
) {
|
||||||
return `${stateObj.attributes.target_temp_low}-${
|
return `${stateObj.attributes.target_temp_low}-${stateObj.attributes.target_temp_high} ${hass.config.unit_system.temperature}`;
|
||||||
stateObj.attributes.target_temp_high
|
|
||||||
} ${hass.config.unit_system.temperature}`;
|
|
||||||
}
|
}
|
||||||
if (stateObj.attributes.temperature != null) {
|
if (stateObj.attributes.temperature != null) {
|
||||||
return `${stateObj.attributes.temperature} ${
|
return `${stateObj.attributes.temperature} ${hass.config.unit_system.temperature}`;
|
||||||
hass.config.unit_system.temperature
|
|
||||||
}`;
|
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
stateObj.attributes.target_humidity_low != null &&
|
stateObj.attributes.target_humidity_low != null &&
|
||||||
stateObj.attributes.target_humidity_high != null
|
stateObj.attributes.target_humidity_high != null
|
||||||
) {
|
) {
|
||||||
return `${stateObj.attributes.target_humidity_low}-${
|
return `${stateObj.attributes.target_humidity_low}-${stateObj.attributes.target_humidity_high}%`;
|
||||||
stateObj.attributes.target_humidity_high
|
|
||||||
}%`;
|
|
||||||
}
|
}
|
||||||
if (stateObj.attributes.humidity != null) {
|
if (stateObj.attributes.humidity != null) {
|
||||||
return `${stateObj.attributes.humidity} %`;
|
return `${stateObj.attributes.humidity} %`;
|
||||||
@ -121,9 +113,7 @@ class HaClimateState extends LocalizeMixin(PolymerElement) {
|
|||||||
const stateString = localize(`state.climate.${stateObj.state}`);
|
const stateString = localize(`state.climate.${stateObj.state}`);
|
||||||
return stateObj.attributes.hvac_action
|
return stateObj.attributes.hvac_action
|
||||||
? `${localize(
|
? `${localize(
|
||||||
`state_attributes.climate.hvac_action.${
|
`state_attributes.climate.hvac_action.${stateObj.attributes.hvac_action}`
|
||||||
stateObj.attributes.hvac_action
|
|
||||||
}`
|
|
||||||
)} (${stateString})`
|
)} (${stateString})`
|
||||||
: stateString;
|
: stateString;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import "@polymer/paper-input/paper-input";
|
|||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
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 "@vaadin/vaadin-combo-box/vaadin-combo-box-light";
|
import "@vaadin/vaadin-combo-box/theme/material/vaadin-combo-box-light";
|
||||||
|
|
||||||
import { EventsMixin } from "../mixins/events-mixin";
|
import { EventsMixin } from "../mixins/events-mixin";
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { classMap, html, customElement } from "@material/mwc-base/base-element";
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
import { html, customElement } from "lit-element";
|
||||||
import { ripple } from "@material/mwc-ripple/ripple-directive.js";
|
import { ripple } from "@material/mwc-ripple/ripple-directive.js";
|
||||||
|
|
||||||
import "@material/mwc-fab";
|
import "@material/mwc-fab";
|
||||||
|
@ -58,14 +58,10 @@ class HaWaterHeaterState extends LocalizeMixin(PolymerElement) {
|
|||||||
stateObj.attributes.target_temp_low != null &&
|
stateObj.attributes.target_temp_low != null &&
|
||||||
stateObj.attributes.target_temp_high != null
|
stateObj.attributes.target_temp_high != null
|
||||||
) {
|
) {
|
||||||
return `${stateObj.attributes.target_temp_low} - ${
|
return `${stateObj.attributes.target_temp_low} - ${stateObj.attributes.target_temp_high} ${hass.config.unit_system.temperature}`;
|
||||||
stateObj.attributes.target_temp_high
|
|
||||||
} ${hass.config.unit_system.temperature}`;
|
|
||||||
}
|
}
|
||||||
if (stateObj.attributes.temperature != null) {
|
if (stateObj.attributes.temperature != null) {
|
||||||
return `${stateObj.attributes.temperature} ${
|
return `${stateObj.attributes.temperature} ${hass.config.unit_system.temperature}`;
|
||||||
hass.config.unit_system.temperature
|
|
||||||
}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
81
src/components/ha-yaml-editor.ts
Normal file
81
src/components/ha-yaml-editor.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { safeDump, safeLoad } from "js-yaml";
|
||||||
|
import "./ha-code-editor";
|
||||||
|
import { LitElement, property, customElement, html } from "lit-element";
|
||||||
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
|
|
||||||
|
const isEmpty = (obj: object) => {
|
||||||
|
for (const key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("ha-yaml-editor")
|
||||||
|
export class HaYamlEditor extends LitElement {
|
||||||
|
@property() public value?: any;
|
||||||
|
@property() public isValid = true;
|
||||||
|
@property() public label?: string;
|
||||||
|
@property() private _yaml?: string;
|
||||||
|
|
||||||
|
protected firstUpdated() {
|
||||||
|
try {
|
||||||
|
this._yaml =
|
||||||
|
this.value && !isEmpty(this.value) ? safeDump(this.value) : "";
|
||||||
|
} catch (err) {
|
||||||
|
alert(`There was an error converting to YAML: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (this._yaml === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
${this.label
|
||||||
|
? html`
|
||||||
|
<p>${this.label}</p>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<ha-code-editor
|
||||||
|
.value=${this._yaml}
|
||||||
|
mode="yaml"
|
||||||
|
.error=${this.isValid === false}
|
||||||
|
@value-changed=${this._onChange}
|
||||||
|
></ha-code-editor>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onChange(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const value = ev.detail.value;
|
||||||
|
let parsed;
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
try {
|
||||||
|
parsed = safeLoad(value);
|
||||||
|
isValid = true;
|
||||||
|
} catch (err) {
|
||||||
|
// Invalid YAML
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
parsed = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = parsed;
|
||||||
|
this.isValid = isValid;
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
|
fireEvent(this, "value-changed", { value: parsed });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-yaml-editor": HaYamlEditor;
|
||||||
|
}
|
||||||
|
}
|
@ -19,9 +19,7 @@ export interface Stream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const computeMJPEGStreamUrl = (entity: CameraEntity) =>
|
export const computeMJPEGStreamUrl = (entity: CameraEntity) =>
|
||||||
`/api/camera_proxy_stream/${entity.entity_id}?token=${
|
`/api/camera_proxy_stream/${entity.entity_id}?token=${entity.attributes.access_token}`;
|
||||||
entity.attributes.access_token
|
|
||||||
}`;
|
|
||||||
|
|
||||||
export const fetchThumbnailUrlWithCache = (
|
export const fetchThumbnailUrlWithCache = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -3,7 +3,7 @@ import { HomeAssistant } from "../types";
|
|||||||
interface ProcessResults {
|
interface ProcessResults {
|
||||||
card: { [key: string]: { [key: string]: string } };
|
card: { [key: string]: { [key: string]: string } };
|
||||||
speech: {
|
speech: {
|
||||||
[SpeechType in "plain" | "ssml"]: { extra_data: any; speech: string }
|
[SpeechType in "plain" | "ssml"]: { extra_data: any; speech: string };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export interface DeviceCondition extends DeviceAutomation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceTrigger extends DeviceAutomation {
|
export interface DeviceTrigger extends DeviceAutomation {
|
||||||
platform: string;
|
platform: "device";
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchDeviceActions = (hass: HomeAssistant, deviceId: string) =>
|
export const fetchDeviceActions = (hass: HomeAssistant, deviceId: string) =>
|
||||||
@ -107,9 +107,7 @@ export const localizeDeviceAutomationAction = (
|
|||||||
state ? computeStateName(state) : "<unknown>",
|
state ? computeStateName(state) : "<unknown>",
|
||||||
"subtype",
|
"subtype",
|
||||||
hass.localize(
|
hass.localize(
|
||||||
`component.${action.domain}.device_automation.action_subtype.${
|
`component.${action.domain}.device_automation.action_subtype.${action.subtype}`
|
||||||
action.subtype
|
|
||||||
}`
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -122,16 +120,12 @@ export const localizeDeviceAutomationCondition = (
|
|||||||
? hass.states[condition.entity_id]
|
? hass.states[condition.entity_id]
|
||||||
: undefined;
|
: undefined;
|
||||||
return hass.localize(
|
return hass.localize(
|
||||||
`component.${condition.domain}.device_automation.condition_type.${
|
`component.${condition.domain}.device_automation.condition_type.${condition.type}`,
|
||||||
condition.type
|
|
||||||
}`,
|
|
||||||
"entity_name",
|
"entity_name",
|
||||||
state ? computeStateName(state) : "<unknown>",
|
state ? computeStateName(state) : "<unknown>",
|
||||||
"subtype",
|
"subtype",
|
||||||
hass.localize(
|
hass.localize(
|
||||||
`component.${condition.domain}.device_automation.condition_subtype.${
|
`component.${condition.domain}.device_automation.condition_subtype.${condition.subtype}`
|
||||||
condition.subtype
|
|
||||||
}`
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -142,16 +136,12 @@ export const localizeDeviceAutomationTrigger = (
|
|||||||
) => {
|
) => {
|
||||||
const state = trigger.entity_id ? hass.states[trigger.entity_id] : undefined;
|
const state = trigger.entity_id ? hass.states[trigger.entity_id] : undefined;
|
||||||
return hass.localize(
|
return hass.localize(
|
||||||
`component.${trigger.domain}.device_automation.trigger_type.${
|
`component.${trigger.domain}.device_automation.trigger_type.${trigger.type}`,
|
||||||
trigger.type
|
|
||||||
}`,
|
|
||||||
"entity_name",
|
"entity_name",
|
||||||
state ? computeStateName(state) : "<unknown>",
|
state ? computeStateName(state) : "<unknown>",
|
||||||
"subtype",
|
"subtype",
|
||||||
hass.localize(
|
hass.localize(
|
||||||
`component.${trigger.domain}.device_automation.trigger_subtype.${
|
`component.${trigger.domain}.device_automation.trigger_subtype.${trigger.subtype}`
|
||||||
trigger.subtype
|
|
||||||
}`
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -149,9 +149,7 @@ export const createHassioSession = async (hass: HomeAssistant) => {
|
|||||||
"POST",
|
"POST",
|
||||||
"hassio/ingress/session"
|
"hassio/ingress/session"
|
||||||
);
|
);
|
||||||
document.cookie = `ingress_session=${
|
document.cookie = `ingress_session=${response.data.session};path=/api/hassio_ingress/`;
|
||||||
response.data.session
|
|
||||||
};path=/api/hassio_ingress/`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const reloadHassioAddons = (hass: HomeAssistant) =>
|
export const reloadHassioAddons = (hass: HomeAssistant) =>
|
||||||
|
@ -18,38 +18,6 @@ export const SCENE_IGNORED_DOMAINS = [
|
|||||||
"zone",
|
"zone",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const SCENE_SAVED_ATTRIBUTES = {
|
|
||||||
light: [
|
|
||||||
"brightness",
|
|
||||||
"color_temp",
|
|
||||||
"effect",
|
|
||||||
"rgb_color",
|
|
||||||
"xy_color",
|
|
||||||
"hs_color",
|
|
||||||
],
|
|
||||||
media_player: [
|
|
||||||
"is_volume_muted",
|
|
||||||
"volume_level",
|
|
||||||
"sound_mode",
|
|
||||||
"source",
|
|
||||||
"media_content_id",
|
|
||||||
"media_content_type",
|
|
||||||
],
|
|
||||||
climate: [
|
|
||||||
"target_temperature",
|
|
||||||
"target_temperature_high",
|
|
||||||
"target_temperature_low",
|
|
||||||
"target_humidity",
|
|
||||||
"fan_mode",
|
|
||||||
"swing_mode",
|
|
||||||
"hvac_mode",
|
|
||||||
"preset_mode",
|
|
||||||
],
|
|
||||||
vacuum: ["cleaning_mode"],
|
|
||||||
fan: ["speed", "current_direction"],
|
|
||||||
water_heather: ["temperature", "operation_mode"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface SceneEntity extends HassEntityBase {
|
export interface SceneEntity extends HassEntityBase {
|
||||||
attributes: HassEntityAttributeBase & { id?: string };
|
attributes: HassEntityAttributeBase & { id?: string };
|
||||||
}
|
}
|
||||||
|
@ -98,9 +98,7 @@ class DialogConfigEntrySystemOptions extends LitElement {
|
|||||||
"ui.dialogs.config_entry_system_options.enable_new_entities_description",
|
"ui.dialogs.config_entry_system_options.enable_new_entities_description",
|
||||||
"integration",
|
"integration",
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
`component.${
|
`component.${this._params.entry.domain}.config.title`
|
||||||
this._params.entry.domain
|
|
||||||
}.config.title`
|
|
||||||
) || this._params.entry.domain
|
) || this._params.entry.domain
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
|
@ -10,7 +10,9 @@ export interface ConfigEntrySystemOptionsDialogParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const loadConfigEntrySystemOptionsDialog = () =>
|
export const loadConfigEntrySystemOptionsDialog = () =>
|
||||||
import(/* webpackChunkName: "config-entry-system-options" */ "./dialog-config-entry-system-options");
|
import(
|
||||||
|
/* webpackChunkName: "config-entry-system-options" */ "./dialog-config-entry-system-options"
|
||||||
|
);
|
||||||
|
|
||||||
export const showConfigEntrySystemOptionsDialog = (
|
export const showConfigEntrySystemOptionsDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
@ -71,9 +71,7 @@ export const showConfigFlowDialog = (
|
|||||||
|
|
||||||
renderShowFormStepFieldLabel(hass, step, field) {
|
renderShowFormStepFieldLabel(hass, step, field) {
|
||||||
return hass.localize(
|
return hass.localize(
|
||||||
`component.${step.handler}.config.step.${step.step_id}.data.${
|
`component.${step.handler}.config.step.${step.step_id}.data.${field.name}`
|
||||||
field.name
|
|
||||||
}`
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -79,7 +79,9 @@ export interface DataEntryFlowDialogParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const loadDataEntryFlowDialog = () =>
|
export const loadDataEntryFlowDialog = () =>
|
||||||
import(/* webpackChunkName: "dialog-config-flow" */ "./dialog-data-entry-flow");
|
import(
|
||||||
|
/* webpackChunkName: "dialog-config-flow" */ "./dialog-data-entry-flow"
|
||||||
|
);
|
||||||
|
|
||||||
export const showFlowDialog = (
|
export const showFlowDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
@ -54,9 +54,7 @@ export const showOptionsFlowDialog = (
|
|||||||
|
|
||||||
renderShowFormStepFieldLabel(hass, step, field) {
|
renderShowFormStepFieldLabel(hass, step, field) {
|
||||||
return hass.localize(
|
return hass.localize(
|
||||||
`component.${configEntry.domain}.options.step.${step.step_id}.data.${
|
`component.${configEntry.domain}.options.step.${step.step_id}.data.${field.name}`
|
||||||
field.name
|
|
||||||
}`
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -12,7 +12,9 @@ export interface DeviceRegistryDetailDialogParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const loadDeviceRegistryDetailDialog = () =>
|
export const loadDeviceRegistryDetailDialog = () =>
|
||||||
import(/* webpackChunkName: "device-registry-detail-dialog" */ "./dialog-device-registry-detail");
|
import(
|
||||||
|
/* webpackChunkName: "device-registry-detail-dialog" */ "./dialog-device-registry-detail"
|
||||||
|
);
|
||||||
|
|
||||||
export const showDeviceRegistryDetailDialog = (
|
export const showDeviceRegistryDetailDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
@ -6,7 +6,9 @@ export interface HaDomainTogglerDialogParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const loadDomainTogglerDialog = () =>
|
export const loadDomainTogglerDialog = () =>
|
||||||
import(/* webpackChunkName: "dialog-domain-toggler" */ "./dialog-domain-toggler");
|
import(
|
||||||
|
/* webpackChunkName: "dialog-domain-toggler" */ "./dialog-domain-toggler"
|
||||||
|
);
|
||||||
|
|
||||||
export const showDomainTogglerDialog = (
|
export const showDomainTogglerDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
@ -100,7 +100,7 @@ class MoreInfoClimate extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${stateObj.attributes.temperature
|
${stateObj.attributes.temperature !== undefined
|
||||||
? html`
|
? html`
|
||||||
<ha-climate-control
|
<ha-climate-control
|
||||||
.value=${stateObj.attributes.temperature}
|
.value=${stateObj.attributes.temperature}
|
||||||
@ -112,8 +112,8 @@ class MoreInfoClimate extends LitElement {
|
|||||||
></ha-climate-control>
|
></ha-climate-control>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${stateObj.attributes.target_temp_low ||
|
${stateObj.attributes.target_temp_low !== undefined ||
|
||||||
stateObj.attributes.target_temp_high
|
stateObj.attributes.target_temp_high !== undefined
|
||||||
? html`
|
? html`
|
||||||
<ha-climate-control
|
<ha-climate-control
|
||||||
.value=${stateObj.attributes.target_temp_low}
|
.value=${stateObj.attributes.target_temp_low}
|
||||||
|
@ -2,7 +2,7 @@ import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
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 "@vaadin/vaadin-date-picker/vaadin-date-picker";
|
import "@vaadin/vaadin-date-picker/theme/material/vaadin-date-picker";
|
||||||
|
|
||||||
import "../../../components/ha-relative-time";
|
import "../../../components/ha-relative-time";
|
||||||
import "../../../components/paper-time-input";
|
import "../../../components/paper-time-input";
|
||||||
|
@ -16,13 +16,14 @@ import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
|||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { DOMAINS_MORE_INFO_NO_HISTORY } from "../../common/const";
|
import { DOMAINS_MORE_INFO_NO_HISTORY } from "../../common/const";
|
||||||
import { EventsMixin } from "../../mixins/events-mixin";
|
import { EventsMixin } from "../../mixins/events-mixin";
|
||||||
|
import LocalizeMixin from "../../mixins/localize-mixin";
|
||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
|
|
||||||
const DOMAINS_NO_INFO = ["camera", "configurator", "history_graph"];
|
const DOMAINS_NO_INFO = ["camera", "configurator", "history_graph"];
|
||||||
/*
|
/*
|
||||||
* @appliesMixin EventsMixin
|
* @appliesMixin EventsMixin
|
||||||
*/
|
*/
|
||||||
class MoreInfoControls extends EventsMixin(PolymerElement) {
|
class MoreInfoControls extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style include="ha-style-dialog">
|
<style include="ha-style-dialog">
|
||||||
@ -68,7 +69,7 @@ class MoreInfoControls extends EventsMixin(PolymerElement) {
|
|||||||
|
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
aria-label="Dismiss dialog"
|
aria-label$="[[localize('ui.dialogs.more_info_control.dismiss')]]"
|
||||||
icon="hass:close"
|
icon="hass:close"
|
||||||
dialog-dismiss
|
dialog-dismiss
|
||||||
></paper-icon-button>
|
></paper-icon-button>
|
||||||
@ -77,6 +78,7 @@ class MoreInfoControls extends EventsMixin(PolymerElement) {
|
|||||||
</div>
|
</div>
|
||||||
<template is="dom-if" if="[[canConfigure]]">
|
<template is="dom-if" if="[[canConfigure]]">
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
|
aria-label$="[[localize('ui.dialogs.more_info_control.settings')]]"
|
||||||
icon="hass:settings"
|
icon="hass:settings"
|
||||||
on-click="_gotoSettings"
|
on-click="_gotoSettings"
|
||||||
></paper-icon-button>
|
></paper-icon-button>
|
||||||
|
@ -47,6 +47,7 @@ class MoreInfoSettings extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
|
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<ha-paper-icon-button-arrow-prev
|
<ha-paper-icon-button-arrow-prev
|
||||||
|
aria-label$="[[localize('ui.dialogs.more_info_settings.back')]]"
|
||||||
on-click="_backTapped"
|
on-click="_backTapped"
|
||||||
></ha-paper-icon-button-arrow-prev>
|
></ha-paper-icon-button-arrow-prev>
|
||||||
<div main-title="">[[_computeStateName(stateObj)]]</div>
|
<div main-title="">[[_computeStateName(stateObj)]]</div>
|
||||||
|
@ -49,10 +49,10 @@ export class HuiNotificationDrawer extends EventsMixin(
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<app-drawer id='drawer' opened="{{open}}" disable-swipe align="start">
|
<app-drawer id="drawer" opened="{{open}}" disable-swipe align="start">
|
||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<div main-title>[[localize('ui.notification_drawer.title')]]</div>
|
<div main-title>[[localize('ui.notification_drawer.title')]]</div>
|
||||||
<ha-paper-icon-button-prev on-click="_closeDrawer"></paper-icon-button>
|
<ha-paper-icon-button-prev on-click="_closeDrawer" aria-label$="[[localize('ui.notification_drawer.close')]]"></paper-icon-button>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
<div class="notifications">
|
<div class="notifications">
|
||||||
<template is="dom-if" if="[[!_empty(notifications)]]">
|
<template is="dom-if" if="[[!_empty(notifications)]]">
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
|
||||||
const loadVoiceCommandDialog = () =>
|
const loadVoiceCommandDialog = () =>
|
||||||
import(/* webpackChunkName: "ha-voice-command-dialog" */ "./ha-voice-command-dialog");
|
import(
|
||||||
|
/* webpackChunkName: "ha-voice-command-dialog" */ "./ha-voice-command-dialog"
|
||||||
|
);
|
||||||
|
|
||||||
export const showVoiceCommandDialog = (element: HTMLElement): void => {
|
export const showVoiceCommandDialog = (element: HTMLElement): void => {
|
||||||
fireEvent(element, "show-dialog", {
|
fireEvent(element, "show-dialog", {
|
||||||
|
@ -5,7 +5,9 @@ export interface ZHADeviceInfoDialogParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const loadZHADeviceInfoDialog = () =>
|
export const loadZHADeviceInfoDialog = () =>
|
||||||
import(/* webpackChunkName: "dialog-zha-device-info" */ "./dialog-zha-device-info");
|
import(
|
||||||
|
/* webpackChunkName: "dialog-zha-device-info" */ "./dialog-zha-device-info"
|
||||||
|
);
|
||||||
|
|
||||||
export const showZHADeviceInfoDialog = (
|
export const showZHADeviceInfoDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
@ -10,6 +10,8 @@ import "../auth/ha-authorize";
|
|||||||
/* polyfill for paper-dropdown */
|
/* polyfill for paper-dropdown */
|
||||||
setTimeout(
|
setTimeout(
|
||||||
() =>
|
() =>
|
||||||
import(/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"),
|
import(
|
||||||
|
/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"
|
||||||
|
),
|
||||||
2000
|
2000
|
||||||
);
|
);
|
||||||
|
@ -23,13 +23,16 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isExternal = location.search.includes("external_auth=1");
|
const isExternal =
|
||||||
|
window.externalApp ||
|
||||||
|
window.webkit?.messageHandlers?.getExternalAuth ||
|
||||||
|
location.search.includes("external_auth=1");
|
||||||
|
|
||||||
const authProm = isExternal
|
const authProm = isExternal
|
||||||
? () =>
|
? () =>
|
||||||
import(/* webpackChunkName: "external_auth" */ "../external_app/external_auth").then(
|
import(
|
||||||
({ createExternalAuth }) => createExternalAuth(hassUrl)
|
/* webpackChunkName: "external_auth" */ "../external_app/external_auth"
|
||||||
)
|
).then(({ createExternalAuth }) => createExternalAuth(hassUrl))
|
||||||
: () =>
|
: () =>
|
||||||
getAuth({
|
getAuth({
|
||||||
hassUrl,
|
hassUrl,
|
||||||
|
@ -74,20 +74,23 @@ export const provideHass = (
|
|||||||
restResponses.push([path, callback]);
|
restResponses.push([path, callback]);
|
||||||
}
|
}
|
||||||
|
|
||||||
mockAPI(new RegExp("states/.+"), (
|
mockAPI(
|
||||||
// @ts-ignore
|
new RegExp("states/.+"),
|
||||||
method,
|
(
|
||||||
path,
|
// @ts-ignore
|
||||||
parameters
|
method,
|
||||||
) => {
|
path,
|
||||||
const [domain, objectId] = path.substr(7).split(".", 2);
|
parameters
|
||||||
if (!domain || !objectId) {
|
) => {
|
||||||
return;
|
const [domain, objectId] = path.substr(7).split(".", 2);
|
||||||
|
if (!domain || !objectId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addEntities(
|
||||||
|
getEntity(domain, objectId, parameters.state, parameters.attributes)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
addEntities(
|
);
|
||||||
getEntity(domain, objectId, parameters.state, parameters.attributes)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const localLanguage = getLocalLanguage();
|
const localLanguage = getLocalLanguage();
|
||||||
|
|
||||||
@ -117,9 +120,7 @@ export const provideHass = (
|
|||||||
? callback(msg)
|
? callback(msg)
|
||||||
: Promise.reject({
|
: Promise.reject({
|
||||||
code: "command_not_mocked",
|
code: "command_not_mocked",
|
||||||
message: `WS Command ${
|
message: `WS Command ${msg.type} is not implemented in provide_hass.`,
|
||||||
msg.type
|
|
||||||
} is not implemented in provide_hass.`,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
subscribeMessage: async (onChange, msg) => {
|
subscribeMessage: async (onChange, msg) => {
|
||||||
@ -128,9 +129,7 @@ export const provideHass = (
|
|||||||
? callback(msg, onChange)
|
? callback(msg, onChange)
|
||||||
: Promise.reject({
|
: Promise.reject({
|
||||||
code: "command_not_mocked",
|
code: "command_not_mocked",
|
||||||
message: `WS Command ${
|
message: `WS Command ${msg.type} is not implemented in provide_hass.`,
|
||||||
msg.type
|
|
||||||
} is not implemented in provide_hass.`,
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
subscribeEvents: async (
|
subscribeEvents: async (
|
||||||
|
@ -45,7 +45,9 @@ export class HomeAssistantAppEl extends HassElement {
|
|||||||
this._initialize();
|
this._initialize();
|
||||||
setTimeout(registerServiceWorker, 1000);
|
setTimeout(registerServiceWorker, 1000);
|
||||||
/* polyfill for paper-dropdown */
|
/* polyfill for paper-dropdown */
|
||||||
import(/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min");
|
import(
|
||||||
|
/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updated(changedProps: PropertyValues): void {
|
protected updated(changedProps: PropertyValues): void {
|
||||||
@ -55,9 +57,10 @@ export class HomeAssistantAppEl extends HassElement {
|
|||||||
this._updateHass({ panelUrl: this._panelUrl });
|
this._updateHass({ panelUrl: this._panelUrl });
|
||||||
}
|
}
|
||||||
if (changedProps.has("hass")) {
|
if (changedProps.has("hass")) {
|
||||||
this.hassChanged(this.hass!, changedProps.get("hass") as
|
this.hassChanged(
|
||||||
| HomeAssistant
|
this.hass!,
|
||||||
| undefined);
|
changedProps.get("hass") as HomeAssistant | undefined
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,33 +12,59 @@ import { removeInitSkeleton } from "../util/init-skeleton";
|
|||||||
const CACHE_COMPONENTS = ["lovelace", "states", "developer-tools"];
|
const CACHE_COMPONENTS = ["lovelace", "states", "developer-tools"];
|
||||||
const COMPONENTS = {
|
const COMPONENTS = {
|
||||||
calendar: () =>
|
calendar: () =>
|
||||||
import(/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-calendar" */ "../panels/calendar/ha-panel-calendar"
|
||||||
|
),
|
||||||
config: () =>
|
config: () =>
|
||||||
import(/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config" */ "../panels/config/ha-panel-config"
|
||||||
|
),
|
||||||
custom: () =>
|
custom: () =>
|
||||||
import(/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-custom" */ "../panels/custom/ha-panel-custom"
|
||||||
|
),
|
||||||
"developer-tools": () =>
|
"developer-tools": () =>
|
||||||
import(/* webpackChunkName: "panel-developer-tools" */ "../panels/developer-tools/ha-panel-developer-tools"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-developer-tools" */ "../panels/developer-tools/ha-panel-developer-tools"
|
||||||
|
),
|
||||||
lovelace: () =>
|
lovelace: () =>
|
||||||
import(/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-lovelace" */ "../panels/lovelace/ha-panel-lovelace"
|
||||||
|
),
|
||||||
states: () =>
|
states: () =>
|
||||||
import(/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-states" */ "../panels/states/ha-panel-states"
|
||||||
|
),
|
||||||
history: () =>
|
history: () =>
|
||||||
import(/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-history" */ "../panels/history/ha-panel-history"
|
||||||
|
),
|
||||||
iframe: () =>
|
iframe: () =>
|
||||||
import(/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-iframe" */ "../panels/iframe/ha-panel-iframe"
|
||||||
|
),
|
||||||
kiosk: () =>
|
kiosk: () =>
|
||||||
import(/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-kiosk" */ "../panels/kiosk/ha-panel-kiosk"
|
||||||
|
),
|
||||||
logbook: () =>
|
logbook: () =>
|
||||||
import(/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-logbook" */ "../panels/logbook/ha-panel-logbook"
|
||||||
|
),
|
||||||
mailbox: () =>
|
mailbox: () =>
|
||||||
import(/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-mailbox" */ "../panels/mailbox/ha-panel-mailbox"
|
||||||
|
),
|
||||||
map: () =>
|
map: () =>
|
||||||
import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"),
|
import(/* webpackChunkName: "panel-map" */ "../panels/map/ha-panel-map"),
|
||||||
profile: () =>
|
profile: () =>
|
||||||
import(/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-profile" */ "../panels/profile/ha-panel-profile"
|
||||||
|
),
|
||||||
"shopping-list": () =>
|
"shopping-list": () =>
|
||||||
import(/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-shopping-list" */ "../panels/shopping-list/ha-panel-shopping-list"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
const getRoutes = (panels: Panels): RouterOptions => {
|
const getRoutes = (panels: Panels): RouterOptions => {
|
||||||
|
@ -84,8 +84,12 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
|
|||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this._fetchOnboardingSteps();
|
this._fetchOnboardingSteps();
|
||||||
import(/* webpackChunkName: "onboarding-integrations" */ "./onboarding-integrations");
|
import(
|
||||||
import(/* webpackChunkName: "onboarding-core-config" */ "./onboarding-core-config");
|
/* webpackChunkName: "onboarding-integrations" */ "./onboarding-integrations"
|
||||||
|
);
|
||||||
|
import(
|
||||||
|
/* webpackChunkName: "onboarding-core-config" */ "./onboarding-core-config"
|
||||||
|
);
|
||||||
registerServiceWorker(false);
|
registerServiceWorker(false);
|
||||||
this.addEventListener("onboarding-step", (ev) => this._handleStepDone(ev));
|
this.addEventListener("onboarding-step", (ev) => this._handleStepDone(ev));
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,9 @@ class OnboardingIntegrations extends LitElement {
|
|||||||
loadConfigFlowDialog();
|
loadConfigFlowDialog();
|
||||||
this._loadConfigEntries();
|
this._loadConfigEntries();
|
||||||
/* polyfill for paper-dropdown */
|
/* polyfill for paper-dropdown */
|
||||||
import(/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min");
|
import(
|
||||||
|
/* webpackChunkName: "polyfill-web-animations-next" */ "web-animations-js/web-animations-next-lite.min"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createFlow() {
|
private _createFlow() {
|
||||||
|
@ -14,7 +14,9 @@ export interface AreaRegistryDetailDialogParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const loadAreaRegistryDetailDialog = () =>
|
export const loadAreaRegistryDetailDialog = () =>
|
||||||
import(/* webpackChunkName: "area-registry-detail-dialog" */ "./dialog-area-registry-detail");
|
import(
|
||||||
|
/* webpackChunkName: "area-registry-detail-dialog" */ "./dialog-area-registry-detail"
|
||||||
|
);
|
||||||
|
|
||||||
export const showAreaRegistryDetailDialog = (
|
export const showAreaRegistryDetailDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
@ -32,6 +32,7 @@ import {
|
|||||||
} from "../../../data/automation";
|
} from "../../../data/automation";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
import { computeRTL } from "../../../common/util/compute_rtl";
|
||||||
|
import { showConfirmationDialog } from "../../../dialogs/confirmation/show-dialog-confirmation";
|
||||||
|
|
||||||
function AutomationEditor(mountEl, props, mergeEl) {
|
function AutomationEditor(mountEl, props, mergeEl) {
|
||||||
return render(h(Automation, props), mountEl, mergeEl);
|
return render(h(Automation, props), mountEl, mergeEl);
|
||||||
@ -210,15 +211,18 @@ export class HaAutomationEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _backTapped(): void {
|
private _backTapped(): void {
|
||||||
if (
|
if (this._dirty) {
|
||||||
this._dirty &&
|
showConfirmationDialog(this, {
|
||||||
!confirm(
|
text: this.hass!.localize(
|
||||||
this.hass!.localize("ui.panel.config.automation.editor.unsaved_confirm")
|
"ui.panel.config.automation.editor.unsaved_confirm"
|
||||||
)
|
),
|
||||||
) {
|
confirmBtnText: this.hass!.localize("ui.common.yes"),
|
||||||
return;
|
cancelBtnText: this.hass!.localize("ui.common.no"),
|
||||||
|
confirm: () => history.back(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
history.back();
|
||||||
}
|
}
|
||||||
history.back();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _delete() {
|
private async _delete() {
|
||||||
|
@ -115,9 +115,7 @@ class HaAutomationPicker extends LitElement {
|
|||||||
<a
|
<a
|
||||||
href=${ifDefined(
|
href=${ifDefined(
|
||||||
automation.attributes.id
|
automation.attributes.id
|
||||||
? `/config/automation/edit/${
|
? `/config/automation/edit/${automation.attributes.id}`
|
||||||
automation.attributes.id
|
|
||||||
}`
|
|
||||||
: undefined
|
: undefined
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -6,7 +6,9 @@ export interface ThingtalkDialogParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const loadThingtalkDialog = () =>
|
export const loadThingtalkDialog = () =>
|
||||||
import(/* webpackChunkName: "thingtalk-dialog" */ "./thingtalk/dialog-thingtalk");
|
import(
|
||||||
|
/* webpackChunkName: "thingtalk-dialog" */ "./thingtalk/dialog-thingtalk"
|
||||||
|
);
|
||||||
|
|
||||||
export const showThingtalkDialog = (
|
export const showThingtalkDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
@ -27,6 +27,7 @@ import { PlaceholderValues } from "./ha-thingtalk-placeholders";
|
|||||||
import { convertThingTalk } from "../../../../data/cloud";
|
import { convertThingTalk } from "../../../../data/cloud";
|
||||||
|
|
||||||
export interface Placeholder {
|
export interface Placeholder {
|
||||||
|
name: string;
|
||||||
index: number;
|
index: number;
|
||||||
fields: string[];
|
fields: string[];
|
||||||
domains: string[];
|
domains: string[];
|
||||||
@ -177,8 +178,21 @@ class DialogThingtalk extends LitElement {
|
|||||||
const placeholderValues = ev.detail.value as PlaceholderValues;
|
const placeholderValues = ev.detail.value as PlaceholderValues;
|
||||||
Object.entries(placeholderValues).forEach(([type, values]) => {
|
Object.entries(placeholderValues).forEach(([type, values]) => {
|
||||||
Object.entries(values).forEach(([index, placeholder]) => {
|
Object.entries(values).forEach(([index, placeholder]) => {
|
||||||
Object.entries(placeholder).forEach(([field, value]) => {
|
const devices = Object.values(placeholder);
|
||||||
this._config[type][index][field] = value;
|
if (devices.length === 1) {
|
||||||
|
Object.entries(devices[0]).forEach(([field, value]) => {
|
||||||
|
this._config[type][index][field] = value;
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const automation = { ...this._config[type][index] };
|
||||||
|
delete this._config[type][index];
|
||||||
|
devices.forEach((fields) => {
|
||||||
|
const newAutomation = { ...automation };
|
||||||
|
Object.entries(fields).forEach(([field, value]) => {
|
||||||
|
newAutomation[field] = value;
|
||||||
|
});
|
||||||
|
this._config[type].push(newAutomation);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -6,8 +6,11 @@ import {
|
|||||||
customElement,
|
customElement,
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
query,
|
PropertyValues,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
|
import "../../../../components/device/ha-area-devices-picker";
|
||||||
|
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { PolymerChangedEvent } from "../../../../polymer-types";
|
import { PolymerChangedEvent } from "../../../../polymer-types";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
@ -17,8 +20,15 @@ import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
|||||||
import { subscribeEntityRegistry } from "../../../../data/entity_registry";
|
import { subscribeEntityRegistry } from "../../../../data/entity_registry";
|
||||||
import { computeDomain } from "../../../../common/entity/compute_domain";
|
import { computeDomain } from "../../../../common/entity/compute_domain";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { HaDevicePicker } from "../../../../components/device/ha-device-picker";
|
|
||||||
import { getPath, applyPatch } from "../../../../common/util/patch";
|
import { getPath, applyPatch } from "../../../../common/util/patch";
|
||||||
|
import {
|
||||||
|
subscribeAreaRegistry,
|
||||||
|
AreaRegistryEntry,
|
||||||
|
} from "../../../../data/area_registry";
|
||||||
|
import {
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
} from "../../../../data/device_registry";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
@ -28,7 +38,23 @@ declare global {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PlaceholderValues {
|
export interface PlaceholderValues {
|
||||||
[key: string]: { [index: number]: { [key: string]: string } };
|
[key: string]: {
|
||||||
|
[index: number]: {
|
||||||
|
[index: number]: { device_id?: string; entity_id?: string };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ExtraInfo {
|
||||||
|
[key: string]: {
|
||||||
|
[index: number]: {
|
||||||
|
[index: number]: {
|
||||||
|
area_id?: string;
|
||||||
|
device_ids?: string[];
|
||||||
|
manualEntity: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeviceEntitiesLookup {
|
interface DeviceEntitiesLookup {
|
||||||
@ -43,9 +69,11 @@ export class ThingTalkPlaceholders extends SubscribeMixin(LitElement) {
|
|||||||
@property() public placeholders!: PlaceholderContainer;
|
@property() public placeholders!: PlaceholderContainer;
|
||||||
@property() private _error?: string;
|
@property() private _error?: string;
|
||||||
private _deviceEntityLookup: DeviceEntitiesLookup = {};
|
private _deviceEntityLookup: DeviceEntitiesLookup = {};
|
||||||
private _manualEntities: PlaceholderValues = {};
|
@property() private _extraInfo: ExtraInfo = {};
|
||||||
@property() private _placeholderValues: PlaceholderValues = {};
|
@property() private _placeholderValues: PlaceholderValues = {};
|
||||||
@query("#device-entity-picker") private _deviceEntityPicker?: HaDevicePicker;
|
private _devices?: DeviceRegistryEntry[];
|
||||||
|
private _areas?: AreaRegistryEntry[];
|
||||||
|
private _search = false;
|
||||||
|
|
||||||
public hassSubscribe() {
|
public hassSubscribe() {
|
||||||
return [
|
return [
|
||||||
@ -66,12 +94,28 @@ export class ThingTalkPlaceholders extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
||||||
|
this._devices = devices;
|
||||||
|
this._searchNames();
|
||||||
|
}),
|
||||||
|
subscribeAreaRegistry(this.hass.connection!, (areas) => {
|
||||||
|
this._areas = areas;
|
||||||
|
this._searchNames();
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
if (changedProps.has("placeholders")) {
|
||||||
|
this._search = true;
|
||||||
|
this._searchNames();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<ha-paper-dialog
|
<ha-paper-dialog
|
||||||
|
modal
|
||||||
with-backdrop
|
with-backdrop
|
||||||
.opened=${this.opened}
|
.opened=${this.opened}
|
||||||
@opened-changed="${this._openedChanged}"
|
@opened-changed="${this._openedChanged}"
|
||||||
@ -93,55 +137,66 @@ export class ThingTalkPlaceholders extends SubscribeMixin(LitElement) {
|
|||||||
</h3>
|
</h3>
|
||||||
${placeholders.map((placeholder) => {
|
${placeholders.map((placeholder) => {
|
||||||
if (placeholder.fields.includes("device_id")) {
|
if (placeholder.fields.includes("device_id")) {
|
||||||
|
const extraInfo = getPath(this._extraInfo, [
|
||||||
|
type,
|
||||||
|
placeholder.index,
|
||||||
|
]);
|
||||||
return html`
|
return html`
|
||||||
<ha-device-picker
|
<ha-area-devices-picker
|
||||||
.type=${type}
|
.type=${type}
|
||||||
.placeholder=${placeholder}
|
.placeholder=${placeholder}
|
||||||
@change=${this._devicePicked}
|
@value-changed=${this._devicePicked}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
.area=${extraInfo ? extraInfo.area_id : undefined}
|
||||||
|
.devices=${extraInfo && extraInfo.device_ids
|
||||||
|
? extraInfo.device_ids
|
||||||
|
: undefined}
|
||||||
.includeDomains=${placeholder.domains}
|
.includeDomains=${placeholder.domains}
|
||||||
.includeDeviceClasses=${placeholder.device_classes}
|
.includeDeviceClasses=${placeholder.device_classes}
|
||||||
.label=${this._getLabel(
|
.label=${this._getLabel(
|
||||||
placeholder.domains,
|
placeholder.domains,
|
||||||
placeholder.device_classes
|
placeholder.device_classes
|
||||||
)}
|
)}
|
||||||
></ha-device-picker>
|
></ha-area-devices-picker>
|
||||||
${(getPath(this._placeholderValues, [
|
${extraInfo && extraInfo.manualEntity
|
||||||
type,
|
|
||||||
placeholder.index,
|
|
||||||
"device_id",
|
|
||||||
]) &&
|
|
||||||
placeholder.fields.includes("entity_id") &&
|
|
||||||
getPath(this._placeholderValues, [
|
|
||||||
type,
|
|
||||||
placeholder.index,
|
|
||||||
"entity_id",
|
|
||||||
]) === undefined) ||
|
|
||||||
getPath(this._manualEntities, [
|
|
||||||
type,
|
|
||||||
placeholder.index,
|
|
||||||
"manual",
|
|
||||||
]) === true
|
|
||||||
? html`
|
? html`
|
||||||
<ha-entity-picker
|
<h3>
|
||||||
id="device-entity-picker"
|
One or more devices have more than one matching
|
||||||
.type=${type}
|
entity, please pick the one you want to use.
|
||||||
.placeholder=${placeholder}
|
</h3>
|
||||||
@change=${this._entityPicked}
|
${Object.keys(extraInfo.manualEntity).map(
|
||||||
.includeDomains=${placeholder.domains}
|
(idx) => html`
|
||||||
.includeDeviceClasses=${placeholder.device_classes}
|
<ha-entity-picker
|
||||||
.hass=${this.hass}
|
id="device-entity-picker"
|
||||||
.label=${this._getLabel(
|
.type=${type}
|
||||||
placeholder.domains,
|
.placeholder=${placeholder}
|
||||||
placeholder.device_classes
|
.index=${idx}
|
||||||
)}
|
@change=${this._entityPicked}
|
||||||
.entityFilter=${(state: HassEntity) =>
|
.includeDomains=${placeholder.domains}
|
||||||
this._deviceEntityLookup[
|
.includeDeviceClasses=${placeholder.device_classes}
|
||||||
this._placeholderValues[type][
|
.hass=${this.hass}
|
||||||
placeholder.index
|
.label=${`${this._getLabel(
|
||||||
].device_id
|
placeholder.domains,
|
||||||
].includes(state.entity_id)}
|
placeholder.device_classes
|
||||||
></ha-entity-picker>
|
)} of device ${this._getDeviceName(
|
||||||
|
getPath(this._placeholderValues, [
|
||||||
|
type,
|
||||||
|
placeholder.index,
|
||||||
|
idx,
|
||||||
|
"device_id",
|
||||||
|
])
|
||||||
|
)}`}
|
||||||
|
.entityFilter=${(state: HassEntity) => {
|
||||||
|
const devId = this._placeholderValues[type][
|
||||||
|
placeholder.index
|
||||||
|
][idx].device_id;
|
||||||
|
return this._deviceEntityLookup[
|
||||||
|
devId
|
||||||
|
].includes(state.entity_id);
|
||||||
|
}}
|
||||||
|
></ha-entity-picker>
|
||||||
|
`
|
||||||
|
)}
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
`;
|
`;
|
||||||
@ -189,17 +244,74 @@ export class ThingTalkPlaceholders extends SubscribeMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _getDeviceName(deviceId: string): string {
|
||||||
|
if (!this._devices) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const foundDevice = this._devices.find((device) => device.id === deviceId);
|
||||||
|
if (!foundDevice) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return foundDevice.name_by_user || foundDevice.name || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private _searchNames() {
|
||||||
|
if (!this._search || !this._areas || !this._devices) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._search = false;
|
||||||
|
Object.entries(this.placeholders).forEach(([type, placeholders]) =>
|
||||||
|
placeholders.forEach((placeholder) => {
|
||||||
|
if (!placeholder.name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const name = placeholder.name;
|
||||||
|
const foundArea = this._areas!.find((area) =>
|
||||||
|
area.name.toLowerCase().includes(name)
|
||||||
|
);
|
||||||
|
if (foundArea) {
|
||||||
|
applyPatch(
|
||||||
|
this._extraInfo,
|
||||||
|
[type, placeholder.index, "area_id"],
|
||||||
|
foundArea.area_id
|
||||||
|
);
|
||||||
|
this.requestUpdate("_extraInfo");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const foundDevices = this._devices!.filter((device) => {
|
||||||
|
const deviceName = device.name_by_user || device.name;
|
||||||
|
if (!deviceName) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return deviceName.toLowerCase().includes(name);
|
||||||
|
});
|
||||||
|
if (foundDevices.length) {
|
||||||
|
applyPatch(
|
||||||
|
this._extraInfo,
|
||||||
|
[type, placeholder.index, "device_ids"],
|
||||||
|
foundDevices.map((device) => device.id)
|
||||||
|
);
|
||||||
|
this.requestUpdate("_extraInfo");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private get _isDone(): boolean {
|
private get _isDone(): boolean {
|
||||||
return Object.entries(this.placeholders).every(([type, placeholders]) =>
|
return Object.entries(this.placeholders).every(([type, placeholders]) =>
|
||||||
placeholders.every((placeholder) =>
|
placeholders.every((placeholder) =>
|
||||||
placeholder.fields.every(
|
placeholder.fields.every((field) => {
|
||||||
(field) =>
|
const entries: {
|
||||||
getPath(this._placeholderValues, [
|
[key: number]: { device_id?: string; entity_id?: string };
|
||||||
type,
|
} = getPath(this._placeholderValues, [type, placeholder.index]);
|
||||||
placeholder.index,
|
if (!entries) {
|
||||||
field,
|
return false;
|
||||||
]) !== undefined
|
}
|
||||||
)
|
const values = Object.values(entries);
|
||||||
|
return values.every(
|
||||||
|
(entry) => entry[field] !== undefined && entry[field] !== ""
|
||||||
|
);
|
||||||
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -212,76 +324,115 @@ export class ThingTalkPlaceholders extends SubscribeMixin(LitElement) {
|
|||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _devicePicked(ev: Event): void {
|
private _devicePicked(ev: CustomEvent): void {
|
||||||
|
const value: string[] = ev.detail.value;
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const target = ev.target as any;
|
const target = ev.target as any;
|
||||||
const placeholder = target.placeholder as Placeholder;
|
const placeholder = target.placeholder as Placeholder;
|
||||||
const value = target.value;
|
|
||||||
const type = target.type;
|
const type = target.type;
|
||||||
applyPatch(
|
|
||||||
this._placeholderValues,
|
let oldValues = getPath(this._placeholderValues, [type, placeholder.index]);
|
||||||
[type, placeholder.index, "device_id"],
|
if (oldValues) {
|
||||||
value
|
oldValues = Object.values(oldValues);
|
||||||
);
|
|
||||||
if (!placeholder.fields.includes("entity_id")) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (value === "") {
|
const oldExtraInfo = getPath(this._extraInfo, [type, placeholder.index]);
|
||||||
delete this._placeholderValues[type][placeholder.index].entity_id;
|
|
||||||
if (this._deviceEntityPicker) {
|
if (this._placeholderValues[type]) {
|
||||||
this._deviceEntityPicker.value = undefined;
|
delete this._placeholderValues[type][placeholder.index];
|
||||||
}
|
}
|
||||||
applyPatch(
|
|
||||||
this._manualEntities,
|
if (this._extraInfo[type]) {
|
||||||
[type, placeholder.index, "manual"],
|
delete this._extraInfo[type][placeholder.index];
|
||||||
false
|
}
|
||||||
);
|
|
||||||
|
if (!value.length) {
|
||||||
this.requestUpdate("_placeholderValues");
|
this.requestUpdate("_placeholderValues");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const devEntities = this._deviceEntityLookup[value];
|
|
||||||
const entities = devEntities.filter((eid) => {
|
value.forEach((deviceId, index) => {
|
||||||
if (placeholder.device_classes) {
|
let oldIndex;
|
||||||
const stateObj = this.hass.states[eid];
|
if (oldValues) {
|
||||||
if (!stateObj) {
|
const oldDevice = oldValues.find((oldVal, idx) => {
|
||||||
return false;
|
oldIndex = idx;
|
||||||
|
return oldVal.device_id === deviceId;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (oldDevice) {
|
||||||
|
applyPatch(
|
||||||
|
this._placeholderValues,
|
||||||
|
[type, placeholder.index, index],
|
||||||
|
oldDevice
|
||||||
|
);
|
||||||
|
if (oldExtraInfo) {
|
||||||
|
applyPatch(
|
||||||
|
this._extraInfo,
|
||||||
|
[type, placeholder.index, index],
|
||||||
|
oldExtraInfo[oldIndex]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return (
|
|
||||||
placeholder.domains.includes(computeDomain(eid)) &&
|
|
||||||
stateObj.attributes.device_class &&
|
|
||||||
placeholder.device_classes.includes(stateObj.attributes.device_class)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return placeholder.domains.includes(computeDomain(eid));
|
|
||||||
});
|
|
||||||
if (entities.length === 0) {
|
|
||||||
// Should not happen because we filter the device picker on domain
|
|
||||||
this._error = `No ${placeholder.domains
|
|
||||||
.map((domain) => this.hass.localize(`domain.${domain}`))
|
|
||||||
.join(", ")} entities found in this device.`;
|
|
||||||
} else if (entities.length === 1) {
|
|
||||||
applyPatch(
|
applyPatch(
|
||||||
this._placeholderValues,
|
this._placeholderValues,
|
||||||
[type, placeholder.index, "entity_id"],
|
[type, placeholder.index, index, "device_id"],
|
||||||
entities[0]
|
deviceId
|
||||||
);
|
);
|
||||||
applyPatch(
|
|
||||||
this._manualEntities,
|
if (!placeholder.fields.includes("entity_id")) {
|
||||||
[type, placeholder.index, "manual"],
|
return;
|
||||||
false
|
|
||||||
);
|
|
||||||
this.requestUpdate("_placeholderValues");
|
|
||||||
} else {
|
|
||||||
delete this._placeholderValues[type][placeholder.index].entity_id;
|
|
||||||
if (this._deviceEntityPicker) {
|
|
||||||
this._deviceEntityPicker.value = undefined;
|
|
||||||
}
|
}
|
||||||
applyPatch(
|
|
||||||
this._manualEntities,
|
const devEntities = this._deviceEntityLookup[deviceId];
|
||||||
[type, placeholder.index, "manual"],
|
|
||||||
true
|
const entities = devEntities.filter((eid) => {
|
||||||
);
|
if (placeholder.device_classes) {
|
||||||
this.requestUpdate("_placeholderValues");
|
const stateObj = this.hass.states[eid];
|
||||||
}
|
if (!stateObj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
placeholder.domains.includes(computeDomain(eid)) &&
|
||||||
|
stateObj.attributes.device_class &&
|
||||||
|
placeholder.device_classes.includes(
|
||||||
|
stateObj.attributes.device_class
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return placeholder.domains.includes(computeDomain(eid));
|
||||||
|
});
|
||||||
|
if (entities.length === 0) {
|
||||||
|
// Should not happen because we filter the device picker on domain
|
||||||
|
this._error = `No ${placeholder.domains
|
||||||
|
.map((domain) => this.hass.localize(`domain.${domain}`))
|
||||||
|
.join(", ")} entities found in this device.`;
|
||||||
|
} else if (entities.length === 1) {
|
||||||
|
applyPatch(
|
||||||
|
this._placeholderValues,
|
||||||
|
[type, placeholder.index, index, "entity_id"],
|
||||||
|
entities[0]
|
||||||
|
);
|
||||||
|
this.requestUpdate("_placeholderValues");
|
||||||
|
} else {
|
||||||
|
delete this._placeholderValues[type][placeholder.index][index]
|
||||||
|
.entity_id;
|
||||||
|
applyPatch(
|
||||||
|
this._extraInfo,
|
||||||
|
[type, placeholder.index, "manualEntity", index],
|
||||||
|
true
|
||||||
|
);
|
||||||
|
this.requestUpdate("_placeholderValues");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent(
|
||||||
|
this.shadowRoot!.querySelector("ha-paper-dialog")! as HTMLElement,
|
||||||
|
"iron-resize"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _entityPicked(ev: Event): void {
|
private _entityPicked(ev: Event): void {
|
||||||
@ -289,9 +440,10 @@ export class ThingTalkPlaceholders extends SubscribeMixin(LitElement) {
|
|||||||
const placeholder = target.placeholder as Placeholder;
|
const placeholder = target.placeholder as Placeholder;
|
||||||
const value = target.value;
|
const value = target.value;
|
||||||
const type = target.type;
|
const type = target.type;
|
||||||
|
const index = target.index || 0;
|
||||||
applyPatch(
|
applyPatch(
|
||||||
this._placeholderValues,
|
this._placeholderValues,
|
||||||
[type, placeholder.index, "entity_id"],
|
[type, placeholder.index, index, "entity_id"],
|
||||||
value
|
value
|
||||||
);
|
);
|
||||||
this.requestUpdate("_placeholderValues");
|
this.requestUpdate("_placeholderValues");
|
||||||
|
@ -0,0 +1,345 @@
|
|||||||
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
|
// tslint:disable-next-line
|
||||||
|
import { PaperListboxElement } from "@polymer/paper-listbox/paper-listbox";
|
||||||
|
import "@polymer/paper-menu-button/paper-menu-button";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
|
import { dynamicContentDirective } from "../../../../common/dom/dynamic-content-directive";
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
|
import "./types/ha-automation-trigger-device";
|
||||||
|
import "./types/ha-automation-trigger-event";
|
||||||
|
import "./types/ha-automation-trigger-state";
|
||||||
|
import "./types/ha-automation-trigger-geo_location";
|
||||||
|
import "./types/ha-automation-trigger-homeassistant";
|
||||||
|
import "./types/ha-automation-trigger-mqtt";
|
||||||
|
import "./types/ha-automation-trigger-numeric_state";
|
||||||
|
import "./types/ha-automation-trigger-sun";
|
||||||
|
import "./types/ha-automation-trigger-template";
|
||||||
|
import "./types/ha-automation-trigger-time";
|
||||||
|
import "./types/ha-automation-trigger-time_pattern";
|
||||||
|
import "./types/ha-automation-trigger-webhook";
|
||||||
|
import "./types/ha-automation-trigger-zone";
|
||||||
|
import { DeviceTrigger } from "../../../../data/device_automation";
|
||||||
|
|
||||||
|
const OPTIONS = [
|
||||||
|
"device",
|
||||||
|
"event",
|
||||||
|
"state",
|
||||||
|
"geo_location",
|
||||||
|
"homeassistant",
|
||||||
|
"mqtt",
|
||||||
|
"numeric_state",
|
||||||
|
"sun",
|
||||||
|
"template",
|
||||||
|
"time",
|
||||||
|
"time_pattern",
|
||||||
|
"webhook",
|
||||||
|
"zone",
|
||||||
|
];
|
||||||
|
|
||||||
|
export interface ForDict {
|
||||||
|
hours?: number | string;
|
||||||
|
minutes?: number | string;
|
||||||
|
seconds?: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StateTrigger {
|
||||||
|
platform: "state";
|
||||||
|
entity_id: string;
|
||||||
|
from?: string | number;
|
||||||
|
to?: string | number;
|
||||||
|
for?: string | number | ForDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MqttTrigger {
|
||||||
|
platform: "mqtt";
|
||||||
|
topic: string;
|
||||||
|
payload?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GeoLocationTrigger {
|
||||||
|
platform: "geo_location";
|
||||||
|
source: "string";
|
||||||
|
zone: "string";
|
||||||
|
event: "enter" | "leave";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HassTrigger {
|
||||||
|
platform: "homeassistant";
|
||||||
|
event: "start" | "shutdown";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NumericStateTrigger {
|
||||||
|
platform: "numeric_state";
|
||||||
|
entity_id: string;
|
||||||
|
above?: number;
|
||||||
|
below?: number;
|
||||||
|
value_template?: string;
|
||||||
|
for?: string | number | ForDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SunTrigger {
|
||||||
|
platform: "sun";
|
||||||
|
offset: number;
|
||||||
|
event: "sunrise" | "sunset";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TimePatternTrigger {
|
||||||
|
platform: "time_pattern";
|
||||||
|
hours?: number | string;
|
||||||
|
minutes?: number | string;
|
||||||
|
seconds?: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WebhookTrigger {
|
||||||
|
platform: "webhook";
|
||||||
|
webhook_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ZoneTrigger {
|
||||||
|
platform: "zone";
|
||||||
|
entity_id: string;
|
||||||
|
zone: string;
|
||||||
|
event: "enter" | "leave";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TimeTrigger {
|
||||||
|
platform: "time";
|
||||||
|
at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TemplateTrigger {
|
||||||
|
platform: "template";
|
||||||
|
value_template: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventTrigger {
|
||||||
|
platform: "event";
|
||||||
|
event_type: string;
|
||||||
|
event_data: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Trigger =
|
||||||
|
| StateTrigger
|
||||||
|
| MqttTrigger
|
||||||
|
| GeoLocationTrigger
|
||||||
|
| HassTrigger
|
||||||
|
| NumericStateTrigger
|
||||||
|
| SunTrigger
|
||||||
|
| TimePatternTrigger
|
||||||
|
| WebhookTrigger
|
||||||
|
| ZoneTrigger
|
||||||
|
| TimeTrigger
|
||||||
|
| TemplateTrigger
|
||||||
|
| EventTrigger
|
||||||
|
| DeviceTrigger;
|
||||||
|
|
||||||
|
export interface TriggerElement extends LitElement {
|
||||||
|
trigger: Trigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const handleChangeEvent = (element: TriggerElement, ev: CustomEvent) => {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const name = (ev.target as any)?.name;
|
||||||
|
if (!name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newVal = ev.detail.value;
|
||||||
|
|
||||||
|
if ((element.trigger[name] || "") === newVal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let newTrigger: Trigger;
|
||||||
|
if (!newVal) {
|
||||||
|
newTrigger = { ...element.trigger };
|
||||||
|
delete newTrigger[name];
|
||||||
|
} else {
|
||||||
|
newTrigger = { ...element.trigger, [name]: newVal };
|
||||||
|
}
|
||||||
|
fireEvent(element, "value-changed", { value: newTrigger });
|
||||||
|
};
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-row")
|
||||||
|
export default class HaAutomationTriggerRow extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: Trigger;
|
||||||
|
@property() private _yamlMode = false;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (!this.trigger) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
const hasEditor = OPTIONS.includes(this.trigger.platform);
|
||||||
|
if (!hasEditor) {
|
||||||
|
this._yamlMode = true;
|
||||||
|
}
|
||||||
|
const selected = OPTIONS.indexOf(this.trigger.platform);
|
||||||
|
return html`
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="card-menu">
|
||||||
|
<paper-menu-button
|
||||||
|
no-animations
|
||||||
|
horizontal-align="right"
|
||||||
|
horizontal-offset="-5"
|
||||||
|
vertical-offset="-5"
|
||||||
|
close-on-activate
|
||||||
|
>
|
||||||
|
<paper-icon-button
|
||||||
|
icon="hass:dots-vertical"
|
||||||
|
slot="dropdown-trigger"
|
||||||
|
></paper-icon-button>
|
||||||
|
<paper-listbox slot="dropdown-content">
|
||||||
|
<paper-item @click=${this._switchYamlMode}>
|
||||||
|
${this._yamlMode
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.edit_ui"
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.edit_yaml"
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
|
<paper-item disabled>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.duplicate"
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
|
<paper-item @click=${this._onDelete}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.delete"
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-menu-button>
|
||||||
|
</div>
|
||||||
|
${this._yamlMode
|
||||||
|
? html`
|
||||||
|
<div style="margin-right: 24px;">
|
||||||
|
${!hasEditor
|
||||||
|
? html`
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.unsupported_platform",
|
||||||
|
"platform",
|
||||||
|
this.trigger.platform
|
||||||
|
)}
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<ha-yaml-editor
|
||||||
|
.value=${this.trigger}
|
||||||
|
@value-changed=${this._onYamlChange}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<paper-dropdown-menu-light
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type_select"
|
||||||
|
)}
|
||||||
|
no-animations
|
||||||
|
>
|
||||||
|
<paper-listbox
|
||||||
|
slot="dropdown-content"
|
||||||
|
.selected=${selected}
|
||||||
|
@iron-select=${this._typeChanged}
|
||||||
|
>
|
||||||
|
${OPTIONS.map(
|
||||||
|
(opt) => html`
|
||||||
|
<paper-item .platform=${opt}>
|
||||||
|
${this.hass.localize(
|
||||||
|
`ui.panel.config.automation.editor.triggers.type.${opt}.label`
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu-light>
|
||||||
|
<div>
|
||||||
|
${dynamicContentDirective(
|
||||||
|
`ha-automation-trigger-${this.trigger.platform}`,
|
||||||
|
{ hass: this.hass, trigger: this.trigger }
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onDelete() {
|
||||||
|
if (
|
||||||
|
confirm(
|
||||||
|
this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.delete_confirm"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
fireEvent(this, "value-changed", { value: null });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _typeChanged(ev: CustomEvent) {
|
||||||
|
const type = ((ev.target as PaperListboxElement)?.selectedItem as any)
|
||||||
|
?.platform;
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const elClass = customElements.get(`ha-automation-trigger-${type}`);
|
||||||
|
|
||||||
|
if (type !== this.trigger.platform) {
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
platform: type,
|
||||||
|
...elClass.defaultConfig,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onYamlChange(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", { value: ev.detail.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _switchYamlMode() {
|
||||||
|
this._yamlMode = !this._yamlMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
.card-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 3;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
.rtl .card-menu {
|
||||||
|
right: auto;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.card-menu paper-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-row": HaAutomationTriggerRow;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
property,
|
||||||
|
CSSResult,
|
||||||
|
css,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@material/mwc-button";
|
||||||
|
import "../../../../components/ha-card";
|
||||||
|
|
||||||
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
|
import "./ha-automation-trigger-row";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger")
|
||||||
|
export default class HaAutomationTrigger extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public triggers;
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
return html`
|
||||||
|
<div class="triggers">
|
||||||
|
${this.triggers.map(
|
||||||
|
(trg, idx) => html`
|
||||||
|
<ha-automation-trigger-row
|
||||||
|
.index=${idx}
|
||||||
|
.trigger=${trg}
|
||||||
|
@value-changed=${this._triggerChanged}
|
||||||
|
.hass=${this.hass}
|
||||||
|
></ha-automation-trigger-row>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
<ha-card>
|
||||||
|
<div class="card-actions add-card">
|
||||||
|
<mwc-button @click=${this._addTrigger}>
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.add"
|
||||||
|
)}
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addTrigger() {
|
||||||
|
const triggers = this.triggers.concat({
|
||||||
|
platform: "state",
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", { value: triggers });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _triggerChanged(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
const triggers = [...this.triggers];
|
||||||
|
const newValue = ev.detail.value;
|
||||||
|
const index = (ev.target as any).index;
|
||||||
|
|
||||||
|
if (newValue === null) {
|
||||||
|
triggers.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
triggers[index] = newValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fireEvent(this, "value-changed", { value: triggers });
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
.triggers,
|
||||||
|
.script {
|
||||||
|
margin-top: -16px;
|
||||||
|
}
|
||||||
|
.triggers ha-card,
|
||||||
|
.script ha-card {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
.add-card mwc-button {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger": HaAutomationTrigger;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,128 @@
|
|||||||
|
import "../../../../../components/device/ha-device-picker";
|
||||||
|
import "../../../../../components/device/ha-device-trigger-picker";
|
||||||
|
import "../../../../../components/ha-form/ha-form";
|
||||||
|
|
||||||
|
import {
|
||||||
|
fetchDeviceTriggerCapabilities,
|
||||||
|
deviceAutomationsEqual,
|
||||||
|
DeviceTrigger,
|
||||||
|
} from "../../../../../data/device_automation";
|
||||||
|
import { LitElement, customElement, property, html } from "lit-element";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-device")
|
||||||
|
export class HaDeviceTrigger extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: DeviceTrigger;
|
||||||
|
@property() private _deviceId?: string;
|
||||||
|
@property() private _capabilities?;
|
||||||
|
private _origTrigger?: DeviceTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return {
|
||||||
|
device_id: "",
|
||||||
|
domain: "",
|
||||||
|
entity_id: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
if (this._deviceId === undefined) {
|
||||||
|
this._deviceId = this.trigger.device_id;
|
||||||
|
}
|
||||||
|
const extraFieldsData =
|
||||||
|
this._capabilities && this._capabilities.extra_fields
|
||||||
|
? this._capabilities.extra_fields.map((item) => {
|
||||||
|
return { [item.name]: this.trigger[item.name] };
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-device-picker
|
||||||
|
.value=${this._deviceId}
|
||||||
|
@value-changed=${this._devicePicked}
|
||||||
|
.hass=${this.hass}
|
||||||
|
label="Device"
|
||||||
|
></ha-device-picker>
|
||||||
|
<ha-device-trigger-picker
|
||||||
|
.value=${this.trigger}
|
||||||
|
.deviceId=${this._deviceId}
|
||||||
|
@value-changed=${this._deviceTriggerPicked}
|
||||||
|
.hass=${this.hass}
|
||||||
|
label="Trigger"
|
||||||
|
></ha-device-trigger-picker>
|
||||||
|
${extraFieldsData
|
||||||
|
? html`
|
||||||
|
<ha-form
|
||||||
|
.data=${Object.assign({}, ...extraFieldsData)}
|
||||||
|
.schema=${this._capabilities.extra_fields}
|
||||||
|
.computeLabel=${this._extraFieldsComputeLabelCallback(
|
||||||
|
this.hass.localize
|
||||||
|
)}
|
||||||
|
@value-changed=${this._extraFieldsChanged}
|
||||||
|
></ha-form>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated() {
|
||||||
|
if (!this._capabilities) {
|
||||||
|
this._getCapabilities();
|
||||||
|
}
|
||||||
|
if (this.trigger) {
|
||||||
|
this._origTrigger = this.trigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedPros) {
|
||||||
|
const prevTrigger = changedPros.get("trigger");
|
||||||
|
if (prevTrigger && !deviceAutomationsEqual(prevTrigger, this.trigger)) {
|
||||||
|
this._getCapabilities();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getCapabilities() {
|
||||||
|
const trigger = this.trigger;
|
||||||
|
|
||||||
|
this._capabilities = trigger.domain
|
||||||
|
? await fetchDeviceTriggerCapabilities(this.hass, trigger)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _devicePicked(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
this._deviceId = ev.target.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deviceTriggerPicked(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
let trigger = ev.detail.value;
|
||||||
|
if (
|
||||||
|
this._origTrigger &&
|
||||||
|
deviceAutomationsEqual(this._origTrigger, trigger)
|
||||||
|
) {
|
||||||
|
trigger = this._origTrigger;
|
||||||
|
}
|
||||||
|
fireEvent(this, "value-changed", { value: trigger });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _extraFieldsChanged(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.trigger,
|
||||||
|
...ev.detail.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _extraFieldsComputeLabelCallback(localize) {
|
||||||
|
// Returns a callback for ha-form to calculate labels per schema object
|
||||||
|
return (schema) =>
|
||||||
|
localize(
|
||||||
|
`ui.panel.config.automation.editor.triggers.type.device.extra_fields.${schema.name}`
|
||||||
|
) || schema.name;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "../../../../../components/ha-yaml-editor";
|
||||||
|
|
||||||
|
import { LitElement, property, customElement } from "lit-element";
|
||||||
|
import {
|
||||||
|
TriggerElement,
|
||||||
|
EventTrigger,
|
||||||
|
handleChangeEvent,
|
||||||
|
} from "../ha-automation-trigger-row";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import { html } from "lit-html";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-event")
|
||||||
|
export class HaEventTrigger extends LitElement implements TriggerElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: EventTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return { event_type: "", event_data: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { event_type, event_data } = this.trigger;
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.event.event_type"
|
||||||
|
)}
|
||||||
|
name="event_type"
|
||||||
|
.value="${event_type}"
|
||||||
|
@value-changed="${this._valueChanged}"
|
||||||
|
></paper-input>
|
||||||
|
<ha-yaml-editor
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.event.event_data"
|
||||||
|
)}
|
||||||
|
.name=${"event_data"}
|
||||||
|
.value=${event_data}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></ha-yaml-editor>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-event": HaEventTrigger;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
import "@polymer/paper-radio-button/paper-radio-button";
|
||||||
|
import "@polymer/paper-radio-group/paper-radio-group";
|
||||||
|
// tslint:disable-next-line
|
||||||
|
import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group";
|
||||||
|
import "../../../../../components/entity/ha-entity-picker";
|
||||||
|
import { LitElement, customElement, property, html } from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import {
|
||||||
|
GeoLocationTrigger,
|
||||||
|
handleChangeEvent,
|
||||||
|
} from "../ha-automation-trigger-row";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-geo_location")
|
||||||
|
export default class HaGeolocationTrigger extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: GeoLocationTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return {
|
||||||
|
source: "",
|
||||||
|
zone: "",
|
||||||
|
event: "enter",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { source, zone, event } = this.trigger;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.geo_location.source"
|
||||||
|
)}
|
||||||
|
name="source"
|
||||||
|
.value=${source}
|
||||||
|
@value-changed="${this._valueChanged}"
|
||||||
|
></paper-input>
|
||||||
|
<ha-entity-picker
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.geo_location.zone"
|
||||||
|
)}
|
||||||
|
.value=${zone}
|
||||||
|
@value-changed=${this._zonePicked}
|
||||||
|
.hass=${this.hass}
|
||||||
|
allow-custom-entity
|
||||||
|
.includeDomains=${["zone"]}
|
||||||
|
></ha-entity-picker>
|
||||||
|
<label id="eventlabel">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.geo_location.event"
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
<paper-radio-group
|
||||||
|
.selected=${event}
|
||||||
|
aria-labelledby="eventlabel"
|
||||||
|
@paper-radio-group-changed=${this._radioGroupPicked}
|
||||||
|
>
|
||||||
|
<paper-radio-button name="enter">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.geo_location.enter"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
<paper-radio-button name="leave">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.geo_location.leave"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
</paper-radio-group>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _zonePicked(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this.trigger, zone: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _radioGroupPicked(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.trigger,
|
||||||
|
event: (ev.target as PaperRadioGroupElement).selected,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-geo_location": HaGeolocationTrigger;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
import "@polymer/paper-radio-button/paper-radio-button";
|
||||||
|
import "@polymer/paper-radio-group/paper-radio-group";
|
||||||
|
// tslint:disable-next-line
|
||||||
|
import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group";
|
||||||
|
import { LitElement, html, property, customElement } from "lit-element";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import { HassTrigger } from "../ha-automation-trigger-row";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-homeassistant")
|
||||||
|
export default class HaHassTrigger extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: HassTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return {
|
||||||
|
event: "start",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { event } = this.trigger;
|
||||||
|
return html`
|
||||||
|
<label id="eventlabel">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.homeassistant.event"
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
<paper-radio-group
|
||||||
|
.selected=${event}
|
||||||
|
aria-labelledby="eventlabel"
|
||||||
|
@paper-radio-group-changed="${this._radioGroupPicked}"
|
||||||
|
>
|
||||||
|
<paper-radio-button name="start">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.homeassistant.start"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
<paper-radio-button name="shutdown">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.homeassistant.shutdown"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
</paper-radio-group>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _radioGroupPicked(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.trigger,
|
||||||
|
event: (ev.target as PaperRadioGroupElement).selected,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-homeassistant": HaHassTrigger;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import { LitElement, customElement, property, html } from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import {
|
||||||
|
handleChangeEvent,
|
||||||
|
TriggerElement,
|
||||||
|
MqttTrigger,
|
||||||
|
} from "../ha-automation-trigger-row";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-mqtt")
|
||||||
|
export class HaMQTTTrigger extends LitElement implements TriggerElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: MqttTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return { topic: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { topic, payload } = this.trigger;
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.mqtt.topic"
|
||||||
|
)}
|
||||||
|
name="topic"
|
||||||
|
.value=${topic}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.mqtt.payload"
|
||||||
|
)}
|
||||||
|
name="payload"
|
||||||
|
.value=${payload}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-mqtt": HaMQTTTrigger;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "../../../../../components/ha-textarea";
|
||||||
|
|
||||||
|
import "../../../../../components/entity/ha-entity-picker";
|
||||||
|
import { LitElement, html, customElement, property } from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import {
|
||||||
|
NumericStateTrigger,
|
||||||
|
ForDict,
|
||||||
|
handleChangeEvent,
|
||||||
|
} from "../ha-automation-trigger-row";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-numeric_state")
|
||||||
|
export default class HaNumericStateTrigger extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: NumericStateTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return {
|
||||||
|
entity_id: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const { value_template, entity_id, below, above } = this.trigger;
|
||||||
|
let trgFor = this.trigger.for;
|
||||||
|
|
||||||
|
if (
|
||||||
|
trgFor &&
|
||||||
|
((trgFor as ForDict).hours ||
|
||||||
|
(trgFor as ForDict).minutes ||
|
||||||
|
(trgFor as ForDict).seconds)
|
||||||
|
) {
|
||||||
|
// If the trigger was defined using the yaml dict syntax, convert it to
|
||||||
|
// the equivalent string format
|
||||||
|
let { hours = 0, minutes = 0, seconds = 0 } = trgFor as ForDict;
|
||||||
|
hours = hours.toString();
|
||||||
|
minutes = minutes.toString().padStart(2, "0");
|
||||||
|
seconds = seconds.toString().padStart(2, "0");
|
||||||
|
|
||||||
|
trgFor = `${hours}:${minutes}:${seconds}`;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<ha-entity-picker
|
||||||
|
.value="${entity_id}"
|
||||||
|
@value-changed="${this._entityPicked}"
|
||||||
|
.hass="${this.hass}"
|
||||||
|
allow-custom-entity
|
||||||
|
></ha-entity-picker>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.numeric_state.above"
|
||||||
|
)}
|
||||||
|
name="above"
|
||||||
|
.value=${above}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.numeric_state.below"
|
||||||
|
)}
|
||||||
|
name="below"
|
||||||
|
.value=${below}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
<ha-textarea
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.numeric_state.value_template"
|
||||||
|
)}
|
||||||
|
name="value_template"
|
||||||
|
.value=${value_template}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
dir="ltr"
|
||||||
|
></ha-textarea>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.state.for"
|
||||||
|
)}
|
||||||
|
name="for"
|
||||||
|
.value=${trgFor}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityPicked(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this.trigger, entity_id: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-numeric_state": HaNumericStateTrigger;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import { customElement, html, LitElement, property } from "lit-element";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
import "../../../../../components/entity/ha-entity-picker";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import {
|
||||||
|
handleChangeEvent,
|
||||||
|
TriggerElement,
|
||||||
|
StateTrigger,
|
||||||
|
ForDict,
|
||||||
|
} from "../ha-automation-trigger-row";
|
||||||
|
import { PolymerChangedEvent } from "../../../../../polymer-types";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-state")
|
||||||
|
export class HaStateTrigger extends LitElement implements TriggerElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: StateTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return { entity_id: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { entity_id, to, from } = this.trigger;
|
||||||
|
let trgFor = this.trigger.for;
|
||||||
|
|
||||||
|
if (
|
||||||
|
trgFor &&
|
||||||
|
((trgFor as ForDict).hours ||
|
||||||
|
(trgFor as ForDict).minutes ||
|
||||||
|
(trgFor as ForDict).seconds)
|
||||||
|
) {
|
||||||
|
// If the trigger was defined using the yaml dict syntax, convert it to
|
||||||
|
// the equivalent string format
|
||||||
|
let { hours = 0, minutes = 0, seconds = 0 } = trgFor as ForDict;
|
||||||
|
hours = hours.toString();
|
||||||
|
minutes = minutes.toString().padStart(2, "0");
|
||||||
|
seconds = seconds.toString().padStart(2, "0");
|
||||||
|
|
||||||
|
trgFor = `${hours}:${minutes}:${seconds}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<ha-entity-picker
|
||||||
|
.value=${entity_id}
|
||||||
|
@value-changed=${this._entityPicked}
|
||||||
|
.hass=${this.hass}
|
||||||
|
allow-custom-entity
|
||||||
|
></ha-entity-picker>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.state.from"
|
||||||
|
)}
|
||||||
|
.name=${"from"}
|
||||||
|
.value=${from}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.state.to"
|
||||||
|
)}
|
||||||
|
.name=${"to"}
|
||||||
|
.value=${to}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.state.for"
|
||||||
|
)}
|
||||||
|
.name=${"for"}
|
||||||
|
.value=${trgFor}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityPicked(ev: PolymerChangedEvent<string>) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this.trigger, entity_id: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-state": HaStateTrigger;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import "@polymer/paper-radio-button/paper-radio-button";
|
||||||
|
import "@polymer/paper-radio-group/paper-radio-group";
|
||||||
|
// tslint:disable-next-line
|
||||||
|
import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group";
|
||||||
|
import { LitElement, customElement, property, html } from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import {
|
||||||
|
SunTrigger,
|
||||||
|
handleChangeEvent,
|
||||||
|
TriggerElement,
|
||||||
|
} from "../ha-automation-trigger-row";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-sun")
|
||||||
|
export class HaSunTrigger extends LitElement implements TriggerElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: SunTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return {
|
||||||
|
event: "sunrise",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { offset, event } = this.trigger;
|
||||||
|
return html`
|
||||||
|
<label id="eventlabel">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.sun.event"
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
<paper-radio-group
|
||||||
|
.selected=${event}
|
||||||
|
aria-labelledby="eventlabel"
|
||||||
|
@paper-radio-group-changed=${this._radioGroupPicked}
|
||||||
|
>
|
||||||
|
<paper-radio-button name="sunrise">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.sun.sunrise"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
<paper-radio-button name="sunset">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.sun.sunset"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
</paper-radio-group>
|
||||||
|
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.sun.offset"
|
||||||
|
)}
|
||||||
|
name="offset"
|
||||||
|
.value=${offset}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _radioGroupPicked(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.trigger,
|
||||||
|
event: (ev.target as PaperRadioGroupElement).selected,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
import "../../../../../components/ha-textarea";
|
||||||
|
import { LitElement, property, html, customElement } from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import {
|
||||||
|
TemplateTrigger,
|
||||||
|
handleChangeEvent,
|
||||||
|
} from "../ha-automation-trigger-row";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-template")
|
||||||
|
export class HaTemplateTrigger extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: TemplateTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return { value_template: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { value_template } = this.trigger;
|
||||||
|
return html`
|
||||||
|
<ha-textarea
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.template.value_template"
|
||||||
|
)}
|
||||||
|
name="value_template"
|
||||||
|
.value=${value_template}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
dir="ltr"
|
||||||
|
></ha-textarea>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import { LitElement, html, property, customElement } from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import {
|
||||||
|
TimeTrigger,
|
||||||
|
handleChangeEvent,
|
||||||
|
TriggerElement,
|
||||||
|
} from "../ha-automation-trigger-row";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-time")
|
||||||
|
export class HaTimeTrigger extends LitElement implements TriggerElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: TimeTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return { at: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { at } = this.trigger;
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.time.at"
|
||||||
|
)}
|
||||||
|
name="at"
|
||||||
|
.value=${at}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import { LitElement, property, html, customElement } from "lit-element";
|
||||||
|
import {
|
||||||
|
TriggerElement,
|
||||||
|
handleChangeEvent,
|
||||||
|
TimePatternTrigger,
|
||||||
|
} from "../ha-automation-trigger-row";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-time_pattern")
|
||||||
|
export class HaTimePatternTrigger extends LitElement implements TriggerElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: TimePatternTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { hours, minutes, seconds } = this.trigger;
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.time_pattern.hours"
|
||||||
|
)}
|
||||||
|
name="hours"
|
||||||
|
.value=${hours}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.time_pattern.minutes"
|
||||||
|
)}
|
||||||
|
name="minutes"
|
||||||
|
.value=${minutes}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.time_pattern.seconds"
|
||||||
|
)}
|
||||||
|
name="seconds"
|
||||||
|
.value=${seconds}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-time_pattern": HaTimePatternTrigger;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import { LitElement, customElement, property, html } from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import {
|
||||||
|
WebhookTrigger,
|
||||||
|
handleChangeEvent,
|
||||||
|
} from "../ha-automation-trigger-row";
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-webhook")
|
||||||
|
export class HaWebhookTrigger extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: WebhookTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return {
|
||||||
|
webhook_id: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { webhook_id: webhookId } = this.trigger;
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.webhook.webhook_id"
|
||||||
|
)}
|
||||||
|
name="webhook_id"
|
||||||
|
.value=${webhookId}
|
||||||
|
@value-changed=${this._valueChanged}
|
||||||
|
></paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
handleChangeEvent(this, ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-webhook": HaWebhookTrigger;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
import "@polymer/paper-radio-button/paper-radio-button";
|
||||||
|
import "@polymer/paper-radio-group/paper-radio-group";
|
||||||
|
// tslint:disable-next-line
|
||||||
|
import { PaperRadioGroupElement } from "@polymer/paper-radio-group/paper-radio-group";
|
||||||
|
import "../../../../../components/entity/ha-entity-picker";
|
||||||
|
|
||||||
|
import { hasLocation } from "../../../../../common/entity/has_location";
|
||||||
|
import { computeStateDomain } from "../../../../../common/entity/compute_state_domain";
|
||||||
|
import { LitElement, property, html, customElement } from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../../../../types";
|
||||||
|
import { ZoneTrigger } from "../ha-automation-trigger-row";
|
||||||
|
import { PolymerChangedEvent } from "../../../../../polymer-types";
|
||||||
|
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||||
|
|
||||||
|
function zoneAndLocationFilter(stateObj) {
|
||||||
|
return hasLocation(stateObj) && computeStateDomain(stateObj) !== "zone";
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("ha-automation-trigger-zone")
|
||||||
|
export class HaZoneTrigger extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public trigger!: ZoneTrigger;
|
||||||
|
|
||||||
|
public static get defaultConfig() {
|
||||||
|
return {
|
||||||
|
entity_id: "",
|
||||||
|
zone: "",
|
||||||
|
event: "enter",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render() {
|
||||||
|
const { entity_id, zone, event } = this.trigger;
|
||||||
|
return html`
|
||||||
|
<ha-entity-picker
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.zone.entity"
|
||||||
|
)}
|
||||||
|
.value=${entity_id}
|
||||||
|
@value-changed=${this._entityPicked}
|
||||||
|
.hass=${this.hass}
|
||||||
|
allow-custom-entity
|
||||||
|
.entityFilter=${zoneAndLocationFilter}
|
||||||
|
></ha-entity-picker>
|
||||||
|
<ha-entity-picker
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.zone.zone"
|
||||||
|
)}
|
||||||
|
.value=${zone}
|
||||||
|
@value-changed=${this._zonePicked}
|
||||||
|
.hass=${this.hass}
|
||||||
|
allow-custom-entity
|
||||||
|
.includeDomains=${["zone"]}
|
||||||
|
></ha-entity-picker>
|
||||||
|
<label id="eventlabel">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.zone.event"
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
|
<paper-radio-group
|
||||||
|
.selected=${event}
|
||||||
|
aria-labelledby="eventlabel"
|
||||||
|
@paper-radio-group-changed=${this._radioGroupPicked}
|
||||||
|
>
|
||||||
|
<paper-radio-button name="enter">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.zone.enter"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
<paper-radio-button name="leave">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.automation.editor.triggers.type.zone.leave"
|
||||||
|
)}
|
||||||
|
</paper-radio-button>
|
||||||
|
</paper-radio-group>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityPicked(ev: PolymerChangedEvent<string>) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this.trigger, entity_id: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _zonePicked(ev: PolymerChangedEvent<string>) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: { ...this.trigger, zone: ev.detail.value },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _radioGroupPicked(ev: CustomEvent) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
fireEvent(this, "value-changed", {
|
||||||
|
value: {
|
||||||
|
...this.trigger,
|
||||||
|
event: (ev.target as PaperRadioGroupElement).selected,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-automation-trigger-zone": HaZoneTrigger;
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,9 @@ export const showCloudCertificateDialog = (
|
|||||||
fireEvent(element, "show-dialog", {
|
fireEvent(element, "show-dialog", {
|
||||||
dialogTag: "dialog-cloud-certificate",
|
dialogTag: "dialog-cloud-certificate",
|
||||||
dialogImport: () =>
|
dialogImport: () =>
|
||||||
import(/* webpackChunkName: "dialog-cloud-certificate" */ "./dialog-cloud-certificate"),
|
import(
|
||||||
|
/* webpackChunkName: "dialog-cloud-certificate" */ "./dialog-cloud-certificate"
|
||||||
|
),
|
||||||
dialogParams: webhookDialogParams,
|
dialogParams: webhookDialogParams,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,9 @@ export const showManageCloudhookDialog = (
|
|||||||
fireEvent(element, "show-dialog", {
|
fireEvent(element, "show-dialog", {
|
||||||
dialogTag: "dialog-manage-cloudhook",
|
dialogTag: "dialog-manage-cloudhook",
|
||||||
dialogImport: () =>
|
dialogImport: () =>
|
||||||
import(/* webpackChunkName: "cloud-webhook-manage-dialog" */ "./dialog-manage-cloudhook"),
|
import(
|
||||||
|
/* webpackChunkName: "cloud-webhook-manage-dialog" */ "./dialog-manage-cloudhook"
|
||||||
|
),
|
||||||
dialogParams: webhookDialogParams,
|
dialogParams: webhookDialogParams,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -46,12 +46,16 @@ class HaConfigCloud extends HassRouterPage {
|
|||||||
register: {
|
register: {
|
||||||
tag: "cloud-register",
|
tag: "cloud-register",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "cloud-register" */ "./register/cloud-register"),
|
import(
|
||||||
|
/* webpackChunkName: "cloud-register" */ "./register/cloud-register"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
"forgot-password": {
|
"forgot-password": {
|
||||||
tag: "cloud-forgot-password",
|
tag: "cloud-forgot-password",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "cloud-forgot-password" */ "./forgot-password/cloud-forgot-password"),
|
import(
|
||||||
|
/* webpackChunkName: "cloud-forgot-password" */ "./forgot-password/cloud-forgot-password"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
tag: "cloud-account",
|
tag: "cloud-account",
|
||||||
@ -59,7 +63,9 @@ class HaConfigCloud extends HassRouterPage {
|
|||||||
"google-assistant": {
|
"google-assistant": {
|
||||||
tag: "cloud-google-assistant",
|
tag: "cloud-google-assistant",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "cloud-google-assistant" */ "./google-assistant/cloud-google-assistant"),
|
import(
|
||||||
|
/* webpackChunkName: "cloud-google-assistant" */ "./google-assistant/cloud-google-assistant"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
alexa: {
|
alexa: {
|
||||||
tag: "cloud-alexa",
|
tag: "cloud-alexa",
|
||||||
|
@ -138,6 +138,9 @@ class HaConfigDashboard extends LitElement {
|
|||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
|
ha-config-navigation:last-child {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
ha-card {
|
ha-card {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@ -148,6 +151,7 @@ class HaConfigDashboard extends LitElement {
|
|||||||
.promo-advanced {
|
.promo-advanced {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
.promo-advanced a {
|
.promo-advanced a {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
|
@ -6,7 +6,9 @@ export interface EntityRegistryDetailDialogParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const loadEntityRegistryDetailDialog = () =>
|
export const loadEntityRegistryDetailDialog = () =>
|
||||||
import(/* webpackChunkName: "entity-registry-detail-dialog" */ "./dialog-entity-registry-detail");
|
import(
|
||||||
|
/* webpackChunkName: "entity-registry-detail-dialog" */ "./dialog-entity-registry-detail"
|
||||||
|
);
|
||||||
|
|
||||||
export const showEntityRegistryDetailDialog = (
|
export const showEntityRegistryDetailDialog = (
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
|
@ -31,82 +31,114 @@ class HaPanelConfig extends HassRouterPage {
|
|||||||
area_registry: {
|
area_registry: {
|
||||||
tag: "ha-config-area-registry",
|
tag: "ha-config-area-registry",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-area-registry" */ "./area_registry/ha-config-area-registry"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-area-registry" */ "./area_registry/ha-config-area-registry"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
automation: {
|
automation: {
|
||||||
tag: "ha-config-automation",
|
tag: "ha-config-automation",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
cloud: {
|
cloud: {
|
||||||
tag: "ha-config-cloud",
|
tag: "ha-config-cloud",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
core: {
|
core: {
|
||||||
tag: "ha-config-core",
|
tag: "ha-config-core",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-core" */ "./core/ha-config-core"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-core" */ "./core/ha-config-core"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
devices: {
|
devices: {
|
||||||
tag: "ha-config-devices",
|
tag: "ha-config-devices",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-devices" */ "./devices/ha-config-devices"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-devices" */ "./devices/ha-config-devices"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
server_control: {
|
server_control: {
|
||||||
tag: "ha-config-server-control",
|
tag: "ha-config-server-control",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-server-control" */ "./server_control/ha-config-server-control"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-server-control" */ "./server_control/ha-config-server-control"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
customize: {
|
customize: {
|
||||||
tag: "ha-config-customize",
|
tag: "ha-config-customize",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-customize" */ "./customize/ha-config-customize"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-customize" */ "./customize/ha-config-customize"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
dashboard: {
|
dashboard: {
|
||||||
tag: "ha-config-dashboard",
|
tag: "ha-config-dashboard",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-dashboard" */ "./dashboard/ha-config-dashboard"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-dashboard" */ "./dashboard/ha-config-dashboard"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
entity_registry: {
|
entity_registry: {
|
||||||
tag: "ha-config-entity-registry",
|
tag: "ha-config-entity-registry",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-entity-registry" */ "./entity_registry/ha-config-entity-registry"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-entity-registry" */ "./entity_registry/ha-config-entity-registry"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
integrations: {
|
integrations: {
|
||||||
tag: "ha-config-integrations",
|
tag: "ha-config-integrations",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-integrations" */ "./integrations/ha-config-integrations"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-integrations" */ "./integrations/ha-config-integrations"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
person: {
|
person: {
|
||||||
tag: "ha-config-person",
|
tag: "ha-config-person",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-person" */ "./person/ha-config-person"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-person" */ "./person/ha-config-person"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
script: {
|
script: {
|
||||||
tag: "ha-config-script",
|
tag: "ha-config-script",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-script" */ "./script/ha-config-script"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-script" */ "./script/ha-config-script"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
scene: {
|
scene: {
|
||||||
tag: "ha-config-scene",
|
tag: "ha-config-scene",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-scene" */ "./scene/ha-config-scene"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-scene" */ "./scene/ha-config-scene"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
users: {
|
users: {
|
||||||
tag: "ha-config-users",
|
tag: "ha-config-users",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-users" */ "./users/ha-config-users"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
zha: {
|
zha: {
|
||||||
tag: "zha-config-panel",
|
tag: "zha-config-panel",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-zha" */ "./zha/zha-config-panel"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-zha" */ "./zha/zha-config-panel"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
zwave: {
|
zwave: {
|
||||||
tag: "ha-config-zwave",
|
tag: "ha-config-zwave",
|
||||||
load: () =>
|
load: () =>
|
||||||
import(/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"),
|
import(
|
||||||
|
/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -133,7 +133,11 @@ export class HaConfigManagerDashboard extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</paper-item-body>
|
</paper-item-body>
|
||||||
<ha-icon-next></ha-icon-next>
|
<ha-icon-next
|
||||||
|
aria-label=${this.hass.localize(
|
||||||
|
"ui.panel.config.integrations.details"
|
||||||
|
)}
|
||||||
|
></ha-icon-next>
|
||||||
</paper-item>
|
</paper-item>
|
||||||
</a>
|
</a>
|
||||||
`
|
`
|
||||||
@ -154,6 +158,7 @@ export class HaConfigManagerDashboard extends LitElement {
|
|||||||
|
|
||||||
<ha-fab
|
<ha-fab
|
||||||
icon="hass:plus"
|
icon="hass:plus"
|
||||||
|
aria-label=${this.hass.localize("ui.panel.config.integrations.new")}
|
||||||
title=${this.hass.localize("ui.panel.config.integrations.new")}
|
title=${this.hass.localize("ui.panel.config.integrations.new")}
|
||||||
@click=${this._createFlow}
|
@click=${this._createFlow}
|
||||||
?rtl=${computeRTL(this.hass!)}
|
?rtl=${computeRTL(this.hass!)}
|
||||||
|
23
src/panels/config/js/automation-component.tsx
Normal file
23
src/panels/config/js/automation-component.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { h, Component, ComponentChild } from "preact";
|
||||||
|
|
||||||
|
export class AutomationComponent<P = {}, S = {}> extends Component<P, S> {
|
||||||
|
// @ts-ignore
|
||||||
|
protected initialized: boolean;
|
||||||
|
|
||||||
|
constructor(props?, context?) {
|
||||||
|
super(props, context);
|
||||||
|
this.initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount() {
|
||||||
|
this.initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillUnmount() {
|
||||||
|
this.initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(_props?, _state?, _context?: any): ComponentChild {
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,8 @@ import "../ha-config-section";
|
|||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-textarea";
|
import "../../../components/ha-textarea";
|
||||||
|
|
||||||
import Trigger from "./trigger/index";
|
import "../automation/trigger/ha-automation-trigger";
|
||||||
|
|
||||||
import Condition from "./condition/index";
|
import Condition from "./condition/index";
|
||||||
import Script from "./script/index";
|
import Script from "./script/index";
|
||||||
|
|
||||||
@ -26,8 +27,8 @@ export default class Automation extends Component<any> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public triggerChanged(trigger) {
|
public triggerChanged(ev: CustomEvent) {
|
||||||
this.props.onChange({ ...this.props.automation, trigger });
|
this.props.onChange({ ...this.props.automation, trigger: ev.detail.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
public conditionChanged(condition) {
|
public conditionChanged(condition) {
|
||||||
@ -90,11 +91,10 @@ export default class Automation extends Component<any> {
|
|||||||
)}
|
)}
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
<Trigger
|
<ha-automation-trigger
|
||||||
trigger={trigger}
|
triggers={trigger}
|
||||||
onChange={this.triggerChanged}
|
onvalue-changed={this.triggerChanged}
|
||||||
hass={hass}
|
hass={hass}
|
||||||
localize={localize}
|
|
||||||
/>
|
/>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu-light";
|
|||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
|
|
||||||
|
import YAMLTextArea from "../yaml_textarea";
|
||||||
|
|
||||||
import DeviceCondition from "./device";
|
import DeviceCondition from "./device";
|
||||||
import LogicalCondition from "./logical";
|
import LogicalCondition from "./logical";
|
||||||
import NumericStateCondition from "./numeric_state";
|
import NumericStateCondition from "./numeric_state";
|
||||||
@ -31,6 +33,7 @@ export default class ConditionEdit extends Component<any> {
|
|||||||
super();
|
super();
|
||||||
|
|
||||||
this.typeChanged = this.typeChanged.bind(this);
|
this.typeChanged = this.typeChanged.bind(this);
|
||||||
|
this.onYamlChange = this.onYamlChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public typeChanged(ev) {
|
public typeChanged(ev) {
|
||||||
@ -44,20 +47,24 @@ export default class ConditionEdit extends Component<any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render({ index, condition, onChange, hass, localize }) {
|
public render({ index, condition, onChange, hass, localize, yamlMode }) {
|
||||||
// tslint:disable-next-line: variable-name
|
// tslint:disable-next-line: variable-name
|
||||||
const Comp = TYPES[condition.condition];
|
const Comp = TYPES[condition.condition];
|
||||||
const selected = OPTIONS.indexOf(condition.condition);
|
const selected = OPTIONS.indexOf(condition.condition);
|
||||||
|
|
||||||
if (!Comp) {
|
if (yamlMode || !Comp) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div style="margin-right: 24px;">
|
||||||
{localize(
|
{!Comp && (
|
||||||
"ui.panel.config.automation.editor.conditions.unsupported_condition",
|
<div>
|
||||||
"condition",
|
{localize(
|
||||||
condition.condition
|
"ui.panel.config.automation.editor.conditions.unsupported_condition",
|
||||||
|
"condition",
|
||||||
|
condition.condition
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<pre>{JSON.stringify(condition, null, 2)}</pre>
|
<YAMLTextArea value={condition} onChange={this.onYamlChange} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -94,4 +101,8 @@ export default class ConditionEdit extends Component<any> {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onYamlChange(condition) {
|
||||||
|
this.props.onChange(this.props.index, condition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,16 @@ import "../../../../components/ha-card";
|
|||||||
import ConditionEdit from "./condition_edit";
|
import ConditionEdit from "./condition_edit";
|
||||||
|
|
||||||
export default class ConditionRow extends Component<any> {
|
export default class ConditionRow extends Component<any> {
|
||||||
|
public state: { yamlMode: boolean };
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
yamlMode: false,
|
||||||
|
};
|
||||||
|
|
||||||
this.onDelete = this.onDelete.bind(this);
|
this.onDelete = this.onDelete.bind(this);
|
||||||
|
this.switchYamlMode = this.switchYamlMode.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onDelete() {
|
public onDelete() {
|
||||||
@ -27,22 +33,32 @@ export default class ConditionRow extends Component<any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(props) {
|
public render(props, { yamlMode }) {
|
||||||
return (
|
return (
|
||||||
<ha-card>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="card-menu">
|
<div class="card-menu" style="z-index: 3">
|
||||||
<paper-menu-button
|
<paper-menu-button
|
||||||
no-animations
|
no-animations
|
||||||
horizontal-align="right"
|
horizontal-align="right"
|
||||||
horizontal-offset="-5"
|
horizontal-offset="-5"
|
||||||
vertical-offset="-5"
|
vertical-offset="-5"
|
||||||
|
close-on-activate
|
||||||
>
|
>
|
||||||
<paper-icon-button
|
<paper-icon-button
|
||||||
icon="hass:dots-vertical"
|
icon="hass:dots-vertical"
|
||||||
slot="dropdown-trigger"
|
slot="dropdown-trigger"
|
||||||
/>
|
/>
|
||||||
<paper-listbox slot="dropdown-content">
|
<paper-listbox slot="dropdown-content">
|
||||||
|
<paper-item onTap={this.switchYamlMode}>
|
||||||
|
{yamlMode
|
||||||
|
? props.localize(
|
||||||
|
"ui.panel.config.automation.editor.edit_ui"
|
||||||
|
)
|
||||||
|
: props.localize(
|
||||||
|
"ui.panel.config.automation.editor.edit_yaml"
|
||||||
|
)}
|
||||||
|
</paper-item>
|
||||||
<paper-item disabled>
|
<paper-item disabled>
|
||||||
{props.localize(
|
{props.localize(
|
||||||
"ui.panel.config.automation.editor.conditions.duplicate"
|
"ui.panel.config.automation.editor.conditions.duplicate"
|
||||||
@ -56,9 +72,15 @@ export default class ConditionRow extends Component<any> {
|
|||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-menu-button>
|
</paper-menu-button>
|
||||||
</div>
|
</div>
|
||||||
<ConditionEdit {...props} />
|
<ConditionEdit {...props} yamlMode={yamlMode} />
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private switchYamlMode() {
|
||||||
|
this.setState({
|
||||||
|
yamlMode: !this.state.yamlMode,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { h, Component } from "preact";
|
import { h } from "preact";
|
||||||
|
|
||||||
import "../../../../components/device/ha-device-picker";
|
import "../../../../components/device/ha-device-picker";
|
||||||
import "../../../../components/device/ha-device-condition-picker";
|
import "../../../../components/device/ha-device-condition-picker";
|
||||||
@ -8,7 +8,10 @@ import {
|
|||||||
fetchDeviceConditionCapabilities,
|
fetchDeviceConditionCapabilities,
|
||||||
deviceAutomationsEqual,
|
deviceAutomationsEqual,
|
||||||
} from "../../../../data/device_automation";
|
} from "../../../../data/device_automation";
|
||||||
export default class DeviceCondition extends Component<any, any> {
|
|
||||||
|
import { AutomationComponent } from "../automation-component";
|
||||||
|
|
||||||
|
export default class DeviceCondition extends AutomationComponent<any, any> {
|
||||||
private _origCondition;
|
private _origCondition;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -20,10 +23,16 @@ export default class DeviceCondition extends Component<any, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public devicePicked(ev) {
|
public devicePicked(ev) {
|
||||||
|
if (!this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.setState({ ...this.state, device_id: ev.target.value });
|
this.setState({ ...this.state, device_id: ev.target.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
public deviceConditionPicked(ev) {
|
public deviceConditionPicked(ev) {
|
||||||
|
if (!this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let condition = ev.target.value;
|
let condition = ev.target.value;
|
||||||
if (
|
if (
|
||||||
this._origCondition &&
|
this._origCondition &&
|
||||||
@ -74,6 +83,7 @@ export default class DeviceCondition extends Component<any, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
|
this.initialized = true;
|
||||||
if (!this.state.capabilities) {
|
if (!this.state.capabilities) {
|
||||||
this._getCapabilities();
|
this._getCapabilities();
|
||||||
}
|
}
|
||||||
@ -98,6 +108,9 @@ export default class DeviceCondition extends Component<any, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _extraFieldsChanged(ev) {
|
private _extraFieldsChanged(ev) {
|
||||||
|
if (!this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.props.onChange(this.props.index, {
|
this.props.onChange(this.props.index, {
|
||||||
...this.props.condition,
|
...this.props.condition,
|
||||||
...ev.detail.value,
|
...ev.detail.value,
|
||||||
@ -108,9 +121,7 @@ export default class DeviceCondition extends Component<any, any> {
|
|||||||
// Returns a callback for ha-form to calculate labels per schema object
|
// Returns a callback for ha-form to calculate labels per schema object
|
||||||
return (schema) =>
|
return (schema) =>
|
||||||
localize(
|
localize(
|
||||||
`ui.panel.config.automation.editor.condition.type.device.extra_fields.${
|
`ui.panel.config.automation.editor.condition.type.device.extra_fields.${schema.name}`
|
||||||
schema.name
|
|
||||||
}`
|
|
||||||
) || schema.name;
|
) || schema.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,22 @@
|
|||||||
import { h, Component } from "preact";
|
import { h } from "preact";
|
||||||
|
|
||||||
import Condition from "./index";
|
import Condition from "./index";
|
||||||
|
import { AutomationComponent } from "../automation-component";
|
||||||
|
|
||||||
export default class LogicalCondition extends Component<any, any> {
|
export default class LogicalCondition extends AutomationComponent<any> {
|
||||||
private _mounted = false;
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.conditionChanged = this.conditionChanged.bind(this);
|
this.conditionChanged = this.conditionChanged.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public conditionChanged(conditions) {
|
public conditionChanged(conditions) {
|
||||||
if (this._mounted) {
|
if (!this.initialized) {
|
||||||
this.props.onChange(this.props.index, {
|
return;
|
||||||
...this.props.condition,
|
|
||||||
conditions,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
this.props.onChange(this.props.index, {
|
||||||
|
...this.props.condition,
|
||||||
public componentWillMount() {
|
conditions,
|
||||||
this._mounted = true;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
public componentWillUnmount() {
|
|
||||||
this._mounted = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { h, Component } from "preact";
|
import { h } from "preact";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "../../../../components/ha-textarea";
|
import "../../../../components/ha-textarea";
|
||||||
import "../../../../components/entity/ha-entity-picker";
|
import "../../../../components/entity/ha-entity-picker";
|
||||||
|
|
||||||
import { onChangeEvent } from "../../../../common/preact/event";
|
import { onChangeEvent } from "../../../../common/preact/event";
|
||||||
|
import { AutomationComponent } from "../automation-component";
|
||||||
|
|
||||||
export default class NumericStateCondition extends Component<any> {
|
export default class NumericStateCondition extends AutomationComponent<any> {
|
||||||
private onChange: (obj: any) => void;
|
private onChange: (obj: any) => void;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -15,6 +16,9 @@ export default class NumericStateCondition extends Component<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public entityPicked(ev) {
|
public entityPicked(ev) {
|
||||||
|
if (!this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.props.onChange(this.props.index, {
|
this.props.onChange(this.props.index, {
|
||||||
...this.props.condition,
|
...this.props.condition,
|
||||||
entity_id: ev.target.value,
|
entity_id: ev.target.value,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { h, Component } from "preact";
|
import { h } from "preact";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "../../../../components/entity/ha-entity-picker";
|
import "../../../../components/entity/ha-entity-picker";
|
||||||
|
|
||||||
import { onChangeEvent } from "../../../../common/preact/event";
|
import { onChangeEvent } from "../../../../common/preact/event";
|
||||||
|
import { AutomationComponent } from "../automation-component";
|
||||||
|
|
||||||
export default class StateCondition extends Component<any> {
|
export default class StateCondition extends AutomationComponent<any> {
|
||||||
private onChange: (obj: any) => void;
|
private onChange: (obj: any) => void;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -14,6 +15,9 @@ export default class StateCondition extends Component<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public entityPicked(ev) {
|
public entityPicked(ev) {
|
||||||
|
if (!this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.props.onChange(this.props.index, {
|
this.props.onChange(this.props.index, {
|
||||||
...this.props.condition,
|
...this.props.condition,
|
||||||
entity_id: ev.target.value,
|
entity_id: ev.target.value,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user