mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-29 04:06:35 +00:00
commit
1c69aa122b
@ -94,22 +94,19 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
|
||||
target_temp_high: 24,
|
||||
target_temp_low: 20,
|
||||
fan_mode: "auto",
|
||||
fan_list: ["auto", "on"],
|
||||
operation_mode: "auto",
|
||||
operation_list: ["auto", "auxHeatOnly", "cool", "heat", "off"],
|
||||
hold_mode: null,
|
||||
away_mode: "off",
|
||||
fan_modes: ["auto", "on"],
|
||||
hvac_modes: ["auto", "cool", "heat", "off"],
|
||||
aux_heat: "off",
|
||||
actual_humidity: 30,
|
||||
fan: "on",
|
||||
climate_mode: "Day",
|
||||
operation: "fan",
|
||||
climate_list: ["Away", "Sleep", "Day", "Home"],
|
||||
fan_min_on_time: 10,
|
||||
friendly_name: localize(
|
||||
"ui.panel.page-demo.config.arsaboo.names.upstairs"
|
||||
),
|
||||
supported_features: 3575,
|
||||
supported_features: 27,
|
||||
preset_mode: "away",
|
||||
preset_modes: ["home", "away", "eco", "sleep"],
|
||||
},
|
||||
},
|
||||
"input_boolean.abodeupdate": {
|
||||
|
@ -14,14 +14,14 @@ const ENTITIES = [
|
||||
target_temp_high: 75,
|
||||
target_temp_low: 70,
|
||||
fan_mode: "Auto Low",
|
||||
fan_list: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
operation_mode: "auto",
|
||||
operation_list: ["heat", "cool", "auto", "off"],
|
||||
hold_mode: "home",
|
||||
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
swing_mode: "Auto",
|
||||
swing_list: ["Auto", "1", "2", "3", "Off"],
|
||||
swing_modes: ["Auto", "1", "2", "3", "Off"],
|
||||
friendly_name: "Ecobee",
|
||||
supported_features: 1014,
|
||||
supported_features: 59,
|
||||
preset_mode: "eco",
|
||||
preset_modes: ["away", "eco"],
|
||||
}),
|
||||
getEntity("climate", "nest", "heat", {
|
||||
current_temperature: 17,
|
||||
@ -29,14 +29,12 @@ const ENTITIES = [
|
||||
max_temp: 25,
|
||||
temperature: 19,
|
||||
fan_mode: "Auto Low",
|
||||
fan_list: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
operation_mode: "heat",
|
||||
operation_list: ["heat", "cool", "auto", "off"],
|
||||
hold_mode: "home",
|
||||
fan_modes: ["On Low", "On High", "Auto Low", "Auto High", "Off"],
|
||||
hvac_modes: ["heat", "cool", "auto", "off"],
|
||||
swing_mode: "Auto",
|
||||
swing_list: ["Auto", "1", "2", "3", "Off"],
|
||||
swing_modes: ["Auto", "1", "2", "3", "Off"],
|
||||
friendly_name: "Nest",
|
||||
supported_features: 1014,
|
||||
supported_features: 43,
|
||||
}),
|
||||
];
|
||||
|
||||
|
@ -56,7 +56,7 @@ class HaGallery extends PolymerElement {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
a paper-item {
|
||||
a {
|
||||
color: var(--primary-text-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
@ -138,12 +138,22 @@ class HaGallery extends PolymerElement {
|
||||
</template>
|
||||
</div>
|
||||
</app-header-layout>
|
||||
<notification-manager id='notifications'></notification-manager>
|
||||
<notification-manager hass=[[_fakeHass]] id='notifications'></notification-manager>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
_fakeHass: {
|
||||
type: Object,
|
||||
// Just enough for computeRTL
|
||||
value: {
|
||||
language: "en",
|
||||
translationMetadata: {
|
||||
translations: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
_demo: {
|
||||
type: String,
|
||||
value: document.location.hash.substr(1),
|
||||
|
@ -44,8 +44,8 @@ module.exports = {
|
||||
to: "static/images/leaflet/",
|
||||
},
|
||||
{
|
||||
from: "../node_modules/@polymer/font-roboto-local/fonts",
|
||||
to: "static/fonts",
|
||||
from: "../node_modules/roboto-fontface/fonts/roboto/*.woff2",
|
||||
to: "static/fonts/roboto/",
|
||||
},
|
||||
{
|
||||
from: "../node_modules/leaflet/dist/images",
|
||||
|
2
setup.py
2
setup.py
@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="home-assistant-frontend",
|
||||
version="20190702.0",
|
||||
version="20190705.0",
|
||||
description="The Home Assistant frontend",
|
||||
url="https://github.com/home-assistant/home-assistant-polymer",
|
||||
author="The Home Assistant Authors",
|
||||
|
@ -38,7 +38,12 @@ class HaClimateState extends LocalizeMixin(PolymerElement) {
|
||||
|
||||
<div class="target">
|
||||
<template is="dom-if" if="[[_hasKnownState(stateObj.state)]]">
|
||||
<span class="state-label"> [[_localizeState(stateObj.state)]] </span>
|
||||
<span class="state-label">
|
||||
[[_localizeState(localize, stateObj.state)]]
|
||||
<template is="dom-if" if="[[stateObj.attributes.preset_mode]]">
|
||||
- [[_localizePreset(localize, stateObj.attributes.preset_mode)]]
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
<div class="unit">[[computeTarget(hass, stateObj)]]</div>
|
||||
</div>
|
||||
@ -83,7 +88,7 @@ class HaClimateState extends LocalizeMixin(PolymerElement) {
|
||||
stateObj.attributes.target_temp_low != null &&
|
||||
stateObj.attributes.target_temp_high != null
|
||||
) {
|
||||
return `${stateObj.attributes.target_temp_low} - ${
|
||||
return `${stateObj.attributes.target_temp_low}-${
|
||||
stateObj.attributes.target_temp_high
|
||||
} ${hass.config.unit_system.temperature}`;
|
||||
}
|
||||
@ -96,9 +101,9 @@ class HaClimateState extends LocalizeMixin(PolymerElement) {
|
||||
stateObj.attributes.target_humidity_low != null &&
|
||||
stateObj.attributes.target_humidity_high != null
|
||||
) {
|
||||
return `${stateObj.attributes.target_humidity_low} - ${
|
||||
return `${stateObj.attributes.target_humidity_low}-${
|
||||
stateObj.attributes.target_humidity_high
|
||||
} %`;
|
||||
}%`;
|
||||
}
|
||||
if (stateObj.attributes.humidity != null) {
|
||||
return `${stateObj.attributes.humidity} %`;
|
||||
@ -111,8 +116,12 @@ class HaClimateState extends LocalizeMixin(PolymerElement) {
|
||||
return state !== "unknown";
|
||||
}
|
||||
|
||||
_localizeState(state) {
|
||||
return this.localize(`state.climate.${state}`) || state;
|
||||
_localizeState(localize, state) {
|
||||
return localize(`state.climate.${state}`) || state;
|
||||
}
|
||||
|
||||
_localizePreset(localize, preset) {
|
||||
return localize(`state_attributes.climate.preset_mode.${preset}`) || preset;
|
||||
}
|
||||
}
|
||||
customElements.define("ha-climate-state", HaClimateState);
|
||||
|
51
src/data/climate.ts
Normal file
51
src/data/climate.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import {
|
||||
HassEntityBase,
|
||||
HassEntityAttributeBase,
|
||||
} from "home-assistant-js-websocket";
|
||||
|
||||
export type HvacMode =
|
||||
| "off"
|
||||
| "heat"
|
||||
| "cool"
|
||||
| "heat_cool"
|
||||
| "auto"
|
||||
| "dry"
|
||||
| "fan_only";
|
||||
|
||||
export type HvacAction = "off" | "Heating" | "cooling" | "drying" | "idle";
|
||||
|
||||
export type ClimateEntity = HassEntityBase & {
|
||||
attributes: HassEntityAttributeBase & {
|
||||
hvac_mode: HvacMode;
|
||||
hvac_modes: HvacMode[];
|
||||
hvac_action?: HvacAction;
|
||||
current_temperature: number;
|
||||
min_temp: number;
|
||||
max_temp: number;
|
||||
temperature: number;
|
||||
target_temp_step?: number;
|
||||
target_temp_high?: number;
|
||||
target_temp_low?: number;
|
||||
humidity?: number;
|
||||
current_humidity?: number;
|
||||
target_humidity_low?: number;
|
||||
target_humidity_high?: number;
|
||||
min_humidity?: number;
|
||||
max_humidity?: number;
|
||||
fan_mode?: string;
|
||||
fan_modes?: string[];
|
||||
preset_mode?: string;
|
||||
preset_modes?: string[];
|
||||
swing_mode?: string;
|
||||
swing_modes?: string[];
|
||||
aux_heat?: "on" | "off";
|
||||
};
|
||||
};
|
||||
|
||||
export const CLIMATE_SUPPORT_TARGET_TEMPERATURE = 1;
|
||||
export const CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE = 2;
|
||||
export const CLIMATE_SUPPORT_TARGET_HUMIDITY = 4;
|
||||
export const CLIMATE_SUPPORT_FAN_MODE = 8;
|
||||
export const CLIMATE_SUPPORT_PRESET_MODE = 16;
|
||||
export const CLIMATE_SUPPORT_SWING_MODE = 32;
|
||||
export const CLIMATE_SUPPORT_AUX_HEAT = 64;
|
@ -1,533 +0,0 @@
|
||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-toggle-button/paper-toggle-button";
|
||||
import { timeOut } from "@polymer/polymer/lib/utils/async";
|
||||
import { Debouncer } from "@polymer/polymer/lib/utils/debounce";
|
||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||
|
||||
import "../../../components/ha-climate-control";
|
||||
import "../../../components/ha-paper-slider";
|
||||
import "../../../components/ha-paper-dropdown-menu";
|
||||
|
||||
import attributeClassNames from "../../../common/entity/attribute_class_names";
|
||||
import featureClassNames from "../../../common/entity/feature_class_names";
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
|
||||
import { EventsMixin } from "../../../mixins/events-mixin";
|
||||
import LocalizeMixin from "../../../mixins/localize-mixin";
|
||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||
|
||||
/*
|
||||
* @appliesMixin EventsMixin
|
||||
* @appliesMixin LocalizeMixin
|
||||
*/
|
||||
class MoreInfoClimate extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
||||
static get template() {
|
||||
return html`
|
||||
<style include="iron-flex"></style>
|
||||
<style>
|
||||
:host {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
.container-on,
|
||||
.container-away_mode,
|
||||
.container-aux_heat,
|
||||
.container-temperature,
|
||||
.container-humidity,
|
||||
.container-operation_list,
|
||||
.container-fan_list,
|
||||
.container-swing_list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.has-on .container-on,
|
||||
.has-away_mode .container-away_mode,
|
||||
.has-aux_heat .container-aux_heat,
|
||||
.has-target_temperature .container-temperature,
|
||||
.has-target_temperature_low .container-temperature,
|
||||
.has-target_temperature_high .container-temperature,
|
||||
.has-target_humidity .container-humidity,
|
||||
.has-operation_mode .container-operation_list,
|
||||
.has-fan_mode .container-fan_list,
|
||||
.has-swing_list .container-swing_list,
|
||||
.has-swing_mode .container-swing_list {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.container-operation_list iron-icon,
|
||||
.container-fan_list iron-icon,
|
||||
.container-swing_list iron-icon {
|
||||
margin: 22px 16px 0 0;
|
||||
}
|
||||
|
||||
ha-paper-dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ha-paper-slider {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container-humidity .single-row {
|
||||
display: flex;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.target-humidity {
|
||||
width: 90px;
|
||||
font-size: 200%;
|
||||
margin: auto;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
ha-climate-control.range-control-left,
|
||||
ha-climate-control.range-control-right {
|
||||
float: left;
|
||||
width: 46%;
|
||||
}
|
||||
ha-climate-control.range-control-left {
|
||||
margin-right: 4%;
|
||||
}
|
||||
ha-climate-control.range-control-right {
|
||||
margin-left: 4%;
|
||||
}
|
||||
|
||||
.humidity {
|
||||
--paper-slider-active-color: var(--paper-blue-400);
|
||||
--paper-slider-secondary-color: var(--paper-blue-400);
|
||||
}
|
||||
|
||||
.single-row {
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class$="[[computeClassNames(stateObj)]]">
|
||||
<template is="dom-if" if="[[supportsOn(stateObj)]]">
|
||||
<div class="container-on">
|
||||
<div class="center horizontal layout single-row">
|
||||
<div class="flex">[[localize('ui.card.climate.on_off')]]</div>
|
||||
<paper-toggle-button
|
||||
checked="[[onToggleChecked]]"
|
||||
on-change="onToggleChanged"
|
||||
>
|
||||
</paper-toggle-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="container-temperature">
|
||||
<div class$="[[stateObj.attributes.operation_mode]]">
|
||||
<div hidden$="[[!supportsTemperatureControls(stateObj)]]">
|
||||
[[localize('ui.card.climate.target_temperature')]]
|
||||
</div>
|
||||
<template is="dom-if" if="[[supportsTemperature(stateObj)]]">
|
||||
<ha-climate-control
|
||||
value="[[stateObj.attributes.temperature]]"
|
||||
units="[[hass.config.unit_system.temperature]]"
|
||||
step="[[computeTemperatureStepSize(hass, stateObj)]]"
|
||||
min="[[stateObj.attributes.min_temp]]"
|
||||
max="[[stateObj.attributes.max_temp]]"
|
||||
on-change="targetTemperatureChanged"
|
||||
>
|
||||
</ha-climate-control>
|
||||
</template>
|
||||
<template is="dom-if" if="[[supportsTemperatureRange(stateObj)]]">
|
||||
<ha-climate-control
|
||||
value="[[stateObj.attributes.target_temp_low]]"
|
||||
units="[[hass.config.unit_system.temperature]]"
|
||||
step="[[computeTemperatureStepSize(hass, stateObj)]]"
|
||||
min="[[stateObj.attributes.min_temp]]"
|
||||
max="[[stateObj.attributes.target_temp_high]]"
|
||||
class="range-control-left"
|
||||
on-change="targetTemperatureLowChanged"
|
||||
>
|
||||
</ha-climate-control>
|
||||
<ha-climate-control
|
||||
value="[[stateObj.attributes.target_temp_high]]"
|
||||
units="[[hass.config.unit_system.temperature]]"
|
||||
step="[[computeTemperatureStepSize(hass, stateObj)]]"
|
||||
min="[[stateObj.attributes.target_temp_low]]"
|
||||
max="[[stateObj.attributes.max_temp]]"
|
||||
class="range-control-right"
|
||||
on-change="targetTemperatureHighChanged"
|
||||
>
|
||||
</ha-climate-control>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template is="dom-if" if="[[supportsHumidity(stateObj)]]">
|
||||
<div class="container-humidity">
|
||||
<div>[[localize('ui.card.climate.target_humidity')]]</div>
|
||||
<div class="single-row">
|
||||
<div class="target-humidity">
|
||||
[[stateObj.attributes.humidity]] %
|
||||
</div>
|
||||
<ha-paper-slider
|
||||
class="humidity"
|
||||
min="[[stateObj.attributes.min_humidity]]"
|
||||
max="[[stateObj.attributes.max_humidity]]"
|
||||
secondary-progress="[[stateObj.attributes.max_humidity]]"
|
||||
step="1"
|
||||
pin=""
|
||||
value="[[stateObj.attributes.humidity]]"
|
||||
on-change="targetHumiditySliderChanged"
|
||||
ignore-bar-touch=""
|
||||
dir="[[rtl]]"
|
||||
>
|
||||
</ha-paper-slider>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template is="dom-if" if="[[supportsOperationMode(stateObj)]]">
|
||||
<div class="container-operation_list">
|
||||
<div class="controls">
|
||||
<ha-paper-dropdown-menu
|
||||
label-float=""
|
||||
dynamic-align=""
|
||||
label="[[localize('ui.card.climate.operation')]]"
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected="[[stateObj.attributes.operation_mode]]"
|
||||
attr-for-selected="item-name"
|
||||
on-selected-changed="handleOperationmodeChanged"
|
||||
>
|
||||
<template
|
||||
is="dom-repeat"
|
||||
items="[[stateObj.attributes.operation_list]]"
|
||||
>
|
||||
<paper-item item-name$="[[item]]"
|
||||
>[[_localizeOperationMode(localize, item)]]</paper-item
|
||||
>
|
||||
</template>
|
||||
</paper-listbox>
|
||||
</ha-paper-dropdown-menu>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template is="dom-if" if="[[supportsFanMode(stateObj)]]">
|
||||
<div class="container-fan_list">
|
||||
<ha-paper-dropdown-menu
|
||||
label-float=""
|
||||
dynamic-align=""
|
||||
label="[[localize('ui.card.climate.fan_mode')]]"
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected="[[stateObj.attributes.fan_mode]]"
|
||||
attr-for-selected="item-name"
|
||||
on-selected-changed="handleFanmodeChanged"
|
||||
>
|
||||
<template
|
||||
is="dom-repeat"
|
||||
items="[[stateObj.attributes.fan_list]]"
|
||||
>
|
||||
<paper-item item-name$="[[item]]"
|
||||
>[[_localizeFanMode(localize, item)]]
|
||||
</paper-item>
|
||||
</template>
|
||||
</paper-listbox>
|
||||
</ha-paper-dropdown-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template is="dom-if" if="[[supportsSwingMode(stateObj)]]">
|
||||
<div class="container-swing_list">
|
||||
<ha-paper-dropdown-menu
|
||||
label-float=""
|
||||
dynamic-align=""
|
||||
label="[[localize('ui.card.climate.swing_mode')]]"
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
selected="[[stateObj.attributes.swing_mode]]"
|
||||
attr-for-selected="item-name"
|
||||
on-selected-changed="handleSwingmodeChanged"
|
||||
>
|
||||
<template
|
||||
is="dom-repeat"
|
||||
items="[[stateObj.attributes.swing_list]]"
|
||||
>
|
||||
<paper-item item-name$="[[item]]">[[item]]</paper-item>
|
||||
</template>
|
||||
</paper-listbox>
|
||||
</ha-paper-dropdown-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template is="dom-if" if="[[supportsAwayMode(stateObj)]]">
|
||||
<div class="container-away_mode">
|
||||
<div class="center horizontal layout single-row">
|
||||
<div class="flex">[[localize('ui.card.climate.away_mode')]]</div>
|
||||
<paper-toggle-button
|
||||
checked="[[awayToggleChecked]]"
|
||||
on-change="awayToggleChanged"
|
||||
>
|
||||
</paper-toggle-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template is="dom-if" if="[[supportsAuxHeat(stateObj)]]">
|
||||
<div class="container-aux_heat">
|
||||
<div class="center horizontal layout single-row">
|
||||
<div class="flex">[[localize('ui.card.climate.aux_heat')]]</div>
|
||||
<paper-toggle-button
|
||||
checked="[[auxToggleChecked]]"
|
||||
on-change="auxToggleChanged"
|
||||
>
|
||||
</paper-toggle-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {
|
||||
type: Object,
|
||||
},
|
||||
|
||||
stateObj: {
|
||||
type: Object,
|
||||
observer: "stateObjChanged",
|
||||
},
|
||||
|
||||
awayToggleChecked: Boolean,
|
||||
auxToggleChecked: Boolean,
|
||||
onToggleChecked: Boolean,
|
||||
|
||||
rtl: {
|
||||
type: String,
|
||||
value: "ltr",
|
||||
computed: "_computeRTLDirection(hass)",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
stateObjChanged(newVal, oldVal) {
|
||||
if (newVal) {
|
||||
this.setProperties({
|
||||
awayToggleChecked: newVal.attributes.away_mode === "on",
|
||||
auxToggleChecked: newVal.attributes.aux_heat === "on",
|
||||
onToggleChecked: newVal.state !== "off",
|
||||
});
|
||||
}
|
||||
|
||||
if (oldVal) {
|
||||
this._debouncer = Debouncer.debounce(
|
||||
this._debouncer,
|
||||
timeOut.after(500),
|
||||
() => {
|
||||
this.fire("iron-resize");
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
computeTemperatureStepSize(hass, stateObj) {
|
||||
if (stateObj.attributes.target_temp_step) {
|
||||
return stateObj.attributes.target_temp_step;
|
||||
}
|
||||
if (hass.config.unit_system.temperature.indexOf("F") !== -1) {
|
||||
return 1;
|
||||
}
|
||||
return 0.5;
|
||||
}
|
||||
|
||||
supportsTemperatureControls(stateObj) {
|
||||
return (
|
||||
this.supportsTemperature(stateObj) ||
|
||||
this.supportsTemperatureRange(stateObj)
|
||||
);
|
||||
}
|
||||
|
||||
supportsTemperature(stateObj) {
|
||||
return (
|
||||
supportsFeature(stateObj, 1) &&
|
||||
typeof stateObj.attributes.temperature === "number"
|
||||
);
|
||||
}
|
||||
|
||||
supportsTemperatureRange(stateObj) {
|
||||
return (
|
||||
supportsFeature(stateObj, 6) &&
|
||||
(typeof stateObj.attributes.target_temp_low === "number" ||
|
||||
typeof stateObj.attributes.target_temp_high === "number")
|
||||
);
|
||||
}
|
||||
|
||||
supportsHumidity(stateObj) {
|
||||
return supportsFeature(stateObj, 8);
|
||||
}
|
||||
|
||||
supportsFanMode(stateObj) {
|
||||
return supportsFeature(stateObj, 64);
|
||||
}
|
||||
|
||||
supportsOperationMode(stateObj) {
|
||||
return supportsFeature(stateObj, 128);
|
||||
}
|
||||
|
||||
supportsSwingMode(stateObj) {
|
||||
return supportsFeature(stateObj, 512);
|
||||
}
|
||||
|
||||
supportsAwayMode(stateObj) {
|
||||
return supportsFeature(stateObj, 1024);
|
||||
}
|
||||
|
||||
supportsAuxHeat(stateObj) {
|
||||
return supportsFeature(stateObj, 2048);
|
||||
}
|
||||
|
||||
supportsOn(stateObj) {
|
||||
return supportsFeature(stateObj, 4096);
|
||||
}
|
||||
|
||||
computeClassNames(stateObj) {
|
||||
const _featureClassNames = {
|
||||
1: "has-target_temperature",
|
||||
2: "has-target_temperature_high",
|
||||
4: "has-target_temperature_low",
|
||||
8: "has-target_humidity",
|
||||
16: "has-target_humidity_high",
|
||||
32: "has-target_humidity_low",
|
||||
64: "has-fan_mode",
|
||||
128: "has-operation_mode",
|
||||
256: "has-hold_mode",
|
||||
512: "has-swing_mode",
|
||||
1024: "has-away_mode",
|
||||
2048: "has-aux_heat",
|
||||
4096: "has-on",
|
||||
};
|
||||
|
||||
var classes = [
|
||||
attributeClassNames(stateObj, [
|
||||
"current_temperature",
|
||||
"current_humidity",
|
||||
]),
|
||||
featureClassNames(stateObj, _featureClassNames),
|
||||
];
|
||||
|
||||
classes.push("more-info-climate");
|
||||
|
||||
return classes.join(" ");
|
||||
}
|
||||
|
||||
targetTemperatureChanged(ev) {
|
||||
const temperature = ev.target.value;
|
||||
if (temperature === this.stateObj.attributes.temperature) return;
|
||||
this.callServiceHelper("set_temperature", { temperature: temperature });
|
||||
}
|
||||
|
||||
targetTemperatureLowChanged(ev) {
|
||||
const targetTempLow = ev.currentTarget.value;
|
||||
if (targetTempLow === this.stateObj.attributes.target_temp_low) return;
|
||||
this.callServiceHelper("set_temperature", {
|
||||
target_temp_low: targetTempLow,
|
||||
target_temp_high: this.stateObj.attributes.target_temp_high,
|
||||
});
|
||||
}
|
||||
|
||||
targetTemperatureHighChanged(ev) {
|
||||
const targetTempHigh = ev.currentTarget.value;
|
||||
if (targetTempHigh === this.stateObj.attributes.target_temp_high) return;
|
||||
this.callServiceHelper("set_temperature", {
|
||||
target_temp_low: this.stateObj.attributes.target_temp_low,
|
||||
target_temp_high: targetTempHigh,
|
||||
});
|
||||
}
|
||||
|
||||
targetHumiditySliderChanged(ev) {
|
||||
const humidity = ev.target.value;
|
||||
if (humidity === this.stateObj.attributes.humidity) return;
|
||||
this.callServiceHelper("set_humidity", { humidity: humidity });
|
||||
}
|
||||
|
||||
awayToggleChanged(ev) {
|
||||
const oldVal = this.stateObj.attributes.away_mode === "on";
|
||||
const newVal = ev.target.checked;
|
||||
if (oldVal === newVal) return;
|
||||
this.callServiceHelper("set_away_mode", { away_mode: newVal });
|
||||
}
|
||||
|
||||
auxToggleChanged(ev) {
|
||||
const oldVal = this.stateObj.attributes.aux_heat === "on";
|
||||
const newVal = ev.target.checked;
|
||||
if (oldVal === newVal) return;
|
||||
this.callServiceHelper("set_aux_heat", { aux_heat: newVal });
|
||||
}
|
||||
|
||||
onToggleChanged(ev) {
|
||||
const oldVal = this.stateObj.state !== "off";
|
||||
const newVal = ev.target.checked;
|
||||
if (oldVal === newVal) return;
|
||||
this.callServiceHelper(newVal ? "turn_on" : "turn_off", {});
|
||||
}
|
||||
|
||||
handleFanmodeChanged(ev) {
|
||||
const oldVal = this.stateObj.attributes.fan_mode;
|
||||
const newVal = ev.detail.value;
|
||||
if (!newVal || oldVal === newVal) return;
|
||||
this.callServiceHelper("set_fan_mode", { fan_mode: newVal });
|
||||
}
|
||||
|
||||
handleOperationmodeChanged(ev) {
|
||||
const oldVal = this.stateObj.attributes.operation_mode;
|
||||
const newVal = ev.detail.value;
|
||||
if (!newVal || oldVal === newVal) return;
|
||||
this.callServiceHelper("set_operation_mode", {
|
||||
operation_mode: newVal,
|
||||
});
|
||||
}
|
||||
|
||||
handleSwingmodeChanged(ev) {
|
||||
const oldVal = this.stateObj.attributes.swing_mode;
|
||||
const newVal = ev.detail.value;
|
||||
if (!newVal || oldVal === newVal) return;
|
||||
this.callServiceHelper("set_swing_mode", { swing_mode: newVal });
|
||||
}
|
||||
|
||||
callServiceHelper(service, data) {
|
||||
// We call stateChanged after a successful call to re-sync the inputs
|
||||
// with the state. It will be out of sync if our service call did not
|
||||
// result in the entity to be turned on. Since the state is not changing,
|
||||
// the resync is not called automatic.
|
||||
/* eslint-disable no-param-reassign */
|
||||
data.entity_id = this.stateObj.entity_id;
|
||||
/* eslint-enable no-param-reassign */
|
||||
this.hass.callService("climate", service, data).then(() => {
|
||||
this.stateObjChanged(this.stateObj);
|
||||
});
|
||||
}
|
||||
|
||||
_localizeOperationMode(localize, mode) {
|
||||
return localize(`state.climate.${mode}`) || mode;
|
||||
}
|
||||
|
||||
_localizeFanMode(localize, mode) {
|
||||
return localize(`state_attributes.climate.fan_mode.${mode}`) || mode;
|
||||
}
|
||||
|
||||
_computeRTLDirection(hass) {
|
||||
return computeRTLDirection(hass);
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("more-info-climate", MoreInfoClimate);
|
501
src/dialogs/more-info/controls/more-info-climate.ts
Normal file
501
src/dialogs/more-info/controls/more-info-climate.ts
Normal file
@ -0,0 +1,501 @@
|
||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
||||
import "@polymer/paper-item/paper-item";
|
||||
import "@polymer/paper-listbox/paper-listbox";
|
||||
import "@polymer/paper-toggle-button/paper-toggle-button";
|
||||
import {
|
||||
LitElement,
|
||||
html,
|
||||
TemplateResult,
|
||||
CSSResult,
|
||||
css,
|
||||
property,
|
||||
PropertyValues,
|
||||
} from "lit-element";
|
||||
|
||||
import "../../../components/ha-climate-control";
|
||||
import "../../../components/ha-paper-slider";
|
||||
import "../../../components/ha-paper-dropdown-menu";
|
||||
|
||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||
|
||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import {
|
||||
ClimateEntity,
|
||||
CLIMATE_SUPPORT_TARGET_TEMPERATURE,
|
||||
CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE,
|
||||
CLIMATE_SUPPORT_TARGET_HUMIDITY,
|
||||
CLIMATE_SUPPORT_FAN_MODE,
|
||||
CLIMATE_SUPPORT_SWING_MODE,
|
||||
CLIMATE_SUPPORT_AUX_HEAT,
|
||||
CLIMATE_SUPPORT_PRESET_MODE,
|
||||
} from "../../../data/climate";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { classMap } from "lit-html/directives/class-map";
|
||||
|
||||
class MoreInfoClimate extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
@property() public stateObj?: ClimateEntity;
|
||||
private _resizeDebounce?: number;
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this.stateObj) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const hass = this.hass;
|
||||
const stateObj = this.stateObj;
|
||||
|
||||
const supportTargetTemperature = supportsFeature(
|
||||
stateObj,
|
||||
CLIMATE_SUPPORT_TARGET_TEMPERATURE
|
||||
);
|
||||
const supportTargetTemperatureRange = supportsFeature(
|
||||
stateObj,
|
||||
CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE
|
||||
);
|
||||
const supportTargetHumidity = supportsFeature(
|
||||
stateObj,
|
||||
CLIMATE_SUPPORT_TARGET_HUMIDITY
|
||||
);
|
||||
const supportFanMode = supportsFeature(stateObj, CLIMATE_SUPPORT_FAN_MODE);
|
||||
const supportPresetMode = supportsFeature(
|
||||
stateObj,
|
||||
CLIMATE_SUPPORT_PRESET_MODE
|
||||
);
|
||||
const supportSwingMode = supportsFeature(
|
||||
stateObj,
|
||||
CLIMATE_SUPPORT_SWING_MODE
|
||||
);
|
||||
const supportAuxHeat = supportsFeature(stateObj, CLIMATE_SUPPORT_AUX_HEAT);
|
||||
|
||||
const temperatureStepSize =
|
||||
stateObj.attributes.target_temp_step ||
|
||||
hass.config.unit_system.temperature.indexOf("F") === -1
|
||||
? 0.5
|
||||
: 1;
|
||||
|
||||
const rtlDirection = computeRTLDirection(hass);
|
||||
|
||||
return html`
|
||||
<div
|
||||
class=${classMap({
|
||||
"has-current_temperature":
|
||||
"current_temperature" in stateObj.attributes,
|
||||
"has-current_humidity": "current_humidity" in stateObj.attributes,
|
||||
"has-target_temperature": supportTargetTemperature,
|
||||
"has-target_temperature_range": supportTargetTemperatureRange,
|
||||
"has-target_humidity": supportTargetHumidity,
|
||||
"has-fan_mode": supportFanMode,
|
||||
"has-swing_mode": supportSwingMode,
|
||||
"has-aux_heat": supportAuxHeat,
|
||||
"has-preset_mode": supportPresetMode,
|
||||
})}
|
||||
>
|
||||
<div class="container-temperature">
|
||||
<div class=${stateObj.state}>
|
||||
${supportTargetTemperature || supportTargetTemperatureRange
|
||||
? html`
|
||||
<div>
|
||||
${hass.localize("ui.card.climate.target_temperature")}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${stateObj.attributes.temperature
|
||||
? html`
|
||||
<ha-climate-control
|
||||
.value=${stateObj.attributes.temperature}
|
||||
.units=${hass.config.unit_system.temperature}
|
||||
.step=${temperatureStepSize}
|
||||
.min=${stateObj.attributes.min_temp}
|
||||
.max=${stateObj.attributes.max_temp}
|
||||
@change=${this._targetTemperatureChanged}
|
||||
></ha-climate-control>
|
||||
`
|
||||
: ""}
|
||||
${stateObj.attributes.target_temp_low ||
|
||||
stateObj.attributes.target_temp_high
|
||||
? html`
|
||||
<ha-climate-control
|
||||
.value=${stateObj.attributes.target_temp_low}
|
||||
.units=${hass.config.unit_system.temperature}
|
||||
.step=${temperatureStepSize}
|
||||
.min=${stateObj.attributes.min_temp}
|
||||
.max=${stateObj.attributes.target_temp_high}
|
||||
class="range-control-left"
|
||||
@change=${this._targetTemperatureLowChanged}
|
||||
></ha-climate-control>
|
||||
<ha-climate-control
|
||||
.value=${stateObj.attributes.target_temp_high}
|
||||
.units=${hass.config.unit_system.temperature}
|
||||
.step=${temperatureStepSize}
|
||||
.min=${stateObj.attributes.target_temp_low}
|
||||
.max=${stateObj.attributes.max_temp}
|
||||
class="range-control-right"
|
||||
@change=${this._targetTemperatureHighChanged}
|
||||
></ha-climate-control>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${supportTargetHumidity
|
||||
? html`
|
||||
<div class="container-humidity">
|
||||
<div>${hass.localize("ui.card.climate.target_humidity")}</div>
|
||||
<div class="single-row">
|
||||
<div class="target-humidity">
|
||||
${stateObj.attributes.humidity} %
|
||||
</div>
|
||||
<ha-paper-slider
|
||||
class="humidity"
|
||||
step="1"
|
||||
pin
|
||||
ignore-bar-touch
|
||||
dir=${rtlDirection}
|
||||
.min=${stateObj.attributes.min_humidity}
|
||||
.max=${stateObj.attributes.max_humidity}
|
||||
.secondaryProgress=${stateObj.attributes.max_humidity}
|
||||
.value=${stateObj.attributes.humidity}
|
||||
@change=${this._targetHumiditySliderChanged}
|
||||
>
|
||||
</ha-paper-slider>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
|
||||
<div class="container-hvac_modes">
|
||||
<div class="controls">
|
||||
<ha-paper-dropdown-menu
|
||||
label-float
|
||||
dynamic-align
|
||||
.label=${hass.localize("ui.card.climate.operation")}
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
attr-for-selected="item-name"
|
||||
.selected=${stateObj.state}
|
||||
@selected-changed=${this._handleOperationmodeChanged}
|
||||
>
|
||||
${stateObj.attributes.hvac_modes.map(
|
||||
(mode) => html`
|
||||
<paper-item item-name=${mode}>
|
||||
${hass.localize(`state.climate.${mode}`)}
|
||||
</paper-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</ha-paper-dropdown-menu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${supportPresetMode
|
||||
? html`
|
||||
<div class="container-preset_modes">
|
||||
<ha-paper-dropdown-menu
|
||||
label-float
|
||||
dynamic-align
|
||||
.label=${hass.localize("ui.card.climate.preset_mode")}
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
attr-for-selected="item-name"
|
||||
.selected=${stateObj.attributes.preset_mode}
|
||||
@selected-changed=${this._handlePresetmodeChanged}
|
||||
>
|
||||
<paper-item item-name="">
|
||||
${hass.localize(
|
||||
`state_attributes.climate.preset_mode.none`
|
||||
)}
|
||||
</paper-item>
|
||||
${stateObj.attributes.preset_modes!.map(
|
||||
(mode) => html`
|
||||
<paper-item item-name=${mode}>
|
||||
${hass.localize(
|
||||
`state_attributes.climate.preset_mode.${mode}`
|
||||
) || mode}
|
||||
</paper-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</ha-paper-dropdown-menu>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${supportFanMode
|
||||
? html`
|
||||
<div class="container-fan_list">
|
||||
<ha-paper-dropdown-menu
|
||||
label-float
|
||||
dynamic-align
|
||||
.label=${hass.localize("ui.card.climate.fan_mode")}
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
attr-for-selected="item-name"
|
||||
.selected=${stateObj.attributes.fan_mode}
|
||||
@selected-changed=${this._handleFanmodeChanged}
|
||||
>
|
||||
${stateObj.attributes.fan_modes!.map(
|
||||
(mode) => html`
|
||||
<paper-item item-name=${mode}>
|
||||
${hass.localize(
|
||||
`state_attributes.climate.fan_mode.${mode}`
|
||||
) || mode}
|
||||
</paper-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</ha-paper-dropdown-menu>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${supportSwingMode
|
||||
? html`
|
||||
<div class="container-swing_list">
|
||||
<ha-paper-dropdown-menu
|
||||
label-float
|
||||
dynamic-align
|
||||
.label=${hass.localize("ui.card.climate.swing_mode")}
|
||||
>
|
||||
<paper-listbox
|
||||
slot="dropdown-content"
|
||||
attr-for-selected="item-name"
|
||||
.selected=${stateObj.attributes.swing_mode}
|
||||
@selected-changed=${this._handleSwingmodeChanged}
|
||||
>
|
||||
${stateObj.attributes.swing_modes!.map(
|
||||
(mode) => html`
|
||||
<paper-item item-name=${mode}>${mode}</paper-item>
|
||||
`
|
||||
)}
|
||||
</paper-listbox>
|
||||
</ha-paper-dropdown-menu>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${supportAuxHeat
|
||||
? html`
|
||||
<div class="container-aux_heat">
|
||||
<div class="center horizontal layout single-row">
|
||||
<div class="flex">
|
||||
${hass.localize("ui.card.climate.aux_heat")}
|
||||
</div>
|
||||
<paper-toggle-button
|
||||
.checked=${stateObj.attributes.aux_heat === "on"}
|
||||
@change=${this._auxToggleChanged}
|
||||
></paper-toggle-button>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
super.updated(changedProps);
|
||||
if (!changedProps.has("stateObj") || !this.stateObj) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._resizeDebounce) {
|
||||
clearTimeout(this._resizeDebounce);
|
||||
}
|
||||
this._resizeDebounce = window.setTimeout(() => {
|
||||
fireEvent(this, "iron-resize");
|
||||
this._resizeDebounce = undefined;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
private _targetTemperatureChanged(ev) {
|
||||
const newVal = ev.target.value;
|
||||
this._callServiceHelper(
|
||||
this.stateObj!.attributes.temperature,
|
||||
newVal,
|
||||
"set_temperature",
|
||||
{ temperature: newVal }
|
||||
);
|
||||
}
|
||||
|
||||
private _targetTemperatureLowChanged(ev) {
|
||||
const newVal = ev.currentTarget.value;
|
||||
this._callServiceHelper(
|
||||
this.stateObj!.attributes.target_temp_low,
|
||||
newVal,
|
||||
"set_temperature",
|
||||
{
|
||||
target_temp_low: newVal,
|
||||
target_temp_high: this.stateObj!.attributes.target_temp_high,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _targetTemperatureHighChanged(ev) {
|
||||
const newVal = ev.currentTarget.value;
|
||||
this._callServiceHelper(
|
||||
this.stateObj!.attributes.target_temp_high,
|
||||
newVal,
|
||||
"set_temperature",
|
||||
{
|
||||
target_temp_low: this.stateObj!.attributes.target_temp_low,
|
||||
target_temp_high: newVal,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _targetHumiditySliderChanged(ev) {
|
||||
const newVal = ev.target.value;
|
||||
this._callServiceHelper(
|
||||
this.stateObj!.attributes.humidity,
|
||||
newVal,
|
||||
"set_humidity",
|
||||
{ humidity: newVal }
|
||||
);
|
||||
}
|
||||
|
||||
private _auxToggleChanged(ev) {
|
||||
const newVal = ev.target.checked;
|
||||
this._callServiceHelper(
|
||||
this.stateObj!.attributes.aux_heat === "on",
|
||||
newVal,
|
||||
"set_aux_heat",
|
||||
{ aux_heat: newVal }
|
||||
);
|
||||
}
|
||||
|
||||
private _handleFanmodeChanged(ev) {
|
||||
const newVal = ev.detail.value;
|
||||
this._callServiceHelper(
|
||||
this.stateObj!.attributes.fan_mode,
|
||||
newVal,
|
||||
"set_fan_mode",
|
||||
{ fan_mode: newVal }
|
||||
);
|
||||
}
|
||||
|
||||
private _handleOperationmodeChanged(ev) {
|
||||
const newVal = ev.detail.value;
|
||||
this._callServiceHelper(this.stateObj!.state, newVal, "set_hvac_mode", {
|
||||
hvac_mode: newVal,
|
||||
});
|
||||
}
|
||||
|
||||
private _handleSwingmodeChanged(ev) {
|
||||
const newVal = ev.detail.value;
|
||||
this._callServiceHelper(
|
||||
this.stateObj!.attributes.swing_mode,
|
||||
newVal,
|
||||
"set_swing_mode",
|
||||
{ swing_mode: newVal }
|
||||
);
|
||||
}
|
||||
|
||||
private _handlePresetmodeChanged(ev) {
|
||||
const newVal = ev.detail.value || null;
|
||||
this._callServiceHelper(
|
||||
this.stateObj!.attributes.preset_mode,
|
||||
newVal,
|
||||
"set_preset_mode",
|
||||
{ preset_mode: newVal }
|
||||
);
|
||||
}
|
||||
|
||||
private async _callServiceHelper(
|
||||
oldVal: unknown,
|
||||
newVal: unknown,
|
||||
service: string,
|
||||
data: {
|
||||
entity_id?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
) {
|
||||
if (oldVal === newVal) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.entity_id = this.stateObj!.entity_id;
|
||||
const curState = this.stateObj;
|
||||
|
||||
await this.hass.callService("climate", service, data);
|
||||
|
||||
// We reset stateObj to re-sync the inputs with the state. It will be out
|
||||
// of sync if our service call did not result in the entity to be turned
|
||||
// on. Since the state is not changing, the resync is not called automatic.
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
// No need to resync if we received a new state.
|
||||
if (this.stateObj !== curState) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.stateObj = undefined;
|
||||
await this.updateComplete;
|
||||
// Only restore if not set yet by a state change
|
||||
if (this.stateObj === undefined) {
|
||||
this.stateObj = curState;
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResult {
|
||||
return css`
|
||||
:host {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
.container-hvac_modes iron-icon,
|
||||
.container-fan_list iron-icon,
|
||||
.container-swing_list iron-icon {
|
||||
margin: 22px 16px 0 0;
|
||||
}
|
||||
|
||||
ha-paper-dropdown-menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
paper-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ha-paper-slider {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container-humidity .single-row {
|
||||
display: flex;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.target-humidity {
|
||||
width: 90px;
|
||||
font-size: 200%;
|
||||
margin: auto;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
ha-climate-control.range-control-left,
|
||||
ha-climate-control.range-control-right {
|
||||
float: left;
|
||||
width: 46%;
|
||||
}
|
||||
ha-climate-control.range-control-left {
|
||||
margin-right: 4%;
|
||||
}
|
||||
ha-climate-control.range-control-right {
|
||||
margin-left: 4%;
|
||||
}
|
||||
|
||||
.humidity {
|
||||
--paper-slider-active-color: var(--paper-blue-400);
|
||||
--paper-slider-secondary-color: var(--paper-blue-400);
|
||||
}
|
||||
|
||||
.single-row {
|
||||
padding: 8px 0;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("more-info-climate", MoreInfoClimate);
|
@ -619,26 +619,6 @@ export const demoServices: HassServices = {
|
||||
},
|
||||
},
|
||||
climate: {
|
||||
set_away_mode: {
|
||||
description: "Turn away mode on/off for climate device.",
|
||||
fields: {
|
||||
entity_id: {
|
||||
description: "Name(s) of entities to change.",
|
||||
example: "climate.kitchen",
|
||||
},
|
||||
away_mode: { description: "New value of away mode.", example: "true" },
|
||||
},
|
||||
},
|
||||
set_hold_mode: {
|
||||
description: "Turn hold mode for climate device.",
|
||||
fields: {
|
||||
entity_id: {
|
||||
description: "Name(s) of entities to change.",
|
||||
example: "climate.kitchen",
|
||||
},
|
||||
hold_mode: { description: "New value of hold mode", example: "away" },
|
||||
},
|
||||
},
|
||||
set_aux_heat: {
|
||||
description: "Turn auxiliary heater on/off for climate device.",
|
||||
fields: {
|
||||
@ -701,16 +681,16 @@ export const demoServices: HassServices = {
|
||||
fan_mode: { description: "New value of fan mode.", example: "On Low" },
|
||||
},
|
||||
},
|
||||
set_operation_mode: {
|
||||
set_hvac_mode: {
|
||||
description: "Set operation mode for climate device.",
|
||||
fields: {
|
||||
entity_id: {
|
||||
description: "Name(s) of entities to change.",
|
||||
example: "climate.nest",
|
||||
},
|
||||
operation_mode: {
|
||||
hvac_mode: {
|
||||
description: "New value of operation mode.",
|
||||
example: "Heat",
|
||||
example: "heat",
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -724,24 +704,6 @@ export const demoServices: HassServices = {
|
||||
swing_mode: { description: "New value of swing mode.", example: "" },
|
||||
},
|
||||
},
|
||||
turn_off: {
|
||||
description: "Turn climate device off.",
|
||||
fields: {
|
||||
entity_id: {
|
||||
description: "Name(s) of entities to change.",
|
||||
example: "climate.kitchen",
|
||||
},
|
||||
},
|
||||
},
|
||||
turn_on: {
|
||||
description: "Turn climate device on.",
|
||||
fields: {
|
||||
entity_id: {
|
||||
description: "Name(s) of entities to change.",
|
||||
example: "climate.kitchen",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
image_processing: {
|
||||
scan: {
|
||||
|
@ -209,11 +209,24 @@ class ClimateEntity extends Entity {
|
||||
return;
|
||||
}
|
||||
|
||||
if (service === "set_operation_mode") {
|
||||
this.update(
|
||||
data.operation_mode === "heat" ? "heat" : data.operation_mode,
|
||||
{ ...this.attributes, operation_mode: data.operation_mode }
|
||||
);
|
||||
if (service === "set_hvac_mode") {
|
||||
this.update(data.hvac_mode, this.attributes);
|
||||
} else if (
|
||||
[
|
||||
"set_temperature",
|
||||
"set_humidity",
|
||||
"set_hvac_mode",
|
||||
"set_fan_mode",
|
||||
"set_preset_mode",
|
||||
"set_swing_mode",
|
||||
"set_aux_heat",
|
||||
].includes(service)
|
||||
) {
|
||||
const { entity_id, ...toSet } = data;
|
||||
this.update(this.state, {
|
||||
...this.attributes,
|
||||
...toSet,
|
||||
});
|
||||
} else {
|
||||
super.handleService(domain, service, data);
|
||||
}
|
||||
|
@ -58,6 +58,8 @@ export class HuiErrorCard extends LitElement implements LovelaceCard {
|
||||
color: white;
|
||||
padding: 8px;
|
||||
font-weight: 500;
|
||||
user-select: text;
|
||||
cursor: default;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@ -17,12 +17,13 @@ import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
|
||||
import computeStateName from "../../../common/entity/compute_state_name";
|
||||
|
||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
|
||||
import { HomeAssistant, ClimateEntity } from "../../../types";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { LovelaceCard, LovelaceCardEditor } from "../types";
|
||||
import { loadRoundslider } from "../../../resources/jquery.roundslider.ondemand";
|
||||
import { UNIT_F } from "../../../common/const";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { ThermostatCardConfig } from "./types";
|
||||
import { ClimateEntity, HvacMode } from "../../../data/climate";
|
||||
|
||||
const thermostatConfig = {
|
||||
radius: 150,
|
||||
@ -35,16 +36,14 @@ const thermostatConfig = {
|
||||
animation: false,
|
||||
};
|
||||
|
||||
const modeIcons = {
|
||||
const modeIcons: { [mode in HvacMode]: string } = {
|
||||
auto: "hass:autorenew",
|
||||
manual: "hass:cursor-pointer",
|
||||
heat_cool: "hass:autorenew",
|
||||
heat: "hass:fire",
|
||||
cool: "hass:snowflake",
|
||||
off: "hass:power",
|
||||
fan_only: "hass:fan",
|
||||
eco: "hass:leaf",
|
||||
dry: "hass:water-percent",
|
||||
idle: "hass:power-sleep",
|
||||
};
|
||||
|
||||
@customElement("hui-thermostat-card")
|
||||
@ -109,9 +108,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
`;
|
||||
}
|
||||
|
||||
const mode = modeIcons[stateObj.attributes.operation_mode || ""]
|
||||
? stateObj.attributes.operation_mode!
|
||||
: "unknown-mode";
|
||||
const mode = stateObj.state in modeIcons ? stateObj.state : "unknown-mode";
|
||||
return html`
|
||||
${this.renderStyle()}
|
||||
<ha-card
|
||||
@ -146,11 +143,23 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
</div>
|
||||
<div class="climate-info">
|
||||
<div id="set-temperature"></div>
|
||||
<div class="current-mode">${this.hass!.localize(
|
||||
`state.climate.${stateObj.state}`
|
||||
)}</div>
|
||||
<div class="current-mode">
|
||||
${this.hass!.localize(`state.climate.${stateObj.state}`)}
|
||||
${
|
||||
stateObj.attributes.preset_mode
|
||||
? html`
|
||||
-
|
||||
${this.hass!.localize(
|
||||
`state_attributes.climate.preset_mode.${
|
||||
stateObj.attributes.preset_mode
|
||||
}`
|
||||
) || stateObj.attributes.preset_mode}
|
||||
`
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
<div class="modes">
|
||||
${(stateObj.attributes.operation_list || []).map((modeItem) =>
|
||||
${stateObj.attributes.hvac_modes.map((modeItem) =>
|
||||
this._renderIcon(modeItem, mode)
|
||||
)}
|
||||
</div>
|
||||
@ -205,7 +214,7 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
private get _stepSize(): number {
|
||||
const stateObj = this.hass!.states[this._config!.entity];
|
||||
const stateObj = this.hass!.states[this._config!.entity] as ClimateEntity;
|
||||
|
||||
if (stateObj.attributes.target_temp_step) {
|
||||
return stateObj.attributes.target_temp_step;
|
||||
@ -348,9 +357,9 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
}
|
||||
|
||||
private _handleModeClick(e: MouseEvent): void {
|
||||
this.hass!.callService("climate", "set_operation_mode", {
|
||||
this.hass!.callService("climate", "set_hvac_mode", {
|
||||
entity_id: this._config!.entity,
|
||||
operation_mode: (e.currentTarget as any).mode,
|
||||
hvac_mode: (e.currentTarget as any).mode,
|
||||
});
|
||||
}
|
||||
|
||||
@ -394,7 +403,8 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.auto {
|
||||
.auto,
|
||||
.heat_cool {
|
||||
--mode-color: var(--auto-color);
|
||||
}
|
||||
.cool {
|
||||
|
@ -11,12 +11,13 @@ export function hasConfigOrEntityChanged(
|
||||
}
|
||||
|
||||
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||
if (oldHass) {
|
||||
return (
|
||||
oldHass.states[element._config!.entity] !==
|
||||
element.hass!.states[element._config!.entity]
|
||||
);
|
||||
if (!oldHass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return (
|
||||
oldHass.states[element._config!.entity] !==
|
||||
element.hass!.states[element._config!.entity] ||
|
||||
oldHass.localize !== element.hass.localize
|
||||
);
|
||||
}
|
||||
|
@ -40,6 +40,16 @@ class HuiTimerEntityRow extends LitElement {
|
||||
this._clearInterval();
|
||||
}
|
||||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
if (this._config && this._config.entity) {
|
||||
const stateObj = this.hass!.states[this._config!.entity];
|
||||
if (stateObj) {
|
||||
this._startInterval(stateObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult | void {
|
||||
if (!this._config || !this.hass) {
|
||||
return html``;
|
||||
|
@ -176,20 +176,12 @@
|
||||
},
|
||||
"climate": {
|
||||
"off": "[%key:state::default::off%]",
|
||||
"on": "[%key:state::default::on%]",
|
||||
"heat": "Heat",
|
||||
"cool": "Cool",
|
||||
"idle": "Idle",
|
||||
"heat_cool": "Auto",
|
||||
"auto": "Auto",
|
||||
"dry": "Dry",
|
||||
"fan_only": "Fan only",
|
||||
"eco": "Eco",
|
||||
"electric": "Electric",
|
||||
"performance": "Performance",
|
||||
"high_demand": "High demand",
|
||||
"heat_pump": "Heat pump",
|
||||
"gas": "Gas",
|
||||
"manual": "Manual"
|
||||
"fan_only": "Fan only"
|
||||
},
|
||||
"configurator": {
|
||||
"configure": "Configure",
|
||||
@ -326,6 +318,16 @@
|
||||
"off": "[%key:state::default::off%]",
|
||||
"on": "[%key:state::default::on%]",
|
||||
"auto": "[%key:state::climate::auto%]"
|
||||
},
|
||||
"preset_mode": {
|
||||
"none": "None",
|
||||
"eco": "Eco",
|
||||
"away": "Away",
|
||||
"boost": "Boost",
|
||||
"comfort": "Comfort",
|
||||
"home": "Home",
|
||||
"sleep": "Sleep",
|
||||
"activity": "Activity"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -393,6 +395,7 @@
|
||||
"operation": "Operation",
|
||||
"fan_mode": "Fan mode",
|
||||
"swing_mode": "Swing mode",
|
||||
"preset_mode": "Preset",
|
||||
"away_mode": "Away mode",
|
||||
"aux_heat": "Aux heat"
|
||||
},
|
||||
|
24
src/types.ts
24
src/types.ts
@ -160,30 +160,6 @@ export interface HomeAssistant {
|
||||
callWS: <T>(msg: MessageBase) => Promise<T>;
|
||||
}
|
||||
|
||||
export type ClimateEntity = HassEntityBase & {
|
||||
attributes: HassEntityAttributeBase & {
|
||||
current_temperature: number;
|
||||
min_temp: number;
|
||||
max_temp: number;
|
||||
temperature: number;
|
||||
target_temp_step?: number;
|
||||
target_temp_high?: number;
|
||||
target_temp_low?: number;
|
||||
target_humidity?: number;
|
||||
target_humidity_low?: number;
|
||||
target_humidity_high?: number;
|
||||
fan_mode?: string;
|
||||
fan_list?: string[];
|
||||
operation_mode?: string;
|
||||
operation_list?: string[];
|
||||
hold_mode?: string;
|
||||
swing_mode?: string;
|
||||
swing_list?: string[];
|
||||
away_mode?: "on" | "off";
|
||||
aux_heat?: "on" | "off";
|
||||
};
|
||||
};
|
||||
|
||||
export type LightEntity = HassEntityBase & {
|
||||
attributes: HassEntityAttributeBase & {
|
||||
min_mireds: number;
|
||||
|
@ -357,6 +357,7 @@
|
||||
"stop": "Zastavit"
|
||||
},
|
||||
"core_config": {
|
||||
"edit_requires_storage": "Editori pois käytöstä, koska asetukset on määritelty configuration.yaml:ssä",
|
||||
"location_name": "Název instalace Home Assistant",
|
||||
"latitude": "Zeměpisná šířka",
|
||||
"longitude": "Zeměpisná délka",
|
||||
@ -996,6 +997,8 @@
|
||||
"labels": {
|
||||
"lights": "Světla",
|
||||
"information": "Informace",
|
||||
"morning_commute": "Työmatka aamulla",
|
||||
"commute_home": "Matka kotiin",
|
||||
"entertainment": "Zábava",
|
||||
"activity": "Aktivita",
|
||||
"hdmi_input": "Vstup HDMI",
|
||||
@ -1006,6 +1009,7 @@
|
||||
"air": "Vzduch"
|
||||
},
|
||||
"unit": {
|
||||
"watching": "tarkkaillaan",
|
||||
"minutes_abbr": "min"
|
||||
}
|
||||
}
|
||||
|
@ -387,7 +387,7 @@
|
||||
"description": "Luo ja muokkaa automaatioita",
|
||||
"picker": {
|
||||
"header": "Automaatioeditori",
|
||||
"introduction": "Automaatioeditorissa voit luoda ja muokata automaatioita. Kannattaa lukea [ohjeet](https:\/\/home-assistant.io\/docs\/automation\/editor\/) (englanniksi), jotta osaat varmasti kirjoittaa automaatiot oikein.",
|
||||
"introduction": "Automaatioeditorissa voit luoda ja muokata automaatioita. Kannattaa lukea ohjeet, jotta osaat varmasti kirjoittaa automaatiot oikein.",
|
||||
"pick_automation": "Valitse automaatio, jota haluat muokata",
|
||||
"no_automations": "Ei muokattavia automaatioita",
|
||||
"add_automation": "Lisää automaatio",
|
||||
@ -561,7 +561,7 @@
|
||||
},
|
||||
"learn_more": "Lisätietoja toiminnoista"
|
||||
},
|
||||
"load_error_not_editable": "Vain automaatiot tiedostossa automations.yaml ovat muokattavissa",
|
||||
"load_error_not_editable": "Vain automaatiot tiedostossa automations.yaml ovat muokattavissa.",
|
||||
"load_error_unknown": "Virhe ladatessa automaatiota ( {err_no} )"
|
||||
}
|
||||
},
|
||||
@ -653,7 +653,7 @@
|
||||
"description": "Yleiskatsaus kaikki kotisi alueista.",
|
||||
"picker": {
|
||||
"header": "Aluekisteri",
|
||||
"introduction": "Alueita käytetään laitteiden järjestämiseen. Näitä tietoja käytetään Kotiavustajassa käyttöliittymän ja käyttöoikeuksien järjestämiseen sekä integroinnin muihin järjestelmiin.",
|
||||
"introduction": "Alueita käytetään laitteiden järjestämiseen. Näitä tietoja käytetään Home Assistantissa käyttöliittymän ja käyttöoikeuksien järjestämiseen sekä integroinnin muihin järjestelmiin.",
|
||||
"introduction2": "Voit sijoittaa laitteita alueelle siirtymällä alla olevan linkin avulla integraatiot-sivulle ja sitten napauttamalla määritettyyn integraatioon, jotta pääset laitteet -kortteihin.",
|
||||
"integrations_page": "Integraatiot",
|
||||
"no_areas": "Et ole vielä luonut alueita!",
|
||||
@ -674,8 +674,8 @@
|
||||
"picker": {
|
||||
"header": "Olemusrekisteri",
|
||||
"unavailable": "(ei saatavilla)",
|
||||
"introduction": "Kotiavustaja pitää rekisteriä jokaisesta havaitetusta olemuksesta, joka voidaan yksilöidä. Kullekin näille yksiköille määritetään olemus-ID, varattu juuri tälle yksikölle.",
|
||||
"introduction2": "Yksikkörekisterin avulla voit ohittaa nimeä, muuttaa yksikön tunnusta tai poistaa merkinnän Kotiavustajasta. Huomaa, että rekisterimerkinnän poistaminen ei poista yksikköä sinäänsä. Sitä voit seuraamalla alla olevaa linkkiä ja poistamalla sitä integrointisivulta.",
|
||||
"introduction": "Home Assistant pitää rekisteriä jokaisesta havaitetusta olemuksesta, joka voidaan yksilöidä. Kullekin näille yksiköille määritetään olemus-ID, varattu juuri tälle yksikölle.",
|
||||
"introduction2": "Yksikkörekisterin avulla voit ohittaa nimeä, muuttaa yksikön tunnusta tai poistaa merkinnän Home Assistantista. Huomaa, että rekisterimerkinnän poistaminen ei poista yksikköä sinäänsä. Sitä voit seuraamalla alla olevaa linkkiä ja poistamalla sitä integrointisivulta.",
|
||||
"integrations_page": "Integraatiot"
|
||||
},
|
||||
"editor": {
|
||||
@ -729,7 +729,7 @@
|
||||
},
|
||||
"long_lived_access_tokens": {
|
||||
"header": "Pitkäaikaiset käyttötunnussanomat",
|
||||
"description": "Luo pitkäikäisiä käyttöoikeustunnuksia, jotta komentosarjasi voivat vuorovaikutttaa Kotiavustajan kanssa. Jokainen tunnus on voimassa 10 vuotta luomisesta. Seuraavat pitkäikäiset käyttöoikeustunnukset ovat tällä hetkellä käytössä.",
|
||||
"description": "Luo pitkäikäisiä käyttöoikeustunnuksia, jotta komentosarjasi voivat vuorovaikutttaa Home Assistantin kanssa. Jokainen tunnus on voimassa 10 vuotta luomisesta. Seuraavat pitkäikäiset käyttöoikeustunnukset ovat tällä hetkellä käytössä.",
|
||||
"learn_auth_requests": "Opi tekemään tunnistautuneita kutsuja.",
|
||||
"created_at": "Luotu {date}",
|
||||
"confirm_delete": "Haluatko varmasti poistaa {name} käyttöoikeustunnuksen?",
|
||||
@ -769,7 +769,7 @@
|
||||
},
|
||||
"page-authorize": {
|
||||
"initializing": "Alustetaan",
|
||||
"authorizing_client": "Olet antamassa pääsyn {clientId} Home Assistant ympäristöösi.",
|
||||
"authorizing_client": "Olet antamassa pääsyn {clientId} Home Assistant -ympäristöösi.",
|
||||
"logging_in_with": "Kirjaudutaan sisään **{authProviderName}**.",
|
||||
"pick_auth_provider": "Tai kirjaudu sisään joillakin seuraavista",
|
||||
"abort_intro": "Kirjautuminen on keskeytetty",
|
||||
@ -881,7 +881,7 @@
|
||||
}
|
||||
},
|
||||
"integration": {
|
||||
"intro": "Laitteet ja palvelut ovat edustettuna Kotiavustajassa integraatioina. Voit määrittää ne nyt tai tehdä sitä myöhemmin kokoonpanonäytöstä.",
|
||||
"intro": "Laitteet ja palvelut ovat edustettuna Home Assistantissa integraatioina. Voit määrittää ne nyt tai tehdä sitä myöhemmin kokoonpanonäytöstä.",
|
||||
"more_integrations": "Lisää",
|
||||
"finish": "Valmis"
|
||||
},
|
||||
@ -967,7 +967,7 @@
|
||||
"entity_non_numeric": "Yksikkö ei ole numeerinen: {entity}"
|
||||
},
|
||||
"changed_toast": {
|
||||
"message": "Lovelace-asetukset päivitettiin, haluatko päivittää näkymää?",
|
||||
"message": "Lovelace-asetukset päivitettiin, haluatko päivittää näkymän?",
|
||||
"refresh": "Päivitä"
|
||||
},
|
||||
"reload_lovelace": "Lataa Lovelace uudelleen"
|
||||
@ -977,8 +977,8 @@
|
||||
"demo": {
|
||||
"demo_by": "kirjoittanut {name}",
|
||||
"next_demo": "Seuraava demo",
|
||||
"introduction": "Tervetuloa kotiin! Olet päätynyt Kotiavustaja-demoon, missä esittelemme yhteisömme parhaat käyttöliittymät.",
|
||||
"learn_more": "Opi enemmän Kotiavustajasta"
|
||||
"introduction": "Tervetuloa kotiin! Olet päätynyt Home Assistant -demoon, missä esittelemme yhteisömme parhaat käyttöliittymät.",
|
||||
"learn_more": "Opi enemmän Home Assistantista"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
@ -1006,7 +1006,7 @@
|
||||
"volume": "Äänenvoimakkuus",
|
||||
"total_tv_time": "TV-aikaa yhteensä",
|
||||
"turn_tv_off": "Sammuta televisio",
|
||||
"air": "Air"
|
||||
"air": "Ilmastointi"
|
||||
},
|
||||
"unit": {
|
||||
"watching": "katsomassa",
|
||||
|
@ -113,8 +113,8 @@
|
||||
"on": "Quente"
|
||||
},
|
||||
"window": {
|
||||
"off": "Fechada",
|
||||
"on": "Aberta"
|
||||
"off": "Fechado",
|
||||
"on": "Aberto"
|
||||
},
|
||||
"lock": {
|
||||
"off": "Bloqueado",
|
||||
@ -499,7 +499,7 @@
|
||||
"label": "Estado numérico",
|
||||
"above": "Acima",
|
||||
"below": "Abaixo",
|
||||
"value_template": "Valor de exemplo (opcional)"
|
||||
"value_template": "Valor do modelo (opcional)"
|
||||
},
|
||||
"sun": {
|
||||
"label": "Sol",
|
||||
@ -512,7 +512,7 @@
|
||||
},
|
||||
"template": {
|
||||
"label": "Modelo",
|
||||
"value_template": "Valor de exemplo"
|
||||
"value_template": "Valor do modelo"
|
||||
},
|
||||
"time": {
|
||||
"label": "Tempo",
|
||||
@ -619,7 +619,8 @@
|
||||
"firmware": "Firmware: {version}",
|
||||
"device_unavailable": "dispositivo indisponível",
|
||||
"entity_unavailable": "entidade indisponível",
|
||||
"no_area": "Sem área"
|
||||
"no_area": "Sem área",
|
||||
"hub": "Conectado via"
|
||||
},
|
||||
"config_flow": {
|
||||
"external_step": {
|
||||
@ -970,6 +971,49 @@
|
||||
"refresh": "Atualizar"
|
||||
},
|
||||
"reload_lovelace": "Recarregar Lovelace"
|
||||
},
|
||||
"page-demo": {
|
||||
"cards": {
|
||||
"demo": {
|
||||
"demo_by": "por {name}",
|
||||
"next_demo": "Próxima demonstração",
|
||||
"introduction": "Bem-vindo! Você chegou no demo do Home Assistant onde mostramos as melhores UIs criados por nossa comunidade.",
|
||||
"learn_more": "Saiba mais sobre o Home Assistant"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"arsaboo": {
|
||||
"names": {
|
||||
"upstairs": "Andar de cima",
|
||||
"family_room": "Quarto Familiar",
|
||||
"kitchen": "Cozinha",
|
||||
"patio": "Pátio",
|
||||
"hallway": "Corredor",
|
||||
"master_bedroom": "Quarto principal",
|
||||
"left": "Esquerda",
|
||||
"right": "Direita",
|
||||
"mirror": "Espelho"
|
||||
},
|
||||
"labels": {
|
||||
"lights": "Luzes",
|
||||
"information": "Informação",
|
||||
"morning_commute": "Comutação da manhã",
|
||||
"commute_home": "Comutar para casa",
|
||||
"entertainment": "Entretenimento",
|
||||
"activity": "Atividade",
|
||||
"hdmi_input": "Entrada HDMI",
|
||||
"hdmi_switcher": "Comutador HDMI",
|
||||
"volume": "Volume",
|
||||
"total_tv_time": "Tempo total de TV",
|
||||
"turn_tv_off": "Desligue a televisão",
|
||||
"air": "Ar"
|
||||
},
|
||||
"unit": {
|
||||
"watching": "assistindo",
|
||||
"minutes_abbr": "min"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sidebar": {
|
||||
|
@ -1006,7 +1006,7 @@
|
||||
"volume": "Volym",
|
||||
"total_tv_time": "TV-tid totalt",
|
||||
"turn_tv_off": "Stäng av TV",
|
||||
"air": "Air"
|
||||
"air": "Fläkt"
|
||||
},
|
||||
"unit": {
|
||||
"watching": "ser på",
|
||||
|
Loading…
x
Reference in New Issue
Block a user