Merge dev and pin action versions

This commit is contained in:
Steve Repsher 2022-12-27 20:34:31 +00:00
commit 555c43caeb
No known key found for this signature in database
GPG Key ID: 776C4F2DACF6131B
38 changed files with 511 additions and 325 deletions

View File

@ -22,12 +22,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
with:
ref: dev
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
@ -60,12 +60,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
with:
ref: master
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn

View File

@ -32,13 +32,13 @@ jobs:
sha: ${{ steps.get-sha.outputs.sha }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
with:
# Checkout PR head instead of merge commit
# Use ref, not SHA, so reruns get the dedupe commit
ref: ${{ github.event.pull_request.head.ref }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
@ -81,11 +81,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
with:
ref: ${{ needs.dedupe.outputs.sha }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
@ -109,11 +109,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
with:
ref: ${{ needs.dedupe.outputs.sha }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
@ -131,11 +131,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
with:
ref: ${{ needs.dedupe.outputs.sha }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
@ -153,11 +153,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
with:
ref: ${{ needs.dedupe.outputs.sha }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn

View File

@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.

View File

@ -23,12 +23,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
with:
ref: dev
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn
@ -61,12 +61,12 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
with:
ref: master
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn

View File

@ -17,10 +17,10 @@ jobs:
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn

View File

@ -22,10 +22,10 @@ jobs:
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
steps:
- name: Check out files from GitHub
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn

View File

@ -21,7 +21,7 @@ jobs:
contents: write
steps:
- name: Checkout the repository
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v4
@ -29,7 +29,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn

View File

@ -24,7 +24,7 @@ jobs:
contents: write # Required to upload release assets
steps:
- name: Checkout the repository
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master
@ -35,7 +35,7 @@ jobs:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
uses: actions/setup-node@v3.5.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: yarn

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 90 days stale policy
uses: actions/stale@v6.0.1
uses: actions/stale@v7.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 90

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v3
uses: actions/checkout@v3.2.0
- name: Upload Translations
run: |

View File

@ -106,6 +106,7 @@
"core-js": "^3.15.2",
"cropperjs": "^1.5.12",
"date-fns": "^2.23.0",
"date-fns-tz": "^1.3.7",
"deep-clone-simple": "^1.1.1",
"deep-freeze": "^0.0.1",
"fuse.js": "^6.0.0",

View File

@ -0,0 +1,52 @@
import { HassEntity } from "home-assistant-js-websocket";
import { EntityRegistryEntry } from "../../data/entity_registry";
import { HomeAssistant } from "../../types";
import { LocalizeFunc } from "../translations/localize";
import { computeDomain } from "./compute_domain";
export const computeAttributeValueDisplay = (
localize: LocalizeFunc,
stateObj: HassEntity,
entities: HomeAssistant["entities"],
attribute: string,
value?: any
): string => {
const entityId = stateObj.entity_id;
const attributeValue =
value !== undefined ? value : stateObj.attributes[attribute];
const domain = computeDomain(entityId);
const entity = entities[entityId] as EntityRegistryEntry | undefined;
const translationKey = entity?.translation_key;
return (
(translationKey &&
localize(
`component.${entity.platform}.entity.${domain}.${translationKey}.state_attributes.${attribute}.state.${attributeValue}`
)) ||
localize(
`component.${domain}.state_attributes._.${attribute}.state.${attributeValue}`
) ||
attributeValue
);
};
export const computeAttributeNameDisplay = (
localize: LocalizeFunc,
stateObj: HassEntity,
entities: HomeAssistant["entities"],
attribute: string
): string => {
const entityId = stateObj.entity_id;
const domain = computeDomain(entityId);
const entity = entities[entityId] as EntityRegistryEntry | undefined;
const translationKey = entity?.translation_key;
return (
(translationKey &&
localize(
`component.${entity.platform}.entity.${domain}.${translationKey}.state_attributes.${attribute}.name`
)) ||
localize(`component.${domain}.state_attributes._.${attribute}.name`) ||
attribute
);
};

View File

@ -86,7 +86,7 @@ export const protocolIntegrationPicked = async (
"ui.panel.config.integrations.config_flow.missing_zwave_zigbee",
{
integration: "Zigbee",
brand: options?.brand || options?.domain || "Z-Wave",
brand: options?.brand || options?.domain || "Zigbee",
supported_hardware_link: html`<a
href=${documentationUrl(
hass,

View File

@ -1,5 +1,7 @@
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property } from "lit/decorators";
import { computeAttributeValueDisplay } from "../common/entity/compute_attribute_display";
import { computeStateDisplay } from "../common/entity/compute_state_display";
import { formatNumber } from "../common/number/format_number";
import { ClimateEntity, CLIMATE_PRESET_NONE } from "../data/climate";
import { isUnavailableState } from "../data/entity";
@ -21,9 +23,12 @@ class HaClimateState extends LitElement {
${this.stateObj.attributes.preset_mode &&
this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
? html`-
${this.hass.localize(
`state_attributes.climate.preset_mode.${this.stateObj.attributes.preset_mode}`
) || this.stateObj.attributes.preset_mode}`
${computeAttributeValueDisplay(
this.hass.localize,
this.stateObj,
this.hass.entities,
"preset_mode"
)}`
: ""}
</span>
<div class="unit">${this._computeTarget()}</div>`
@ -112,13 +117,19 @@ class HaClimateState extends LitElement {
return this.hass.localize(`state.default.${this.stateObj.state}`);
}
const stateString = this.hass.localize(
`component.climate.state._.${this.stateObj.state}`
const stateString = computeStateDisplay(
this.hass.localize,
this.stateObj,
this.hass.locale,
this.hass.entities
);
return this.stateObj.attributes.hvac_action
? `${this.hass.localize(
`state_attributes.climate.hvac_action.${this.stateObj.attributes.hvac_action}`
? `${computeAttributeValueDisplay(
this.hass.localize,
this.stateObj,
this.hass.entities,
"hvac_action"
)} (${stateString})`
: stateString;
}

View File

@ -85,7 +85,7 @@ export class HaForm extends LitElement implements HaFormElement {
.selector=${item.selector}
.value=${getValue(this.data, item)}
.label=${this._computeLabel(item, this.data)}
.disabled=${item.disabled || this.disabled}
.disabled=${item.disabled || this.disabled || false}
.helper=${this._computeHelper(item)}
.required=${item.required || false}
.context=${this._generateContext(item)}
@ -95,7 +95,7 @@ export class HaForm extends LitElement implements HaFormElement {
data: getValue(this.data, item),
label: this._computeLabel(item, this.data),
helper: this._computeHelper(item),
disabled: this.disabled || item.disabled,
disabled: this.disabled || item.disabled || false,
hass: this.hass,
computeLabel: this.computeLabel,
computeHelper: this.computeHelper,

View File

@ -88,6 +88,10 @@ export class HaSelectSelector extends LitElement {
const value =
!this.value || this.value === "" ? [] : (this.value as string[]);
const optionItems = options.filter(
(option) => !option.disabled && !value?.includes(option.value)
);
return html`
${value?.length
? html`<ha-chip-set>
@ -118,11 +122,11 @@ export class HaSelectSelector extends LitElement {
.disabled=${this.disabled}
.required=${this.required && !value.length}
.value=${this._filter}
.filteredItems=${options.filter(
(option) => !option.disabled && !value?.includes(option.value)
)}
.items=${optionItems}
.allowCustomValue=${this.selector.select.custom_value ?? false}
@filter-changed=${this._filterChanged}
@value-changed=${this._comboBoxValueChanged}
@opened-changed=${this._openedChanged}
></ha-combo-box>
`;
}
@ -130,11 +134,14 @@ export class HaSelectSelector extends LitElement {
if (this.selector.select?.custom_value) {
if (
this.value !== undefined &&
!Array.isArray(this.value) &&
!options.find((option) => option.value === this.value)
) {
options.unshift({ value: this.value, label: this.value });
}
const optionItems = options.filter((option) => !option.disabled);
return html`
<ha-combo-box
item-value-path="value"
@ -144,10 +151,11 @@ export class HaSelectSelector extends LitElement {
.helper=${this.helper}
.disabled=${this.disabled}
.required=${this.required}
.items=${options.filter((item) => !item.disabled)}
.items=${optionItems}
.value=${this.value}
@filter-changed=${this._filterChanged}
@value-changed=${this._comboBoxValueChanged}
@opened-changed=${this._openedChanged}
></ha-combo-box>
`;
}
@ -190,7 +198,7 @@ export class HaSelectSelector extends LitElement {
private _valueChanged(ev) {
ev.stopPropagation();
const value = ev.detail?.value || ev.target.value;
if (this.disabled || !value) {
if (this.disabled || value === undefined) {
return;
}
fireEvent(this, "value-changed", {
@ -271,13 +279,16 @@ export class HaSelectSelector extends LitElement {
});
}
private _openedChanged(ev?: CustomEvent): void {
if (ev?.detail.value) {
this._filterChanged();
}
}
private _filterChanged(ev?: CustomEvent): void {
this._filter = ev?.detail.value || "";
const filteredItems = this.comboBox.items?.filter((item) => {
if (this.selector.select?.multiple && this.value?.includes(item.value)) {
return false;
}
const label = item.label || item.value;
return label.toLowerCase().includes(this._filter?.toLowerCase());
});

View File

@ -23,63 +23,8 @@ interface CachedResults {
data: HistoryResult;
}
// This is a different interface, a different cache :(
interface RecentCacheResults {
created: number;
language: string;
data: Promise<HistoryResult>;
}
const RECENT_THRESHOLD = 60000; // 1 minute
const RECENT_CACHE: { [cacheKey: string]: RecentCacheResults } = {};
const stateHistoryCache: { [cacheKey: string]: CachedResults } = {};
// Cached type 1 function. Without cache config.
export const getRecent = (
hass: HomeAssistant,
entityId: string,
startTime: Date,
endTime: Date,
localize: LocalizeFunc,
language: string
) => {
const cacheKey = entityId;
const cache = RECENT_CACHE[cacheKey];
if (
cache &&
Date.now() - cache.created < RECENT_THRESHOLD &&
cache.language === language
) {
return cache.data;
}
const noAttributes = !entityIdHistoryNeedsAttributes(hass, entityId);
const prom = fetchRecentWS(
hass,
entityId,
startTime,
endTime,
false,
undefined,
true,
noAttributes
).then(
(stateHistory) => computeHistory(hass, stateHistory, localize),
(err) => {
delete RECENT_CACHE[entityId];
throw err;
}
);
RECENT_CACHE[cacheKey] = {
created: Date.now(),
language,
data: prom,
};
return prom;
};
// Cache type 2 functionality
function getEmptyCache(
language: string,
@ -97,7 +42,7 @@ function getEmptyCache(
export const getRecentWithCache = (
hass: HomeAssistant,
entityId: string,
entityIds: string[],
cacheConfig: CacheConfig,
localize: LocalizeFunc,
language: string
@ -132,7 +77,9 @@ export const getRecentWithCache = (
}
const curCacheProm = cache.prom;
const noAttributes = !entityIdHistoryNeedsAttributes(hass, entityId);
const noAttributes = !entityIds.some((entityId) =>
entityIdHistoryNeedsAttributes(hass, entityId)
);
const genProm = async () => {
let fetchedHistory: HistoryStates;
@ -142,7 +89,7 @@ export const getRecentWithCache = (
curCacheProm,
fetchRecentWS(
hass,
entityId,
entityIds,
toFetchStartTime,
endTime,
appendingToCache,

View File

@ -2,7 +2,6 @@ import {
HassEntityAttributeBase,
HassEntityBase,
} from "home-assistant-js-websocket";
import { TranslationDict } from "../types";
export type HvacMode =
| "off"
@ -15,12 +14,13 @@ export type HvacMode =
export const CLIMATE_PRESET_NONE = "none";
type ClimateAttributes = TranslationDict["state_attributes"]["climate"];
export type HvacAction = keyof ClimateAttributes["hvac_action"];
export type FanMode = keyof ClimateAttributes["fan_mode"];
export type PresetMode =
| keyof ClimateAttributes["preset_mode"]
| typeof CLIMATE_PRESET_NONE;
export type HvacAction =
| "off"
| "heating"
| "cooling"
| "drying"
| "idle"
| "fan";
export type ClimateEntity = HassEntityBase & {
attributes: HassEntityAttributeBase & {
@ -40,23 +40,25 @@ export type ClimateEntity = HassEntityBase & {
target_humidity_high?: number;
min_humidity?: number;
max_humidity?: number;
fan_mode?: FanMode;
fan_modes?: FanMode[];
preset_mode?: PresetMode;
preset_modes?: PresetMode[];
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;
export const enum ClimateEntityFeature {
TARGET_TEMPERATURE = 1,
TARGET_TEMPERATURE_RANGE = 2,
TARGET_HUMIDITY = 4,
FAN_MODE = 8,
PRESET_MODE = 16,
SWING_MODE = 32,
AUX_HEAT = 64,
}
const hvacModeOrdering: { [key in HvacMode]: number } = {
auto: 1,

View File

@ -451,7 +451,7 @@ const getEnergyData = async (
...(await fetchStatistics(
hass!,
compareStartMinHour,
end,
endCompare,
waterStatIds,
period,
waterUnits,

View File

@ -1,4 +1,8 @@
import { HassEntities, HassEntity } from "home-assistant-js-websocket";
import {
HassEntities,
HassEntity,
HassEntityAttributeBase,
} from "home-assistant-js-websocket";
import { computeDomain } from "../common/entity/compute_domain";
import { computeStateDisplayFromEntityAttributes } from "../common/entity/compute_state_display";
import { computeStateNameFromEntityAttributes } from "../common/entity/compute_state_name";
@ -117,7 +121,7 @@ export const fetchRecent = (
export const fetchRecentWS = (
hass: HomeAssistant,
entityId: string, // This may be CSV
entityIds: string[],
startTime: Date,
endTime: Date,
skipInitialState = false,
@ -133,7 +137,7 @@ export const fetchRecentWS = (
include_start_time_state: !skipInitialState,
minimal_response: minimalResponse,
no_attributes: noAttributes || false,
entity_ids: entityId.split(","),
entity_ids: entityIds,
});
export const fetchDate = (
@ -160,9 +164,9 @@ export const fetchDateWS = (
start_time: startTime.toISOString(),
end_time: endTime.toISOString(),
minimal_response: true,
no_attributes: !entityIds
.map((entityId) => entityIdHistoryNeedsAttributes(hass, entityId))
.reduce((cur, next) => cur || next, false),
no_attributes: !entityIds.some((entityId) =>
entityIdHistoryNeedsAttributes(hass, entityId)
),
};
if (entityIds.length !== 0) {
return hass.callWS<HistoryStates>({ ...params, entity_ids: entityIds });
@ -195,13 +199,22 @@ const processTimelineEntity = (
if (data.length > 0 && state.s === data[data.length - 1].state) {
continue;
}
const currentAttributes: HassEntityAttributeBase = {};
if (current_state?.attributes.device_class) {
currentAttributes.device_class = current_state?.attributes.device_class;
}
data.push({
state_localize: computeStateDisplayFromEntityAttributes(
localize,
language,
entities,
entityId,
state.a || first.a,
{
...(state.a || first.a),
...currentAttributes,
},
state.s
),
state: state.s,

View File

@ -44,6 +44,7 @@ declare global {
export type TranslationCategory =
| "title"
| "state"
| "state_attributes"
| "entity"
| "config"
| "config_panel"

View File

@ -11,6 +11,11 @@ import { property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../common/dom/fire_event";
import { stopPropagation } from "../../../common/dom/stop_propagation";
import {
computeAttributeNameDisplay,
computeAttributeValueDisplay,
} from "../../../common/entity/compute_attribute_display";
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { supportsFeature } from "../../../common/entity/supports-feature";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import "../../../components/ha-climate-control";
@ -19,13 +24,7 @@ import "../../../components/ha-slider";
import "../../../components/ha-switch";
import {
ClimateEntity,
CLIMATE_SUPPORT_AUX_HEAT,
CLIMATE_SUPPORT_FAN_MODE,
CLIMATE_SUPPORT_PRESET_MODE,
CLIMATE_SUPPORT_SWING_MODE,
CLIMATE_SUPPORT_TARGET_HUMIDITY,
CLIMATE_SUPPORT_TARGET_TEMPERATURE,
CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE,
ClimateEntityFeature,
compareClimateHvacModes,
} from "../../../data/climate";
import { HomeAssistant } from "../../../types";
@ -47,26 +46,32 @@ class MoreInfoClimate extends LitElement {
const supportTargetTemperature = supportsFeature(
stateObj,
CLIMATE_SUPPORT_TARGET_TEMPERATURE
ClimateEntityFeature.TARGET_TEMPERATURE
);
const supportTargetTemperatureRange = supportsFeature(
stateObj,
CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
);
const supportTargetHumidity = supportsFeature(
stateObj,
CLIMATE_SUPPORT_TARGET_HUMIDITY
ClimateEntityFeature.TARGET_HUMIDITY
);
const supportFanMode = supportsFeature(
stateObj,
ClimateEntityFeature.FAN_MODE
);
const supportFanMode = supportsFeature(stateObj, CLIMATE_SUPPORT_FAN_MODE);
const supportPresetMode = supportsFeature(
stateObj,
CLIMATE_SUPPORT_PRESET_MODE
ClimateEntityFeature.PRESET_MODE
);
const supportSwingMode = supportsFeature(
stateObj,
CLIMATE_SUPPORT_SWING_MODE
ClimateEntityFeature.SWING_MODE
);
const supportAuxHeat = supportsFeature(
stateObj,
ClimateEntityFeature.AUX_HEAT
);
const supportAuxHeat = supportsFeature(stateObj, CLIMATE_SUPPORT_AUX_HEAT);
const temperatureStepSize =
stateObj.attributes.target_temp_step ||
@ -94,7 +99,12 @@ class MoreInfoClimate extends LitElement {
${supportTargetTemperature || supportTargetTemperatureRange
? html`
<div>
${hass.localize("ui.card.climate.target_temperature")}
${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"temperature"
)}
</div>
`
: ""}
@ -145,7 +155,14 @@ class MoreInfoClimate extends LitElement {
${supportTargetHumidity
? html`
<div class="container-humidity">
<div>${hass.localize("ui.card.climate.target_humidity")}</div>
<div>
${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"humidity"
)}
</div>
<div class="single-row">
<div class="target-humidity">
${stateObj.attributes.humidity} %
@ -182,7 +199,13 @@ class MoreInfoClimate extends LitElement {
.map(
(mode) => html`
<mwc-list-item .value=${mode}>
${hass.localize(`component.climate.state._.${mode}`)}
${computeStateDisplay(
hass.localize,
stateObj,
hass.locale,
hass.entities,
mode
)}
</mwc-list-item>
`
)}
@ -194,7 +217,12 @@ class MoreInfoClimate extends LitElement {
? html`
<div class="container-preset_modes">
<ha-select
.label=${hass.localize("ui.card.climate.preset_mode")}
.label=${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"preset_mode"
)}
.value=${stateObj.attributes.preset_mode}
fixedMenuPosition
naturalMenuWidth
@ -204,9 +232,13 @@ class MoreInfoClimate extends LitElement {
${stateObj.attributes.preset_modes!.map(
(mode) => html`
<mwc-list-item .value=${mode}>
${hass.localize(
`state_attributes.climate.preset_mode.${mode}`
) || mode}
${computeAttributeValueDisplay(
hass.localize,
stateObj,
hass.entities,
"preset_mode",
mode
)}
</mwc-list-item>
`
)}
@ -218,7 +250,12 @@ class MoreInfoClimate extends LitElement {
? html`
<div class="container-fan_list">
<ha-select
.label=${hass.localize("ui.card.climate.fan_mode")}
.label=${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"fan_mode"
)}
.value=${stateObj.attributes.fan_mode}
fixedMenuPosition
naturalMenuWidth
@ -228,9 +265,13 @@ class MoreInfoClimate extends LitElement {
${stateObj.attributes.fan_modes!.map(
(mode) => html`
<mwc-list-item .value=${mode}>
${hass.localize(
`state_attributes.climate.fan_mode.${mode}`
) || mode}
${computeAttributeValueDisplay(
hass.localize,
stateObj,
hass.entities,
"fan_mode",
mode
)}
</mwc-list-item>
`
)}
@ -242,7 +283,12 @@ class MoreInfoClimate extends LitElement {
? html`
<div class="container-swing_list">
<ha-select
.label=${hass.localize("ui.card.climate.swing_mode")}
.label=${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"swing_mode"
)}
.value=${stateObj.attributes.swing_mode}
fixedMenuPosition
naturalMenuWidth
@ -251,7 +297,15 @@ class MoreInfoClimate extends LitElement {
>
${stateObj.attributes.swing_modes!.map(
(mode) => html`
<mwc-list-item .value=${mode}>${mode}</mwc-list-item>
<mwc-list-item .value=${mode}>
${computeAttributeValueDisplay(
hass.localize,
stateObj,
hass.entities,
"swing_mode",
mode
)}
</mwc-list-item>
`
)}
</ha-select>
@ -263,7 +317,12 @@ class MoreInfoClimate extends LitElement {
<div class="container-aux_heat">
<div class="center horizontal layout single-row">
<div class="flex">
${hass.localize("ui.card.climate.aux_heat")}
${computeAttributeNameDisplay(
hass.localize,
stateObj,
hass.entities,
"aux_heat"
)}
</div>
<ha-switch
.checked=${stateObj.attributes.aux_heat === "on"}

View File

@ -139,7 +139,7 @@ export class MoreInfoHistory extends LitElement {
}
this._stateHistory = await getRecentWithCache(
this.hass!,
this.entityId,
[this.entityId],
{
cacheKey: `more_info.${this.entityId}`,
hoursToShow: 24,

View File

@ -138,6 +138,8 @@ export class HomeAssistantAppEl extends QuickBarMixin(HassElement) {
// @ts-ignore
this._loadHassTranslations(this.hass!.language, "state");
// @ts-ignore
this._loadHassTranslations(this.hass!.language, "state_attributes");
// @ts-ignore
this._loadHassTranslations(this.hass!.language, "entity");
document.addEventListener(

View File

@ -1,6 +1,7 @@
import "@material/mwc-button";
import { mdiCalendarClock, mdiClose } from "@mdi/js";
import { addDays, isSameDay } from "date-fns/esm";
import { toDate } from "date-fns-tz";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { property, state } from "lit/decorators";
import { RRule, Weekday } from "rrule";
@ -185,11 +186,12 @@ class DialogCalendarEventDetail extends LitElement {
};
private _formatDateRange() {
const start = new Date(this._data!.dtstart);
// Parse a dates in the browser timezone
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const start = toDate(this._data!.dtstart, { timeZone: timeZone });
const endValue = toDate(this._data!.dtend, { timeZone: timeZone });
// All day events should be displayed as a day earlier
const end = isDate(this._data.dtend)
? addDays(new Date(this._data!.dtend), -1)
: new Date(this._data!.dtend);
const end = isDate(this._data.dtend) ? addDays(endValue, -1) : endValue;
// The range can be shortened when the start and end are on the same day.
if (isSameDay(start, end)) {
if (isDate(this._data.dtstart)) {

View File

@ -7,6 +7,7 @@ import {
differenceInMilliseconds,
startOfHour,
} from "date-fns/esm";
import { formatInTimeZone, toDate } from "date-fns-tz";
import { HassEntity } from "home-assistant-js-websocket";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
@ -60,6 +61,12 @@ class DialogCalendarEventEditor extends LitElement {
@state() private _submitting = false;
// Dates are manipulated and displayed in the browser timezone
// which may be different from the Home Assistant timezone. When
// events are persisted, they are relative to the Home Assistant
// timezone, but floating without a timezone.
private _timeZone?: string;
public showDialog(params: CalendarEventEditDialogParams): void {
this._error = undefined;
this._info = undefined;
@ -71,6 +78,9 @@ class DialogCalendarEventEditor extends LitElement {
computeStateDomain(stateObj) === "calendar" &&
supportsFeature(stateObj, CalendarEntityFeature.CREATE_EVENT)
)?.entity_id;
this._timeZone =
Intl.DateTimeFormat().resolvedOptions().timeZone ||
this.hass.config.time_zone;
if (params.entry) {
const entry = params.entry!;
this._allDay = isDate(entry.dtstart);
@ -281,20 +291,30 @@ class DialogCalendarEventEditor extends LitElement {
private _isEditableCalendar = (entityStateObj: HassEntity) =>
supportsFeature(entityStateObj, CalendarEntityFeature.CREATE_EVENT);
private _getLocaleStrings = memoizeOne((startDate?: Date, endDate?: Date) =>
// en-CA locale used for date format YYYY-MM-DD
// en-GB locale used for 24h time format HH:MM:SS
{
const timeZone = this.hass.config.time_zone;
return {
startDate: startDate?.toLocaleDateString("en-CA", { timeZone }),
startTime: startDate?.toLocaleTimeString("en-GB", { timeZone }),
endDate: endDate?.toLocaleDateString("en-CA", { timeZone }),
endTime: endDate?.toLocaleTimeString("en-GB", { timeZone }),
};
}
private _getLocaleStrings = memoizeOne(
(startDate?: Date, endDate?: Date) => ({
startDate: this._formatDate(startDate!),
startTime: this._formatTime(startDate!),
endDate: this._formatDate(endDate!),
endTime: this._formatTime(endDate!),
})
);
// Formats a date in specified timezone, or defaulting to browser display timezone
private _formatDate(date: Date, timeZone: string = this._timeZone!): string {
return formatInTimeZone(date, timeZone, "yyyy-MM-dd");
}
// Formats a time in specified timezone, or defaulting to browser display timezone
private _formatTime(date: Date, timeZone: string = this._timeZone!): string {
return formatInTimeZone(date, timeZone, "HH:mm:ss"); // 24 hr
}
// Parse a date in the browser timezone
private _parseDate(dateStr: string): Date {
return toDate(dateStr, { timeZone: this._timeZone! });
}
private _clearInfo() {
this._info = undefined;
}
@ -319,27 +339,14 @@ class DialogCalendarEventEditor extends LitElement {
// Store previous event duration
const duration = differenceInMilliseconds(this._dtend!, this._dtstart!);
this._dtstart = new Date(
ev.detail.value +
"T" +
this._dtstart!.toLocaleTimeString("en-GB", {
timeZone: this.hass.config.time_zone,
})
this._dtstart = this._parseDate(
`${ev.detail.value}T${this._formatTime(this._dtstart!)}`
);
// Prevent that the end time can be before the start time. Try to keep the
// duration the same.
if (this._dtend! <= this._dtstart!) {
const newEnd = addMilliseconds(this._dtstart, duration);
// en-CA locale used for date format YYYY-MM-DD
// en-GB locale used for 24h time format HH:MM:SS
this._dtend = new Date(
`${newEnd.toLocaleDateString("en-CA", {
timeZone: this.hass.config.time_zone,
})}T${newEnd.toLocaleTimeString("en-GB", {
timeZone: this.hass.config.time_zone,
})}`
);
this._dtend = addMilliseconds(this._dtstart, duration);
this._info = this.hass.localize(
"ui.components.calendar.event.end_auto_adjusted"
);
@ -347,12 +354,8 @@ class DialogCalendarEventEditor extends LitElement {
}
private _endDateChanged(ev: CustomEvent) {
this._dtend = new Date(
ev.detail.value +
"T" +
this._dtend!.toLocaleTimeString("en-GB", {
timeZone: this.hass.config.time_zone,
})
this._dtend = this._parseDate(
`${ev.detail.value}T${this._formatTime(this._dtend!)}`
);
}
@ -360,25 +363,14 @@ class DialogCalendarEventEditor extends LitElement {
// Store previous event duration
const duration = differenceInMilliseconds(this._dtend!, this._dtstart!);
this._dtstart = new Date(
this._dtstart!.toLocaleDateString("en-CA", {
timeZone: this.hass.config.time_zone,
}) +
"T" +
ev.detail.value
this._dtstart = this._parseDate(
`${this._formatDate(this._dtstart!)}T${ev.detail.value}`
);
// Prevent that the end time can be before the start time. Try to keep the
// duration the same.
if (this._dtend! <= this._dtstart!) {
const newEnd = addMilliseconds(new Date(this._dtstart), duration);
this._dtend = new Date(
`${newEnd.toLocaleDateString("en-CA", {
timeZone: this.hass.config.time_zone,
})}T${newEnd.toLocaleTimeString("en-GB", {
timeZone: this.hass.config.time_zone,
})}`
);
this._dtend = addMilliseconds(new Date(this._dtstart), duration);
this._info = this.hass.localize(
"ui.components.calendar.event.end_auto_adjusted"
);
@ -386,36 +378,32 @@ class DialogCalendarEventEditor extends LitElement {
}
private _endTimeChanged(ev: CustomEvent) {
this._dtend = new Date(
this._dtend!.toLocaleDateString("en-CA", {
timeZone: this.hass.config.time_zone,
}) +
"T" +
ev.detail.value
this._dtend = this._parseDate(
`${this._formatDate(this._dtend!)}T${ev.detail.value}`
);
}
private _calculateData() {
const { startDate, startTime, endDate, endTime } = this._getLocaleStrings(
this._dtstart,
this._dtend
);
const data: CalendarEventMutableParams = {
summary: this._summary,
description: this._description,
rrule: this._rrule,
rrule: this._rrule || undefined,
dtstart: "",
dtend: "",
};
if (this._allDay) {
data.dtstart = startDate!;
data.dtstart = this._formatDate(this._dtstart!);
// End date/time is exclusive when persisted
data.dtend = addDays(new Date(this._dtend!), 1).toLocaleDateString(
"en-CA"
);
data.dtend = this._formatDate(addDays(this._dtend!, 1));
} else {
data.dtstart = `${startDate}T${startTime}`;
data.dtend = `${endDate}T${endTime}`;
data.dtstart = `${this._formatDate(
this._dtstart!,
this.hass.config.time_zone
)}T${this._formatTime(this._dtstart!, this.hass.config.time_zone)}`;
data.dtend = `${this._formatDate(
this._dtend!,
this.hass.config.time_zone
)}T${this._formatTime(this._dtend!, this.hass.config.time_zone)}`;
}
return data;
}

View File

@ -1,5 +1,5 @@
import "@material/mwc-button";
import { css, html, LitElement, TemplateResult } from "lit";
import { css, html, LitElement, PropertyValues, TemplateResult } from "lit";
import { customElement, property, state } from "lit/decorators";
import "../../../../../components/ha-card";
import {
@ -13,6 +13,7 @@ import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import "../../../../../components/ha-alert";
import { showPromptDialog } from "../../../../../dialogs/generic/show-dialog-box";
import { navigate } from "../../../../../common/navigate";
@customElement("matter-config-panel")
export class MatterConfigPanel extends LitElement {
@ -22,6 +23,8 @@ export class MatterConfigPanel extends LitElement {
@state() private _error?: string;
private _curMatterDevices?: Set<string>;
private get _canCommissionMatter() {
return this.hass.auth.external?.config.canCommissionMatter;
}
@ -68,7 +71,30 @@ export class MatterConfigPanel extends LitElement {
`;
}
protected override updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (!this._curMatterDevices || !changedProps.has("hass")) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (!oldHass || oldHass.devices === this.hass.devices) {
return;
}
const newMatterDevices = Object.values(this.hass.devices).filter(
(device) =>
device.identifiers.find((identifier) => identifier[0] === "matter") &&
!this._curMatterDevices!.has(device.id)
);
if (newMatterDevices.length) {
navigate(`/config/devices/device/${newMatterDevices[0].id}`);
}
}
private _startMobileCommissioning() {
this._redirectOnNewDevice();
this.hass.auth.external!.fireMessage({
type: "matter/commission",
});
@ -112,6 +138,7 @@ export class MatterConfigPanel extends LitElement {
return;
}
this._error = undefined;
this._redirectOnNewDevice();
try {
await commissionMatterDevice(this.hass, code);
} catch (err: any) {
@ -130,6 +157,7 @@ export class MatterConfigPanel extends LitElement {
return;
}
this._error = undefined;
this._redirectOnNewDevice();
try {
await acceptSharedMatterDevice(this.hass, Number(code));
} catch (err: any) {
@ -155,6 +183,19 @@ export class MatterConfigPanel extends LitElement {
}
}
private _redirectOnNewDevice() {
if (this._curMatterDevices) {
return;
}
this._curMatterDevices = new Set(
Object.values(this.hass.devices)
.filter((device) =>
device.identifiers.find((identifier) => identifier[0] === "matter")
)
.map((device) => device.id)
);
}
static styles = [
haStyle,
css`

View File

@ -50,28 +50,30 @@ class HaPanelDevMqtt extends LitElement {
)}
>
<div class="card-content">
<ha-textfield
.label=${this.hass.localize("ui.panel.config.mqtt.topic")}
.value=${this.topic}
@change=${this._handleTopic}
></ha-textfield>
<ha-select
.label=${this.hass.localize("ui.panel.config.mqtt.qos")}
.value=${this.qos}
@selected=${this._handleQos}
>${qosLevel.map(
(qos) =>
html`<mwc-list-item .value=${qos}>${qos}</mwc-list-item>`
)}
</ha-select>
<ha-formfield
label=${this.hass!.localize("ui.panel.config.mqtt.retain")}
>
<ha-switch
@change=${this._handleRetain}
.checked=${this.retain}
></ha-switch>
</ha-formfield>
<div class="panel-dev-mqtt-fields">
<ha-textfield
.label=${this.hass.localize("ui.panel.config.mqtt.topic")}
.value=${this.topic}
@change=${this._handleTopic}
></ha-textfield>
<ha-select
.label=${this.hass.localize("ui.panel.config.mqtt.qos")}
.value=${this.qos}
@selected=${this._handleQos}
>${qosLevel.map(
(qos) =>
html`<mwc-list-item .value=${qos}>${qos}</mwc-list-item>`
)}
</ha-select>
<ha-formfield
label=${this.hass!.localize("ui.panel.config.mqtt.retain")}
>
<ha-switch
@change=${this._handleRetain}
.checked=${this.retain}
></ha-switch>
</ha-formfield>
</div>
<p>${this.hass.localize("ui.panel.config.mqtt.payload")}</p>
<ha-code-editor
mode="jinja2"
@ -160,6 +162,28 @@ class HaPanelDevMqtt extends LitElement {
margin: 0 auto;
direction: ltr;
}
.panel-dev-mqtt-fields {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
ha-select {
width: 96px;
margin: 0 8px;
}
ha-textfield {
flex: 1;
}
@media screen and (max-width: 600px) {
ha-select {
margin-left: 0px;
margin-top: 8px;
}
ha-textfield {
flex: auto;
width: 100%;
}
}
ha-card:first-child {
margin-bottom: 16px;
}

View File

@ -47,32 +47,35 @@ class MqttSubscribeCard extends LitElement {
header=${this.hass.localize("ui.panel.config.mqtt.description_listen")}
>
<form>
<ha-textfield
.label=${this._subscribed
? this.hass.localize("ui.panel.config.mqtt.listening_to")
: this.hass.localize("ui.panel.config.mqtt.subscribe_to")}
.disabled=${this._subscribed !== undefined}
.value=${this._topic}
@change=${this._handleTopic}
></ha-textfield>
<ha-select
.label=${this.hass.localize("ui.panel.config.mqtt.qos")}
.disabled=${this._subscribed !== undefined}
.value=${this._qos}
@selected=${this._handleQos}
>${qosLevel.map(
(qos) => html`<mwc-list-item .value=${qos}>${qos}</mwc-list-item>`
)}
</ha-select>
<mwc-button
.disabled=${this._topic === ""}
@click=${this._handleSubmit}
type="submit"
>
${this._subscribed
? this.hass.localize("ui.panel.config.mqtt.stop_listening")
: this.hass.localize("ui.panel.config.mqtt.start_listening")}
</mwc-button>
<div class="panel-dev-mqtt-subscribe-fields">
<ha-textfield
.label=${this._subscribed
? this.hass.localize("ui.panel.config.mqtt.listening_to")
: this.hass.localize("ui.panel.config.mqtt.subscribe_to")}
.disabled=${this._subscribed !== undefined}
.value=${this._topic}
@change=${this._handleTopic}
></ha-textfield>
<ha-select
.label=${this.hass.localize("ui.panel.config.mqtt.qos")}
.disabled=${this._subscribed !== undefined}
.value=${this._qos}
@selected=${this._handleQos}
>${qosLevel.map(
(qos) =>
html`<mwc-list-item .value=${qos}>${qos}</mwc-list-item>`
)}
</ha-select>
<mwc-button
.disabled=${this._topic === ""}
@click=${this._handleSubmit}
type="submit"
>
${this._subscribed
? this.hass.localize("ui.panel.config.mqtt.stop_listening")
: this.hass.localize("ui.panel.config.mqtt.start_listening")}
</mwc-button>
</div>
</form>
<div class="events">
${this._messages.map(
@ -170,6 +173,28 @@ class MqttSubscribeCard extends LitElement {
pre {
font-family: var(--code-font-family, monospace);
}
.panel-dev-mqtt-subscribe-fields {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
ha-select {
width: 96px;
margin: 0 8px;
}
ha-textfield {
flex: 1;
}
@media screen and (max-width: 600px) {
ha-select {
margin-left: 0px;
margin-top: 8px;
}
ha-textfield {
flex: auto;
width: 100%;
}
}
`;
}
}

View File

@ -279,7 +279,9 @@ export class HuiEnergySourcesTableCard
? html`<tr class="mdc-data-table__row total">
<td class="mdc-data-table__cell"></td>
<th class="mdc-data-table__cell" scope="row">
Solar total
${this.hass.localize(
"ui.panel.lovelace.cards.energy.energy_sources_table.solar_total"
)}
</th>
${compare
? html`<td

View File

@ -295,7 +295,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
}
.triggered {
--alarm-state-color: rgb(var(--rgb-state-alarm-trigger-color));
--alarm-state-color: rgb(var(--rgb-state-alarm-triggered-color));
animation: pulse 1s infinite;
}

View File

@ -162,7 +162,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
this._stateHistory = {
...(await getRecentWithCache(
this.hass!,
this._cacheConfig!.cacheKey,
this._configEntities!.map((config) => config.entity),
this._cacheConfig!,
this.hass!.localize,
this.hass!.language

View File

@ -24,6 +24,8 @@ import { classMap } from "lit/directives/class-map";
import { UNIT_F } from "../../../common/const";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeAttributeValueDisplay } from "../../../common/entity/compute_attribute_display";
import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { formatNumber } from "../../../common/number/format_number";
import "../../../components/ha-card";
@ -213,11 +215,17 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
>
${
stateObj.attributes.hvac_action
? this.hass!.localize(
`state_attributes.climate.hvac_action.${stateObj.attributes.hvac_action}`
? computeAttributeValueDisplay(
this.hass.localize,
stateObj,
this.hass.entities,
"hvac_action"
)
: this.hass!.localize(
`component.climate.state._.${stateObj.state}`
: computeStateDisplay(
this.hass.localize,
stateObj,
this.hass.locale,
this.hass.entities
)
}
${
@ -225,9 +233,12 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
? html`
-
${this.hass!.localize(
`state_attributes.climate.preset_mode.${stateObj.attributes.preset_mode}`
) || stateObj.attributes.preset_mode}
${computeAttributeValueDisplay(
this.hass.localize,
stateObj,
this.hass.entities,
"preset_mode"
)}
`
: ""
}

View File

@ -193,7 +193,7 @@ export class HuiImage extends LitElement {
style=${styleMap({
paddingBottom: useRatio
? `${((100 * this._ratio!.h) / this._ratio!.w).toFixed(2)}%`
: !this._lastImageHeight
: this._lastImageHeight === undefined
? "56.25%"
: undefined,
backgroundImage:
@ -206,7 +206,7 @@ export class HuiImage extends LitElement {
: undefined,
})}
class="container ${classMap({
ratio: useRatio || !this._lastImageHeight,
ratio: useRatio || this._lastImageHeight === undefined,
})}"
>
${this.cameraImage && this.cameraView === "live"

View File

@ -38,9 +38,11 @@ import { fireEvent } from "../../common/dom/fire_event";
import scrollToTarget from "../../common/dom/scroll-to-target";
import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event";
import { navigate } from "../../common/navigate";
import { constructUrlCurrentPath } from "../../common/url/construct-url";
import {
addSearchParam,
extractSearchParam,
extractSearchParamsObject,
removeSearchParam,
} from "../../common/url/search-params";
import { computeRTLDirection } from "../../common/util/compute_rtl";
import { debounce } from "../../common/util/debounce";
@ -556,8 +558,16 @@ class HUIRoot extends LitElement {
protected firstUpdated() {
// Check for requested edit mode
if (extractSearchParam("edit") === "1") {
const searchParams = extractSearchParamsObject();
if (searchParams.edit === "1") {
this.lovelace!.setEditMode(true);
} else if (searchParams.conversation === "1") {
showVoiceCommandDialog(this);
window.history.replaceState(
null,
"",
constructUrlCurrentPath(removeSearchParam("conversation"))
);
}
}

View File

@ -41,6 +41,10 @@ export const getMyRedirects = (hasSupervisor: boolean): Redirects => ({
server_controls: {
redirect: "/developer-tools/yaml",
},
calendar: {
component: "calendar",
redirect: "/calendar",
},
config: {
redirect: "/config/dashboard",
},

View File

@ -22,31 +22,6 @@
}
},
"state_attributes": {
"climate": {
"fan_mode": {
"off": "Off",
"on": "On",
"auto": "Auto"
},
"preset_mode": {
"none": "None",
"eco": "Eco",
"away": "Away",
"boost": "Boost",
"comfort": "Comfort",
"home": "Home",
"sleep": "Sleep",
"activity": "Activity"
},
"hvac_action": {
"off": "Off",
"heating": "Heating",
"cooling": "Cooling",
"drying": "Drying",
"idle": "Idle",
"fan": "Fan"
}
},
"humidifier": {
"mode": {
"normal": "Normal",
@ -140,7 +115,6 @@
"climate": {
"currently": "Currently",
"on_off": "On / off",
"target_temperature": "Target temperature",
"target_temperature_entity": "{name} target temperature",
"target_temperature_mode": "{name} target temperature {mode}",
"current_temperature": "{name} current temperature",
@ -148,13 +122,8 @@
"cooling": "{name} cooling",
"high": "high",
"low": "low",
"target_humidity": "Target humidity",
"operation": "Operation",
"fan_mode": "Fan mode",
"swing_mode": "Swing mode",
"preset_mode": "Preset",
"away_mode": "Away mode",
"aux_heat": "Aux heat"
"away_mode": "Away mode"
},
"counter": {
"actions": {
@ -3225,7 +3194,7 @@
"mqtt": {
"title": "MQTT",
"description_publish": "Publish a packet",
"topic": "topic",
"topic": "Topic",
"payload": "Payload (template allowed)",
"publish": "Publish",
"description_listen": "Listen to a topic",
@ -3808,6 +3777,7 @@
"energy_sources_table": {
"grid_total": "Grid total",
"gas_total": "Gas total",
"solar_total": "Solar total",
"water_total": "Water total",
"source": "Source",
"energy": "Energy",

View File

@ -6993,6 +6993,15 @@ __metadata:
languageName: node
linkType: hard
"date-fns-tz@npm:^1.3.7":
version: 1.3.7
resolution: "date-fns-tz@npm:1.3.7"
peerDependencies:
date-fns: ">=2.0.0"
checksum: b749613669223056d5e6d715114c94bec57234b676d0cea0c72ca710626c81e9ea04df6441852a5fec74b42c5f27b2f076e13697ec43da360b67806a3042a10e
languageName: node
linkType: hard
"date-fns@npm:^2.23.0":
version: 2.23.0
resolution: "date-fns@npm:2.23.0"
@ -9427,6 +9436,7 @@ fsevents@^1.2.7:
core-js: ^3.15.2
cropperjs: ^1.5.12
date-fns: ^2.23.0
date-fns-tz: ^1.3.7
deep-clone-simple: ^1.1.1
deep-freeze: ^0.0.1
del: ^4.0.0