mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-04 06:57:47 +00:00
Merge dev and pin action versions
This commit is contained in:
commit
555c43caeb
8
.github/workflows/cast_deployment.yaml
vendored
8
.github/workflows/cast_deployment.yaml
vendored
@ -22,12 +22,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@ -60,12 +60,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
20
.github/workflows/ci.yaml
vendored
20
.github/workflows/ci.yaml
vendored
@ -32,13 +32,13 @@ jobs:
|
|||||||
sha: ${{ steps.get-sha.outputs.sha }}
|
sha: ${{ steps.get-sha.outputs.sha }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
with:
|
with:
|
||||||
# Checkout PR head instead of merge commit
|
# Checkout PR head instead of merge commit
|
||||||
# Use ref, not SHA, so reruns get the dedupe commit
|
# Use ref, not SHA, so reruns get the dedupe commit
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
ref: ${{ github.event.pull_request.head.ref }}
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@ -81,11 +81,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.dedupe.outputs.sha }}
|
ref: ${{ needs.dedupe.outputs.sha }}
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@ -109,11 +109,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.dedupe.outputs.sha }}
|
ref: ${{ needs.dedupe.outputs.sha }}
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@ -131,11 +131,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.dedupe.outputs.sha }}
|
ref: ${{ needs.dedupe.outputs.sha }}
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@ -153,11 +153,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
with:
|
with:
|
||||||
ref: ${{ needs.dedupe.outputs.sha }}
|
ref: ${{ needs.dedupe.outputs.sha }}
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
with:
|
with:
|
||||||
# We must fetch at least the immediate parents so that if this is
|
# We must fetch at least the immediate parents so that if this is
|
||||||
# a pull request then we can checkout the head.
|
# a pull request then we can checkout the head.
|
||||||
|
8
.github/workflows/demo_deployment.yaml
vendored
8
.github/workflows/demo_deployment.yaml
vendored
@ -23,12 +23,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
with:
|
with:
|
||||||
ref: dev
|
ref: dev
|
||||||
|
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
@ -61,12 +61,12 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
with:
|
with:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
4
.github/workflows/design_deployment.yaml
vendored
4
.github/workflows/design_deployment.yaml
vendored
@ -17,10 +17,10 @@ jobs:
|
|||||||
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
url: ${{ steps.deploy.outputs.NETLIFY_LIVE_URL || steps.deploy.outputs.NETLIFY_URL }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
|
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
4
.github/workflows/design_preview.yaml
vendored
4
.github/workflows/design_preview.yaml
vendored
@ -22,10 +22,10 @@ jobs:
|
|||||||
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
if: github.repository == 'home-assistant/frontend' && contains(github.event.pull_request.labels.*.name, 'needs design preview')
|
||||||
steps:
|
steps:
|
||||||
- name: Check out files from GitHub
|
- name: Check out files from GitHub
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
|
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
4
.github/workflows/nightly.yaml
vendored
4
.github/workflows/nightly.yaml
vendored
@ -21,7 +21,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
@ -29,7 +29,7 @@ jobs:
|
|||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
4
.github/workflows/release.yaml
vendored
4
.github/workflows/release.yaml
vendored
@ -24,7 +24,7 @@ jobs:
|
|||||||
contents: write # Required to upload release assets
|
contents: write # Required to upload release assets
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
|
|
||||||
- name: Verify version
|
- name: Verify version
|
||||||
uses: home-assistant/actions/helpers/verify-version@master
|
uses: home-assistant/actions/helpers/verify-version@master
|
||||||
@ -35,7 +35,7 @@ jobs:
|
|||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
- name: Set up Node ${{ env.NODE_VERSION }}
|
- name: Set up Node ${{ env.NODE_VERSION }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3.5.1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ env.NODE_VERSION }}
|
node-version: ${{ env.NODE_VERSION }}
|
||||||
cache: yarn
|
cache: yarn
|
||||||
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: 90 days stale policy
|
- name: 90 days stale policy
|
||||||
uses: actions/stale@v6.0.1
|
uses: actions/stale@v7.0.0
|
||||||
with:
|
with:
|
||||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
days-before-stale: 90
|
days-before-stale: 90
|
||||||
|
2
.github/workflows/translations.yaml
vendored
2
.github/workflows/translations.yaml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3.2.0
|
||||||
|
|
||||||
- name: Upload Translations
|
- name: Upload Translations
|
||||||
run: |
|
run: |
|
||||||
|
@ -106,6 +106,7 @@
|
|||||||
"core-js": "^3.15.2",
|
"core-js": "^3.15.2",
|
||||||
"cropperjs": "^1.5.12",
|
"cropperjs": "^1.5.12",
|
||||||
"date-fns": "^2.23.0",
|
"date-fns": "^2.23.0",
|
||||||
|
"date-fns-tz": "^1.3.7",
|
||||||
"deep-clone-simple": "^1.1.1",
|
"deep-clone-simple": "^1.1.1",
|
||||||
"deep-freeze": "^0.0.1",
|
"deep-freeze": "^0.0.1",
|
||||||
"fuse.js": "^6.0.0",
|
"fuse.js": "^6.0.0",
|
||||||
|
52
src/common/entity/compute_attribute_display.ts
Normal file
52
src/common/entity/compute_attribute_display.ts
Normal 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
|
||||||
|
);
|
||||||
|
};
|
@ -86,7 +86,7 @@ export const protocolIntegrationPicked = async (
|
|||||||
"ui.panel.config.integrations.config_flow.missing_zwave_zigbee",
|
"ui.panel.config.integrations.config_flow.missing_zwave_zigbee",
|
||||||
{
|
{
|
||||||
integration: "Zigbee",
|
integration: "Zigbee",
|
||||||
brand: options?.brand || options?.domain || "Z-Wave",
|
brand: options?.brand || options?.domain || "Zigbee",
|
||||||
supported_hardware_link: html`<a
|
supported_hardware_link: html`<a
|
||||||
href=${documentationUrl(
|
href=${documentationUrl(
|
||||||
hass,
|
hass,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
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 { formatNumber } from "../common/number/format_number";
|
||||||
import { ClimateEntity, CLIMATE_PRESET_NONE } from "../data/climate";
|
import { ClimateEntity, CLIMATE_PRESET_NONE } from "../data/climate";
|
||||||
import { isUnavailableState } from "../data/entity";
|
import { isUnavailableState } from "../data/entity";
|
||||||
@ -21,9 +23,12 @@ class HaClimateState extends LitElement {
|
|||||||
${this.stateObj.attributes.preset_mode &&
|
${this.stateObj.attributes.preset_mode &&
|
||||||
this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
|
this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
|
||||||
? html`-
|
? html`-
|
||||||
${this.hass.localize(
|
${computeAttributeValueDisplay(
|
||||||
`state_attributes.climate.preset_mode.${this.stateObj.attributes.preset_mode}`
|
this.hass.localize,
|
||||||
) || this.stateObj.attributes.preset_mode}`
|
this.stateObj,
|
||||||
|
this.hass.entities,
|
||||||
|
"preset_mode"
|
||||||
|
)}`
|
||||||
: ""}
|
: ""}
|
||||||
</span>
|
</span>
|
||||||
<div class="unit">${this._computeTarget()}</div>`
|
<div class="unit">${this._computeTarget()}</div>`
|
||||||
@ -112,13 +117,19 @@ class HaClimateState extends LitElement {
|
|||||||
return this.hass.localize(`state.default.${this.stateObj.state}`);
|
return this.hass.localize(`state.default.${this.stateObj.state}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateString = this.hass.localize(
|
const stateString = computeStateDisplay(
|
||||||
`component.climate.state._.${this.stateObj.state}`
|
this.hass.localize,
|
||||||
|
this.stateObj,
|
||||||
|
this.hass.locale,
|
||||||
|
this.hass.entities
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.stateObj.attributes.hvac_action
|
return this.stateObj.attributes.hvac_action
|
||||||
? `${this.hass.localize(
|
? `${computeAttributeValueDisplay(
|
||||||
`state_attributes.climate.hvac_action.${this.stateObj.attributes.hvac_action}`
|
this.hass.localize,
|
||||||
|
this.stateObj,
|
||||||
|
this.hass.entities,
|
||||||
|
"hvac_action"
|
||||||
)} (${stateString})`
|
)} (${stateString})`
|
||||||
: stateString;
|
: stateString;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
.selector=${item.selector}
|
.selector=${item.selector}
|
||||||
.value=${getValue(this.data, item)}
|
.value=${getValue(this.data, item)}
|
||||||
.label=${this._computeLabel(item, this.data)}
|
.label=${this._computeLabel(item, this.data)}
|
||||||
.disabled=${item.disabled || this.disabled}
|
.disabled=${item.disabled || this.disabled || false}
|
||||||
.helper=${this._computeHelper(item)}
|
.helper=${this._computeHelper(item)}
|
||||||
.required=${item.required || false}
|
.required=${item.required || false}
|
||||||
.context=${this._generateContext(item)}
|
.context=${this._generateContext(item)}
|
||||||
@ -95,7 +95,7 @@ export class HaForm extends LitElement implements HaFormElement {
|
|||||||
data: getValue(this.data, item),
|
data: getValue(this.data, item),
|
||||||
label: this._computeLabel(item, this.data),
|
label: this._computeLabel(item, this.data),
|
||||||
helper: this._computeHelper(item),
|
helper: this._computeHelper(item),
|
||||||
disabled: this.disabled || item.disabled,
|
disabled: this.disabled || item.disabled || false,
|
||||||
hass: this.hass,
|
hass: this.hass,
|
||||||
computeLabel: this.computeLabel,
|
computeLabel: this.computeLabel,
|
||||||
computeHelper: this.computeHelper,
|
computeHelper: this.computeHelper,
|
||||||
|
@ -88,6 +88,10 @@ export class HaSelectSelector extends LitElement {
|
|||||||
const value =
|
const value =
|
||||||
!this.value || this.value === "" ? [] : (this.value as string[]);
|
!this.value || this.value === "" ? [] : (this.value as string[]);
|
||||||
|
|
||||||
|
const optionItems = options.filter(
|
||||||
|
(option) => !option.disabled && !value?.includes(option.value)
|
||||||
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
${value?.length
|
${value?.length
|
||||||
? html`<ha-chip-set>
|
? html`<ha-chip-set>
|
||||||
@ -118,11 +122,11 @@ export class HaSelectSelector extends LitElement {
|
|||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.required=${this.required && !value.length}
|
.required=${this.required && !value.length}
|
||||||
.value=${this._filter}
|
.value=${this._filter}
|
||||||
.filteredItems=${options.filter(
|
.items=${optionItems}
|
||||||
(option) => !option.disabled && !value?.includes(option.value)
|
.allowCustomValue=${this.selector.select.custom_value ?? false}
|
||||||
)}
|
|
||||||
@filter-changed=${this._filterChanged}
|
@filter-changed=${this._filterChanged}
|
||||||
@value-changed=${this._comboBoxValueChanged}
|
@value-changed=${this._comboBoxValueChanged}
|
||||||
|
@opened-changed=${this._openedChanged}
|
||||||
></ha-combo-box>
|
></ha-combo-box>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -130,11 +134,14 @@ export class HaSelectSelector extends LitElement {
|
|||||||
if (this.selector.select?.custom_value) {
|
if (this.selector.select?.custom_value) {
|
||||||
if (
|
if (
|
||||||
this.value !== undefined &&
|
this.value !== undefined &&
|
||||||
|
!Array.isArray(this.value) &&
|
||||||
!options.find((option) => option.value === this.value)
|
!options.find((option) => option.value === this.value)
|
||||||
) {
|
) {
|
||||||
options.unshift({ value: this.value, label: this.value });
|
options.unshift({ value: this.value, label: this.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const optionItems = options.filter((option) => !option.disabled);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-combo-box
|
<ha-combo-box
|
||||||
item-value-path="value"
|
item-value-path="value"
|
||||||
@ -144,10 +151,11 @@ export class HaSelectSelector extends LitElement {
|
|||||||
.helper=${this.helper}
|
.helper=${this.helper}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.required=${this.required}
|
.required=${this.required}
|
||||||
.items=${options.filter((item) => !item.disabled)}
|
.items=${optionItems}
|
||||||
.value=${this.value}
|
.value=${this.value}
|
||||||
@filter-changed=${this._filterChanged}
|
@filter-changed=${this._filterChanged}
|
||||||
@value-changed=${this._comboBoxValueChanged}
|
@value-changed=${this._comboBoxValueChanged}
|
||||||
|
@opened-changed=${this._openedChanged}
|
||||||
></ha-combo-box>
|
></ha-combo-box>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@ -190,7 +198,7 @@ export class HaSelectSelector extends LitElement {
|
|||||||
private _valueChanged(ev) {
|
private _valueChanged(ev) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
const value = ev.detail?.value || ev.target.value;
|
const value = ev.detail?.value || ev.target.value;
|
||||||
if (this.disabled || !value) {
|
if (this.disabled || value === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fireEvent(this, "value-changed", {
|
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 {
|
private _filterChanged(ev?: CustomEvent): void {
|
||||||
this._filter = ev?.detail.value || "";
|
this._filter = ev?.detail.value || "";
|
||||||
|
|
||||||
const filteredItems = this.comboBox.items?.filter((item) => {
|
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;
|
const label = item.label || item.value;
|
||||||
return label.toLowerCase().includes(this._filter?.toLowerCase());
|
return label.toLowerCase().includes(this._filter?.toLowerCase());
|
||||||
});
|
});
|
||||||
|
@ -23,63 +23,8 @@ interface CachedResults {
|
|||||||
data: HistoryResult;
|
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 } = {};
|
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
|
// Cache type 2 functionality
|
||||||
function getEmptyCache(
|
function getEmptyCache(
|
||||||
language: string,
|
language: string,
|
||||||
@ -97,7 +42,7 @@ function getEmptyCache(
|
|||||||
|
|
||||||
export const getRecentWithCache = (
|
export const getRecentWithCache = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityId: string,
|
entityIds: string[],
|
||||||
cacheConfig: CacheConfig,
|
cacheConfig: CacheConfig,
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
language: string
|
language: string
|
||||||
@ -132,7 +77,9 @@ export const getRecentWithCache = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const curCacheProm = cache.prom;
|
const curCacheProm = cache.prom;
|
||||||
const noAttributes = !entityIdHistoryNeedsAttributes(hass, entityId);
|
const noAttributes = !entityIds.some((entityId) =>
|
||||||
|
entityIdHistoryNeedsAttributes(hass, entityId)
|
||||||
|
);
|
||||||
|
|
||||||
const genProm = async () => {
|
const genProm = async () => {
|
||||||
let fetchedHistory: HistoryStates;
|
let fetchedHistory: HistoryStates;
|
||||||
@ -142,7 +89,7 @@ export const getRecentWithCache = (
|
|||||||
curCacheProm,
|
curCacheProm,
|
||||||
fetchRecentWS(
|
fetchRecentWS(
|
||||||
hass,
|
hass,
|
||||||
entityId,
|
entityIds,
|
||||||
toFetchStartTime,
|
toFetchStartTime,
|
||||||
endTime,
|
endTime,
|
||||||
appendingToCache,
|
appendingToCache,
|
||||||
|
@ -2,7 +2,6 @@ import {
|
|||||||
HassEntityAttributeBase,
|
HassEntityAttributeBase,
|
||||||
HassEntityBase,
|
HassEntityBase,
|
||||||
} from "home-assistant-js-websocket";
|
} from "home-assistant-js-websocket";
|
||||||
import { TranslationDict } from "../types";
|
|
||||||
|
|
||||||
export type HvacMode =
|
export type HvacMode =
|
||||||
| "off"
|
| "off"
|
||||||
@ -15,12 +14,13 @@ export type HvacMode =
|
|||||||
|
|
||||||
export const CLIMATE_PRESET_NONE = "none";
|
export const CLIMATE_PRESET_NONE = "none";
|
||||||
|
|
||||||
type ClimateAttributes = TranslationDict["state_attributes"]["climate"];
|
export type HvacAction =
|
||||||
export type HvacAction = keyof ClimateAttributes["hvac_action"];
|
| "off"
|
||||||
export type FanMode = keyof ClimateAttributes["fan_mode"];
|
| "heating"
|
||||||
export type PresetMode =
|
| "cooling"
|
||||||
| keyof ClimateAttributes["preset_mode"]
|
| "drying"
|
||||||
| typeof CLIMATE_PRESET_NONE;
|
| "idle"
|
||||||
|
| "fan";
|
||||||
|
|
||||||
export type ClimateEntity = HassEntityBase & {
|
export type ClimateEntity = HassEntityBase & {
|
||||||
attributes: HassEntityAttributeBase & {
|
attributes: HassEntityAttributeBase & {
|
||||||
@ -40,23 +40,25 @@ export type ClimateEntity = HassEntityBase & {
|
|||||||
target_humidity_high?: number;
|
target_humidity_high?: number;
|
||||||
min_humidity?: number;
|
min_humidity?: number;
|
||||||
max_humidity?: number;
|
max_humidity?: number;
|
||||||
fan_mode?: FanMode;
|
fan_mode?: string;
|
||||||
fan_modes?: FanMode[];
|
fan_modes?: string[];
|
||||||
preset_mode?: PresetMode;
|
preset_mode?: string;
|
||||||
preset_modes?: PresetMode[];
|
preset_modes?: string[];
|
||||||
swing_mode?: string;
|
swing_mode?: string;
|
||||||
swing_modes?: string[];
|
swing_modes?: string[];
|
||||||
aux_heat?: "on" | "off";
|
aux_heat?: "on" | "off";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CLIMATE_SUPPORT_TARGET_TEMPERATURE = 1;
|
export const enum ClimateEntityFeature {
|
||||||
export const CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE = 2;
|
TARGET_TEMPERATURE = 1,
|
||||||
export const CLIMATE_SUPPORT_TARGET_HUMIDITY = 4;
|
TARGET_TEMPERATURE_RANGE = 2,
|
||||||
export const CLIMATE_SUPPORT_FAN_MODE = 8;
|
TARGET_HUMIDITY = 4,
|
||||||
export const CLIMATE_SUPPORT_PRESET_MODE = 16;
|
FAN_MODE = 8,
|
||||||
export const CLIMATE_SUPPORT_SWING_MODE = 32;
|
PRESET_MODE = 16,
|
||||||
export const CLIMATE_SUPPORT_AUX_HEAT = 64;
|
SWING_MODE = 32,
|
||||||
|
AUX_HEAT = 64,
|
||||||
|
}
|
||||||
|
|
||||||
const hvacModeOrdering: { [key in HvacMode]: number } = {
|
const hvacModeOrdering: { [key in HvacMode]: number } = {
|
||||||
auto: 1,
|
auto: 1,
|
||||||
|
@ -451,7 +451,7 @@ const getEnergyData = async (
|
|||||||
...(await fetchStatistics(
|
...(await fetchStatistics(
|
||||||
hass!,
|
hass!,
|
||||||
compareStartMinHour,
|
compareStartMinHour,
|
||||||
end,
|
endCompare,
|
||||||
waterStatIds,
|
waterStatIds,
|
||||||
period,
|
period,
|
||||||
waterUnits,
|
waterUnits,
|
||||||
|
@ -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 { computeDomain } from "../common/entity/compute_domain";
|
||||||
import { computeStateDisplayFromEntityAttributes } from "../common/entity/compute_state_display";
|
import { computeStateDisplayFromEntityAttributes } from "../common/entity/compute_state_display";
|
||||||
import { computeStateNameFromEntityAttributes } from "../common/entity/compute_state_name";
|
import { computeStateNameFromEntityAttributes } from "../common/entity/compute_state_name";
|
||||||
@ -117,7 +121,7 @@ export const fetchRecent = (
|
|||||||
|
|
||||||
export const fetchRecentWS = (
|
export const fetchRecentWS = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
entityId: string, // This may be CSV
|
entityIds: string[],
|
||||||
startTime: Date,
|
startTime: Date,
|
||||||
endTime: Date,
|
endTime: Date,
|
||||||
skipInitialState = false,
|
skipInitialState = false,
|
||||||
@ -133,7 +137,7 @@ export const fetchRecentWS = (
|
|||||||
include_start_time_state: !skipInitialState,
|
include_start_time_state: !skipInitialState,
|
||||||
minimal_response: minimalResponse,
|
minimal_response: minimalResponse,
|
||||||
no_attributes: noAttributes || false,
|
no_attributes: noAttributes || false,
|
||||||
entity_ids: entityId.split(","),
|
entity_ids: entityIds,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const fetchDate = (
|
export const fetchDate = (
|
||||||
@ -160,9 +164,9 @@ export const fetchDateWS = (
|
|||||||
start_time: startTime.toISOString(),
|
start_time: startTime.toISOString(),
|
||||||
end_time: endTime.toISOString(),
|
end_time: endTime.toISOString(),
|
||||||
minimal_response: true,
|
minimal_response: true,
|
||||||
no_attributes: !entityIds
|
no_attributes: !entityIds.some((entityId) =>
|
||||||
.map((entityId) => entityIdHistoryNeedsAttributes(hass, entityId))
|
entityIdHistoryNeedsAttributes(hass, entityId)
|
||||||
.reduce((cur, next) => cur || next, false),
|
),
|
||||||
};
|
};
|
||||||
if (entityIds.length !== 0) {
|
if (entityIds.length !== 0) {
|
||||||
return hass.callWS<HistoryStates>({ ...params, entity_ids: entityIds });
|
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) {
|
if (data.length > 0 && state.s === data[data.length - 1].state) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentAttributes: HassEntityAttributeBase = {};
|
||||||
|
if (current_state?.attributes.device_class) {
|
||||||
|
currentAttributes.device_class = current_state?.attributes.device_class;
|
||||||
|
}
|
||||||
|
|
||||||
data.push({
|
data.push({
|
||||||
state_localize: computeStateDisplayFromEntityAttributes(
|
state_localize: computeStateDisplayFromEntityAttributes(
|
||||||
localize,
|
localize,
|
||||||
language,
|
language,
|
||||||
entities,
|
entities,
|
||||||
entityId,
|
entityId,
|
||||||
state.a || first.a,
|
{
|
||||||
|
...(state.a || first.a),
|
||||||
|
...currentAttributes,
|
||||||
|
},
|
||||||
state.s
|
state.s
|
||||||
),
|
),
|
||||||
state: state.s,
|
state: state.s,
|
||||||
|
@ -44,6 +44,7 @@ declare global {
|
|||||||
export type TranslationCategory =
|
export type TranslationCategory =
|
||||||
| "title"
|
| "title"
|
||||||
| "state"
|
| "state"
|
||||||
|
| "state_attributes"
|
||||||
| "entity"
|
| "entity"
|
||||||
| "config"
|
| "config"
|
||||||
| "config_panel"
|
| "config_panel"
|
||||||
|
@ -11,6 +11,11 @@ import { property } from "lit/decorators";
|
|||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
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 { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../../common/util/compute_rtl";
|
||||||
import "../../../components/ha-climate-control";
|
import "../../../components/ha-climate-control";
|
||||||
@ -19,13 +24,7 @@ import "../../../components/ha-slider";
|
|||||||
import "../../../components/ha-switch";
|
import "../../../components/ha-switch";
|
||||||
import {
|
import {
|
||||||
ClimateEntity,
|
ClimateEntity,
|
||||||
CLIMATE_SUPPORT_AUX_HEAT,
|
ClimateEntityFeature,
|
||||||
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,
|
|
||||||
compareClimateHvacModes,
|
compareClimateHvacModes,
|
||||||
} from "../../../data/climate";
|
} from "../../../data/climate";
|
||||||
import { HomeAssistant } from "../../../types";
|
import { HomeAssistant } from "../../../types";
|
||||||
@ -47,26 +46,32 @@ class MoreInfoClimate extends LitElement {
|
|||||||
|
|
||||||
const supportTargetTemperature = supportsFeature(
|
const supportTargetTemperature = supportsFeature(
|
||||||
stateObj,
|
stateObj,
|
||||||
CLIMATE_SUPPORT_TARGET_TEMPERATURE
|
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||||
);
|
);
|
||||||
const supportTargetTemperatureRange = supportsFeature(
|
const supportTargetTemperatureRange = supportsFeature(
|
||||||
stateObj,
|
stateObj,
|
||||||
CLIMATE_SUPPORT_TARGET_TEMPERATURE_RANGE
|
ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
|
||||||
);
|
);
|
||||||
const supportTargetHumidity = supportsFeature(
|
const supportTargetHumidity = supportsFeature(
|
||||||
stateObj,
|
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(
|
const supportPresetMode = supportsFeature(
|
||||||
stateObj,
|
stateObj,
|
||||||
CLIMATE_SUPPORT_PRESET_MODE
|
ClimateEntityFeature.PRESET_MODE
|
||||||
);
|
);
|
||||||
const supportSwingMode = supportsFeature(
|
const supportSwingMode = supportsFeature(
|
||||||
stateObj,
|
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 =
|
const temperatureStepSize =
|
||||||
stateObj.attributes.target_temp_step ||
|
stateObj.attributes.target_temp_step ||
|
||||||
@ -94,7 +99,12 @@ class MoreInfoClimate extends LitElement {
|
|||||||
${supportTargetTemperature || supportTargetTemperatureRange
|
${supportTargetTemperature || supportTargetTemperatureRange
|
||||||
? html`
|
? html`
|
||||||
<div>
|
<div>
|
||||||
${hass.localize("ui.card.climate.target_temperature")}
|
${computeAttributeNameDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.entities,
|
||||||
|
"temperature"
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
@ -145,7 +155,14 @@ class MoreInfoClimate extends LitElement {
|
|||||||
${supportTargetHumidity
|
${supportTargetHumidity
|
||||||
? html`
|
? html`
|
||||||
<div class="container-humidity">
|
<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="single-row">
|
||||||
<div class="target-humidity">
|
<div class="target-humidity">
|
||||||
${stateObj.attributes.humidity} %
|
${stateObj.attributes.humidity} %
|
||||||
@ -182,7 +199,13 @@ class MoreInfoClimate extends LitElement {
|
|||||||
.map(
|
.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<mwc-list-item .value=${mode}>
|
<mwc-list-item .value=${mode}>
|
||||||
${hass.localize(`component.climate.state._.${mode}`)}
|
${computeStateDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.locale,
|
||||||
|
hass.entities,
|
||||||
|
mode
|
||||||
|
)}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@ -194,7 +217,12 @@ class MoreInfoClimate extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<div class="container-preset_modes">
|
<div class="container-preset_modes">
|
||||||
<ha-select
|
<ha-select
|
||||||
.label=${hass.localize("ui.card.climate.preset_mode")}
|
.label=${computeAttributeNameDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.entities,
|
||||||
|
"preset_mode"
|
||||||
|
)}
|
||||||
.value=${stateObj.attributes.preset_mode}
|
.value=${stateObj.attributes.preset_mode}
|
||||||
fixedMenuPosition
|
fixedMenuPosition
|
||||||
naturalMenuWidth
|
naturalMenuWidth
|
||||||
@ -204,9 +232,13 @@ class MoreInfoClimate extends LitElement {
|
|||||||
${stateObj.attributes.preset_modes!.map(
|
${stateObj.attributes.preset_modes!.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<mwc-list-item .value=${mode}>
|
<mwc-list-item .value=${mode}>
|
||||||
${hass.localize(
|
${computeAttributeValueDisplay(
|
||||||
`state_attributes.climate.preset_mode.${mode}`
|
hass.localize,
|
||||||
) || mode}
|
stateObj,
|
||||||
|
hass.entities,
|
||||||
|
"preset_mode",
|
||||||
|
mode
|
||||||
|
)}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@ -218,7 +250,12 @@ class MoreInfoClimate extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<div class="container-fan_list">
|
<div class="container-fan_list">
|
||||||
<ha-select
|
<ha-select
|
||||||
.label=${hass.localize("ui.card.climate.fan_mode")}
|
.label=${computeAttributeNameDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.entities,
|
||||||
|
"fan_mode"
|
||||||
|
)}
|
||||||
.value=${stateObj.attributes.fan_mode}
|
.value=${stateObj.attributes.fan_mode}
|
||||||
fixedMenuPosition
|
fixedMenuPosition
|
||||||
naturalMenuWidth
|
naturalMenuWidth
|
||||||
@ -228,9 +265,13 @@ class MoreInfoClimate extends LitElement {
|
|||||||
${stateObj.attributes.fan_modes!.map(
|
${stateObj.attributes.fan_modes!.map(
|
||||||
(mode) => html`
|
(mode) => html`
|
||||||
<mwc-list-item .value=${mode}>
|
<mwc-list-item .value=${mode}>
|
||||||
${hass.localize(
|
${computeAttributeValueDisplay(
|
||||||
`state_attributes.climate.fan_mode.${mode}`
|
hass.localize,
|
||||||
) || mode}
|
stateObj,
|
||||||
|
hass.entities,
|
||||||
|
"fan_mode",
|
||||||
|
mode
|
||||||
|
)}
|
||||||
</mwc-list-item>
|
</mwc-list-item>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@ -242,7 +283,12 @@ class MoreInfoClimate extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<div class="container-swing_list">
|
<div class="container-swing_list">
|
||||||
<ha-select
|
<ha-select
|
||||||
.label=${hass.localize("ui.card.climate.swing_mode")}
|
.label=${computeAttributeNameDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.entities,
|
||||||
|
"swing_mode"
|
||||||
|
)}
|
||||||
.value=${stateObj.attributes.swing_mode}
|
.value=${stateObj.attributes.swing_mode}
|
||||||
fixedMenuPosition
|
fixedMenuPosition
|
||||||
naturalMenuWidth
|
naturalMenuWidth
|
||||||
@ -251,7 +297,15 @@ class MoreInfoClimate extends LitElement {
|
|||||||
>
|
>
|
||||||
${stateObj.attributes.swing_modes!.map(
|
${stateObj.attributes.swing_modes!.map(
|
||||||
(mode) => html`
|
(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>
|
</ha-select>
|
||||||
@ -263,7 +317,12 @@ class MoreInfoClimate extends LitElement {
|
|||||||
<div class="container-aux_heat">
|
<div class="container-aux_heat">
|
||||||
<div class="center horizontal layout single-row">
|
<div class="center horizontal layout single-row">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
${hass.localize("ui.card.climate.aux_heat")}
|
${computeAttributeNameDisplay(
|
||||||
|
hass.localize,
|
||||||
|
stateObj,
|
||||||
|
hass.entities,
|
||||||
|
"aux_heat"
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${stateObj.attributes.aux_heat === "on"}
|
.checked=${stateObj.attributes.aux_heat === "on"}
|
||||||
|
@ -139,7 +139,7 @@ export class MoreInfoHistory extends LitElement {
|
|||||||
}
|
}
|
||||||
this._stateHistory = await getRecentWithCache(
|
this._stateHistory = await getRecentWithCache(
|
||||||
this.hass!,
|
this.hass!,
|
||||||
this.entityId,
|
[this.entityId],
|
||||||
{
|
{
|
||||||
cacheKey: `more_info.${this.entityId}`,
|
cacheKey: `more_info.${this.entityId}`,
|
||||||
hoursToShow: 24,
|
hoursToShow: 24,
|
||||||
|
@ -138,6 +138,8 @@ export class HomeAssistantAppEl extends QuickBarMixin(HassElement) {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this._loadHassTranslations(this.hass!.language, "state");
|
this._loadHassTranslations(this.hass!.language, "state");
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
this._loadHassTranslations(this.hass!.language, "state_attributes");
|
||||||
|
// @ts-ignore
|
||||||
this._loadHassTranslations(this.hass!.language, "entity");
|
this._loadHassTranslations(this.hass!.language, "entity");
|
||||||
|
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { mdiCalendarClock, mdiClose } from "@mdi/js";
|
import { mdiCalendarClock, mdiClose } from "@mdi/js";
|
||||||
import { addDays, isSameDay } from "date-fns/esm";
|
import { addDays, isSameDay } from "date-fns/esm";
|
||||||
|
import { toDate } from "date-fns-tz";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { RRule, Weekday } from "rrule";
|
import { RRule, Weekday } from "rrule";
|
||||||
@ -185,11 +186,12 @@ class DialogCalendarEventDetail extends LitElement {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private _formatDateRange() {
|
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
|
// All day events should be displayed as a day earlier
|
||||||
const end = isDate(this._data.dtend)
|
const end = isDate(this._data.dtend) ? addDays(endValue, -1) : endValue;
|
||||||
? addDays(new Date(this._data!.dtend), -1)
|
|
||||||
: new Date(this._data!.dtend);
|
|
||||||
// The range can be shortened when the start and end are on the same day.
|
// The range can be shortened when the start and end are on the same day.
|
||||||
if (isSameDay(start, end)) {
|
if (isSameDay(start, end)) {
|
||||||
if (isDate(this._data.dtstart)) {
|
if (isDate(this._data.dtstart)) {
|
||||||
|
@ -7,6 +7,7 @@ import {
|
|||||||
differenceInMilliseconds,
|
differenceInMilliseconds,
|
||||||
startOfHour,
|
startOfHour,
|
||||||
} from "date-fns/esm";
|
} from "date-fns/esm";
|
||||||
|
import { formatInTimeZone, toDate } from "date-fns-tz";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
@ -60,6 +61,12 @@ class DialogCalendarEventEditor extends LitElement {
|
|||||||
|
|
||||||
@state() private _submitting = false;
|
@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 {
|
public showDialog(params: CalendarEventEditDialogParams): void {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._info = undefined;
|
this._info = undefined;
|
||||||
@ -71,6 +78,9 @@ class DialogCalendarEventEditor extends LitElement {
|
|||||||
computeStateDomain(stateObj) === "calendar" &&
|
computeStateDomain(stateObj) === "calendar" &&
|
||||||
supportsFeature(stateObj, CalendarEntityFeature.CREATE_EVENT)
|
supportsFeature(stateObj, CalendarEntityFeature.CREATE_EVENT)
|
||||||
)?.entity_id;
|
)?.entity_id;
|
||||||
|
this._timeZone =
|
||||||
|
Intl.DateTimeFormat().resolvedOptions().timeZone ||
|
||||||
|
this.hass.config.time_zone;
|
||||||
if (params.entry) {
|
if (params.entry) {
|
||||||
const entry = params.entry!;
|
const entry = params.entry!;
|
||||||
this._allDay = isDate(entry.dtstart);
|
this._allDay = isDate(entry.dtstart);
|
||||||
@ -281,20 +291,30 @@ class DialogCalendarEventEditor extends LitElement {
|
|||||||
private _isEditableCalendar = (entityStateObj: HassEntity) =>
|
private _isEditableCalendar = (entityStateObj: HassEntity) =>
|
||||||
supportsFeature(entityStateObj, CalendarEntityFeature.CREATE_EVENT);
|
supportsFeature(entityStateObj, CalendarEntityFeature.CREATE_EVENT);
|
||||||
|
|
||||||
private _getLocaleStrings = memoizeOne((startDate?: Date, endDate?: Date) =>
|
private _getLocaleStrings = memoizeOne(
|
||||||
// en-CA locale used for date format YYYY-MM-DD
|
(startDate?: Date, endDate?: Date) => ({
|
||||||
// en-GB locale used for 24h time format HH:MM:SS
|
startDate: this._formatDate(startDate!),
|
||||||
{
|
startTime: this._formatTime(startDate!),
|
||||||
const timeZone = this.hass.config.time_zone;
|
endDate: this._formatDate(endDate!),
|
||||||
return {
|
endTime: this._formatTime(endDate!),
|
||||||
startDate: startDate?.toLocaleDateString("en-CA", { timeZone }),
|
})
|
||||||
startTime: startDate?.toLocaleTimeString("en-GB", { timeZone }),
|
|
||||||
endDate: endDate?.toLocaleDateString("en-CA", { timeZone }),
|
|
||||||
endTime: endDate?.toLocaleTimeString("en-GB", { timeZone }),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 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() {
|
private _clearInfo() {
|
||||||
this._info = undefined;
|
this._info = undefined;
|
||||||
}
|
}
|
||||||
@ -319,27 +339,14 @@ class DialogCalendarEventEditor extends LitElement {
|
|||||||
// Store previous event duration
|
// Store previous event duration
|
||||||
const duration = differenceInMilliseconds(this._dtend!, this._dtstart!);
|
const duration = differenceInMilliseconds(this._dtend!, this._dtstart!);
|
||||||
|
|
||||||
this._dtstart = new Date(
|
this._dtstart = this._parseDate(
|
||||||
ev.detail.value +
|
`${ev.detail.value}T${this._formatTime(this._dtstart!)}`
|
||||||
"T" +
|
|
||||||
this._dtstart!.toLocaleTimeString("en-GB", {
|
|
||||||
timeZone: this.hass.config.time_zone,
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Prevent that the end time can be before the start time. Try to keep the
|
// Prevent that the end time can be before the start time. Try to keep the
|
||||||
// duration the same.
|
// duration the same.
|
||||||
if (this._dtend! <= this._dtstart!) {
|
if (this._dtend! <= this._dtstart!) {
|
||||||
const newEnd = addMilliseconds(this._dtstart, duration);
|
this._dtend = 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._info = this.hass.localize(
|
this._info = this.hass.localize(
|
||||||
"ui.components.calendar.event.end_auto_adjusted"
|
"ui.components.calendar.event.end_auto_adjusted"
|
||||||
);
|
);
|
||||||
@ -347,12 +354,8 @@ class DialogCalendarEventEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _endDateChanged(ev: CustomEvent) {
|
private _endDateChanged(ev: CustomEvent) {
|
||||||
this._dtend = new Date(
|
this._dtend = this._parseDate(
|
||||||
ev.detail.value +
|
`${ev.detail.value}T${this._formatTime(this._dtend!)}`
|
||||||
"T" +
|
|
||||||
this._dtend!.toLocaleTimeString("en-GB", {
|
|
||||||
timeZone: this.hass.config.time_zone,
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,25 +363,14 @@ class DialogCalendarEventEditor extends LitElement {
|
|||||||
// Store previous event duration
|
// Store previous event duration
|
||||||
const duration = differenceInMilliseconds(this._dtend!, this._dtstart!);
|
const duration = differenceInMilliseconds(this._dtend!, this._dtstart!);
|
||||||
|
|
||||||
this._dtstart = new Date(
|
this._dtstart = this._parseDate(
|
||||||
this._dtstart!.toLocaleDateString("en-CA", {
|
`${this._formatDate(this._dtstart!)}T${ev.detail.value}`
|
||||||
timeZone: this.hass.config.time_zone,
|
|
||||||
}) +
|
|
||||||
"T" +
|
|
||||||
ev.detail.value
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Prevent that the end time can be before the start time. Try to keep the
|
// Prevent that the end time can be before the start time. Try to keep the
|
||||||
// duration the same.
|
// duration the same.
|
||||||
if (this._dtend! <= this._dtstart!) {
|
if (this._dtend! <= this._dtstart!) {
|
||||||
const newEnd = addMilliseconds(new Date(this._dtstart), duration);
|
this._dtend = 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._info = this.hass.localize(
|
this._info = this.hass.localize(
|
||||||
"ui.components.calendar.event.end_auto_adjusted"
|
"ui.components.calendar.event.end_auto_adjusted"
|
||||||
);
|
);
|
||||||
@ -386,36 +378,32 @@ class DialogCalendarEventEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _endTimeChanged(ev: CustomEvent) {
|
private _endTimeChanged(ev: CustomEvent) {
|
||||||
this._dtend = new Date(
|
this._dtend = this._parseDate(
|
||||||
this._dtend!.toLocaleDateString("en-CA", {
|
`${this._formatDate(this._dtend!)}T${ev.detail.value}`
|
||||||
timeZone: this.hass.config.time_zone,
|
|
||||||
}) +
|
|
||||||
"T" +
|
|
||||||
ev.detail.value
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _calculateData() {
|
private _calculateData() {
|
||||||
const { startDate, startTime, endDate, endTime } = this._getLocaleStrings(
|
|
||||||
this._dtstart,
|
|
||||||
this._dtend
|
|
||||||
);
|
|
||||||
const data: CalendarEventMutableParams = {
|
const data: CalendarEventMutableParams = {
|
||||||
summary: this._summary,
|
summary: this._summary,
|
||||||
description: this._description,
|
description: this._description,
|
||||||
rrule: this._rrule,
|
rrule: this._rrule || undefined,
|
||||||
dtstart: "",
|
dtstart: "",
|
||||||
dtend: "",
|
dtend: "",
|
||||||
};
|
};
|
||||||
if (this._allDay) {
|
if (this._allDay) {
|
||||||
data.dtstart = startDate!;
|
data.dtstart = this._formatDate(this._dtstart!);
|
||||||
// End date/time is exclusive when persisted
|
// End date/time is exclusive when persisted
|
||||||
data.dtend = addDays(new Date(this._dtend!), 1).toLocaleDateString(
|
data.dtend = this._formatDate(addDays(this._dtend!, 1));
|
||||||
"en-CA"
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
data.dtstart = `${startDate}T${startTime}`;
|
data.dtstart = `${this._formatDate(
|
||||||
data.dtend = `${endDate}T${endTime}`;
|
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;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import "@material/mwc-button";
|
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 { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../../../components/ha-card";
|
import "../../../../../components/ha-card";
|
||||||
import {
|
import {
|
||||||
@ -13,6 +13,7 @@ import { haStyle } from "../../../../../resources/styles";
|
|||||||
import { HomeAssistant } from "../../../../../types";
|
import { HomeAssistant } from "../../../../../types";
|
||||||
import "../../../../../components/ha-alert";
|
import "../../../../../components/ha-alert";
|
||||||
import { showPromptDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
import { showPromptDialog } from "../../../../../dialogs/generic/show-dialog-box";
|
||||||
|
import { navigate } from "../../../../../common/navigate";
|
||||||
|
|
||||||
@customElement("matter-config-panel")
|
@customElement("matter-config-panel")
|
||||||
export class MatterConfigPanel extends LitElement {
|
export class MatterConfigPanel extends LitElement {
|
||||||
@ -22,6 +23,8 @@ export class MatterConfigPanel extends LitElement {
|
|||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
|
private _curMatterDevices?: Set<string>;
|
||||||
|
|
||||||
private get _canCommissionMatter() {
|
private get _canCommissionMatter() {
|
||||||
return this.hass.auth.external?.config.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() {
|
private _startMobileCommissioning() {
|
||||||
|
this._redirectOnNewDevice();
|
||||||
this.hass.auth.external!.fireMessage({
|
this.hass.auth.external!.fireMessage({
|
||||||
type: "matter/commission",
|
type: "matter/commission",
|
||||||
});
|
});
|
||||||
@ -112,6 +138,7 @@ export class MatterConfigPanel extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
|
this._redirectOnNewDevice();
|
||||||
try {
|
try {
|
||||||
await commissionMatterDevice(this.hass, code);
|
await commissionMatterDevice(this.hass, code);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@ -130,6 +157,7 @@ export class MatterConfigPanel extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
|
this._redirectOnNewDevice();
|
||||||
try {
|
try {
|
||||||
await acceptSharedMatterDevice(this.hass, Number(code));
|
await acceptSharedMatterDevice(this.hass, Number(code));
|
||||||
} catch (err: any) {
|
} 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 = [
|
static styles = [
|
||||||
haStyle,
|
haStyle,
|
||||||
css`
|
css`
|
||||||
|
@ -50,28 +50,30 @@ class HaPanelDevMqtt extends LitElement {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ha-textfield
|
<div class="panel-dev-mqtt-fields">
|
||||||
.label=${this.hass.localize("ui.panel.config.mqtt.topic")}
|
<ha-textfield
|
||||||
.value=${this.topic}
|
.label=${this.hass.localize("ui.panel.config.mqtt.topic")}
|
||||||
@change=${this._handleTopic}
|
.value=${this.topic}
|
||||||
></ha-textfield>
|
@change=${this._handleTopic}
|
||||||
<ha-select
|
></ha-textfield>
|
||||||
.label=${this.hass.localize("ui.panel.config.mqtt.qos")}
|
<ha-select
|
||||||
.value=${this.qos}
|
.label=${this.hass.localize("ui.panel.config.mqtt.qos")}
|
||||||
@selected=${this._handleQos}
|
.value=${this.qos}
|
||||||
>${qosLevel.map(
|
@selected=${this._handleQos}
|
||||||
(qos) =>
|
>${qosLevel.map(
|
||||||
html`<mwc-list-item .value=${qos}>${qos}</mwc-list-item>`
|
(qos) =>
|
||||||
)}
|
html`<mwc-list-item .value=${qos}>${qos}</mwc-list-item>`
|
||||||
</ha-select>
|
)}
|
||||||
<ha-formfield
|
</ha-select>
|
||||||
label=${this.hass!.localize("ui.panel.config.mqtt.retain")}
|
<ha-formfield
|
||||||
>
|
label=${this.hass!.localize("ui.panel.config.mqtt.retain")}
|
||||||
<ha-switch
|
>
|
||||||
@change=${this._handleRetain}
|
<ha-switch
|
||||||
.checked=${this.retain}
|
@change=${this._handleRetain}
|
||||||
></ha-switch>
|
.checked=${this.retain}
|
||||||
</ha-formfield>
|
></ha-switch>
|
||||||
|
</ha-formfield>
|
||||||
|
</div>
|
||||||
<p>${this.hass.localize("ui.panel.config.mqtt.payload")}</p>
|
<p>${this.hass.localize("ui.panel.config.mqtt.payload")}</p>
|
||||||
<ha-code-editor
|
<ha-code-editor
|
||||||
mode="jinja2"
|
mode="jinja2"
|
||||||
@ -160,6 +162,28 @@ class HaPanelDevMqtt extends LitElement {
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
direction: ltr;
|
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 {
|
ha-card:first-child {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
@ -47,32 +47,35 @@ class MqttSubscribeCard extends LitElement {
|
|||||||
header=${this.hass.localize("ui.panel.config.mqtt.description_listen")}
|
header=${this.hass.localize("ui.panel.config.mqtt.description_listen")}
|
||||||
>
|
>
|
||||||
<form>
|
<form>
|
||||||
<ha-textfield
|
<div class="panel-dev-mqtt-subscribe-fields">
|
||||||
.label=${this._subscribed
|
<ha-textfield
|
||||||
? this.hass.localize("ui.panel.config.mqtt.listening_to")
|
.label=${this._subscribed
|
||||||
: this.hass.localize("ui.panel.config.mqtt.subscribe_to")}
|
? this.hass.localize("ui.panel.config.mqtt.listening_to")
|
||||||
.disabled=${this._subscribed !== undefined}
|
: this.hass.localize("ui.panel.config.mqtt.subscribe_to")}
|
||||||
.value=${this._topic}
|
.disabled=${this._subscribed !== undefined}
|
||||||
@change=${this._handleTopic}
|
.value=${this._topic}
|
||||||
></ha-textfield>
|
@change=${this._handleTopic}
|
||||||
<ha-select
|
></ha-textfield>
|
||||||
.label=${this.hass.localize("ui.panel.config.mqtt.qos")}
|
<ha-select
|
||||||
.disabled=${this._subscribed !== undefined}
|
.label=${this.hass.localize("ui.panel.config.mqtt.qos")}
|
||||||
.value=${this._qos}
|
.disabled=${this._subscribed !== undefined}
|
||||||
@selected=${this._handleQos}
|
.value=${this._qos}
|
||||||
>${qosLevel.map(
|
@selected=${this._handleQos}
|
||||||
(qos) => html`<mwc-list-item .value=${qos}>${qos}</mwc-list-item>`
|
>${qosLevel.map(
|
||||||
)}
|
(qos) =>
|
||||||
</ha-select>
|
html`<mwc-list-item .value=${qos}>${qos}</mwc-list-item>`
|
||||||
<mwc-button
|
)}
|
||||||
.disabled=${this._topic === ""}
|
</ha-select>
|
||||||
@click=${this._handleSubmit}
|
<mwc-button
|
||||||
type="submit"
|
.disabled=${this._topic === ""}
|
||||||
>
|
@click=${this._handleSubmit}
|
||||||
${this._subscribed
|
type="submit"
|
||||||
? this.hass.localize("ui.panel.config.mqtt.stop_listening")
|
>
|
||||||
: this.hass.localize("ui.panel.config.mqtt.start_listening")}
|
${this._subscribed
|
||||||
</mwc-button>
|
? this.hass.localize("ui.panel.config.mqtt.stop_listening")
|
||||||
|
: this.hass.localize("ui.panel.config.mqtt.start_listening")}
|
||||||
|
</mwc-button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="events">
|
<div class="events">
|
||||||
${this._messages.map(
|
${this._messages.map(
|
||||||
@ -170,6 +173,28 @@ class MqttSubscribeCard extends LitElement {
|
|||||||
pre {
|
pre {
|
||||||
font-family: var(--code-font-family, monospace);
|
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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,9 @@ export class HuiEnergySourcesTableCard
|
|||||||
? html`<tr class="mdc-data-table__row total">
|
? html`<tr class="mdc-data-table__row total">
|
||||||
<td class="mdc-data-table__cell"></td>
|
<td class="mdc-data-table__cell"></td>
|
||||||
<th class="mdc-data-table__cell" scope="row">
|
<th class="mdc-data-table__cell" scope="row">
|
||||||
Solar total
|
${this.hass.localize(
|
||||||
|
"ui.panel.lovelace.cards.energy.energy_sources_table.solar_total"
|
||||||
|
)}
|
||||||
</th>
|
</th>
|
||||||
${compare
|
${compare
|
||||||
? html`<td
|
? html`<td
|
||||||
|
@ -295,7 +295,7 @@ class HuiAlarmPanelCard extends LitElement implements LovelaceCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.triggered {
|
.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;
|
animation: pulse 1s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||||||
this._stateHistory = {
|
this._stateHistory = {
|
||||||
...(await getRecentWithCache(
|
...(await getRecentWithCache(
|
||||||
this.hass!,
|
this.hass!,
|
||||||
this._cacheConfig!.cacheKey,
|
this._configEntities!.map((config) => config.entity),
|
||||||
this._cacheConfig!,
|
this._cacheConfig!,
|
||||||
this.hass!.localize,
|
this.hass!.localize,
|
||||||
this.hass!.language
|
this.hass!.language
|
||||||
|
@ -24,6 +24,8 @@ import { classMap } from "lit/directives/class-map";
|
|||||||
import { UNIT_F } from "../../../common/const";
|
import { UNIT_F } from "../../../common/const";
|
||||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
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 { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { formatNumber } from "../../../common/number/format_number";
|
import { formatNumber } from "../../../common/number/format_number";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
@ -213,11 +215,17 @@ export class HuiThermostatCard extends LitElement implements LovelaceCard {
|
|||||||
>
|
>
|
||||||
${
|
${
|
||||||
stateObj.attributes.hvac_action
|
stateObj.attributes.hvac_action
|
||||||
? this.hass!.localize(
|
? computeAttributeValueDisplay(
|
||||||
`state_attributes.climate.hvac_action.${stateObj.attributes.hvac_action}`
|
this.hass.localize,
|
||||||
|
stateObj,
|
||||||
|
this.hass.entities,
|
||||||
|
"hvac_action"
|
||||||
)
|
)
|
||||||
: this.hass!.localize(
|
: computeStateDisplay(
|
||||||
`component.climate.state._.${stateObj.state}`
|
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
|
stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
|
||||||
? html`
|
? html`
|
||||||
-
|
-
|
||||||
${this.hass!.localize(
|
${computeAttributeValueDisplay(
|
||||||
`state_attributes.climate.preset_mode.${stateObj.attributes.preset_mode}`
|
this.hass.localize,
|
||||||
) || stateObj.attributes.preset_mode}
|
stateObj,
|
||||||
|
this.hass.entities,
|
||||||
|
"preset_mode"
|
||||||
|
)}
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
|
@ -193,7 +193,7 @@ export class HuiImage extends LitElement {
|
|||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
paddingBottom: useRatio
|
paddingBottom: useRatio
|
||||||
? `${((100 * this._ratio!.h) / this._ratio!.w).toFixed(2)}%`
|
? `${((100 * this._ratio!.h) / this._ratio!.w).toFixed(2)}%`
|
||||||
: !this._lastImageHeight
|
: this._lastImageHeight === undefined
|
||||||
? "56.25%"
|
? "56.25%"
|
||||||
: undefined,
|
: undefined,
|
||||||
backgroundImage:
|
backgroundImage:
|
||||||
@ -206,7 +206,7 @@ export class HuiImage extends LitElement {
|
|||||||
: undefined,
|
: undefined,
|
||||||
})}
|
})}
|
||||||
class="container ${classMap({
|
class="container ${classMap({
|
||||||
ratio: useRatio || !this._lastImageHeight,
|
ratio: useRatio || this._lastImageHeight === undefined,
|
||||||
})}"
|
})}"
|
||||||
>
|
>
|
||||||
${this.cameraImage && this.cameraView === "live"
|
${this.cameraImage && this.cameraView === "live"
|
||||||
|
@ -38,9 +38,11 @@ import { fireEvent } from "../../common/dom/fire_event";
|
|||||||
import scrollToTarget from "../../common/dom/scroll-to-target";
|
import scrollToTarget from "../../common/dom/scroll-to-target";
|
||||||
import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event";
|
import { shouldHandleRequestSelectedEvent } from "../../common/mwc/handle-request-selected-event";
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
|
import { constructUrlCurrentPath } from "../../common/url/construct-url";
|
||||||
import {
|
import {
|
||||||
addSearchParam,
|
addSearchParam,
|
||||||
extractSearchParam,
|
extractSearchParamsObject,
|
||||||
|
removeSearchParam,
|
||||||
} from "../../common/url/search-params";
|
} from "../../common/url/search-params";
|
||||||
import { computeRTLDirection } from "../../common/util/compute_rtl";
|
import { computeRTLDirection } from "../../common/util/compute_rtl";
|
||||||
import { debounce } from "../../common/util/debounce";
|
import { debounce } from "../../common/util/debounce";
|
||||||
@ -556,8 +558,16 @@ class HUIRoot extends LitElement {
|
|||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
// Check for requested edit mode
|
// Check for requested edit mode
|
||||||
if (extractSearchParam("edit") === "1") {
|
const searchParams = extractSearchParamsObject();
|
||||||
|
if (searchParams.edit === "1") {
|
||||||
this.lovelace!.setEditMode(true);
|
this.lovelace!.setEditMode(true);
|
||||||
|
} else if (searchParams.conversation === "1") {
|
||||||
|
showVoiceCommandDialog(this);
|
||||||
|
window.history.replaceState(
|
||||||
|
null,
|
||||||
|
"",
|
||||||
|
constructUrlCurrentPath(removeSearchParam("conversation"))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,10 @@ export const getMyRedirects = (hasSupervisor: boolean): Redirects => ({
|
|||||||
server_controls: {
|
server_controls: {
|
||||||
redirect: "/developer-tools/yaml",
|
redirect: "/developer-tools/yaml",
|
||||||
},
|
},
|
||||||
|
calendar: {
|
||||||
|
component: "calendar",
|
||||||
|
redirect: "/calendar",
|
||||||
|
},
|
||||||
config: {
|
config: {
|
||||||
redirect: "/config/dashboard",
|
redirect: "/config/dashboard",
|
||||||
},
|
},
|
||||||
|
@ -22,31 +22,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"state_attributes": {
|
"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": {
|
"humidifier": {
|
||||||
"mode": {
|
"mode": {
|
||||||
"normal": "Normal",
|
"normal": "Normal",
|
||||||
@ -140,7 +115,6 @@
|
|||||||
"climate": {
|
"climate": {
|
||||||
"currently": "Currently",
|
"currently": "Currently",
|
||||||
"on_off": "On / off",
|
"on_off": "On / off",
|
||||||
"target_temperature": "Target temperature",
|
|
||||||
"target_temperature_entity": "{name} target temperature",
|
"target_temperature_entity": "{name} target temperature",
|
||||||
"target_temperature_mode": "{name} target temperature {mode}",
|
"target_temperature_mode": "{name} target temperature {mode}",
|
||||||
"current_temperature": "{name} current temperature",
|
"current_temperature": "{name} current temperature",
|
||||||
@ -148,13 +122,8 @@
|
|||||||
"cooling": "{name} cooling",
|
"cooling": "{name} cooling",
|
||||||
"high": "high",
|
"high": "high",
|
||||||
"low": "low",
|
"low": "low",
|
||||||
"target_humidity": "Target humidity",
|
|
||||||
"operation": "Operation",
|
"operation": "Operation",
|
||||||
"fan_mode": "Fan mode",
|
"away_mode": "Away mode"
|
||||||
"swing_mode": "Swing mode",
|
|
||||||
"preset_mode": "Preset",
|
|
||||||
"away_mode": "Away mode",
|
|
||||||
"aux_heat": "Aux heat"
|
|
||||||
},
|
},
|
||||||
"counter": {
|
"counter": {
|
||||||
"actions": {
|
"actions": {
|
||||||
@ -3225,7 +3194,7 @@
|
|||||||
"mqtt": {
|
"mqtt": {
|
||||||
"title": "MQTT",
|
"title": "MQTT",
|
||||||
"description_publish": "Publish a packet",
|
"description_publish": "Publish a packet",
|
||||||
"topic": "topic",
|
"topic": "Topic",
|
||||||
"payload": "Payload (template allowed)",
|
"payload": "Payload (template allowed)",
|
||||||
"publish": "Publish",
|
"publish": "Publish",
|
||||||
"description_listen": "Listen to a topic",
|
"description_listen": "Listen to a topic",
|
||||||
@ -3808,6 +3777,7 @@
|
|||||||
"energy_sources_table": {
|
"energy_sources_table": {
|
||||||
"grid_total": "Grid total",
|
"grid_total": "Grid total",
|
||||||
"gas_total": "Gas total",
|
"gas_total": "Gas total",
|
||||||
|
"solar_total": "Solar total",
|
||||||
"water_total": "Water total",
|
"water_total": "Water total",
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
"energy": "Energy",
|
"energy": "Energy",
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -6993,6 +6993,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"date-fns@npm:^2.23.0":
|
||||||
version: 2.23.0
|
version: 2.23.0
|
||||||
resolution: "date-fns@npm:2.23.0"
|
resolution: "date-fns@npm:2.23.0"
|
||||||
@ -9427,6 +9436,7 @@ fsevents@^1.2.7:
|
|||||||
core-js: ^3.15.2
|
core-js: ^3.15.2
|
||||||
cropperjs: ^1.5.12
|
cropperjs: ^1.5.12
|
||||||
date-fns: ^2.23.0
|
date-fns: ^2.23.0
|
||||||
|
date-fns-tz: ^1.3.7
|
||||||
deep-clone-simple: ^1.1.1
|
deep-clone-simple: ^1.1.1
|
||||||
deep-freeze: ^0.0.1
|
deep-freeze: ^0.0.1
|
||||||
del: ^4.0.0
|
del: ^4.0.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user