mirror of
https://github.com/home-assistant/frontend.git
synced 2025-09-02 13:00:27 +00:00
Compare commits
48 Commits
analytics-
...
20210429.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
764ae7e0b6 | ||
![]() |
da0bfa1945 | ||
![]() |
3c61d709b5 | ||
![]() |
ffc92a7b63 | ||
![]() |
af0c7b5a50 | ||
![]() |
1904c4d057 | ||
![]() |
542f169b36 | ||
![]() |
65a30bf60c | ||
![]() |
2e51da32f0 | ||
![]() |
0562242043 | ||
![]() |
debcdefc21 | ||
![]() |
6b7e78320d | ||
![]() |
0de3f3a332 | ||
![]() |
4fcb4d449e | ||
![]() |
408fe25abd | ||
![]() |
236e5e0b25 | ||
![]() |
ebe0caba83 | ||
![]() |
9d33c0cfaf | ||
![]() |
7962130a0c | ||
![]() |
9690434cac | ||
![]() |
7304544c37 | ||
![]() |
5a3408c242 | ||
![]() |
16996f25af | ||
![]() |
0c12586019 | ||
![]() |
93a1adaa56 | ||
![]() |
83e65e2cc6 | ||
![]() |
36586b798e | ||
![]() |
20c351949f | ||
![]() |
b63bd92d81 | ||
![]() |
17d3755152 | ||
![]() |
d7c0c2ea72 | ||
![]() |
7c823c98ae | ||
![]() |
97508a6f31 | ||
![]() |
2507a41b6e | ||
![]() |
9b628546c1 | ||
![]() |
d0837fada8 | ||
![]() |
520647d72f | ||
![]() |
51c888845c | ||
![]() |
e4606219bc | ||
![]() |
716335df2c | ||
![]() |
ad4f90c502 | ||
![]() |
a1bdfa7560 | ||
![]() |
34ca807044 | ||
![]() |
8af55efdb3 | ||
![]() |
6393072e68 | ||
![]() |
9e1a8b646b | ||
![]() |
c810e541ea | ||
![]() |
66432608ed |
@@ -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",
|
||||
|
@@ -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",
|
||||
},
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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"],
|
||||
}),
|
||||
];
|
||||
|
13
package.json
13
package.json
@@ -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",
|
||||
|
2
setup.py
2
setup.py
@@ -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",
|
||||
|
@@ -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];
|
||||
};
|
||||
|
@@ -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;
|
||||
|
@@ -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>`;
|
||||
|
@@ -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;
|
||||
|
@@ -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}
|
||||
|
@@ -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 ");
|
||||
|
@@ -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({
|
||||
|
@@ -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" });
|
||||
|
@@ -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;
|
||||
|
@@ -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>;
|
||||
|
@@ -112,7 +112,7 @@ export interface ChooseActionChoice {
|
||||
|
||||
export interface ChooseAction {
|
||||
alias?: string;
|
||||
choose: ChooseActionChoice[] | null;
|
||||
choose: ChooseActionChoice | ChooseActionChoice[] | null;
|
||||
default?: Action | Action[];
|
||||
}
|
||||
|
||||
|
@@ -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";
|
||||
|
@@ -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 },
|
||||
});
|
||||
|
@@ -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;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
@@ -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() {
|
||||
|
@@ -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") {
|
||||
|
@@ -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" />
|
||||
|
@@ -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}
|
||||
|
@@ -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,
|
||||
|
@@ -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"
|
||||
|
@@ -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;
|
||||
}),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -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 },
|
||||
|
@@ -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() {
|
||||
|
@@ -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}
|
||||
|
@@ -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,
|
||||
|
@@ -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>
|
||||
`;
|
||||
|
@@ -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;
|
||||
|
@@ -640,6 +640,7 @@ export class HaIntegrationCard extends LitElement {
|
||||
flex: 1;
|
||||
margin-left: 8px;
|
||||
padding-top: 2px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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,
|
||||
});
|
||||
};
|
@@ -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"),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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) {
|
||||
|
@@ -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() {
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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"
|
||||
|
@@ -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
|
||||
);
|
||||
|
||||
/**
|
||||
|
@@ -85,7 +85,7 @@ export class OriginalStatesStrategy {
|
||||
return {
|
||||
views: [
|
||||
{
|
||||
strategy: { name: "original-states" },
|
||||
strategy: { type: "original-states" },
|
||||
title: info.hass.config.location_name,
|
||||
},
|
||||
],
|
||||
|
@@ -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);
|
||||
|
@@ -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 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
@@ -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": "Видимост",
|
||||
|
@@ -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ó.",
|
||||
|
@@ -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"
|
||||
},
|
||||
|
@@ -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.",
|
||||
|
@@ -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.",
|
||||
|
@@ -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.",
|
||||
|
@@ -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.",
|
||||
|
@@ -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 컨트롤러가 이제 제외 모드에 있습니다",
|
||||
|
@@ -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",
|
||||
|
@@ -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": {
|
||||
|
@@ -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 находится в режиме отключения.",
|
||||
|
@@ -1103,7 +1103,7 @@
|
||||
"force_narrow": {
|
||||
"header": "ซ่อนแถบด้านข้างเสมอ"
|
||||
},
|
||||
"is_owner": "ในฐานะ \"เจ้าของ\"",
|
||||
"is_owner": "คุณเป็นเจ้าของ",
|
||||
"language": {
|
||||
"dropdown_label": "ภาษา",
|
||||
"header": "ภาษา",
|
||||
|
@@ -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": {
|
||||
|
@@ -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": "實體篩選器",
|
||||
|
Reference in New Issue
Block a user