mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-01 13:37:47 +00:00
commit
806e70b6c9
@ -7,31 +7,23 @@ trigger:
|
||||
- "*"
|
||||
pr: none
|
||||
variables:
|
||||
- name: versionBuilder
|
||||
value: "5.2"
|
||||
- group: github
|
||||
- name: versionWheels
|
||||
value: '1.1-3.7-alpine3.10'
|
||||
- name: versionNode
|
||||
value: '12.1'
|
||||
- group: twine
|
||||
resources:
|
||||
repositories:
|
||||
- repository: azure
|
||||
type: github
|
||||
name: 'home-assistant/ci-azure'
|
||||
endpoint: 'home-assistant'
|
||||
|
||||
|
||||
stages:
|
||||
- stage: "Validate"
|
||||
jobs:
|
||||
- job: "VersionValidate"
|
||||
pool:
|
||||
vmImage: "ubuntu-latest"
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
displayName: "Use Python 3.7"
|
||||
inputs:
|
||||
versionSpec: "3.7"
|
||||
- script: |
|
||||
setup_version="$(python setup.py -V)"
|
||||
branch_version="$(Build.SourceBranchName)"
|
||||
|
||||
if [ "${setup_version}" != "${branch_version}" ]; then
|
||||
echo "Version of tag ${branch_version} don't match with ${setup_version}!"
|
||||
exit 1
|
||||
fi
|
||||
displayName: "Check version of branch/tag"
|
||||
- template: templates/azp-job-version.yaml@azure
|
||||
|
||||
- stage: "Build"
|
||||
jobs:
|
||||
@ -44,9 +36,9 @@ stages:
|
||||
inputs:
|
||||
versionSpec: "3.7"
|
||||
- task: NodeTool@0
|
||||
displayName: "Use Node 12.1"
|
||||
displayName: "Use Node $(versionNode)"
|
||||
inputs:
|
||||
versionSpec: "12.1"
|
||||
versionSpec: "$(versionNode)"
|
||||
- script: pip install twine wheel
|
||||
displayName: "Install tools"
|
||||
- script: |
|
||||
@ -55,3 +47,18 @@ stages:
|
||||
|
||||
script/release
|
||||
displayName: "Build and release package"
|
||||
- template: templates/azp-job-wheels.yaml@azure
|
||||
parameters:
|
||||
builderVersion: '$(versionWheels)'
|
||||
builderApk: 'build-base'
|
||||
wheelsLocal: true
|
||||
preBuild:
|
||||
- task: NodeTool@0
|
||||
displayName: "Use Node $(versionNode)"
|
||||
inputs:
|
||||
versionSpec: "$(versionNode)"
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
yarn install
|
||||
script/build_frontend
|
||||
|
@ -1,3 +1,5 @@
|
||||
# Run it twice, second time we just delete.
|
||||
aws s3 sync dist s3://cast.home-assistant.io --acl public-read
|
||||
aws s3 sync dist s3://cast.home-assistant.io --acl public-read --delete
|
||||
# Don't delete as it might break open sites that need to load code splitted things.
|
||||
# aws s3 sync dist s3://cast.home-assistant.io --acl public-read --delete
|
||||
# Todo : update JS first, HTML last.
|
||||
|
@ -118,7 +118,42 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section-header">How does Home Assistant Cast work?</div>
|
||||
<div class="section-header" id="https">
|
||||
Why does Home Assistant Cast require me to authorize my Home Assistant
|
||||
instance?
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
You're currently looking at the Home Assistant Cast launcher
|
||||
application. This is a standalone application to launch Home Assistant
|
||||
Cast on your Chromecast. Because Chromecasts do not allow us to log in
|
||||
to Home Assistant, we need to supply authentication to it from the
|
||||
launcher. This authentication is obtained when you authorize your
|
||||
instance. Your authentication credentials will remain in your browser
|
||||
and on your Cast device.
|
||||
</p>
|
||||
<p>
|
||||
Your authentication credentials or Home Assistant url are never sent
|
||||
to the Cloud. You can validate this behavior in
|
||||
<a
|
||||
href="https://github.com/home-assistant/home-assistant-polymer/tree/dev/cast"
|
||||
target="_blank"
|
||||
>the source code</a
|
||||
>.
|
||||
</p>
|
||||
<p>
|
||||
The launcher application exists to make it possible to use Home
|
||||
Assistant Cast with older versions of Home Assistant.
|
||||
</p>
|
||||
<p>
|
||||
Starting with Home Assistant 0.97, Home Assistant Cast is also built
|
||||
into the Lovelace UI as a special entities card row. Since the
|
||||
Lovelace UI already has authentication, you will be able to start
|
||||
casting right away.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section-header">Wat does Home Assistant Cast do?</div>
|
||||
<div class="card-content">
|
||||
<p>
|
||||
Home Assistant Cast is a receiver application for the Chromecast. When
|
||||
|
@ -42,10 +42,13 @@
|
||||
})();
|
||||
</script>
|
||||
<script>
|
||||
var _gaq=[['_setAccount','UA-57927901-9'],['_trackPageview']];
|
||||
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
|
||||
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
|
||||
s.parentNode.insertBefore(g,s)}(document,'script'));
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-57927901-9', 'auto');
|
||||
ga('send', 'pageview', location.pathname.includes("auth_callback") === -1 ? location.pathname : "/");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -9,4 +9,10 @@
|
||||
font-size: initial;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
var _gaq=[['_setAccount','UA-57927901-10'],['_trackPageview']];
|
||||
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
|
||||
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
|
||||
s.parentNode.insertBefore(g,s)}(document,'script'));
|
||||
</script>
|
||||
</html>
|
||||
|
@ -158,16 +158,6 @@ class HcCast extends LitElement {
|
||||
|
||||
protected updated(changedProps) {
|
||||
super.updated(changedProps);
|
||||
if (this.castManager && this.castManager.status) {
|
||||
const selectEl = this.shadowRoot!.querySelector("select");
|
||||
if (selectEl) {
|
||||
this.shadowRoot!.querySelector("select")!.value =
|
||||
this.castManager.castConnectedToOurHass &&
|
||||
!this.castManager.status.showDemo
|
||||
? this.castManager.status.lovelacePath || ""
|
||||
: "";
|
||||
}
|
||||
}
|
||||
this.toggleAttribute(
|
||||
"hide-icons",
|
||||
this.lovelaceConfig
|
||||
|
@ -16,8 +16,10 @@ import "./hc-launch-screen";
|
||||
@customElement("hc-lovelace")
|
||||
class HcLovelace extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public lovelaceConfig!: LovelaceConfig;
|
||||
@property() public viewPath?: string;
|
||||
|
||||
@property() public viewPath?: string | number;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
const index = this._viewIndex;
|
||||
@ -50,10 +52,11 @@ class HcLovelace extends LitElement {
|
||||
|
||||
protected updated(changedProps) {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (changedProps.has("viewPath") || changedProps.has("lovelaceConfig")) {
|
||||
const index = this._viewIndex;
|
||||
|
||||
if (index) {
|
||||
if (index !== undefined) {
|
||||
this.shadowRoot!.querySelector("hui-view")!.style.background =
|
||||
this.lovelaceConfig.views[index].background ||
|
||||
this.lovelaceConfig.background ||
|
||||
@ -64,7 +67,7 @@ class HcLovelace extends LitElement {
|
||||
|
||||
private get _viewIndex() {
|
||||
const selectedView = this.viewPath;
|
||||
const selectedViewInt = parseInt(selectedView!, 10);
|
||||
const selectedViewInt = parseInt(selectedView as string, 10);
|
||||
for (let i = 0; i < this.lovelaceConfig.views.length; i++) {
|
||||
if (
|
||||
this.lovelaceConfig.views[i].path === selectedView ||
|
||||
@ -77,7 +80,6 @@ class HcLovelace extends LitElement {
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
// We're applying a 10% transform so it all shows a little bigger.
|
||||
return css`
|
||||
:host {
|
||||
min-height: 100vh;
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { HassElement } from "../../../../src/state/hass-element";
|
||||
import {
|
||||
getAuth,
|
||||
createConnection,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket";
|
||||
import { customElement, TemplateResult, html, property } from "lit-element";
|
||||
import { HassElement } from "../../../../src/state/hass-element";
|
||||
import {
|
||||
HassMessage,
|
||||
ConnectMessage,
|
||||
@ -26,9 +26,13 @@ import { isNavigationClick } from "../../../../src/common/dom/is-navigation-clic
|
||||
@customElement("hc-main")
|
||||
export class HcMain extends HassElement {
|
||||
@property() private _showDemo = false;
|
||||
|
||||
@property() private _lovelaceConfig?: LovelaceConfig;
|
||||
@property() private _lovelacePath: string | null = null;
|
||||
|
||||
@property() private _lovelacePath: string | number | null = null;
|
||||
|
||||
@property() private _error?: string;
|
||||
|
||||
private _unsubLovelace?: UnsubscribeFunc;
|
||||
|
||||
public processIncomingMessage(msg: HassMessage) {
|
||||
@ -53,7 +57,7 @@ export class HcMain extends HassElement {
|
||||
`;
|
||||
}
|
||||
|
||||
if (!this._lovelaceConfig || !this._lovelacePath) {
|
||||
if (!this._lovelaceConfig || this._lovelacePath === null) {
|
||||
return html`
|
||||
<hc-launch-screen
|
||||
.hass=${this.hass}
|
||||
|
@ -20,7 +20,7 @@
|
||||
"@material/mwc-base": "^0.6.0",
|
||||
"@material/mwc-button": "^0.6.0",
|
||||
"@material/mwc-ripple": "^0.6.0",
|
||||
"@mdi/svg": "3.7.95",
|
||||
"@mdi/svg": "3.9.97",
|
||||
"@polymer/app-layout": "^3.0.2",
|
||||
"@polymer/app-localize-behavior": "^3.0.1",
|
||||
"@polymer/app-route": "^3.0.2",
|
||||
|
10
script/setup
Executable file
10
script/setup
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
# Resolve all frontend dependencies that the application requires to develop.
|
||||
|
||||
# Stop on errors
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
script/bootstrap
|
||||
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="home-assistant-frontend",
|
||||
version="20190805.0",
|
||||
version="20190811.0",
|
||||
description="The Home Assistant frontend",
|
||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||
author="The Home Assistant Authors",
|
||||
|
@ -106,7 +106,6 @@ class HaAuthFlow extends litLocalizeLiteMixin(LitElement) {
|
||||
? html`
|
||||
<ha-markdown
|
||||
.content=${this._computeStepDescription(step)}
|
||||
allow-svg
|
||||
></ha-markdown>
|
||||
`
|
||||
: html``}
|
||||
|
@ -285,7 +285,7 @@ class HaWeatherCard extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
hail: "hass:weather-hail",
|
||||
lightning: "hass:weather-lightning",
|
||||
"lightning-rainy": "hass:weather-lightning-rainy",
|
||||
partlycloudy: "hass:weather-partlycloudy",
|
||||
partlycloudy: "hass:weather-partly-cloudy",
|
||||
pouring: "hass:weather-pouring",
|
||||
rainy: "hass:weather-rainy",
|
||||
snowy: "hass:weather-snowy",
|
||||
|
@ -1,10 +1,10 @@
|
||||
// Nessages to be processed inside the Cast Receiver app
|
||||
|
||||
import { Auth } from "home-assistant-js-websocket";
|
||||
import { CastManager } from "./cast_manager";
|
||||
|
||||
import { BaseCastMessage } from "./types";
|
||||
import { CAST_DEV_HASS_URL, CAST_DEV } from "./const";
|
||||
import { Auth } from "home-assistant-js-websocket";
|
||||
|
||||
export interface GetStatusMessage extends BaseCastMessage {
|
||||
type: "get_status";
|
||||
@ -19,7 +19,7 @@ export interface ConnectMessage extends BaseCastMessage {
|
||||
|
||||
export interface ShowLovelaceViewMessage extends BaseCastMessage {
|
||||
type: "show_lovelace_view";
|
||||
viewPath: string | null;
|
||||
viewPath: string | number | null;
|
||||
}
|
||||
|
||||
export interface ShowDemoMessage extends BaseCastMessage {
|
||||
@ -64,7 +64,6 @@ export const ensureConnectedCastSession = (cast: CastManager, auth: Auth) => {
|
||||
if (cast.castConnectedToOurHass) {
|
||||
unsub();
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -7,7 +7,7 @@ export interface ReceiverStatusMessage extends BaseCastMessage {
|
||||
connected: boolean;
|
||||
showDemo: boolean;
|
||||
hassUrl?: string;
|
||||
lovelacePath?: string | null;
|
||||
lovelacePath?: string | number | null;
|
||||
}
|
||||
|
||||
export type SenderMessage = ReceiverStatusMessage;
|
||||
|
@ -3,11 +3,7 @@ import { EventsMixin } from "../mixins/events-mixin";
|
||||
|
||||
let loaded = null;
|
||||
|
||||
/**
|
||||
* White list allowed svg tag.
|
||||
* Only put in the tag used in QR code, can be extend in future.
|
||||
*/
|
||||
const svgWhiteList = ["svg", "path"];
|
||||
const tagWhiteList = ["svg", "path", "ha-icon"];
|
||||
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
||||
@ -16,12 +12,8 @@ class HaMarkdown extends EventsMixin(PolymerElement) {
|
||||
static get properties() {
|
||||
return {
|
||||
content: {
|
||||
type: String,
|
||||
observer: "_render",
|
||||
},
|
||||
allowSvg: {
|
||||
type: Boolean,
|
||||
value: false,
|
||||
type: String,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -51,7 +43,9 @@ class HaMarkdown extends EventsMixin(PolymerElement) {
|
||||
}
|
||||
|
||||
_render() {
|
||||
if (this._scriptLoaded === 0 || this._renderScheduled) return;
|
||||
if (this._scriptLoaded === 0 || this._renderScheduled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._renderScheduled = true;
|
||||
|
||||
@ -62,14 +56,13 @@ class HaMarkdown extends EventsMixin(PolymerElement) {
|
||||
if (this._scriptLoaded === 1) {
|
||||
this.innerHTML = this.filterXSS(
|
||||
this.marked(this.content, {
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: true,
|
||||
}),
|
||||
{
|
||||
onIgnoreTag: this.allowSvg
|
||||
? (tag, html) => (svgWhiteList.indexOf(tag) >= 0 ? html : null)
|
||||
: null,
|
||||
onIgnoreTag: (tag, html) =>
|
||||
tagWhiteList.indexOf(tag) >= 0 ? html : null,
|
||||
}
|
||||
);
|
||||
this._resize();
|
||||
|
@ -36,21 +36,21 @@ const SHOW_AFTER_SPACER = ["config", "developer-tools", "hassio"];
|
||||
|
||||
const SUPPORT_SCROLL_IF_NEEDED = "scrollIntoViewIfNeeded" in document.body;
|
||||
|
||||
const SORT_VALUE = {
|
||||
const SORT_VALUE_URL_PATHS = {
|
||||
map: 1,
|
||||
logbook: 2,
|
||||
history: 3,
|
||||
"developer-tools": 9,
|
||||
hassio: 10,
|
||||
configuration: 11,
|
||||
config: 11,
|
||||
};
|
||||
|
||||
const panelSorter = (a, b) => {
|
||||
const aBuiltIn = a.component_name in SORT_VALUE;
|
||||
const bBuiltIn = b.component_name in SORT_VALUE;
|
||||
const aBuiltIn = a.url_path in SORT_VALUE_URL_PATHS;
|
||||
const bBuiltIn = b.url_path in SORT_VALUE_URL_PATHS;
|
||||
|
||||
if (aBuiltIn && bBuiltIn) {
|
||||
return SORT_VALUE[a.component_name] - SORT_VALUE[b.component_name];
|
||||
return SORT_VALUE_URL_PATHS[a.url_path] - SORT_VALUE_URL_PATHS[b.url_path];
|
||||
}
|
||||
if (aBuiltIn) {
|
||||
return -1;
|
||||
@ -81,7 +81,7 @@ const computePanels = (hass: HomeAssistant): [PanelInfo[], PanelInfo[]] => {
|
||||
if (!panel.title) {
|
||||
return;
|
||||
}
|
||||
(SHOW_AFTER_SPACER.includes(panel.component_name)
|
||||
(SHOW_AFTER_SPACER.includes(panel.url_path)
|
||||
? afterSpacer
|
||||
: beforeSpacer
|
||||
).push(panel);
|
||||
|
20
src/data/ws-templates.ts
Normal file
20
src/data/ws-templates.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Connection, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
|
||||
interface RenderTemplateResult {
|
||||
result: string;
|
||||
}
|
||||
|
||||
export const subscribeRenderTemplate = (
|
||||
conn: Connection,
|
||||
onChange: (result: string) => void,
|
||||
params: {
|
||||
template: string;
|
||||
entity_ids?: string | string[];
|
||||
variables?: object;
|
||||
}
|
||||
): Promise<UnsubscribeFunc> => {
|
||||
return conn.subscribeMessage(
|
||||
(msg: RenderTemplateResult) => onChange(msg.result),
|
||||
{ type: "render_template", ...params }
|
||||
);
|
||||
};
|
@ -37,7 +37,7 @@ class StepFlowAbort extends LitElement {
|
||||
<div class="content">
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description} allow-svg></ha-markdown>
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
|
@ -56,7 +56,7 @@ class StepFlowCreateEntry extends LitElement {
|
||||
<div class="content">
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description} allow-svg></ha-markdown>
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
<p>Created config for ${step.title}.</p>
|
||||
|
@ -51,7 +51,7 @@ class StepFlowExternal extends LitElement {
|
||||
</p>
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description} allow-svg></ha-markdown>
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
<div class="open-button">
|
||||
|
@ -79,7 +79,7 @@ class StepFlowForm extends LitElement {
|
||||
: ""}
|
||||
${description
|
||||
? html`
|
||||
<ha-markdown .content=${description} allow-svg></ha-markdown>
|
||||
<ha-markdown .content=${description}></ha-markdown>
|
||||
`
|
||||
: ""}
|
||||
<ha-form
|
||||
|
@ -162,7 +162,7 @@ class MoreInfoWeather extends LocalizeMixin(PolymerElement) {
|
||||
hail: "hass:weather-hail",
|
||||
lightning: "hass:weather-lightning",
|
||||
"lightning-rainy": "hass:weather-lightning-rainy",
|
||||
partlycloudy: "hass:weather-partlycloudy",
|
||||
partlycloudy: "hass:weather-partly-cloudy",
|
||||
pouring: "hass:weather-pouring",
|
||||
rainy: "hass:weather-rainy",
|
||||
snowy: "hass:weather-snowy",
|
||||
|
@ -24,7 +24,10 @@ export interface MockHomeAssistant extends HomeAssistant {
|
||||
updateHass(obj: Partial<MockHomeAssistant>);
|
||||
updateStates(newStates: HassEntities);
|
||||
addEntities(entites: Entity | Entity[], replace?: boolean);
|
||||
mockWS(type: string, callback: (msg: any) => any);
|
||||
mockWS(
|
||||
type: string,
|
||||
callback: (msg: any, onChange?: (response: any) => void) => any
|
||||
);
|
||||
mockAPI(path: string | RegExp, callback: MockRestCallback);
|
||||
mockEvent(event);
|
||||
mockTheme(theme: { [key: string]: string } | null);
|
||||
@ -108,7 +111,7 @@ export const provideHass = (
|
||||
console.error(`Unknown WS command: ${msg.type}`);
|
||||
}
|
||||
},
|
||||
sendMessagePromise: (msg) => {
|
||||
sendMessagePromise: async (msg) => {
|
||||
const callback = wsCommands[msg.type];
|
||||
return callback
|
||||
? callback(msg)
|
||||
@ -119,6 +122,17 @@ export const provideHass = (
|
||||
} is not implemented in provide_hass.`,
|
||||
});
|
||||
},
|
||||
subscribeMessage: async (onChange, msg) => {
|
||||
const callback = wsCommands[msg.type];
|
||||
return callback
|
||||
? callback(msg, onChange)
|
||||
: Promise.reject({
|
||||
code: "command_not_mocked",
|
||||
message: `WS Command ${
|
||||
msg.type
|
||||
} is not implemented in provide_hass.`,
|
||||
});
|
||||
},
|
||||
subscribeEvents: async (
|
||||
// @ts-ignore
|
||||
callback,
|
||||
|
@ -128,6 +128,13 @@ class HaConfigSectionServerControl extends LocalizeMixin(PolymerElement) {
|
||||
hidden$="[[!scriptLoaded(hass)]]"
|
||||
>[[localize('ui.panel.config.server_control.section.reloading.script')]]
|
||||
</ha-call-service-button>
|
||||
<ha-call-service-button
|
||||
hass="[[hass]]"
|
||||
domain="scene"
|
||||
service="reload"
|
||||
hidden$="[[!sceneLoaded(hass)]]"
|
||||
>[[localize('ui.panel.config.server_control.section.reloading.scene')]]
|
||||
</ha-call-service-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
</template>
|
||||
@ -200,6 +207,10 @@ class HaConfigSectionServerControl extends LocalizeMixin(PolymerElement) {
|
||||
return isComponentLoaded(hass, "script");
|
||||
}
|
||||
|
||||
sceneLoaded(hass) {
|
||||
return isComponentLoaded(hass, "scene");
|
||||
}
|
||||
|
||||
validateConfig() {
|
||||
this.validating = true;
|
||||
this.validateLog = "";
|
||||
|
@ -8,12 +8,15 @@ import {
|
||||
CSSResult,
|
||||
} from "lit-element";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-markdown";
|
||||
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { MarkdownCardConfig } from "./types";
|
||||
import { subscribeRenderTemplate } from "../../../data/ws-templates";
|
||||
|
||||
@customElement("hui-markdown-card")
|
||||
export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
@ -27,11 +30,16 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
@property() private _config?: MarkdownCardConfig;
|
||||
@property() private _content?: string = "";
|
||||
@property() private _unsubRenderTemplate?: Promise<UnsubscribeFunc>;
|
||||
@property() private _hass?: HomeAssistant;
|
||||
|
||||
public getCardSize(): number {
|
||||
return (
|
||||
this._config!.content.split("\n").length + (this._config!.title ? 1 : 0)
|
||||
);
|
||||
return this._config === undefined
|
||||
? 3
|
||||
: this._config.card_size === undefined
|
||||
? this._config.content.split("\n").length + (this._config.title ? 1 : 0)
|
||||
: this._config.card_size;
|
||||
}
|
||||
|
||||
public setConfig(config: MarkdownCardConfig): void {
|
||||
@ -40,6 +48,20 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
this._config = config;
|
||||
|
||||
this._disconnect();
|
||||
if (this._hass) {
|
||||
this._connect();
|
||||
}
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
this._disconnect();
|
||||
}
|
||||
|
||||
public set hass(hass) {
|
||||
this._hass = hass;
|
||||
this._connect();
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
@ -53,12 +75,47 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
|
||||
class="markdown ${classMap({
|
||||
"no-header": !this._config.title,
|
||||
})}"
|
||||
.content="${this._config.content}"
|
||||
.content="${this._content}"
|
||||
></ha-markdown>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _connect() {
|
||||
if (!this._unsubRenderTemplate && this._hass && this._config) {
|
||||
this._unsubRenderTemplate = subscribeRenderTemplate(
|
||||
this._hass.connection,
|
||||
(result) => {
|
||||
this._content = result;
|
||||
},
|
||||
{
|
||||
template: this._config.content,
|
||||
entity_ids: this._config.entity_id,
|
||||
}
|
||||
);
|
||||
this._unsubRenderTemplate.catch(() => {
|
||||
this._content = this._config!.content;
|
||||
this._unsubRenderTemplate = undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _disconnect() {
|
||||
if (this._unsubRenderTemplate) {
|
||||
try {
|
||||
const unsub = await this._unsubRenderTemplate;
|
||||
this._unsubRenderTemplate = undefined;
|
||||
await unsub();
|
||||
} catch (e) {
|
||||
if (e.code === "not_found") {
|
||||
// If we get here, the connection was probably already closed. Ignore.
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
ha-markdown {
|
||||
|
@ -118,6 +118,8 @@ export interface MarkdownCardConfig extends LovelaceCardConfig {
|
||||
type: "markdown";
|
||||
content: string;
|
||||
title?: string;
|
||||
card_size?: number;
|
||||
entity_ids?: string | string[];
|
||||
}
|
||||
|
||||
export interface MediaControlCardConfig extends LovelaceCardConfig {
|
||||
|
281
src/panels/lovelace/editor/card-editor/hui-card-editor.ts
Normal file
281
src/panels/lovelace/editor/card-editor/hui-card-editor.ts
Normal file
@ -0,0 +1,281 @@
|
||||
import {
|
||||
html,
|
||||
css,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
CSSResult,
|
||||
customElement,
|
||||
property,
|
||||
} from "lit-element";
|
||||
|
||||
import yaml from "js-yaml";
|
||||
|
||||
import "@material/mwc-button";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||
import { LovelaceCardEditor } from "../../types";
|
||||
import { getCardElementTag } from "../../common/get-card-element-tag";
|
||||
|
||||
import "../../components/hui-yaml-editor";
|
||||
// This is not a duplicate import, one is for types, one is for element.
|
||||
// tslint:disable-next-line
|
||||
import { HuiYamlEditor } from "../../components/hui-yaml-editor";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { EntityConfig } from "../../entity-rows/types";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"entities-changed": {
|
||||
entities: EntityConfig[];
|
||||
};
|
||||
"config-changed": {
|
||||
config: LovelaceCardConfig;
|
||||
error?: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export interface UIConfigChangedEvent extends Event {
|
||||
detail: {
|
||||
config: LovelaceCardConfig;
|
||||
};
|
||||
}
|
||||
|
||||
@customElement("hui-card-editor")
|
||||
export class HuiCardEditor extends LitElement {
|
||||
@property() public hass?: HomeAssistant;
|
||||
|
||||
@property() private _yaml?: string;
|
||||
@property() private _config?: LovelaceCardConfig;
|
||||
@property() private _configElement?: LovelaceCardEditor;
|
||||
@property() private _configElType?: string;
|
||||
@property() private _GUImode: boolean = true;
|
||||
// Error: Configuration broken - do not save
|
||||
@property() private _error?: string;
|
||||
// Warning: GUI editor can't handle configuration - ok to save
|
||||
@property() private _warning?: string;
|
||||
@property() private _loading: boolean = false;
|
||||
|
||||
public get yaml(): string {
|
||||
return this._yaml || "";
|
||||
}
|
||||
public set yaml(_yaml: string) {
|
||||
this._yaml = _yaml;
|
||||
try {
|
||||
this._config = yaml.safeLoad(this.yaml);
|
||||
this._updateConfigElement();
|
||||
setTimeout(() => {
|
||||
if (this._yamlEditor) {
|
||||
this._yamlEditor.codemirror.refresh();
|
||||
}
|
||||
}, 1);
|
||||
this._error = undefined;
|
||||
} catch (err) {
|
||||
this._error = err.message;
|
||||
}
|
||||
fireEvent(this, "config-changed", {
|
||||
config: this.value!,
|
||||
error: this._error,
|
||||
});
|
||||
}
|
||||
|
||||
public get value(): LovelaceCardConfig | undefined {
|
||||
return this._config;
|
||||
}
|
||||
public set value(config: LovelaceCardConfig | undefined) {
|
||||
if (JSON.stringify(config) !== JSON.stringify(this._config || {})) {
|
||||
this.yaml = yaml.safeDump(config);
|
||||
}
|
||||
}
|
||||
|
||||
public get hasError(): boolean {
|
||||
return this._error !== undefined;
|
||||
}
|
||||
|
||||
private get _yamlEditor(): HuiYamlEditor {
|
||||
return this.shadowRoot!.querySelector("hui-yaml-editor")!;
|
||||
}
|
||||
|
||||
public toggleMode() {
|
||||
this._GUImode = !this._GUImode;
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<div class="wrapper">
|
||||
${this._GUImode
|
||||
? html`
|
||||
<div class="gui-editor">
|
||||
${this._loading
|
||||
? html`
|
||||
<paper-spinner
|
||||
active
|
||||
alt="Loading"
|
||||
class="center margin-bot"
|
||||
></paper-spinner>
|
||||
`
|
||||
: this._configElement}
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<div class="yaml-editor">
|
||||
<hui-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.value=${this.yaml}
|
||||
@yaml-changed=${this._handleYAMLChanged}
|
||||
></hui-yaml-editor>
|
||||
</div>
|
||||
`}
|
||||
${this._error
|
||||
? html`
|
||||
<div class="error">
|
||||
${this._error}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this._warning
|
||||
? html`
|
||||
<div class="warning">
|
||||
${this._warning}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<div class="buttons">
|
||||
<mwc-button
|
||||
@click=${this.toggleMode}
|
||||
?disabled=${this._warning || this._error}
|
||||
?unelevated=${this._GUImode === false}
|
||||
>
|
||||
<ha-icon icon="mdi:code-braces"></ha-icon>
|
||||
</mwc-button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected updated(changedProperties) {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (changedProperties.has("_GUImode")) {
|
||||
if (this._GUImode === false) {
|
||||
// Refresh code editor when switching to yaml mode
|
||||
this._yamlEditor.codemirror.refresh();
|
||||
this._yamlEditor.codemirror.focus();
|
||||
}
|
||||
fireEvent(this as HTMLElement, "iron-resize");
|
||||
}
|
||||
}
|
||||
|
||||
private _handleUIConfigChanged(ev: UIConfigChangedEvent) {
|
||||
ev.stopPropagation();
|
||||
const config = ev.detail.config;
|
||||
this.value = config;
|
||||
}
|
||||
private _handleYAMLChanged(ev) {
|
||||
ev.stopPropagation();
|
||||
const newYaml = ev.detail.value;
|
||||
if (newYaml !== this.yaml) {
|
||||
this.yaml = newYaml;
|
||||
}
|
||||
}
|
||||
|
||||
private async _updateConfigElement(): Promise<void> {
|
||||
if (!this.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cardType = this.value.type;
|
||||
let configElement = this._configElement;
|
||||
try {
|
||||
this._error = undefined;
|
||||
this._warning = undefined;
|
||||
|
||||
if (this._configElType !== cardType) {
|
||||
// If the card type has changed, we need to load a new GUI editor
|
||||
if (!this.value.type) {
|
||||
throw new Error("No card type defined");
|
||||
}
|
||||
|
||||
const tag = getCardElementTag(cardType);
|
||||
|
||||
// Check if the card type exists
|
||||
const elClass = customElements.get(tag);
|
||||
if (!elClass) {
|
||||
throw new Error(`Unknown card type encountered: ${cardType}.`);
|
||||
}
|
||||
|
||||
this._loading = true;
|
||||
// Check if a GUI editor exists
|
||||
if (elClass && elClass.getConfigElement) {
|
||||
configElement = await elClass.getConfigElement();
|
||||
} else {
|
||||
configElement = undefined;
|
||||
throw Error(`WARNING: No GUI editor available for: ${cardType}`);
|
||||
}
|
||||
|
||||
this._configElement = configElement;
|
||||
this._configElType = cardType;
|
||||
}
|
||||
|
||||
// Setup GUI editor and check that it can handle the current config
|
||||
try {
|
||||
this._configElement!.setConfig(this.value);
|
||||
} catch (err) {
|
||||
throw Error(`WARNING: ${err.message}`);
|
||||
}
|
||||
|
||||
// Perform final setup
|
||||
this._configElement!.hass = this.hass;
|
||||
this._configElement!.addEventListener("config-changed", (ev) =>
|
||||
this._handleUIConfigChanged(ev as UIConfigChangedEvent)
|
||||
);
|
||||
|
||||
return;
|
||||
} catch (err) {
|
||||
if (err.message.startsWith("WARNING:")) {
|
||||
this._warning = err.message.substr(8);
|
||||
} else {
|
||||
this._error = err;
|
||||
}
|
||||
this._GUImode = false;
|
||||
} finally {
|
||||
this._loading = false;
|
||||
fireEvent(this, "iron-resize");
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
display: flex;
|
||||
}
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
.gui-editor,
|
||||
.yaml-editor {
|
||||
padding: 8px 0px;
|
||||
}
|
||||
.error {
|
||||
color: #ef5350;
|
||||
}
|
||||
.warning {
|
||||
color: #ffa726;
|
||||
}
|
||||
.buttons {
|
||||
text-align: right;
|
||||
padding: 8px 0px;
|
||||
}
|
||||
paper-spinner {
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-card-editor": HuiCardEditor;
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||
import { getCardElementTag } from "../../common/get-card-element-tag";
|
||||
import { CardPickTarget } from "../types";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
|
||||
const cards = [
|
||||
{ name: "Alarm panel", type: "alarm-panel" },
|
||||
@ -60,6 +61,9 @@ export class HuiCardPicker extends LitElement {
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
<div class="cards-container">
|
||||
<mwc-button @click="${this._manualPicked}">MANUAL CARD</mwc-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -85,6 +89,12 @@ export class HuiCardPicker extends LitElement {
|
||||
];
|
||||
}
|
||||
|
||||
private _manualPicked(): void {
|
||||
fireEvent(this, "config-changed", {
|
||||
config: { type: "" },
|
||||
});
|
||||
}
|
||||
|
||||
private _cardPicked(ev: Event): void {
|
||||
const type = (ev.currentTarget! as CardPickTarget).type;
|
||||
const tag = getCardElementTag(type);
|
||||
@ -97,7 +107,7 @@ export class HuiCardPicker extends LitElement {
|
||||
config = { ...config, ...cardConfig };
|
||||
}
|
||||
|
||||
this.cardPicked!(config);
|
||||
fireEvent(this, "config-changed", { config });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
import {
|
||||
css,
|
||||
html,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
CSSResultArray,
|
||||
customElement,
|
||||
property,
|
||||
} from "lit-element";
|
||||
@ -9,11 +11,17 @@ import {
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||
import "./hui-edit-card";
|
||||
import "./hui-dialog-pick-card";
|
||||
import "./hui-card-editor";
|
||||
// tslint:disable-next-line
|
||||
import { HuiCardEditor } from "./hui-card-editor";
|
||||
import "./hui-card-preview";
|
||||
import "./hui-card-picker";
|
||||
import { EditCardDialogParams } from "./show-edit-card-dialog";
|
||||
import { addCard, replaceCard } from "../config-util";
|
||||
|
||||
import "../../../../components/dialog/ha-paper-dialog";
|
||||
import { haStyleDialog } from "../../../../resources/styles";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
@ -33,72 +41,227 @@ export class HuiDialogEditCard extends LitElement {
|
||||
|
||||
@property() private _cardConfig?: LovelaceCardConfig;
|
||||
|
||||
@property() private _newCard?: boolean;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._cardPicked = this._cardPicked.bind(this);
|
||||
this._cancel = this._cancel.bind(this);
|
||||
this._save = this._save.bind(this);
|
||||
}
|
||||
@property() private _saving: boolean = false;
|
||||
@property() private _error?: string;
|
||||
|
||||
public async showDialog(params: EditCardDialogParams): Promise<void> {
|
||||
this._params = params;
|
||||
const [view, card] = params.path;
|
||||
this._newCard = card !== undefined ? false : true;
|
||||
this._cardConfig =
|
||||
card !== undefined
|
||||
? params.lovelace.config.views[view].cards![card]
|
||||
: undefined;
|
||||
}
|
||||
|
||||
private get _cardEditorEl(): HuiCardEditor | null {
|
||||
return this.shadowRoot!.querySelector("hui-card-editor");
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this._params) {
|
||||
return html``;
|
||||
}
|
||||
if (!this._cardConfig) {
|
||||
// Card picker
|
||||
return html`
|
||||
<hui-dialog-pick-card
|
||||
.hass="${this.hass}"
|
||||
.cardPicked="${this._cardPicked}"
|
||||
.closeDialog="${this._cancel}"
|
||||
></hui-dialog-pick-card>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<hui-edit-card
|
||||
.hass="${this.hass}"
|
||||
.lovelace="${this._params.lovelace}"
|
||||
.cardConfig="${this._cardConfig}"
|
||||
.closeDialog="${this._cancel}"
|
||||
.saveCard="${this._save}"
|
||||
.newCard="${this._newCard}"
|
||||
>
|
||||
</hui-edit-card>
|
||||
<ha-paper-dialog with-backdrop opened modal>
|
||||
<h2>
|
||||
${this.hass!.localize("ui.panel.lovelace.editor.edit_card.header")}
|
||||
</h2>
|
||||
<paper-dialog-scrollable>
|
||||
${this._cardConfig === undefined
|
||||
? html`
|
||||
<hui-card-picker
|
||||
.hass="${this.hass}"
|
||||
@config-changed="${this._handleConfigChanged}"
|
||||
></hui-card-picker>
|
||||
`
|
||||
: html`
|
||||
<div class="content">
|
||||
<div class="element-editor">
|
||||
<hui-card-editor
|
||||
.hass="${this.hass}"
|
||||
.value="${this._cardConfig}"
|
||||
@config-changed="${this._handleConfigChanged}"
|
||||
></hui-card-editor>
|
||||
</div>
|
||||
<div class="element-preview">
|
||||
<hui-card-preview
|
||||
.hass="${this.hass}"
|
||||
.config="${this._cardConfig}"
|
||||
class=${this._error ? "blur" : ""}
|
||||
></hui-card-preview>
|
||||
${this._error
|
||||
? html`
|
||||
<paper-spinner
|
||||
active
|
||||
alt="Can't update card"
|
||||
></paper-spinner>
|
||||
`
|
||||
: ``}
|
||||
</div>
|
||||
</div>
|
||||
`}
|
||||
</paper-dialog-scrollable>
|
||||
<div class="paper-dialog-buttons">
|
||||
<mwc-button @click="${this._close}">
|
||||
${this.hass!.localize("ui.common.cancel")}
|
||||
</mwc-button>
|
||||
<mwc-button
|
||||
?disabled="${!this._canSave || this._saving}"
|
||||
@click="${this._save}"
|
||||
>
|
||||
${this._saving
|
||||
? html`
|
||||
<paper-spinner active alt="Saving"></paper-spinner>
|
||||
`
|
||||
: this.hass!.localize("ui.common.save")}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-paper-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _cardPicked(cardConf: LovelaceCardConfig): void {
|
||||
this._cardConfig = cardConf;
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
:host {
|
||||
--code-mirror-max-height: calc(100vh - 176px);
|
||||
}
|
||||
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
/* overrule the ha-style-dialog max-height on small screens */
|
||||
ha-paper-dialog {
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 660px) {
|
||||
ha-paper-dialog {
|
||||
width: 845px;
|
||||
}
|
||||
}
|
||||
|
||||
ha-paper-dialog {
|
||||
max-width: 845px;
|
||||
}
|
||||
|
||||
.center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 -10px;
|
||||
}
|
||||
.content hui-card-preview {
|
||||
margin: 4px auto;
|
||||
max-width: 390px;
|
||||
}
|
||||
.content .element-editor {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
ha-paper-dialog {
|
||||
max-width: none;
|
||||
width: 1000px;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-direction: row;
|
||||
}
|
||||
.content > * {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.content hui-card-preview {
|
||||
padding: 8px 0;
|
||||
margin: auto 10px;
|
||||
max-width: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
mwc-button paper-spinner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.element-editor {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.blur {
|
||||
filter: blur(2px) grayscale(100%);
|
||||
}
|
||||
.element-preview {
|
||||
position: relative;
|
||||
}
|
||||
.element-preview paper-spinner {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
hui-card-preview {
|
||||
padding-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
private _cancel(): void {
|
||||
private _handleConfigChanged(ev) {
|
||||
this._cardConfig = ev.detail.config;
|
||||
this._error = ev.detail.error;
|
||||
}
|
||||
|
||||
private _close(): void {
|
||||
this._params = undefined;
|
||||
this._cardConfig = undefined;
|
||||
this._error = undefined;
|
||||
}
|
||||
|
||||
private async _save(cardConf: LovelaceCardConfig): Promise<void> {
|
||||
private get _canSave(): boolean {
|
||||
if (this._saving) {
|
||||
return false;
|
||||
}
|
||||
if (this._cardConfig === undefined) {
|
||||
return false;
|
||||
}
|
||||
if (this._cardEditorEl && this._cardEditorEl.hasError) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private async _save(): Promise<void> {
|
||||
const lovelace = this._params!.lovelace;
|
||||
this._saving = true;
|
||||
await lovelace.saveConfig(
|
||||
this._params!.path.length === 1
|
||||
? addCard(lovelace.config, this._params!.path as [number], cardConf)
|
||||
? addCard(
|
||||
lovelace.config,
|
||||
this._params!.path as [number],
|
||||
this._cardConfig!
|
||||
)
|
||||
: replaceCard(
|
||||
lovelace.config,
|
||||
this._params!.path as [number, number],
|
||||
cardConf
|
||||
this._cardConfig!
|
||||
)
|
||||
);
|
||||
this._saving = false;
|
||||
this._close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,88 +0,0 @@
|
||||
import {
|
||||
html,
|
||||
css,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
CSSResult,
|
||||
customElement,
|
||||
} from "lit-element";
|
||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||
|
||||
import "../../../../components/dialog/ha-paper-dialog";
|
||||
|
||||
import { haStyleDialog } from "../../../../resources/styles";
|
||||
|
||||
import "./hui-card-picker";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||
|
||||
@customElement("hui-dialog-pick-card")
|
||||
export class HuiDialogPickCard extends LitElement {
|
||||
public hass?: HomeAssistant;
|
||||
public cardPicked?: (cardConf: LovelaceCardConfig) => void;
|
||||
public closeDialog?: () => void;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
<ha-paper-dialog
|
||||
with-backdrop
|
||||
opened
|
||||
@opened-changed="${this._openedChanged}"
|
||||
>
|
||||
<h2>
|
||||
${this.hass!.localize("ui.panel.lovelace.editor.edit_card.header")}
|
||||
</h2>
|
||||
<paper-dialog-scrollable>
|
||||
<hui-card-picker
|
||||
.hass="${this.hass}"
|
||||
.cardPicked="${this.cardPicked}"
|
||||
></hui-card-picker>
|
||||
</paper-dialog-scrollable>
|
||||
<div class="paper-dialog-buttons">
|
||||
<mwc-button @click="${this._skipPick}">MANUAL CARD</mwc-button>
|
||||
</div>
|
||||
</ha-paper-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _openedChanged(ev): void {
|
||||
if (!ev.detail.value) {
|
||||
this.closeDialog!();
|
||||
}
|
||||
}
|
||||
|
||||
private _skipPick() {
|
||||
this.cardPicked!({ type: "" });
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
/* overrule the ha-style-dialog max-height on small screens */
|
||||
ha-paper-dialog {
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 660px) {
|
||||
ha-paper-dialog {
|
||||
width: 650px;
|
||||
}
|
||||
}
|
||||
|
||||
ha-paper-dialog {
|
||||
max-width: 650px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-dialog-pick-card": HuiDialogPickCard;
|
||||
}
|
||||
}
|
@ -1,493 +0,0 @@
|
||||
import {
|
||||
html,
|
||||
css,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
CSSResult,
|
||||
customElement,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
import yaml from "js-yaml";
|
||||
|
||||
import { haStyleDialog } from "../../../../resources/styles";
|
||||
|
||||
import "@polymer/paper-spinner/paper-spinner";
|
||||
import "@polymer/paper-dialog/paper-dialog";
|
||||
import "../../../../components/dialog/ha-paper-dialog";
|
||||
// This is not a duplicate import, one is for types, one is for element.
|
||||
// tslint:disable-next-line
|
||||
import { HaPaperDialog } from "../../../../components/dialog/ha-paper-dialog";
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||
import { HomeAssistant } from "../../../../types";
|
||||
import { LovelaceCardConfig } from "../../../../data/lovelace";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
|
||||
import "../../components/hui-yaml-editor";
|
||||
// This is not a duplicate import, one is for types, one is for element.
|
||||
// tslint:disable-next-line
|
||||
import { HuiYamlEditor } from "../../components/hui-yaml-editor";
|
||||
import "./hui-card-preview";
|
||||
// This is not a duplicate import, one is for types, one is for element.
|
||||
// tslint:disable-next-line
|
||||
import { HuiCardPreview } from "./hui-card-preview";
|
||||
import { LovelaceCardEditor, Lovelace } from "../../types";
|
||||
import { ConfigError } from "../types";
|
||||
import { EntityConfig } from "../../entity-rows/types";
|
||||
import { getCardElementTag } from "../../common/get-card-element-tag";
|
||||
import { afterNextRender } from "../../../../common/util/render-status";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"entities-changed": {
|
||||
entities: EntityConfig[];
|
||||
};
|
||||
"config-changed": {
|
||||
config: LovelaceCardConfig;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@customElement("hui-edit-card")
|
||||
export class HuiEditCard extends LitElement {
|
||||
@property() public hass?: HomeAssistant;
|
||||
|
||||
@property() public cardConfig?: LovelaceCardConfig;
|
||||
|
||||
public lovelace?: Lovelace;
|
||||
|
||||
public closeDialog?: () => void;
|
||||
|
||||
public saveCard?: (cardConf: LovelaceCardConfig) => Promise<void>;
|
||||
|
||||
public newCard?: boolean;
|
||||
|
||||
@property() private _configElement?: LovelaceCardEditor | null;
|
||||
|
||||
@property() private _uiEditor?: boolean;
|
||||
|
||||
@property() private _cardConfig?: LovelaceCardConfig;
|
||||
|
||||
@property() private _configState?: string;
|
||||
|
||||
@property() private _loading?: boolean;
|
||||
|
||||
@property() private _saving: boolean;
|
||||
|
||||
@property() private _errorMsg?: TemplateResult;
|
||||
|
||||
private get _dialog(): HaPaperDialog {
|
||||
return this.shadowRoot!.querySelector("ha-paper-dialog")!;
|
||||
}
|
||||
|
||||
private get _previewEl(): HuiCardPreview {
|
||||
return this.shadowRoot!.querySelector("hui-card-preview")!;
|
||||
}
|
||||
|
||||
// tslint:disable-next-line
|
||||
private __cardYaml: string | undefined;
|
||||
|
||||
private get _cardYaml(): string | undefined {
|
||||
if (!this.__cardYaml) {
|
||||
this.__cardYaml = yaml.safeDump(this._cardConfig);
|
||||
}
|
||||
return this.__cardYaml;
|
||||
}
|
||||
|
||||
private set _cardYaml(yml: string | undefined) {
|
||||
this.__cardYaml = yml;
|
||||
}
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
this._saving = false;
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
|
||||
if (!changedProperties.has("cardConfig")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._cardConfig = undefined;
|
||||
this._cardYaml = undefined;
|
||||
this._configState = "OK";
|
||||
this._uiEditor = true;
|
||||
this._errorMsg = undefined;
|
||||
this._configElement = undefined;
|
||||
|
||||
this._loading = true;
|
||||
this._loadConfigElement(this.cardConfig!);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
let content;
|
||||
let preview;
|
||||
if (this._configElement !== undefined) {
|
||||
content = html`
|
||||
<div class="element-editor">
|
||||
${this._uiEditor
|
||||
? this._configElement
|
||||
: html`
|
||||
<hui-yaml-editor
|
||||
.hass="${this.hass}"
|
||||
.value="${this._cardYaml}"
|
||||
@yaml-changed="${this._handleYamlChanged}"
|
||||
@yaml-save="${this._save}"
|
||||
></hui-yaml-editor>
|
||||
`}
|
||||
</div>
|
||||
`;
|
||||
|
||||
preview = html`
|
||||
<hui-card-preview .hass="${this.hass}"> </hui-card-preview>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-paper-dialog
|
||||
with-backdrop
|
||||
opened
|
||||
modal
|
||||
@opened-changed="${this._openedChanged}"
|
||||
>
|
||||
<h2>
|
||||
${this.hass!.localize("ui.panel.lovelace.editor.edit_card.header")}
|
||||
</h2>
|
||||
<paper-spinner
|
||||
?active="${this._loading}"
|
||||
alt="Loading"
|
||||
class="center margin-bot"
|
||||
></paper-spinner>
|
||||
<paper-dialog-scrollable
|
||||
class="${classMap({ hidden: this._loading! })}"
|
||||
>
|
||||
${this._errorMsg
|
||||
? html`
|
||||
<div class="error">${this._errorMsg}</div>
|
||||
`
|
||||
: ""}
|
||||
<div class="content">${content}${preview}</div>
|
||||
</paper-dialog-scrollable>
|
||||
${!this._loading
|
||||
? html`
|
||||
<div class="paper-dialog-buttons">
|
||||
<mwc-button
|
||||
class="toggle-button"
|
||||
?disabled="${this._configElement === null ||
|
||||
this._configState !== "OK"}"
|
||||
@click="${this._toggleEditor}"
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.lovelace.editor.edit_card.toggle_editor"
|
||||
)}</mwc-button
|
||||
>
|
||||
<mwc-button @click="${this.closeDialog}"
|
||||
>${this.hass!.localize("ui.common.cancel")}</mwc-button
|
||||
>
|
||||
<mwc-button
|
||||
?disabled="${this._saving || this._configState !== "OK"}"
|
||||
@click="${this._save}"
|
||||
>
|
||||
<paper-spinner
|
||||
?active="${this._saving}"
|
||||
alt="Saving"
|
||||
></paper-spinner>
|
||||
${this.hass!.localize("ui.common.save")}
|
||||
</mwc-button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-paper-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _loadedDialog(): Promise<void> {
|
||||
await this.updateComplete;
|
||||
this._loading = false;
|
||||
this._resizeDialog();
|
||||
if (!this._uiEditor) {
|
||||
afterNextRender(() => {
|
||||
this.yamlEditor.codemirror.refresh();
|
||||
this._resizeDialog();
|
||||
this.yamlEditor.codemirror.focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _resizeDialog(): Promise<void> {
|
||||
await this.updateComplete;
|
||||
fireEvent(this._dialog as HTMLElement, "iron-resize");
|
||||
}
|
||||
|
||||
private async _save(): Promise<void> {
|
||||
if (!this._isConfigValid()) {
|
||||
alert("Your config is not valid, please fix your config before saving.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._isConfigChanged()) {
|
||||
this.closeDialog!();
|
||||
return;
|
||||
}
|
||||
|
||||
this._saving = true;
|
||||
|
||||
try {
|
||||
await this.saveCard!(this._cardConfig!);
|
||||
this._cardYaml = undefined;
|
||||
this.closeDialog!();
|
||||
} catch (err) {
|
||||
alert(`Saving failed: ${err.message}`);
|
||||
} finally {
|
||||
this._saving = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _handleYamlChanged(ev: CustomEvent): void {
|
||||
try {
|
||||
this._cardConfig = yaml.safeLoad(ev.detail.value);
|
||||
this._updatePreview(this._cardConfig!);
|
||||
this._configState = "OK";
|
||||
} catch (err) {
|
||||
this._configState = "YAML_ERROR";
|
||||
this._setPreviewError({
|
||||
type: "YAML Error",
|
||||
message: err,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _handleUIConfigChanged(value: LovelaceCardConfig): void {
|
||||
this._cardConfig = value;
|
||||
this._updatePreview(value);
|
||||
}
|
||||
|
||||
private async _updatePreview(config: LovelaceCardConfig): Promise<void> {
|
||||
await this.updateComplete;
|
||||
|
||||
if (!this._previewEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._previewEl.config = config;
|
||||
|
||||
if (this._loading) {
|
||||
this._loadedDialog();
|
||||
} else {
|
||||
this._resizeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private _setPreviewError(error: ConfigError): void {
|
||||
if (!this._previewEl) {
|
||||
return;
|
||||
}
|
||||
this._previewEl.error = error;
|
||||
|
||||
this._resizeDialog();
|
||||
}
|
||||
|
||||
private async _toggleEditor(): Promise<void> {
|
||||
this._cardYaml = undefined;
|
||||
if (this._uiEditor) {
|
||||
this._uiEditor = false;
|
||||
} else if (this._configElement) {
|
||||
const success = await this._loadConfigElement(this._cardConfig!);
|
||||
if (!success) {
|
||||
this._loadedDialog();
|
||||
} else {
|
||||
this._uiEditor = true;
|
||||
this._configElement.setConfig(this._cardConfig!);
|
||||
}
|
||||
}
|
||||
this._resizeDialog();
|
||||
}
|
||||
|
||||
private _isConfigValid(): boolean {
|
||||
if (!this._cardConfig) {
|
||||
return false;
|
||||
}
|
||||
if (this._configState === "OK") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private _isConfigChanged(): boolean {
|
||||
if (this.newCard) {
|
||||
return true;
|
||||
}
|
||||
return JSON.stringify(this._cardConfig) !== JSON.stringify(this.cardConfig);
|
||||
}
|
||||
|
||||
private async _loadConfigElement(conf: LovelaceCardConfig): Promise<boolean> {
|
||||
if (!conf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._errorMsg = undefined;
|
||||
this._loading = true;
|
||||
this._configElement = undefined;
|
||||
|
||||
const tag = getCardElementTag(conf.type);
|
||||
|
||||
const elClass = customElements.get(tag);
|
||||
let configElement;
|
||||
|
||||
this._cardConfig = conf;
|
||||
|
||||
if (elClass && elClass.getConfigElement) {
|
||||
configElement = await elClass.getConfigElement();
|
||||
} else {
|
||||
this._updatePreview(conf);
|
||||
this._uiEditor = false;
|
||||
this._configElement = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
configElement.setConfig(conf);
|
||||
} catch (err) {
|
||||
this._errorMsg = html`
|
||||
Your config is not supported by the UI editor:<br /><b>${err.message}</b
|
||||
><br />Falling back to YAML editor.
|
||||
`;
|
||||
this._updatePreview(conf);
|
||||
this._uiEditor = false;
|
||||
this._configElement = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
configElement.hass = this.hass;
|
||||
configElement.addEventListener("config-changed", (ev) =>
|
||||
this._handleUIConfigChanged(ev.detail.config)
|
||||
);
|
||||
this._configElement = configElement;
|
||||
await this.updateComplete;
|
||||
this._updatePreview(conf);
|
||||
return true;
|
||||
}
|
||||
|
||||
private _openedChanged(ev): void {
|
||||
if (!ev.detail.value) {
|
||||
this.closeDialog!();
|
||||
}
|
||||
}
|
||||
|
||||
private get yamlEditor(): HuiYamlEditor {
|
||||
return this.shadowRoot!.querySelector("hui-yaml-editor")!;
|
||||
}
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
:host {
|
||||
--code-mirror-max-height: calc(100vh - 176px);
|
||||
}
|
||||
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
/* overrule the ha-style-dialog max-height on small screens */
|
||||
ha-paper-dialog {
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 660px) {
|
||||
ha-paper-dialog {
|
||||
width: 845px;
|
||||
}
|
||||
}
|
||||
|
||||
ha-paper-dialog {
|
||||
max-width: 845px;
|
||||
}
|
||||
|
||||
.center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 -10px;
|
||||
}
|
||||
.content hui-card-preview {
|
||||
margin-top: 16px;
|
||||
margin: 0 auto;
|
||||
max-width: 390px;
|
||||
}
|
||||
.content .element-editor {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
ha-paper-dialog {
|
||||
max-width: none;
|
||||
width: 1000px;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex-direction: row;
|
||||
}
|
||||
.content > * {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.content hui-card-preview {
|
||||
padding-top: 0;
|
||||
margin: 0 10px;
|
||||
max-width: 490px;
|
||||
}
|
||||
}
|
||||
|
||||
.margin-bot {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
mwc-button paper-spinner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
paper-spinner {
|
||||
display: none;
|
||||
}
|
||||
paper-spinner[active] {
|
||||
display: block;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.element-editor {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.error {
|
||||
color: #ef5350;
|
||||
border-bottom: 1px solid #ef5350;
|
||||
}
|
||||
hui-card-preview {
|
||||
padding-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
}
|
||||
.toggle-button {
|
||||
margin-right: auto;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"hui-edit-card": HuiEditCard;
|
||||
}
|
||||
}
|
@ -30,7 +30,7 @@ export interface CastConfig {
|
||||
type: "cast";
|
||||
icon: string;
|
||||
name: string;
|
||||
view: string;
|
||||
view: string | number;
|
||||
// Hide the row if either unsupported browser or no API available.
|
||||
hide_if_unavailable: boolean;
|
||||
}
|
||||
|
@ -184,6 +184,7 @@ export class HUIView extends LitElement {
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.updated(changedProperties);
|
||||
|
||||
const hass = this.hass!;
|
||||
const lovelace = this.lovelace!;
|
||||
|
||||
if (lovelace.editMode && !editCodeLoaded) {
|
||||
@ -191,6 +192,7 @@ export class HUIView extends LitElement {
|
||||
import(/* webpackChunkName: "hui-view-editable" */ "./hui-view-editable");
|
||||
}
|
||||
|
||||
const hassChanged = changedProperties.has("hass");
|
||||
let editModeChanged = false;
|
||||
let configChanged = false;
|
||||
|
||||
@ -205,21 +207,38 @@ export class HUIView extends LitElement {
|
||||
|
||||
if (configChanged) {
|
||||
this._createBadges(lovelace.config.views[this.index!]);
|
||||
} else if (changedProperties.has("hass")) {
|
||||
} else if (hassChanged) {
|
||||
this._badges.forEach((badge) => {
|
||||
const { element, entityId } = badge;
|
||||
element.hass = this.hass!;
|
||||
element.state = this.hass!.states[entityId];
|
||||
element.hass = hass;
|
||||
element.state = hass.states[entityId];
|
||||
});
|
||||
}
|
||||
|
||||
if (configChanged || editModeChanged || changedProperties.has("columns")) {
|
||||
this._createCards(lovelace.config.views[this.index!]);
|
||||
} else if (changedProperties.has("hass")) {
|
||||
} else if (hassChanged) {
|
||||
this._cards.forEach((element) => {
|
||||
element.hass = this.hass;
|
||||
});
|
||||
}
|
||||
|
||||
const oldHass = changedProperties.get("hass") as this["hass"] | undefined;
|
||||
|
||||
if (
|
||||
configChanged ||
|
||||
editModeChanged ||
|
||||
(hassChanged &&
|
||||
oldHass &&
|
||||
(hass.themes !== oldHass.themes ||
|
||||
hass.selectedTheme !== oldHass.selectedTheme))
|
||||
) {
|
||||
applyThemesOnElement(
|
||||
this,
|
||||
hass.themes,
|
||||
lovelace.config.views[this.index!].theme
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _addCard(): void {
|
||||
@ -311,8 +330,6 @@ export class HUIView extends LitElement {
|
||||
});
|
||||
|
||||
this._cards = elements;
|
||||
|
||||
applyThemesOnElement(root, this.hass!.themes, config.theme);
|
||||
}
|
||||
|
||||
private _rebuildCard(
|
||||
|
@ -23,11 +23,13 @@ class HuiCastRow extends LitElement implements EntityRow {
|
||||
public hass!: HomeAssistant;
|
||||
|
||||
@property() private _config?: CastConfig;
|
||||
|
||||
@property() private _castManager?: CastManager | null;
|
||||
|
||||
@property() private _noHTTPS = false;
|
||||
|
||||
public setConfig(config: CastConfig): void {
|
||||
if (!config || !config.view) {
|
||||
if (!config || config.view === undefined || config.view === null) {
|
||||
throw new Error("Invalid Configuration: 'view' required");
|
||||
}
|
||||
|
||||
@ -66,6 +68,8 @@ class HuiCastRow extends LitElement implements EntityRow {
|
||||
<google-cast-launcher></google-cast-launcher>
|
||||
<mwc-button
|
||||
@click=${this._sendLovelace}
|
||||
.unelevated=${this._castManager.status &&
|
||||
this._config.view === this._castManager.status.lovelacePath}
|
||||
.disabled=${!this._castManager.status}
|
||||
>
|
||||
SHOW
|
||||
@ -120,6 +124,7 @@ class HuiCastRow extends LitElement implements EntityRow {
|
||||
:host {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow: visible;
|
||||
}
|
||||
ha-icon {
|
||||
padding: 8px;
|
||||
@ -127,7 +132,6 @@ class HuiCastRow extends LitElement implements EntityRow {
|
||||
}
|
||||
.flex {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
margin-left: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@ -144,6 +148,7 @@ class HuiCastRow extends LitElement implements EntityRow {
|
||||
align-items: center;
|
||||
}
|
||||
google-cast-launcher {
|
||||
margin-right: 0.57em;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
|
@ -91,7 +91,6 @@ class HaMfaModuleSetupFlow extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
>
|
||||
<ha-markdown
|
||||
content="[[_computeStepDescription(localize, _step)]]"
|
||||
allow-svg
|
||||
></ha-markdown>
|
||||
</template>
|
||||
|
||||
|
@ -1,193 +0,0 @@
|
||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../../components/ha-card";
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../resources/ha-style";
|
||||
|
||||
import { getOptimisticFrontendUserDataCollection } from "../../data/frontend";
|
||||
|
||||
import { EventsMixin } from "../../mixins/events-mixin";
|
||||
import LocalizeMixin from "../../mixins/localize-mixin";
|
||||
|
||||
import "./ha-change-password-card";
|
||||
import "./ha-mfa-modules-card";
|
||||
import "./ha-advanced-mode-card";
|
||||
import "./ha-refresh-tokens-card";
|
||||
import "./ha-long-lived-access-tokens-card";
|
||||
|
||||
import "./ha-pick-language-row";
|
||||
import "./ha-pick-theme-row";
|
||||
import "./ha-push-notifications-row";
|
||||
import "./ha-force-narrow-row";
|
||||
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
||||
* @appliesMixin LocalizeMixin
|
||||
*/
|
||||
class HaPanelProfile extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="ha-style">
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: block;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.content > * {
|
||||
display: block;
|
||||
margin: 24px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<app-header-layout has-scrolling-region>
|
||||
<app-header slot="header" fixed>
|
||||
<app-toolbar>
|
||||
<ha-menu-button
|
||||
hass="[[hass]]"
|
||||
narrow="[[narrow]]"
|
||||
></ha-menu-button>
|
||||
<div main-title>[[localize('panel.profile')]]</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
|
||||
<div class="content">
|
||||
<ha-card header="[[hass.user.name]]">
|
||||
<div class="card-content">
|
||||
[[localize('ui.panel.profile.current_user', 'fullName',
|
||||
hass.user.name)]]
|
||||
<template is="dom-if" if="[[hass.user.is_owner]]"
|
||||
>[[localize('ui.panel.profile.is_owner')]]</template
|
||||
>
|
||||
</div>
|
||||
|
||||
<hello-world hass="[[hass]]"></hello-world>
|
||||
|
||||
<ha-pick-language-row
|
||||
narrow="[[narrow]]"
|
||||
hass="[[hass]]"
|
||||
></ha-pick-language-row>
|
||||
<ha-pick-theme-row
|
||||
narrow="[[narrow]]"
|
||||
hass="[[hass]]"
|
||||
></ha-pick-theme-row>
|
||||
<template
|
||||
is="dom-if"
|
||||
if="[[_showNarrowRow(hass.dockedSidebar, narrow)]]"
|
||||
>
|
||||
<ha-force-narrow-row
|
||||
narrow="[[narrow]]"
|
||||
hass="[[hass]]"
|
||||
></ha-force-narrow-row>
|
||||
</template>
|
||||
<ha-push-notifications-row
|
||||
narrow="[[narrow]]"
|
||||
hass="[[hass]]"
|
||||
></ha-push-notifications-row>
|
||||
|
||||
<div class="card-actions">
|
||||
<mwc-button class="warning" on-click="_handleLogOut"
|
||||
>[[localize('ui.panel.profile.logout')]]</mwc-button
|
||||
>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
<template is="dom-if" if="[[_canChangePassword(hass.user)]]">
|
||||
<ha-change-password-card hass="[[hass]]"></ha-change-password-card>
|
||||
</template>
|
||||
|
||||
<ha-mfa-modules-card
|
||||
hass="[[hass]]"
|
||||
mfa-modules="[[hass.user.mfa_modules]]"
|
||||
></ha-mfa-modules-card>
|
||||
|
||||
<template is="dom-if" if="[[_isAdmin(hass.user)]]">
|
||||
<ha-advanced-mode-card
|
||||
hass="[[hass]]"
|
||||
core-user-data="[[_coreUserData]]"
|
||||
></ha-advanced-mode-card>
|
||||
</template>
|
||||
|
||||
<ha-refresh-tokens-card
|
||||
hass="[[hass]]"
|
||||
refresh-tokens="[[_refreshTokens]]"
|
||||
on-hass-refresh-tokens="_refreshRefreshTokens"
|
||||
></ha-refresh-tokens-card>
|
||||
|
||||
<ha-long-lived-access-tokens-card
|
||||
hass="[[hass]]"
|
||||
refresh-tokens="[[_refreshTokens]]"
|
||||
on-hass-refresh-tokens="_refreshRefreshTokens"
|
||||
></ha-long-lived-access-tokens-card>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: Object,
|
||||
narrow: Boolean,
|
||||
_refreshTokens: Array,
|
||||
_coreUserData: Object,
|
||||
};
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._refreshRefreshTokens();
|
||||
this._unsubCoreData = getOptimisticFrontendUserDataCollection(
|
||||
this.hass.connection,
|
||||
"core"
|
||||
).subscribe((coreUserData) => {
|
||||
this._coreUserData = coreUserData;
|
||||
});
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this._unsubCoreData) {
|
||||
this._unsubCoreData();
|
||||
this._unsubCoreData = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async _refreshRefreshTokens() {
|
||||
this._refreshTokens = await this.hass.callWS({
|
||||
type: "auth/refresh_tokens",
|
||||
});
|
||||
}
|
||||
|
||||
_handleLogOut() {
|
||||
this.fire("hass-logout");
|
||||
}
|
||||
|
||||
_canChangePassword(user) {
|
||||
return user.credentials.some(
|
||||
(cred) => cred.auth_provider_type === "homeassistant"
|
||||
);
|
||||
}
|
||||
|
||||
_isAdmin(user) {
|
||||
return user.is_admin;
|
||||
}
|
||||
|
||||
_showNarrowRow(dockedSidebar, narrow) {
|
||||
return dockedSidebar === "auto" ? !narrow : true;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-panel-profile", HaPanelProfile);
|
200
src/panels/profile/ha-panel-profile.ts
Normal file
200
src/panels/profile/ha-panel-profile.ts
Normal file
@ -0,0 +1,200 @@
|
||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||
import "@polymer/app-layout/app-header/app-header";
|
||||
import "@polymer/paper-item/paper-item-body";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@material/mwc-button";
|
||||
import "@polymer/app-layout/app-toolbar/app-toolbar";
|
||||
|
||||
import "../../components/ha-card";
|
||||
import "../../components/ha-menu-button";
|
||||
import "../../resources/ha-style";
|
||||
|
||||
import {
|
||||
getOptimisticFrontendUserDataCollection,
|
||||
CoreFrontendUserData,
|
||||
} from "../../data/frontend";
|
||||
|
||||
import "./ha-change-password-card";
|
||||
import "./ha-mfa-modules-card";
|
||||
import "./ha-advanced-mode-card";
|
||||
import "./ha-refresh-tokens-card";
|
||||
import "./ha-long-lived-access-tokens-card";
|
||||
|
||||
import "./ha-pick-language-row";
|
||||
import "./ha-pick-theme-row";
|
||||
import "./ha-push-notifications-row";
|
||||
import "./ha-force-narrow-row";
|
||||
import {
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
html,
|
||||
CSSResultArray,
|
||||
css,
|
||||
property,
|
||||
} from "lit-element";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
|
||||
class HaPanelProfile extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public narrow!: boolean;
|
||||
@property() private _refreshTokens?: unknown[];
|
||||
@property() private _coreUserData?: CoreFrontendUserData | null;
|
||||
private _unsubCoreData?: UnsubscribeFunc;
|
||||
|
||||
public connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._refreshRefreshTokens();
|
||||
this._unsubCoreData = getOptimisticFrontendUserDataCollection(
|
||||
this.hass.connection,
|
||||
"core"
|
||||
).subscribe((coreUserData) => {
|
||||
this._coreUserData = coreUserData;
|
||||
});
|
||||
}
|
||||
|
||||
public disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
if (this._unsubCoreData) {
|
||||
this._unsubCoreData();
|
||||
this._unsubCoreData = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
return html`
|
||||
<app-header-layout has-scrolling-region>
|
||||
<app-header slot="header" fixed>
|
||||
<app-toolbar>
|
||||
<ha-menu-button
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
<div main-title>${this.hass.localize("panel.profile")}</div>
|
||||
</app-toolbar>
|
||||
</app-header>
|
||||
|
||||
<div class="content">
|
||||
<ha-card .header=${this.hass.user!.name}>
|
||||
<div class="card-content">
|
||||
${this.hass.localize(
|
||||
"ui.panel.profile.current_user",
|
||||
"fullName",
|
||||
this.hass.user!.name
|
||||
)}
|
||||
${this.hass.user!.is_owner
|
||||
? this.hass.localize("ui.panel.profile.is_owner")
|
||||
: ""}
|
||||
</div>
|
||||
|
||||
<ha-pick-language-row
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
></ha-pick-language-row>
|
||||
<ha-pick-theme-row
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
></ha-pick-theme-row>
|
||||
${this.hass.dockedSidebar !== "auto" || !this.narrow
|
||||
? html`
|
||||
<ha-force-narrow-row
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
></ha-force-narrow-row>
|
||||
`
|
||||
: ""}
|
||||
<ha-push-notifications-row
|
||||
.narrow=${this.narrow}
|
||||
.hass=${this.hass}
|
||||
></ha-push-notifications-row>
|
||||
|
||||
<div class="card-actions">
|
||||
<mwc-button class="warning" @click=${this._handleLogOut}>
|
||||
${this.hass.localize("ui.panel.profile.logout")}
|
||||
</mwc-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
|
||||
${this.hass.user!.credentials.some(
|
||||
(cred) => cred.auth_provider_type === "homeassistant"
|
||||
)
|
||||
? html`
|
||||
<ha-change-password-card
|
||||
.hass=${this.hass}
|
||||
></ha-change-password-card>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<ha-mfa-modules-card
|
||||
.hass=${this.hass}
|
||||
.mfaModules=${this.hass.user!.mfa_modules}
|
||||
></ha-mfa-modules-card>
|
||||
|
||||
${this.hass.user!.is_admin
|
||||
? html`
|
||||
<ha-advanced-mode-card
|
||||
.hass=${this.hass}
|
||||
coreUserData=${this._coreUserData}
|
||||
></ha-advanced-mode-card>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<ha-refresh-tokens-card
|
||||
.hass=${this.hass}
|
||||
.refreshTokens=${this._refreshTokens}
|
||||
@hass-refresh-tokens=${this._refreshRefreshTokens}
|
||||
></ha-refresh-tokens-card>
|
||||
|
||||
<ha-long-lived-access-tokens-card
|
||||
.hass=${this.hass}
|
||||
.refreshTokens=${this._refreshTokens}
|
||||
@hass-refresh-tokens=${this._refreshRefreshTokens}
|
||||
></ha-long-lived-access-tokens-card>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _refreshRefreshTokens() {
|
||||
this._refreshTokens = await this.hass.callWS({
|
||||
type: "auth/refresh_tokens",
|
||||
});
|
||||
}
|
||||
|
||||
private _handleLogOut() {
|
||||
fireEvent(this, "hass-logout");
|
||||
}
|
||||
|
||||
static get styles(): CSSResultArray {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
:host {
|
||||
-ms-user-select: initial;
|
||||
-webkit-user-select: initial;
|
||||
-moz-user-select: initial;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: block;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.content > * {
|
||||
display: block;
|
||||
margin: 24px 0;
|
||||
}
|
||||
|
||||
.promo-advanced {
|
||||
text-align: center;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("ha-panel-profile", HaPanelProfile);
|
@ -620,7 +620,8 @@
|
||||
"core": "Reload core",
|
||||
"group": "Reload groups",
|
||||
"automation": "Reload automations",
|
||||
"script": "Reload scripts"
|
||||
"script": "Reload scripts",
|
||||
"scene": "Reload scenes"
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "Server management",
|
||||
|
@ -624,6 +624,9 @@
|
||||
},
|
||||
"values": {
|
||||
"header": "Knooppuntwaardes"
|
||||
},
|
||||
"node_config": {
|
||||
"set_config_parameter": "Stel Config-parameter in"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -536,7 +536,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "إدارة شبكة Z-Wave"
|
||||
"description": "إدارة شبكة Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "تعيين معلمة التكوين"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "المستخدمين",
|
||||
|
@ -624,6 +624,9 @@
|
||||
},
|
||||
"values": {
|
||||
"header": "Стойности на устройсто"
|
||||
},
|
||||
"node_config": {
|
||||
"set_config_parameter": "Задайте параметър Config"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -273,7 +273,10 @@
|
||||
},
|
||||
"config": {
|
||||
"zwave": {
|
||||
"caption": "Z-Wave"
|
||||
"caption": "Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Podesite parametar Config"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -351,7 +351,7 @@
|
||||
"header": "Konfigurace a správa serveru",
|
||||
"introduction": "Moc dobře víme, že změna konfigurace může být velmi únavným procesem. Tato sekce se proto pokusí udělat váš život alespoň trochu jednodušší.",
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Editori pois käytöstä, koska asetukset on määritelty configuration.yaml:ssä",
|
||||
"edit_requires_storage": "Editor je zakázán, protože konfigurace je uložena v configuration.yaml.",
|
||||
"location_name": "Název instalace Home Assistant",
|
||||
"latitude": "Zeměpisná šířka",
|
||||
"longitude": "Zeměpisná délka",
|
||||
@ -588,7 +588,18 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Spravujte svou síť Z-Wave"
|
||||
"description": "Spravujte svou síť Z-Wave",
|
||||
"services": {
|
||||
"test_network": "Testovat síť",
|
||||
"save_config": "Uložit konfiguraci"
|
||||
},
|
||||
"common": {
|
||||
"unknown": "neznámý"
|
||||
},
|
||||
"node_config": {
|
||||
"seconds": "sekund",
|
||||
"set_config_parameter": "Nastavte konfigurační parametr"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "Uživatelé",
|
||||
@ -711,6 +722,25 @@
|
||||
"device_tracker_picked": "Sledovat zařízení",
|
||||
"device_tracker_pick": "Vyberte zařízení, které chcete sledovat"
|
||||
}
|
||||
},
|
||||
"server_control": {
|
||||
"section": {
|
||||
"validation": {
|
||||
"check_config": "Zkontrolujte konfiguraci",
|
||||
"valid": "Konfigurace je v pořádku!",
|
||||
"invalid": "Konfigurace není v pořádku!"
|
||||
},
|
||||
"reloading": {
|
||||
"heading": "Konfigurace se načítá",
|
||||
"core": "Znovu načíst jádro",
|
||||
"group": "Znovu načíst skupiny",
|
||||
"automation": "Znovu načíst automatizace",
|
||||
"script": "Znovu načíst skripty"
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "Správa serveru"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
|
@ -575,7 +575,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Rheoli eich rhwydwaith Z-Wave"
|
||||
"description": "Rheoli eich rhwydwaith Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Gosod Paramedr Ffurfweddu"
|
||||
}
|
||||
},
|
||||
"cloud": {
|
||||
"description_login": "Wedi mewngofnodi fel {email}",
|
||||
|
@ -320,7 +320,7 @@
|
||||
"title": "Ereignisse"
|
||||
},
|
||||
"templates": {
|
||||
"title": "Templates"
|
||||
"title": "Vorlage"
|
||||
},
|
||||
"mqtt": {
|
||||
"title": "MQTT"
|
||||
@ -349,10 +349,10 @@
|
||||
"introduction": "Hier ist es möglich, deine Komponenten und Home Assistant zu konfigurieren. Noch ist nicht alles über die GUI einstellbar, aber wir arbeiten daran.",
|
||||
"core": {
|
||||
"caption": "Allgemein",
|
||||
"description": "Überprüfe deine Konfigurationsdatei und steuere den Server",
|
||||
"description": "Bearbeite die allgemeine Konfiguration von Home Assistant",
|
||||
"section": {
|
||||
"core": {
|
||||
"header": "Einstellungen und Serversteuerung",
|
||||
"header": "Allgemeine Einstellungen",
|
||||
"introduction": "Die Konfiguration zu ändern ist ein mühsamer Prozess. Das wissen wir. Dieser Bereich versucht dir das Leben etwas leichter zu gestalten.",
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Editor deaktiviert, da die Konfiguration in configuration.yaml gespeichert ist.",
|
||||
@ -602,7 +602,7 @@
|
||||
"network_starting": "Z-Wave Netzwerk wird gestartet ...",
|
||||
"network_starting_note": "Dies kann je nach Größe des Netzwerks eine Weile dauern.",
|
||||
"network_started": "Z-Wave Netzwerk gestartet",
|
||||
"network_started_note_some_queried": "Wache Knoten wurden abgefragt. Schlafende knoten werden abgefragt wenn sie aufwachen.",
|
||||
"network_started_note_some_queried": "Aktive Knoten wurden abgefragt. Schlafende Knoten werden abgefragt, sobald sie aktiv werden.",
|
||||
"network_started_note_all_queried": "Alle Knoten wurden abgefragt."
|
||||
},
|
||||
"services": {
|
||||
@ -620,10 +620,22 @@
|
||||
"common": {
|
||||
"value": "Wert",
|
||||
"instance": "Instanz",
|
||||
"index": "Index"
|
||||
"index": "Index",
|
||||
"unknown": "Unbekannt",
|
||||
"wakeup_interval": "Aufwachintervall"
|
||||
},
|
||||
"values": {
|
||||
"header": "Knotenwerte"
|
||||
},
|
||||
"node_config": {
|
||||
"header": "Knotenkonfiguration",
|
||||
"seconds": "Sekunden",
|
||||
"set_wakeup": "Aufwachintervall einrichten",
|
||||
"config_parameter": "Konfigurationsparameter",
|
||||
"config_value": "Konfigurationswert",
|
||||
"true": "Richtig",
|
||||
"false": "Falsch",
|
||||
"set_config_parameter": "Konfiguration speichern"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
@ -750,18 +762,18 @@
|
||||
},
|
||||
"server_control": {
|
||||
"caption": "Serversteuerung",
|
||||
"description": "Neustarten und Stoppen des Home Assistant-Servers",
|
||||
"description": "Neustarten und Stoppen des Home Assistant Servers",
|
||||
"section": {
|
||||
"validation": {
|
||||
"heading": "Konfiguration überprüfen",
|
||||
"introduction": "Überprüfe deine Konfiguration wenn du kürzlich Änderungen vorgenommen hast und sicherstellen möchtest, dass alle Änderungen gültig sind",
|
||||
"introduction": "Überprüfe deine Konfiguration, wenn du kürzlich Änderungen vorgenommen hast und sicherstellen möchtest, dass alle Änderungen gültig sind",
|
||||
"check_config": "Konfiguration prüfen",
|
||||
"valid": "Konfiguration in Ordnung",
|
||||
"invalid": "Konfiguration ungültig"
|
||||
},
|
||||
"reloading": {
|
||||
"heading": "Konfiguration neu laden",
|
||||
"introduction": "Einige Komponenten von Home Assistant können ohne einen Neustart neu geladen werden. \"Neu laden\" entlädt dabei die aktuelle Konfiguration und lädt die Neue.",
|
||||
"introduction": "Einige Komponenten von Home Assistant können ohne einen Neustart neu geladen werden. \"Neu laden\" entlädt dabei die aktuelle Konfiguration und lädt die neue Konfiguration.",
|
||||
"core": "Hauptsystem neu laden",
|
||||
"group": "Gruppen neu laden",
|
||||
"automation": "Automatisierungen neu laden",
|
||||
@ -1358,7 +1370,7 @@
|
||||
"away": "Abwesend",
|
||||
"boost": "Maximal",
|
||||
"comfort": "Komfort",
|
||||
"home": "Zuhause",
|
||||
"home": "Zu Hause",
|
||||
"sleep": "Schlafen",
|
||||
"activity": "Aktivität"
|
||||
},
|
||||
|
@ -588,7 +588,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Διαχειριστείτε το δίκτυο Z-Wave"
|
||||
"description": "Διαχειριστείτε το δίκτυο Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Ορίστε την παράμετρο διαμόρφωσης"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "Χρήστες",
|
||||
|
@ -588,7 +588,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "",
|
||||
"description": "Administrar su red Z-Wave"
|
||||
"description": "Administrar su red Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Establecer parámetro de configuración"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "Usuarios",
|
||||
|
@ -349,7 +349,7 @@
|
||||
"introduction": "Aquí puedes configurar tus componentes y Home Assistant. Aún no es posible configurar todo desde la interfaz de usuario, pero estamos trabajando en ello.",
|
||||
"core": {
|
||||
"caption": "Configuración general",
|
||||
"description": "Valida tu archivo de configuración y controla el servidor",
|
||||
"description": "Cambiar la configuración general de Home Assistant",
|
||||
"section": {
|
||||
"core": {
|
||||
"header": "Configuración y control del servidor",
|
||||
@ -620,10 +620,22 @@
|
||||
"common": {
|
||||
"value": "Valor",
|
||||
"instance": "Instancia",
|
||||
"index": "Índice"
|
||||
"index": "Índice",
|
||||
"unknown": "Desconocido",
|
||||
"wakeup_interval": "Intervalo de activación"
|
||||
},
|
||||
"values": {
|
||||
"header": "Valores del nodo"
|
||||
},
|
||||
"node_config": {
|
||||
"header": "Opciones de configuración de nodo",
|
||||
"seconds": "segundos",
|
||||
"set_wakeup": "Configurar el intervalo de activación",
|
||||
"config_parameter": "Parámetro de configuración",
|
||||
"config_value": "Valor de configuración",
|
||||
"true": "Cierto",
|
||||
"false": "Falso",
|
||||
"set_config_parameter": "Establecer parámetro de configuración"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -583,7 +583,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Halda oma Z-Wave võrku"
|
||||
"description": "Halda oma Z-Wave võrku",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Seadista parameeter Config"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "Kasutajad",
|
||||
|
@ -299,7 +299,10 @@
|
||||
"description": "Scriptak sortu eta editatu"
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave"
|
||||
"caption": "Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Ezarri konfigurazio-parametroa"
|
||||
}
|
||||
},
|
||||
"automation": {
|
||||
"picker": {
|
||||
|
@ -522,7 +522,10 @@
|
||||
}
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave"
|
||||
"caption": "Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "تنظیم پارامتر تنظیم کنید"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "کاربران",
|
||||
|
@ -594,6 +594,9 @@
|
||||
"test_network": "Testaa verkkoyhteys",
|
||||
"save_config": "Tallenna asetukset",
|
||||
"cancel_command": "Peruuta komento"
|
||||
},
|
||||
"node_config": {
|
||||
"set_config_parameter": "Aseta asetusparametri"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
@ -1263,7 +1266,7 @@
|
||||
"notify": "Ilmoita",
|
||||
"plant": "Kasvi",
|
||||
"proximity": "Läheisyys",
|
||||
"remote": "Etä",
|
||||
"remote": "Kauko-ohjaus",
|
||||
"scene": "Skene",
|
||||
"script": "Skripti",
|
||||
"sensor": "Sensori",
|
||||
|
@ -160,7 +160,7 @@
|
||||
"not_home": "Absent"
|
||||
},
|
||||
"fan": {
|
||||
"off": "Arrêt",
|
||||
"off": "Éteint",
|
||||
"on": "Marche"
|
||||
},
|
||||
"group": {
|
||||
@ -179,7 +179,7 @@
|
||||
"problem": "Problème"
|
||||
},
|
||||
"input_boolean": {
|
||||
"off": "Arrêt",
|
||||
"off": "Arrêté",
|
||||
"on": "Marche"
|
||||
},
|
||||
"light": {
|
||||
@ -191,7 +191,7 @@
|
||||
"unlocked": "Déverrouillé"
|
||||
},
|
||||
"media_player": {
|
||||
"off": "Arrêt",
|
||||
"off": "Éteint",
|
||||
"on": "Marche",
|
||||
"playing": "Lecture en cours",
|
||||
"paused": "En pause",
|
||||
@ -594,13 +594,15 @@
|
||||
"caption": "Z-Wave",
|
||||
"description": "Gérez votre réseau Z-Wave",
|
||||
"network_management": {
|
||||
"header": "Gestion de réseau Z-Wave"
|
||||
"header": "Gestion de réseau Z-Wave",
|
||||
"introduction": "Exécutez les commandes qui affectent le réseau Z-Wave. Vous ne saurez pas si la plupart des commandes ont réussi, mais vous pouvez consulter le journal OZW pour essayer de le savoir."
|
||||
},
|
||||
"network_status": {
|
||||
"network_stopped": "Réseau Z-Wave arrêté",
|
||||
"network_starting": "Démarrage du réseau Z-Wave...",
|
||||
"network_starting_note": "Ceci peut prendre un certain temps en fonction du débit de votre réseau.",
|
||||
"network_started": "Le réseau Z-Wave a été démarré",
|
||||
"network_started_note_some_queried": "Les nœuds éveillés ont été interrogés. Les nœuds en veille seront interrogés à leur réveil.",
|
||||
"network_started_note_all_queried": "Tous les nœuds ont été interrogés."
|
||||
},
|
||||
"services": {
|
||||
@ -608,6 +610,7 @@
|
||||
"stop_network": "Arrêter le réseau",
|
||||
"heal_network": "Soigner le réseau",
|
||||
"test_network": "Tester le réseau",
|
||||
"soft_reset": "Redémarrage",
|
||||
"save_config": "Enregistrer la configuration",
|
||||
"add_node_secure": "Ajouter un nœud sécurisé",
|
||||
"add_node": "Ajouter un nœud",
|
||||
@ -617,10 +620,22 @@
|
||||
"common": {
|
||||
"value": "Valeur",
|
||||
"instance": "Instance",
|
||||
"index": "Indice"
|
||||
"index": "Indice",
|
||||
"unknown": "Inconnu",
|
||||
"wakeup_interval": "Intervalle de réveil"
|
||||
},
|
||||
"values": {
|
||||
"header": "Valeurs des nœuds"
|
||||
},
|
||||
"node_config": {
|
||||
"header": "Options de configuration du nœud",
|
||||
"seconds": "Secondes",
|
||||
"set_wakeup": "Définir l'intervalle de réveil",
|
||||
"config_parameter": "Paramètre de configuration",
|
||||
"config_value": "Valeur de configuration",
|
||||
"true": "Vrai",
|
||||
"false": "Faux",
|
||||
"set_config_parameter": "Définir le paramètre de configuration"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
@ -746,15 +761,19 @@
|
||||
}
|
||||
},
|
||||
"server_control": {
|
||||
"caption": "Contrôle du serveur",
|
||||
"description": "Redémarrez et arrêtez le serveur Home Assistant",
|
||||
"section": {
|
||||
"validation": {
|
||||
"heading": "Validation de la configuration",
|
||||
"introduction": "Validez votre configuration si vous l'avez récemment modifiée et que vous voulez vous assurer que tout est valide",
|
||||
"check_config": "Vérifiez la configuration",
|
||||
"valid": "Configuration valide !",
|
||||
"invalid": "Configuration invalide"
|
||||
},
|
||||
"reloading": {
|
||||
"heading": "Rechargement de la configuration",
|
||||
"introduction": "Certaines parties de Home Assistant peuvent être rechargées sans nécessiter un redémarrage. Appuyer sur \"recharger\" déchargera leur configuration actuelle et chargera la nouvelle.",
|
||||
"core": "Recharger le noyau",
|
||||
"group": "Recharger les groupes",
|
||||
"automation": "Recharger les automatisations",
|
||||
@ -762,6 +781,7 @@
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "Gestion du serveur",
|
||||
"introduction": "Contrôlez votre serveur Home Assistant ... depuis Home Assistant.",
|
||||
"restart": "Redémarrer",
|
||||
"stop": "Arrêter"
|
||||
}
|
||||
@ -839,7 +859,8 @@
|
||||
},
|
||||
"logout": "Déconnexion",
|
||||
"force_narrow": {
|
||||
"header": "Toujours cacher la barre latérale"
|
||||
"header": "Toujours cacher la barre latérale",
|
||||
"description": "Cela masquera la barre latérale par défaut, semblable à l'expérience mobile."
|
||||
}
|
||||
},
|
||||
"page-authorize": {
|
||||
@ -1257,7 +1278,7 @@
|
||||
"dialogs": {
|
||||
"more_info_settings": {
|
||||
"save": "Sauvegarder",
|
||||
"name": "Surcharge du nom",
|
||||
"name": "Outrepasser le nom",
|
||||
"entity_id": "ID de l'entité"
|
||||
},
|
||||
"more_info_control": {
|
||||
|
@ -141,7 +141,8 @@
|
||||
"high_demand": "ביקוש גבוה",
|
||||
"heat_pump": "משאבת חום",
|
||||
"gas": "גז",
|
||||
"manual": "מדריך ל"
|
||||
"manual": "מדריך ל",
|
||||
"heat_cool": "חימום\/קירור"
|
||||
},
|
||||
"configurator": {
|
||||
"configure": "הגדר",
|
||||
@ -323,6 +324,9 @@
|
||||
},
|
||||
"mqtt": {
|
||||
"title": "MQTT"
|
||||
},
|
||||
"info": {
|
||||
"title": "מידע"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -588,7 +592,51 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "נהל את רשת ה- Z-Wave שלך"
|
||||
"description": "נהל את רשת ה- Z-Wave שלך",
|
||||
"network_management": {
|
||||
"header": "ניהול רשת ה Z-Wave",
|
||||
"introduction": "הפעל פקודות המשפיעות על רשת ה-Z Wave. לא תקבל משוב אם רוב הפקודות הצליחו, אך באפשרותך לבדוק את יומן ה-OZW כדי לנסות לגלות."
|
||||
},
|
||||
"network_status": {
|
||||
"network_stopped": "רשת ה Z-Wave נעצרה",
|
||||
"network_starting": "מפעיל את רשת ה Z-Wave",
|
||||
"network_starting_note": "הדבר עשוי להימשך זמן מה בהתאם לגודל הרשת.",
|
||||
"network_started": "רשת ה Z-Wave הופעלה",
|
||||
"network_started_note_some_queried": "רכיבים דלוקים נבדקו. רכיבים כבויים יבדקו כאשר ידלקו.",
|
||||
"network_started_note_all_queried": "כל הרכיבים נבדקו"
|
||||
},
|
||||
"services": {
|
||||
"start_network": "הפעל רשת",
|
||||
"stop_network": "עצור רשת",
|
||||
"heal_network": "ריפוי רשת",
|
||||
"test_network": "בדיקת רשת",
|
||||
"soft_reset": "איפוס רך",
|
||||
"save_config": "שמור קונפיגורציה",
|
||||
"add_node_secure": "הוסף רכיב מאובטח",
|
||||
"add_node": "הוסף רכיב",
|
||||
"remove_node": "הסר רכיב",
|
||||
"cancel_command": "ביטול פקודה"
|
||||
},
|
||||
"common": {
|
||||
"value": "ערך תצורה",
|
||||
"instance": "פרמטר",
|
||||
"index": "אינדקס",
|
||||
"unknown": "לא ידוע",
|
||||
"wakeup_interval": "מרווח פעולה"
|
||||
},
|
||||
"values": {
|
||||
"header": "ערכים"
|
||||
},
|
||||
"node_config": {
|
||||
"header": "עריכת קונפיגורציה",
|
||||
"seconds": "שניות",
|
||||
"set_wakeup": "בחירת מרווח פעולה",
|
||||
"config_parameter": "פרמטר תצורה",
|
||||
"config_value": "ערך תצורה",
|
||||
"true": "True",
|
||||
"false": "False",
|
||||
"set_config_parameter": "הגדר פרמטר Config"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "משתמשים",
|
||||
@ -667,7 +715,7 @@
|
||||
},
|
||||
"area_registry": {
|
||||
"caption": "מאגר האזורים",
|
||||
"description": "סקירה של כל האזורים בביתך.",
|
||||
"description": "סקירה של כל האזורים בביתך",
|
||||
"picker": {
|
||||
"header": "מאגר האזורים",
|
||||
"introduction": "אזורים משמשים לארגון המיקום של ההתקנים. Home Assistant יעשה שימוש במידע זה בכדי לסייע לך בארגון הממשק, ההרשאות והאינטגרציות שלך עם מערכות אחרות.",
|
||||
@ -711,6 +759,33 @@
|
||||
"device_tracker_picked": "עקוב אחר מכשיר",
|
||||
"device_tracker_pick": "בחר מכשיר למעקב"
|
||||
}
|
||||
},
|
||||
"server_control": {
|
||||
"caption": "בקרת שרת",
|
||||
"description": "שליטה על שרת ה Home Assistant",
|
||||
"section": {
|
||||
"validation": {
|
||||
"heading": "בדיקת הקונפיגורציה",
|
||||
"introduction": "אמת את הקונפיגורציה שלך אם ביצעת לאחרונה שינויים מסוימים וברצונך לוודא שהיא תקינה",
|
||||
"check_config": "בדיקת הקונפיגורציה",
|
||||
"valid": "קונפיגורציה תקינה!",
|
||||
"invalid": "קונפיגורציה לא תקנית"
|
||||
},
|
||||
"reloading": {
|
||||
"heading": "טען מחדש קונפיגורציה",
|
||||
"introduction": "חלקים מסוימים של Home Assistant יכולים להטען מחדש ללא צורך בהפעלה מחדש.\nטעינה מחדש תשנה את הקונפיגורציה הנוכחית ותטען קונפיגורציה חדשה.",
|
||||
"core": "טען מחדש את הליבה",
|
||||
"group": "טען מחדש קבוצות",
|
||||
"automation": "טען מחדש אוטומציות",
|
||||
"script": "טען מחדש סקריפטים"
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "ניהול שרת",
|
||||
"introduction": "לשלוט על שרת הHome Assistant שלך... מHome Assistant.",
|
||||
"restart": "אתחול",
|
||||
"stop": "עצור"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
@ -781,6 +856,11 @@
|
||||
"step_done": "הגדרה בוצעה עבור {step}",
|
||||
"close": "סגור",
|
||||
"submit": "שלח"
|
||||
},
|
||||
"logout": "התנתק",
|
||||
"force_narrow": {
|
||||
"header": "הסתר תמיד את הסרגל הצדדי",
|
||||
"description": "פעולה זו תסתיר את הסרגל הצדדי כברירת מחדל, בדומה לחוויה בנייד."
|
||||
}
|
||||
},
|
||||
"page-authorize": {
|
||||
@ -1137,7 +1217,8 @@
|
||||
"fan_mode": "מצב מאורר",
|
||||
"swing_mode": "מצב נדנוד",
|
||||
"away_mode": "מצב מחוץ לבית",
|
||||
"aux_heat": "מסייע חום"
|
||||
"aux_heat": "מסייע חום",
|
||||
"preset_mode": "מצב מוגדר"
|
||||
},
|
||||
"lock": {
|
||||
"code": "קוד",
|
||||
@ -1282,6 +1363,24 @@
|
||||
"off": "כבוי",
|
||||
"on": "דולק",
|
||||
"auto": "Auto"
|
||||
},
|
||||
"preset_mode": {
|
||||
"none": "לא נבחר",
|
||||
"eco": "חסכון",
|
||||
"away": "לא בבית",
|
||||
"boost": "מוגבר",
|
||||
"comfort": "נוחות",
|
||||
"home": "בבית",
|
||||
"sleep": "שינה",
|
||||
"activity": "פעילות"
|
||||
},
|
||||
"hvac_action": {
|
||||
"off": "כבוי",
|
||||
"heating": "חימום",
|
||||
"cooling": "קירור",
|
||||
"drying": "מייבש",
|
||||
"idle": "כבוי",
|
||||
"fan": "מאוורר"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -132,7 +132,10 @@
|
||||
},
|
||||
"config": {
|
||||
"zwave": {
|
||||
"caption": "Z-Wave"
|
||||
"caption": "Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "कॉन्फ़िगरेशन पैरामीटर सेट करें"
|
||||
}
|
||||
},
|
||||
"automation": {
|
||||
"editor": {
|
||||
|
@ -515,7 +515,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Upravljajte mrežom Z-Wave"
|
||||
"description": "Upravljajte mrežom Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Postavite parametar Config"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "Korisnici",
|
||||
|
@ -562,7 +562,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Kelola jaringan Z-Wave anda"
|
||||
"description": "Kelola jaringan Z-Wave anda",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Setel Parameter Konfigurasi"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "Pengguna",
|
||||
|
@ -562,7 +562,8 @@
|
||||
"cancel_command": "Hætta við skipun"
|
||||
},
|
||||
"node_config": {
|
||||
"seconds": "sekúndur"
|
||||
"seconds": "sekúndur",
|
||||
"set_config_parameter": "Stilltu Config Parameter"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -324,6 +324,9 @@
|
||||
},
|
||||
"mqtt": {
|
||||
"title": "MQTT"
|
||||
},
|
||||
"info": {
|
||||
"title": "Info"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -590,9 +593,49 @@
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Gestisci la tua rete Z-Wave",
|
||||
"network_management": {
|
||||
"header": "Gestione della rete Z-Wave",
|
||||
"introduction": "Eseguire comandi che interessano la rete Z-Wave. Non otterrete un feedback sul successo della maggior parte dei comandi, ma potete controllare il Log OZW per cercare di scoprirlo."
|
||||
},
|
||||
"network_status": {
|
||||
"network_stopped": "Rete Z-Wave arrestata",
|
||||
"network_starting": "Inizializzazione della rete Z-Wave...",
|
||||
"network_started": "Rete Z-Wave inizializzata"
|
||||
"network_starting_note": "Questo potrebbe richiedere del tempo a seconda delle dimensioni della tua rete.",
|
||||
"network_started": "Rete Z-Wave inizializzata",
|
||||
"network_started_note_some_queried": "Sono stati interrogati i nodi attivi. I nodi dormienti verranno interrogati quando si attiveranno.",
|
||||
"network_started_note_all_queried": "Tutti i nodi sono stati interrogati."
|
||||
},
|
||||
"services": {
|
||||
"start_network": "Avvia la rete",
|
||||
"stop_network": "Ferma la rete",
|
||||
"heal_network": "Testa la rete",
|
||||
"test_network": "Testa la rete",
|
||||
"soft_reset": "Soft Reset",
|
||||
"save_config": "Salva configurazione",
|
||||
"add_node_secure": "Aggiungi nodo sicuro",
|
||||
"add_node": "Aggiungi Nodo",
|
||||
"remove_node": "Rimuovi nodo",
|
||||
"cancel_command": "Annulla Comando"
|
||||
},
|
||||
"common": {
|
||||
"value": "Valore",
|
||||
"instance": "Esempio",
|
||||
"index": "Indice",
|
||||
"unknown": "sconosciuto",
|
||||
"wakeup_interval": "Intervallo di sveglia"
|
||||
},
|
||||
"values": {
|
||||
"header": "Valori del nodo"
|
||||
},
|
||||
"node_config": {
|
||||
"header": "Opzioni di configurazione del nodo",
|
||||
"seconds": "secondi",
|
||||
"set_wakeup": "Imposta intervallo di riattivazione",
|
||||
"config_parameter": "Parametro di configurazione",
|
||||
"config_value": "Valore di configurazione",
|
||||
"true": "Vero",
|
||||
"false": "False",
|
||||
"set_config_parameter": "Imposta parametro di configurazione"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
@ -716,6 +759,33 @@
|
||||
"device_tracker_picked": "Traccia dispositivo",
|
||||
"device_tracker_pick": "Scegli il dispositivo da tracciare"
|
||||
}
|
||||
},
|
||||
"server_control": {
|
||||
"caption": "Gestione del server",
|
||||
"description": "Riavviare e arrestare il server Home Assistant",
|
||||
"section": {
|
||||
"validation": {
|
||||
"heading": "Convalida della configurazione",
|
||||
"introduction": "Convalidare la configurazione se di recente sono state apportate alcune modifiche alla configurazione e si desidera assicurarsi che sia tutto valido",
|
||||
"check_config": "Controlla la configurazione",
|
||||
"valid": "Configurazione valida!",
|
||||
"invalid": "Configurazione non valida"
|
||||
},
|
||||
"reloading": {
|
||||
"heading": "Ricaricamento della configurazione",
|
||||
"introduction": "Alcune parti di Home Assistant possono essere ricaricate senza richiedere il riavvio. Cliccando su ricarica si rimuoverà la loro configurazione corrente e si caricherà quella nuova.",
|
||||
"core": "Ricarica core",
|
||||
"group": "Ricarica i gruppi",
|
||||
"automation": "Ricarica automazioni",
|
||||
"script": "Ricarica script"
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "Gestione del server",
|
||||
"introduction": "Controllare il server Home Assistant... da Home Assistant.",
|
||||
"restart": "Riavviare",
|
||||
"stop": "Stop"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
@ -1147,7 +1217,8 @@
|
||||
"fan_mode": "Ventilazione",
|
||||
"swing_mode": "Modo oscillazione",
|
||||
"away_mode": "Modalità Assente",
|
||||
"aux_heat": "Riscaldamento ausiliario"
|
||||
"aux_heat": "Riscaldamento ausiliario",
|
||||
"preset_mode": "Preset"
|
||||
},
|
||||
"lock": {
|
||||
"code": "Codice",
|
||||
@ -1296,6 +1367,8 @@
|
||||
"preset_mode": {
|
||||
"none": "Nessuna",
|
||||
"eco": "Eco",
|
||||
"away": "Fuori Casa",
|
||||
"boost": "Boost",
|
||||
"comfort": "Comfort",
|
||||
"home": "Casa",
|
||||
"sleep": "Sleep",
|
||||
@ -1303,6 +1376,7 @@
|
||||
},
|
||||
"hvac_action": {
|
||||
"off": "Spento",
|
||||
"heating": "Riscaldamento",
|
||||
"cooling": "Raffreddamento",
|
||||
"drying": "Asciugatura",
|
||||
"idle": "Inattivo",
|
||||
|
@ -322,7 +322,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Z-waveネットワークを管理します"
|
||||
"description": "Z-waveネットワークを管理します",
|
||||
"node_config": {
|
||||
"set_config_parameter": "構成パラメーターの設定"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "ユーザー",
|
||||
|
@ -602,7 +602,7 @@
|
||||
"network_starting": "Z-Wave 네트워크 시작 중...",
|
||||
"network_starting_note": "네트워크 규모에 따라 다소 시간이 걸릴 수 있습니다.",
|
||||
"network_started": "Z-Wave 네트워크 시작됨",
|
||||
"network_started_note_some_queried": "절전 모드 해제 노드가 쿼리되었습니다. 절전 노드는 절전 모드 해제 시 쿼리됩니다.",
|
||||
"network_started_note_some_queried": "절전모드 해제 노드가 쿼리되었습니다. 절전모드 노드는 절전모드 해제 상태일 때 쿼리됩니다.",
|
||||
"network_started_note_all_queried": "모든 노드가 쿼리되었습니다."
|
||||
},
|
||||
"services": {
|
||||
@ -621,8 +621,8 @@
|
||||
"value": "값",
|
||||
"instance": "인스턴스",
|
||||
"index": "색인",
|
||||
"unknown": "알 수 없슴",
|
||||
"wakeup_interval": "절전 모드 해제 간격"
|
||||
"unknown": "알 수 없음",
|
||||
"wakeup_interval": "절전모드 해제 간격"
|
||||
},
|
||||
"values": {
|
||||
"header": "노드 값"
|
||||
@ -630,7 +630,7 @@
|
||||
"node_config": {
|
||||
"header": "노드 구성 옵션",
|
||||
"seconds": "초",
|
||||
"set_wakeup": "절전 모드 해제 간격 설정",
|
||||
"set_wakeup": "절전모드 해제 간격 설정",
|
||||
"config_parameter": "구성 파라메터",
|
||||
"config_value": "구성 값",
|
||||
"true": "참",
|
||||
|
@ -620,10 +620,22 @@
|
||||
"common": {
|
||||
"value": "Wäert",
|
||||
"instance": "Instanz",
|
||||
"index": "Index"
|
||||
"index": "Index",
|
||||
"unknown": "Onbekannt",
|
||||
"wakeup_interval": "Intervall fir z'erwächen"
|
||||
},
|
||||
"values": {
|
||||
"header": "Wäerter vum Apparat"
|
||||
},
|
||||
"node_config": {
|
||||
"header": "Node Konfiguratioun Optiounen",
|
||||
"seconds": "Sekonnen",
|
||||
"set_wakeup": "Intervall fir z'erwächen définéieren",
|
||||
"config_parameter": "Konfiguratioun's Parameter",
|
||||
"config_value": "Konfiguratioun's Wäert",
|
||||
"true": "Richteg",
|
||||
"false": "Falsch",
|
||||
"set_config_parameter": "Konfiguratioun's Parameter setzen"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
@ -747,6 +759,33 @@
|
||||
"device_tracker_picked": "Aparat suivéieren",
|
||||
"device_tracker_pick": "Wielt den Apparat aus fir ze suivéieren"
|
||||
}
|
||||
},
|
||||
"server_control": {
|
||||
"caption": "Kontroll vum Server",
|
||||
"description": "Start an Stop vum Home Assistant Server",
|
||||
"section": {
|
||||
"validation": {
|
||||
"heading": "Validatioun vun der Konfiguratioun",
|
||||
"introduction": "Validéiert är Konfiguratioun wann Dir viru kuerzem e puer Ännerungen an ärer Konfiguratioun gemaacht hutt a wëllt sécher sinn datt alles gëlteg ass",
|
||||
"check_config": "Konfiguratioun iwwerpréiwen",
|
||||
"valid": "Konfiguratioun gëlteg!",
|
||||
"invalid": "Konfiguratioun ongëlteg"
|
||||
},
|
||||
"reloading": {
|
||||
"heading": "Konfiguratioun gëtt frësch gelueden",
|
||||
"introduction": "E puer Deeler vum Home Assistant kënne frësch geluede ginn ouni datt een Neistart néideg ass. Klick op nei luede fir di aktuell Konfiguratioun z'entlueden an di nei Konfiguratioun ze lueden.",
|
||||
"core": "Kär néi lueden",
|
||||
"group": "Gruppe nei lueden",
|
||||
"automation": "Automatisme nei lueden",
|
||||
"script": "Skripte nei lueden"
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "Serververwaltung",
|
||||
"introduction": "Kontrolléiert ären Home Assistant Server ... vun Home Assistant aus.",
|
||||
"restart": "Neistart",
|
||||
"stop": "Stop"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
|
@ -168,7 +168,10 @@
|
||||
},
|
||||
"config": {
|
||||
"zwave": {
|
||||
"caption": "Z-Wave"
|
||||
"caption": "Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Nustatykite parametrą „Config“"
|
||||
}
|
||||
},
|
||||
"automation": {
|
||||
"editor": {
|
||||
|
@ -562,6 +562,9 @@
|
||||
"description": "Pārvaldiet Z-Wave tīklu",
|
||||
"services": {
|
||||
"save_config": "Saglabāt konfigurāciju"
|
||||
},
|
||||
"node_config": {
|
||||
"set_config_parameter": "Iestatīt konfigurācijas parametru"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -587,7 +587,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Administrer Z-Wave-nettverket ditt"
|
||||
"description": "Administrer Z-Wave-nettverket ditt",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Angi konfigurasjonsparameter"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "Brukarar",
|
||||
@ -974,8 +977,8 @@
|
||||
"entity_non_numeric": "Oppføringa er ikkje numerisk: {entity}"
|
||||
},
|
||||
"changed_toast": {
|
||||
"message": "Lovelace konfigurasjonen blei oppdatert, ønskjer du å oppfriske?",
|
||||
"refresh": "Oppfrisk"
|
||||
"message": "Lovelace konfigurasjonen vart oppdatert. Ønsker du å friske opp?",
|
||||
"refresh": "Friske opp"
|
||||
},
|
||||
"reload_lovelace": "Omlast Lovelace"
|
||||
},
|
||||
|
@ -624,6 +624,9 @@
|
||||
},
|
||||
"values": {
|
||||
"header": "Valores de nó"
|
||||
},
|
||||
"node_config": {
|
||||
"set_config_parameter": "Definir o parâmetro de configuração"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -588,7 +588,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Gerir a sua rede Z-Wave"
|
||||
"description": "Gerir a sua rede Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Definir o parâmetro de configuração"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "Utilizadores",
|
||||
|
@ -624,6 +624,9 @@
|
||||
},
|
||||
"values": {
|
||||
"header": "Valoare nod"
|
||||
},
|
||||
"node_config": {
|
||||
"set_config_parameter": "Setați parametrul de configurare"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -397,7 +397,7 @@
|
||||
},
|
||||
"customize": {
|
||||
"caption": "Кастомизация",
|
||||
"description": "Настраивайте Ваши объекты",
|
||||
"description": "Настраивайте атрибуты объектов",
|
||||
"picker": {
|
||||
"header": "Кастомизация",
|
||||
"introduction": "Настройка атрибутов объектов. Добавленные или изменённые настройки сразу же вступают в силу. Удаленные настройки вступят в силу после обновления объекта."
|
||||
@ -761,12 +761,12 @@
|
||||
}
|
||||
},
|
||||
"server_control": {
|
||||
"caption": "Управление сервером",
|
||||
"description": "Перезапуск или остановка Home Assistant",
|
||||
"caption": "Сервер",
|
||||
"description": "Управляйте сервером Home Assistant",
|
||||
"section": {
|
||||
"validation": {
|
||||
"heading": "Проверка конфигурации",
|
||||
"introduction": "Проверьте файлы конфигурации, если Вы внесли в них изменения",
|
||||
"introduction": "Проверьте файлы конфигурации, если Вы внесли в них изменения.",
|
||||
"check_config": "Начать проверку",
|
||||
"valid": "Конфигурация выполнена верно",
|
||||
"invalid": "Ошибка в конфигурации"
|
||||
@ -781,7 +781,7 @@
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "Управление сервером",
|
||||
"introduction": "Управляйте Вашим сервером Home Assistant... из Home Assistant",
|
||||
"introduction": "Управляйте Вашим сервером Home Assistant... из Home Assistant.",
|
||||
"restart": "Перезапустить",
|
||||
"stop": "Остановить"
|
||||
}
|
||||
|
@ -588,7 +588,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Spravujte svoju Z-Wave sieť"
|
||||
"description": "Spravujte svoju Z-Wave sieť",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Nastavte konfiguračný parameter"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "Používatelia",
|
||||
|
@ -624,6 +624,9 @@
|
||||
},
|
||||
"values": {
|
||||
"header": "Vrednosti vozlišča"
|
||||
},
|
||||
"node_config": {
|
||||
"set_config_parameter": "Nastavite Config Parameter"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -22,7 +22,10 @@
|
||||
},
|
||||
"config": {
|
||||
"zwave": {
|
||||
"caption": "Z-Wave"
|
||||
"caption": "Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Подесите параметар Цонфиг"
|
||||
}
|
||||
},
|
||||
"automation": {
|
||||
"editor": {
|
||||
|
@ -63,7 +63,10 @@
|
||||
},
|
||||
"config": {
|
||||
"zwave": {
|
||||
"caption": "Z-Wave"
|
||||
"caption": "Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Подесите параметар Цонфиг"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"editor": {
|
||||
|
@ -597,22 +597,41 @@
|
||||
"network_stopped": "Z-Wave-nätverk stoppat",
|
||||
"network_starting": "Startar Z-Wave-nätverk...",
|
||||
"network_starting_note": "Detta kan ta ett tag beroende på storleken på ditt nätverk.",
|
||||
"network_started": "Z-Wave nätverk startat"
|
||||
"network_started": "Z-Wave nätverk startat",
|
||||
"network_started_note_some_queried": "Vakna noder har frågats. Vilande noder frågas när de vaknar.",
|
||||
"network_started_note_all_queried": "Alla noder har frågats."
|
||||
},
|
||||
"services": {
|
||||
"start_network": "Starta Nätverket",
|
||||
"stop_network": "Stoppa nätverket",
|
||||
"heal_network": "Reparera nätverk",
|
||||
"test_network": "Testa nätverk",
|
||||
"soft_reset": "Mjuk återställning",
|
||||
"save_config": "Spara konfiguration",
|
||||
"add_node_secure": "Lägg till nod med säkerhet",
|
||||
"add_node": "Lägg till nod",
|
||||
"remove_node": "Ta bort nod",
|
||||
"cancel_command": "Avbryt kommandot"
|
||||
},
|
||||
"common": {
|
||||
"value": "Värde",
|
||||
"instance": "Instans",
|
||||
"index": "Index"
|
||||
"index": "Index",
|
||||
"unknown": "okänt",
|
||||
"wakeup_interval": "Uppvakningsintervall"
|
||||
},
|
||||
"values": {
|
||||
"header": "Nodvärden"
|
||||
},
|
||||
"node_config": {
|
||||
"header": "Alternativ för nodkonfiguration",
|
||||
"seconds": "Sekunder",
|
||||
"set_wakeup": "Ställ in uppvakningsintervall",
|
||||
"config_parameter": "Konfigurera parameter",
|
||||
"config_value": "Konfigurera värde",
|
||||
"true": "Sant",
|
||||
"false": "Falskt"
|
||||
"false": "Falskt",
|
||||
"set_config_parameter": "Ställ in konfigurations parameter"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
@ -738,11 +757,26 @@
|
||||
}
|
||||
},
|
||||
"server_control": {
|
||||
"caption": "Serverhantering",
|
||||
"description": "Starta om och stoppa Home Assistant-servern",
|
||||
"section": {
|
||||
"validation": {
|
||||
"heading": "Validering av konfiguration",
|
||||
"introduction": "Kontrollera din konfiguration om du nyligen gjort ändringar och du vill säkerställa att den är giltig",
|
||||
"check_config": "Kontrollera konfigurationen",
|
||||
"valid": "Konfigurationen är giltig!",
|
||||
"invalid": "Konfigurationen är inte giltig"
|
||||
},
|
||||
"reloading": {
|
||||
"introduction": "Vissa delar av Home Assistant kan laddas om utan att en omstart krävs. Att trycka på \"ladda om\" innebär att den nuvarande konfiguration inaktiveras och den nya laddas."
|
||||
"heading": "Konfigurationen laddas om",
|
||||
"introduction": "Vissa delar av Home Assistant kan laddas om utan att en omstart krävs. Att trycka på \"ladda om\" innebär att den nuvarande konfiguration inaktiveras och den nya laddas.",
|
||||
"core": "Ladda om kärnan",
|
||||
"group": "Ladda om grupper",
|
||||
"automation": "Ladda om automationer",
|
||||
"script": "Ladda om skript"
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "Serverhantering",
|
||||
"introduction": "Kontrollera din Home Assistant-server ... från Home Assistant.",
|
||||
"restart": "Starta om",
|
||||
"stop": "Stoppa"
|
||||
|
@ -245,7 +245,10 @@
|
||||
},
|
||||
"config": {
|
||||
"zwave": {
|
||||
"caption": "Z-Wave"
|
||||
"caption": "Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "கட்டமைப்பு அளவுருவை அமைக்கவும்"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -454,7 +454,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "మీ Z- వేవ్ నెట్వర్క్ని నిర్వహించండి"
|
||||
"description": "మీ Z- వేవ్ నెట్వర్క్ని నిర్వహించండి",
|
||||
"node_config": {
|
||||
"set_config_parameter": "కాన్ఫిగర్ పారామితిని సెట్ చేయండి"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "వినియోగదారులు",
|
||||
|
@ -588,7 +588,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "จัดการเครือข่าย Z-Wave"
|
||||
"description": "จัดการเครือข่าย Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "ตั้งค่าพารามิเตอร์การกำหนดค่า"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "ผู้ใช้",
|
||||
|
@ -565,7 +565,10 @@
|
||||
},
|
||||
"zwave": {
|
||||
"caption": "Z-Wave",
|
||||
"description": "Керуйте мережею Z-Wave"
|
||||
"description": "Керуйте мережею Z-Wave",
|
||||
"node_config": {
|
||||
"set_config_parameter": "Встановити параметр налаштування"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
"caption": "Користувачі",
|
||||
|
@ -277,7 +277,8 @@
|
||||
"default": {
|
||||
"unknown": "KB",
|
||||
"unavailable": "KKD",
|
||||
"error": "Lỗi"
|
||||
"error": "Lỗi",
|
||||
"entity_not_found": "Không tìm thấy thực thể"
|
||||
},
|
||||
"alarm_control_panel": {
|
||||
"armed": "VT",
|
||||
@ -600,6 +601,13 @@
|
||||
"common": {
|
||||
"value": "Giá trị",
|
||||
"index": "Mục lục"
|
||||
},
|
||||
"node_config": {
|
||||
"config_parameter": "Cấu hình tham số",
|
||||
"config_value": "Giá trị cấu hình",
|
||||
"true": "Đúng",
|
||||
"false": "Sai",
|
||||
"set_config_parameter": "Đặt tham số cấu hình"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
@ -712,6 +720,8 @@
|
||||
}
|
||||
},
|
||||
"server_control": {
|
||||
"caption": "Điều khiển máy chủ",
|
||||
"description": "Khởi động lại và dừng máy chủ Home Assistant",
|
||||
"section": {
|
||||
"validation": {
|
||||
"heading": "Xác nhận cấu hình",
|
||||
@ -725,7 +735,14 @@
|
||||
"introduction": "Một số phần của Home Assistant có thể tải lại mà không yêu cầu khởi động lại. Nhấn nút tải lại sẽ gỡ bỏ cấu hình hiện tại của nó và tải một cấu hình mới.",
|
||||
"core": "Tải lại lõi",
|
||||
"group": "Tải lại nhóm",
|
||||
"automation": "Tải lại Tự động hóa"
|
||||
"automation": "Tải lại Tự động hóa",
|
||||
"script": "Tải lại Kịch bản"
|
||||
},
|
||||
"server_management": {
|
||||
"heading": "Quản lý máy chủ",
|
||||
"introduction": "Kiểm soát máy chủ Home Assistant của bạn từ Home Assistant.",
|
||||
"restart": "Khởi động lại",
|
||||
"stop": "Dừng"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -927,7 +944,9 @@
|
||||
"intro": "Xin chào {name} , chào mừng bạn đến với Home Assistant. Bạn muốn đặt tên nhà của bạn thế nào?",
|
||||
"intro_location": "Chúng tôi muốn biết nơi bạn sống. Thông tin này sẽ giúp hiển thị thông tin và thiết lập tự động dựa trên mặt trời. Dữ liệu này không bao giờ được chia sẻ bên ngoài mạng của bạn.",
|
||||
"intro_location_detect": "Chúng tôi có thể giúp bạn điền thông tin này bằng cách yêu cầu một lần cho dịch vụ bên ngoài.",
|
||||
"location_name_default": "Nhà"
|
||||
"location_name_default": "Nhà",
|
||||
"button_detect": "Phát hiện",
|
||||
"finish": "Kế tiếp"
|
||||
}
|
||||
},
|
||||
"lovelace": {
|
||||
@ -943,6 +962,7 @@
|
||||
"go_to_integrations_page": "Chuyển đến trang tích hợp."
|
||||
},
|
||||
"picture-elements": {
|
||||
"hold": "Giữ:",
|
||||
"tap": "Nhấn:",
|
||||
"navigate_to": "Điều hướng đến {location}",
|
||||
"toggle": "Chuyển đổi {name}",
|
||||
@ -1000,7 +1020,12 @@
|
||||
"warning": {
|
||||
"entity_not_found": "Thực thể không có sẵn: {entity}",
|
||||
"entity_non_numeric": "Thực thể không phải là số: {entity}"
|
||||
}
|
||||
},
|
||||
"changed_toast": {
|
||||
"message": "Cấu hình Lovelace đã được cập nhật, bạn có muốn làm mới không?",
|
||||
"refresh": "Làm tươi"
|
||||
},
|
||||
"reload_lovelace": "Tải lại Lovelace"
|
||||
},
|
||||
"page-demo": {
|
||||
"cards": {
|
||||
@ -1016,6 +1041,9 @@
|
||||
"kitchen": "Phòng bếp",
|
||||
"patio": "Sân trong",
|
||||
"hallway": "Hành lang"
|
||||
},
|
||||
"unit": {
|
||||
"minutes_abbr": "phút"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1126,7 +1154,8 @@
|
||||
"fan_mode": "Chế độ quạt",
|
||||
"swing_mode": "Chế độ swing",
|
||||
"away_mode": "Chế độ đi vắng",
|
||||
"aux_heat": "Nhiệt phụ trợ"
|
||||
"aux_heat": "Nhiệt phụ trợ",
|
||||
"preset_mode": "Đặt trước"
|
||||
},
|
||||
"lock": {
|
||||
"code": "Mã",
|
||||
@ -1278,7 +1307,9 @@
|
||||
"away": "Đi vắng",
|
||||
"boost": "Tăng cường",
|
||||
"comfort": "Thoải mái",
|
||||
"home": "Ở nhà"
|
||||
"home": "Ở nhà",
|
||||
"sleep": "Ngủ",
|
||||
"activity": "Hoạt động"
|
||||
},
|
||||
"hvac_action": {
|
||||
"off": "Tắt",
|
||||
|
@ -605,6 +605,9 @@
|
||||
"common": {
|
||||
"value": "值",
|
||||
"instance": "实例"
|
||||
},
|
||||
"node_config": {
|
||||
"set_config_parameter": "设置配置参数"
|
||||
}
|
||||
},
|
||||
"users": {
|
||||
|
@ -847,10 +847,10 @@
|
||||
dependencies:
|
||||
"@material/feature-targeting" "^0.44.1"
|
||||
|
||||
"@mdi/svg@3.7.95":
|
||||
version "3.7.95"
|
||||
resolved "https://registry.yarnpkg.com/@mdi/svg/-/svg-3.7.95.tgz#178207f08cb91dc9111afef7e748aefe41b54019"
|
||||
integrity sha512-5ZStRxq4PFATwurnjN1CgCGCfP8nwJfHuqUozTMaF/qKC7rlSBS0Sm+VQQDmlJBO3JjiG+dQgd7ZdtanI/w/mw==
|
||||
"@mdi/svg@3.9.97":
|
||||
version "3.9.97"
|
||||
resolved "https://registry.yarnpkg.com/@mdi/svg/-/svg-3.9.97.tgz#653fb2249e47c62bd89664ab1479eb5e16a3051c"
|
||||
integrity sha512-ppU8jzKWZIau6DPWywZPtcDw1y5o+EopdtomfpYS96kLf1Lu7qEnIBeh+7QQUjhepKSexO/XW11/mMGecFkOdw==
|
||||
|
||||
"@mrmlnc/readdir-enhanced@^2.2.1":
|
||||
version "2.2.1"
|
||||
|
Loading…
x
Reference in New Issue
Block a user