mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-31 05:06:38 +00:00
commit
bbdaa4b7c1
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="home-assistant-frontend",
|
||||
version="20190508.0",
|
||||
version="20190509.0",
|
||||
description="The Home Assistant frontend",
|
||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||
author="The Home Assistant Authors",
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { LitElement, html, property, PropertyValues } from "lit-element";
|
||||
import {
|
||||
LitElement,
|
||||
html,
|
||||
property,
|
||||
PropertyValues,
|
||||
CSSResult,
|
||||
css,
|
||||
} from "lit-element";
|
||||
import "@material/mwc-button";
|
||||
import "../components/ha-form";
|
||||
import "../components/ha-markdown";
|
||||
@ -20,19 +27,6 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<style>
|
||||
:host {
|
||||
/* So we can set min-height to avoid jumping during loading */
|
||||
display: block;
|
||||
}
|
||||
.action {
|
||||
margin: 24px 0 8px;
|
||||
text-align: center;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
<form>
|
||||
${this._renderForm()}
|
||||
</form>
|
||||
@ -168,7 +162,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateStep(data);
|
||||
await this._updateStep(data);
|
||||
} else {
|
||||
this._state = "error";
|
||||
this._errorMessage = data.message;
|
||||
@ -199,7 +193,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
document.location.assign(url);
|
||||
}
|
||||
|
||||
private _updateStep(step: ConfigFlowStep) {
|
||||
private async _updateStep(step: ConfigFlowStep) {
|
||||
let stepData: any = null;
|
||||
if (
|
||||
this._step &&
|
||||
@ -215,6 +209,15 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
if (stepData != null) {
|
||||
this._stepData = stepData;
|
||||
}
|
||||
|
||||
await this.updateComplete;
|
||||
// 100ms to give all the form elements time to initialize.
|
||||
setTimeout(() => {
|
||||
const form = this.shadowRoot!.querySelector("ha-form");
|
||||
if (form) {
|
||||
(form as any).focus();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
private _computeStepDescription(step: ConfigFlowStepForm) {
|
||||
@ -282,7 +285,7 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
this._redirect(newStep.result);
|
||||
return;
|
||||
}
|
||||
this._updateStep(newStep);
|
||||
await this._updateStep(newStep);
|
||||
} catch (err) {
|
||||
// tslint:disable-next-line: no-console
|
||||
console.error("Error submitting step", err);
|
||||
@ -292,5 +295,21 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
this.style.setProperty("min-height", "");
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
/* So we can set min-height to avoid jumping during loading */
|
||||
display: block;
|
||||
}
|
||||
.action {
|
||||
margin: 24px 0 8px;
|
||||
text-align: center;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
customElements.define("ha-auth-flow", HaAuthFlow);
|
||||
|
@ -48,12 +48,12 @@ class HaCameraStream extends LitElement {
|
||||
}
|
||||
|
||||
return html`
|
||||
${this._shouldRenderMJPEG
|
||||
${__DEMO__ || this._shouldRenderMJPEG
|
||||
? html`
|
||||
<img
|
||||
@load=${this._elementResized}
|
||||
.src=${__DEMO__
|
||||
? "/demo/webcamp.jpg"
|
||||
? `/api/camera_proxy_stream/${this.stateObj.entity_id}`
|
||||
: computeMJPEGStreamUrl(this.stateObj)}
|
||||
.alt=${computeStateName(this.stateObj)}
|
||||
/>
|
||||
|
@ -189,6 +189,18 @@ class HaForm extends EventsMixin(PolymerElement) {
|
||||
};
|
||||
}
|
||||
|
||||
focus() {
|
||||
const input = this.shadowRoot.querySelector(
|
||||
"ha-form, paper-input, ha-paper-slider, paper-checkbox, paper-dropdown-menu"
|
||||
);
|
||||
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
input.focus();
|
||||
}
|
||||
|
||||
_isArray(val) {
|
||||
return Array.isArray(val);
|
||||
}
|
||||
|
@ -3,6 +3,15 @@ import { createCollection } from "home-assistant-js-websocket";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import { LocalizeFunc } from "../common/translations/localize";
|
||||
|
||||
export interface DataEntryFlowProgressedEvent {
|
||||
type: "data_entry_flow_progressed";
|
||||
data: {
|
||||
handler: string;
|
||||
flow_id: string;
|
||||
refresh: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ConfigEntry {
|
||||
entry_id: string;
|
||||
domain: string;
|
||||
@ -38,6 +47,15 @@ export interface ConfigFlowStepForm {
|
||||
description_placeholders: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface ConfigFlowStepExternal {
|
||||
type: "external";
|
||||
flow_id: string;
|
||||
handler: string;
|
||||
step_id: string;
|
||||
url: string;
|
||||
description_placeholders: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface ConfigFlowStepCreateEntry {
|
||||
type: "create_entry";
|
||||
version: number;
|
||||
@ -60,6 +78,7 @@ export interface ConfigFlowStepAbort {
|
||||
|
||||
export type ConfigFlowStep =
|
||||
| ConfigFlowStepForm
|
||||
| ConfigFlowStepExternal
|
||||
| ConfigFlowStepCreateEntry
|
||||
| ConfigFlowStepAbort;
|
||||
|
||||
|
@ -34,6 +34,7 @@ import { HaConfigFlowParams } from "./show-dialog-config-flow";
|
||||
import "./step-flow-pick-handler";
|
||||
import "./step-flow-loading";
|
||||
import "./step-flow-form";
|
||||
import "./step-flow-external";
|
||||
import "./step-flow-abort";
|
||||
import "./step-flow-create-entry";
|
||||
import {
|
||||
@ -155,6 +156,13 @@ class ConfigFlowDialog extends LitElement {
|
||||
.hass=${this.hass}
|
||||
></step-flow-form>
|
||||
`
|
||||
: this._step.type === "external"
|
||||
? html`
|
||||
<step-flow-external
|
||||
.step=${this._step}
|
||||
.hass=${this.hass}
|
||||
></step-flow-external>
|
||||
`
|
||||
: this._step.type === "abort"
|
||||
? html`
|
||||
<step-flow-abort
|
||||
@ -251,7 +259,7 @@ class ConfigFlowDialog extends LitElement {
|
||||
return;
|
||||
}
|
||||
const flowFinished = Boolean(
|
||||
this._step && ["success", "abort"].includes(this._step.type)
|
||||
this._step && ["create_entry", "abort"].includes(this._step.type)
|
||||
);
|
||||
|
||||
// If we created this flow, delete it now.
|
||||
|
106
src/dialogs/config-flow/step-flow-external.ts
Normal file
106
src/dialogs/config-flow/step-flow-external.ts
Normal file
@ -0,0 +1,106 @@
|
||||
import {
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
html,
|
||||
customElement,
|
||||
property,
|
||||
CSSResultArray,
|
||||
css,
|
||||
} from "lit-element";
|
||||
import "@material/mwc-button";
|
||||
|
||||
import {
|
||||
ConfigFlowStepExternal,
|
||||
DataEntryFlowProgressedEvent,
|
||||
fetchConfigFlow,
|
||||
} from "../../data/config_entries";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { localizeKey } from "../../common/translations/localize";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { configFlowContentStyles } from "./styles";
|
||||
|
||||
@customElement("step-flow-external")
|
||||
class StepFlowExternal extends LitElement {
|
||||
@property()
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@property()
|
||||
private step!: ConfigFlowStepExternal;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const localize = this.hass.localize;
|
||||
const step = this.step;
|
||||
|
||||
const description = localizeKey(
|
||||
localize,
|
||||
`component.${step.handler}.config.${step.step_id}.description`,
|
||||
step.description_placeholders
|
||||
);
|
||||
|
||||
return html`
|
||||
<h2>
|
||||
${localize(
|
||||
`component.${step.handler}.config.step.${step.step_id}.title`
|
||||
)}
|
||||
</h2>
|
||||
<div class="content">
|
||||
<p>
|
||||
${localize(
|
||||
"ui.panel.config.integrations.config_flow.external_step.description"
|
||||
)}
|
||||
</p>
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description} allow-svg></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
<div class="open-button">
|
||||
<a href=${this.step.url} target="_blank">
|
||||
<mwc-button raised>
|
||||
${localize(
|
||||
"ui.panel.config.integrations.config_flow.external_step.open_site"
|
||||
)}
|
||||
</mwc-button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.hass.connection.subscribeEvents<DataEntryFlowProgressedEvent>(
|
||||
async (ev) => {
|
||||
if (ev.data.flow_id !== this.step.flow_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const step = await fetchConfigFlow(this.hass, this.step.flow_id);
|
||||
fireEvent(this, "flow-update", { step });
|
||||
},
|
||||
"data_entry_flow_progressed"
|
||||
);
|
||||
window.open(this.step.url);
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
configFlowContentStyles,
|
||||
css`
|
||||
.open-button {
|
||||
text-align: center;
|
||||
padding: 24px 0;
|
||||
}
|
||||
.open-button a {
|
||||
text-decoration: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"step-flow-external": StepFlowExternal;
|
||||
}
|
||||
}
|
@ -22,7 +22,9 @@ let es5Loaded: Promise<unknown> | undefined;
|
||||
window.loadES5Adapter = () => {
|
||||
if (!es5Loaded) {
|
||||
es5Loaded = Promise.all([
|
||||
loadJS(`${__STATIC_PATH__}custom-elements-es5-adapter.js`).catch(),
|
||||
loadJS(
|
||||
`${__STATIC_PATH__}/polyfills/custom-elements-es5-adapter.js`
|
||||
).catch(),
|
||||
import(/* webpackChunkName: "compat" */ "./compatibility"),
|
||||
]);
|
||||
}
|
||||
@ -47,7 +49,9 @@ function initialize(panel: CustomPanelInfo, properties: {}) {
|
||||
let start: Promise<unknown> = Promise.resolve();
|
||||
|
||||
if (!webComponentsSupported) {
|
||||
start = start.then(() => loadJS("/static/webcomponents-bundle.js"));
|
||||
start = start.then(() =>
|
||||
loadJS(`${__STATIC_PATH__}/polyfills/webcomponents-bundle.js`)
|
||||
);
|
||||
}
|
||||
|
||||
if (__BUILD__ === "es5") {
|
||||
|
@ -18,8 +18,8 @@ export default class WaitAction extends Component {
|
||||
onTemplateChange(ev) {
|
||||
this.props.onChange(
|
||||
this.props.index,
|
||||
Object.assign({}, this.props.trigger, {
|
||||
[ev.target.name]: ev.target.value,
|
||||
Object.assign({}, this.props.action, {
|
||||
[ev.target.getAttribute("name")]: ev.target.value,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -53,6 +53,9 @@ const addEntities = (entities: Set<string>, obj) => {
|
||||
if (obj.cards) {
|
||||
obj.cards.forEach((card) => addEntities(entities, card));
|
||||
}
|
||||
if (obj.elements) {
|
||||
obj.elements.forEach((card) => addEntities(entities, card));
|
||||
}
|
||||
if (obj.badges) {
|
||||
obj.badges.forEach((badge) => addEntityId(entities, badge));
|
||||
}
|
||||
|
@ -850,6 +850,12 @@
|
||||
"device_unavailable": "device unavailable",
|
||||
"entity_unavailable": "entity unavailable",
|
||||
"no_area": "No Area"
|
||||
},
|
||||
"config_flow": {
|
||||
"external_step": {
|
||||
"description": "This step requires you to visit an external website to be completed.",
|
||||
"open_site": "Open website"
|
||||
}
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -855,6 +855,11 @@
|
||||
"required_fields": "Omple tots els camps obligatoris",
|
||||
"password_not_match": "Les contrasenyes no coincideixen"
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"intro": "Els dispositius i serveis es representen a Home Assistant com a integracions. Pots configurar-los ara o més tard des de la pàgina de configuració.",
|
||||
"more_integrations": "Més",
|
||||
"finish": "Finalitza"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
@ -603,6 +603,12 @@
|
||||
"device_unavailable": "device unavailable",
|
||||
"entity_unavailable": "entity unavailable",
|
||||
"no_area": "No Area"
|
||||
},
|
||||
"config_flow": {
|
||||
"external_step": {
|
||||
"description": "This step requires you to visit an external website to be completed.",
|
||||
"open_site": "Open website"
|
||||
}
|
||||
}
|
||||
},
|
||||
"zha": {
|
||||
|
@ -855,6 +855,11 @@
|
||||
"required_fields": "Complete todos los campos requeridos",
|
||||
"password_not_match": "Las contraseñas no coinciden"
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"intro": "Los dispositivos y servicios están representados en Home Assistant como integraciones. Puede configurarlos ahora, o hacerlo más tarde desde la pantalla de configuración.",
|
||||
"more_integrations": "Más",
|
||||
"finish": "Terminar"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
@ -868,6 +873,14 @@
|
||||
"title": "Bienvenido a casa",
|
||||
"no_devices": "Esta página te permite controlar tus dispositivos, aunque parece que aún no has configurado ninguno. Dirígete a la página de integraciones para empezar.",
|
||||
"go_to_integrations_page": "Ir a la página de integraciones."
|
||||
},
|
||||
"picture-elements": {
|
||||
"hold": "Mantener:",
|
||||
"tap": "Toque:",
|
||||
"navigate_to": "Navegar a {location}",
|
||||
"toggle": "Alternar {name}",
|
||||
"call_service": "Ejecutar servicio {name}",
|
||||
"more_info": "Mostrar más información: {name}"
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
@ -925,7 +938,8 @@
|
||||
},
|
||||
"sidebar": {
|
||||
"log_out": "Cerrar sesión",
|
||||
"developer_tools": "Herramientas para desarrolladores"
|
||||
"developer_tools": "Herramientas para desarrolladores",
|
||||
"external_app_configuration": "Configuración de la aplicación"
|
||||
},
|
||||
"common": {
|
||||
"loading": "Cargando",
|
||||
|
@ -581,7 +581,8 @@
|
||||
"cloud": {
|
||||
"caption": "Cloud Home Assistant",
|
||||
"description_login": "Connecté en tant que {email}",
|
||||
"description_not_login": "Pas connecté"
|
||||
"description_not_login": "Pas connecté",
|
||||
"description_features": "Contrôle hors de la maison, intégration avec Alexa et Google Assistant."
|
||||
},
|
||||
"integrations": {
|
||||
"caption": "Intégrations",
|
||||
@ -854,6 +855,11 @@
|
||||
"required_fields": "Remplissez tous les champs requis",
|
||||
"password_not_match": "Les mots de passe ne correspondent pas"
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"intro": "Les appareils et les services sont représentés dans Home Assistant sous forme d’intégrations. Vous pouvez les configurer maintenant ou le faire plus tard à partir de l'écran de configuration.",
|
||||
"more_integrations": "Plus",
|
||||
"finish": "Terminer"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user