Compare commits

...

48 Commits

Author SHA1 Message Date
Bram Kragten
764ae7e0b6 Merge pull request #9045 from home-assistant/dev 2021-04-29 22:21:03 +02:00
Bram Kragten
da0bfa1945 Bumped version to 20210429.0 2021-04-29 21:56:36 +02:00
Bram Kragten
3c61d709b5 Fix RGBWW colors (#9039) 2021-04-29 21:53:22 +02:00
Joakim Sørensen
ffc92a7b63 Fix overlap on integration card (#9037) 2021-04-29 12:36:07 -07:00
Paulus Schoutsen
af0c7b5a50 Fix grid card size (#9044)
* Fix grid card size

* Remove console

* Rename
2021-04-29 12:31:46 -07:00
Bram Kragten
1904c4d057 Fix race in theme setting (#9027)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-04-29 21:04:31 +02:00
Bram Kragten
542f169b36 Fix saving Lovelace config (#9041) 2021-04-29 09:05:58 -07:00
Bram Kragten
65a30bf60c Fix onboarding dark styles (#9040) 2021-04-29 08:55:45 -07:00
Bram Kragten
2e51da32f0 Handle when choose is not an array (#9028) 2021-04-29 08:53:58 -07:00
Philip Allgaier
0562242043 Format "IP" and "MAC" attribute names (#9034) 2021-04-29 15:55:51 +02:00
GitHub Action
debcdefc21 Translation update 2021-04-29 00:48:48 +00:00
Paulus Schoutsen
6b7e78320d Merge pull request #9024 from home-assistant/dev 2021-04-28 10:47:16 -07:00
Paulus Schoutsen
0de3f3a332 Merge remote-tracking branch 'origin/master' into dev 2021-04-28 10:29:18 -07:00
Paulus Schoutsen
4fcb4d449e Bumped version to 20210428.0 2021-04-28 10:28:50 -07:00
Bram Kragten
408fe25abd Update workbox, add expiration on cache (#9020) 2021-04-28 10:26:30 -07:00
Paulus Schoutsen
236e5e0b25 Show subtrace steps when selecting a node (#9023)
* Show subtrace steps when selecting a node

* Limit logic to just child conditions
2021-04-28 10:23:30 -07:00
Charles Garwood
ebe0caba83 MVP Z-Wave JS Log Viewer (#9008)
* Add element to subscribe to ZJS logs

* set log level and adjust styling

* review comments

* add ZWaveJS to function names

* use flexbox

* Review comments

* import

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-04-28 13:00:13 -04:00
Philip Allgaier
9d33c0cfaf Fix unit support in gauge editor (#9021) 2021-04-28 18:19:51 +02:00
Paulus Schoutsen
7962130a0c Show entities on area page (#8980)
Co-authored-by: Philip Allgaier <mail@spacegaier.de>
2021-04-28 17:01:43 +02:00
David F. Mulcahey
9690434cac Reconfigure ZHA device take 2 (#8990)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-04-28 12:30:09 +02:00
GitHub Action
7304544c37 Translation update 2021-04-28 00:48:54 +00:00
Bram Kragten
5a3408c242 Bumped version to 20210427.0 2021-04-28 00:22:22 +02:00
Bram Kragten
16996f25af Update demo lights (#9011) 2021-04-28 00:21:23 +02:00
J. Nick Koston
0c12586019 Display cumulative setup seconds on the config info page (#8720)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
Co-authored-by: Philip Allgaier <philip.allgaier@gmx.de>
2021-04-27 23:38:29 +02:00
Joakim Sørensen
93a1adaa56 Remove analytics translations (#9002) 2021-04-27 22:44:28 +02:00
Charles Garwood
83e65e2cc6 Add UI for Z-Wave JS Device Reinterview (#8957) 2021-04-27 13:30:15 -07:00
Bram Kragten
36586b798e Use supported_color_modes to determine what UI elements to show (#8961) 2021-04-27 10:43:25 -07:00
Paulus Schoutsen
20c351949f Strategy: Update name -> type (#9006) 2021-04-27 10:04:52 -07:00
Bram Kragten
b63bd92d81 Bump codemirror with iOS fix (#8992) 2021-04-27 16:29:20 +02:00
Bram Kragten
17d3755152 Bumped version to 20210407.3 2021-04-09 20:05:32 +02:00
Paulus Schoutsen
d7c0c2ea72 Fix failed conditions reason (#8870) 2021-04-09 20:05:17 +02:00
Charles Garwood
7c823c98ae Add units to Z-Wave JS Node Config inputs (#8869)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-04-09 20:04:56 +02:00
Bram Kragten
97508a6f31 Update value of date input (#8865) 2021-04-09 20:04:29 +02:00
Paulus Schoutsen
2507a41b6e Pass narrow (#8864) 2021-04-09 20:04:03 +02:00
Paulus Schoutsen
9b628546c1 Remove owner guard from analytics (#8842) 2021-04-08 21:00:00 +02:00
Bram Kragten
d0837fada8 Bumped version to 20210407.2 2021-04-08 20:56:03 +02:00
Paulus Schoutsen
520647d72f Add logbook note (#8843)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
2021-04-08 20:55:10 +02:00
Bram Kragten
51c888845c Handle choose being null (#8859)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
2021-04-08 20:54:51 +02:00
Bram Kragten
e4606219bc Check if logbook component loaded when fetching trace (#8861) 2021-04-08 20:54:34 +02:00
Philip Allgaier
716335df2c Use number format setting for attribute rows (#8844) 2021-04-08 20:54:14 +02:00
Philip Allgaier
ad4f90c502 Mention unique ID requirement in trace button tooltip (#8853) 2021-04-08 20:53:57 +02:00
Donnie
a1bdfa7560 Fix spinner regression and remove unnecessary twoline config (#8847) 2021-04-08 20:53:43 +02:00
Joakim Sørensen
34ca807044 Merge pull request #8841 from home-assistant/dev 2021-04-07 16:36:31 +02:00
Bram Kragten
8af55efdb3 Merge pull request #8836 from home-assistant/dev 2021-04-07 10:43:35 +02:00
Paulus Schoutsen
6393072e68 Merge pull request #8826 from home-assistant/dev 2021-04-06 09:49:27 -07:00
Bram Kragten
9e1a8b646b Merge pull request #8803 from home-assistant/dev
20210402.1
2021-04-02 21:15:13 +02:00
Paulus Schoutsen
c810e541ea Merge pull request #8795 from home-assistant/dev 2021-04-01 15:33:43 -07:00
Bram Kragten
66432608ed Merge pull request #8774 from home-assistant/dev 2021-03-31 17:27:43 +02:00
68 changed files with 3796 additions and 1687 deletions

View File

@@ -246,11 +246,15 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
"light.living_room_lights": {
entity_id: "light.living_room_lights",
state: "off",
state: "on",
attributes: {
min_mireds: 111,
max_mireds: 400,
brightness: 175,
color_temp: 300,
supported_color_modes: ["brightness", "color_temp"],
friendly_name: "Living Room Lights",
color_mode: "color_temp",
supported_features: 55,
},
},
@@ -263,13 +267,27 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
},
"light.kitchen_lights": {
entity_id: "light.kitchen_lights",
state: "on",
attributes: {
min_mireds: 111,
max_mireds: 400,
brightness: 200,
rgb_color: [255, 175, 96],
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "rgb",
friendly_name: "Kitchen Lights",
supported_features: 55,
},
},
"light.lifx5": {
entity_id: "light.lifx5",
state: "off",
attributes: {
friendly_name: "Kitchen Lights",
supported_color_modes: ["brightness"],
friendly_name: "Garage Lights",
supported_features: 1,
},
},
"sensor.plexspy": {
entity_id: "sensor.plexspy",
state: "0",
@@ -482,16 +500,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
icon: "hademo:history",
},
},
"light.lifx5": {
entity_id: "light.lifx5",
state: "on",
attributes: {
min_mireds: 111,
max_mireds: 400,
friendly_name: "Garage Lights",
supported_features: 55,
},
},
"sensor.alok_to_home": {
entity_id: "sensor.alok_to_home",
state: "41",

View File

@@ -1114,6 +1114,9 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
min_mireds: 153,
max_mireds: 500,
brightness: 63,
color_temp: 200,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
friendly_name: "Upstairs lights",
supported_features: 63,
custom_ui_state_card: "state-card-custom-ui",
@@ -1125,6 +1128,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
friendly_name: "Walk in closet lights",
supported_features: 41,
supported_color_modes: ["brightness", "color_temp"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:wall-sconce",
},
@@ -1136,6 +1140,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
brightness: 254,
friendly_name: "Outdoor lights",
supported_features: 41,
supported_color_modes: ["brightness"],
color_mode: "brightness",
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:wall-sconce",
},
@@ -1148,6 +1154,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500,
brightness: 128,
color_temp: 366,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
effect_list: ["colorloop"],
friendly_name: "Downstairs lights",
supported_features: 63,
@@ -1307,6 +1315,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
min_mireds: 153,
max_mireds: 500,
supported_color_modes: ["brightness", "color_temp"],
is_deconz_group: false,
friendly_name: "Bedside Lamp",
supported_features: 63,
@@ -1320,6 +1329,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
min_mireds: 153,
max_mireds: 500,
supported_color_modes: ["brightness", "color_temp"],
is_deconz_group: false,
friendly_name: "Floorlamp Reading Light",
supported_features: 43,
@@ -1335,6 +1345,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500,
brightness: 128,
color_temp: 366,
supported_color_modes: ["brightness", "color_temp", "rgb"],
color_mode: "color_temp",
effect_list: ["colorloop"],
is_deconz_group: false,
friendly_name: "Hallway window light",
@@ -1349,6 +1361,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
brightness: 77,
is_deconz_group: false,
supported_color_modes: ["brightness"],
friendly_name: "Isa Ceiling Light",
supported_features: 41,
custom_ui_state_card: "state-card-custom-ui",
@@ -1363,6 +1376,8 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
max_mireds: 500,
brightness: 150,
color_temp: 366,
supported_color_modes: ["brightness", "color_temp"],
color_mode: "color_temp",
effect_list: ["colorloop"],
is_deconz_group: false,
friendly_name: "Floorlamp",
@@ -1377,6 +1392,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
friendly_name: "Bedroom Ceiling Light",
supported_features: 41,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light",
},
@@ -1387,6 +1403,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
attributes: {
friendly_name: "Nightlight",
supported_features: 17,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:lamp",
},
@@ -1753,6 +1770,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 2.2,
friendly_name: "Upstairs Hallway Light",
supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light",
},
@@ -1768,6 +1786,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 0,
friendly_name: "Dining Room Light",
supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:ceiling-light",
},
@@ -1783,6 +1802,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 0,
friendly_name: "Living room Spotlights",
supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light",
},
@@ -1799,6 +1819,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 2.5,
friendly_name: "Passage Lights",
supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light",
},
@@ -1843,6 +1864,7 @@ export const demoEntitiesTeachingbirds: DemoConfig["entities"] = () =>
power_consumption: 37.4,
friendly_name: "Kitchen Lights",
supported_features: 33,
supported_color_modes: ["brightness"],
custom_ui_state_card: "state-card-custom-ui",
icon: "mdi:track-light",
},

View File

@@ -15,6 +15,10 @@ class DemoCard extends PolymerElement {
margin: 0 0 20px;
color: var(--primary-color);
}
h2 small {
font-size: 0.5em;
color: var(--primary-text-color);
}
#card {
max-width: 400px;
width: 100vw;
@@ -34,7 +38,12 @@ class DemoCard extends PolymerElement {
}
}
</style>
<h2>[[config.heading]]</h2>
<h2>
[[config.heading]]
<template is="dom-if" if="[[_size]]">
<small>(size [[_size]])</small>
</template>
</h2>
<div class="root">
<div id="card"></div>
<template is="dom-if" if="[[showConfig]]">
@@ -55,6 +64,9 @@ class DemoCard extends PolymerElement {
observer: "_configChanged",
},
showConfig: Boolean,
_size: {
type: Number,
},
};
}
@@ -70,6 +82,17 @@ class DemoCard extends PolymerElement {
const el = this._createCardElement(safeLoad(config.config)[0]);
card.appendChild(el);
this._getSize(el);
}
async _getSize(el) {
await customElements.whenDefined(el.localName);
if (!("getCardSize" in el)) {
this._size = undefined;
return;
}
this._size = await el.getCardSize();
}
_createCardElement(cardConfig) {

View File

@@ -49,6 +49,100 @@ const ENTITIES = [
];
const CONFIGS = [
{
heading: "Default Grid",
config: `
- type: grid
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
- type: entity
entity: device_tracker.demo_anne_therese
`,
},
{
heading: "Non-square Grid with 2 columns",
config: `
- type: grid
columns: 2
square: false
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
`,
},
{
heading: "Default Grid with title",
config: `
- type: grid
title: Kitchen
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
- type: entity
entity: device_tracker.demo_anne_therese
`,
},
{
heading: "Columns 4",
config: `
- type: grid
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
`,
},
{
heading: "Columns 2",
config: `
- type: grid
columns: 2
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
`,
},
{
heading: "Columns 1",
config: `
- type: grid
columns: 1
cards:
- type: entity
entity: light.kitchen_lights
`,
},
{
heading: "Vertical Stack",
config: `
@@ -99,45 +193,9 @@ const CONFIGS = [
entity: light.bed_light
`,
},
{
heading: "Default Grid",
config: `
- type: grid
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
- type: entity
entity: device_tracker.demo_anne_therese
`,
},
{
heading: "Non-square Grid with 2 columns",
config: `
- type: grid
columns: 2
square: false
cards:
- type: entity
entity: light.kitchen_lights
- type: entity
entity: light.bed_light
- type: entity
entity: device_tracker.demo_paulus
- type: sensor
entity: sensor.illumination
graph: line
`,
},
];
@customElement("demo-hui-stack-card")
@customElement("demo-hui-grid-and-stack-card")
class DemoStack extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
@@ -155,4 +213,8 @@ class DemoStack extends LitElement {
}
}
customElements.define("demo-hui-stack-card", DemoStack);
declare global {
interface HTMLElementTagNameMap {
"demo-hui-grid-and-stack-card": DemoStack;
}
}

View File

@@ -9,13 +9,10 @@ import {
} from "lit-element";
import "../../../src/components/ha-card";
import {
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
LightColorModes,
SUPPORT_EFFECT,
SUPPORT_FLASH,
SUPPORT_TRANSITION,
SUPPORT_WHITE_VALUE,
} from "../../../src/data/light";
import "../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../src/fake_data/entity";
@@ -32,7 +29,8 @@ const ENTITIES = [
getEntity("light", "kitchen_light", "on", {
friendly_name: "Brightness Light",
brightness: 200,
supported_features: SUPPORT_BRIGHTNESS,
supported_color_modes: [LightColorModes.BRIGHTNESS],
color_mode: LightColorModes.BRIGHTNESS,
}),
getEntity("light", "color_temperature_light", "on", {
friendly_name: "White Color Temperature Light",
@@ -40,20 +38,96 @@ const ENTITIES = [
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
],
color_mode: LightColorModes.COLOR_TEMP,
}),
getEntity("light", "color_effectslight", "on", {
friendly_name: "Color Effets Light",
getEntity("light", "color_hs_light", "on", {
friendly_name: "Color HS Light",
brightness: 255,
hs_color: [30, 100],
white_value: 36,
supported_features:
SUPPORT_BRIGHTNESS +
SUPPORT_EFFECT +
SUPPORT_FLASH +
SUPPORT_COLOR +
SUPPORT_TRANSITION +
SUPPORT_WHITE_VALUE,
rgb_color: [30, 100, 255],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.HS,
],
color_mode: LightColorModes.HS,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgb_ct_light", "on", {
friendly_name: "Color RGB + CT Light",
brightness: 255,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.RGB,
],
color_mode: LightColorModes.COLOR_TEMP,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_RGB_light", "on", {
friendly_name: "Color Effets Light",
brightness: 255,
rgb_color: [30, 100, 255],
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [LightColorModes.BRIGHTNESS, LightColorModes.RGB],
color_mode: LightColorModes.RGB,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgbw_light", "on", {
friendly_name: "Color RGBW Light",
brightness: 255,
rgbw_color: [30, 100, 255, 125],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.RGBW,
],
color_mode: LightColorModes.RGBW,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_rgbww_light", "on", {
friendly_name: "Color RGBWW Light",
brightness: 255,
rgbww_color: [30, 100, 255, 125, 10],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.RGBWW,
],
color_mode: LightColorModes.RGBWW,
effect_list: ["random", "colorloop"],
}),
getEntity("light", "color_xy_light", "on", {
friendly_name: "Color XY Light",
brightness: 255,
xy_color: [30, 100],
rgb_color: [30, 100, 255],
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_EFFECT + SUPPORT_FLASH + SUPPORT_TRANSITION,
supported_color_modes: [
LightColorModes.BRIGHTNESS,
LightColorModes.COLOR_TEMP,
LightColorModes.XY,
],
color_mode: LightColorModes.XY,
effect_list: ["random", "colorloop"],
}),
];

View File

@@ -138,10 +138,12 @@
"vue": "^2.6.11",
"vue2-daterange-picker": "^0.5.1",
"web-animations-js": "^2.3.2",
"workbox-core": "^5.1.3",
"workbox-precaching": "^5.1.3",
"workbox-routing": "^5.1.3",
"workbox-strategies": "^5.1.3",
"workbox-cacheable-response": "^6.1.5",
"workbox-core": "^6.1.5",
"workbox-expiration": "^6.1.5",
"workbox-precaching": "^6.1.5",
"workbox-routing": "^6.1.5",
"workbox-strategies": "^6.1.5",
"xss": "^1.0.6"
},
"devDependencies": {
@@ -166,7 +168,6 @@
"@types/chai": "^4.1.7",
"@types/chromecast-caf-receiver": "^5.0.11",
"@types/chromecast-caf-sender": "^1.0.3",
"@types/codemirror": "^0.0.97",
"@types/js-yaml": "^3.12.1",
"@types/leaflet": "^1.4.3",
"@types/leaflet-draw": "^1.0.1",
@@ -233,7 +234,7 @@
"webpack-cli": "^4.5.0",
"webpack-dev-server": "^3.11.2",
"webpack-manifest-plugin": "^3.0.0",
"workbox-build": "^5.1.3"
"workbox-build": "^6.1.5"
},
"_comment": "Polymer fixed to 3.1 because 3.2 throws on logbook page",
"_comment_2": "Fix in https://github.com/Polymer/polymer/pull/5569",

View File

@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup(
name="home-assistant-frontend",
version="20210423.0",
version="20210429.0",
description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors",

View File

@@ -102,3 +102,18 @@ export const lab2hex = (lab: [number, number, number]): string => {
const rgb = lab2rgb(lab);
return rgb2hex(rgb);
};
export const rgb2hsv = (
rgb: [number, number, number]
): [number, number, number] => {
const [r, g, b] = rgb;
const v = Math.max(r, g, b);
const c = v - Math.min(r, g, b);
const h =
c && (v === r ? (g - b) / c : v === g ? 2 + (b - r) / c : 4 + (r - g) / c);
return [60 * (h < 0 ? h + 6 : h), v && c / v, v];
};
export const rgb2hs = (rgb: [number, number, number]): [number, number] => {
return rgb2hsv(rgb).slice(0, 2) as [number, number];
};

View File

@@ -99,12 +99,8 @@ export class StateBadge extends LitElement {
hostStyle.backgroundImage = `url(${imageUrl})`;
this._showIcon = false;
} else if (stateObj.state === "on") {
if (stateObj.attributes.hs_color && this.stateColor !== false) {
const hue = stateObj.attributes.hs_color[0];
const sat = stateObj.attributes.hs_color[1];
if (sat > 10) {
iconStyle.color = `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
}
if (this.stateColor !== false && stateObj.attributes.rgb_color) {
iconStyle.color = `rgb(${stateObj.attributes.rgb_color.join(",")})`;
}
if (stateObj.attributes.brightness && this.stateColor !== false) {
const brightness = stateObj.attributes.brightness;

View File

@@ -6,5 +6,6 @@ export const analyticsLearnMore = (hass: HomeAssistant) => html`<a
.href=${documentationUrl(hass, "/integrations/analytics/")}
target="_blank"
rel="noreferrer"
>${hass.localize("ui.panel.config.core.section.core.analytics.learn_more")}</a
>`;
>
How we process your data
</a>`;

View File

@@ -8,7 +8,6 @@ import {
property,
TemplateResult,
} from "lit-element";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { fireEvent } from "../common/dom/fire_event";
import { Analytics, AnalyticsPreferences } from "../data/analytics";
import { haStyle } from "../resources/styles";
@@ -17,7 +16,18 @@ import "./ha-checkbox";
import type { HaCheckbox } from "./ha-checkbox";
import "./ha-settings-row";
const ADDITIONAL_PREFERENCES = ["usage", "statistics"];
const ADDITIONAL_PREFERENCES = [
{
key: "usage",
title: "Usage",
description: "Details of what you use with Home Assistant",
},
{
key: "statistics",
title: "Statistical data",
description: "Counts containing total number of datapoints",
},
];
declare global {
interface HASSDomEvents {
@@ -48,14 +58,10 @@ export class HaAnalytics extends LitElement {
</ha-checkbox>
</span>
<span slot="heading" data-for="base">
${this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.base.title`
)}
Basic analytics
</span>
<span slot="description" data-for="base">
${this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.base.description`
)}
This includes information about your system.
</span>
</ha-settings-row>
${ADDITIONAL_PREFERENCES.map(
@@ -64,44 +70,23 @@ export class HaAnalytics extends LitElement {
<span slot="prefix">
<ha-checkbox
@change=${this._handleRowCheckboxClick}
.checked=${this.analytics?.preferences[preference]}
.preference=${preference}
name=${preference}
.checked=${this.analytics?.preferences[preference.key]}
.preference=${preference.key}
name=${preference.key}
>
</ha-checkbox>
${!baseEnabled
? html`<paper-tooltip animation-delay="0" position="right"
>${this.hass.localize(
"ui.panel.config.core.section.core.analytics.needs_base"
)}
? html`<paper-tooltip animation-delay="0" position="right">
You need to enable basic analytics for this option to be
available
</paper-tooltip>`
: ""}
</span>
<span slot="heading" data-for=${preference}>
${preference === "usage"
? isComponentLoaded(this.hass, "hassio")
? this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.usage_supervisor.title`
)
: this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.usage.title`
)
: this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.${preference}.title`
)}
<span slot="heading" data-for=${preference.key}>
${preference.title}
</span>
<span slot="description" data-for=${preference}>
${preference !== "usage"
? this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.${preference}.description`
)
: isComponentLoaded(this.hass, "hassio")
? this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.usage_supervisor.description`
)
: this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.usage.description`
)}
<span slot="description" data-for=${preference.key}>
${preference.description}
</span>
</ha-settings-row>`
)}
@@ -117,14 +102,10 @@ export class HaAnalytics extends LitElement {
</ha-checkbox>
</span>
<span slot="heading" data-for="diagnostics">
${this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.diagnostics.title`
)}
Diagnostics
</span>
<span slot="description" data-for="diagnostics">
${this.hass.localize(
`ui.panel.config.core.section.core.analytics.preference.diagnostics.description`
)}
Share crash reports when unexpected errors occur.
</span>
</ha-settings-row>
`;
@@ -161,7 +142,10 @@ export class HaAnalytics extends LitElement {
preferences[preference] = checkbox.checked;
if (ADDITIONAL_PREFERENCES.includes(preference) && checkbox.checked) {
if (
ADDITIONAL_PREFERENCES.some((entry) => entry.key === preference) &&
checkbox.checked
) {
preferences.base = true;
} else if (preference === "base" && !checkbox.checked) {
preferences.usage = false;

View File

@@ -9,6 +9,7 @@ import {
property,
TemplateResult,
} from "lit-element";
import { styleMap } from "lit-html/directives/style-map";
import { fireEvent } from "../common/dom/fire_event";
import type { ToggleButton } from "../types";
import "./ha-svg-icon";
@@ -19,6 +20,8 @@ export class HaButtonToggleGroup extends LitElement {
@property() public active?: string;
@property({ type: Boolean }) public fullWidth = false;
protected render(): TemplateResult {
return html`
<div>
@@ -33,6 +36,11 @@ export class HaButtonToggleGroup extends LitElement {
<ha-svg-icon .path=${button.iconPath}></ha-svg-icon>
</mwc-icon-button>`
: html`<mwc-button
style=${styleMap({
width: this.fullWidth
? `${100 / this.buttons.length}%`
: "initial",
})}
.value=${button.value}
?active=${this.active === button.value}
@click=${this._handleClick}

View File

@@ -2,7 +2,7 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { EventsMixin } from "../mixins/events-mixin";
import { rgb2hs } from "../common/color/convert-color";
/**
* Color-picker custom element
*
@@ -114,6 +114,12 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
observer: "applyHsColor",
},
// use these properties to update the state via attributes
desiredRgbColor: {
type: Object,
observer: "applyRgbColor",
},
// width, height and radius apply to the coordinates of
// of the canvas.
// border width are relative to these numbers
@@ -177,8 +183,11 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
this.drawMarker();
if (this.desiredHsColor) {
this.setMarkerOnColor(this.desiredHsColor);
this.applyColorToCanvas(this.desiredHsColor);
this.applyHsColor(this.desiredHsColor);
}
if (this.desiredRgbColor) {
this.applyRgbColor(this.desiredRgbColor);
}
this.interactionLayer.addEventListener("mousedown", (ev) =>
@@ -282,12 +291,13 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
processUserSelect(ev) {
const canvasXY = this.convertToCanvasCoordinates(ev.clientX, ev.clientY);
const hs = this.getColor(canvasXY.x, canvasXY.y);
this.onColorSelect(hs);
const rgb = this.getRgbColor(canvasXY.x, canvasXY.y);
this.onColorSelect(hs, rgb);
}
// apply color to marker position and canvas
onColorSelect(hs) {
this.setMarkerOnColor(hs); // marker always follows mounse 'raw' hs value (= mouse position)
onColorSelect(hs, rgb) {
this.setMarkerOnColor(hs); // marker always follows mouse 'raw' hs value (= mouse position)
if (!this.ignoreSegments) {
// apply segments if needed
hs = this.applySegmentFilter(hs);
@@ -301,11 +311,11 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
// eventually after throttle limit has passed
clearTimeout(this.ensureFinalSelect);
this.ensureFinalSelect = setTimeout(() => {
this.fireColorSelected(hs); // do it for the final time
this.fireColorSelected(hs, rgb); // do it for the final time
}, this.throttle);
return;
}
this.fireColorSelected(hs); // do it
this.fireColorSelected(hs, rgb); // do it
this.colorSelectIsThrottled = true;
setTimeout(() => {
this.colorSelectIsThrottled = false;
@@ -313,9 +323,9 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
}
// set color values and fire colorselected event
fireColorSelected(hs) {
fireColorSelected(hs, rgb) {
this.hsColor = hs;
this.fire("colorselected", { hs: { h: hs.h, s: hs.s } });
this.fire("colorselected", { hs, rgb });
}
/*
@@ -363,6 +373,11 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
this.applyColorToCanvas(hs);
}
applyRgbColor(rgb) {
const [h, s] = rgb2hs(rgb);
this.applyHsColor({ h, s });
}
/*
* input processing helpers
*/
@@ -395,6 +410,15 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
return { h: hue, s: sat };
}
getRgbColor(x, y) {
// get current pixel
const imageData = this.backgroundLayer
.getContext("2d")
.getImageData(x + 250, y + 250, 1, 1);
const pixel = imageData.data;
return { r: pixel[0], g: pixel[1], b: pixel[2] };
}
applySegmentFilter(hs) {
// apply hue segment steps
if (this.hueSegments) {
@@ -468,7 +492,7 @@ class HaColorPicker extends EventsMixin(PolymerElement) {
.getPropertyValue("--wheel-bordercolor")
.trim();
const wheelShadow = wheelStyle.getPropertyValue("--wheel-shadow").trim();
// extract shadow properties from CCS variable
// extract shadow properties from CSS variable
// the shadow should be defined as: "10px 5px 5px 0px COLOR"
if (wheelShadow !== "none") {
const values = wheelShadow.split("px ");

View File

@@ -143,7 +143,7 @@ class HatScriptGraph extends LitElement {
const trace = this.trace.trace[path] as ChooseActionTraceStep[] | undefined;
const trace_path = trace?.[0].result
? trace[0].result.choice === "default"
? [config.choose?.length || 0]
? [Array.isArray(config.choose) ? config.choose.length : 0]
: [trace[0].result.choice]
: [];
return html`
@@ -167,31 +167,33 @@ class HatScriptGraph extends LitElement {
nofocus
></hat-graph-node>
${config.choose?.map((branch, i) => {
const branch_path = `${path}/choose/${i}`;
const track_this =
trace !== undefined && trace[0].result?.choice === i;
if (track_this) {
this.trackedNodes[branch_path] = { config, path: branch_path };
}
return html`
<hat-graph>
<hat-graph-node
.iconPath=${!trace || track_this
? mdiCheckBoxOutline
: mdiCheckboxBlankOutline}
@focus=${this.selectNode(config, branch_path)}
class=${classMap({
active: this.selected === branch_path,
track: track_this,
})}
></hat-graph-node>
${ensureArray(branch.sequence).map((action, j) =>
this.render_node(action, `${branch_path}/sequence/${j}`)
)}
</hat-graph>
`;
})}
${config.choose
? ensureArray(config.choose)?.map((branch, i) => {
const branch_path = `${path}/choose/${i}`;
const track_this =
trace !== undefined && trace[0].result?.choice === i;
if (track_this) {
this.trackedNodes[branch_path] = { config, path: branch_path };
}
return html`
<hat-graph>
<hat-graph-node
.iconPath=${!trace || track_this
? mdiCheckBoxOutline
: mdiCheckboxBlankOutline}
@focus=${this.selectNode(config, branch_path)}
class=${classMap({
active: this.selected === branch_path,
track: track_this,
})}
></hat-graph-node>
${ensureArray(branch.sequence).map((action, j) =>
this.render_node(action, `${branch_path}/sequence/${j}`)
)}
</hat-graph>
`;
})
: ""}
<hat-graph>
<hat-graph-spacer
class=${classMap({

View File

@@ -24,6 +24,11 @@ export interface IntegrationManifest {
| "local_push";
}
export interface IntegrationSetup {
domain: string;
seconds?: number;
}
export const integrationIssuesUrl = (
domain: string,
manifest: IntegrationManifest
@@ -44,3 +49,6 @@ export const fetchIntegrationManifest = (
hass: HomeAssistant,
integration: string
) => hass.callWS<IntegrationManifest>({ type: "manifest/get", integration });
export const fetchIntegrationSetups = (hass: HomeAssistant) =>
hass.callWS<IntegrationSetup[]>({ type: "integration/setup_info" });

View File

@@ -3,26 +3,84 @@ import {
HassEntityBase,
} from "home-assistant-js-websocket";
export enum LightColorModes {
UNKNOWN = "unknown",
ONOFF = "onoff",
BRIGHTNESS = "brightness",
COLOR_TEMP = "color_temp",
HS = "hs",
XY = "xy",
RGB = "rgb",
RGBW = "rgbw",
RGBWW = "rgbww",
}
const modesSupportingColor = [
LightColorModes.HS,
LightColorModes.XY,
LightColorModes.RGB,
LightColorModes.RGBW,
LightColorModes.RGBWW,
];
const modesSupportingDimming = [
...modesSupportingColor,
LightColorModes.COLOR_TEMP,
LightColorModes.BRIGHTNESS,
];
export const SUPPORT_EFFECT = 4;
export const SUPPORT_FLASH = 8;
export const SUPPORT_TRANSITION = 32;
export const lightSupportsColorMode = (
entity: LightEntity,
mode: LightColorModes
) => {
return entity.attributes.supported_color_modes?.includes(mode);
};
export const lightIsInColorMode = (entity: LightEntity) => {
return modesSupportingColor.includes(entity.attributes.color_mode);
};
export const lightSupportsColor = (entity: LightEntity) => {
return entity.attributes.supported_color_modes?.some((mode) =>
modesSupportingColor.includes(mode)
);
};
export const lightSupportsDimming = (entity: LightEntity) => {
return entity.attributes.supported_color_modes?.some((mode) =>
modesSupportingDimming.includes(mode)
);
};
export const getLightCurrentModeRgbColor = (
entity: LightEntity
): number[] | undefined =>
entity.attributes.color_mode === LightColorModes.RGBWW
? entity.attributes.rgbww_color
: entity.attributes.color_mode === LightColorModes.RGBW
? entity.attributes.rgbw_color
: entity.attributes.rgb_color;
interface LightEntityAttributes extends HassEntityAttributeBase {
min_mireds: number;
max_mireds: number;
friendly_name: string;
brightness: number;
hs_color: number[];
hs_color: [number, number];
rgb_color: [number, number, number];
rgbw_color: [number, number, number, number];
rgbww_color: [number, number, number, number, number];
color_temp: number;
white_value: number;
effect?: string;
effect_list: string[] | null;
supported_color_modes: LightColorModes[];
color_mode: LightColorModes;
}
export interface LightEntity extends HassEntityBase {
attributes: LightEntityAttributes;
}
export const SUPPORT_BRIGHTNESS = 1;
export const SUPPORT_COLOR_TEMP = 2;
export const SUPPORT_EFFECT = 4;
export const SUPPORT_FLASH = 8;
export const SUPPORT_COLOR = 16;
export const SUPPORT_TRANSITION = 32;
export const SUPPORT_WHITE_VALUE = 128;

View File

@@ -20,7 +20,7 @@ export interface LovelacePanelConfig {
export interface LovelaceConfig {
title?: string;
strategy?: {
name: string;
type: string;
options?: Record<string, unknown>;
};
views: LovelaceViewConfig[];
@@ -82,7 +82,7 @@ export interface LovelaceViewConfig {
title?: string;
type?: string;
strategy?: {
name: string;
type: string;
options?: Record<string, unknown>;
};
badges?: Array<string | LovelaceBadgeConfig>;

View File

@@ -112,7 +112,7 @@ export interface ChooseActionChoice {
export interface ChooseAction {
alias?: string;
choose: ChooseActionChoice[] | null;
choose: ChooseActionChoice | ChooseActionChoice[] | null;
default?: Action | Action[];
}

View File

@@ -55,6 +55,52 @@ export interface Cluster {
type: string;
}
export interface ClusterConfigurationData {
cluster_name: string;
cluster_id: number;
success: boolean;
}
export interface ClusterAttributeData {
cluster_name: string;
cluster_id: number;
attributes: AttributeConfigurationStatus[];
}
export interface AttributeConfigurationStatus {
id: number;
name: string;
success: boolean | undefined;
min: number;
max: number;
change: number;
}
export interface ClusterConfigurationStatus {
cluster: Cluster;
bindSuccess: boolean | undefined;
attributes: Map<number, AttributeConfigurationStatus>;
}
interface ClusterConfigurationBindEvent {
type: "zha_channel_bind";
zha_channel_msg_data: ClusterConfigurationData;
}
interface ClusterConfigurationReportConfigurationEvent {
type: "zha_channel_configure_reporting";
zha_channel_msg_data: ClusterAttributeData;
}
interface ClusterConfigurationEventFinish {
type: "zha_channel_cfg_done";
}
export type ClusterConfigurationEvent =
| ClusterConfigurationReportConfigurationEvent
| ClusterConfigurationBindEvent
| ClusterConfigurationEventFinish;
export interface Command {
name: string;
id: number;
@@ -89,10 +135,10 @@ export interface ZHAGroupMember {
export const reconfigureNode = (
hass: HomeAssistant,
ieeeAddress: string,
callbackFunction: any
callbackFunction: (message: ClusterConfigurationEvent) => void
) => {
return hass.connection.subscribeMessage(
(message) => callbackFunction(message),
(message: ClusterConfigurationEvent) => callbackFunction(message),
{
type: "zha/devices/reconfigure",
ieee: ieeeAddress,
@@ -323,3 +369,7 @@ export const DEVICE_MESSAGE_TYPES = [
DEVICE_FULLY_INITIALIZED,
];
export const LOG_OUTPUT = "log_output";
export const ZHA_CHANNEL_MSG = "zha_channel_message";
export const ZHA_CHANNEL_MSG_BIND = "zha_channel_bind";
export const ZHA_CHANNEL_MSG_CFG_RPT = "zha_channel_configure_reporting";
export const ZHA_CHANNEL_CFG_DONE = "zha_channel_cfg_done";

View File

@@ -1,3 +1,4 @@
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { HomeAssistant } from "../types";
import { DeviceRegistryEntry } from "./device_registry";
@@ -71,6 +72,11 @@ export interface ZWaveJSDataCollectionStatus {
opted_in: boolean;
}
export interface ZWaveJSRefreshNodeStatusMessage {
event: string;
stage?: string;
}
export enum NodeStatus {
Unknown,
Asleep,
@@ -151,9 +157,25 @@ export const setNodeConfigParameter = (
return hass.callWS(data);
};
export const getIdentifiersFromDevice = function (
export const reinterviewNode = (
hass: HomeAssistant,
entry_id: string,
node_id: number,
callbackFunction: (message: ZWaveJSRefreshNodeStatusMessage) => void
): Promise<UnsubscribeFunc> => {
return hass.connection.subscribeMessage(
(message: any) => callbackFunction(message),
{
type: "zwave_js/refresh_node_info",
entry_id: entry_id,
node_id: node_id,
}
);
};
export const getIdentifiersFromDevice = (
device: DeviceRegistryEntry
): ZWaveJSNodeIdentifiers | undefined {
): ZWaveJSNodeIdentifiers | undefined => {
if (!device) {
return undefined;
}
@@ -171,3 +193,48 @@ export const getIdentifiersFromDevice = function (
home_id: identifiers[0],
};
};
export interface ZWaveJSLogMessage {
timestamp: string;
level: string;
primary_tags: string;
message: string | string[];
}
export const subscribeZWaveJSLogs = (
hass: HomeAssistant,
entry_id: string,
callback: (message: ZWaveJSLogMessage) => void
) =>
hass.connection.subscribeMessage<ZWaveJSLogMessage>(callback, {
type: "zwave_js/subscribe_logs",
entry_id,
});
export interface ZWaveJSLogConfig {
level: string;
enabled: boolean;
filename: string;
log_to_file: boolean;
force_console: boolean;
}
export const fetchZWaveJSLogConfig = (
hass: HomeAssistant,
entry_id: string
): Promise<ZWaveJSLogConfig> =>
hass.callWS({
type: "zwave_js/get_log_config",
entry_id,
});
export const setZWaveJSLogLevel = (
hass: HomeAssistant,
entry_id: string,
level: string
): Promise<ZWaveJSLogConfig> =>
hass.callWS({
type: "zwave_js/update_log_config",
entry_id,
config: { level },
});

View File

@@ -11,7 +11,6 @@ import {
PropertyValues,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-attributes";
import "../../../components/ha-color-picker";
@@ -19,19 +18,22 @@ import "../../../components/ha-icon-button";
import "../../../components/ha-labeled-slider";
import "../../../components/ha-paper-dropdown-menu";
import {
getLightCurrentModeRgbColor,
LightColorModes,
LightEntity,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
lightIsInColorMode,
lightSupportsColor,
lightSupportsColorMode,
lightSupportsDimming,
SUPPORT_EFFECT,
SUPPORT_WHITE_VALUE,
} from "../../../data/light";
import type { HomeAssistant } from "../../../types";
import "../../../components/ha-button-toggle-group";
interface HueSatColor {
h: number;
s: number;
}
const toggleButtons = [
{ label: "Color", value: "color" },
{ label: "Temperature", value: LightColorModes.COLOR_TEMP },
];
@customElement("more-info-light")
class MoreInfoLight extends LitElement {
@@ -41,28 +43,51 @@ class MoreInfoLight extends LitElement {
@internalProperty() private _brightnessSliderValue = 0;
@internalProperty() private _ctSliderValue = 0;
@internalProperty() private _ctSliderValue?: number;
@internalProperty() private _wvSliderValue = 0;
@internalProperty() private _cwSliderValue?: number;
@internalProperty() private _wwSliderValue?: number;
@internalProperty() private _wvSliderValue?: number;
@internalProperty() private _colorBrightnessSliderValue?: number;
@internalProperty() private _brightnessAdjusted?: number;
@internalProperty() private _hueSegments = 24;
@internalProperty() private _saturationSegments = 8;
@internalProperty() private _colorPickerColor?: HueSatColor;
@internalProperty() private _colorPickerColor?: [number, number, number];
@internalProperty() private _mode?: "color" | LightColorModes.COLOR_TEMP;
protected render(): TemplateResult {
if (!this.hass || !this.stateObj) {
return html``;
}
const supportsTemp = lightSupportsColorMode(
this.stateObj,
LightColorModes.COLOR_TEMP
);
const supportsRgbww = lightSupportsColorMode(
this.stateObj,
LightColorModes.RGBWW
);
const supportsRgbw =
!supportsRgbww &&
lightSupportsColorMode(this.stateObj, LightColorModes.RGBW);
const supportsColor =
supportsRgbww || supportsRgbw || lightSupportsColor(this.stateObj);
return html`
<div
class="content ${classMap({
"is-on": this.stateObj.state === "on",
})}"
>
${supportsFeature(this.stateObj!, SUPPORT_BRIGHTNESS)
<div class="content">
${lightSupportsDimming(this.stateObj)
? html`
<ha-labeled-slider
caption=${this.hass.localize("ui.card.light.brightness")}
@@ -77,7 +102,17 @@ class MoreInfoLight extends LitElement {
: ""}
${this.stateObj.state === "on"
? html`
${supportsFeature(this.stateObj, SUPPORT_COLOR_TEMP)
${supportsTemp || supportsColor ? html`<hr></hr>` : ""}
${supportsTemp && supportsColor
? html`<ha-button-toggle-group
fullWidth
.buttons=${toggleButtons}
.active=${this._mode}
@value-changed=${this._modeChanged}
></ha-button-toggle-group>`
: ""}
${supportsTemp &&
(!supportsColor || this._mode === LightColorModes.COLOR_TEMP)
? html`
<ha-labeled-slider
class="color_temp"
@@ -91,27 +126,16 @@ class MoreInfoLight extends LitElement {
@change=${this._ctSliderChanged}
pin
></ha-labeled-slider>
<hr></hr>
`
: ""}
${supportsFeature(this.stateObj, SUPPORT_WHITE_VALUE)
? html`
<ha-labeled-slider
caption=${this.hass.localize("ui.card.light.white_value")}
icon="hass:file-word-box"
max="255"
.value=${this._wvSliderValue}
@change=${this._wvSliderChanged}
pin
></ha-labeled-slider>
`
: ""}
${supportsFeature(this.stateObj, SUPPORT_COLOR)
${supportsColor && (!supportsTemp || this._mode === "color")
? html`
<div class="segmentationContainer">
<ha-color-picker
class="color"
@colorselected=${this._colorPicked}
.desiredHsColor=${this._colorPickerColor}
.desiredRgbColor=${this._colorPickerColor}
throttle="500"
.hueSegments=${this._hueSegments}
.saturationSegments=${this._saturationSegments}
@@ -123,6 +147,67 @@ class MoreInfoLight extends LitElement {
class="segmentationButton"
></ha-icon-button>
</div>
${
supportsRgbw || supportsRgbww
? html`<ha-labeled-slider
.caption=${this.hass.localize(
"ui.card.light.color_brightness"
)}
icon="hass:brightness-7"
max="100"
.value=${this._colorBrightnessSliderValue ?? 100}
@change=${this._colorBrightnessSliderChanged}
pin
></ha-labeled-slider>`
: ""
}
${
supportsRgbw
? html`
<ha-labeled-slider
.caption=${this.hass.localize(
"ui.card.light.white_value"
)}
icon="hass:file-word-box"
max="100"
.name=${"wv"}
.value=${this._wvSliderValue}
@change=${this._wvSliderChanged}
pin
></ha-labeled-slider>
`
: ""
}
${
supportsRgbww
? html`
<ha-labeled-slider
.caption=${this.hass.localize(
"ui.card.light.cold_white_value"
)}
icon="hass:file-word-box-outline"
max="100"
.name=${"cw"}
.value=${this._cwSliderValue}
@change=${this._wvSliderChanged}
pin
></ha-labeled-slider>
<ha-labeled-slider
.caption=${this.hass.localize(
"ui.card.light.warm_white_value"
)}
icon="hass:file-word-box"
max="100"
.name=${"ww"}
.value=${this._wwSliderValue}
@change=${this._wvSliderChanged}
pin
></ha-labeled-slider>
`
: ""
}
<hr></hr>
`
: ""}
${supportsFeature(this.stateObj, SUPPORT_EFFECT) &&
@@ -151,34 +236,86 @@ class MoreInfoLight extends LitElement {
: ""}
<ha-attributes
.stateObj=${this.stateObj}
extra-filters="brightness,color_temp,white_value,effect_list,effect,hs_color,rgb_color,xy_color,min_mireds,max_mireds,entity_id,supported_color_modes,color_mode"
extra-filters="brightness,color_temp,white_value,effect_list,effect,hs_color,rgb_color,rgbw_color,rgbww_color,xy_color,min_mireds,max_mireds,entity_id,supported_color_modes,color_mode"
></ha-attributes>
</div>
`;
}
protected updated(changedProps: PropertyValues): void {
protected updated(changedProps: PropertyValues<this>) {
if (!changedProps.has("stateObj")) {
return;
}
const stateObj = this.stateObj! as LightEntity;
if (changedProps.has("stateObj")) {
if (stateObj.state === "on") {
this._brightnessSliderValue = Math.round(
(stateObj.attributes.brightness * 100) / 255
);
this._ctSliderValue = stateObj.attributes.color_temp;
this._wvSliderValue = stateObj.attributes.white_value;
const oldStateObj = changedProps.get("stateObj") as LightEntity | undefined;
if (stateObj.attributes.hs_color) {
this._colorPickerColor = {
h: stateObj.attributes.hs_color[0],
s: stateObj.attributes.hs_color[1] / 100,
};
if (stateObj.state === "on") {
// Don't change tab when the color mode changes
if (
oldStateObj?.entity_id !== stateObj.entity_id ||
oldStateObj?.state !== stateObj.state
) {
this._mode = lightIsInColorMode(this.stateObj!)
? "color"
: LightColorModes.COLOR_TEMP;
}
let brightnessAdjust = 100;
if (
stateObj.attributes.color_mode === LightColorModes.RGB &&
!lightSupportsColorMode(stateObj, LightColorModes.RGBWW) &&
!lightSupportsColorMode(stateObj, LightColorModes.RGBW)
) {
const maxVal = Math.max(...stateObj.attributes.rgb_color);
if (maxVal < 255) {
this._brightnessAdjusted = maxVal;
brightnessAdjust = (this._brightnessAdjusted / 255) * 100;
}
} else {
this._brightnessSliderValue = 0;
this._brightnessAdjusted = undefined;
}
this._brightnessSliderValue = Math.round(
(stateObj.attributes.brightness * brightnessAdjust) / 255
);
this._ctSliderValue = stateObj.attributes.color_temp;
this._wvSliderValue =
stateObj.attributes.color_mode === LightColorModes.RGBW
? Math.round((stateObj.attributes.rgbw_color[3] * 100) / 255)
: undefined;
this._cwSliderValue =
stateObj.attributes.color_mode === LightColorModes.RGBWW
? Math.round((stateObj.attributes.rgbww_color[3] * 100) / 255)
: undefined;
this._wwSliderValue =
stateObj.attributes.color_mode === LightColorModes.RGBWW
? Math.round((stateObj.attributes.rgbww_color[4] * 100) / 255)
: undefined;
this._colorBrightnessSliderValue =
stateObj.attributes.color_mode === LightColorModes.RGBWW
? Math.round(
(Math.max(...stateObj.attributes.rgbww_color.slice(0, 3)) * 100) /
255
)
: stateObj.attributes.color_mode === LightColorModes.RGBW
? Math.round(
(Math.max(...stateObj.attributes.rgbw_color.slice(0, 3)) * 100) /
255
)
: undefined;
this._colorPickerColor = getLightCurrentModeRgbColor(stateObj)?.slice(
0,
3
) as [number, number, number] | undefined;
} else {
this._brightnessSliderValue = 0;
}
}
private _modeChanged(ev: CustomEvent) {
this._mode = ev.detail.value;
}
private _effectChanged(ev: CustomEvent) {
const newVal = ev.detail.item.itemName;
@@ -193,12 +330,29 @@ class MoreInfoLight extends LitElement {
}
private _brightnessSliderChanged(ev: CustomEvent) {
const bri = parseInt((ev.target as any).value, 10);
const bri = Number((ev.target as any).value);
if (isNaN(bri)) {
return;
}
if (this._brightnessAdjusted) {
const rgb =
this.stateObj!.attributes.rgb_color ||
([0, 0, 0] as [number, number, number]);
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
brightness_pct: bri,
rgb_color: this._adjustColorBrightness(
rgb,
this._brightnessAdjusted,
true
),
});
return;
}
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
brightness_pct: bri,
@@ -206,7 +360,7 @@ class MoreInfoLight extends LitElement {
}
private _ctSliderChanged(ev: CustomEvent) {
const ct = parseInt((ev.target as any).value, 10);
const ct = Number((ev.target as any).value);
if (isNaN(ct)) {
return;
@@ -219,18 +373,70 @@ class MoreInfoLight extends LitElement {
}
private _wvSliderChanged(ev: CustomEvent) {
const wv = parseInt((ev.target as any).value, 10);
const target = ev.target as any;
let wv = Number(target.value);
const name = target.name;
if (isNaN(wv)) {
return;
}
wv = Math.min(255, Math.round((wv * 255) / 100));
const rgb = getLightCurrentModeRgbColor(this.stateObj!);
if (name === "wv") {
const rgbw_color = rgb || [0, 0, 0, 0];
rgbw_color[3] = wv;
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
rgbw_color,
});
return;
}
const rgbww_color = rgb || [0, 0, 0, 0, 0];
while (rgbww_color.length < 5) {
rgbww_color.push(0);
}
rgbww_color[name === "cw" ? 3 : 4] = wv;
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
white_value: wv,
rgbww_color,
});
}
private _colorBrightnessSliderChanged(ev: CustomEvent) {
const target = ev.target as any;
let value = Number(target.value);
if (isNaN(value)) {
return;
}
value = (value * 255) / 100;
const rgb = (getLightCurrentModeRgbColor(this.stateObj!)?.slice(0, 3) || [
255,
255,
255,
]) as [number, number, number];
this._setRgbColor(
this._adjustColorBrightness(
// first normalize the value
this._colorBrightnessSliderValue
? this._adjustColorBrightness(
rgb,
(this._colorBrightnessSliderValue * 255) / 100,
true
)
: rgb,
value
)
);
}
private _segmentClick() {
if (this._hueSegments === 24 && this._saturationSegments === 8) {
this._hueSegments = 0;
@@ -241,15 +447,90 @@ class MoreInfoLight extends LitElement {
}
}
private _adjustColorBrightness(
rgbColor: [number, number, number],
value?: number,
invert = false
) {
if (value !== undefined && value !== 255) {
let ratio = value / 255;
if (invert) {
ratio = 1 / ratio;
}
rgbColor[0] = Math.min(255, Math.round(rgbColor[0] * ratio));
rgbColor[1] = Math.min(255, Math.round(rgbColor[1] * ratio));
rgbColor[2] = Math.min(255, Math.round(rgbColor[2] * ratio));
}
return rgbColor;
}
private _setRgbColor(rgbColor: [number, number, number]) {
if (lightSupportsColorMode(this.stateObj!, LightColorModes.RGBWW)) {
const rgbww_color: [number, number, number, number, number] = this
.stateObj!.attributes.rgbww_color
? [...this.stateObj!.attributes.rgbww_color]
: [0, 0, 0, 0, 0];
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
rgbww_color: rgbColor.concat(rgbww_color.slice(3)),
});
} else if (lightSupportsColorMode(this.stateObj!, LightColorModes.RGBW)) {
const rgbw_color: [number, number, number, number] = this.stateObj!
.attributes.rgbw_color
? [...this.stateObj!.attributes.rgbw_color]
: [0, 0, 0, 0];
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
rgbw_color: rgbColor.concat(rgbw_color.slice(3)),
});
}
}
/**
* Called when a new color has been picked.
* should be throttled with the 'throttle=' attribute of the color picker
*/
private _colorPicked(ev: CustomEvent) {
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
hs_color: [ev.detail.hs.h, ev.detail.hs.s * 100],
});
if (
lightSupportsColorMode(this.stateObj!, LightColorModes.RGBWW) ||
lightSupportsColorMode(this.stateObj!, LightColorModes.RGBW)
) {
this._setRgbColor(
this._colorBrightnessSliderValue
? this._adjustColorBrightness(
[ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b],
(this._colorBrightnessSliderValue * 255) / 100
)
: [ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b]
);
} else if (lightSupportsColorMode(this.stateObj!, LightColorModes.RGB)) {
const rgb_color = [ev.detail.rgb.r, ev.detail.rgb.g, ev.detail.rgb.b] as [
number,
number,
number
];
if (this._brightnessAdjusted) {
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
brightness_pct: this._brightnessSliderValue,
rgb_color: this._adjustColorBrightness(
rgb_color,
this._brightnessAdjusted,
true
),
});
} else {
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
rgb_color,
});
}
} else {
this.hass.callService("light", "turn_on", {
entity_id: this.stateObj!.entity_id,
hs_color: [ev.detail.hs.h, ev.detail.hs.s * 100],
});
}
}
static get styles(): CSSResult {
@@ -275,11 +556,18 @@ class MoreInfoLight extends LitElement {
);
/* The color temp minimum value shouldn't be rendered differently. It's not "off". */
--paper-slider-knob-start-border-color: var(--primary-color);
margin-bottom: 4px;
}
.segmentationContainer {
position: relative;
max-height: 500px;
display: flex;
justify-content: center;
}
ha-button-toggle-group {
margin: 8px 0px;
}
ha-color-picker {
@@ -293,12 +581,19 @@ class MoreInfoLight extends LitElement {
.segmentationButton {
position: absolute;
top: 5%;
left: 0;
color: var(--secondary-text-color);
}
paper-item {
cursor: pointer;
}
hr {
border-color: var(--divider-color);
border-bottom: none;
margin: 8px 0;
}
`;
}
}

View File

@@ -10,9 +10,11 @@ import {
NetworkOnly,
StaleWhileRevalidate,
} from "workbox-strategies";
import { CacheableResponsePlugin } from "workbox-cacheable-response";
import { ExpirationPlugin } from "workbox-expiration";
const noFallBackRegEx = new RegExp(
`${location.host}/(api|static|auth|frontend_latest|frontend_es5|local)/.*`
"/(api|static|auth|frontend_latest|frontend_es5|local)/.*"
);
// Clean up caches from older workboxes and old service workers.
@@ -31,27 +33,22 @@ function initRouting() {
// Cache static content (including translations) on first access.
registerRoute(
new RegExp(`${location.host}/(static|frontend_latest|frontend_es5)/.+`),
new RegExp("/(static|frontend_latest|frontend_es5)/.+"),
new CacheFirst({ matchOptions: { ignoreSearch: true } })
);
// Get api from network.
registerRoute(
new RegExp(`${location.host}/(api|auth)/.*`),
new NetworkOnly()
);
registerRoute(new RegExp("/(api|auth)/.*"), new NetworkOnly());
// Get manifest, service worker, onboarding from network.
registerRoute(
new RegExp(
`${location.host}/(service_worker.js|manifest.json|onboarding.html)`
),
new RegExp("/(service_worker.js|manifest.json|onboarding.html)"),
new NetworkOnly()
);
// For the root "/" we ignore search
registerRoute(
new RegExp(`^${location.host}/(\\?.*)?$`),
new RegExp(/\/(\?.*)?$/),
new StaleWhileRevalidate({ matchOptions: { ignoreSearch: true } })
);
@@ -59,7 +56,20 @@ function initRouting() {
// This includes "/states" response and user files from "/local".
// First access might bring stale data from cache, but a single refresh will bring updated
// file.
registerRoute(new RegExp(`${location.host}/.*`), new StaleWhileRevalidate());
registerRoute(
new RegExp(/\/.*/),
new StaleWhileRevalidate({
cacheName: "file-cache",
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxAgeSeconds: 60 * 60 * 24,
}),
],
})
);
}
function initPushNotifications() {

View File

@@ -81,9 +81,27 @@ class LightEntity extends Entity {
if (service === "turn_on") {
// eslint-disable-next-line
let { brightness, hs_color, brightness_pct } = data;
brightness = (255 * brightness_pct) / 100;
this.update("on", { ...this.attributes, brightness, hs_color });
let { hs_color, brightness_pct, rgb_color, color_temp } = data;
const attrs = { ...this.attributes };
if (brightness_pct) {
attrs.brightness = (255 * brightness_pct) / 100;
} else if (!attrs.brightness) {
attrs.brightness = 255;
}
if (hs_color) {
attrs.color_mode = "hs";
attrs.hs_color = hs_color;
}
if (rgb_color) {
attrs.color_mode = "rgb";
attrs.rgb_color = rgb_color;
}
if (color_temp) {
attrs.color_mode = "color_temp";
attrs.color_temp = color_temp;
delete attrs.rgb_color;
}
this.update("on", attrs);
} else if (service === "turn_off") {
this.update("off");
} else if (service === "toggle") {

View File

@@ -2,7 +2,11 @@
<html>
<head>
<title>Home Assistant</title>
<link rel="modulepreload" href="<%= latestPageJS %>" crossorigin="use-credentials" />
<link
rel="modulepreload"
href="<%= latestPageJS %>"
crossorigin="use-credentials"
/>
<%= renderTemplate('_header') %>
<style>
html {
@@ -15,8 +19,19 @@
border-radius: 4px;
max-width: 432px;
margin: 64px auto 0;
box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px, rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px;
background-color: #fff;
box-shadow: var(
--ha-card-box-shadow,
rgba(0, 0, 0, 0.25) 0px 54px 55px,
rgba(0, 0, 0, 0.12) 0px -12px 30px,
rgba(0, 0, 0, 0.12) 0px 4px 6px,
rgba(0, 0, 0, 0.17) 0px 12px 13px,
rgba(0, 0, 0, 0.09) 0px -3px 5px
);
background: var(
--ha-card-background,
var(--card-background-color, white)
);
color: var(--primary-text-color, #212121);
}
.header {
@@ -34,7 +49,6 @@
@media (prefers-color-scheme: dark) {
html {
background-color: #111111;
color: #e1e1e1;
}
}
@@ -45,10 +59,9 @@
margin: 0;
}
}
</style>
</head>
<body id='particles'>
<body id="particles">
<div class="content">
<div class="header">
<img src="/static/icons/favicon-192x192.png" height="52" width="52" />

View File

@@ -32,13 +32,9 @@ class OnboardingAnalytics extends LitElement {
protected render(): TemplateResult {
return html`
<p>
${this.hass.localize(
"ui.panel.config.core.section.core.analytics.introduction",
"link",
html`<a href="https://analytics.home-assistant.io" target="_blank"
>analytics.home-assistant.io</a
>`
)}
Share anonymized information from your installation to help make Home
Assistant better and help us convince manufacturers to add local control
and privacy-focused features.
</p>
<ha-analytics
@analytics-preferences-changed=${this._preferencesChanged}

View File

@@ -22,12 +22,16 @@ import {
import {
computeDeviceName,
DeviceRegistryEntry,
devicesInArea,
} from "../../../data/device_registry";
import {
computeEntityRegistryName,
EntityRegistryEntry,
} from "../../../data/entity_registry";
import { findRelated, RelatedResult } from "../../../data/search";
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
import { showEntityEditorDialog } from "../entities/show-dialog-entity-editor";
import { configSections } from "../ha-panel-config";
import {
loadAreaRegistryDetailDialog,
@@ -44,6 +48,8 @@ class HaConfigAreaPage extends LitElement {
@property() public devices!: DeviceRegistryEntry[];
@property() public entities!: EntityRegistryEntry[];
@property({ type: Boolean, reflect: true }) public narrow!: boolean;
@property() public isWide!: boolean;
@@ -58,9 +64,39 @@ class HaConfigAreaPage extends LitElement {
| AreaRegistryEntry
| undefined => areas.find((area) => area.area_id === areaId));
private _devices = memoizeOne(
(areaId: string, devices: DeviceRegistryEntry[]): DeviceRegistryEntry[] =>
devicesInArea(devices, areaId)
private _memberships = memoizeOne(
(
areaId: string,
registryDevices: DeviceRegistryEntry[],
registryEntities: EntityRegistryEntry[]
) => {
const devices = new Map();
for (const device of registryDevices) {
if (device.area_id === areaId) {
devices.set(device.id, device);
}
}
const entities: EntityRegistryEntry[] = [];
const indirectEntities: EntityRegistryEntry[] = [];
for (const entity of registryEntities) {
if (entity.area_id) {
if (entity.area_id === areaId) {
entities.push(entity);
}
} else if (devices.has(entity.device_id)) {
indirectEntities.push(entity);
}
}
return {
devices: Array.from(devices.values()),
entities,
indirectEntities,
};
}
);
protected firstUpdated(changedProps) {
@@ -87,7 +123,11 @@ class HaConfigAreaPage extends LitElement {
`;
}
const devices = this._devices(this.areaId, this.devices);
const { devices, entities } = this._memberships(
this.areaId,
this.devices,
this.entities
);
return html`
<hass-tabs-subpage
@@ -144,6 +184,33 @@ class HaConfigAreaPage extends LitElement {
>
`}
</ha-card>
<ha-card
.header=${this.hass.localize(
"ui.panel.config.areas.editor.linked_entities_caption"
)}
>${entities.length
? entities.map(
(entity) =>
html`
<paper-item
@click=${this._openEntity}
.entity=${entity}
>
<paper-item-body>
${computeEntityRegistryName(this.hass, entity)}
</paper-item-body>
<ha-icon-next></ha-icon-next>
</paper-item>
`
)
: html`
<paper-item class="no-link"
>${this.hass.localize(
"ui.panel.config.areas.editor.no_linked_entities"
)}</paper-item
>
`}
</ha-card>
</div>
<div class="column">
${isComponentLoaded(this.hass, "automation")
@@ -299,6 +366,14 @@ class HaConfigAreaPage extends LitElement {
this._openDialog(entry);
}
private _openEntity(ev) {
const entry: EntityRegistryEntry = (ev.currentTarget as any).entity;
showEntityEditorDialog(this, {
entity_id: entry.entity_id,
entry,
});
}
private _openDialog(entry?: AreaRegistryEntry) {
showAreaRegistryDetailDialog(this, {
entry,

View File

@@ -24,10 +24,8 @@ import {
AreaRegistryEntry,
createAreaRegistryEntry,
} from "../../../data/area_registry";
import {
DeviceRegistryEntry,
devicesInArea,
} from "../../../data/device_registry";
import type { DeviceRegistryEntry } from "../../../data/device_registry";
import type { EntityRegistryEntry } from "../../../data/entity_registry";
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
import "../../../layouts/hass-loading-screen";
import "../../../layouts/hass-tabs-subpage-data-table";
@@ -53,12 +51,39 @@ export class HaConfigAreasDashboard extends LitElement {
@property() public devices!: DeviceRegistryEntry[];
@property() public entities!: EntityRegistryEntry[];
private _areas = memoizeOne(
(areas: AreaRegistryEntry[], devices: DeviceRegistryEntry[]) => {
(
areas: AreaRegistryEntry[],
devices: DeviceRegistryEntry[],
entities: EntityRegistryEntry[]
) => {
return areas.map((area) => {
const devicesInArea = new Set();
for (const device of devices) {
if (device.area_id === area.area_id) {
devicesInArea.add(device.id);
}
}
let entitiesInArea = 0;
for (const entity of entities) {
if (
entity.area_id
? entity.area_id === area.area_id
: devicesInArea.has(entity.device_id)
) {
entitiesInArea++;
}
}
return {
...area,
devices: devicesInArea(devices, area.area_id).length,
devices: devicesInArea.size,
entities: entitiesInArea,
};
});
}
@@ -97,6 +122,15 @@ export class HaConfigAreasDashboard extends LitElement {
width: "20%",
direction: "asc",
},
entities: {
title: this.hass.localize(
"ui.panel.config.areas.data_table.entities"
),
sortable: true,
type: "numeric",
width: "20%",
direction: "asc",
},
}
);
@@ -110,7 +144,7 @@ export class HaConfigAreasDashboard extends LitElement {
.tabs=${configSections.integrations}
.route=${this.route}
.columns=${this._columns(this.narrow)}
.data=${this._areas(this.areas, this.devices)}
.data=${this._areas(this.areas, this.devices, this.entities)}
@row-click=${this._handleRowClicked}
.noDataText=${this.hass.localize(
"ui.panel.config.areas.picker.no_areas"

View File

@@ -15,6 +15,10 @@ import {
DeviceRegistryEntry,
subscribeDeviceRegistry,
} from "../../../data/device_registry";
import {
EntityRegistryEntry,
subscribeEntityRegistry,
} from "../../../data/entity_registry";
import {
HassRouterPage,
RouterOptions,
@@ -51,6 +55,9 @@ class HaConfigAreas extends HassRouterPage {
@internalProperty()
private _deviceRegistryEntries: DeviceRegistryEntry[] = [];
@internalProperty()
private _entityRegistryEntries: EntityRegistryEntry[] = [];
@internalProperty() private _areas: AreaRegistryEntry[] = [];
private _unsubs?: UnsubscribeFunc[];
@@ -90,6 +97,7 @@ class HaConfigAreas extends HassRouterPage {
pageEl.entries = this._configEntries;
pageEl.devices = this._deviceRegistryEntries;
pageEl.entities = this._entityRegistryEntries;
pageEl.areas = this._areas;
pageEl.narrow = this.narrow;
pageEl.isWide = this.isWide;
@@ -113,6 +121,9 @@ class HaConfigAreas extends HassRouterPage {
subscribeDeviceRegistry(this.hass.connection, (entries) => {
this._deviceRegistryEntries = entries;
}),
subscribeEntityRegistry(this.hass.connection, (entries) => {
this._entityRegistryEntries = entries;
}),
];
}
}

View File

@@ -10,6 +10,7 @@ import {
} from "lit-element";
import { html } from "lit-html";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { ensureArray } from "../../../../../common/ensure-array";
import { Condition } from "../../../../../data/automation";
import { Action, ChooseAction } from "../../../../../data/script";
import { haStyle } from "../../../../../resources/styles";
@@ -31,7 +32,7 @@ export class HaChooseAction extends LitElement implements ActionElement {
const action = this.action;
return html`
${(action.choose || []).map(
${(action.choose ? ensureArray(action.choose) : []).map(
(option, idx) => html`<ha-card>
<mwc-icon-button
.idx=${idx}
@@ -101,7 +102,9 @@ export class HaChooseAction extends LitElement implements ActionElement {
ev.stopPropagation();
const value = ev.detail.value as Condition[];
const index = (ev.target as any).idx;
const choose = this.action.choose ? [...this.action.choose] : [];
const choose = this.action.choose
? [...ensureArray(this.action.choose)]
: [];
choose[index].conditions = value;
fireEvent(this, "value-changed", {
value: { ...this.action, choose },
@@ -112,7 +115,9 @@ export class HaChooseAction extends LitElement implements ActionElement {
ev.stopPropagation();
const value = ev.detail.value as Action[];
const index = (ev.target as any).idx;
const choose = this.action.choose ? [...this.action.choose] : [];
const choose = this.action.choose
? [...ensureArray(this.action.choose)]
: [];
choose[index].sequence = value;
fireEvent(this, "value-changed", {
value: { ...this.action, choose },
@@ -120,7 +125,9 @@ export class HaChooseAction extends LitElement implements ActionElement {
}
private _addOption() {
const choose = this.action.choose ? [...this.action.choose] : [];
const choose = this.action.choose
? [...ensureArray(this.action.choose)]
: [];
choose.push({ conditions: [], sequence: [] });
fireEvent(this, "value-changed", {
value: { ...this.action, choose },
@@ -129,7 +136,9 @@ export class HaChooseAction extends LitElement implements ActionElement {
private _removeOption(ev: CustomEvent) {
const index = (ev.currentTarget as any).idx;
const choose = this.action.choose ? [...this.action.choose] : [];
const choose = this.action.choose
? [...ensureArray(this.action.choose)]
: [];
choose.splice(index, 1);
fireEvent(this, "value-changed", {
value: { ...this.action, choose },

View File

@@ -99,33 +99,63 @@ export class HaAutomationTracePathDetails extends LitElement {
return "This node was not executed and so no further trace information is available.";
}
const data: ActionTraceStep[] = paths[this.selected.path];
const parts: TemplateResult[][] = [];
return data.map((trace, idx) => {
const {
path,
timestamp,
result,
error,
changed_variables,
...rest
} = trace as any;
let active = false;
const childConditionsPrefix = `${this.selected.path}/conditions/`;
return html`
${data.length === 1 ? "" : html`<h3>Iteration ${idx + 1}</h3>`}
Executed:
${formatDateTimeWithSeconds(new Date(timestamp), this.hass.locale)}<br />
${result
? html`Result:
<pre>${safeDump(result)}</pre>`
: error
? html`<div class="error">Error: ${error}</div>`
: ""}
${Object.keys(rest).length === 0
? ""
: html`<pre>${safeDump(rest)}</pre>`}
`;
});
for (const curPath of Object.keys(this.trace.trace)) {
// Include all child conditions too
if (active) {
if (!curPath.startsWith(childConditionsPrefix)) {
break;
}
} else if (curPath === this.selected.path) {
active = true;
} else {
continue;
}
const data: ActionTraceStep[] = paths[curPath];
parts.push(
data.map((trace, idx) => {
const {
path,
timestamp,
result,
error,
changed_variables,
...rest
} = trace as any;
return html`
${curPath === this.selected.path
? ""
: html`<h2>
Condition ${curPath.substr(childConditionsPrefix.length)}
</h2>`}
${data.length === 1 ? "" : html`<h3>Iteration ${idx + 1}</h3>`}
Executed:
${formatDateTimeWithSeconds(
new Date(timestamp),
this.hass.locale
)}<br />
${result
? html`Result:
<pre>${safeDump(result)}</pre>`
: error
? html`<div class="error">Error: ${error}</div>`
: ""}
${Object.keys(rest).length === 0
? ""
: html`<pre>${safeDump(rest)}</pre>`}
`;
})
);
}
return parts;
}
private _renderSelectedConfig() {

View File

@@ -40,21 +40,13 @@ class ConfigAnalytics extends LitElement {
: undefined;
return html`
<ha-card
.header=${this.hass.localize(
"ui.panel.config.core.section.core.analytics.header"
)}
>
<ha-card header="Analytics">
<div class="card-content">
${error ? html`<div class="error">${error}</div>` : ""}
<p>
${this.hass.localize(
"ui.panel.config.core.section.core.analytics.introduction",
"link",
html`<a href="https://analytics.home-assistant.io" target="_blank"
>analytics.home-assistant.io</a
>`
)}
Share anonymized information from your installation to help make
Home Assistant better and help us convince manufacturers to add
local control and privacy-focused features.
</p>
<ha-analytics
@analytics-preferences-changed=${this._preferencesChanged}

View File

@@ -11,9 +11,13 @@ import {
TemplateResult,
} from "lit-element";
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
import {
getIdentifiersFromDevice,
ZWaveJSNodeIdentifiers,
} from "../../../../../../data/zwave_js";
import { haStyle } from "../../../../../../resources/styles";
import { HomeAssistant } from "../../../../../../types";
import { showZWaveJSReinterviewNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-reinterview-node";
@customElement("ha-device-actions-zwave_js")
export class HaDeviceActionsZWaveJS extends LitElement {
@@ -23,9 +27,19 @@ export class HaDeviceActionsZWaveJS extends LitElement {
@internalProperty() private _entryId?: string;
@internalProperty() private _nodeId?: number;
protected updated(changedProperties: PropertyValues) {
if (changedProperties.has("device")) {
this._entryId = this.device.config_entries[0];
const identifiers:
| ZWaveJSNodeIdentifiers
| undefined = getIdentifiersFromDevice(this.device);
if (!identifiers) {
return;
}
this._nodeId = identifiers.node_id;
}
}
@@ -40,9 +54,22 @@ export class HaDeviceActionsZWaveJS extends LitElement {
)}
</mwc-button>
</a>
<mwc-button @click=${this._reinterviewClicked}
>Re-interview Device</mwc-button
>
`;
}
private async _reinterviewClicked() {
if (!this._nodeId || !this._entryId) {
return;
}
showZWaveJSReinterviewNodeDialog(this, {
entry_id: this._entryId,
node_id: this._nodeId,
});
}
static get styles(): CSSResult[] {
return [
haStyle,

View File

@@ -140,7 +140,10 @@ class HaConfigInfo extends LitElement {
</div>
<div class="content">
<system-health-card .hass=${this.hass}></system-health-card>
<integrations-card .hass=${this.hass}></integrations-card>
<integrations-card
.hass=${this.hass}
.narrow=${this.narrow}
></integrations-card>
</div>
</hass-tabs-subpage>
`;

View File

@@ -13,8 +13,10 @@ import "../../../components/ha-card";
import {
domainToName,
fetchIntegrationManifests,
fetchIntegrationSetups,
integrationIssuesUrl,
IntegrationManifest,
IntegrationSetup,
} from "../../../data/integration";
import { HomeAssistant } from "../../../types";
import { brandsUrl } from "../../../util/brands-url";
@@ -23,10 +25,16 @@ import { brandsUrl } from "../../../util/brands-url";
class IntegrationsCard extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Boolean }) public narrow = false;
@internalProperty() private _manifests?: {
[domain: string]: IntegrationManifest;
};
@internalProperty() private _setups?: {
[domain: string]: IntegrationSetup;
};
private _sortedIntegrations = memoizeOne((components: string[]) => {
return Array.from(
new Set(
@@ -40,6 +48,7 @@ class IntegrationsCard extends LitElement {
firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this._fetchManifests();
this._fetchSetups();
}
protected render(): TemplateResult {
@@ -48,10 +57,47 @@ class IntegrationsCard extends LitElement {
.header=${this.hass.localize("ui.panel.config.info.integrations")}
>
<table class="card-content">
<thead>
<tr>
<th></th>
${!this.narrow
? html`<th></th>
<th></th>
<th></th>`
: ""}
<th>Setup time</th>
</tr>
</thead>
<tbody>
${this._sortedIntegrations(this.hass!.config.components).map(
(domain) => {
const manifest = this._manifests && this._manifests[domain];
const docLink = manifest
? html`<a
href=${manifest.documentation}
target="_blank"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.config.info.documentation"
)}</a
>`
: "";
const issueLink =
manifest && (manifest.is_built_in || manifest.issue_tracker)
? html`
<a
href=${integrationIssuesUrl(domain, manifest)}
target="_blank"
rel="noreferrer"
>${this.hass.localize(
"ui.panel.config.info.issues"
)}</a
>
`
: "";
const setupSeconds = this._setups?.[domain]?.seconds?.toFixed(
2
);
return html`
<tr>
<td>
@@ -64,39 +110,25 @@ class IntegrationsCard extends LitElement {
<td class="name">
${domainToName(this.hass.localize, domain, manifest)}<br />
<span class="domain">${domain}</span>
${this.narrow
? html`<div class="mobile-row">
<div>${docLink} ${issueLink}</div>
${setupSeconds ? html`${setupSeconds}s` : ""}
</div>`
: ""}
</td>
${!manifest
${this.narrow
? ""
: html`
<td>
<a
href=${manifest.documentation}
target="_blank"
rel="noreferrer"
>
${this.hass.localize(
"ui.panel.config.info.documentation"
)}
</a>
${docLink}
</td>
<td>
${issueLink}
</td>
<td class="setup">
${setupSeconds ? html`${setupSeconds}s` : ""}
</td>
${manifest.is_built_in || manifest.issue_tracker
? html`
<td>
<a
href=${integrationIssuesUrl(
domain,
manifest
)}
target="_blank"
rel="noreferrer"
>
${this.hass.localize(
"ui.panel.config.info.issues"
)}
</a>
</td>
`
: ""}
`}
</tr>
`;
@@ -116,9 +148,21 @@ class IntegrationsCard extends LitElement {
this._manifests = manifests;
}
private async _fetchSetups() {
const setups = {};
for (const setup of await fetchIntegrationSetups(this.hass)) {
setups[setup.domain] = setup;
}
this._setups = setups;
}
static get styles(): CSSResult {
return css`
td {
table {
width: 100%;
}
td,
th {
padding: 0 8px;
}
td:first-child {
@@ -127,9 +171,22 @@ class IntegrationsCard extends LitElement {
td.name {
padding: 8px;
}
td.setup {
text-align: right;
}
th {
text-align: right;
}
.domain {
color: var(--secondary-text-color);
}
.mobile-row {
display: flex;
justify-content: space-between;
}
.mobile-row a:not(:last-of-type) {
margin-right: 4px;
}
img {
display: block;
max-height: 40px;

View File

@@ -640,6 +640,7 @@ export class HaIntegrationCard extends LitElement {
flex: 1;
margin-left: 8px;
padding-top: 2px;
padding-right: 2px;
}
.content {

View File

@@ -1,3 +1,4 @@
import "../../../components/ha-svg-icon";
import { mdiPackageVariant, mdiCloud } from "@mdi/js";
import "@polymer/paper-tooltip/paper-tooltip";
import {
@@ -24,7 +25,7 @@ export class HaIntegrationHeader extends LitElement {
@property() public label!: string;
@property() public manifest?: IntegrationManifest;
@property({ attribute: false }) public manifest?: IntegrationManifest;
protected render(): TemplateResult {
let primary: string;
@@ -84,6 +85,7 @@ export class HaIntegrationHeader extends LitElement {
<div class="primary">${primary}</div>
${secondary ? html`<div class="secondary">${secondary}</div>` : ""}
</div>
${icons.length === 0
? ""
: html`
@@ -118,14 +120,17 @@ export class HaIntegrationHeader extends LitElement {
color: var(--text-on-state-color);
text-align: center;
padding: 2px;
border-top-left-radius: var(--ha-card-border-radius, 4px);
border-top-right-radius: var(--ha-card-border-radius, 4px);
}
.header {
display: flex;
position: relative;
padding: 16px 8px 8px 16px;
padding: 0 8px 8px 16px;
}
.header img {
margin-right: 16px;
margin-top: 16px;
width: 40px;
height: 40px;
}
@@ -142,6 +147,8 @@ export class HaIntegrationHeader extends LitElement {
}
.primary {
font-size: 16px;
margin-top: 16px;
margin-right: 2px;
font-weight: 400;
color: var(--primary-text-color);
}
@@ -150,18 +157,20 @@ export class HaIntegrationHeader extends LitElement {
color: var(--secondary-text-color);
}
.icons {
position: absolute;
top: 0px;
right: 16px;
margin-right: 8px;
margin-left: auto;
height: 28px;
color: var(--text-on-state-color, var(--secondary-text-color));
background-color: var(--state-color, #e0e0e0);
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
padding: 1px 4px 2px;
display: flex;
float: right;
}
.icons ha-svg-icon {
width: 20px;
height: 20px;
margin: 4px;
}
paper-tooltip {
white-space: nowrap;

View File

@@ -8,42 +8,62 @@ import {
property,
TemplateResult,
} from "lit-element";
import { createCloseHeading } from "../../../../../components/ha-dialog";
import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
import "@material/mwc-button/mwc-button";
import { haStyleDialog } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { ZHAReconfigureDeviceDialogParams } from "./show-dialog-zha-reconfigure-device";
import { IronAutogrowTextareaElement } from "@polymer/iron-autogrow-textarea";
import "@polymer/paper-input/paper-textarea";
import "../../../../../components/ha-circular-progress";
import { LOG_OUTPUT, reconfigureNode } from "../../../../../data/zha";
import "../../../../../components/ha-svg-icon";
import {
AttributeConfigurationStatus,
Cluster,
ClusterConfigurationEvent,
ClusterConfigurationStatus,
fetchClustersForZhaNode,
reconfigureNode,
ZHA_CHANNEL_CFG_DONE,
ZHA_CHANNEL_MSG_BIND,
ZHA_CHANNEL_MSG_CFG_RPT,
} from "../../../../../data/zha";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { createCloseHeading } from "../../../../../components/ha-dialog";
@customElement("dialog-zha-reconfigure-device")
class DialogZHAReconfigureDevice extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@internalProperty() private _active = false;
@internalProperty() private _status?: string;
@internalProperty() private _formattedEvents = "";
@internalProperty() private _stages?: string[];
@internalProperty()
private _params: ZHAReconfigureDeviceDialogParams | undefined = undefined;
@internalProperty() private _clusterConfigurationStatuses?: Map<
number,
ClusterConfigurationStatus
> = new Map();
private _subscribed?: Promise<() => Promise<void>>;
@internalProperty() private _params:
| ZHAReconfigureDeviceDialogParams
| undefined = undefined;
private _reconfigureDeviceTimeoutHandle: any = undefined;
@internalProperty() private _allSuccessful = true;
public async showDialog(
params: ZHAReconfigureDeviceDialogParams
): Promise<void> {
@internalProperty() private _showDetails = false;
private _subscribed?: Promise<UnsubscribeFunc>;
public showDialog(params: ZHAReconfigureDeviceDialogParams): void {
this._params = params;
this._subscribe(params);
this._stages = undefined;
}
public closeDialog(): void {
this._unsubscribe();
this._formattedEvents = "";
this._params = undefined;
this._status = undefined;
this._stages = undefined;
this._clusterConfigurationStatuses = undefined;
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
@@ -51,58 +71,311 @@ class DialogZHAReconfigureDevice extends LitElement {
if (!this._params) {
return html``;
}
return html`
<ha-dialog
open
hideActions
@closing="${this.closeDialog}"
@closed="${this.closeDialog}"
.heading=${createCloseHeading(
this.hass,
this.hass.localize(`ui.dialogs.zha_reconfigure_device.heading`)
this.hass.localize(`ui.dialogs.zha_reconfigure_device.heading`) +
": " +
(this._params?.device.user_given_name || this._params?.device.name)
)}
>
<div class="searching">
${this._active
? html`
<h1>
${this._params?.device.user_given_name ||
this._params?.device.name}
</h1>
<ha-circular-progress
active
alt="Searching"
></ha-circular-progress>
`
: ""}
</div>
<paper-textarea
readonly
max-rows="10"
class="log"
value="${this._formattedEvents}"
>
</paper-textarea>
${!this._status
? html`
<p>
${this.hass.localize(
"ui.dialogs.zha_reconfigure_device.introduction"
)}
</p>
<p>
<em>
${this.hass.localize(
"ui.dialogs.zha_reconfigure_device.battery_device_warning"
)}
</em>
</p>
<mwc-button
slot="primaryAction"
@click=${this._startReconfiguration}
>
${this.hass.localize(
"ui.dialogs.zha_reconfigure_device.start_reconfiguration"
)}
</mwc-button>
`
: ``}
${this._status === "started"
? html`
<div class="flex-container">
<ha-circular-progress active></ha-circular-progress>
<div class="status">
<p>
<b>
${this.hass.localize(
"ui.dialogs.zha_reconfigure_device.in_progress"
)}
</b>
</p>
<p>
${this.hass.localize(
"ui.dialogs.zha_reconfigure_device.run_in_background"
)}
</p>
</div>
</div>
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.dialogs.generic.close")}
</mwc-button>
<mwc-button slot="secondaryAction" @click=${this._toggleDetails}>
${this._showDetails
? this.hass.localize(
`ui.dialogs.zha_reconfigure_device.button_hide`
)
: this.hass.localize(
`ui.dialogs.zha_reconfigure_device.button_show`
)}
</mwc-button>
`
: ``}
${this._status === "failed"
? html`
<div class="flex-container">
<ha-svg-icon
.path=${mdiCloseCircle}
class="failed"
></ha-svg-icon>
<div class="status">
<p>
${this.hass.localize(
"ui.dialogs.zha_reconfigure_device.configuration_failed"
)}
</p>
</div>
</div>
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.dialogs.generic.close")}
</mwc-button>
<mwc-button slot="secondaryAction" @click=${this._toggleDetails}>
${this._showDetails
? this.hass.localize(
`ui.dialogs.zha_reconfigure_device.button_hide`
)
: this.hass.localize(
`ui.dialogs.zha_reconfigure_device.button_show`
)}
</mwc-button>
`
: ``}
${this._status === "finished"
? html`
<div class="flex-container">
<ha-svg-icon
.path=${mdiCheckCircle}
class="success"
></ha-svg-icon>
<div class="status">
<p>
${this.hass.localize(
"ui.dialogs.zha_reconfigure_device.configuration_complete"
)}
</p>
</div>
</div>
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.dialogs.generic.close")}
</mwc-button>
<mwc-button slot="secondaryAction" @click=${this._toggleDetails}>
${this._showDetails
? this.hass.localize(
`ui.dialogs.zha_reconfigure_device.button_hide`
)
: this.hass.localize(
`ui.dialogs.zha_reconfigure_device.button_show`
)}
</mwc-button>
`
: ``}
${this._stages
? html`
<div class="stages">
${this._stages.map(
(stage) => html`
<span class="stage">
<ha-svg-icon
.path=${mdiCheckCircle}
class="success"
></ha-svg-icon>
${stage}
</span>
`
)}
</div>
`
: ""}
${this._showDetails
? html`
<div class="wrapper">
<h2 class="grid-item">
${this.hass.localize(
`ui.dialogs.zha_reconfigure_device.cluster_header`
)}
</h2>
<h2 class="grid-item">
${this.hass.localize(
`ui.dialogs.zha_reconfigure_device.bind_header`
)}
</h2>
<h2 class="grid-item">
${this.hass.localize(
`ui.dialogs.zha_reconfigure_device.reporting_header`
)}
</h2>
${this._clusterConfigurationStatuses!.size > 0
? html`
${Array.from(
this._clusterConfigurationStatuses!.values()
).map(
(clusterStatus) => html`
<div class="grid-item">
${clusterStatus.cluster.name}
</div>
<div class="grid-item">
${clusterStatus.bindSuccess !== undefined
? clusterStatus.bindSuccess
? html`
<span class="stage">
<ha-svg-icon
.path=${mdiCheckCircle}
class="success"
></ha-svg-icon>
</span>
`
: html`
<span class="stage">
<ha-svg-icon
.path=${mdiCloseCircle}
class="failed"
></ha-svg-icon>
</span>
`
: ""}
</div>
<div class="grid-item">
${clusterStatus.attributes.size > 0
? html`
<div class="attributes">
<div class="grid-item">
${this.hass.localize(
`ui.dialogs.zha_reconfigure_device.attribute`
)}
</div>
<div class="grid-item">
<div>
${this.hass.localize(
`ui.dialogs.zha_reconfigure_device.min_max_change`
)}
</div>
</div>
${Array.from(
clusterStatus.attributes.values()
).map(
(attribute) => html`
<span class="grid-item">
${attribute.name}:
${attribute.success
? html`
<span class="stage">
<ha-svg-icon
.path=${mdiCheckCircle}
class="success"
></ha-svg-icon>
</span>
`
: html`
<span class="stage">
<ha-svg-icon
.path=${mdiCloseCircle}
class="failed"
></ha-svg-icon>
</span>
`}
</span>
<div class="grid-item">
${attribute.min}/${attribute.max}/${attribute.change}
</div>
`
)}
</div>
`
: ""}
</div>
`
)}
`
: ""}
</div>
`
: ""}
</ha-dialog>
`;
}
private _handleMessage(message: any): void {
if (message.type === LOG_OUTPUT) {
this._formattedEvents += message.log_entry.message + "\n";
const paperTextArea = this.shadowRoot!.querySelector("paper-textarea");
if (paperTextArea) {
const textArea = (paperTextArea.inputElement as IronAutogrowTextareaElement)
.textarea;
textArea.scrollTop = textArea.scrollHeight;
private async _startReconfiguration(): Promise<void> {
if (!this.hass || !this._params) {
return;
}
this._clusterConfigurationStatuses = new Map(
(await fetchClustersForZhaNode(this.hass, this._params.device.ieee)).map(
(cluster: Cluster) => [
cluster.id,
{
cluster: cluster,
bindSuccess: undefined,
attributes: new Map<number, AttributeConfigurationStatus>(),
},
]
)
);
this._subscribe(this._params);
this._status = "started";
}
private _handleMessage(message: ClusterConfigurationEvent): void {
if (message.type === ZHA_CHANNEL_CFG_DONE) {
this._unsubscribe();
this._status = this._allSuccessful ? "finished" : "failed";
} else {
const clusterConfigurationStatus = this._clusterConfigurationStatuses!.get(
message.zha_channel_msg_data.cluster_id
);
if (message.type === ZHA_CHANNEL_MSG_BIND) {
if (!this._stages) {
this._stages = ["binding"];
}
const success = message.zha_channel_msg_data.success;
clusterConfigurationStatus!.bindSuccess = success;
this._allSuccessful = this._allSuccessful && success;
}
if (message.type === ZHA_CHANNEL_MSG_CFG_RPT) {
if (this._stages && !this._stages.includes("reporting")) {
this._stages.push("reporting");
}
const attributes = message.zha_channel_msg_data.attributes;
Object.keys(attributes).forEach((name) => {
const attribute = attributes[name];
clusterConfigurationStatus!.attributes.set(attribute.id, attribute);
this._allSuccessful = this._allSuccessful && attribute.success;
});
}
this.requestUpdate();
}
}
private _unsubscribe(): void {
this._active = false;
if (this._reconfigureDeviceTimeoutHandle) {
clearTimeout(this._reconfigureDeviceTimeoutHandle);
}
if (this._subscribed) {
this._subscribed.then((unsub) => unsub());
this._subscribed = undefined;
@@ -113,33 +386,66 @@ class DialogZHAReconfigureDevice extends LitElement {
if (!this.hass) {
return;
}
this._active = true;
this._subscribed = reconfigureNode(
this.hass,
params.device.ieee,
this._handleMessage.bind(this)
);
this._reconfigureDeviceTimeoutHandle = setTimeout(
() => this._unsubscribe(),
60000
);
}
private _toggleDetails() {
this._showDetails = !this._showDetails;
}
static get styles(): CSSResult[] {
return [
haStyleDialog,
css`
ha-circular-progress {
padding: 20px;
.wrapper {
display: grid;
grid-template-columns: 3fr 1fr 2fr;
}
.searching {
margin-top: 20px;
.attributes {
display: grid;
grid-template-columns: 1fr 1fr;
}
.grid-item {
border: 1px solid;
padding: 7px;
}
.success {
color: var(--success-color);
}
.failed {
color: var(--warning-color);
}
.flex-container {
display: flex;
flex-direction: column;
align-items: center;
}
.log {
padding: 16px;
.stages {
margin-top: 16px;
}
.stage ha-svg-icon {
width: 16px;
height: 16px;
}
.stage {
padding: 8px;
}
ha-svg-icon {
width: 68px;
height: 48px;
}
.flex-container ha-circular-progress,
.flex-container ha-svg-icon {
margin-right: 20px;
}
`,
];

View File

@@ -0,0 +1,262 @@
import "@material/mwc-button/mwc-button";
import { mdiCheckCircle, mdiCloseCircle } from "@mdi/js";
import {
CSSResult,
customElement,
html,
LitElement,
property,
internalProperty,
TemplateResult,
css,
} from "lit-element";
import "../../../../../components/ha-circular-progress";
import { createCloseHeading } from "../../../../../components/ha-dialog";
import { haStyleDialog } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { ZWaveJSReinterviewNodeDialogParams } from "./show-dialog-zwave_js-reinterview-node";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { reinterviewNode } from "../../../../../data/zwave_js";
@customElement("dialog-zwave_js-reinterview-node")
class DialogZWaveJSReinterviewNode extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@internalProperty() private entry_id?: string;
@internalProperty() private node_id?: number;
@internalProperty() private _status?: string;
@internalProperty() private _stages?: string[];
private _subscribed?: Promise<UnsubscribeFunc>;
public async showDialog(
params: ZWaveJSReinterviewNodeDialogParams
): Promise<void> {
this._stages = undefined;
this.entry_id = params.entry_id;
this.node_id = params.node_id;
}
protected render(): TemplateResult {
if (!this.entry_id) {
return html``;
}
return html`
<ha-dialog
open
@closed=${this.closeDialog}
.heading=${createCloseHeading(
this.hass,
this.hass.localize("ui.panel.config.zwave_js.reinterview_node.title")
)}
>
${!this._status
? html`
<p>
${this.hass.localize(
"ui.panel.config.zwave_js.reinterview_node.introduction"
)}
</p>
<p>
<em>
${this.hass.localize(
"ui.panel.config.zwave_js.reinterview_node.battery_device_warning"
)}
</em>
</p>
<mwc-button slot="primaryAction" @click=${this._startReinterview}>
${this.hass.localize(
"ui.panel.config.zwave_js.reinterview_node.start_reinterview"
)}
</mwc-button>
`
: ``}
${this._status === "started"
? html`
<div class="flex-container">
<ha-circular-progress active></ha-circular-progress>
<div class="status">
<p>
<b>
${this.hass.localize(
"ui.panel.config.zwave_js.reinterview_node.in_progress"
)}
</b>
</p>
<p>
${this.hass.localize(
"ui.panel.config.zwave_js.reinterview_node.run_in_background"
)}
</p>
</div>
</div>
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.panel.config.zwave_js.common.close")}
</mwc-button>
`
: ``}
${this._status === "failed"
? html`
<div class="flex-container">
<ha-svg-icon
.path=${mdiCloseCircle}
class="failed"
></ha-svg-icon>
<div class="status">
<p>
${this.hass.localize(
"ui.panel.config.zwave_js.reinterview_node.interview_failed"
)}
</p>
</div>
</div>
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.panel.config.zwave_js.common.close")}
</mwc-button>
`
: ``}
${this._status === "finished"
? html`
<div class="flex-container">
<ha-svg-icon
.path=${mdiCheckCircle}
class="success"
></ha-svg-icon>
<div class="status">
<p>
${this.hass.localize(
"ui.panel.config.zwave_js.reinterview_node.interview_complete"
)}
</p>
</div>
</div>
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
${this.hass.localize("ui.panel.config.zwave_js.common.close")}
</mwc-button>
`
: ``}
${this._stages
? html`
<div class="stages">
${this._stages.map(
(stage) => html`
<span class="stage">
<ha-svg-icon
.path=${mdiCheckCircle}
class="success"
></ha-svg-icon>
${stage}
</span>
`
)}
</div>
`
: ""}
</ha-dialog>
`;
}
private _startReinterview(): void {
if (!this.hass) {
return;
}
this._subscribed = reinterviewNode(
this.hass,
this.entry_id!,
this.node_id!,
this._handleMessage.bind(this)
);
}
private _handleMessage(message: any): void {
if (message.event === "interview started") {
this._status = "started";
}
if (message.event === "interview stage completed") {
if (this._stages === undefined) {
this._stages = [message.stage];
} else {
this._stages = [...this._stages, message.stage];
}
}
if (message.event === "interview failed") {
this._unsubscribe();
this._status = "failed";
}
if (message.event === "interview completed") {
this._unsubscribe();
this._status = "finished";
}
}
private _unsubscribe(): void {
if (this._subscribed) {
this._subscribed.then((unsub) => unsub());
this._subscribed = undefined;
}
}
public closeDialog(): void {
this.entry_id = undefined;
this.node_id = undefined;
this._status = undefined;
this._stages = undefined;
this._unsubscribe();
fireEvent(this, "dialog-closed", { dialog: this.localName });
}
static get styles(): CSSResult[] {
return [
haStyleDialog,
css`
.success {
color: var(--success-color);
}
.failed {
color: var(--warning-color);
}
.flex-container {
display: flex;
align-items: center;
}
.stages {
margin-top: 16px;
}
.stage ha-svg-icon {
width: 16px;
height: 16px;
}
.stage {
padding: 8px;
}
ha-svg-icon {
width: 68px;
height: 48px;
}
.flex-container ha-circular-progress,
.flex-container ha-svg-icon {
margin-right: 20px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-zwave_js-reinterview-node": DialogZWaveJSReinterviewNode;
}
}

View File

@@ -0,0 +1,20 @@
import { fireEvent } from "../../../../../common/dom/fire_event";
export interface ZWaveJSReinterviewNodeDialogParams {
entry_id: string;
node_id: number;
}
export const loadReinterviewNodeDialog = () =>
import("./dialog-zwave_js-reinterview-node");
export const showZWaveJSReinterviewNodeDialog = (
element: HTMLElement,
reinterviewNodeDialogParams: ZWaveJSReinterviewNodeDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-zwave_js-reinterview-node",
dialogImport: loadReinterviewNodeDialog,
dialogParams: reinterviewNodeDialogParams,
});
};

View File

@@ -7,7 +7,7 @@ import { HomeAssistant } from "../../../../../types";
import { navigate } from "../../../../../common/navigate";
import { PageNavigation } from "../../../../../layouts/hass-tabs-subpage";
import { mdiServerNetwork } from "@mdi/js";
import { mdiServerNetwork, mdiMathLog } from "@mdi/js";
export const configTabs: PageNavigation[] = [
{
@@ -15,6 +15,11 @@ export const configTabs: PageNavigation[] = [
path: `/config/zwave_js/dashboard`,
iconPath: mdiServerNetwork,
},
{
translationKey: "ui.panel.config.zwave_js.navigation.logs",
path: `/config/zwave_js/logs`,
iconPath: mdiMathLog,
},
];
@customElement("zwave_js-config-router")
@@ -41,6 +46,10 @@ class ZWaveJSConfigRouter extends HassRouterPage {
tag: "zwave_js-node-config",
load: () => import("./zwave_js-node-config"),
},
logs: {
tag: "zwave_js-logs",
load: () => import("./zwave_js-logs"),
},
},
};

View File

@@ -0,0 +1,157 @@
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
html,
property,
customElement,
LitElement,
CSSResultArray,
internalProperty,
query,
} from "lit-element";
import "@polymer/paper-listbox/paper-listbox";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import {
fetchZWaveJSLogConfig,
setZWaveJSLogLevel,
subscribeZWaveJSLogs,
ZWaveJSLogConfig,
} from "../../../../../data/zwave_js";
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
import { HomeAssistant, Route } from "../../../../../types";
import { configTabs } from "./zwave_js-config-router";
import "../../../../../layouts/hass-tabs-subpage";
import { haStyle } from "../../../../../resources/styles";
@customElement("zwave_js-logs")
class ZWaveJSLogs extends SubscribeMixin(LitElement) {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ type: Object }) public route!: Route;
@property({ type: Boolean }) public narrow!: boolean;
@property() public configEntryId!: string;
@internalProperty() private _logConfig?: ZWaveJSLogConfig;
@query("textarea", true) private _textarea?: HTMLTextAreaElement;
public hassSubscribe(): Array<UnsubscribeFunc | Promise<UnsubscribeFunc>> {
return [
subscribeZWaveJSLogs(this.hass, this.configEntryId, (log) => {
if (!this.hasUpdated) {
return;
}
if (Array.isArray(log.message)) {
for (const line of log.message) {
this._textarea!.value += `${line}\n`;
}
} else {
this._textarea!.value += `${log.message}\n`;
}
}),
];
}
protected render() {
return html`
<hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.tabs=${configTabs}
>
<div class="container">
<ha-card>
<div class="card-header">
<h1>
${this.hass.localize("ui.panel.config.zwave_js.logs.title")}
</h1>
</div>
<div class="card-content">
${this._logConfig
? html`
<paper-dropdown-menu
dynamic-align
.label=${this.hass.localize(
"ui.panel.config.zwave_js.logs.log_level"
)}
>
<paper-listbox
slot="dropdown-content"
.selected=${this._logConfig.level}
attr-for-selected="value"
@iron-select=${this._dropdownSelected}
>
<paper-item value="error">Error</paper-item>
<paper-item value="warn">Warn</paper-item>
<paper-item value="info">Info</paper-item>
<paper-item value="verbose">Verbose</paper-item>
<paper-item value="debug">Debug</paper-item>
<paper-item value="silly">Silly</paper-item>
</paper-listbox>
</paper-dropdown-menu>
`
: ""}
</div>
</ha-card>
<textarea readonly></textarea>
</div>
</hass-tabs-subpage>
`;
}
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this._fetchData();
}
private async _fetchData() {
if (!this.configEntryId) {
return;
}
this._logConfig = await fetchZWaveJSLogConfig(
this.hass!,
this.configEntryId
);
}
private _dropdownSelected(ev) {
if (ev.target === undefined || this._logConfig === undefined) {
return;
}
if (this._logConfig.level === ev.target.selected) {
return;
}
setZWaveJSLogLevel(this.hass!, this.configEntryId, ev.target.selected);
}
static get styles(): CSSResultArray {
return [
haStyle,
css`
.container {
display: flex;
flex-direction: column;
height: 100%;
box-sizing: border-box;
padding: 16px;
}
textarea {
flex-grow: 1;
padding: 16px;
}
ha-card {
margin: 16px 0;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zwave_js-logs": ZWaveJSLogs;
}
}

View File

@@ -301,14 +301,10 @@ export class HuiButtonCard extends LitElement implements LovelaceCard {
}
private _computeColor(stateObj: HassEntity | LightEntity): string {
if (!stateObj.attributes.hs_color || !this._config?.state_color) {
return "";
if (this._config?.state_color && stateObj.attributes.rgb_color) {
return `rgb(${stateObj.attributes.rgb_color.join(",")})`;
}
const [hue, sat] = stateObj.attributes.hs_color;
if (sat <= 10) {
return "";
}
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
return "";
}
private _handleAction(ev: ActionHandlerEvent) {

View File

@@ -5,6 +5,11 @@ import { GridCardConfig } from "./types";
import { LovelaceCardEditor } from "../types";
const DEFAULT_COLUMNS = 3;
const SQUARE_ROW_HEIGHTS_BY_COLUMNS = {
1: 5,
2: 3,
3: 2,
};
class HuiGridCard extends HuiStackCard<GridCardConfig> {
public static async getConfigElement(): Promise<LovelaceCardEditor> {
@@ -18,8 +23,11 @@ class HuiGridCard extends HuiStackCard<GridCardConfig> {
}
if (this.square) {
// When we're square, each row is size 2.
return (this._cards.length / this.columns) * 2;
const rowHeight = SQUARE_ROW_HEIGHTS_BY_COLUMNS[this.columns] || 1;
return (
(this._cards.length / this.columns) * rowHeight +
(this._config.title ? 1 : 0)
);
}
const promises: Array<Promise<number> | number> = [];
@@ -28,11 +36,16 @@ class HuiGridCard extends HuiStackCard<GridCardConfig> {
promises.push(computeCardSize(element));
}
const results = await Promise.all(promises);
const cardSizes = await Promise.all(promises);
const maxCardSize = Math.max(...results);
let totalHeight = this._config.title ? 1 : 0;
return maxCardSize * (this._cards.length / this.columns);
// Each column will adjust to max card size of it's row
for (let start = 0; start < cardSizes.length; start += this.columns) {
totalHeight += Math.max(...cardSizes.slice(start, start + this.columns));
}
return totalHeight;
}
get columns() {

View File

@@ -18,11 +18,10 @@ import { fireEvent } from "../../../common/dom/fire_event";
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { stateIcon } from "../../../common/entity/state_icon";
import { supportsFeature } from "../../../common/entity/supports-feature";
import "../../../components/ha-card";
import "../../../components/ha-icon-button";
import { UNAVAILABLE, UNAVAILABLE_STATES } from "../../../data/entity";
import { LightEntity, SUPPORT_BRIGHTNESS } from "../../../data/light";
import { LightEntity, lightSupportsDimming } from "../../../data/light";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive";
@@ -121,17 +120,14 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
@value-changing=${this._dragEvent}
@value-changed=${this._setBrightness}
style=${styleMap({
visibility: supportsFeature(stateObj, SUPPORT_BRIGHTNESS)
visibility: lightSupportsDimming(stateObj)
? "visible"
: "hidden",
})}
></round-slider>
<ha-icon-button
class="light-button ${classMap({
"slider-center": supportsFeature(
stateObj,
SUPPORT_BRIGHTNESS
),
"slider-center": lightSupportsDimming(stateObj),
"state-on": stateObj.state === "on",
"state-unavailable": stateObj.state === UNAVAILABLE,
})}"
@@ -244,14 +240,12 @@ export class HuiLightCard extends LitElement implements LovelaceCard {
}
private _computeColor(stateObj: LightEntity): string {
if (stateObj.state === "off" || !stateObj.attributes.hs_color) {
if (stateObj.state === "off") {
return "";
}
const [hue, sat] = stateObj.attributes.hs_color;
if (sat <= 10) {
return "";
}
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
return stateObj.attributes.rgb_color
? `rgb(${stateObj.attributes.rgb_color.join(",")})`
: "";
}
private _handleAction(ev: ActionHandlerEvent) {

View File

@@ -112,7 +112,7 @@ export class HuiGaugeCardEditor extends LitElement
"ui.panel.lovelace.editor.card.config.optional"
)})"
.value=${this._unit}
configValue="unit"
.configValue=${"unit"}
@value-changed=${this._valueChanged}
></paper-input>
<hui-theme-select-editor

View File

@@ -363,11 +363,22 @@ class LovelacePanel extends LitElement {
mode: previousMode,
} = this.lovelace!;
newConfig = this._checkLovelaceConfig(newConfig);
let conf: LovelaceConfig;
// If strategy defined, apply it here.
if (newConfig.strategy) {
conf = await generateLovelaceDashboardStrategy({
config: newConfig,
hass: this.hass!,
narrow: this.narrow,
});
} else {
conf = newConfig;
}
try {
// Optimistic update
this._updateLovelace({
config: newConfig,
rawConfig: undefined,
config: conf,
rawConfig: newConfig,
mode: "storage",
});
this._ignoreNextUpdateEvent = true;

View File

@@ -104,7 +104,7 @@ export class HuiGraphHeaderFooter extends LitElement
if (!this._coordinates) {
return html`
<div class="container">
<ha-circular-progress active></ha-circular-progress>
<ha-circular-progress active size="small"></ha-circular-progress>
</div>
`;
}
@@ -210,7 +210,7 @@ export class HuiGraphHeaderFooter extends LitElement
return css`
ha-circular-progress {
position: absolute;
top: calc(50% - 28px);
top: calc(50% - 14px);
}
.container {
display: flex;

View File

@@ -115,8 +115,8 @@ class LovelaceFullConfigEditor extends LitElement {
!this._saving &&
oldLovelace &&
this.lovelace &&
oldLovelace.config !== this.lovelace.config &&
!deepEqual(oldLovelace.config, this.lovelace.config)
oldLovelace.rawConfig !== this.lovelace.rawConfig &&
!deepEqual(oldLovelace.rawConfig, this.lovelace.rawConfig)
) {
showToast(this, {
message: this.hass!.localize(
@@ -124,7 +124,7 @@ class LovelaceFullConfigEditor extends LitElement {
),
action: {
action: () => {
this.yamlEditor.value = safeDump(this.lovelace!.config);
this.yamlEditor.value = safeDump(this.lovelace!.rawConfig);
},
text: this.hass!.localize(
"ui.panel.lovelace.editor.raw_editor.reload"

View File

@@ -32,17 +32,17 @@ const strategies: Record<
const getLovelaceStrategy = async <
T extends LovelaceDashboardStrategy | LovelaceViewStrategy
>(
name: string
strategyType: string
): Promise<T> => {
if (name in strategies) {
return strategies[name] as T;
if (strategyType in strategies) {
return strategies[strategyType] as T;
}
if (!name.startsWith(CUSTOM_PREFIX)) {
if (!strategyType.startsWith(CUSTOM_PREFIX)) {
throw new Error("Unknown strategy");
}
const tag = `ll-strategy-${name.substr(CUSTOM_PREFIX.length)}`;
const tag = `ll-strategy-${strategyType.substr(CUSTOM_PREFIX.length)}`;
if (
(await Promise.race([
@@ -69,14 +69,14 @@ const generateStrategy = async <T extends keyof GenerateMethods>(
generateMethod: T,
renderError: (err: string | Error) => AsyncReturnType<GenerateMethods[T]>,
info: Parameters<GenerateMethods[T]>[0],
name: string | undefined
strategyType: string | undefined
): Promise<ReturnType<GenerateMethods[T]>> => {
if (!name) {
return renderError("No strategy name found");
if (!strategyType) {
return renderError("No strategy type found");
}
try {
const strategy = (await getLovelaceStrategy(name)) as any;
const strategy = (await getLovelaceStrategy(strategyType)) as any;
return await strategy[generateMethod](info);
} catch (err) {
if (err.message !== "timeout") {
@@ -90,7 +90,7 @@ const generateStrategy = async <T extends keyof GenerateMethods>(
export const generateLovelaceDashboardStrategy = async (
info: Parameters<LovelaceDashboardStrategy["generateDashboard"]>[0],
name?: string
strategyType?: string
): ReturnType<LovelaceDashboardStrategy["generateDashboard"]> =>
generateStrategy(
"generateDashboard",
@@ -108,12 +108,12 @@ export const generateLovelaceDashboardStrategy = async (
],
}),
info,
name || info.config?.strategy?.name
strategyType || info.config?.strategy?.type
);
export const generateLovelaceViewStrategy = async (
info: Parameters<LovelaceViewStrategy["generateView"]>[0],
name?: string
strategyType?: string
): ReturnType<LovelaceViewStrategy["generateView"]> =>
generateStrategy(
"generateView",
@@ -126,7 +126,7 @@ export const generateLovelaceViewStrategy = async (
],
}),
info,
name || info.view?.strategy?.name
strategyType || info.view?.strategy?.type
);
/**

View File

@@ -85,7 +85,7 @@ export class OriginalStatesStrategy {
return {
views: [
{
strategy: { name: "original-states" },
strategy: { type: "original-states" },
title: info.hass.config.location_name,
},
],

View File

@@ -22,6 +22,8 @@ const mql = matchMedia("(prefers-color-scheme: dark)");
export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
class extends superClass {
private _themeApplied = false;
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this.addEventListener("settheme", (ev) => {
@@ -32,7 +34,7 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
storeState(this.hass!);
});
mql.addListener((ev) => this._applyTheme(ev.matches));
if (mql.matches) {
if (!this._themeApplied && mql.matches) {
applyThemesOnElement(
document.documentElement,
{
@@ -51,6 +53,7 @@ export default <T extends Constructor<HassBaseEl>>(superClass: T) =>
super.hassConnected();
subscribeThemes(this.hass!.connection, (themes) => {
this._themeApplied = true;
this._updateHass({ themes });
invalidateThemeCache();
this._applyTheme(mql.matches);

View File

@@ -172,7 +172,10 @@
"light": {
"brightness": "Brightness",
"color_temperature": "Color temperature",
"white_value": "White value",
"white_value": "White brightness",
"color_brightness": "Color brightness",
"cold_white_value": "Cold white brightness",
"warm_white_value": "Warm white brightness",
"effect": "Effect"
},
"lock": {
@@ -757,7 +760,22 @@
"update": "Update"
},
"zha_reconfigure_device": {
"heading": "Reconfiguring device"
"heading": "Reconfiguring device",
"configuring_alt": "Configuring",
"introduction": "Reconfigure a device on your Zigbee network. Use this feature if your device is not functioning correctly.",
"battery_device_warning": "You will need to wake battery powered devices before starting the reconfiguration process. Refer to your device's manual for instructions on how to wake the device.",
"run_in_background": "You can close this dialog and the reconfiguration will continue in the background.",
"start_reconfiguration": "Start Reconfiguration",
"in_progress": "The device is being reconfigured. This may take some time.",
"configuration_failed": "The device reconfiguration failed. Additional information may be available in the logs.",
"configuration_complete": "Device reconfiguration complete.",
"button_show": "Show Details",
"button_hide": "Hide Details",
"cluster_header": "Cluster",
"bind_header": "Binding",
"reporting_header": "Reporting",
"attribute": "Attribute",
"min_max_change": "min/max/change"
},
"zha_device_info": {
"manuf": "by {manufacturer}",
@@ -887,7 +905,8 @@
"description": "Group devices and entities into areas",
"data_table": {
"area": "Area",
"devices": "Devices"
"devices": "Devices",
"entities": "Entities"
},
"picker": {
"header": "Areas",
@@ -905,7 +924,9 @@
"name": "Name",
"name_required": "Name is required",
"area_id": "Area ID",
"unknown_error": "Unknown error"
"unknown_error": "Unknown error",
"linked_entities_caption": "Entities",
"no_linked_entities": "There are no entities linked to this area."
},
"delete": {
"confirmation_title": "Are you sure you want to delete this area?",
@@ -994,34 +1015,6 @@
"save_button": "Save",
"external_url": "External URL",
"internal_url": "Internal URL"
},
"analytics": {
"header": "Analytics",
"introduction": "Share your installation information to help make Home Assistant better and help us convince manufacturers to add local control and privacy-focused features.",
"needs_base": "You need to enable base analytics for this option to be available",
"preference": {
"base": {
"title": "Basic analytics",
"description": "Instance ID, version and installation type."
},
"diagnostics": {
"title": "Diagnostics",
"description": "Share crash reports when unexpected errors occur."
},
"usage": {
"title": "Used Integrations",
"description": "Names and version information."
},
"usage_supervisor": {
"title": "Used integrations and add-ons",
"description": "Names, versions and capabilities."
},
"statistics": {
"title": "Usage statistics",
"description": "Total used entities, users and other elements."
}
},
"learn_more": "How we process your data"
}
}
}
@@ -2569,7 +2562,8 @@
},
"zwave_js": {
"navigation": {
"network": "Network"
"network": "Network",
"logs": "Logs"
},
"common": {
"network": "Network",
@@ -2646,6 +2640,20 @@
"follow_device_instructions": "Follow the directions that came with your device to trigger exclusion on the device.",
"exclusion_failed": "The node could not be removed. Please check the logs for more information.",
"exclusion_finished": "Node {id} has been removed from your Z-Wave network."
},
"reinterview_node": {
"title": "Re-interview a Z-Wave Device",
"introduction": "Re-interview a device on your Z-Wave network. Use this feature if your device has missing or incorrect functionality.",
"battery_device_warning": "You will need to wake battery powered devices before starting the re-interview. Refer to your device's manual for instructions on how to wake the device.",
"run_in_background": "You can close this dialog and the interview will continue in the background.",
"start_reinterview": "Start Re-interview",
"in_progress": "The device is being interviewed. This may take some time.",
"interview_failed": "The device interview failed. Additional information may be available in the logs.",
"interview_complete": "Device interview complete."
},
"logs": {
"title": "Z-Wave JS Logs",
"log_level": "Log Level"
}
}
},
@@ -3933,4 +3941,4 @@
}
}
}
}
}

View File

@@ -122,6 +122,10 @@ export default hassAttributeUtil;
// Convert from internal snake_case format to user-friendly format
export function formatAttributeName(value: string): string {
value = value.replace(/_/g, " ").replace(/\bid\b/g, "ID");
value = value
.replace(/_/g, " ")
.replace(/\bid\b/g, "ID")
.replace(/\bip\b/g, "IP")
.replace(/\bmac\b/g, "MAC");
return value.charAt(0).toUpperCase() + value.slice(1);
}

View File

@@ -852,9 +852,9 @@
"add_entity_id": "Изберете обект",
"expand_area_id": "Разгънете тази област в отделните устройства и обекти, които тя съдържа. След разширяване няма да актуализира устройствата и обектите, когато зоната се промени.",
"expand_device_id": "Разширете това устройство в отделни обекти. След разширяване няма да актуализира обектите, когато устройството се промени.",
"remove_area_id": "Премахване на област",
"remove_device_id": "Премахване на устройство",
"remove_entity_id": "Премахване на обект"
"remove_area_id": "Премахване на областта",
"remove_device_id": "Премахване на устройството",
"remove_entity_id": "Премахване на обекта"
},
"user-picker": {
"add_user": "Добавяне на потребител",
@@ -1042,6 +1042,7 @@
"automation": "Презареждане на автоматизациите",
"command_line": "Презаредете обекти на командния ред",
"core": "Презаредете местоположение и персонализации",
"filter": "Филтриране на обектите",
"group": "Презаредете групи, групови обекти и услуги за уведомяване",
"history_stats": "Презаредете обекти с исторически данни",
"homekit": "Презареди HomeKit",
@@ -1078,7 +1079,7 @@
"buttons": {
"add": "Добавете устройства чрез това устройство",
"clusters": "Управление на клъстери",
"device_children": "Преглед на дъщерни ",
"device_children": "Преглед на дъщерни",
"reconfigure": "Преконфигуриране на устройство",
"remove": "Премахване на устройство",
"zigbee_information": "Подпис на Zigbee устройството"
@@ -1128,7 +1129,7 @@
"observer": "Проверете Надзорника",
"reboot": "Опитайте да рестартирате хоста",
"system_health": "Проверете здравето на системата",
"title": "Не може да се зареди Супервайзър-панела!",
"title": "Не може да се зареди Supervisor панела!",
"wait": "Ако току-що сте стартирали, дайте достатъчно време за старт на Супервайзъра."
}
},
@@ -1148,6 +1149,7 @@
"intergration_starting": "Стартиране на {integration}, не всичко ще бъде налично, докато процесът не приключи.",
"service_call_failed": "Неуспешно повикване на услуга {service}.",
"started": "Home Assistant стартира успешно!",
"starting": "Home Assistant стартира, не всичко ще е на разположение, докато не приключи.",
"triggered": "Задействане {name}",
"wrapping_up_startup": "Финализиране на стартирането, не всичко ще бъде налично, докато процесът не приключи."
},
@@ -1595,7 +1597,7 @@
"title": "Google Assistant"
},
"integrations": "Интеграции",
"integrations_introduction": "Интеграциите за облака Assistant Cloud позволяват свързването облачни услуги, като се избягва публичното излагане на инсталацията Home Assistant в интернет.",
"integrations_introduction": "Интеграциите за Home Assistant Cloud ви позволяват свързването с услуги в облака, без да се налага да излагате публично своя Home Assistant в интернет.",
"integrations_introduction2": "Проверете уебсайта за",
"integrations_link_all_features": "всички налични функции",
"manage_account": "Управление на акаунта",
@@ -1604,14 +1606,14 @@
"remote": {
"access_is_being_prepared": "Подготвя се отдалечен достъп. Ще ви уведомим, когато е готов.",
"certificate_info": "Информация за сертификата",
"info": "Облакът Home Assistant осигурява защитена отдалечена връзка с вашата инсталация, докато сте далеч от дома. ",
"info": "Home Assistant Cloud осигурява сигурна отдалечена връзка с вашата инсталация, докато сте извън дома.",
"instance_is_available": "Вашата инсталация е налична на",
"instance_will_be_available": "Вашата инсталация ще е налична на",
"link_learn_how_it_works": "Научете как работи",
"title": "Дистанционен контрол"
},
"sign_out": "Отписване",
"thank_you_note": "Благодарим Ви, че сте част от Home Assistant Cloud. Именно заради хора като вас ние сме в състояние да направим страхотно изживяване при автоматизацията на дома за всички. Благодарим Ви!",
"thank_you_note": "Благодарим Ви, че сте част от Home Assistant Cloud. Именно заради хора като вас ние сме в състояние да направим страхотно изживяване за всички при автоматизацията на дома. Благодарим Ви!",
"tts": {
"default_language": "Език по подразбиране за използване",
"dialog": {
@@ -1652,6 +1654,7 @@
"certificate_expiration_date": "Дата на изтичане на сертификата:",
"certificate_information": "Информация за сертификата",
"close": "Затвори",
"fingerprint": "Отпечатък на сертификата:",
"will_be_auto_renewed": "ще бъде автоматично подновен"
},
"dialog_cloudhook": {
@@ -1988,8 +1991,11 @@
"caption": "Интеграции",
"config_entry": {
"area": "В {area}",
"check_the_logs": "Проверете журналите",
"configure": "Конфигуриране",
"delete": "Изтриване",
"delete_confirm": "Сигурни ли сте, че искате да изтриете интеграцията?",
"depends_on_cloud": "Зависи от облака",
"device_unavailable": "Устройството е недостъпно",
"devices": "{count} {count, plural,\n one {устройство}\n other {устройства}\n}",
"disable_restart_confirm": "Рестартирайте Home Assistant за да завършите деактивирането на тази интеграция",
@@ -2074,6 +2080,7 @@
"caption": "Журнали",
"clear": "Изчистване",
"custom_integration": "персонализирана интеграция",
"description": "Преглед на журналите на Home Assistant",
"error_from_custom_integration": "Тази грешка произтича от персонализирана интеграция.",
"level": {
"critical": "КРИТИЧНО",
@@ -2084,7 +2091,7 @@
},
"load_full_log": "Зареждане на пълния журнал на Home Assistant",
"loading_log": "Зарежда се журнала за грешки...",
"multiple_messages": "съобщението е възникнало за първи път в {time} и се показва {counter} пъти",
"multiple_messages": "съобщението е възникнало за първи път на {time} и се показва {counter} пъти",
"no_issues": "Няма нови проблеми!",
"refresh": "Опресняване"
},
@@ -2337,6 +2344,7 @@
"reloading": {
"automation": "Презареждане на автоматизациите",
"core": "Презареждане на местоположението и персонализациите",
"filter": "Филтриране на обектите",
"group": "Презареждане на групите, груповите обекти и услугите за уведомяване",
"heading": "Презареждане на YAML конфигурацията",
"homekit": "Презареждане на HomeKit",
@@ -2412,6 +2420,7 @@
"editor": {
"activate_user": "Активиране на потребител",
"active": "Активен",
"active_tooltip": "Контролира дали потребителят може да влиза",
"admin": "Администратор",
"caption": "Преглед на потребителя",
"change_password": "Смяна на парола",
@@ -2464,6 +2473,13 @@
"clusters": "Клъстери",
"value": "Стойност"
},
"configuration_page": {
"shortcuts_title": "Преки пътища",
"update_button": "Актуализиране на конфигурацията",
"zha_options": {
"title": "Глобални опции"
}
},
"device_pairing_card": {
"CONFIGURED": "Конфигурирането завърши",
"CONFIGURED_status_text": "Инициализация",
@@ -2525,6 +2541,7 @@
},
"zwave_js": {
"add_node": {
"inclusion_failed": "Възелът не може да бъде добавен. Моля, проверете журналите за повече информация.",
"title": "Добавяне на Z-Wave възел"
},
"button": "Конфигуриране",
@@ -2567,6 +2584,7 @@
"unknown": "Неизвестен"
},
"remove_node": {
"exclusion_failed": "Възелът не може да бъде премахнат. Моля, проверете журналите за повече информация.",
"title": "Премахване на Z-Wave възел"
}
},
@@ -2656,11 +2674,14 @@
},
"states": {
"attributes": "Атрибути",
"copy_id": "Копиране на идентификатора в клипборда",
"current_entities": "Настоящи обекти",
"description1": "Задайте представянето на устройство или обект в Home Assistant.",
"description2": "Промяната е фиктивна, до следваща актуализация на устройството или обекта.",
"entity": "Обект",
"filter_attributes": "Филтриране на атрибути",
"filter_attributes": "Филтриране на атрибутите",
"filter_entities": "Филтриране на обектите",
"filter_states": "Филтриране на състоянията",
"last_changed": "Последна промяна",
"last_updated": "Последна актуализация",
"more_info": "Повече информация",
@@ -2990,6 +3011,8 @@
"edit": "Редактиране на изгледа",
"header": "Конфигурация на изглед",
"header_name": "Конфигурация на изглед {name}",
"move_left": "Преместване на изгледа наляво",
"move_right": "Преместване на изгледа надясно",
"tab_badges": "Значки",
"tab_settings": "Настройки",
"tab_visibility": "Видимост",

View File

@@ -561,9 +561,12 @@
},
"light": {
"brightness": "Brillantor",
"cold_white_value": "Brillantor de blancs freds",
"color_brightness": "Brillantor del color",
"color_temperature": "Temperatura de color",
"effect": "Efecte",
"white_value": "Ajust de blanc"
"warm_white_value": "Brillantor de blancs calents",
"white_value": "Brillantor de blanc"
},
"lock": {
"code": "Codi",
@@ -1194,7 +1197,19 @@
}
},
"zha_reconfigure_device": {
"heading": "S'està reconfigurant el dispositiu"
"attribute": "Atribut",
"bind_header": "Vinculació",
"button_hide": "Amaga els detalls",
"button_show": "Mostra els detalls",
"cluster_header": "Clúster",
"configuration_complete": "Reconfiguració de dispositiu completada.",
"configuration_failed": "Ha fallat la reconfiguració del dispositiu. Pots trobar informació addicional als registres.",
"configuring_alt": "Configurant",
"heading": "S'està reconfigurant el dispositiu",
"in_progress": "El dispositiu s'està reconfigurant. Això pot trigar una estona.",
"min_max_change": "min/max/canvi",
"run_in_background": "Pots tancar aquest diàleg, la reconfiguració continuarà en segon pla.",
"start_reconfiguration": "Inicia la reconfiguració"
}
},
"duration": {
@@ -1257,7 +1272,8 @@
"caption": "Àrees",
"data_table": {
"area": "Àrea",
"devices": "Dispositius"
"devices": "Dispositius",
"entities": "Entitats"
},
"delete": {
"confirmation_text": "Tots els dispositius d'aquesta àrea quedaran sense assignar.",
@@ -1269,8 +1285,10 @@
"create": "Crea",
"default_name": "Nova àrea",
"delete": "Elimina",
"linked_entities_caption": "Entitats",
"name": "Nom",
"name_required": "Nom obligatori",
"no_linked_entities": "No hi ha entitats vinculades amb aquesta àrea.",
"unknown_error": "Error desconegut",
"update": "Actualitza"
},
@@ -2921,7 +2939,12 @@
"node_status": "Estat del node",
"zwave_info": "Informació Z-Wave"
},
"logs": {
"log_level": "Nivell dels registres",
"title": "Registres de Z-Wave JS"
},
"navigation": {
"logs": "Registres",
"network": "Xarxa"
},
"network_status": {
@@ -2948,6 +2971,13 @@
"dead": "Mort",
"unknown": "Desconegut"
},
"reinterview_node": {
"in_progress": "S'està consultant el dispositiu. Això pot trigar una estona.",
"interview_complete": "Consulta del dispositiu completada.",
"interview_failed": "Ha fallat la consulta del dispositiu. Pots trobar informació addicional als registres.",
"start_reinterview": "Torna a iniciar consulta",
"title": "Torna a consultar un dispositiu Z-Wave"
},
"remove_node": {
"cancel_exclusion": "Cancel·la l'exclusió",
"controller_in_exclusion_mode": "El controlador Z-Wave ara està en mode d'exclusió.",

View File

@@ -1256,7 +1256,8 @@
"caption": "Oblasti",
"data_table": {
"area": "Oblast",
"devices": "Zařízení"
"devices": "Zařízení",
"entities": "Entity"
},
"delete": {
"confirmation_text": "Všechna zařízení v této oblasti budou nastavena jako nepřiřazena.",
@@ -1268,8 +1269,10 @@
"create": "VYTVOŘIT",
"default_name": "Nová oblast",
"delete": "Odstranit",
"linked_entities_caption": "Entity",
"name": "Název",
"name_required": "Název je povinný",
"no_linked_entities": "S touto oblastí nejsou spojeny žádné entity.",
"unknown_error": "Neznámá chyba",
"update": "Aktualizovat"
},

View File

@@ -561,9 +561,12 @@
},
"light": {
"brightness": "Brightness",
"cold_white_value": "Cold white brightness",
"color_brightness": "Color brightness",
"color_temperature": "Color temperature",
"effect": "Effect",
"white_value": "White value"
"warm_white_value": "Warm white brightness",
"white_value": "White brightness"
},
"lock": {
"code": "Code",
@@ -1194,7 +1197,22 @@
}
},
"zha_reconfigure_device": {
"heading": "Reconfiguring device"
"attribute": "Attribute",
"battery_device_warning": "You will need to wake battery powered devices before starting the reconfiguration process. Refer to your device's manual for instructions on how to wake the device.",
"bind_header": "Binding",
"button_hide": "Hide Details",
"button_show": "Show Details",
"cluster_header": "Cluster",
"configuration_complete": "Device reconfiguration complete.",
"configuration_failed": "The device reconfiguration failed. Additional information may be available in the logs.",
"configuring_alt": "Configuring",
"heading": "Reconfiguring device",
"in_progress": "The device is being reconfigured. This may take some time.",
"introduction": "Reconfigure a device on your Zigbee network. Use this feature if your device is not functioning correctly.",
"min_max_change": "min/max/change",
"reporting_header": "Reporting",
"run_in_background": "You can close this dialog and the reconfiguration will continue in the background.",
"start_reconfiguration": "Start Reconfiguration"
}
},
"duration": {
@@ -1257,7 +1275,8 @@
"caption": "Areas",
"data_table": {
"area": "Area",
"devices": "Devices"
"devices": "Devices",
"entities": "Entities"
},
"delete": {
"confirmation_text": "All devices in this area will become unassigned.",
@@ -1269,8 +1288,10 @@
"create": "Create",
"default_name": "New Area",
"delete": "Delete",
"linked_entities_caption": "Entities",
"name": "Name",
"name_required": "Name is required",
"no_linked_entities": "There are no entities linked to this area.",
"unknown_error": "Unknown error",
"update": "Update"
},
@@ -2921,7 +2942,12 @@
"node_status": "Node Status",
"zwave_info": "Z-Wave Info"
},
"logs": {
"log_level": "Log Level",
"title": "Z-Wave JS Logs"
},
"navigation": {
"logs": "Logs",
"network": "Network"
},
"network_status": {
@@ -2948,6 +2974,16 @@
"dead": "Dead",
"unknown": "Unknown"
},
"reinterview_node": {
"battery_device_warning": "You will need to wake battery powered devices before starting the re-interview. Refer to your device's manual for instructions on how to wake the device.",
"in_progress": "The device is being interviewed. This may take some time.",
"interview_complete": "Device interview complete.",
"interview_failed": "The device interview failed. Additional information may be available in the logs.",
"introduction": "Re-interview a device on your Z-Wave network. Use this feature if your device has missing or incorrect functionality.",
"run_in_background": "You can close this dialog and the interview will continue in the background.",
"start_reinterview": "Start Re-interview",
"title": "Re-interview a Z-Wave Device"
},
"remove_node": {
"cancel_exclusion": "Cancel Exclusion",
"controller_in_exclusion_mode": "Your Z-Wave controller is now in exclusion mode.",

View File

@@ -561,9 +561,12 @@
},
"light": {
"brightness": "Brillo",
"cold_white_value": "Brillo del blanco frío",
"color_brightness": "Brillo del color",
"color_temperature": "Temperatura del color",
"effect": "Efecto",
"white_value": "Valor de blanco"
"warm_white_value": "Brillo del blanco cálido",
"white_value": "Brillo del blanco"
},
"lock": {
"code": "Código",
@@ -886,6 +889,7 @@
}
},
"service-control": {
"integration_doc": "Documentación sobre la integración",
"required": "Este campo es obligatorio",
"service_data": "Datos del servicio",
"target": "Objetivos",
@@ -2935,6 +2939,9 @@
"header": "Configuración del dispositivo Z-Wave",
"introduction": "Administra y ajusta los parámetros de configuración específicos del dispositivo (nodo) para el dispositivo seleccionado",
"parameter_is_read_only": "Este parámetro es de solo lectura.",
"set_param_accepted": "El parámetro se ha actualizado.",
"set_param_error": "Se ha producido un error.",
"set_param_queued": "El cambio de parámetro se ha puesto en cola y se actualizará cuando el dispositivo se active.",
"zwave_js_device_database": "Base de datos de dispositivos Z-Wave JS"
},
"node_status": {
@@ -2944,6 +2951,16 @@
"dead": "No responde",
"unknown": "Desconocido"
},
"reinterview_node": {
"battery_device_warning": "Deberás reactivar los dispositivos que funcionan con baterías antes de comenzar la reentrevista. Consulta el manual de tu dispositivo para obtener instrucciones sobre cómo reactivar el dispositivo.",
"in_progress": "El dispositivo está siendo entrevistado. Esto puede llevar algún tiempo.",
"interview_complete": "Entrevista con el dispositivo completa.",
"interview_failed": "La entrevista del dispositivo falló. Es posible que haya información adicional disponible en los registros.",
"introduction": "Vuelve a entrevistar un dispositivo en tu red Z-Wave. Utiliza esta función si tu dispositivo tiene una funcionalidad incorrecta o ausente.",
"run_in_background": "Puedes cerrar este cuadro de diálogo y la entrevista continuará en segundo plano.",
"start_reinterview": "Iniciar la reentrevista",
"title": "Volver a entrevistar un dispositivo Z-Wave"
},
"remove_node": {
"cancel_exclusion": "Cancelar exclusión",
"controller_in_exclusion_mode": "Tu controlador Z-Wave ahora está en modo de exclusión.",

View File

@@ -561,9 +561,12 @@
},
"light": {
"brightness": "Heledus",
"cold_white_value": "Külma valguse heledus",
"color_brightness": "Värvilise valguse heledus",
"color_temperature": "Värvustemperatuur",
"effect": "Efekt",
"white_value": "Valge väärtus"
"warm_white_value": "Sooja valguse heledus",
"white_value": "Valge valguse heledus"
},
"lock": {
"code": "Kood",
@@ -886,6 +889,7 @@
}
},
"service-control": {
"integration_doc": "Sidumise teave",
"required": "See väli on nõutav",
"service_data": "Teenuse andmed",
"target": "Eesmärgid",
@@ -1193,7 +1197,22 @@
}
},
"zha_reconfigure_device": {
"heading": "Seadme sätete muutmine"
"attribute": "Atribuut",
"battery_device_warning": "Patareitoitel seadmed peavad olema ärkvel, et neid taasseadistada. Seadme äratamiseks vaata oma seadme kasutusjuhendit.",
"bind_header": "Sidumine",
"button_hide": "Peida üksikasjad",
"button_show": "Kuva üksikasjad",
"cluster_header": "Kobar",
"configuration_complete": "Seadme ümberseadistamine on lõpule viidud.",
"configuration_failed": "Seadme ümberseadistamine nurjus. Lisateavet võib saada logidest.",
"configuring_alt": "Seadistan",
"heading": "Seadme sätete muutmine",
"in_progress": "Seadet muudetakse ümber. See võib võtta aega.",
"introduction": "Seadista uuesti seadet Zigbee võrgus. Kasuta seda funktsiooni kui seadmel on puuduv või vale funktsionaalsus.",
"min_max_change": "min/max/muutus",
"reporting_header": "Aruandlus",
"run_in_background": "Saad selle dialoogi sulgeda ja ümberseadistamine jätkub taustal.",
"start_reconfiguration": "Alusta ümberseadistamist"
}
},
"duration": {
@@ -1256,7 +1275,8 @@
"caption": "Alade register",
"data_table": {
"area": "Ala",
"devices": "Seadmed"
"devices": "Seadmed",
"entities": "Olemid"
},
"delete": {
"confirmation_text": "Kõik sellele ala seadmed jäävad peremehetuks.",
@@ -1268,8 +1288,10 @@
"create": "LOO",
"default_name": "Uus ala",
"delete": "KUSTUTA",
"linked_entities_caption": "Olemid",
"name": "Nimi",
"name_required": "Nimi on kohustuslik",
"no_linked_entities": "Selle alaga pole seotud ühtegi olemit.",
"unknown_error": "Tundmatu viga",
"update": "UUENDA"
},
@@ -2920,7 +2942,12 @@
"node_status": "Sõlme olek",
"zwave_info": "Z-Wave teave"
},
"logs": {
"log_level": "Logimise tase",
"title": "Z-Wave JS logid"
},
"navigation": {
"logs": "Logid",
"network": "Võrk"
},
"network_status": {
@@ -2935,6 +2962,9 @@
"header": "Z-Wave seadme häälestamine",
"introduction": "Valitud seadme (sõlme) spetsiifiliste seadistuste haldamine ja muutmine",
"parameter_is_read_only": "See parameeter on kirjutuskaitstud.",
"set_param_accepted": "Parameeter on uuendatud.",
"set_param_error": "Ilmnes tõrge.",
"set_param_queued": "Parameetri uuendamine on ootel ja seda värskendatakse seadme ärkamisel.",
"zwave_js_device_database": "Z-Wave JS seadmete andmebaas"
},
"node_status": {
@@ -2944,6 +2974,16 @@
"dead": "Kadunud",
"unknown": "Teadmata"
},
"reinterview_node": {
"battery_device_warning": "Patareitoitel seadmed peavad olema ärkvel, et neid küsitleda. Seadme äratamiseks vaata oma seadme kasutusjuhendit.",
"in_progress": "Seadet küsitletakse. See võib võtta aega.",
"interview_complete": "Seadme küsitlemine on lõpetatud.",
"interview_failed": "Seadme küsitlemine nurjus. Lisateave võib olla kättesaadav logides.",
"introduction": "Küsitle uuesti seadet Z-Wave-võrgus. Kasuta seda funktsiooni kui seadmel on puuduv või vale funktsionaalsus.",
"run_in_background": "Saad selle dialoogi sulgeda ja küsitlus jätkub taustal.",
"start_reinterview": "Alusta küsitlust uuesti",
"title": "Z-Wave seadme taasküsitlemine"
},
"remove_node": {
"cancel_exclusion": "Tühista välistamine",
"controller_in_exclusion_mode": "Z-Wave kontroller on nüüd välistamisrežiimis.",

View File

@@ -561,9 +561,12 @@
},
"light": {
"brightness": "Luminosità",
"cold_white_value": "Luminosità bianca fredda",
"color_brightness": "Luminosità del colore",
"color_temperature": "Temperatura colore",
"effect": "Effetto",
"white_value": "Valore bianco"
"warm_white_value": "Luminosità bianca calda",
"white_value": "Luminosità del bianco"
},
"lock": {
"code": "Codice",
@@ -1194,7 +1197,22 @@
}
},
"zha_reconfigure_device": {
"heading": "Riconfigurazione del dispositivo"
"attribute": "Attributo",
"battery_device_warning": "Sarà necessario riattivare i dispositivi alimentati a batteria prima di avviare il processo di riconfigurazione. Fare riferimento al manuale del dispositivo per istruzioni su come riattivare il dispositivo.",
"bind_header": "Collegamento",
"button_hide": "Nascondi dettagli",
"button_show": "Mostra dettagli",
"cluster_header": "Grappolo",
"configuration_complete": "Riconfigurazione del dispositivo completata.",
"configuration_failed": "La riconfigurazione del dispositivo non è riuscita. Ulteriori informazioni potrebbero essere disponibili nei registri.",
"configuring_alt": "Configurazione",
"heading": "Riconfigurazione del dispositivo",
"in_progress": "Il dispositivo è in fase di riconfigurazione. Potrebbe volerci un po' di tempo.",
"introduction": "Riconfigura un dispositivo sulla tua rete Zigbee. Usa questa funzione se il tuo dispositivo non funziona correttamente.",
"min_max_change": "min/max/cambio",
"reporting_header": "Segnalazione",
"run_in_background": "È possibile chiudere questa finestra di dialogo e la riconfigurazione continuerà in background.",
"start_reconfiguration": "Avvia la riconfigurazione"
}
},
"duration": {
@@ -1257,7 +1275,8 @@
"caption": "Aree",
"data_table": {
"area": "Area",
"devices": "Dispositivi"
"devices": "Dispositivi",
"entities": "Entità"
},
"delete": {
"confirmation_text": "Tutti i dispositivi in quest'area non saranno assegnati.",
@@ -1269,8 +1288,10 @@
"create": "Crea",
"default_name": "Nuova area",
"delete": "Elimina",
"linked_entities_caption": "Entità",
"name": "Nome",
"name_required": "Il nome è obbligatorio",
"no_linked_entities": "Non ci sono entità collegate a quest'area.",
"unknown_error": "Errore sconosciuto",
"update": "Aggiorna"
},
@@ -2936,6 +2957,9 @@
"header": "Configurazione del dispositivo Z-Wave",
"introduction": "Gestire e regolare i parametri di configurazione specifici del dispositivo (nodo) per il dispositivo selezionato",
"parameter_is_read_only": "Questo parametro è di sola lettura.",
"set_param_accepted": "Il parametro è stato aggiornato.",
"set_param_error": "Si è verificato un errore.",
"set_param_queued": "La modifica del parametro è stata messa in coda e verrà aggiornata quando il dispositivo si riattiva.",
"zwave_js_device_database": "Database dei dispositivi Z-Wave JS"
},
"node_status": {
@@ -2945,6 +2969,16 @@
"dead": "Disattivo",
"unknown": "Sconosciuto"
},
"reinterview_node": {
"battery_device_warning": "Sarà necessario riattivare i dispositivi alimentati a batteria prima di iniziare la nuova interrogazione. Fare riferimento al manuale del dispositivo per istruzioni su come riattivare il dispositivo.",
"in_progress": "Il dispositivo viene interrogato. Potrebbe volerci un po' di tempo.",
"interview_complete": "Interrogazione del dispositivo completata.",
"interview_failed": "L'interrogazione al dispositivo non è riuscita. Ulteriori informazioni potrebbero essere disponibili nei registri.",
"introduction": "Interroga nuovamente un dispositivo sulla tua rete Z-Wave. Usa questa funzione se il tuo dispositivo ha funzionalità mancanti o errate.",
"run_in_background": "Puoi chiudere questa finestra di dialogo e l'interrogazione continuerà in background.",
"start_reinterview": "Inizia una nuova interrogazione",
"title": "Re-interroga un dispositivo Z-Wave"
},
"remove_node": {
"cancel_exclusion": "Annulla esclusione",
"controller_in_exclusion_mode": "Il tuo controller Z-Wave è ora in modalità di esclusione.",

View File

@@ -561,8 +561,11 @@
},
"light": {
"brightness": "밝기",
"cold_white_value": "주광색 밝기",
"color_brightness": "색상 밝기",
"color_temperature": "색온도",
"effect": "효과",
"warm_white_value": "전구색 밝기",
"white_value": "흰색 값"
},
"lock": {
@@ -886,6 +889,7 @@
}
},
"service-control": {
"integration_doc": "통합구성요소 설명서",
"required": "이 입력란은 필수 요소입니다",
"service_data": "서비스 데이터",
"target": "대상",
@@ -1193,7 +1197,22 @@
}
},
"zha_reconfigure_device": {
"heading": "기기 재구성 중"
"attribute": "속성",
"battery_device_warning": "재구성하기 전에 배터리 전원 장치의 절전 모드를 해제해야 합니다. 절전 모드를 해제하는 방법은 기기 설명서를 참조하십시오.",
"bind_header": "묶기",
"button_hide": "세부 정보 숨기기",
"button_show": "세부 정보 표시",
"cluster_header": "클러스터",
"configuration_complete": "장치 재구성이 완료되었습니다.",
"configuration_failed": "장치 재구성에 실패했습니다. 로그에서 정보를 확인할 수 있습니다.",
"configuring_alt": "구성",
"heading": "기기 재구성 중",
"in_progress": "장치가 재구성 중입니다. 시간이 좀 걸릴 수 있습니다.",
"introduction": "Zigbee 네트워크에서 장치를 재구성합니다. 장치가 올바르게 작동하지 않는 경우에 이 기능을 사용하십시오.",
"min_max_change": "최소/최대/변경",
"reporting_header": "보고",
"run_in_background": "이 대화 상자를 닫을 수 있으며 재구성은 백그라운드에서 계속됩니다.",
"start_reconfiguration": "재구성 시작"
}
},
"duration": {
@@ -2785,6 +2804,13 @@
"manufacturer_code_override": "제조업체 코드 재정의",
"value": "값"
},
"configuration_page": {
"update_button": "구성 업데이트",
"zha_options": {
"default_light_transition": "조명 전환 시간 기본 값(초)",
"title": "전역 옵션"
}
},
"device_pairing_card": {
"CONFIGURED": "구성이 완료되었습니다",
"CONFIGURED_status_text": "초기화 중",
@@ -2924,6 +2950,9 @@
"header": "Z-Wave 기기 구성",
"introduction": "선택한 기기(노드)의 매개 변수를 관리하고 조정합니다.",
"parameter_is_read_only": "이 매개 변수는 읽기 전용입니다.",
"set_param_accepted": "매개 변수가 업데이트되었습니다.",
"set_param_error": "오류가 발생했습니다.",
"set_param_queued": "장치의 절전모드가 해제되면 매개 변수가 업데이트됩니다.",
"zwave_js_device_database": "Z-Wave JS 기기 데이터베이스"
},
"node_status": {
@@ -2933,6 +2962,16 @@
"dead": "사용불가",
"unknown": "알 수 없음"
},
"reinterview_node": {
"battery_device_warning": "재인터뷰를 시작하기 전에 배터리 전원 장치의 절전 모드를 해제해야 합니다. 절전 모드를 해제하는 방법은 기기 설명서를 참조하십시오.",
"in_progress": "장치가 인터뷰중입니다. 시간이 좀 걸릴 수 있습니다.",
"interview_complete": "장치 인터뷰가 완료되었습니다.",
"interview_failed": "장치 인터뷰에 실패했습니다. 로그에서 정보를 확인할 수 있습니다.",
"introduction": "Z-Wave 네트워크 상의 장치를 다시 인터뷰합니다. 장치가 사라지거나 오동작할 경우에 이 기능을 사용하십시오.",
"run_in_background": "이 대화 상자를 닫을 수 있으며 인터뷰는 백그라운드에서 계속됩니다.",
"start_reinterview": "재인터뷰 시작",
"title": "Z-Wave 장치 재인터뷰"
},
"remove_node": {
"cancel_exclusion": "제외 취소하기",
"controller_in_exclusion_mode": "Z-Wave 컨트롤러가 이제 제외 모드에 있습니다",

View File

@@ -561,9 +561,12 @@
},
"light": {
"brightness": "Lysstyrke",
"cold_white_value": "Kald hvit lysstyrke",
"color_brightness": "Farge lysstyrke",
"color_temperature": "Fargetemperatur",
"effect": "Effekt",
"white_value": "Hvit verdi"
"warm_white_value": "Varm hvit lysstyrke",
"white_value": "Hvit lysstyrke"
},
"lock": {
"code": "Kode",
@@ -886,6 +889,7 @@
}
},
"service-control": {
"integration_doc": "Dokumentasjon for integrering",
"required": "Dette feltet er påkrevd",
"service_data": "Tjenestedata",
"target": "Mål",
@@ -2198,7 +2202,7 @@
"migration_error": "Migrasjonsfeil",
"not_loaded": "Ikke lastet",
"setup_error": "Kunne ikke konfigurere",
"setup_retry": "Prøver på nytt for å sette opp"
"setup_retry": "Prøver på nytt oppsett"
},
"system_options": "Systemalternativer",
"unnamed_entry": "Ikke navngitt oppføring"
@@ -2787,6 +2791,15 @@
"manufacturer_code_override": "Overstyring av produsentkode",
"value": "Verdi"
},
"configuration_page": {
"shortcuts_title": "Snarveier",
"update_button": "Oppdater konfigurasjon",
"zha_options": {
"default_light_transition": "Standard overgangstid for lys (sekunder)",
"enable_identify_on_join": "Aktiver identifiseringseffekt når enheter blir med i nettverket",
"title": "Globale alternativer"
}
},
"device_pairing_card": {
"CONFIGURED": "Konfigurasjonen er fullført",
"CONFIGURED_status_text": "Initialiserer",
@@ -2926,6 +2939,9 @@
"header": "Z-Wave enhetskonfigurasjon",
"introduction": "Administrer og juster enhets (node) spesifikke konfigurasjonsparametere for den valgte enheten",
"parameter_is_read_only": "Denne parameteren er skrivebeskyttet.",
"set_param_accepted": "Parameteren er oppdatert.",
"set_param_error": "Det oppstod en feil.",
"set_param_queued": "Parameterendringen har blitt satt i kø, og vil bli oppdatert når enheten våkner.",
"zwave_js_device_database": "Z-Wave JS Device Database"
},
"node_status": {
@@ -2935,6 +2951,16 @@
"dead": "Død",
"unknown": "Ukjent"
},
"reinterview_node": {
"battery_device_warning": "Du må vekke batteridrevne enheter før du begynner på nytt intervju. Se enhetens håndbok for instruksjoner om hvordan du vekker enheten.",
"in_progress": "Enheten blir intervjuet. Dette kan ta litt tid.",
"interview_complete": "Enhetsintervjuet er fullført.",
"interview_failed": "Enhetsintervjuet mislyktes. Ytterligere informasjon kan være tilgjengelig i loggene.",
"introduction": "Intervju en enhet på nytt på Z-Wave-nettverket. Bruk denne funksjonen hvis enheten mangler eller ikke har feil funksjonalitet.",
"run_in_background": "Du kan lukke denne dialogen, og intervjuet fortsetter i bakgrunnen.",
"start_reinterview": "Start Re-intervju",
"title": "Intervju en Z-Wave-enhet på nytt"
},
"remove_node": {
"cancel_exclusion": "Avbryt ekskludering",
"controller_in_exclusion_mode": "Z-Wave-kontrolleren er nå i ekskluderingsmodus",

View File

@@ -561,8 +561,11 @@
},
"light": {
"brightness": "Helderheid",
"cold_white_value": "Koud wit helderheid",
"color_brightness": "Kleurhelderheid",
"color_temperature": "Kleurtemperatuur",
"effect": "Effect",
"warm_white_value": "Warm wit helderheid",
"white_value": "Witwaarde"
},
"lock": {
@@ -886,6 +889,7 @@
}
},
"service-control": {
"integration_doc": "Integratiedocumentatie",
"required": "Dit veld is verplicht",
"service_data": "Service data",
"target": "Doelen",
@@ -1193,7 +1197,22 @@
}
},
"zha_reconfigure_device": {
"heading": "Apparaat opnieuw configureren"
"attribute": "Attribuut",
"battery_device_warning": "U moet apparaten met een batterij wekken voordat u het herconfiguratieproces start. Raadpleeg de handleiding van uw toestel voor instructies over hoe u het toestel kunt wekken.",
"bind_header": "Binding",
"button_hide": "Details verbergen",
"button_show": "Details weergeven",
"cluster_header": "Cluster",
"configuration_complete": "Herconfiguratie van apparaat voltooid.",
"configuration_failed": "De herconfiguratie van het apparaat is mislukt. Mogelijk is er aanvullende informatie beschikbaar in de logboeken.",
"configuring_alt": "Configureren",
"heading": "Apparaat opnieuw configureren",
"in_progress": "Het apparaat wordt opnieuw geconfigureerd. Dit kan wat tijd kosten.",
"introduction": "Herconfigureer een apparaat op je Zigbee netwerk. Gebruik deze functie als uw apparaat niet correct functioneert.",
"min_max_change": "mix/max/verander",
"reporting_header": "Rapporteren",
"run_in_background": "U kunt dit dialoogvenster sluiten en de herconfiguratie wordt op de achtergrond voortgezet.",
"start_reconfiguration": "Start herconfiguratie"
}
},
"duration": {
@@ -1256,7 +1275,8 @@
"caption": "Gebieden",
"data_table": {
"area": "Gebied",
"devices": "Apparaten"
"devices": "Apparaten",
"entities": "Entiteiten"
},
"delete": {
"confirmation_text": "Alle apparaten in dit gebied zullen niet meer toegewezen zijn.",
@@ -1268,8 +1288,10 @@
"create": "Aanmaken",
"default_name": "Nieuw Gebied",
"delete": "Verwijderen",
"linked_entities_caption": "Entiteiten",
"name": "Naam",
"name_required": "Naam is vereist",
"no_linked_entities": "Er zijn geen entiteiten gekoppeld aan dit gebied.",
"unknown_error": "Onbekende fout",
"update": "Bijwerken"
},
@@ -2935,6 +2957,9 @@
"header": "Z-Wave apparaatconfiguratie",
"introduction": "Beheren en aanpassen van apparaat (node) specifieke configuratieparameters voor het geselecteerde apparaat",
"parameter_is_read_only": "Deze parameter is alleen-lezen.",
"set_param_accepted": "De parameter is bijgewerkt.",
"set_param_error": "Er is een fout opgetreden.",
"set_param_queued": "De parameterwijziging is in een wachtrij geplaatst, en zal worden bijgewerkt wanneer het apparaat wakker wordt.",
"zwave_js_device_database": "Z-Wave JS Apparaat Database"
},
"node_status": {

View File

@@ -561,9 +561,12 @@
},
"light": {
"brightness": "Яркость",
"cold_white_value": "Яркость холодного белого",
"color_brightness": "Яркость цвета",
"color_temperature": "Цветовая температура",
"effect": "Эффект",
"white_value": "Значение для белого цвета"
"warm_white_value": "Яркость теплого белого",
"white_value": "Яркость белого"
},
"lock": {
"code": "Код",
@@ -886,6 +889,7 @@
}
},
"service-control": {
"integration_doc": "Документация по интеграции",
"required": "Обязательное поле",
"service_data": "Данные службы",
"target": "Цели",
@@ -1193,7 +1197,22 @@
}
},
"zha_reconfigure_device": {
"heading": "Перенастройка устройства"
"attribute": "Атрибут",
"battery_device_warning": "Перед повторной настройкой устройства с батарейным питанием необходимо вывести из спящего режима. О том, как это сделать, Вы можете узнать из инструкций к Вашему устройству.",
"bind_header": "Привязка",
"button_hide": "Скрыть подробности",
"button_show": "Подробнее",
"cluster_header": "Кластер",
"configuration_complete": "Настройка устройства завершена",
"configuration_failed": "Не удалось перенастроить устройство. Дополнительная информация может быть получена в журналах.",
"configuring_alt": "Настройка",
"heading": "Перенастройка устройства",
"in_progress": "Устройство настраивается, это может занять некоторое время.",
"introduction": "Повторная настройка устройства в Вашей сети Zigbee. Используйте эту функцию, если устройство работает некорректно.",
"min_max_change": "мин/макс/изменение",
"reporting_header": "Получение отчёта",
"run_in_background": "Вы можете закрыть это диалоговое окно, настройка продолжится в фоновом режиме.",
"start_reconfiguration": "Начать повторную настройку"
}
},
"duration": {
@@ -1256,7 +1275,8 @@
"caption": "Помещения",
"data_table": {
"area": "Помещение",
"devices": "Устройства"
"devices": "Устройства",
"entities": "Объекты"
},
"delete": {
"confirmation_text": "Связанные устройства потеряют привязку к помещению.",
@@ -1268,8 +1288,10 @@
"create": "Добавить",
"default_name": "Новое помещение",
"delete": "Удалить",
"linked_entities_caption": "Объекты",
"name": "Название",
"name_required": "Укажите название помещения",
"no_linked_entities": "С этим помещением не связаны никакие объекты.",
"unknown_error": "Неизвестная ошибка.",
"update": "Обновить"
},
@@ -2920,7 +2942,12 @@
"node_status": "Статус узла",
"zwave_info": "Информация о Z-Wave"
},
"logs": {
"log_level": "Уровень",
"title": "Журналы Z-Wave JS"
},
"navigation": {
"logs": "Журналы",
"network": "Сеть"
},
"network_status": {
@@ -2935,6 +2962,9 @@
"header": "Настройки устройства Z-Wave",
"introduction": "Настройка параметров конфигурации выбранного узла для выбранного устройства.",
"parameter_is_read_only": "Этот параметр доступен только для чтения.",
"set_param_accepted": "Параметр обновлен",
"set_param_error": "Произошла ошибка",
"set_param_queued": "Изменение параметра поставлено в очередь и будет обновлено, когда устройство выйдет из спящего режима",
"zwave_js_device_database": "базой данных устройств Z-Wave JS"
},
"node_status": {
@@ -2944,6 +2974,16 @@
"dead": "Мертвый",
"unknown": "Неизвестно"
},
"reinterview_node": {
"battery_device_warning": "Перед повторным опросом устройства с батарейным питанием необходимо вывести из спящего режима. О том, как это сделать, Вы можете узнать из инструкций к Вашему устройству.",
"in_progress": "Устройство опрашивается, это может занять некоторое время.",
"interview_complete": "Опрос устройства завершен.",
"interview_failed": "Опрос устройства не удался. Дополнительная информация может быть получена в журналах.",
"introduction": "Повторный опрос устройства в Вашей сети Z-Wave. Используйте эту функцию, если это устройство не работает или работает неправильно.",
"run_in_background": "Вы можете закрыть это диалоговое окно, опрос продолжится в фоновом режиме.",
"start_reinterview": "Начать повторный опрос",
"title": "Повторный опрос устройства Z-Wave"
},
"remove_node": {
"cancel_exclusion": "Отменить отключение",
"controller_in_exclusion_mode": "Ваш контроллер Z-Wave находится в режиме отключения.",

View File

@@ -1103,7 +1103,7 @@
"force_narrow": {
"header": "ซ่อนแถบด้านข้างเสมอ"
},
"is_owner": "ในฐานะ \"เจ้าของ\"",
"is_owner": "คุณเป็นเจ้าของ",
"language": {
"dropdown_label": "ภาษา",
"header": "ภาษา",

View File

@@ -886,6 +886,7 @@
}
},
"service-control": {
"integration_doc": "集成文档",
"required": "此字段为必填字段",
"service_data": "服务数据",
"target": "目标",
@@ -2935,6 +2936,9 @@
"header": "Z-Wave 设备配置",
"introduction": "管理并调整所选设备(节点)的具体配置参数。",
"parameter_is_read_only": "此参数为只读。",
"set_param_accepted": "参数已更新。",
"set_param_error": "发生错误。",
"set_param_queued": "参数更改操作已加入队列,将在设备唤醒时更新。",
"zwave_js_device_database": "Z-Wave JS 设备数据库"
},
"node_status": {

View File

@@ -561,9 +561,12 @@
},
"light": {
"brightness": "亮度",
"cold_white_value": "冷白亮度",
"color_brightness": "色彩亮度",
"color_temperature": "色溫",
"effect": "場景",
"white_value": "白色值"
"warm_white_value": "暖白亮度",
"white_value": "白亮度"
},
"lock": {
"code": "密碼",
@@ -886,6 +889,7 @@
}
},
"service-control": {
"integration_doc": "整合文件",
"required": "必填欄位",
"service_data": "服務資料",
"target": "目標",
@@ -2198,7 +2202,7 @@
"migration_error": "遷移錯誤",
"not_loaded": "未載入",
"setup_error": "設定失敗",
"setup_retry": "重設定中"
"setup_retry": "重設定中"
},
"system_options": "系統選項",
"unnamed_entry": "未命名實體"
@@ -2660,7 +2664,7 @@
"confirm_restart": "確定要重啟 Home Assistant 嗎?",
"confirm_stop": "確定要停止 Home Assistant 嗎?",
"heading": "服務器管理",
"introduction": " Home Assistant控制 Home Assistant 伺服器。",
"introduction": "使用 Home Assistant、於離家時遠端控制 Home Assistant 伺服器。",
"restart": "重啟",
"stop": "停止"
},
@@ -2787,6 +2791,15 @@
"manufacturer_code_override": "製造商代碼覆寫",
"value": "數值"
},
"configuration_page": {
"shortcuts_title": "捷徑",
"update_button": "更新設定",
"zha_options": {
"default_light_transition": "預設燈光轉換時間(秒)",
"enable_identify_on_join": "當裝置加入網路時、開啟識別效果",
"title": "Global 選項"
}
},
"device_pairing_card": {
"CONFIGURED": "設定完成",
"CONFIGURED_status_text": "初始化中",
@@ -2926,6 +2939,9 @@
"header": "Z-Wave 裝置設定",
"introduction": "管理與調整所選擇裝置 (節點) 之特定設定參數",
"parameter_is_read_only": "此參數為唯讀。",
"set_param_accepted": "參數已更新。",
"set_param_error": "發生錯誤。",
"set_param_queued": "參數變更已排序、將會於裝置喚醒時更新。",
"zwave_js_device_database": "Z-Wave JS 裝置資料庫"
},
"node_status": {
@@ -2935,6 +2951,16 @@
"dead": "失效",
"unknown": "未知"
},
"reinterview_node": {
"battery_device_warning": "電池供電裝置必須先喚醒以重新探訪。請參考您的裝置手冊以了解如何喚醒裝置。",
"in_progress": "正在探訪裝置,可能需要一點時間。",
"interview_complete": "裝置探訪完成。",
"interview_failed": "裝置探訪失敗。日誌中可能包含其他相關資訊。",
"introduction": "重新探訪 Z-Wave 網路中的裝置。假如裝置缺少、或者功能不正確時,請使用此選項。",
"run_in_background": "可以關閉此對話框、探訪將於背景繼續執行。",
"start_reinterview": "開始重新探訪",
"title": "重新探訪 Z-Wave 裝置"
},
"remove_node": {
"cancel_exclusion": "取消排除",
"controller_in_exclusion_mode": "Z-Wave 控制器目前處於排除模式。",
@@ -3085,8 +3111,8 @@
"attributes": "屬性",
"copy_id": "複製 ID 至剪貼簿",
"current_entities": "目前實體",
"description1": "設定 Home Assistant 裝置代表。",
"description2": "將不會與實際裝置進行通訊。",
"description1": "設定 Home Assistant 實體目前狀態。",
"description2": "假如實體屬於裝置,將不會與實際裝置進行通訊。",
"entity": "實體",
"filter_attributes": "屬性篩選器",
"filter_entities": "實體篩選器",

2290
yarn.lock

File diff suppressed because it is too large Load Diff