mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-10 05:29:34 +00:00
Compare commits
23 Commits
20180903.0
...
20180911.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e3a137c675 | ||
![]() |
10aa99abdc | ||
![]() |
34567d451f | ||
![]() |
494e3dc62c | ||
![]() |
0997274f29 | ||
![]() |
76161329b6 | ||
![]() |
8505750958 | ||
![]() |
4077105db1 | ||
![]() |
3f31d83a55 | ||
![]() |
d729e3c567 | ||
![]() |
9af75f9a43 | ||
![]() |
d32d334a2e | ||
![]() |
94006a843c | ||
![]() |
4790590327 | ||
![]() |
7cf7763e21 | ||
![]() |
0d7979a72f | ||
![]() |
300425e698 | ||
![]() |
59010baf89 | ||
![]() |
47fcb122a2 | ||
![]() |
bbb50b1397 | ||
![]() |
ae8724d699 | ||
![]() |
2169f6979d | ||
![]() |
9cc577e9c7 |
28
Dockerfile
28
Dockerfile
@@ -1,25 +1,31 @@
|
|||||||
FROM node:8.2.1-alpine
|
FROM node:8.9-alpine
|
||||||
|
|
||||||
# install yarn
|
# install yarn
|
||||||
ENV PATH /root/.yarn/bin:$PATH
|
ENV PATH /root/.yarn/bin:$PATH
|
||||||
|
|
||||||
|
## Install/force base tools
|
||||||
RUN apk update \
|
RUN apk update \
|
||||||
&& apk add curl bash binutils tar git python3 \
|
&& apk add make g++ curl bash binutils tar git python2 python3 \
|
||||||
&& rm -rf /var/cache/apk/* \
|
&& rm -rf /var/cache/apk/* \
|
||||||
&& /bin/bash \
|
&& /bin/bash \
|
||||||
&& touch ~/.bashrc \
|
&& touch ~/.bashrc
|
||||||
&& curl -o- -L https://yarnpkg.com/install.sh | bash
|
|
||||||
|
|
||||||
|
## Install yarn
|
||||||
|
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||||
|
|
||||||
|
## Setup the project
|
||||||
RUN mkdir -p /frontend
|
RUN mkdir -p /frontend
|
||||||
|
|
||||||
WORKDIR /frontend
|
WORKDIR /frontend
|
||||||
|
|
||||||
ENV NODE_ENV production
|
COPY package.json yarn.lock ./
|
||||||
|
|
||||||
COPY package.json ./
|
RUN yarn install --frozen-lockfile
|
||||||
RUN yarn
|
|
||||||
|
|
||||||
COPY bower.json ./
|
|
||||||
RUN ./node_modules/.bin/bower install --allow-root
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
CMD [ "/bin/bash", "./script/build_frontend" ]
|
|
||||||
|
COPY script/docker_entrypoint.sh /usr/bin/docker_entrypoint.sh
|
||||||
|
|
||||||
|
RUN chmod +x /usr/bin/docker_entrypoint.sh
|
||||||
|
|
||||||
|
CMD [ "docker_entrypoint.sh" ]
|
||||||
|
12
README.md
12
README.md
@@ -16,6 +16,18 @@ This is the repository for the official [Home Assistant](https://home-assistant.
|
|||||||
- Gallery: `cd gallery && script/develop_gallery`
|
- Gallery: `cd gallery && script/develop_gallery`
|
||||||
- Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
|
- Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
|
||||||
|
|
||||||
|
## Frontend development
|
||||||
|
|
||||||
|
### Classic environment
|
||||||
|
A complete guide can be found at the following [link](https://www.home-assistant.io/developers/frontend/). It describes a short guide for the build of project.
|
||||||
|
|
||||||
|
### Docker environment
|
||||||
|
It is possible to compile the project and/or run commands in the development environment having only the [Docker](https://www.docker.com) pre-installed in the system. On the root of project you can do:
|
||||||
|
* `sh ./script/docker_run.sh build` Build all the project with one command
|
||||||
|
* `sh ./script/docker_run.sh bash` Open an interactive shell (the same environment generated by the *classic environment*) where you can run commands. This bash work on your project directory and any change on your file is automatically present within your build bash.
|
||||||
|
|
||||||
|
**Note**: if you have installed `npm` in addition to the `docker`, you can use the commands `npm run docker_build` and `npm run bash` to get a full build or bash as explained above
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Home Assistant is open-source and Apache 2 licensed. Feel free to browse the repository, learn and reuse parts in your own projects.
|
Home Assistant is open-source and Apache 2 licensed. Feel free to browse the repository, learn and reuse parts in your own projects.
|
||||||
|
@@ -10,7 +10,9 @@
|
|||||||
"build": "script/build_frontend",
|
"build": "script/build_frontend",
|
||||||
"lint": "eslint src hassio/src gallery/src test-mocha && polymer lint",
|
"lint": "eslint src hassio/src gallery/src test-mocha && polymer lint",
|
||||||
"mocha": "node_modules/.bin/mocha --opts test-mocha/mocha.opts",
|
"mocha": "node_modules/.bin/mocha --opts test-mocha/mocha.opts",
|
||||||
"test": "npm run lint && npm run mocha"
|
"test": "npm run lint && npm run mocha",
|
||||||
|
"docker_build": "sh ./script/docker_run.sh build $npm_package_version",
|
||||||
|
"bash": "sh ./script/docker_run.sh bash $npm_package_version"
|
||||||
},
|
},
|
||||||
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
"author": "Paulus Schoutsen <Paulus@PaulusSchoutsen.nl> (http://paulusschoutsen.nl)",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@@ -68,7 +70,7 @@
|
|||||||
"es6-object-assign": "^1.1.0",
|
"es6-object-assign": "^1.1.0",
|
||||||
"eslint-import-resolver-webpack": "^0.10.0",
|
"eslint-import-resolver-webpack": "^0.10.0",
|
||||||
"fecha": "^2.3.3",
|
"fecha": "^2.3.3",
|
||||||
"home-assistant-js-websocket": "^3.0.0",
|
"home-assistant-js-websocket": "^3.1.2",
|
||||||
"intl-messageformat": "^2.2.0",
|
"intl-messageformat": "^2.2.0",
|
||||||
"js-yaml": "^3.12.0",
|
"js-yaml": "^3.12.0",
|
||||||
"leaflet": "^1.3.1",
|
"leaflet": "^1.3.1",
|
||||||
@@ -84,6 +86,7 @@
|
|||||||
"xss": "^1.0.3"
|
"xss": "^1.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@gfx/zopfli": "^1.0.8",
|
||||||
"babel-core": "^6.26.3",
|
"babel-core": "^6.26.3",
|
||||||
"babel-eslint": "^8.2.3",
|
"babel-eslint": "^8.2.3",
|
||||||
"babel-loader": "^7.1.4",
|
"babel-loader": "^7.1.4",
|
||||||
@@ -93,7 +96,7 @@
|
|||||||
"babel-preset-env": "^1.7.0",
|
"babel-preset-env": "^1.7.0",
|
||||||
"babel-preset-es2015": "^6.24.1",
|
"babel-preset-es2015": "^6.24.1",
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"compression-webpack-plugin": "^1.1.11",
|
"compression-webpack-plugin": "^2.0.0",
|
||||||
"copy-webpack-plugin": "^4.5.1",
|
"copy-webpack-plugin": "^4.5.1",
|
||||||
"del": "^3.0.0",
|
"del": "^3.0.0",
|
||||||
"eslint": "^4.19.1",
|
"eslint": "^4.19.1",
|
||||||
|
14
script/docker_entrypoint.sh
Normal file
14
script/docker_entrypoint.sh
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Docker entry point inspired by travis build and script/build_frontend
|
||||||
|
|
||||||
|
# Stop on errors
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Build the frontend but not used the npm run build
|
||||||
|
/bin/bash script/build_frontend
|
||||||
|
|
||||||
|
# TEST
|
||||||
|
npm run test
|
||||||
|
|
||||||
|
#
|
||||||
|
#xvfb-run wct
|
103
script/docker_run.sh
Executable file
103
script/docker_run.sh
Executable file
@@ -0,0 +1,103 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Basic Docker Management scripts
|
||||||
|
# With this script you can build software, or enter an agnostic development environment and run commands interactively.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
check_mandatory_tools(){
|
||||||
|
if [ "x$(which docker)" == "x" ]; then
|
||||||
|
echo "UNKNOWN - Missing docker binary! Are you sure it is installed and reachable?"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker info > /dev/null 2>&1
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "UNKNOWN - Unable to talk to the docker daemon! Maybe the docker daemon is not running"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_dev_image(){
|
||||||
|
if [[ "$(docker images -q ${IMAGE_NAME}:$IMAGE_TAG 2> /dev/null)" == "" ]]; then
|
||||||
|
echo "UNKNOWN - Can't find the development docker image ${IMAGE_NAME}:$IMAGE_TAG"
|
||||||
|
while true; do
|
||||||
|
read -p "Do you want to create it now?" yn
|
||||||
|
case $yn in
|
||||||
|
[Yy]* ) create_image; break;;
|
||||||
|
[Nn]* ) exit 3;;
|
||||||
|
* ) echo "Please answer y or n";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Building the basic image for compiling the production frontend
|
||||||
|
create_image(){
|
||||||
|
docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Execute interactive bash on basic image
|
||||||
|
#
|
||||||
|
run_bash_on_docker(){
|
||||||
|
|
||||||
|
check_dev_image
|
||||||
|
|
||||||
|
docker run -it \
|
||||||
|
-v $PWD/:/frontend/ \
|
||||||
|
-v /frontend/node_modules \
|
||||||
|
-v /frontend/bower_components \
|
||||||
|
${IMAGE_NAME}:${IMAGE_TAG} /bin/bash
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Execute the basic image for compiling the production frontend
|
||||||
|
#
|
||||||
|
build_all(){
|
||||||
|
|
||||||
|
check_dev_image
|
||||||
|
|
||||||
|
docker run -it \
|
||||||
|
-v $PWD/:/frontend/ \
|
||||||
|
-v /frontend/node_modules \
|
||||||
|
-v /frontend/bower_components \
|
||||||
|
${IMAGE_NAME}:${IMAGE_TAG} /bin/bash script/build_frontend
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Init Global Variable
|
||||||
|
IMAGE_NAME=home_assistant_fe_image
|
||||||
|
IMAGE_TAG=${2:-latest}
|
||||||
|
|
||||||
|
check_mandatory_tools
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
setup_env)
|
||||||
|
create_image
|
||||||
|
;;
|
||||||
|
bash)
|
||||||
|
run_bash_on_docker
|
||||||
|
;;
|
||||||
|
build)
|
||||||
|
build_all
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "NAME"
|
||||||
|
echo " Docker Management."
|
||||||
|
echo ""
|
||||||
|
echo "SYNOPSIS"
|
||||||
|
echo " ${0} command [version]"
|
||||||
|
echo ""
|
||||||
|
echo "DESCRIPTION"
|
||||||
|
echo " With this script you can build software, or enter an agnostic development environment and run commands interactively."
|
||||||
|
echo ""
|
||||||
|
echo " The command are:"
|
||||||
|
echo " setup_env Create develop images"
|
||||||
|
echo " bash Run bash on develop enviroments"
|
||||||
|
echo " build Run silent build"
|
||||||
|
echo ""
|
||||||
|
echo " The version is optional, if not inserted it assumes \"latest\". "
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
2
setup.py
2
setup.py
@@ -1,7 +1,7 @@
|
|||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
setup(name='home-assistant-frontend',
|
setup(name='home-assistant-frontend',
|
||||||
version='20180903.0',
|
version='20180911.0',
|
||||||
description='The Home Assistant frontend',
|
description='The Home Assistant frontend',
|
||||||
url='https://github.com/home-assistant/home-assistant-polymer',
|
url='https://github.com/home-assistant/home-assistant-polymer',
|
||||||
author='The Home Assistant Authors',
|
author='The Home Assistant Authors',
|
||||||
|
@@ -94,6 +94,12 @@ class HaAuthorize extends LocalizeLiteMixin(PolymerElement) {
|
|||||||
const response = await window.providersPromise;
|
const response = await window.providersPromise;
|
||||||
const authProviders = await response.json();
|
const authProviders = await response.json();
|
||||||
|
|
||||||
|
// Forward to main screen which will redirect to right onboarding page.
|
||||||
|
if (response.status === 400 && authProviders.code === 'onboarding_required') {
|
||||||
|
location.href = '/';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (authProviders.length === 0) {
|
if (authProviders.length === 0) {
|
||||||
alert('No auth providers returned. Unable to finish login.');
|
alert('No auth providers returned. Unable to finish login.');
|
||||||
return;
|
return;
|
||||||
|
70
src/common/auth/external_auth.js
Normal file
70
src/common/auth/external_auth.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* Auth class that connects to a native app for authentication.
|
||||||
|
*/
|
||||||
|
import { Auth } from 'home-assistant-js-websocket';
|
||||||
|
|
||||||
|
const CALLBACK_SET_TOKEN = 'externalAuthSetToken';
|
||||||
|
const CALLBACK_REVOKE_TOKEN = 'externalAuthRevokeToken';
|
||||||
|
|
||||||
|
if (!window.externalApp && !window.webkit) {
|
||||||
|
throw new Error('External auth requires either externalApp or webkit defined on Window object.');
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class ExternalAuth extends Auth {
|
||||||
|
constructor(hassUrl) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.data = {
|
||||||
|
hassUrl,
|
||||||
|
access_token: '',
|
||||||
|
// This will trigger connection to do a refresh right away
|
||||||
|
expires: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshAccessToken() {
|
||||||
|
const responseProm = new Promise((resolve, reject) => {
|
||||||
|
window[CALLBACK_SET_TOKEN] = (success, data) => (success ? resolve(data) : reject(data));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Allow promise to set resolve on window object.
|
||||||
|
await 0;
|
||||||
|
|
||||||
|
const callbackPayload = { callback: CALLBACK_SET_TOKEN };
|
||||||
|
|
||||||
|
if (window.externalApp) {
|
||||||
|
window.externalApp.getExternalAuth(callbackPayload);
|
||||||
|
} else {
|
||||||
|
window.webkit.messageHandlers.getExternalAuth.postMessage(callbackPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Response we expect back:
|
||||||
|
// {
|
||||||
|
// "access_token": "qwere",
|
||||||
|
// "expires_in": 1800
|
||||||
|
// }
|
||||||
|
const tokens = await responseProm;
|
||||||
|
|
||||||
|
this.data.access_token = tokens.access_token;
|
||||||
|
this.data.expires = (tokens.expires_in * 1000) + Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
async revoke() {
|
||||||
|
const responseProm = new Promise((resolve, reject) => {
|
||||||
|
window[CALLBACK_REVOKE_TOKEN] = (success, data) => (success ? resolve(data) : reject(data));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Allow promise to set resolve on window object.
|
||||||
|
await 0;
|
||||||
|
|
||||||
|
const callbackPayload = { callback: CALLBACK_REVOKE_TOKEN };
|
||||||
|
|
||||||
|
if (window.externalApp) {
|
||||||
|
window.externalApp.revokeExternalAuth(callbackPayload);
|
||||||
|
} else {
|
||||||
|
window.webkit.messageHandlers.revokeExternalAuth.postMessage(callbackPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
await responseProm;
|
||||||
|
}
|
||||||
|
}
|
@@ -9,6 +9,7 @@ import EventsMixin from '../../mixins/events-mixin.js';
|
|||||||
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
||||||
|
|
||||||
import computeStateName from '../../common/entity/compute_state_name.js';
|
import computeStateName from '../../common/entity/compute_state_name.js';
|
||||||
|
import computeDomain from '../../common/entity/compute_domain.js';
|
||||||
import isComponentLoaded from '../../common/config/is_component_loaded.js';
|
import isComponentLoaded from '../../common/config/is_component_loaded.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -44,7 +45,10 @@ class MoreInfoSettings extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
<app-toolbar>
|
<app-toolbar>
|
||||||
<paper-icon-button icon="hass:arrow-left" on-click="_backTapped"></paper-icon-button>
|
<paper-icon-button icon="hass:arrow-left" on-click="_backTapped"></paper-icon-button>
|
||||||
<div main-title="">[[_computeStateName(stateObj)]]</div>
|
<div main-title="">[[_computeStateName(stateObj)]]</div>
|
||||||
<paper-button on-click="_save">[[localize('ui.dialogs.more_info_settings.save')]]</paper-button>
|
<paper-button
|
||||||
|
on-click="_save"
|
||||||
|
disabled='[[_computeInvalid(_entityId)]]'
|
||||||
|
>[[localize('ui.dialogs.more_info_settings.save')]]</paper-button>
|
||||||
</app-toolbar>
|
</app-toolbar>
|
||||||
|
|
||||||
<div class="form">
|
<div class="form">
|
||||||
@@ -55,6 +59,8 @@ class MoreInfoSettings extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
<paper-input
|
<paper-input
|
||||||
value="{{_entityId}}"
|
value="{{_entityId}}"
|
||||||
label="[[localize('ui.dialogs.more_info_settings.entity_id')]]"
|
label="[[localize('ui.dialogs.more_info_settings.entity_id')]]"
|
||||||
|
error-message="Domain needs to stay the same"
|
||||||
|
invalid='[[_computeInvalid(_entityId)]]'
|
||||||
></paper-input>
|
></paper-input>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -90,6 +96,10 @@ class MoreInfoSettings extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|||||||
return isComponentLoaded(hass, 'config.entity_registry');
|
return isComponentLoaded(hass, 'config.entity_registry');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_computeInvalid(entityId) {
|
||||||
|
return computeDomain(this.stateObj.entity_id) !== computeDomain(entityId);
|
||||||
|
}
|
||||||
|
|
||||||
_registryInfoChanged(newVal) {
|
_registryInfoChanged(newVal) {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
|
@@ -4,6 +4,7 @@ import {
|
|||||||
subscribeConfig,
|
subscribeConfig,
|
||||||
subscribeEntities,
|
subscribeEntities,
|
||||||
subscribeServices,
|
subscribeServices,
|
||||||
|
ERR_INVALID_AUTH,
|
||||||
} from 'home-assistant-js-websocket';
|
} from 'home-assistant-js-websocket';
|
||||||
|
|
||||||
import { loadTokens, saveTokens } from '../common/auth/token_storage.js';
|
import { loadTokens, saveTokens } from '../common/auth/token_storage.js';
|
||||||
@@ -11,21 +12,45 @@ import { subscribePanels } from '../data/ws-panels.js';
|
|||||||
import { subscribeThemes } from '../data/ws-themes.js';
|
import { subscribeThemes } from '../data/ws-themes.js';
|
||||||
import { subscribeUser } from '../data/ws-user.js';
|
import { subscribeUser } from '../data/ws-user.js';
|
||||||
|
|
||||||
window.hassAuth = getAuth({
|
const hassUrl = `${location.protocol}//${location.host}`;
|
||||||
hassUrl: `${location.protocol}//${location.host}`,
|
const isExternal = location.search.includes('external_auth=1');
|
||||||
saveTokens,
|
|
||||||
loadTokens: () => Promise.resolve(loadTokens()),
|
|
||||||
});
|
|
||||||
|
|
||||||
window.hassConnection = window.hassAuth.then((auth) => {
|
const authProm = isExternal ?
|
||||||
if (location.search.includes('auth_callback=1')) {
|
() => import('../common/auth/external_auth.js')
|
||||||
history.replaceState(null, null, location.pathname);
|
.then(mod => new mod.default(hassUrl)) :
|
||||||
|
() => getAuth({
|
||||||
|
hassUrl,
|
||||||
|
saveTokens,
|
||||||
|
loadTokens: () => Promise.resolve(loadTokens()),
|
||||||
|
});
|
||||||
|
|
||||||
|
const connProm = async (auth) => {
|
||||||
|
try {
|
||||||
|
const conn = await createConnection({ auth });
|
||||||
|
|
||||||
|
// Clear url if we have been able to establish a connection
|
||||||
|
if (location.search.includes('auth_callback=1')) {
|
||||||
|
history.replaceState(null, null, location.pathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { auth, conn };
|
||||||
|
} catch (err) {
|
||||||
|
if (err !== ERR_INVALID_AUTH) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
// We can get invalid auth if auth tokens were stored that are no longer valid
|
||||||
|
// Clear stored tokens.
|
||||||
|
if (!isExternal) saveTokens(null);
|
||||||
|
auth = await authProm();
|
||||||
|
const conn = await createConnection({ auth });
|
||||||
|
return { auth, conn };
|
||||||
}
|
}
|
||||||
return createConnection({ auth });
|
};
|
||||||
});
|
|
||||||
|
window.hassConnection = authProm().then(connProm);
|
||||||
|
|
||||||
// Start fetching some of the data that we will need.
|
// Start fetching some of the data that we will need.
|
||||||
window.hassConnection.then((conn) => {
|
window.hassConnection.then(({ conn }) => {
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
subscribeEntities(conn, noop);
|
subscribeEntities(conn, noop);
|
||||||
subscribeConfig(conn, noop);
|
subscribeConfig(conn, noop);
|
||||||
|
@@ -27,9 +27,16 @@ export default superClass => class extends superClass {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleLogout() {
|
async _handleLogout() {
|
||||||
this.hass.connection.close();
|
try {
|
||||||
clearState();
|
await this.hass.auth.revoke();
|
||||||
document.location.href = '/';
|
this.hass.connection.close();
|
||||||
|
clearState();
|
||||||
|
document.location.href = '/';
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(err);
|
||||||
|
alert('Log out failed');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -13,6 +13,7 @@ import EventsMixin from '../../mixins/events-mixin.js';
|
|||||||
|
|
||||||
import { getState } from '../../util/ha-pref-storage.js';
|
import { getState } from '../../util/ha-pref-storage.js';
|
||||||
import { getActiveTranslation } from '../../util/hass-translation.js';
|
import { getActiveTranslation } from '../../util/hass-translation.js';
|
||||||
|
import { fetchWithAuth } from '../../util/fetch-with-auth.js';
|
||||||
import hassCallApi from '../../util/hass-call-api.js';
|
import hassCallApi from '../../util/hass-call-api.js';
|
||||||
import computeStateName from '../../common/entity/compute_state_name.js';
|
import computeStateName from '../../common/entity/compute_state_name.js';
|
||||||
import { subscribePanels } from '../../data/ws-panels';
|
import { subscribePanels } from '../../data/ws-panels';
|
||||||
@@ -25,7 +26,16 @@ export default superClass =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
async _handleConnProm() {
|
async _handleConnProm() {
|
||||||
const [auth, conn] = await Promise.all([window.hassAuth, window.hassConnection]);
|
let auth;
|
||||||
|
let conn;
|
||||||
|
try {
|
||||||
|
const result = await window.hassConnection;
|
||||||
|
auth = result.auth;
|
||||||
|
conn = result.conn;
|
||||||
|
} catch (err) {
|
||||||
|
this._error = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.hass = Object.assign({
|
this.hass = Object.assign({
|
||||||
auth,
|
auth,
|
||||||
@@ -80,23 +90,9 @@ export default superClass =>
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
callApi: async (method, path, parameters) => {
|
callApi: async (method, path, parameters) =>
|
||||||
const host = window.location.protocol + '//' + window.location.host;
|
hassCallApi(auth, method, path, parameters),
|
||||||
|
fetchWithAuth: (path, init) => fetchWithAuth(auth, `${auth.data.hassUrl}${path}`, init),
|
||||||
try {
|
|
||||||
if (auth.expired) await auth.refreshAccessToken();
|
|
||||||
} catch (err) {
|
|
||||||
if (err === ERR_INVALID_AUTH) {
|
|
||||||
// Trigger auth flow
|
|
||||||
location.reload();
|
|
||||||
// ensure further JS is not executed
|
|
||||||
await new Promise(() => {});
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await hassCallApi(host, auth, method, path, parameters);
|
|
||||||
},
|
|
||||||
// For messages that do not get a response
|
// For messages that do not get a response
|
||||||
sendWS: (msg) => {
|
sendWS: (msg) => {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
@@ -51,7 +51,7 @@ class HomeAssistant extends ext(PolymerElement, [
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template is="dom-if" if="[[!showMain]]" restamp>
|
<template is="dom-if" if="[[!showMain]]" restamp>
|
||||||
<ha-init-page></ha-init-page>
|
<ha-init-page error='[[_error]]'></ha-init-page>
|
||||||
</template>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -73,6 +73,10 @@ class HomeAssistant extends ext(PolymerElement, [
|
|||||||
computed: 'computePanelUrl(routeData)',
|
computed: 'computePanelUrl(routeData)',
|
||||||
observer: 'panelUrlChanged',
|
observer: 'panelUrlChanged',
|
||||||
},
|
},
|
||||||
|
_error: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,12 +1,8 @@
|
|||||||
import '@polymer/iron-flex-layout/iron-flex-layout-classes.js';
|
import '@polymer/iron-flex-layout/iron-flex-layout-classes.js';
|
||||||
import '@polymer/paper-button/paper-button.js';
|
import '@polymer/paper-button/paper-button.js';
|
||||||
import '@polymer/paper-checkbox/paper-checkbox.js';
|
|
||||||
import '@polymer/paper-input/paper-input.js';
|
|
||||||
import '@polymer/paper-spinner/paper-spinner.js';
|
import '@polymer/paper-spinner/paper-spinner.js';
|
||||||
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
||||||
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
||||||
import { ERR_CANNOT_CONNECT, ERR_INVALID_AUTH } from 'home-assistant-js-websocket';
|
|
||||||
|
|
||||||
|
|
||||||
import LocalizeMixin from '../mixins/localize-mixin.js';
|
import LocalizeMixin from '../mixins/localize-mixin.js';
|
||||||
import EventsMixin from '../mixins/events-mixin.js';
|
import EventsMixin from '../mixins/events-mixin.js';
|
||||||
@@ -26,82 +22,26 @@ class HaInitPage extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
|
|
||||||
<div class="layout vertical center center-center fit">
|
<div class="layout vertical center center-center fit">
|
||||||
<img src="/static/icons/favicon-192x192.png" height="192">
|
<img src="/static/icons/favicon-192x192.png" height="192">
|
||||||
<paper-spinner active="true"></paper-spinner>
|
<paper-spinner active="[[!error]]"></paper-spinner>
|
||||||
Loading data
|
<template is='dom-if' if='[[error]]'>
|
||||||
|
Unable to connect to Home Assistant.
|
||||||
|
<paper-button on-click='_retry'>Retry</paper-button>
|
||||||
|
</template>
|
||||||
|
<template is='dom-if' if='[[!error]]'>
|
||||||
|
Loading data
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
ready() {
|
static get properties() {
|
||||||
super.ready();
|
return {
|
||||||
this.addEventListener('keydown', ev => this.passwordKeyDown(ev));
|
error: Boolean,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
computeLoadingMsg(isValidating) {
|
_retry() {
|
||||||
return isValidating ? 'Connecting' : 'Loading data';
|
location.reload();
|
||||||
}
|
|
||||||
|
|
||||||
computeShowSpinner(forceShowLoading, isValidating) {
|
|
||||||
return forceShowLoading || isValidating;
|
|
||||||
}
|
|
||||||
|
|
||||||
isValidatingChanged(newVal) {
|
|
||||||
if (!newVal) {
|
|
||||||
setTimeout(() => {
|
|
||||||
if (this.$.passwordInput.inputElement.inputElement) {
|
|
||||||
this.$.passwordInput.inputElement.inputElement.focus();
|
|
||||||
}
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
passwordKeyDown(ev) {
|
|
||||||
// validate on enter
|
|
||||||
if (ev.keyCode === 13) {
|
|
||||||
this.validatePassword();
|
|
||||||
ev.preventDefault();
|
|
||||||
// clear error after we start typing again
|
|
||||||
} else if (this.errorMessage) {
|
|
||||||
this.errorMessage = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
validatePassword() {
|
|
||||||
var auth = this.password;
|
|
||||||
this.$.hideKeyboardOnFocus.focus();
|
|
||||||
const connProm = window.createHassConnection(auth);
|
|
||||||
this.fire('try-connection', { connProm });
|
|
||||||
|
|
||||||
if (this.$.rememberLogin.checked) {
|
|
||||||
connProm.then(function () {
|
|
||||||
localStorage.authToken = auth;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handleConnectionPromiseChanged(newVal) {
|
|
||||||
if (!newVal) return;
|
|
||||||
|
|
||||||
var el = this;
|
|
||||||
this.isValidating = true;
|
|
||||||
|
|
||||||
this.connectionPromise.then(
|
|
||||||
function () {
|
|
||||||
el.isValidating = false;
|
|
||||||
el.password = '';
|
|
||||||
},
|
|
||||||
function (errCode) {
|
|
||||||
el.isValidating = false;
|
|
||||||
|
|
||||||
if (errCode === ERR_CANNOT_CONNECT) {
|
|
||||||
el.errorMessage = 'Unable to connect';
|
|
||||||
} else if (errCode === ERR_INVALID_AUTH) {
|
|
||||||
el.errorMessage = 'Invalid password';
|
|
||||||
} else {
|
|
||||||
el.errorMessage = 'Unknown error: ' + errCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,11 +4,8 @@ import '@polymer/paper-input/paper-input.js';
|
|||||||
import '@polymer/paper-button/paper-button.js';
|
import '@polymer/paper-button/paper-button.js';
|
||||||
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
||||||
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
||||||
import hassCallApi from '../util/hass-call-api.js';
|
|
||||||
import localizeLiteMixin from '../mixins/localize-lite-mixin.js';
|
import localizeLiteMixin from '../mixins/localize-lite-mixin.js';
|
||||||
|
|
||||||
const callApi = (method, path, data) => hassCallApi('', {}, method, path, data);
|
|
||||||
|
|
||||||
class HaOnboarding extends localizeLiteMixin(PolymerElement) {
|
class HaOnboarding extends localizeLiteMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
@@ -141,12 +138,23 @@ class HaOnboarding extends localizeLiteMixin(PolymerElement) {
|
|||||||
this._errorMsg = '';
|
this._errorMsg = '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await callApi('post', 'onboarding/users', {
|
const response = await fetch('/api/onboarding/users', {
|
||||||
name: this._name,
|
method: 'POST',
|
||||||
username: this._username,
|
credentials: 'same-origin',
|
||||||
password: this._password,
|
body: JSON.stringify({
|
||||||
|
name: this._name,
|
||||||
|
username: this._username,
|
||||||
|
password: this._password,
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
throw {
|
||||||
|
message: `Bad response from server: ${response.status}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
document.location = '/';
|
document.location = '/';
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
|
@@ -97,9 +97,7 @@ class HaConfigCloudAccount extends EventsMixin(PolymerElement) {
|
|||||||
With the Alexa integration for Home Assistant Cloud you'll be able to control all your Home Assistant devices via any Alexa-enabled device.
|
With the Alexa integration for Home Assistant Cloud you'll be able to control all your Home Assistant devices via any Alexa-enabled device.
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://alexa.amazon.com/spa/index.html#skills/dp/B0772J1QKB/?ref=skill_dsk_skb_sr_2" target="_blank">
|
To activate, search in the Alexa app for the Home Assistant Smart Home skill.
|
||||||
Activate the Home Assistant skill for Alexa
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="https://www.home-assistant.io/cloud/alexa/" target="_blank">
|
<a href="https://www.home-assistant.io/cloud/alexa/" target="_blank">
|
||||||
|
@@ -29,9 +29,6 @@ class HaUserEditor extends EventsMixin(NavigateMixin(LocalizeMixin(PolymerElemen
|
|||||||
paper-card:last-child {
|
paper-card:last-child {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
paper-button {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<hass-subpage header="View user">
|
<hass-subpage header="View user">
|
||||||
@@ -57,9 +54,12 @@ class HaUserEditor extends EventsMixin(NavigateMixin(LocalizeMixin(PolymerElemen
|
|||||||
</paper-card>
|
</paper-card>
|
||||||
<paper-card>
|
<paper-card>
|
||||||
<div class='card-actions'>
|
<div class='card-actions'>
|
||||||
<paper-button on-click='_deleteUser'>
|
<paper-button on-click='_deleteUser' disabled='[[user.system_generated]]'>
|
||||||
[[localize('ui.panel.config.users.editor.delete_user')]]
|
[[localize('ui.panel.config.users.editor.delete_user')]]
|
||||||
</paper-button>
|
</paper-button>
|
||||||
|
<template is='dom-if' if='[[user.system_generated]]'>
|
||||||
|
Unable to remove system generated users.
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</paper-card>
|
</paper-card>
|
||||||
</hass-subpage>
|
</hass-subpage>
|
||||||
|
@@ -50,7 +50,12 @@ class HaUserPicker extends EventsMixin(NavigateMixin(LocalizeMixin(PolymerElemen
|
|||||||
<paper-item>
|
<paper-item>
|
||||||
<paper-item-body two-line>
|
<paper-item-body two-line>
|
||||||
<div>[[_withDefault(user.name, 'Unnamed User')]]</div>
|
<div>[[_withDefault(user.name, 'Unnamed User')]]</div>
|
||||||
<div secondary="">[[user.id]]</div>
|
<div secondary="">
|
||||||
|
[[user.id]]
|
||||||
|
<template is='dom-if' if='[[user.system_generated]]'>
|
||||||
|
- System Generated
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</paper-item-body>
|
</paper-item-body>
|
||||||
<iron-icon icon="hass:chevron-right"></iron-icon>
|
<iron-icon icon="hass:chevron-right"></iron-icon>
|
||||||
</paper-item>
|
</paper-item>
|
||||||
|
@@ -21,7 +21,6 @@ import './zwave-groups.js';
|
|||||||
import './zwave-log.js';
|
import './zwave-log.js';
|
||||||
import './zwave-network.js';
|
import './zwave-network.js';
|
||||||
import './zwave-node-config.js';
|
import './zwave-node-config.js';
|
||||||
import './zwave-node-information.js';
|
|
||||||
import './zwave-usercodes.js';
|
import './zwave-usercodes.js';
|
||||||
import './zwave-values.js';
|
import './zwave-values.js';
|
||||||
import './zwave-node-protection.js';
|
import './zwave-node-protection.js';
|
||||||
@@ -29,12 +28,14 @@ import './zwave-node-protection.js';
|
|||||||
import sortByName from '../../../common/entity/states_sort_by_name.js';
|
import sortByName from '../../../common/entity/states_sort_by_name.js';
|
||||||
import computeStateName from '../../../common/entity/compute_state_name.js';
|
import computeStateName from '../../../common/entity/compute_state_name.js';
|
||||||
import computeStateDomain from '../../../common/entity/compute_state_domain.js';
|
import computeStateDomain from '../../../common/entity/compute_state_domain.js';
|
||||||
|
import EventsMixin from '../../../mixins/events-mixin.js';
|
||||||
import LocalizeMixin from '../../../mixins/localize-mixin.js';
|
import LocalizeMixin from '../../../mixins/localize-mixin.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
|
* @appliesMixin EventsMixin
|
||||||
*/
|
*/
|
||||||
class HaConfigZwave extends LocalizeMixin(PolymerElement) {
|
class HaConfigZwave extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style include="iron-flex ha-style ha-form-style">
|
<style include="iron-flex ha-style ha-form-style">
|
||||||
@@ -203,13 +204,14 @@ class HaConfigZwave extends LocalizeMixin(PolymerElement) {
|
|||||||
service="test_node"
|
service="test_node"
|
||||||
hidden$="[[!showHelp]]">
|
hidden$="[[!showHelp]]">
|
||||||
</ha-service-description>
|
</ha-service-description>
|
||||||
|
<paper-button on-click="_nodeMoreInfo">Node Information</paper-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="device-picker">
|
<div class="device-picker">
|
||||||
<paper-dropdown-menu label="Entities of this node" dynamic-align="" class="flex">
|
<paper-dropdown-menu label="Entities of this node" dynamic-align="" class="flex">
|
||||||
<paper-listbox slot="dropdown-content" selected="{{selectedEntity}}">
|
<paper-listbox slot="dropdown-content" selected="{{selectedEntity}}">
|
||||||
<template is="dom-repeat" items="[[entities]]" as="state">
|
<template is="dom-repeat" items="[[entities]]" as="state">
|
||||||
<paper-item>[[computeSelectCaptionEnt(state)]]</paper-item>
|
<paper-item>[[state.entity_id]]</paper-item>
|
||||||
</template>
|
</template>
|
||||||
</paper-listbox>
|
</paper-listbox>
|
||||||
</paper-dropdown-menu>
|
</paper-dropdown-menu>
|
||||||
@@ -269,12 +271,6 @@ class HaConfigZwave extends LocalizeMixin(PolymerElement) {
|
|||||||
</paper-card>
|
</paper-card>
|
||||||
|
|
||||||
<template is="dom-if" if="[[computeIsNodeSelected(selectedNode)]]">
|
<template is="dom-if" if="[[computeIsNodeSelected(selectedNode)]]">
|
||||||
<!--Node info card-->
|
|
||||||
<zwave-node-information
|
|
||||||
id="zwave-node-information"
|
|
||||||
nodes="[[nodes]]"
|
|
||||||
selected-node="[[selectedNode]]"
|
|
||||||
></zwave-node-information>
|
|
||||||
|
|
||||||
<!--Value card-->
|
<!--Value card-->
|
||||||
<zwave-values
|
<zwave-values
|
||||||
@@ -563,6 +559,10 @@ class HaConfigZwave extends LocalizeMixin(PolymerElement) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_nodeMoreInfo() {
|
||||||
|
this.fire('hass-more-info', { entityId: this.nodes[this.selectedNode].entity_id });
|
||||||
|
}
|
||||||
|
|
||||||
_saveEntity() {
|
_saveEntity() {
|
||||||
const data = {
|
const data = {
|
||||||
ignored: this.entityIgnored,
|
ignored: this.entityIgnored,
|
||||||
|
@@ -26,20 +26,18 @@ class OzwLog extends PolymerElement {
|
|||||||
padding-right: 24px;
|
padding-right: 24px;
|
||||||
padding-bottom: 24px;
|
padding-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<ha-config-section is-wide="[[isWide]]">
|
<ha-config-section is-wide="[[isWide]]">
|
||||||
<span slot="header">OZW Log</span>
|
<span slot="header">OZW Log</span>
|
||||||
<paper-card>
|
<paper-card>
|
||||||
<div class="device-picker">
|
<div class="device-picker">
|
||||||
<paper-input label="Number of last log lines." type="number" min="0" max="1000" step="10" value="{{numLogLines}}">
|
<paper-input label="Number of last log lines." type="number" min="0" max="1000" step="10" value="{{_numLogLines}}">
|
||||||
</paper-input>
|
</paper-input>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<paper-button raised="" on-click="refreshLog">Refresh</paper-button>
|
<paper-button raised="true" on-click="_openLogWindow">Load</paper-button>
|
||||||
</div>
|
<paper-button raised="true" on-click="_tailLog" disabled="{{_completeLog}}">Tail</paper-button>
|
||||||
<div class="help-text">
|
|
||||||
<pre>[[ozwLogs]]</pre>
|
|
||||||
</div>
|
|
||||||
</paper-card>
|
</paper-card>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
`;
|
`;
|
||||||
@@ -54,25 +52,53 @@ class OzwLog extends PolymerElement {
|
|||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
ozwLogs: {
|
_ozwLogs: String,
|
||||||
type: String,
|
|
||||||
value: 'Refresh to pull log'
|
_completeLog: {
|
||||||
|
type: Boolean,
|
||||||
|
value: true
|
||||||
},
|
},
|
||||||
|
|
||||||
numLogLines: {
|
_numLogLines: {
|
||||||
type: Number,
|
type: Number,
|
||||||
value: 0
|
value: 0,
|
||||||
|
observer: '_isCompleteLog'
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_intervalId: String,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshLog() {
|
async _tailLog() {
|
||||||
this.ozwLogs = 'Loading ozw log...';
|
const ozwWindow = await this._openLogWindow();
|
||||||
this.hass.callApi('GET', 'zwave/ozwlog?lines=' + this.numLogLines)
|
this.setProperties({
|
||||||
.then((info) => {
|
_intervalId: setInterval(() => { this._refreshLog(ozwWindow); }, 1500) });
|
||||||
this.ozwLogs = info;
|
}
|
||||||
});
|
|
||||||
|
async _openLogWindow() {
|
||||||
|
const info = await this.hass.callApi('GET', 'zwave/ozwlog?lines=' + this._numLogLines);
|
||||||
|
this.setProperties({ _ozwLogs: info });
|
||||||
|
const ozwWindow = window.open('', 'OpenZwave internal log', 'toolbar');
|
||||||
|
ozwWindow.document.title = 'OpenZwave internal logfile';
|
||||||
|
ozwWindow.document.body.innerText = this._ozwLogs;
|
||||||
|
return ozwWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _refreshLog(ozwWindow) {
|
||||||
|
if (ozwWindow.closed === true) {
|
||||||
|
clearInterval(this._intervalId);
|
||||||
|
this.setProperties({ _intervalId: null });
|
||||||
|
} else {
|
||||||
|
const info = await this.hass.callApi('GET', 'zwave/ozwlog?lines=' + this._numLogLines);
|
||||||
|
this.setProperties({ _ozwLogs: info });
|
||||||
|
ozwWindow.document.body.innerText = this._ozwLogs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_isCompleteLog() {
|
||||||
|
if (this._numLogLines !== '0') {
|
||||||
|
this.setProperties({ _completeLog: false });
|
||||||
|
} else { this.setProperties({ _completeLog: true }); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('ozw-log', OzwLog);
|
customElements.define('ozw-log', OzwLog);
|
||||||
|
@@ -1,74 +0,0 @@
|
|||||||
import '@polymer/paper-button/paper-button.js';
|
|
||||||
import '@polymer/paper-card/paper-card.js';
|
|
||||||
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
|
||||||
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
|
||||||
|
|
||||||
class ZwaveNodeInformation extends PolymerElement {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex ha-style">
|
|
||||||
.content {
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-info {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-card {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
max-width: 600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
paper-button[toggles][active] {
|
|
||||||
background: lightgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
<paper-card heading="Node Information">
|
|
||||||
<div class="card-actions">
|
|
||||||
<paper-button toggles="" raised="" noink="" active="{{nodeInfoActive}}">Show</paper-button>
|
|
||||||
</div>
|
|
||||||
<template is="dom-if" if="{{nodeInfoActive}}">
|
|
||||||
<template is="dom-repeat" items="[[selectedNodeAttrs]]" as="state">
|
|
||||||
<div class="node-info">
|
|
||||||
<span>[[state]]</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</paper-card>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
nodes: Array,
|
|
||||||
|
|
||||||
selectedNode: {
|
|
||||||
type: Number,
|
|
||||||
value: -1,
|
|
||||||
observer: 'nodeChanged'
|
|
||||||
},
|
|
||||||
|
|
||||||
selectedNodeAttrs: Array,
|
|
||||||
|
|
||||||
nodeInfoActive: Boolean,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeChanged(selectedNode) {
|
|
||||||
if (!this.nodes || selectedNode === -1) return;
|
|
||||||
const nodeAttrs = this.nodes[this.selectedNode].attributes;
|
|
||||||
const att = [];
|
|
||||||
Object.keys(nodeAttrs).forEach((key) => {
|
|
||||||
att.push(key + ': ' + nodeAttrs[key]);
|
|
||||||
});
|
|
||||||
this.selectedNodeAttrs = att.sort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define('zwave-node-information', ZwaveNodeInformation);
|
|
@@ -9,6 +9,24 @@ import computeStateName from '../../../common/entity/compute_state_name.js';
|
|||||||
|
|
||||||
class HuiGenericEntityRow extends PolymerElement {
|
class HuiGenericEntityRow extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
return html`
|
||||||
|
${this.styleTemplate}
|
||||||
|
<template is="dom-if" if="[[_stateObj]]">
|
||||||
|
${this.stateBadgeTemplate}
|
||||||
|
<div class="flex">
|
||||||
|
${this.infoTemplate}
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template is="dom-if" if="[[!_stateObj]]">
|
||||||
|
<div class="not-found">
|
||||||
|
Entity not available: [[config.entity]]
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
@@ -50,36 +68,36 @@ class HuiGenericEntityRow extends PolymerElement {
|
|||||||
flex: 0 0 40px;
|
flex: 0 0 40px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<template is="dom-if" if="[[_stateObj]]">
|
`;
|
||||||
<state-badge
|
}
|
||||||
state-obj="[[_stateObj]]"
|
|
||||||
override-icon="[[config.icon]]"
|
static get stateBadgeTemplate() {
|
||||||
></state-badge>
|
return html`
|
||||||
<div class="flex">
|
<state-badge
|
||||||
<div class="info">
|
state-obj="[[_stateObj]]"
|
||||||
[[_computeName(config.name, _stateObj)]]
|
override-icon="[[config.icon]]"
|
||||||
<template is="dom-if" if="[[config.secondary_info]]">
|
></state-badge>
|
||||||
<template is="dom-if" if="[[_equals(config.secondary_info, 'entity-id')]]">
|
`;
|
||||||
<div class="secondary">
|
}
|
||||||
[[_stateObj.entity_id]]
|
|
||||||
</div>
|
static get infoTemplate() {
|
||||||
</template>
|
return html`
|
||||||
<template is="dom-if" if="[[_equals(config.secondary_info, 'last-changed')]]">
|
<div class="info">
|
||||||
<ha-relative-time
|
[[_computeName(config.name, _stateObj)]]
|
||||||
hass="[[hass]]"
|
<template is="dom-if" if="[[config.secondary_info]]">
|
||||||
datetime="[[_stateObj.last_changed]]"
|
<template is="dom-if" if="[[_equals(config.secondary_info, 'entity-id')]]">
|
||||||
></ha-relative-time>
|
<div class="secondary">
|
||||||
</template>
|
[[_stateObj.entity_id]]
|
||||||
</template>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
<slot></slot>
|
<template is="dom-if" if="[[_equals(config.secondary_info, 'last-changed')]]">
|
||||||
</div>
|
<ha-relative-time
|
||||||
</template>
|
hass="[[hass]]"
|
||||||
<template is="dom-if" if="[[!_stateObj]]">
|
datetime="[[_stateObj.last_changed]]"
|
||||||
<div class="not-found">
|
></ha-relative-time>
|
||||||
Entity not available: [[config.entity]]
|
</template>
|
||||||
</div>
|
</template>
|
||||||
</template>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,21 +6,33 @@ import '../components/hui-generic-entity-row.js';
|
|||||||
|
|
||||||
class HuiClimateEntityRow extends PolymerElement {
|
class HuiClimateEntityRow extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
return html`
|
||||||
|
${this.styleTemplate}
|
||||||
|
<hui-generic-entity-row
|
||||||
|
hass="[[hass]]"
|
||||||
|
config="[[_config]]"
|
||||||
|
>
|
||||||
|
${this.climateControlTemplate}
|
||||||
|
</hui-generic-entity-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-climate-state {
|
ha-climate-state {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<hui-generic-entity-row
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get climateControlTemplate() {
|
||||||
|
return html`
|
||||||
|
<ha-climate-state
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
state-obj="[[_stateObj]]"
|
||||||
>
|
></ha-climate-state>
|
||||||
<ha-climate-state
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[_stateObj]]"
|
|
||||||
></ha-climate-state>
|
|
||||||
</hui-generic-entity-row>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,6 +8,18 @@ import CoverEntity from '../../../util/cover-model.js';
|
|||||||
|
|
||||||
class HuiCoverEntityRow extends PolymerElement {
|
class HuiCoverEntityRow extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
return html`
|
||||||
|
${this.styleTemplate}
|
||||||
|
<hui-generic-entity-row
|
||||||
|
hass="[[hass]]"
|
||||||
|
config="[[_config]]"
|
||||||
|
>
|
||||||
|
${this.coverControlTemplate}
|
||||||
|
</hui-generic-entity-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
ha-cover-controls,
|
ha-cover-controls,
|
||||||
@@ -15,17 +27,17 @@ class HuiCoverEntityRow extends PolymerElement {
|
|||||||
margin-right: -.57em;
|
margin-right: -.57em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<hui-generic-entity-row
|
`;
|
||||||
hass="[[hass]]"
|
}
|
||||||
config="[[_config]]"
|
|
||||||
>
|
static get coverControlTemplate() {
|
||||||
<template is="dom-if" if="[[!_entityObj.isTiltOnly]]">
|
return html`
|
||||||
<ha-cover-controls hass="[[hass]]" state-obj="[[_stateObj]]"></ha-cover-controls>
|
<template is="dom-if" if="[[!_entityObj.isTiltOnly]]">
|
||||||
</template>
|
<ha-cover-controls hass="[[hass]]" state-obj="[[_stateObj]]"></ha-cover-controls>
|
||||||
<template is="dom-if" if="[[_entityObj.isTiltOnly]]">
|
</template>
|
||||||
<ha-cover-tilt-controls hass="[[hass]]" state-obj="[[_stateObj]]"></ha-cover-tilt-controls>
|
<template is="dom-if" if="[[_entityObj.isTiltOnly]]">
|
||||||
</template>
|
<ha-cover-tilt-controls hass="[[hass]]" state-obj="[[_stateObj]]"></ha-cover-tilt-controls>
|
||||||
</hui-generic-entity-row>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,21 +18,27 @@ class HuiGroupEntityRow extends LocalizeMixin(PolymerElement) {
|
|||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
<template is="dom-if" if="[[_canToggle]]">
|
${this.groupControlTemplate}
|
||||||
<ha-entity-toggle
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[_stateObj]]"
|
|
||||||
></ha-entity-toggle>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[!_canToggle]]">
|
|
||||||
<div>
|
|
||||||
[[_computeState(_stateObj)]]
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get groupControlTemplate() {
|
||||||
|
return html`
|
||||||
|
<template is="dom-if" if="[[_canToggle]]">
|
||||||
|
<ha-entity-toggle
|
||||||
|
hass="[[hass]]"
|
||||||
|
state-obj="[[_stateObj]]"
|
||||||
|
></ha-entity-toggle>
|
||||||
|
</template>
|
||||||
|
<template is="dom-if" if="[[!_canToggle]]">
|
||||||
|
<div>
|
||||||
|
[[_computeState(_stateObj)]]
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
|
@@ -9,6 +9,19 @@ import '../components/hui-generic-entity-row.js';
|
|||||||
|
|
||||||
class HuiInputNumberEntityRow extends mixinBehaviors([IronResizableBehavior], PolymerElement) {
|
class HuiInputNumberEntityRow extends mixinBehaviors([IronResizableBehavior], PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
return html`
|
||||||
|
${this.styleTemplate}
|
||||||
|
<hui-generic-entity-row
|
||||||
|
hass="[[hass]]"
|
||||||
|
config="[[_config]]"
|
||||||
|
id="input_number_card"
|
||||||
|
>
|
||||||
|
${this.inputNumberControlTemplate}
|
||||||
|
</hui-generic-entity-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
.flex {
|
.flex {
|
||||||
@@ -23,41 +36,40 @@ class HuiInputNumberEntityRow extends mixinBehaviors([IronResizableBehavior], Po
|
|||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<hui-generic-entity-row
|
`;
|
||||||
hass="[[hass]]"
|
}
|
||||||
config="[[_config]]"
|
|
||||||
id="input_number_card"
|
static get inputNumberControlTemplate() {
|
||||||
>
|
return html`
|
||||||
<div>
|
<div>
|
||||||
<template is="dom-if" if="[[_equals(_stateObj.attributes.mode, 'slider')]]">
|
<template is="dom-if" if="[[_equals(_stateObj.attributes.mode, 'slider')]]">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<paper-slider
|
<paper-slider
|
||||||
min="[[_min]]"
|
|
||||||
max="[[_max]]"
|
|
||||||
value="{{_value}}"
|
|
||||||
step="[[_step]]"
|
|
||||||
pin
|
|
||||||
on-change="_selectedValueChanged"
|
|
||||||
ignore-bar-touch
|
|
||||||
></paper-slider>
|
|
||||||
<span class="state">[[_value]] [[_stateObj.attributes.unit_of_measurement]]</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[_equals(_stateObj.attributes.mode, 'box')]]">
|
|
||||||
<paper-input
|
|
||||||
no-label-float
|
|
||||||
auto-validate
|
|
||||||
pattern="[0-9]+([\\.][0-9]+)?"
|
|
||||||
step="[[_step]]"
|
|
||||||
min="[[_min]]"
|
min="[[_min]]"
|
||||||
max="[[_max]]"
|
max="[[_max]]"
|
||||||
value="{{_value}}"
|
value="{{_value}}"
|
||||||
type="number"
|
step="[[_step]]"
|
||||||
|
pin
|
||||||
on-change="_selectedValueChanged"
|
on-change="_selectedValueChanged"
|
||||||
></paper-input>
|
ignore-bar-touch
|
||||||
</template>
|
></paper-slider>
|
||||||
</div>
|
<span class="state">[[_value]] [[_stateObj.attributes.unit_of_measurement]]</span>
|
||||||
</hui-generic-entity-row>
|
</div>
|
||||||
|
</template>
|
||||||
|
<template is="dom-if" if="[[_equals(_stateObj.attributes.mode, 'box')]]">
|
||||||
|
<paper-input
|
||||||
|
no-label-float
|
||||||
|
auto-validate
|
||||||
|
pattern="[0-9]+([\\.][0-9]+)?"
|
||||||
|
step="[[_step]]"
|
||||||
|
min="[[_min]]"
|
||||||
|
max="[[_max]]"
|
||||||
|
value="{{_value}}"
|
||||||
|
type="number"
|
||||||
|
on-change="_selectedValueChanged"
|
||||||
|
></paper-input>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,21 +16,7 @@ import EventsMixin from '../../../mixins/events-mixin.js';
|
|||||||
class HuiInputSelectEntityRow extends EventsMixin(PolymerElement) {
|
class HuiInputSelectEntityRow extends EventsMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
${this.styleTemplate}
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
paper-dropdown-menu {
|
|
||||||
margin-left: 16px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.not-found {
|
|
||||||
flex: 1;
|
|
||||||
background-color: yellow;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<template is="dom-if" if="[[_stateObj]]">
|
<template is="dom-if" if="[[_stateObj]]">
|
||||||
<state-badge state-obj="[[_stateObj]]"></state-badge>
|
<state-badge state-obj="[[_stateObj]]"></state-badge>
|
||||||
<paper-dropdown-menu on-click="_stopPropagation" selected-item-label="{{_selected}}" label="[[_computeName(_config.name, _stateObj)]]">
|
<paper-dropdown-menu on-click="_stopPropagation" selected-item-label="{{_selected}}" label="[[_computeName(_config.name, _stateObj)]]">
|
||||||
@@ -49,6 +35,26 @@ class HuiInputSelectEntityRow extends EventsMixin(PolymerElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
paper-dropdown-menu {
|
||||||
|
margin-left: 16px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.not-found {
|
||||||
|
flex: 1;
|
||||||
|
background-color: yellow;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
|
@@ -11,21 +11,27 @@ class HuiInputTextEntityRow extends PolymerElement {
|
|||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
<paper-input
|
${this.inputTextControlTemplate}
|
||||||
no-label-float
|
|
||||||
minlength="[[_stateObj.attributes.min]]"
|
|
||||||
maxlength="[[_stateObj.attributes.max]]"
|
|
||||||
value="{{_value}}"
|
|
||||||
auto-validate="[[_stateObj.attributes.pattern]]"
|
|
||||||
pattern="[[_stateObj.attributes.pattern]]"
|
|
||||||
type="[[_stateObj.attributes.mode]]"
|
|
||||||
on-change="_selectedValueChanged"
|
|
||||||
placeholder="(empty value)"
|
|
||||||
></paper-input>
|
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get inputTextControlTemplate() {
|
||||||
|
return html`
|
||||||
|
<paper-input
|
||||||
|
no-label-float
|
||||||
|
minlength="[[_stateObj.attributes.min]]"
|
||||||
|
maxlength="[[_stateObj.attributes.max]]"
|
||||||
|
value="{{_value}}"
|
||||||
|
auto-validate="[[_stateObj.attributes.pattern]]"
|
||||||
|
pattern="[[_stateObj.attributes.pattern]]"
|
||||||
|
type="[[_stateObj.attributes.mode]]"
|
||||||
|
on-change="_selectedValueChanged"
|
||||||
|
placeholder="(empty value)"
|
||||||
|
></paper-input>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
|
@@ -11,6 +11,18 @@ import LocalizeMixin from '../../../mixins/localize-mixin.js';
|
|||||||
*/
|
*/
|
||||||
class HuiLockEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiLockEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
return html`
|
||||||
|
${this.styleTemplate}
|
||||||
|
<hui-generic-entity-row
|
||||||
|
hass="[[hass]]"
|
||||||
|
config="[[_config]]"
|
||||||
|
>
|
||||||
|
${this.lockControlTemplate}
|
||||||
|
</hui-generic-entity-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
paper-button {
|
paper-button {
|
||||||
@@ -19,14 +31,14 @@ class HuiLockEntityRow extends LocalizeMixin(PolymerElement) {
|
|||||||
margin-right: -.57em;
|
margin-right: -.57em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<hui-generic-entity-row
|
`;
|
||||||
hass="[[hass]]"
|
}
|
||||||
config="[[_config]]"
|
|
||||||
>
|
static get lockControlTemplate() {
|
||||||
<paper-button on-click="_callService">
|
return html`
|
||||||
[[_computeButtonTitle(_stateObj.state)]]
|
<paper-button on-click="_callService">
|
||||||
</paper-button>
|
[[_computeButtonTitle(_stateObj.state)]]
|
||||||
</hui-generic-entity-row>
|
</paper-button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,6 +11,18 @@ import LocalizeMixin from '../../../mixins/localize-mixin.js';
|
|||||||
*/
|
*/
|
||||||
class HuiSceneEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiSceneEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
return html`
|
||||||
|
${this.styleTemplate}
|
||||||
|
<hui-generic-entity-row
|
||||||
|
hass="[[hass]]"
|
||||||
|
config="[[_config]]"
|
||||||
|
>
|
||||||
|
${this.sceneControlTemplate}
|
||||||
|
</hui-generic-entity-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
paper-button {
|
paper-button {
|
||||||
@@ -19,14 +31,14 @@ class HuiSceneEntityRow extends LocalizeMixin(PolymerElement) {
|
|||||||
margin-right: -.57em;
|
margin-right: -.57em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<hui-generic-entity-row
|
`;
|
||||||
hass="[[hass]]"
|
}
|
||||||
config="[[_config]]"
|
|
||||||
>
|
static get sceneControlTemplate() {
|
||||||
<paper-button on-click="_callService">
|
return html`
|
||||||
[[localize('ui.card.scene.activate')]]
|
<paper-button on-click="_callService">
|
||||||
</paper-button>
|
[[localize('ui.card.scene.activate')]]
|
||||||
</hui-generic-entity-row>
|
</paper-button>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,6 +12,18 @@ import LocalizeMixin from '../../../mixins/localize-mixin.js';
|
|||||||
*/
|
*/
|
||||||
class HuiScriptEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiScriptEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
return html`
|
||||||
|
${this.styleTemplate}
|
||||||
|
<hui-generic-entity-row
|
||||||
|
hass="[[hass]]"
|
||||||
|
config="[[_config]]"
|
||||||
|
>
|
||||||
|
${this.scriptControlTemplate}
|
||||||
|
</hui-generic-entity-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
paper-button {
|
paper-button {
|
||||||
@@ -20,17 +32,17 @@ class HuiScriptEntityRow extends LocalizeMixin(PolymerElement) {
|
|||||||
margin-right: -.57em;
|
margin-right: -.57em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<hui-generic-entity-row
|
`;
|
||||||
hass="[[hass]]"
|
}
|
||||||
config="[[_config]]"
|
|
||||||
>
|
static get scriptControlTemplate() {
|
||||||
<template is="dom-if" if="[[_stateObj.attributes.can_cancel]]">
|
return html`
|
||||||
<ha-entity-toggle state-obj="[[_stateObj]]" hass="[[hass]]"></ha-entity-toggle>
|
<template is="dom-if" if="[[_stateObj.attributes.can_cancel]]">
|
||||||
</template>
|
<ha-entity-toggle state-obj="[[_stateObj]]" hass="[[hass]]"></ha-entity-toggle>
|
||||||
<template is="dom-if" if="[[!_stateObj.attributes.can_cancel]]">
|
</template>
|
||||||
<paper-button on-click="_callService">[[localize('ui.card.script.execute')]]</paper-button>
|
<template is="dom-if" if="[[!_stateObj.attributes.can_cancel]]">
|
||||||
</template>
|
<paper-button on-click="_callService">[[localize('ui.card.script.execute')]]</paper-button>
|
||||||
</hui-generic-entity-row>
|
</template>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,20 +12,32 @@ import LocalizeMixin from '../../../mixins/localize-mixin.js';
|
|||||||
*/
|
*/
|
||||||
class HuiTextEntityRow extends LocalizeMixin(PolymerElement) {
|
class HuiTextEntityRow extends LocalizeMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
return html`
|
||||||
|
${this.styleTemplate}
|
||||||
|
<hui-generic-entity-row
|
||||||
|
hass="[[hass]]"
|
||||||
|
config="[[_config]]"
|
||||||
|
>
|
||||||
|
${this.textControlTemplate}
|
||||||
|
</hui-generic-entity-row>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
div {
|
div {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<hui-generic-entity-row
|
`;
|
||||||
hass="[[hass]]"
|
}
|
||||||
config="[[_config]]"
|
|
||||||
>
|
static get textControlTemplate() {
|
||||||
<div>
|
return html`
|
||||||
[[_computeState(_stateObj)]]
|
<div>
|
||||||
</div>
|
[[_computeState(_stateObj)]]
|
||||||
</hui-generic-entity-row>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,13 +13,19 @@ class HuiTimerEntityRow extends PolymerElement {
|
|||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
<div>
|
${this.timerControlTemplate}
|
||||||
[[_computeDisplay(_stateObj, _timeRemaining)]]
|
|
||||||
</div>
|
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get timerControlTemplate() {
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
[[_computeDisplay(_stateObj, _timeRemaining)]]
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
|
@@ -18,21 +18,27 @@ class HuiToggleEntityRow extends LocalizeMixin(PolymerElement) {
|
|||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config="[[_config]]"
|
config="[[_config]]"
|
||||||
>
|
>
|
||||||
<template is="dom-if" if="[[_canToggle]]">
|
${this.toggleControlTemplate}
|
||||||
<ha-entity-toggle
|
|
||||||
hass="[[hass]]"
|
|
||||||
state-obj="[[_stateObj]]"
|
|
||||||
></ha-entity-toggle>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[!_canToggle]]">
|
|
||||||
<div>
|
|
||||||
[[_computeState(_stateObj)]]
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</hui-generic-entity-row>
|
</hui-generic-entity-row>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get toggleControlTemplate() {
|
||||||
|
return html`
|
||||||
|
<template is="dom-if" if="[[_canToggle]]">
|
||||||
|
<ha-entity-toggle
|
||||||
|
hass="[[hass]]"
|
||||||
|
state-obj="[[_stateObj]]"
|
||||||
|
></ha-entity-toggle>
|
||||||
|
</template>
|
||||||
|
<template is="dom-if" if="[[!_canToggle]]">
|
||||||
|
<div>
|
||||||
|
[[_computeState(_stateObj)]]
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
|
@@ -7,6 +7,19 @@ import callService from '../common/call-service.js';
|
|||||||
|
|
||||||
class HuiCallServiceRow extends PolymerElement {
|
class HuiCallServiceRow extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
return html`
|
||||||
|
${this.styleTemplate}
|
||||||
|
<ha-icon icon="[[_config.icon]]"></ha-icon>
|
||||||
|
<div class="flex">
|
||||||
|
<div>
|
||||||
|
[[_config.name]]
|
||||||
|
</div>
|
||||||
|
<paper-button on-click="_callService">[[_config.action_name]]</paper-button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
:host {
|
:host {
|
||||||
@@ -36,13 +49,6 @@ class HuiCallServiceRow extends PolymerElement {
|
|||||||
margin-right: -.57em;
|
margin-right: -.57em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<ha-icon icon="[[_config.icon]]"></ha-icon>
|
|
||||||
<div class="flex">
|
|
||||||
<div>
|
|
||||||
[[_config.name]]
|
|
||||||
</div>
|
|
||||||
<paper-button on-click="_callService">[[_config.action_name]]</paper-button>
|
|
||||||
</div>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,6 +5,18 @@ import '../../../components/ha-icon.js';
|
|||||||
|
|
||||||
class HuiWeblinkRow extends PolymerElement {
|
class HuiWeblinkRow extends PolymerElement {
|
||||||
static get template() {
|
static get template() {
|
||||||
|
return html`
|
||||||
|
${this.styleTemplate}
|
||||||
|
<a href="[[_config.url]]">
|
||||||
|
<ha-icon icon="[[_config.icon]]"></ha-icon>
|
||||||
|
<div>
|
||||||
|
[[_config.name]]
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styleTemplate() {
|
||||||
return html`
|
return html`
|
||||||
<style>
|
<style>
|
||||||
a {
|
a {
|
||||||
@@ -24,12 +36,6 @@ class HuiWeblinkRow extends PolymerElement {
|
|||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<a href="[[_config.url]]">
|
|
||||||
<ha-icon icon="[[_config.icon]]"></ha-icon>
|
|
||||||
<div>
|
|
||||||
[[_config.name]]
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
158
src/panels/mailbox/ha-dialog-show-audio-message.js
Normal file
158
src/panels/mailbox/ha-dialog-show-audio-message.js
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
import '@polymer/paper-button/paper-button.js';
|
||||||
|
import '@polymer/paper-dialog/paper-dialog.js';
|
||||||
|
import '@polymer/paper-spinner/paper-spinner.js';
|
||||||
|
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
||||||
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
||||||
|
|
||||||
|
import '../../resources/ha-style.js';
|
||||||
|
|
||||||
|
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @appliesMixin LocalizeMixin
|
||||||
|
*/
|
||||||
|
class HaDialogShowAudioMessage extends LocalizeMixin(PolymerElement) {
|
||||||
|
static get template() {
|
||||||
|
return html`
|
||||||
|
<style include="ha-style-dialog">
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
@media all and (max-width: 500px) {
|
||||||
|
paper-dialog {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-height: calc(100% - 64px);
|
||||||
|
|
||||||
|
position: fixed !important;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
overflow: scroll;
|
||||||
|
border-bottom-left-radius: 0px;
|
||||||
|
border-bottom-right-radius: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
paper-dialog {
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
paper-dialog p {
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-dialog id="mp3dialog" with-backdrop opened="{{_opened}}" on-opened-changed="_openedChanged">
|
||||||
|
<h2>
|
||||||
|
[[localize('ui.panel.mailbox.playback_title')]]
|
||||||
|
<div class='icon'>
|
||||||
|
<template is="dom-if" if="[[_loading]]">
|
||||||
|
<paper-spinner active></paper-spinner>
|
||||||
|
</template>
|
||||||
|
<template is="dom-if" if="[[!_loading]]">
|
||||||
|
<paper-icon-button
|
||||||
|
on-click='openDeleteDialog'
|
||||||
|
icon='hass:delete'
|
||||||
|
></paper-icon-button>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</h2>
|
||||||
|
<div id="transcribe"></div>
|
||||||
|
<div>
|
||||||
|
<template is="dom-if" if="[[_errorMsg]]">
|
||||||
|
<div class='error'>[[_errorMsg]]</div>
|
||||||
|
</template>
|
||||||
|
<audio id="mp3" preload="none" controls> <source id="mp3src" src="" type="audio/mpeg" /></audio>
|
||||||
|
</div>
|
||||||
|
</paper-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: Object,
|
||||||
|
|
||||||
|
_currentMessage: Object,
|
||||||
|
|
||||||
|
// Error message when can't talk to server etc
|
||||||
|
_errorMsg: String,
|
||||||
|
|
||||||
|
_loading: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
_opened: {
|
||||||
|
type: Boolean,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
showDialog({ hass, message }) {
|
||||||
|
this.hass = hass;
|
||||||
|
this._loading = true;
|
||||||
|
this._errorMsg = null;
|
||||||
|
this._currentMessage = message;
|
||||||
|
this._opened = true;
|
||||||
|
this.$.transcribe.innerText = message.message;
|
||||||
|
const platform = message.platform;
|
||||||
|
const mp3 = this.$.mp3;
|
||||||
|
mp3.src = null;
|
||||||
|
const url = `/api/mailbox/media/${platform}/${message.sha}`;
|
||||||
|
this.hass.fetchWithAuth(url)
|
||||||
|
.then((response) => {
|
||||||
|
if (response.ok) {
|
||||||
|
return response.blob();
|
||||||
|
}
|
||||||
|
return Promise.reject({
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then((blob) => {
|
||||||
|
this._loading = false;
|
||||||
|
mp3.src = window.URL.createObjectURL(blob);
|
||||||
|
mp3.play();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
this._loading = false;
|
||||||
|
this._errorMsg = `Error loading audio: ${err.statusText}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openDeleteDialog() {
|
||||||
|
if (confirm(this.localize('ui.panel.mailbox.delete_prompt'))) {
|
||||||
|
this.deleteSelected();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteSelected() {
|
||||||
|
const msg = this._currentMessage;
|
||||||
|
this.hass.callApi('DELETE', `mailbox/delete/${msg.platform}/${msg.sha}`);
|
||||||
|
this._dialogDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
_dialogDone() {
|
||||||
|
this.$.mp3.pause();
|
||||||
|
this.setProperties({
|
||||||
|
_currentMessage: null,
|
||||||
|
_errorMsg: null,
|
||||||
|
_loading: false,
|
||||||
|
_opened: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_openedChanged(ev) {
|
||||||
|
// Closed dialog by clicking on the overlay
|
||||||
|
// Check against dialogClosedCallback to make sure we didn't change
|
||||||
|
// programmatically
|
||||||
|
if (!ev.detail.value) {
|
||||||
|
this._dialogDone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
customElements.define('ha-dialog-show-audio-message', HaDialogShowAudioMessage);
|
@@ -3,7 +3,6 @@ import '@polymer/app-layout/app-header/app-header.js';
|
|||||||
import '@polymer/app-layout/app-toolbar/app-toolbar.js';
|
import '@polymer/app-layout/app-toolbar/app-toolbar.js';
|
||||||
import '@polymer/paper-button/paper-button.js';
|
import '@polymer/paper-button/paper-button.js';
|
||||||
import '@polymer/paper-card/paper-card.js';
|
import '@polymer/paper-card/paper-card.js';
|
||||||
import '@polymer/paper-dialog/paper-dialog.js';
|
|
||||||
import '@polymer/paper-input/paper-textarea.js';
|
import '@polymer/paper-input/paper-textarea.js';
|
||||||
import '@polymer/paper-item/paper-item-body.js';
|
import '@polymer/paper-item/paper-item-body.js';
|
||||||
import '@polymer/paper-item/paper-item.js';
|
import '@polymer/paper-item/paper-item.js';
|
||||||
@@ -17,6 +16,8 @@ import '../../resources/ha-style.js';
|
|||||||
import formatDateTime from '../../common/datetime/format_date_time.js';
|
import formatDateTime from '../../common/datetime/format_date_time.js';
|
||||||
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
||||||
|
|
||||||
|
let registeredDialog = false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
*/
|
*/
|
||||||
@@ -57,32 +58,8 @@ class HaPanelMailbox extends LocalizeMixin(PolymerElement) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
paper-dialog {
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
paper-dialog p {
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
#mp3dialog paper-icon-button {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (max-width: 450px) {
|
@media all and (max-width: 450px) {
|
||||||
paper-dialog {
|
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
max-height: calc(100% - 64px);
|
|
||||||
|
|
||||||
position: fixed !important;
|
|
||||||
bottom: 0px;
|
|
||||||
left: 0px;
|
|
||||||
right: 0px;
|
|
||||||
overflow: scroll;
|
|
||||||
border-bottom-left-radius: 0px;
|
|
||||||
border-bottom-right-radius: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@@ -128,28 +105,6 @@ class HaPanelMailbox extends LocalizeMixin(PolymerElement) {
|
|||||||
</paper-card>
|
</paper-card>
|
||||||
</div>
|
</div>
|
||||||
</app-header-layout>
|
</app-header-layout>
|
||||||
|
|
||||||
<paper-dialog with-backdrop id="mp3dialog" on-iron-overlay-closed="_mp3Closed">
|
|
||||||
<h2>
|
|
||||||
[[localize('ui.panel.mailbox.playback_title')]]
|
|
||||||
<paper-icon-button
|
|
||||||
on-click='openDeleteDialog'
|
|
||||||
icon='hass:delete'
|
|
||||||
></paper-icon-button>
|
|
||||||
</h2>
|
|
||||||
<div id="transcribe"></div>
|
|
||||||
<div>
|
|
||||||
<audio id="mp3" preload="none" controls> <source id="mp3src" src="" type="audio/mpeg" /></audio>
|
|
||||||
</div>
|
|
||||||
</paper-dialog>
|
|
||||||
|
|
||||||
<paper-dialog with-backdrop id="confirmdel">
|
|
||||||
<p>[[localize('ui.panel.mailbox.delete_prompt')]]</p>
|
|
||||||
<div class="buttons">
|
|
||||||
<paper-button dialog-dismiss>[[localize('ui.common.cancel')]]</paper-button>
|
|
||||||
<paper-button dialog-confirm autofocus on-click="deleteSelected">[[localize('ui.panel.mailbox.delete_button')]]</paper-button>
|
|
||||||
</div>
|
|
||||||
</paper-dialog>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,15 +131,19 @@ class HaPanelMailbox extends LocalizeMixin(PolymerElement) {
|
|||||||
_messages: {
|
_messages: {
|
||||||
type: Array,
|
type: Array,
|
||||||
},
|
},
|
||||||
|
|
||||||
currentMessage: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
|
if (!registeredDialog) {
|
||||||
|
registeredDialog = true;
|
||||||
|
this.fire('register-dialog', {
|
||||||
|
dialogShowEvent: 'show-audio-message-dialog',
|
||||||
|
dialogTag: 'ha-dialog-show-audio-message',
|
||||||
|
dialogImport: () => import('./ha-dialog-show-audio-message.js'),
|
||||||
|
});
|
||||||
|
}
|
||||||
this.hassChanged = this.hassChanged.bind(this);
|
this.hassChanged = this.hassChanged.bind(this);
|
||||||
this.hass.connection.subscribeEvents(this.hassChanged, 'mailbox_updated')
|
this.hass.connection.subscribeEvents(this.hassChanged, 'mailbox_updated')
|
||||||
.then(function (unsub) { this._unsubEvents = unsub; }.bind(this));
|
.then(function (unsub) { this._unsubEvents = unsub; }.bind(this));
|
||||||
@@ -209,35 +168,19 @@ class HaPanelMailbox extends LocalizeMixin(PolymerElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openMP3Dialog(event) {
|
openMP3Dialog(event) {
|
||||||
var platform = event.model.item.platform;
|
this.fire('show-audio-message-dialog', {
|
||||||
this.currentMessage = event.model.item;
|
hass: this.hass,
|
||||||
this.$.mp3dialog.open();
|
message: event.model.item,
|
||||||
this.$.mp3src.src = '/api/mailbox/media/' + platform + '/' + event.model.item.sha;
|
});
|
||||||
this.$.transcribe.innerText = event.model.item.message;
|
|
||||||
this.$.mp3.load();
|
|
||||||
this.$.mp3.play();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_mp3Closed() {
|
|
||||||
this.$.mp3.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
openDeleteDialog() {
|
|
||||||
this.$.confirmdel.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteSelected() {
|
|
||||||
var msg = this.currentMessage;
|
|
||||||
this.hass.callApi('DELETE', 'mailbox/delete/' + msg.platform + '/' + msg.sha);
|
|
||||||
this.$.mp3dialog.close();
|
|
||||||
}
|
|
||||||
getMessages() {
|
getMessages() {
|
||||||
const items = this.platforms.map(function (platform) {
|
const items = this.platforms.map(function (platform) {
|
||||||
return this.hass.callApi('GET', 'mailbox/messages/' + platform).then(function (values) {
|
return this.hass.callApi('GET', `mailbox/messages/${platform}`).then(function (values) {
|
||||||
var platformItems = [];
|
const platformItems = [];
|
||||||
var arrayLength = values.length;
|
const arrayLength = values.length;
|
||||||
for (var i = 0; i < arrayLength; i++) {
|
for (let i = 0; i < arrayLength; i++) {
|
||||||
var datetime = formatDateTime(new Date(values[i].info.origtime * 1000));
|
const datetime = formatDateTime(new Date(values[i].info.origtime * 1000));
|
||||||
platformItems.push({
|
platformItems.push({
|
||||||
timestamp: datetime,
|
timestamp: datetime,
|
||||||
caller: values[i].info.callerid,
|
caller: values[i].info.callerid,
|
||||||
@@ -251,15 +194,9 @@ class HaPanelMailbox extends LocalizeMixin(PolymerElement) {
|
|||||||
});
|
});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
return Promise.all(items).then(function (platformItems) {
|
return Promise.all(items).then(function (platformItems) {
|
||||||
var arrayLength = items.length;
|
return [].concat(...platformItems).sort(function (a, b) {
|
||||||
var final = [];
|
|
||||||
for (var i = 0; i < arrayLength; i++) {
|
|
||||||
final = final.concat(platformItems[i]);
|
|
||||||
}
|
|
||||||
final.sort(function (a, b) {
|
|
||||||
return new Date(b.timestamp) - new Date(a.timestamp);
|
return new Date(b.timestamp) - new Date(a.timestamp);
|
||||||
});
|
});
|
||||||
return final;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
127
src/panels/profile/ha-long-lived-access-tokens-card.js
Normal file
127
src/panels/profile/ha-long-lived-access-tokens-card.js
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import '@polymer/paper-button/paper-button.js';
|
||||||
|
|
||||||
|
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
||||||
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
||||||
|
import EventsMixin from '../../mixins/events-mixin.js';
|
||||||
|
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
||||||
|
import formatDateTime from '../../common/datetime/format_date_time.js';
|
||||||
|
|
||||||
|
import '../../resources/ha-style.js';
|
||||||
|
|
||||||
|
import './ha-settings-row.js';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @appliesMixin EventsMixin
|
||||||
|
* @appliesMixin LocalizeMixin
|
||||||
|
*/
|
||||||
|
class HaLongLivedTokens extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||||
|
static get template() {
|
||||||
|
return html`
|
||||||
|
<style include="ha-style">
|
||||||
|
paper-card {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.card-content {
|
||||||
|
margin: -1em 0;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
paper-icon-button {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-card heading="[[localize('ui.panel.profile.long_lived_access_tokens.header')]]">
|
||||||
|
<div class="card-content">
|
||||||
|
<p>
|
||||||
|
[[localize('ui.panel.profile.long_lived_access_tokens.description')]]
|
||||||
|
<a href='https://developers.home-assistant.io/docs/en/auth_api.html#making-authenticated-requests' target='_blank'>
|
||||||
|
[[localize('ui.panel.profile.long_lived_access_tokens.learn_auth_requests')]]
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<template is='dom-if' if='[[!_tokens.length]]'>
|
||||||
|
<p>[[localize('ui.panel.profile.long_lived_access_tokens.empty_state')]]</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<template is='dom-repeat' items='[[_tokens]]'>
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot='heading'>[[item.client_name]]</span>
|
||||||
|
<span slot='description'>[[_formatCreatedAt(item.created_at)]]</span>
|
||||||
|
<paper-icon-button icon="hass:delete" on-click='_handleDelete'></paper-icon-button>
|
||||||
|
</ha-settings-row>
|
||||||
|
</template>
|
||||||
|
<div class='card-actions'>
|
||||||
|
<paper-button on-click='_handleCreate'>
|
||||||
|
[[localize('ui.panel.profile.long_lived_access_tokens.create')]]
|
||||||
|
</paper-button>
|
||||||
|
</div>
|
||||||
|
</paper-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: Object,
|
||||||
|
refreshTokens: Array,
|
||||||
|
_tokens: {
|
||||||
|
type: Array,
|
||||||
|
computed: '_computeTokens(refreshTokens)'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_computeTokens(refreshTokens) {
|
||||||
|
return refreshTokens.filter(tkn => tkn.type === 'long_lived_access_token').reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
_formatTitle(name) {
|
||||||
|
return this.localize(
|
||||||
|
'ui.panel.profile.long_lived_access_tokens.token_title',
|
||||||
|
'name', name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_formatCreatedAt(created) {
|
||||||
|
return this.localize(
|
||||||
|
'ui.panel.profile.long_lived_access_tokens.created_at',
|
||||||
|
'date', formatDateTime(new Date(created))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleCreate() {
|
||||||
|
const name = prompt(this.localize('ui.panel.profile.long_lived_access_tokens.prompt_name'));
|
||||||
|
if (!name) return;
|
||||||
|
try {
|
||||||
|
const token = await this.hass.callWS({
|
||||||
|
type: 'auth/long_lived_access_token',
|
||||||
|
lifespan: 3650,
|
||||||
|
client_name: name,
|
||||||
|
});
|
||||||
|
prompt(this.localize('ui.panel.profile.long_lived_access_tokens.prompt_copy_token'), token);
|
||||||
|
this.fire('hass-refresh-tokens');
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(err);
|
||||||
|
alert(this.localize('ui.panel.profile.long_lived_access_tokens.create_failed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleDelete(ev) {
|
||||||
|
if (!confirm(this.localize('ui.panel.profile.long_lived_access_tokens.confirm_delete', 'name', ev.model.item.client_name))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.hass.callWS({
|
||||||
|
type: 'auth/delete_refresh_token',
|
||||||
|
refresh_token_id: ev.model.item.id,
|
||||||
|
});
|
||||||
|
this.fire('hass-refresh-tokens');
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(err);
|
||||||
|
alert(this.localize('ui.panel.profile.long_lived_access_tokens.delete_failed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('ha-long-lived-access-tokens-card', HaLongLivedTokens);
|
@@ -15,6 +15,9 @@ import EventsMixin from '../../mixins/events-mixin.js';
|
|||||||
|
|
||||||
import './ha-change-password-card.js';
|
import './ha-change-password-card.js';
|
||||||
import './ha-mfa-modules-card.js';
|
import './ha-mfa-modules-card.js';
|
||||||
|
import './ha-refresh-tokens-card.js';
|
||||||
|
import './ha-long-lived-access-tokens-card.js';
|
||||||
|
|
||||||
import './ha-pick-language-row.js';
|
import './ha-pick-language-row.js';
|
||||||
import './ha-pick-theme-row.js';
|
import './ha-pick-theme-row.js';
|
||||||
import './ha-push-notifications-row.js';
|
import './ha-push-notifications-row.js';
|
||||||
@@ -84,7 +87,22 @@ class HaPanelProfile extends EventsMixin(PolymerElement) {
|
|||||||
<ha-change-password-card hass="[[hass]]"></ha-change-password-card>
|
<ha-change-password-card hass="[[hass]]"></ha-change-password-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<ha-mfa-modules-card hass='[[hass]]' mfa-modules='[[hass.user.mfa_modules]]'></ha-mfa-modules-card>
|
<ha-mfa-modules-card
|
||||||
|
hass='[[hass]]'
|
||||||
|
mfa-modules='[[hass.user.mfa_modules]]'
|
||||||
|
></ha-mfa-modules-card>
|
||||||
|
|
||||||
|
<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>
|
</div>
|
||||||
</app-header-layout>
|
</app-header-layout>
|
||||||
`;
|
`;
|
||||||
@@ -95,9 +113,21 @@ class HaPanelProfile extends EventsMixin(PolymerElement) {
|
|||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
narrow: Boolean,
|
||||||
showMenu: Boolean,
|
showMenu: Boolean,
|
||||||
|
_refreshTokens: Array,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this._refreshRefreshTokens();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _refreshRefreshTokens() {
|
||||||
|
this._refreshTokens = await this.hass.callWS({
|
||||||
|
type: 'auth/refresh_tokens'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_handleLogOut() {
|
_handleLogOut() {
|
||||||
this.fire('hass-logout');
|
this.fire('hass-logout');
|
||||||
}
|
}
|
||||||
|
82
src/panels/profile/ha-refresh-tokens-card.js
Normal file
82
src/panels/profile/ha-refresh-tokens-card.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import '@polymer/paper-icon-button/paper-icon-button.js';
|
||||||
|
|
||||||
|
import { html } from '@polymer/polymer/lib/utils/html-tag.js';
|
||||||
|
import { PolymerElement } from '@polymer/polymer/polymer-element.js';
|
||||||
|
import EventsMixin from '../../mixins/events-mixin.js';
|
||||||
|
import LocalizeMixin from '../../mixins/localize-mixin.js';
|
||||||
|
import formatDateTime from '../../common/datetime/format_date_time.js';
|
||||||
|
|
||||||
|
import './ha-settings-row.js';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @appliesMixin EventsMixin
|
||||||
|
* @appliesMixin LocalizeMixin
|
||||||
|
*/
|
||||||
|
class HaRefreshTokens extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||||
|
static get template() {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
paper-card {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
paper-icon-button {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<paper-card heading="[[localize('ui.panel.profile.refresh_tokens.header')]]">
|
||||||
|
<div class="card-content">[[localize('ui.panel.profile.refresh_tokens.description')]]</div>
|
||||||
|
<template is='dom-repeat' items='[[_computeTokens(refreshTokens)]]'>
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot='heading'>[[_formatTitle(item.client_id)]]</span>
|
||||||
|
<span slot='description'>[[_formatCreatedAt(item.created_at)]]</span>
|
||||||
|
<paper-icon-button icon="hass:delete" on-click='_handleDelete'></paper-icon-button>
|
||||||
|
</ha-settings-row>
|
||||||
|
</template>
|
||||||
|
</paper-card>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
static get properties() {
|
||||||
|
return {
|
||||||
|
hass: Object,
|
||||||
|
refreshTokens: Array,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_computeTokens(refreshTokens) {
|
||||||
|
return refreshTokens.filter(tkn => tkn.type === 'normal').reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
_formatTitle(clientId) {
|
||||||
|
return this.localize(
|
||||||
|
'ui.panel.profile.refresh_tokens.token_title',
|
||||||
|
'clientId', clientId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_formatCreatedAt(created) {
|
||||||
|
return this.localize(
|
||||||
|
'ui.panel.profile.refresh_tokens.created_at',
|
||||||
|
'date', formatDateTime(new Date(created))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleDelete(ev) {
|
||||||
|
if (!confirm(this.localize('ui.panel.profile.refresh_tokens.confirm_delete', 'name', ev.model.item.client_id))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.hass.callWS({
|
||||||
|
type: 'auth/delete_refresh_token',
|
||||||
|
refresh_token_id: ev.model.item.id,
|
||||||
|
});
|
||||||
|
this.fire('hass-refresh-tokens');
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(err);
|
||||||
|
alert(this.localize('ui.panel.profile.refresh_tokens.delete_failed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('ha-refresh-tokens-card', HaRefreshTokens);
|
@@ -746,6 +746,27 @@
|
|||||||
"error_no_theme": "No themes available.",
|
"error_no_theme": "No themes available.",
|
||||||
"link_promo": "Learn about themes",
|
"link_promo": "Learn about themes",
|
||||||
"dropdown_label": "Theme"
|
"dropdown_label": "Theme"
|
||||||
|
},
|
||||||
|
"refresh_tokens": {
|
||||||
|
"header": "Refresh Tokens",
|
||||||
|
"description": "Each refresh token represents a login session. Refresh tokens will be automatically removed when you click log out. Below a list of refresh tokens that are currently active for your account.",
|
||||||
|
"token_title": "Refresh token for {clientId}",
|
||||||
|
"created_at": "Created at {date}",
|
||||||
|
"confirm_delete": "Are you sure you want to delete the refresh token for {name}?",
|
||||||
|
"delete_failed": "Failed to delete the refresh token."
|
||||||
|
},
|
||||||
|
"long_lived_access_tokens": {
|
||||||
|
"header": "Long-Lived Access Tokens",
|
||||||
|
"description": "Create long-lived access tokens to allow your scripts to interact with your Home Assistant instance. Each token will be valid for 10 years from creation. The following long-lived access tokens are currently active.",
|
||||||
|
"learn_auth_requests": "Learn how to make authenticated requests.",
|
||||||
|
"created_at": "Created at {date}",
|
||||||
|
"confirm_delete": "Are you sure you want to delete the access token for {name}?",
|
||||||
|
"delete_failed": "Failed to delete the access token.",
|
||||||
|
"create": "Create Token",
|
||||||
|
"create_failed": "Failed to create the access token.",
|
||||||
|
"prompt_name": "Name?",
|
||||||
|
"prompt_copy_token": "Copy your access token. It will not be shown again.",
|
||||||
|
"empty_state": "You have no long-lived access tokens yet."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"shopping-list": {
|
"shopping-list": {
|
||||||
|
9
src/util/fetch-with-auth.js
Normal file
9
src/util/fetch-with-auth.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export const fetchWithAuth = async (auth, input, init = {}) => {
|
||||||
|
if (auth.expired) await auth.refreshAccessToken();
|
||||||
|
init.credentials = 'same-origin';
|
||||||
|
if (!init.headers) {
|
||||||
|
init.headers = {};
|
||||||
|
}
|
||||||
|
init.headers.authorization = `Bearer ${auth.accessToken}`;
|
||||||
|
return await fetch(input, init);
|
||||||
|
};
|
@@ -1,52 +1,57 @@
|
|||||||
export default function hassCallApi(host, auth, method, path, parameters) {
|
import { fetchWithAuth } from './fetch-with-auth.js';
|
||||||
var url = host + '/api/' + path;
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
/* eslint-disable no-throw-literal */
|
||||||
var req = new XMLHttpRequest();
|
|
||||||
req.open(method, url, true);
|
|
||||||
req.setRequestHeader('authorization', `Bearer ${auth.accessToken}`);
|
|
||||||
|
|
||||||
req.onload = function () {
|
export default async function hassCallApi(auth, method, path, parameters) {
|
||||||
let body = req.responseText;
|
const url = `${auth.data.hassUrl}/api/${path}`;
|
||||||
const contentType = req.getResponseHeader('content-type');
|
|
||||||
|
|
||||||
if (contentType && contentType.indexOf('application/json') !== -1) {
|
const init = {
|
||||||
try {
|
method: method,
|
||||||
body = JSON.parse(req.responseText);
|
headers: {},
|
||||||
} catch (err) {
|
};
|
||||||
reject({
|
|
||||||
error: 'Unable to parse JSON response',
|
|
||||||
status_code: req.status,
|
|
||||||
body: body,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.status > 199 && req.status < 300) {
|
if (parameters) {
|
||||||
resolve(body);
|
init.headers['Content-Type'] = 'application/json;charset=UTF-8';
|
||||||
} else {
|
init.body = JSON.stringify(parameters);
|
||||||
reject({
|
}
|
||||||
error: 'Response error: ' + req.status,
|
|
||||||
status_code: req.status,
|
let response;
|
||||||
body: body
|
|
||||||
});
|
try {
|
||||||
}
|
response = await fetchWithAuth(auth, url, init);
|
||||||
|
} catch (err) {
|
||||||
|
throw {
|
||||||
|
error: 'Request error',
|
||||||
|
status_code: undefined,
|
||||||
|
body: undefined,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
req.onerror = function () {
|
let body = null;
|
||||||
reject({
|
|
||||||
error: 'Request error',
|
|
||||||
status_code: req.status,
|
|
||||||
body: req.responseText,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (parameters) {
|
const contentType = response.headers.get('content-type');
|
||||||
req.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
|
|
||||||
req.send(JSON.stringify(parameters));
|
if (contentType && contentType.includes('application/json')) {
|
||||||
} else {
|
try {
|
||||||
req.send();
|
body = await response.json();
|
||||||
|
} catch (err) {
|
||||||
|
throw {
|
||||||
|
error: 'Unable to parse JSON response',
|
||||||
|
status_code: err.status,
|
||||||
|
body: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
body = await response.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw {
|
||||||
|
error: `Response error: ${response.status}`,
|
||||||
|
status_code: response.status,
|
||||||
|
body: body
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return body;
|
||||||
}
|
}
|
||||||
|
@@ -562,7 +562,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_auth": "Невалидно потребителско име или парола"
|
"invalid_auth": "Невалидно потребителско име или парола",
|
||||||
|
"invalid_code": "Невалиден код за аутентикация"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
|
@@ -550,7 +550,7 @@
|
|||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Inicializuji",
|
"initializing": "Inicializuji",
|
||||||
"authorizing_client": "Chystáte se dát {clientId} práva pro Home Assistant instanci.",
|
"authorizing_client": "Chystáte se dát {clientId} práva pro Home Assistant instanci.",
|
||||||
"logging_in_with": "Přihlásit se pomocí ** {authProviderName} **.",
|
"logging_in_with": "Přihlásit se pomocí **{authProviderName}**.",
|
||||||
"pick_auth_provider": "Nebo se přihlaste s",
|
"pick_auth_provider": "Nebo se přihlaste s",
|
||||||
"abort_intro": "Přihlášení bylo zrušeno",
|
"abort_intro": "Přihlášení bylo zrušeno",
|
||||||
"form": {
|
"form": {
|
||||||
|
@@ -550,7 +550,7 @@
|
|||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Initialiserer",
|
"initializing": "Initialiserer",
|
||||||
"authorizing_client": "Du er ved at give {clientId} adgang til din Home Assistant-instans.",
|
"authorizing_client": "Du er ved at give {clientId} adgang til din Home Assistant-instans.",
|
||||||
"logging_in_with": "Log ind med ** {authProviderName} **.",
|
"logging_in_with": "Log ind med **{authProviderName}**.",
|
||||||
"pick_auth_provider": "Eller log ind med",
|
"pick_auth_provider": "Eller log ind med",
|
||||||
"abort_intro": "Login afbrudt",
|
"abort_intro": "Login afbrudt",
|
||||||
"form": {
|
"form": {
|
||||||
@@ -569,7 +569,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "To-faktor godkendelseskode"
|
"code": "To-faktor godkendelseskode"
|
||||||
},
|
},
|
||||||
"description": "Åbn ** {mfa_module_name} ** på din enhed for at se din to-faktor godkendelseskode og bekræft din identitet:"
|
"description": "Åbn **{mfa_module_name}** på din enhed for at se din to-faktor godkendelseskode og bekræft din identitet:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -590,9 +590,9 @@
|
|||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"data": {
|
"data": {
|
||||||
"code": "To faktor godkendelseskode"
|
"code": "To-faktor godkendelseskode"
|
||||||
},
|
},
|
||||||
"description": "Åbn ** {mfa_module_name} ** på din enhed for at se din to-faktor godkendelseskode og bekræft din identitet:"
|
"description": "Åbn **{mfa_module_name}** på din enhed for at se din to-faktor godkendelseskode og bekræft din identitet:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -601,7 +601,7 @@
|
|||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"no_api_password_set": "Du har ikke konfigureret en API-adgangskode.",
|
"no_api_password_set": "Du har ikke konfigureret en API-adgangskode.",
|
||||||
"login_expired": "Sessionen er udløbet, log venligst ind igen"
|
"login_expired": "Session er udløbet, log ind igen."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
|
@@ -545,6 +545,27 @@
|
|||||||
"error_no_theme": "No themes available.",
|
"error_no_theme": "No themes available.",
|
||||||
"link_promo": "Learn about themes",
|
"link_promo": "Learn about themes",
|
||||||
"dropdown_label": "Theme"
|
"dropdown_label": "Theme"
|
||||||
|
},
|
||||||
|
"refresh_tokens": {
|
||||||
|
"header": "Refresh Tokens",
|
||||||
|
"description": "Each refresh token represents a login session. Refresh tokens will be automatically removed when you click log out. Below a list of refresh tokens that are currently active for your account.",
|
||||||
|
"token_title": "Refresh token for {clientId}",
|
||||||
|
"created_at": "Created at {date}",
|
||||||
|
"confirm_delete": "Are you sure you want to delete the refresh token for {name}?",
|
||||||
|
"delete_failed": "Failed to delete the refresh token."
|
||||||
|
},
|
||||||
|
"long_lived_access_tokens": {
|
||||||
|
"header": "Long-Lived Access Tokens",
|
||||||
|
"description": "Create long-lived access tokens to allow your scripts to interact with your Home Assistant instance. Each token will be valid for 10 years from creation. The following long-lived access tokens are currently active.",
|
||||||
|
"learn_auth_requests": "Learn how to make authenticated requests.",
|
||||||
|
"created_at": "Created at {date}",
|
||||||
|
"confirm_delete": "Are you sure you want to delete the access token for {name}?",
|
||||||
|
"delete_failed": "Failed to delete the access token.",
|
||||||
|
"create": "Create Token",
|
||||||
|
"create_failed": "Failed to create the access token.",
|
||||||
|
"prompt_name": "Name?",
|
||||||
|
"prompt_copy_token": "Copy your access token. It will not be shown again.",
|
||||||
|
"empty_state": "You have no long-lived access tokens yet."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
|
@@ -550,7 +550,7 @@
|
|||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Inicializando",
|
"initializing": "Inicializando",
|
||||||
"authorizing_client": "Está por dar acceso a {clientId} a su instancia de Home Assistant.",
|
"authorizing_client": "Está por dar acceso a {clientId} a su instancia de Home Assistant.",
|
||||||
"logging_in_with": "Iniciando sesión con ** {authProviderName} **.",
|
"logging_in_with": "Iniciando sesión con **{authProviderName}**.",
|
||||||
"pick_auth_provider": "O inicia sesión con",
|
"pick_auth_provider": "O inicia sesión con",
|
||||||
"abort_intro": "Inicio de sesión cancelado",
|
"abort_intro": "Inicio de sesión cancelado",
|
||||||
"form": {
|
"form": {
|
||||||
@@ -590,18 +590,18 @@
|
|||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"data": {
|
"data": {
|
||||||
"code": "Código de autenticado de dos factores"
|
"code": "Código de autenticación de dos factores"
|
||||||
},
|
},
|
||||||
"description": "Abra el **{mfa_module_name}** en su dispositivo para ver su código de autenticar de dos factores y verificar su identidad:"
|
"description": "Abra el **{mfa_module_name}** en su dispositivo para ver su código de autenticar de dos factores y verificar su identidad:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_auth": "Contraseña API inválida",
|
"invalid_auth": "Contraseña API inválida",
|
||||||
"invalid_code": "Código de autenticado invalido"
|
"invalid_code": "Código de autenticación inválido"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"no_api_password_set": "No tienes una contraseña API configurada.",
|
"no_api_password_set": "No tienes una contraseña API configurada.",
|
||||||
"login_expired": "La sesión ha expirado, por favor inicie sesión nuevamente."
|
"login_expired": "La sesión expiró, por favor inicie sesión nuevamente."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
|
@@ -550,7 +550,7 @@
|
|||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Inicializando",
|
"initializing": "Inicializando",
|
||||||
"authorizing_client": "Está por dar acceso a {clientId} a su instancia de Home Assistant.",
|
"authorizing_client": "Está por dar acceso a {clientId} a su instancia de Home Assistant.",
|
||||||
"logging_in_with": "Iniciando sesión con ** {authProviderName} **.",
|
"logging_in_with": "Iniciando sesión con **{authProviderName}**.",
|
||||||
"pick_auth_provider": "O inicia sesión con",
|
"pick_auth_provider": "O inicia sesión con",
|
||||||
"abort_intro": "Inicio de sesión cancelado",
|
"abort_intro": "Inicio de sesión cancelado",
|
||||||
"form": {
|
"form": {
|
||||||
@@ -590,18 +590,18 @@
|
|||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"data": {
|
"data": {
|
||||||
"code": "Código de autenticar de dos factores"
|
"code": "Código de autenticado de dos factores"
|
||||||
},
|
},
|
||||||
"description": "Abra el **{mfa_module_name}** en su dispositivo para ver su código autenticado de dos factores y verificar su identidad:"
|
"description": "Abra el **{mfa_module_name}** en su dispositivo para ver su código autenticado de dos factores y verificar su identidad:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_auth": "Contraseña de API inválida",
|
"invalid_auth": "Contraseña de API inválida",
|
||||||
"invalid_code": "Código de verificación invalido"
|
"invalid_code": "Código de autenticación inválido"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"no_api_password_set": "No tienes una contraseña de API configurada.",
|
"no_api_password_set": "No tienes una contraseña de API configurada.",
|
||||||
"login_expired": "Sesión vencida, por favor iniciar sesión de nuevo."
|
"login_expired": "La sesión expiró, por favor inicie sesión de nuevo."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
|
@@ -546,6 +546,95 @@
|
|||||||
"link_promo": "Lisateave teemade kohta",
|
"link_promo": "Lisateave teemade kohta",
|
||||||
"dropdown_label": "Teema"
|
"dropdown_label": "Teema"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"page-authorize": {
|
||||||
|
"initializing": "Lähtestan",
|
||||||
|
"authorizing_client": "Kavatsed anda {clientId} jaoks juurdepääsu oma Home Assistant serverile.",
|
||||||
|
"logging_in_with": "Login sisse **{authProviderName}** abil.",
|
||||||
|
"pick_auth_provider": "Või logi sisse, kasutades",
|
||||||
|
"abort_intro": "Sisselogimine katkestatud",
|
||||||
|
"form": {
|
||||||
|
"working": "Palun oota",
|
||||||
|
"unknown_error": "Midagi läks valesti",
|
||||||
|
"providers": {
|
||||||
|
"homeassistant": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"username": "Kasutajanimi",
|
||||||
|
"password": "Salasõna"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mfa": {
|
||||||
|
"data": {
|
||||||
|
"code": "Kaheastmeline autentimiskood"
|
||||||
|
},
|
||||||
|
"description": "Ava oma seadmes **{mfa_module_name}**, et näha oma kahetasemelise autentimise koodi ja tõendada oma isikut:"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_auth": "Vale kasutajanimi või salasõna",
|
||||||
|
"invalid_code": "Vigane autentimiskood"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"login_expired": "Sessioon aegus, palun logi uuesti sisse."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"legacy_api_password": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"password": "API salasõna"
|
||||||
|
},
|
||||||
|
"description": "Palun seadista http seadetes oma API salasõna:"
|
||||||
|
},
|
||||||
|
"mfa": {
|
||||||
|
"data": {
|
||||||
|
"code": "Kaheastmeline autentimiskood"
|
||||||
|
},
|
||||||
|
"description": "Ava oma seadmes **{mfa_module_name}**, et näha oma kahetasemelise autentimise koodi ja tõendada oma isikut:"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_auth": "Vale API salasõna",
|
||||||
|
"invalid_code": "Vigane autentimiskood"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"no_api_password_set": "Sul pole API salasõna seadistatud",
|
||||||
|
"login_expired": "Sessioon aegus, palun logi uuesti sisse."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"trusted_networks": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"user": "Kasutaja"
|
||||||
|
},
|
||||||
|
"description": "Palun vali kasutaja, kellena soovid sisse logida:"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"not_whitelisted": "Sinu arvuti ei ole lubatute nimekirjas."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"page-onboarding": {
|
||||||
|
"intro": "Kas oled valmis oma kodu ellu äratama, oma privaatsust tagasi võitma ja ühinema ülemaailmse nokitsejate kogukonnaga?",
|
||||||
|
"user": {
|
||||||
|
"intro": "Alustame kasutajakonto loomisega.",
|
||||||
|
"required_field": "Nõutud",
|
||||||
|
"data": {
|
||||||
|
"name": "Nimi",
|
||||||
|
"username": "Kasutajanimi",
|
||||||
|
"password": "Salasõna"
|
||||||
|
},
|
||||||
|
"create_account": "Loo konto",
|
||||||
|
"error": {
|
||||||
|
"required_fields": "Täida kõik nõutud väljad"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
@@ -710,6 +799,11 @@
|
|||||||
"ask": "Kas soovid selle sisselogimise salvestada?",
|
"ask": "Kas soovid selle sisselogimise salvestada?",
|
||||||
"decline": "Tänan ei",
|
"decline": "Tänan ei",
|
||||||
"confirm": "Salvesta sisselogimine"
|
"confirm": "Salvesta sisselogimine"
|
||||||
|
},
|
||||||
|
"notification_drawer": {
|
||||||
|
"click_to_configure": "{entity} seadistamiseks klõpsa nuppu",
|
||||||
|
"empty": "Teavitusi pole",
|
||||||
|
"title": "Teavitused"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain": {
|
"domain": {
|
||||||
|
@@ -569,7 +569,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "Code d'authentification à deux facteurs"
|
"code": "Code d'authentification à deux facteurs"
|
||||||
},
|
},
|
||||||
"description": "Ouvrez le ** {mfa_module_name} ** sur votre appareil pour afficher votre code d'authentification à deux facteurs et vérifier votre identité:"
|
"description": "Ouvrez le **{mfa_module_name}** sur votre appareil pour afficher votre code d'authentification à deux facteurs et vérifier votre identité:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -592,7 +592,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "Code d'authentification à deux facteurs"
|
"code": "Code d'authentification à deux facteurs"
|
||||||
},
|
},
|
||||||
"description": "Ouvrez le ** {mfa_module_name} ** sur votre appareil pour afficher votre code d'authentification à deux facteurs et vérifier votre identité:"
|
"description": "Ouvrez le **{mfa_module_name}** sur votre appareil pour afficher votre code d'authentification à deux facteurs et vérifier votre identité:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
"config": "Konfigurasi",
|
"config": "Konfigurasi",
|
||||||
"states": "Ikhtisar",
|
"states": "Ikhtisar",
|
||||||
"map": "Peta",
|
"map": "Peta",
|
||||||
"logbook": "Buku Catatan",
|
"logbook": "Catatan Log",
|
||||||
"history": "Riwayat",
|
"history": "Riwayat",
|
||||||
"mailbox": "Kotak pesan",
|
"mailbox": "Kotak pesan",
|
||||||
"shopping_list": "Daftar belanja",
|
"shopping_list": "Daftar belanja",
|
||||||
@@ -72,8 +72,8 @@
|
|||||||
"on": "Tidak aman"
|
"on": "Tidak aman"
|
||||||
},
|
},
|
||||||
"presence": {
|
"presence": {
|
||||||
"off": "di luar",
|
"off": "Keluar",
|
||||||
"on": "di rumah"
|
"on": "Rumah"
|
||||||
},
|
},
|
||||||
"battery": {
|
"battery": {
|
||||||
"off": "Normal",
|
"off": "Normal",
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
"locked": "Terkunci",
|
"locked": "Terkunci",
|
||||||
"unlocked": "Terbuka",
|
"unlocked": "Terbuka",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"problem": "Malasah"
|
"problem": "Masalah"
|
||||||
},
|
},
|
||||||
"input_boolean": {
|
"input_boolean": {
|
||||||
"off": "Off",
|
"off": "Off",
|
||||||
@@ -183,6 +183,7 @@
|
|||||||
"on": "On",
|
"on": "On",
|
||||||
"playing": "Memainkan",
|
"playing": "Memainkan",
|
||||||
"paused": "Jeda",
|
"paused": "Jeda",
|
||||||
|
"idle": "Diam",
|
||||||
"standby": "Siaga"
|
"standby": "Siaga"
|
||||||
},
|
},
|
||||||
"plant": {
|
"plant": {
|
||||||
@@ -231,6 +232,13 @@
|
|||||||
"snowy-rainy": "Bersalju, hujan",
|
"snowy-rainy": "Bersalju, hujan",
|
||||||
"sunny": "Cerah",
|
"sunny": "Cerah",
|
||||||
"windy": "Berangin"
|
"windy": "Berangin"
|
||||||
|
},
|
||||||
|
"vacuum": {
|
||||||
|
"cleaning": "Membersihkan",
|
||||||
|
"error": "Kesalahan",
|
||||||
|
"idle": "Siaga",
|
||||||
|
"paused": "Berhenti",
|
||||||
|
"returning": "Kembali ke dock"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"state_badge": {
|
"state_badge": {
|
||||||
@@ -334,13 +342,15 @@
|
|||||||
"to": "Ke"
|
"to": "Ke"
|
||||||
},
|
},
|
||||||
"homeassistant": {
|
"homeassistant": {
|
||||||
|
"label": "Home Assistant",
|
||||||
"event": "Event:",
|
"event": "Event:",
|
||||||
"start": "Mulai",
|
"start": "Mulai",
|
||||||
"shutdown": "Matikan"
|
"shutdown": "Matikan"
|
||||||
},
|
},
|
||||||
"mqtt": {
|
"mqtt": {
|
||||||
"label": "MQTT",
|
"label": "MQTT",
|
||||||
"topic": "Topik"
|
"topic": "Topik",
|
||||||
|
"payload": "Payload (opsional)"
|
||||||
},
|
},
|
||||||
"numeric_state": {
|
"numeric_state": {
|
||||||
"label": "Status nomor",
|
"label": "Status nomor",
|
||||||
@@ -460,6 +470,122 @@
|
|||||||
"zwave": {
|
"zwave": {
|
||||||
"caption": "Z-Wave",
|
"caption": "Z-Wave",
|
||||||
"description": "Kelola jaringan Z-Wave anda"
|
"description": "Kelola jaringan Z-Wave anda"
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"caption": "Pengguna",
|
||||||
|
"description": "Kelola pengguna",
|
||||||
|
"picker": {
|
||||||
|
"title": "Pengguna"
|
||||||
|
},
|
||||||
|
"editor": {
|
||||||
|
"rename_user": "Ubah nama pengguna",
|
||||||
|
"change_password": "Ganti kata sandi",
|
||||||
|
"activate_user": "Aktifkan pengguna",
|
||||||
|
"deactivate_user": "Nonaktifkan pengguna",
|
||||||
|
"delete_user": "Hapus pengguna"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profile": {
|
||||||
|
"push_notifications": {
|
||||||
|
"header": "Pemberitahuan push",
|
||||||
|
"description": "Kirim pemberitahuan ke perangkat ini.",
|
||||||
|
"error_load_platform": "Konfigurasi notify.html5.",
|
||||||
|
"error_use_https": "Membutuhkan SSL diaktifkan untuk frontend.",
|
||||||
|
"push_notifications": "Pemberitahuan push",
|
||||||
|
"link_promo": "Pelajari lebih lanjut"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"header": "Bahasa",
|
||||||
|
"link_promo": "Bantu menerjemahkan",
|
||||||
|
"dropdown_label": "Bahasa"
|
||||||
|
},
|
||||||
|
"themes": {
|
||||||
|
"header": "Tema",
|
||||||
|
"error_no_theme": "Tidak ada tema yang tersedia.",
|
||||||
|
"link_promo": "Pelajari tentang tema",
|
||||||
|
"dropdown_label": "Tema"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"page-authorize": {
|
||||||
|
"initializing": "Inisialisasi",
|
||||||
|
"authorizing_client": "Anda akan memberi akses {clientId} ke Home Assistant Anda.",
|
||||||
|
"logging_in_with": "Login dengan **{authProviderName}**.",
|
||||||
|
"pick_auth_provider": "Atau login dengan",
|
||||||
|
"abort_intro": "Login dibatalkan",
|
||||||
|
"form": {
|
||||||
|
"working": "Mohon tunggu",
|
||||||
|
"unknown_error": "Ada yang salah",
|
||||||
|
"providers": {
|
||||||
|
"homeassistant": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"username": "Nama pengguna",
|
||||||
|
"password": "Kata sandi"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mfa": {
|
||||||
|
"data": {
|
||||||
|
"code": "Kode Autentikasi Dua Faktor"
|
||||||
|
},
|
||||||
|
"description": "Buka ** {mfa_module_name} ** pada perangkat Anda untuk melihat kode otentikasi dua-faktor Anda dan verifikasi identitas Anda:"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_auth": "Username dan password salah",
|
||||||
|
"invalid_code": "Kode autentikasi tidak valid"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"login_expired": "Sesi kedaluwarsa, harap masuk lagi."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"legacy_api_password": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"password": "Kata Sandi API"
|
||||||
|
},
|
||||||
|
"description": "Silakan masukkan kata sandi API di konfigurasi http Anda:"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"invalid_auth": "Kata sandi API tidak valid"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"no_api_password_set": "Anda tidak memiliki kata sandi API yang dikonfigurasi."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"trusted_networks": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"user": "Pengguna"
|
||||||
|
},
|
||||||
|
"description": "Silakan pilih pengguna yang ingin login sebagai:"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"not_whitelisted": "Komputer Anda tidak masuk daftar putih."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"page-onboarding": {
|
||||||
|
"intro": "Apakah Anda siap untuk membuat rumah Anda lebih hidup, merebut kembali privasi Anda dan bergabung dengan komunitas tinkerers dunia?",
|
||||||
|
"user": {
|
||||||
|
"intro": "Mari kita mulai dengan membuat akun pengguna.",
|
||||||
|
"required_field": "Wajib",
|
||||||
|
"data": {
|
||||||
|
"name": "Nama",
|
||||||
|
"username": "Username",
|
||||||
|
"password": "Kata sandi"
|
||||||
|
},
|
||||||
|
"create_account": "Buat Akun",
|
||||||
|
"error": {
|
||||||
|
"required_fields": "Isi semua bidang yang wajib diisi"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -515,6 +641,51 @@
|
|||||||
"w": "B"
|
"w": "B"
|
||||||
},
|
},
|
||||||
"forecast": "Ramalan cuaca"
|
"forecast": "Ramalan cuaca"
|
||||||
|
},
|
||||||
|
"alarm_control_panel": {
|
||||||
|
"code": "Kode",
|
||||||
|
"clear_code": "Hapus"
|
||||||
|
},
|
||||||
|
"automation": {
|
||||||
|
"last_triggered": "Terakhir terpicu",
|
||||||
|
"trigger": "Pemicu"
|
||||||
|
},
|
||||||
|
"cover": {
|
||||||
|
"position": "Posisi",
|
||||||
|
"tilt_position": "Posisi kemiringan"
|
||||||
|
},
|
||||||
|
"fan": {
|
||||||
|
"speed": "Kecepatan",
|
||||||
|
"direction": "Arah"
|
||||||
|
},
|
||||||
|
"light": {
|
||||||
|
"brightness": "Kecerahan",
|
||||||
|
"color_temperature": "Temperatur warna",
|
||||||
|
"white_value": "Nilai putih",
|
||||||
|
"effect": "Efek"
|
||||||
|
},
|
||||||
|
"climate": {
|
||||||
|
"on_off": "Hidup \/ mati",
|
||||||
|
"target_temperature": "Target suhu",
|
||||||
|
"target_humidity": "Target kelembaban",
|
||||||
|
"swing_mode": "Mode ayunan"
|
||||||
|
},
|
||||||
|
"lock": {
|
||||||
|
"lock": "Kunci",
|
||||||
|
"unlock": "Membuka"
|
||||||
|
},
|
||||||
|
"media_player": {
|
||||||
|
"source": "Sumber",
|
||||||
|
"sound_mode": "Mode suara"
|
||||||
|
},
|
||||||
|
"vacuum": {
|
||||||
|
"actions": {
|
||||||
|
"resume_cleaning": "Lanjutkan pembersihan",
|
||||||
|
"return_to_base": "Kembali ke dock",
|
||||||
|
"start_cleaning": "Mulai membersihkan",
|
||||||
|
"turn_on": "Nyalakan",
|
||||||
|
"turn_off": "Matikan"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
@@ -527,6 +698,9 @@
|
|||||||
"service": "Layanan"
|
"service": "Layanan"
|
||||||
},
|
},
|
||||||
"relative_time": {
|
"relative_time": {
|
||||||
|
"past": "lalu",
|
||||||
|
"future": "dalam",
|
||||||
|
"never": "Tak pernah",
|
||||||
"duration": {
|
"duration": {
|
||||||
"second": "{count} {count, plural,\n one {detik}\n other {detik}\n}",
|
"second": "{count} {count, plural,\n one {detik}\n other {detik}\n}",
|
||||||
"minute": "{count} {count, plural,\n one {menit}\n other {menit}\n}",
|
"minute": "{count} {count, plural,\n one {menit}\n other {menit}\n}",
|
||||||
@@ -534,15 +708,35 @@
|
|||||||
"day": "{count} {count, plural,\n one {hari}\n other {hari}\n}",
|
"day": "{count} {count, plural,\n one {hari}\n other {hari}\n}",
|
||||||
"week": "{count} {count, plural,\n one {minggu}\n other {minggu}\n}"
|
"week": "{count} {count, plural,\n one {minggu}\n other {minggu}\n}"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"history_charts": {
|
||||||
|
"loading_history": "Memuat riwayat status ...",
|
||||||
|
"no_history_found": "Tidak ada riwayat status yang ditemukan."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notification_toast": {
|
"notification_toast": {
|
||||||
|
"entity_turned_on": "{entity} sudah dinyalakan.",
|
||||||
|
"entity_turned_off": "{entity} sudah dimatikan.",
|
||||||
|
"service_called": "Layanan {service} dipanggil.",
|
||||||
|
"service_call_failed": "Gagal memanggil layanan {service} .",
|
||||||
"connection_lost": "Koneksi terputus. Menghubungan kembali ..."
|
"connection_lost": "Koneksi terputus. Menghubungan kembali ..."
|
||||||
},
|
},
|
||||||
|
"dialogs": {
|
||||||
|
"more_info_settings": {
|
||||||
|
"save": "Simpan",
|
||||||
|
"name": "Nama",
|
||||||
|
"entity_id": "Entiti ID"
|
||||||
|
}
|
||||||
|
},
|
||||||
"auth_store": {
|
"auth_store": {
|
||||||
"ask": "Apakah Anda ingin menyimpan login ini?",
|
"ask": "Apakah Anda ingin menyimpan login ini?",
|
||||||
"decline": "Tidak, terima kasih",
|
"decline": "Tidak, terima kasih",
|
||||||
"confirm": "Simpan login"
|
"confirm": "Simpan login"
|
||||||
|
},
|
||||||
|
"notification_drawer": {
|
||||||
|
"click_to_configure": "Klik tombol untuk mengonfigurasi {entity}",
|
||||||
|
"empty": "Tidak ada pemberitahuan",
|
||||||
|
"title": "Pemberitahuan"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain": {
|
"domain": {
|
||||||
@@ -577,7 +771,8 @@
|
|||||||
"sun": "Matahari",
|
"sun": "Matahari",
|
||||||
"switch": "Sakelar",
|
"switch": "Sakelar",
|
||||||
"updater": "Updater",
|
"updater": "Updater",
|
||||||
"zwave": "Z-Wave"
|
"zwave": "Z-Wave",
|
||||||
|
"vacuum": "Vakum"
|
||||||
},
|
},
|
||||||
"attribute": {
|
"attribute": {
|
||||||
"weather": {
|
"weather": {
|
||||||
|
@@ -550,7 +550,7 @@
|
|||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Inizializzazione",
|
"initializing": "Inizializzazione",
|
||||||
"authorizing_client": "Stai per dare accesso {clientId} alla tua istanza di Assistente Home.",
|
"authorizing_client": "Stai per dare accesso {clientId} alla tua istanza di Assistente Home.",
|
||||||
"logging_in_with": "Accesso con ** {authProviderName} **.",
|
"logging_in_with": "Accesso con **{authProviderName}**.",
|
||||||
"pick_auth_provider": "Oppure accedi con",
|
"pick_auth_provider": "Oppure accedi con",
|
||||||
"abort_intro": "Login interrotto",
|
"abort_intro": "Login interrotto",
|
||||||
"form": {
|
"form": {
|
||||||
@@ -569,7 +569,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "Codice di autenticazione a due fattori"
|
"code": "Codice di autenticazione a due fattori"
|
||||||
},
|
},
|
||||||
"description": "Apri ** {mfa_module_name} ** sul tuo dispositivo per visualizzare il codice di autenticazione a due fattori e verificare la tua identità:"
|
"description": "Apri il **{mfa_module_name}** sul tuo dispositivo per visualizzare il codice di autenticazione a due fattori e verificare la tua identità:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -592,7 +592,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "Codice di autenticazione a due fattori"
|
"code": "Codice di autenticazione a due fattori"
|
||||||
},
|
},
|
||||||
"description": "Apri il **{mfa_module_name}** sul tuo dispositivo per vedere il codice di autenticazione a due fattori (2FA) e verifica la tua identità:"
|
"description": "Apri il **{mfa_module_name}** sul tuo dispositivo per visualizzare il codice di autenticazione a due fattori e verificare la tua identità:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -601,7 +601,7 @@
|
|||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"no_api_password_set": "Non hai una password API configurata.",
|
"no_api_password_set": "Non hai una password API configurata.",
|
||||||
"login_expired": "Sessione scaduta, effettua nuovamente l'accesso."
|
"login_expired": "Sessione scaduta, effettua nuovamente il login."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
|
@@ -569,7 +569,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "2 단계 인증 코드"
|
"code": "2 단계 인증 코드"
|
||||||
},
|
},
|
||||||
"description": "2 단계 인증 코드 및 신원을 확인하기 위해 기기에서 ** {mfa_module_name} ** 을(를) 열어주세요:"
|
"description": "2 단계 인증 코드 및 신원을 확인하기 위해 기기에서 **{mfa_module_name}** 을(를) 열어주세요:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -586,13 +586,13 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"password": "API 비밀번호"
|
"password": "API 비밀번호"
|
||||||
},
|
},
|
||||||
"description": "http config 에 API 암호를 입력해주세요:"
|
"description": "configuration.yaml 에 설정한 api_password 를 입력해주세요:"
|
||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"data": {
|
"data": {
|
||||||
"code": "2 단계 인증 코드"
|
"code": "2 단계 인증 코드"
|
||||||
},
|
},
|
||||||
"description": "2 단계 인증 코드 및 신원을 확인하기 위해 기기에서 ** {mfa_module_name} ** 을(를) 열어주세요:"
|
"description": "2 단계 인증 코드 및 신원을 확인하기 위해 기기에서 **{mfa_module_name}** 을(를) 열어주세요:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -600,8 +600,8 @@
|
|||||||
"invalid_code": "잘못된 인증 코드"
|
"invalid_code": "잘못된 인증 코드"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"no_api_password_set": "API 비밀번호를 구성하지 않았습니다.",
|
"no_api_password_set": "API 비밀번호를 설정하지 않았습니다.",
|
||||||
"login_expired": "세션이 만료되었습니다. 다시 로그인 해주세요"
|
"login_expired": "세션이 만료되었습니다. 다시 로그인 해주세요."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
|
@@ -64,9 +64,12 @@
|
|||||||
"not_home": "Prom"
|
"not_home": "Prom"
|
||||||
},
|
},
|
||||||
"media_player": {
|
"media_player": {
|
||||||
|
"off": "Izslēgts",
|
||||||
|
"on": "Ieslēgts",
|
||||||
"playing": "Atskaņo",
|
"playing": "Atskaņo",
|
||||||
"paused": "Apturēts",
|
"paused": "Apturēts",
|
||||||
"idle": "Dīkstāvē"
|
"idle": "Dīkstāvē",
|
||||||
|
"standby": "Gaidīšanas režīmā"
|
||||||
},
|
},
|
||||||
"plant": {
|
"plant": {
|
||||||
"ok": "Labi",
|
"ok": "Labi",
|
||||||
|
@@ -550,7 +550,7 @@
|
|||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Initialiserer",
|
"initializing": "Initialiserer",
|
||||||
"authorizing_client": "Du er i ferd med å gi {clientId} tilgang til din Home Assistant",
|
"authorizing_client": "Du er i ferd med å gi {clientId} tilgang til din Home Assistant",
|
||||||
"logging_in_with": "Logg inn med ** {authProviderName} **.",
|
"logging_in_with": "Logg inn med **{authProviderName}**.",
|
||||||
"pick_auth_provider": "Eller logg inn med",
|
"pick_auth_provider": "Eller logg inn med",
|
||||||
"abort_intro": "Innlogging avbrutt",
|
"abort_intro": "Innlogging avbrutt",
|
||||||
"form": {
|
"form": {
|
||||||
@@ -569,7 +569,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "To-faktor autentiseringskode"
|
"code": "To-faktor autentiseringskode"
|
||||||
},
|
},
|
||||||
"description": "Åpne ** {mfa_module_name} ** på enheten din for å se din tofaktors autentiseringskode og bekrefte identiteten din:"
|
"description": "Åpne **{mfa_module_name}** på enheten din for å se din tofaktors autentiseringskode og bekrefte identiteten din:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -592,7 +592,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "To-faktor autentiseringskode"
|
"code": "To-faktor autentiseringskode"
|
||||||
},
|
},
|
||||||
"description": "Åpne **{mfa_module_name}** på din enhet for å vise to-faktor autentiseringkode og vertifiser din identitet."
|
"description": "Åpne **{mfa_module_name}** på enheten din for å se din tofaktors autentiseringskode og bekrefte identiteten din:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -601,7 +601,7 @@
|
|||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"no_api_password_set": "Du har ikke konfigurert et API-passord.",
|
"no_api_password_set": "Du har ikke konfigurert et API-passord.",
|
||||||
"login_expired": "Sesjonen har utløpt, vennligst logg inn igjen: "
|
"login_expired": "Økten er utløpt, vennligst logg inn på nytt"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"default": {
|
"default": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
"on": "włączony",
|
"on": "Włączony",
|
||||||
"unknown": "nieznany",
|
"unknown": "nieznany",
|
||||||
"unavailable": "niedostępny"
|
"unavailable": "niedostępny"
|
||||||
},
|
},
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"binary_sensor": {
|
"binary_sensor": {
|
||||||
"default": {
|
"default": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
"on": "włączony"
|
"on": "Włączony"
|
||||||
},
|
},
|
||||||
"moisture": {
|
"moisture": {
|
||||||
"off": "sucho",
|
"off": "sucho",
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
},
|
},
|
||||||
"calendar": {
|
"calendar": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
"on": "włączony"
|
"on": "Włączony"
|
||||||
},
|
},
|
||||||
"camera": {
|
"camera": {
|
||||||
"recording": "nagrywanie",
|
"recording": "nagrywanie",
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
},
|
},
|
||||||
"climate": {
|
"climate": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
"on": "włączony",
|
"on": "Włączony",
|
||||||
"heat": "ogrzewanie",
|
"heat": "ogrzewanie",
|
||||||
"cool": "chłodzenie",
|
"cool": "chłodzenie",
|
||||||
"idle": "nieaktywny",
|
"idle": "nieaktywny",
|
||||||
@@ -162,7 +162,7 @@
|
|||||||
},
|
},
|
||||||
"fan": {
|
"fan": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
"on": "włączony"
|
"on": "Włączony"
|
||||||
},
|
},
|
||||||
"group": {
|
"group": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
},
|
},
|
||||||
"input_boolean": {
|
"input_boolean": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
"on": "włączony"
|
"on": "Włączony"
|
||||||
},
|
},
|
||||||
"light": {
|
"light": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
@@ -193,7 +193,7 @@
|
|||||||
},
|
},
|
||||||
"media_player": {
|
"media_player": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
"on": "włączony",
|
"on": "Włączony",
|
||||||
"playing": "odtwarzanie",
|
"playing": "odtwarzanie",
|
||||||
"paused": "pauza",
|
"paused": "pauza",
|
||||||
"idle": "nieaktywny",
|
"idle": "nieaktywny",
|
||||||
@@ -205,37 +205,37 @@
|
|||||||
},
|
},
|
||||||
"remote": {
|
"remote": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
"on": "włączony"
|
"on": "Włączony"
|
||||||
},
|
},
|
||||||
"scene": {
|
"scene": {
|
||||||
"scening": "sceny"
|
"scening": "sceny"
|
||||||
},
|
},
|
||||||
"script": {
|
"script": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
"on": "włączony"
|
"on": "Włączony"
|
||||||
},
|
},
|
||||||
"sensor": {
|
"sensor": {
|
||||||
"off": "wyłączony",
|
"off": "wyłączony",
|
||||||
"on": "włączony"
|
"on": "Włączony"
|
||||||
},
|
},
|
||||||
"sun": {
|
"sun": {
|
||||||
"above_horizon": "powyżej horyzontu",
|
"above_horizon": "Powyżej horyzontu",
|
||||||
"below_horizon": "poniżej horyzontu"
|
"below_horizon": "Poniżej horyzontu"
|
||||||
},
|
},
|
||||||
"switch": {
|
"switch": {
|
||||||
"off": "wyłączony",
|
"off": "Wyłączony",
|
||||||
"on": "włączony"
|
"on": "Włączony"
|
||||||
},
|
},
|
||||||
"zwave": {
|
"zwave": {
|
||||||
"default": {
|
"default": {
|
||||||
"initializing": "inicjalizacja",
|
"initializing": "Inicjalizacja",
|
||||||
"dead": "martwy",
|
"dead": "Nieaktywny",
|
||||||
"sleeping": "uśpiony",
|
"sleeping": "Uśpiony",
|
||||||
"ready": "gotowy"
|
"ready": "Gotowy"
|
||||||
},
|
},
|
||||||
"query_stage": {
|
"query_stage": {
|
||||||
"initializing": "inicjalizacja ({query_stage})",
|
"initializing": "inicjalizacja ({query_stage})",
|
||||||
"dead": "martwy ({query_stage})"
|
"dead": "Nieaktywny ({query_stage})"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"weather": {
|
"weather": {
|
||||||
@@ -550,7 +550,7 @@
|
|||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Inicjowanie",
|
"initializing": "Inicjowanie",
|
||||||
"authorizing_client": "Czy na pewno chcesz dać dostęp {clientId} do Twojej instancji Home Assistant.",
|
"authorizing_client": "Czy na pewno chcesz dać dostęp {clientId} do Twojej instancji Home Assistant.",
|
||||||
"logging_in_with": "Logowanie za pomocą ** {authProviderName} **.",
|
"logging_in_with": "Logowanie za pomocą **{authProviderName}**.",
|
||||||
"pick_auth_provider": "Lub zaloguj się za pomocą",
|
"pick_auth_provider": "Lub zaloguj się za pomocą",
|
||||||
"abort_intro": "Logowanie przerwane",
|
"abort_intro": "Logowanie przerwane",
|
||||||
"form": {
|
"form": {
|
||||||
@@ -569,7 +569,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "Dwuskładnikowy kod uwierzytelniający"
|
"code": "Dwuskładnikowy kod uwierzytelniający"
|
||||||
},
|
},
|
||||||
"description": "Otwórz ** {mfa_module_name} ** na urządzeniu, aby wyświetlić dwuskładnikowy kod uwierzytelniający i zweryfikować swoją tożsamość:"
|
"description": "Otwórz **{mfa_module_name}** na urządzeniu, aby wyświetlić dwuskładnikowy kod uwierzytelniający i zweryfikować swoją tożsamość:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -592,7 +592,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "Dwuskładnikowy kod uwierzytelniający"
|
"code": "Dwuskładnikowy kod uwierzytelniający"
|
||||||
},
|
},
|
||||||
"description": "Otwórz ** {mfa_module_name} ** na urządzeniu, aby wyświetlić dwuskładnikowy kod uwierzytelniający i zweryfikować swoją tożsamość:"
|
"description": "Otwórz **{mfa_module_name}** na urządzeniu, aby wyświetlić dwuskładnikowy kod uwierzytelniający i zweryfikować swoją tożsamość:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -610,7 +610,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"user": "Użytkownik"
|
"user": "Użytkownik"
|
||||||
},
|
},
|
||||||
"description": "Proszę wybrać użytkownika, na którego chcesz się zalogować jako:"
|
"description": "Proszę wybrać użytkownika, na którego chcesz się zalogować:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
@@ -621,7 +621,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"page-onboarding": {
|
"page-onboarding": {
|
||||||
"intro": "Czy jesteś gotowy, aby obudzić swój dom, odzyskać prywatność i dołączyć do światowej społeczności majsterkowiczów?",
|
"intro": "Czy jesteś gotowy, aby ożywić swój dom, odzyskać prywatność i dołączyć do światowej społeczności majsterkowiczów?",
|
||||||
"user": {
|
"user": {
|
||||||
"intro": "Zacznijmy od utworzenia konta użytkownika.",
|
"intro": "Zacznijmy od utworzenia konta użytkownika.",
|
||||||
"required_field": "Wymagane",
|
"required_field": "Wymagane",
|
||||||
|
@@ -550,7 +550,7 @@
|
|||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Iniciando",
|
"initializing": "Iniciando",
|
||||||
"authorizing_client": "Você está prestes a dar acesso pra {clientId} à sua instância do Home Assistant.",
|
"authorizing_client": "Você está prestes a dar acesso pra {clientId} à sua instância do Home Assistant.",
|
||||||
"logging_in_with": "Fazendo login com ** {authProviderName} **.",
|
"logging_in_with": "Fazendo login com **{authProviderName}**.",
|
||||||
"pick_auth_provider": "Ou entre com",
|
"pick_auth_provider": "Ou entre com",
|
||||||
"abort_intro": "Login cancelado",
|
"abort_intro": "Login cancelado",
|
||||||
"form": {
|
"form": {
|
||||||
|
@@ -550,7 +550,7 @@
|
|||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "A inicializar",
|
"initializing": "A inicializar",
|
||||||
"authorizing_client": "Você está prestes a dar acesso a {clientId} à sua instância do Home Assistant.",
|
"authorizing_client": "Você está prestes a dar acesso a {clientId} à sua instância do Home Assistant.",
|
||||||
"logging_in_with": "Fazendo login com ** {authProviderName} **.",
|
"logging_in_with": "Fazendo login com **{authProviderName}**.",
|
||||||
"pick_auth_provider": "Ou faça login com",
|
"pick_auth_provider": "Ou faça login com",
|
||||||
"abort_intro": "Login abortado",
|
"abort_intro": "Login abortado",
|
||||||
"form": {
|
"form": {
|
||||||
|
@@ -526,11 +526,53 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"profile": {
|
||||||
|
"push_notifications": {
|
||||||
|
"header": "Notificări",
|
||||||
|
"description": "Trimiteți notificări către acest dispozitiv.",
|
||||||
|
"error_load_platform": "Configurați notify.html5.",
|
||||||
|
"error_use_https": "Necesită SSL activat pentru interfaţă.",
|
||||||
|
"push_notifications": "Notificări",
|
||||||
|
"link_promo": "Aflați mai multe"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"header": "Limba",
|
||||||
|
"link_promo": "Ajută la traducere",
|
||||||
|
"dropdown_label": "Limba"
|
||||||
|
},
|
||||||
|
"themes": {
|
||||||
|
"header": "Temă",
|
||||||
|
"error_no_theme": "Nu există teme disponibile.",
|
||||||
|
"link_promo": "Aflați mai multe despre teme",
|
||||||
|
"dropdown_label": "Temă"
|
||||||
|
}
|
||||||
|
},
|
||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
|
"initializing": "Inițializează",
|
||||||
|
"logging_in_with": "Conectare cu **{authProviderName}**.",
|
||||||
|
"pick_auth_provider": "Sau conectați-vă cu",
|
||||||
|
"abort_intro": "Conectare intrerupta",
|
||||||
"form": {
|
"form": {
|
||||||
|
"working": "Te rog așteaptă",
|
||||||
|
"unknown_error": "Ceva n-a mers bine",
|
||||||
"providers": {
|
"providers": {
|
||||||
"homeassistant": {
|
"homeassistant": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"username": "Nume de utilizator",
|
||||||
|
"password": "Parola"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mfa": {
|
||||||
|
"data": {
|
||||||
|
"code": "Autentificare cu doi factori"
|
||||||
|
},
|
||||||
|
"description": "Deschideti **{mfa_module_name}** pe dispozitivul d-voastra pentru a vedea codul de 'two-factor authentication' si a va verifica indentitatea:"
|
||||||
|
}
|
||||||
|
},
|
||||||
"error": {
|
"error": {
|
||||||
|
"invalid_auth": "nume de utilizator sau parola incorecte",
|
||||||
"invalid_code": "Codul 'Two-factor Authentication' invalid"
|
"invalid_code": "Codul 'Two-factor Authentication' invalid"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
@@ -540,31 +582,57 @@
|
|||||||
"legacy_api_password": {
|
"legacy_api_password": {
|
||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"password": "Parola API"
|
||||||
|
},
|
||||||
"description": "Introduceti parola API in http config:"
|
"description": "Introduceti parola API in http config:"
|
||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"data": {
|
"data": {
|
||||||
"code": "Codul 'Two-factor Authentication'"
|
"code": "Autentificare cu doi factori"
|
||||||
},
|
},
|
||||||
"description": "Deschideti **{mfa_module_name}** pe dispozitivul d-voastra pentru a vedea codul de 'two-factor authentication' si a va verifica indentitatea:"
|
"description": "Deschideti **{mfa_module_name}** pe dispozitivul d-voastra pentru a vedea codul de 'two-factor authentication' si a va verifica indentitatea:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_code": "Cod de autentificare invalid"
|
"invalid_auth": "Parola API nevalidă",
|
||||||
|
"invalid_code": "Codul 'Two-factor Authentication' invalid"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"login_expired": "Sesiune expirata, e nevoie sa va logati din nou."
|
"no_api_password_set": "Nu aveți o parolă API configurată.",
|
||||||
|
"login_expired": "Sesiunea a expirat, va rugam logati-va din nou."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
"step": {
|
"step": {
|
||||||
"init": {
|
"init": {
|
||||||
|
"data": {
|
||||||
|
"user": "Utilizator"
|
||||||
|
},
|
||||||
"description": "Selectati utilizatorul cu care doriti sa va logati:"
|
"description": "Selectati utilizatorul cu care doriti sa va logati:"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"not_whitelisted": "Calculatorul dvs. nu este pe lista albă."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"page-onboarding": {
|
||||||
|
"user": {
|
||||||
|
"intro": "Să începem prin crearea unui cont de utilizator.",
|
||||||
|
"required_field": "Necesar",
|
||||||
|
"data": {
|
||||||
|
"name": "Nume",
|
||||||
|
"username": "Nume de utilizator",
|
||||||
|
"password": "Parola"
|
||||||
|
},
|
||||||
|
"create_account": "Creează cont",
|
||||||
|
"error": {
|
||||||
|
"required_fields": "Completați toate câmpurile obligatorii"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
@@ -724,6 +792,16 @@
|
|||||||
"name": "Nume",
|
"name": "Nume",
|
||||||
"entity_id": "ID-ul entității"
|
"entity_id": "ID-ul entității"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"auth_store": {
|
||||||
|
"ask": "Doriți să salvați aceste date de conectare?",
|
||||||
|
"decline": "Nu, mulţumesc",
|
||||||
|
"confirm": "Salvați datele de conectare"
|
||||||
|
},
|
||||||
|
"notification_drawer": {
|
||||||
|
"click_to_configure": "Faceți clic pe buton pentru a configura {entity}",
|
||||||
|
"empty": "Nicio notificare",
|
||||||
|
"title": "Notificări"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain": {
|
"domain": {
|
||||||
|
@@ -319,7 +319,7 @@
|
|||||||
"introduction": "Изменение конфигурации может быть утомительным процессом. Мы знаем. Этот раздел попытается сделать вашу жизнь немного легче.",
|
"introduction": "Изменение конфигурации может быть утомительным процессом. Мы знаем. Этот раздел попытается сделать вашу жизнь немного легче.",
|
||||||
"validation": {
|
"validation": {
|
||||||
"heading": "Проверка конфигурации",
|
"heading": "Проверка конфигурации",
|
||||||
"introduction": "Проверьте свою конфигурацию, если вы внесли в нее некоторые изменения и хотите убедиться, что она действительна",
|
"introduction": "Проверьте свою конфигурацию, если вы внесли в нее некоторые изменения и хотите убедиться в ее работоспособности",
|
||||||
"check_config": "Проверить конфигурацию",
|
"check_config": "Проверить конфигурацию",
|
||||||
"valid": "Конфигурация выполнена верно!",
|
"valid": "Конфигурация выполнена верно!",
|
||||||
"invalid": "Ошибка в конфигурации"
|
"invalid": "Ошибка в конфигурации"
|
||||||
@@ -504,7 +504,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"script": {
|
"script": {
|
||||||
"caption": "Скрипт",
|
"caption": "Скрипты",
|
||||||
"description": "Создавайте и редактируйте скрипты"
|
"description": "Создавайте и редактируйте скрипты"
|
||||||
},
|
},
|
||||||
"zwave": {
|
"zwave": {
|
||||||
@@ -782,8 +782,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notification_toast": {
|
"notification_toast": {
|
||||||
"entity_turned_on": "{entity} включение.",
|
"entity_turned_on": "{entity} включается",
|
||||||
"entity_turned_off": "{entity} выключение.",
|
"entity_turned_off": "{entity} выключается",
|
||||||
"service_called": "Вызов службы {service}.",
|
"service_called": "Вызов службы {service}.",
|
||||||
"service_call_failed": "Не удалось вызвать службу {service}.",
|
"service_call_failed": "Не удалось вызвать службу {service}.",
|
||||||
"connection_lost": "Соединение потеряно. Переподключение ..."
|
"connection_lost": "Соединение потеряно. Переподключение ..."
|
||||||
|
@@ -550,7 +550,7 @@
|
|||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Inicializacija",
|
"initializing": "Inicializacija",
|
||||||
"authorizing_client": "Dali boste {clientId} dostop do vašega Home assistenta.",
|
"authorizing_client": "Dali boste {clientId} dostop do vašega Home assistenta.",
|
||||||
"logging_in_with": "Prijava z ** {authProviderName} **.",
|
"logging_in_with": "Prijava z **{authProviderName}**.",
|
||||||
"pick_auth_provider": "Ali se prijavite z",
|
"pick_auth_provider": "Ali se prijavite z",
|
||||||
"abort_intro": "Prijava prekinjena",
|
"abort_intro": "Prijava prekinjena",
|
||||||
"form": {
|
"form": {
|
||||||
@@ -569,7 +569,7 @@
|
|||||||
"data": {
|
"data": {
|
||||||
"code": "Dvofaktorska koda za avtorizacijo"
|
"code": "Dvofaktorska koda za avtorizacijo"
|
||||||
},
|
},
|
||||||
"description": "V svoji napravi odprite ** {mfa_module_name} **, da si ogledate svojo dvofaktorsko kodo za preverjanje pristnosti in preverite svojo identiteto:"
|
"description": "V svoji napravi odprite **{mfa_module_name}**, da si ogledate svojo dvofaktorsko kodo za preverjanje pristnosti in preverite svojo identiteto:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -590,9 +590,9 @@
|
|||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"data": {
|
"data": {
|
||||||
"code": "Dvofaktorska avtorizacijska koda"
|
"code": "Dvofaktorska koda za avtorizacijo"
|
||||||
},
|
},
|
||||||
"description": "Odprite **{mfa_module_name}** na vaši napravi za ogled dvofaktorske avtorizacijske kode in potrdite vašo identiteto:"
|
"description": "V svoji napravi odprite **{mfa_module_name}**, da si ogledate svojo dvofaktorsko kodo za preverjanje pristnosti in preverite svojo identiteto:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -601,7 +601,7 @@
|
|||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"no_api_password_set": "Nimate nastavljenega gesla API-ja.",
|
"no_api_password_set": "Nimate nastavljenega gesla API-ja.",
|
||||||
"login_expired": "Seja je potekla, prosim ponovno se prijavite."
|
"login_expired": "Seja je potekla, prosimo, prijavite se znova."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
|
@@ -549,10 +549,10 @@
|
|||||||
},
|
},
|
||||||
"page-authorize": {
|
"page-authorize": {
|
||||||
"initializing": "Initierar",
|
"initializing": "Initierar",
|
||||||
"authorizing_client": "Du håller på att ge {clientid} behörighet till din Home Assistant.",
|
"authorizing_client": "Du håller på att ge {clientId} behörighet till din Home Assistant.",
|
||||||
"logging_in_with": "Loggar in med **{authProviderName}**.",
|
"logging_in_with": "Loggar in med **{authProviderName}**.",
|
||||||
"pick_auth_provider": "Eller logga in med",
|
"pick_auth_provider": "Eller logga in med",
|
||||||
"abort_intro": "Login avbruten",
|
"abort_intro": "Inloggning avbruten",
|
||||||
"form": {
|
"form": {
|
||||||
"working": "Vänligen vänta",
|
"working": "Vänligen vänta",
|
||||||
"unknown_error": "Något gick fel",
|
"unknown_error": "Något gick fel",
|
||||||
@@ -568,7 +568,8 @@
|
|||||||
"mfa": {
|
"mfa": {
|
||||||
"data": {
|
"data": {
|
||||||
"code": "Tvåfaktorsautentiseringskod"
|
"code": "Tvåfaktorsautentiseringskod"
|
||||||
}
|
},
|
||||||
|
"description": "Öppna **{mfa_module_name}** på din enhet för att visa din tvåfaktors autentiseringskod och verifiera din identitet:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
@@ -589,18 +590,18 @@
|
|||||||
},
|
},
|
||||||
"mfa": {
|
"mfa": {
|
||||||
"data": {
|
"data": {
|
||||||
"code": "Tvåfaktorsautentiseringskod:"
|
"code": "Tvåfaktorsautentiseringskod"
|
||||||
},
|
},
|
||||||
"description": "Öppna **(mfa_module_name)** på din enhet för att se din tvåfaktorsautentiseringskod och verifiera din identitet:"
|
"description": "Öppna **{mfa_module_name}** på din enhet för att visa din tvåfaktors autentiseringskod och verifiera din identitet:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_auth": "Ogiltigt API-lösenord",
|
"invalid_auth": "Ogiltigt API-lösenord",
|
||||||
"invalid_code": "Felaktig autentiserings kod"
|
"invalid_code": "Ogiltig autentiseringskod"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"no_api_password_set": "Du har inget API lösenord konfigurerat",
|
"no_api_password_set": "Du har inget API lösenord konfigurerat",
|
||||||
"login_expired": "Sessionen avslutades, logga in igen."
|
"login_expired": "Sessionen avslutades. Logga in igen."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
@@ -620,6 +621,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"page-onboarding": {
|
"page-onboarding": {
|
||||||
|
"intro": "Är du redo att väcka ditt hem, återta din integritet och gå med i en världsomspännande gemenskap av hemmafixare?",
|
||||||
"user": {
|
"user": {
|
||||||
"intro": "Låt oss börja med att skapa ett användarkonto",
|
"intro": "Låt oss börja med att skapa ett användarkonto",
|
||||||
"required_field": "Krävs",
|
"required_field": "Krävs",
|
||||||
@@ -628,7 +630,10 @@
|
|||||||
"username": "Användarnamn",
|
"username": "Användarnamn",
|
||||||
"password": "Lösenord"
|
"password": "Lösenord"
|
||||||
},
|
},
|
||||||
"create_account": "Skapa konto"
|
"create_account": "Skapa konto",
|
||||||
|
"error": {
|
||||||
|
"required_fields": "Fyll i alla fält som krävs"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -797,7 +802,7 @@
|
|||||||
},
|
},
|
||||||
"notification_drawer": {
|
"notification_drawer": {
|
||||||
"click_to_configure": "Klicka på knappen för att konfigurera {entity}",
|
"click_to_configure": "Klicka på knappen för att konfigurera {entity}",
|
||||||
"empty": "Ingra notiser",
|
"empty": "Inga notiser",
|
||||||
"title": "Notiser"
|
"title": "Notiser"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@@ -24,7 +24,7 @@
|
|||||||
},
|
},
|
||||||
"alarm_control_panel": {
|
"alarm_control_panel": {
|
||||||
"armed": "Vũ trang",
|
"armed": "Vũ trang",
|
||||||
"disarmed": "Giải giáp",
|
"disarmed": "Vô hiệu hóa",
|
||||||
"armed_home": "Vũ trang khi ở nhà",
|
"armed_home": "Vũ trang khi ở nhà",
|
||||||
"armed_away": "Vũ trang khi đi vắng",
|
"armed_away": "Vũ trang khi đi vắng",
|
||||||
"armed_night": "Vũ trang đêm",
|
"armed_night": "Vũ trang đêm",
|
||||||
@@ -248,7 +248,7 @@
|
|||||||
},
|
},
|
||||||
"alarm_control_panel": {
|
"alarm_control_panel": {
|
||||||
"armed": "VT",
|
"armed": "VT",
|
||||||
"disarmed": "Giải giáp",
|
"disarmed": "Vô hiệu hoá",
|
||||||
"pending": "Chờ",
|
"pending": "Chờ",
|
||||||
"arming": "Đang VT",
|
"arming": "Đang VT",
|
||||||
"disarming": "Giải giáp"
|
"disarming": "Giải giáp"
|
||||||
@@ -459,6 +459,19 @@
|
|||||||
"delete_user": "Xóa người dùng"
|
"delete_user": "Xóa người dùng"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"page-authorize": {
|
||||||
|
"form": {
|
||||||
|
"providers": {
|
||||||
|
"trusted_networks": {
|
||||||
|
"step": {
|
||||||
|
"init": {
|
||||||
|
"description": "Vui lòng lựa chọn một tài khoản để đăng nhập"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
@@ -526,8 +539,8 @@
|
|||||||
"code": "Mã số",
|
"code": "Mã số",
|
||||||
"clear_code": "Xóa",
|
"clear_code": "Xóa",
|
||||||
"disarm": "Không vũ trang",
|
"disarm": "Không vũ trang",
|
||||||
"arm_home": "VT Ở nhà",
|
"arm_home": "An ninh khi ở nhà",
|
||||||
"arm_away": "VT Đi vắng"
|
"arm_away": "An ninh khi đi vắng"
|
||||||
},
|
},
|
||||||
"automation": {
|
"automation": {
|
||||||
"last_triggered": "Kích hoạt lần cuối",
|
"last_triggered": "Kích hoạt lần cuối",
|
||||||
@@ -611,7 +624,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain": {
|
"domain": {
|
||||||
"alarm_control_panel": "Bảng điều khiển báo động",
|
"alarm_control_panel": "Bảng điều khiển an ninh",
|
||||||
"automation": "Tự động hóa",
|
"automation": "Tự động hóa",
|
||||||
"binary_sensor": "Cảm biến nhị phân",
|
"binary_sensor": "Cảm biến nhị phân",
|
||||||
"calendar": "Lịch",
|
"calendar": "Lịch",
|
||||||
|
@@ -564,10 +564,20 @@
|
|||||||
"username": "使用者名稱",
|
"username": "使用者名稱",
|
||||||
"password": "使用者密碼"
|
"password": "使用者密碼"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"mfa": {
|
||||||
|
"data": {
|
||||||
|
"code": "兩步驟驗證碼"
|
||||||
|
},
|
||||||
|
"description": "開啟裝置上的 **{mfa_module_name}** 以獲得兩步驟驗證碼,並進行驗證:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_auth": "使用者名稱或密碼無效"
|
"invalid_auth": "使用者名稱或密碼無效",
|
||||||
|
"invalid_code": "驗證碼無效"
|
||||||
|
},
|
||||||
|
"abort": {
|
||||||
|
"login_expired": "登入階段已逾時,請重新登錄。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"legacy_api_password": {
|
"legacy_api_password": {
|
||||||
@@ -575,14 +585,23 @@
|
|||||||
"init": {
|
"init": {
|
||||||
"data": {
|
"data": {
|
||||||
"password": "API 密碼"
|
"password": "API 密碼"
|
||||||
}
|
},
|
||||||
|
"description": "請輸入 Http 設定中的 API 密鑰:"
|
||||||
|
},
|
||||||
|
"mfa": {
|
||||||
|
"data": {
|
||||||
|
"code": "兩步驟驗證碼"
|
||||||
|
},
|
||||||
|
"description": "開啟裝置上的 **{mfa_module_name}** 以獲得兩步驟驗證碼,並進行驗證:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"invalid_auth": "API 密碼無效"
|
"invalid_auth": "API 密碼無效",
|
||||||
|
"invalid_code": "驗證碼無效"
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"no_api_password_set": "尚未設定 API 密碼。"
|
"no_api_password_set": "尚未設定 API 密碼。",
|
||||||
|
"login_expired": "登入階段已逾時,請重新登錄。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"trusted_networks": {
|
"trusted_networks": {
|
||||||
@@ -590,7 +609,8 @@
|
|||||||
"init": {
|
"init": {
|
||||||
"data": {
|
"data": {
|
||||||
"user": "使用者"
|
"user": "使用者"
|
||||||
}
|
},
|
||||||
|
"description": "請選擇所要登錄的用戶:"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
|
@@ -6,6 +6,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin');
|
|||||||
const WorkboxPlugin = require('workbox-webpack-plugin');
|
const WorkboxPlugin = require('workbox-webpack-plugin');
|
||||||
const CompressionPlugin = require("compression-webpack-plugin");
|
const CompressionPlugin = require("compression-webpack-plugin");
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
|
const zopfli = require('@gfx/zopfli');
|
||||||
const translationMetadata = require('./build-translations/translationMetadata.json');
|
const translationMetadata = require('./build-translations/translationMetadata.json');
|
||||||
|
|
||||||
const version = fs.readFileSync('setup.py', 'utf8').match(/\d{8}[^']*/);
|
const version = fs.readFileSync('setup.py', 'utf8').match(/\d{8}[^']*/);
|
||||||
@@ -138,7 +139,10 @@ function createConfig(isProdBuild, latestBuild) {
|
|||||||
/\.LICENSE$/,
|
/\.LICENSE$/,
|
||||||
/\.py$/,
|
/\.py$/,
|
||||||
/\.txt$/,
|
/\.txt$/,
|
||||||
]
|
],
|
||||||
|
algorithm(input, compressionOptions, callback) {
|
||||||
|
return zopfli.gzip(input, compressionOptions, callback);
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
new WorkboxPlugin.InjectManifest({
|
new WorkboxPlugin.InjectManifest({
|
||||||
swSrc: './src/entrypoints/service-worker-bootstrap.js',
|
swSrc: './src/entrypoints/service-worker-bootstrap.js',
|
||||||
@@ -151,32 +155,13 @@ function createConfig(isProdBuild, latestBuild) {
|
|||||||
/hass-icons.js$/,
|
/hass-icons.js$/,
|
||||||
/\.chunk\.js$/,
|
/\.chunk\.js$/,
|
||||||
],
|
],
|
||||||
// Static assets get cached during runtime. But these we want to explicitly cache
|
|
||||||
// Need to be done using templatedUrls because prefix is /static
|
|
||||||
globDirectory: '.',
|
|
||||||
globIgnores: [],
|
|
||||||
modifyUrlPrefix: {
|
|
||||||
'hass_frontend': '/static'
|
|
||||||
},
|
|
||||||
templatedUrls: {
|
templatedUrls: {
|
||||||
[`/static/translations/${translationMetadata['translations']['en']['fingerprints']['en']}`]: [
|
[`/static/translations/${translationMetadata['translations']['en']['fingerprints']['en']}`]: 'build-translations/output/en.json',
|
||||||
'build-translations/output/en.json'
|
'/static/icons/favicon-192x192.png': 'public/icons/favicon-192x192.png',
|
||||||
],
|
'/static/fonts/roboto/Roboto-Light.ttf': 'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Light.ttf',
|
||||||
'/static/icons/favicon-192x192.png': [
|
'/static/fonts/roboto/Roboto-Medium.ttf': 'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Medium.ttf',
|
||||||
'public/icons/favicon-192x192.png'
|
'/static/fonts/roboto/Roboto-Regular.ttf': 'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Regular.ttf',
|
||||||
],
|
'/static/fonts/roboto/Roboto-Bold.ttf': 'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Bold.ttf',
|
||||||
'/static/fonts/roboto/Roboto-Light.ttf': [
|
|
||||||
'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Light.ttf'
|
|
||||||
],
|
|
||||||
'/static/fonts/roboto/Roboto-Medium.ttf': [
|
|
||||||
'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Medium.ttf'
|
|
||||||
],
|
|
||||||
'/static/fonts/roboto/Roboto-Regular.ttf': [
|
|
||||||
'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Regular.ttf'
|
|
||||||
],
|
|
||||||
'/static/fonts/roboto/Roboto-Bold.ttf': [
|
|
||||||
'node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Bold.ttf'
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
new HtmlWebpackPlugin({
|
new HtmlWebpackPlugin({
|
||||||
|
135
yarn.lock
135
yarn.lock
@@ -577,6 +577,12 @@
|
|||||||
lodash "^4.17.5"
|
lodash "^4.17.5"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
|
"@gfx/zopfli@^1.0.8":
|
||||||
|
version "1.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@gfx/zopfli/-/zopfli-1.0.8.tgz#4bad09d5c8bd8156e018716228e9d84e2347b3f0"
|
||||||
|
dependencies:
|
||||||
|
base64-js "^1.0.0"
|
||||||
|
|
||||||
"@mdi/svg@^2.4.85":
|
"@mdi/svg@^2.4.85":
|
||||||
version "2.7.94"
|
version "2.7.94"
|
||||||
resolved "https://registry.yarnpkg.com/@mdi/svg/-/svg-2.7.94.tgz#5f2b03c363b10f7e4cf8c8a5fdc81cbd838bf44b"
|
resolved "https://registry.yarnpkg.com/@mdi/svg/-/svg-2.7.94.tgz#5f2b03c363b10f7e4cf8c8a5fdc81cbd838bf44b"
|
||||||
@@ -1953,6 +1959,10 @@ agent-base@2:
|
|||||||
extend "~3.0.0"
|
extend "~3.0.0"
|
||||||
semver "~5.0.1"
|
semver "~5.0.1"
|
||||||
|
|
||||||
|
ajv-errors@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59"
|
||||||
|
|
||||||
ajv-keywords@^2.1.0:
|
ajv-keywords@^2.1.0:
|
||||||
version "2.1.1"
|
version "2.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
|
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762"
|
||||||
@@ -3135,7 +3145,7 @@ base64-js@1.2.0:
|
|||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
|
||||||
|
|
||||||
base64-js@^1.0.2:
|
base64-js@^1.0.0, base64-js@^1.0.2:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
|
||||||
|
|
||||||
@@ -3492,7 +3502,7 @@ bytes@3.0.0:
|
|||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
|
||||||
|
|
||||||
cacache@^10.0.1, cacache@^10.0.4:
|
cacache@^10.0.4:
|
||||||
version "10.0.4"
|
version "10.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460"
|
resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3510,6 +3520,25 @@ cacache@^10.0.1, cacache@^10.0.4:
|
|||||||
unique-filename "^1.1.0"
|
unique-filename "^1.1.0"
|
||||||
y18n "^4.0.0"
|
y18n "^4.0.0"
|
||||||
|
|
||||||
|
cacache@^11.2.0:
|
||||||
|
version "11.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.2.0.tgz#617bdc0b02844af56310e411c0878941d5739965"
|
||||||
|
dependencies:
|
||||||
|
bluebird "^3.5.1"
|
||||||
|
chownr "^1.0.1"
|
||||||
|
figgy-pudding "^3.1.0"
|
||||||
|
glob "^7.1.2"
|
||||||
|
graceful-fs "^4.1.11"
|
||||||
|
lru-cache "^4.1.3"
|
||||||
|
mississippi "^3.0.0"
|
||||||
|
mkdirp "^0.5.1"
|
||||||
|
move-concurrently "^1.0.1"
|
||||||
|
promise-inflight "^1.0.1"
|
||||||
|
rimraf "^2.6.2"
|
||||||
|
ssri "^6.0.0"
|
||||||
|
unique-filename "^1.1.0"
|
||||||
|
y18n "^4.0.0"
|
||||||
|
|
||||||
cache-base@^1.0.1:
|
cache-base@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
|
resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
|
||||||
@@ -4004,13 +4033,14 @@ compressible@~2.0.14:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mime-db ">= 1.34.0 < 2"
|
mime-db ">= 1.34.0 < 2"
|
||||||
|
|
||||||
compression-webpack-plugin@^1.1.11:
|
compression-webpack-plugin@^2.0.0:
|
||||||
version "1.1.11"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-1.1.11.tgz#8384c7a6ead1d2e2efb190bdfcdcf35878ed8266"
|
resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-2.0.0.tgz#46476350c1eb27f783dccc79ac2f709baa2cffbc"
|
||||||
dependencies:
|
dependencies:
|
||||||
cacache "^10.0.1"
|
cacache "^11.2.0"
|
||||||
find-cache-dir "^1.0.0"
|
find-cache-dir "^2.0.0"
|
||||||
neo-async "^2.5.0"
|
neo-async "^2.5.0"
|
||||||
|
schema-utils "^1.0.0"
|
||||||
serialize-javascript "^1.4.0"
|
serialize-javascript "^1.4.0"
|
||||||
webpack-sources "^1.0.1"
|
webpack-sources "^1.0.1"
|
||||||
|
|
||||||
@@ -5449,6 +5479,10 @@ fecha@^2.3.3, "fecha@https://github.com/balloob/fecha/archive/51d14fd0eb4781e2ec
|
|||||||
version "2.3.3"
|
version "2.3.3"
|
||||||
resolved "https://github.com/balloob/fecha/archive/51d14fd0eb4781e2ecf265d1c3080706259133b5.tar.gz#bfb1e49121dd7821601af35faf4fe93dbd19200a"
|
resolved "https://github.com/balloob/fecha/archive/51d14fd0eb4781e2ecf265d1c3080706259133b5.tar.gz#bfb1e49121dd7821601af35faf4fe93dbd19200a"
|
||||||
|
|
||||||
|
figgy-pudding@^3.1.0, figgy-pudding@^3.5.1:
|
||||||
|
version "3.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
|
||||||
|
|
||||||
figures@^1.3.5:
|
figures@^1.3.5:
|
||||||
version "1.7.0"
|
version "1.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
|
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
|
||||||
@@ -5528,6 +5562,14 @@ find-cache-dir@^1.0.0:
|
|||||||
make-dir "^1.0.0"
|
make-dir "^1.0.0"
|
||||||
pkg-dir "^2.0.0"
|
pkg-dir "^2.0.0"
|
||||||
|
|
||||||
|
find-cache-dir@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d"
|
||||||
|
dependencies:
|
||||||
|
commondir "^1.0.1"
|
||||||
|
make-dir "^1.0.0"
|
||||||
|
pkg-dir "^3.0.0"
|
||||||
|
|
||||||
find-index@^0.1.1:
|
find-index@^0.1.1:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4"
|
resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4"
|
||||||
@@ -5569,6 +5611,12 @@ find-up@^2.0.0, find-up@^2.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
locate-path "^2.0.0"
|
locate-path "^2.0.0"
|
||||||
|
|
||||||
|
find-up@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
|
||||||
|
dependencies:
|
||||||
|
locate-path "^3.0.0"
|
||||||
|
|
||||||
findup-sync@^0.4.2:
|
findup-sync@^0.4.2:
|
||||||
version "0.4.3"
|
version "0.4.3"
|
||||||
resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12"
|
resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12"
|
||||||
@@ -6513,9 +6561,9 @@ hoek@4.x.x:
|
|||||||
version "4.2.1"
|
version "4.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
|
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
|
||||||
|
|
||||||
home-assistant-js-websocket@^3.0.0:
|
home-assistant-js-websocket@^3.1.2:
|
||||||
version "3.0.0"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-3.0.0.tgz#498828a29827bdd1f3e99cf3b5e152694cededbf"
|
resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-3.1.2.tgz#c82e79eb644eac0206cd9c07d10c83c7d052c2b7"
|
||||||
|
|
||||||
home-or-tmp@^2.0.0:
|
home-or-tmp@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
@@ -7540,6 +7588,13 @@ locate-path@^2.0.0:
|
|||||||
p-locate "^2.0.0"
|
p-locate "^2.0.0"
|
||||||
path-exists "^3.0.0"
|
path-exists "^3.0.0"
|
||||||
|
|
||||||
|
locate-path@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
|
||||||
|
dependencies:
|
||||||
|
p-locate "^3.0.0"
|
||||||
|
path-exists "^3.0.0"
|
||||||
|
|
||||||
lodash._baseassign@^3.0.0:
|
lodash._baseassign@^3.0.0:
|
||||||
version "3.2.0"
|
version "3.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
|
resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
|
||||||
@@ -7972,7 +8027,7 @@ lru-cache@2:
|
|||||||
version "2.7.3"
|
version "2.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952"
|
||||||
|
|
||||||
lru-cache@^4.0.1, lru-cache@^4.0.2, lru-cache@^4.1.1:
|
lru-cache@^4.0.1, lru-cache@^4.0.2, lru-cache@^4.1.1, lru-cache@^4.1.3:
|
||||||
version "4.1.3"
|
version "4.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c"
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -8292,6 +8347,21 @@ mississippi@^2.0.0:
|
|||||||
stream-each "^1.1.0"
|
stream-each "^1.1.0"
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
|
|
||||||
|
mississippi@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022"
|
||||||
|
dependencies:
|
||||||
|
concat-stream "^1.5.0"
|
||||||
|
duplexify "^3.4.2"
|
||||||
|
end-of-stream "^1.1.0"
|
||||||
|
flush-write-stream "^1.0.0"
|
||||||
|
from2 "^2.1.0"
|
||||||
|
parallel-transform "^1.1.0"
|
||||||
|
pump "^3.0.0"
|
||||||
|
pumpify "^1.3.3"
|
||||||
|
stream-each "^1.1.0"
|
||||||
|
through2 "^2.0.0"
|
||||||
|
|
||||||
mixin-deep@^1.2.0:
|
mixin-deep@^1.2.0:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
|
resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
|
||||||
@@ -8878,12 +8948,24 @@ p-limit@^1.0.0, p-limit@^1.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-try "^1.0.0"
|
p-try "^1.0.0"
|
||||||
|
|
||||||
|
p-limit@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec"
|
||||||
|
dependencies:
|
||||||
|
p-try "^2.0.0"
|
||||||
|
|
||||||
p-locate@^2.0.0:
|
p-locate@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
|
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
|
||||||
dependencies:
|
dependencies:
|
||||||
p-limit "^1.1.0"
|
p-limit "^1.1.0"
|
||||||
|
|
||||||
|
p-locate@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
|
||||||
|
dependencies:
|
||||||
|
p-limit "^2.0.0"
|
||||||
|
|
||||||
p-map@^1.1.1:
|
p-map@^1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a"
|
resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a"
|
||||||
@@ -8898,6 +8980,10 @@ p-try@^1.0.0:
|
|||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
|
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
|
||||||
|
|
||||||
|
p-try@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
|
||||||
|
|
||||||
package-json@^2.0.0:
|
package-json@^2.0.0:
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/package-json/-/package-json-2.4.0.tgz#0d15bd67d1cbbddbb2ca222ff2edb86bcb31a8bb"
|
resolved "https://registry.yarnpkg.com/package-json/-/package-json-2.4.0.tgz#0d15bd67d1cbbddbb2ca222ff2edb86bcb31a8bb"
|
||||||
@@ -9157,6 +9243,12 @@ pkg-dir@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
find-up "^2.1.0"
|
find-up "^2.1.0"
|
||||||
|
|
||||||
|
pkg-dir@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
|
||||||
|
dependencies:
|
||||||
|
find-up "^3.0.0"
|
||||||
|
|
||||||
plist@^2.0.1:
|
plist@^2.0.1:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/plist/-/plist-2.1.0.tgz#57ccdb7a0821df21831217a3cad54e3e146a1025"
|
resolved "https://registry.yarnpkg.com/plist/-/plist-2.1.0.tgz#57ccdb7a0821df21831217a3cad54e3e146a1025"
|
||||||
@@ -9700,6 +9792,13 @@ pump@^2.0.0, pump@^2.0.1:
|
|||||||
end-of-stream "^1.1.0"
|
end-of-stream "^1.1.0"
|
||||||
once "^1.3.1"
|
once "^1.3.1"
|
||||||
|
|
||||||
|
pump@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||||
|
dependencies:
|
||||||
|
end-of-stream "^1.1.0"
|
||||||
|
once "^1.3.1"
|
||||||
|
|
||||||
pumpify@^1.3.3:
|
pumpify@^1.3.3:
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
|
resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
|
||||||
@@ -10397,6 +10496,14 @@ schema-utils@^0.4.4, schema-utils@^0.4.5:
|
|||||||
ajv "^6.1.0"
|
ajv "^6.1.0"
|
||||||
ajv-keywords "^3.1.0"
|
ajv-keywords "^3.1.0"
|
||||||
|
|
||||||
|
schema-utils@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770"
|
||||||
|
dependencies:
|
||||||
|
ajv "^6.1.0"
|
||||||
|
ajv-errors "^1.0.0"
|
||||||
|
ajv-keywords "^3.1.0"
|
||||||
|
|
||||||
scoped-regex@^1.0.0:
|
scoped-regex@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-1.0.0.tgz#a346bb1acd4207ae70bd7c0c7ca9e566b6baddb8"
|
resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-1.0.0.tgz#a346bb1acd4207ae70bd7c0c7ca9e566b6baddb8"
|
||||||
@@ -10925,6 +11032,12 @@ ssri@^5.2.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "^5.1.1"
|
safe-buffer "^5.1.1"
|
||||||
|
|
||||||
|
ssri@^6.0.0:
|
||||||
|
version "6.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
|
||||||
|
dependencies:
|
||||||
|
figgy-pudding "^3.5.1"
|
||||||
|
|
||||||
stable@^0.1.6:
|
stable@^0.1.6:
|
||||||
version "0.1.8"
|
version "0.1.8"
|
||||||
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
|
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
|
||||||
|
Reference in New Issue
Block a user