mirror of
https://github.com/home-assistant/frontend.git
synced 2025-12-23 00:17:27 +00:00
Compare commits
102 Commits
checkbox-i
...
20251203.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b0bd9d577 | ||
|
|
d839152fd1 | ||
|
|
407cb79805 | ||
|
|
7817ebe983 | ||
|
|
7e58cedd49 | ||
|
|
06334a039c | ||
|
|
6e5853a1c0 | ||
|
|
f4f4520773 | ||
|
|
94453dfba5 | ||
|
|
0ce0247a2c | ||
|
|
ce8cabbad9 | ||
|
|
0802841606 | ||
|
|
cb93e1b741 | ||
|
|
30c383a2fc | ||
|
|
73ee235fef | ||
|
|
31603ea7b2 | ||
|
|
17c1043cfc | ||
|
|
da255dce40 | ||
|
|
0c68072f8f | ||
|
|
d197fd8f76 | ||
|
|
a961a87872 | ||
|
|
cc96c707b9 | ||
|
|
4b73713f2a | ||
|
|
c001102f15 | ||
|
|
c1e5e0bfcb | ||
|
|
a1412e90fd | ||
|
|
f6f40c1679 | ||
|
|
d77bebe96b | ||
|
|
1260af0b45 | ||
|
|
1d37eec411 | ||
|
|
5a52f83358 | ||
|
|
60724eb952 | ||
|
|
de5778079e | ||
|
|
f3710650f2 | ||
|
|
feb35dbc4f | ||
|
|
ee9e101fa6 | ||
|
|
24b16360a6 | ||
|
|
109c81a00d | ||
|
|
eaa1ddbf61 | ||
|
|
b11cb57a1e | ||
|
|
87b5f58779 | ||
|
|
8dac53c672 | ||
|
|
d0966bf35a | ||
|
|
6ba4fc0808 | ||
|
|
bd582ff816 | ||
|
|
d34bf83da0 | ||
|
|
b0cfb31bf3 | ||
|
|
6c39e5d2c5 | ||
|
|
7b51e71092 | ||
|
|
8a82483685 | ||
|
|
bb691fa7a2 | ||
|
|
2232db9c0f | ||
|
|
5375665dc6 | ||
|
|
480122f40a | ||
|
|
ee5c54030a | ||
|
|
b73f50e864 | ||
|
|
b9836073b7 | ||
|
|
a40512e0b5 | ||
|
|
b2122570fb | ||
|
|
885f9333d2 | ||
|
|
f812e7e9fb | ||
|
|
64dad39f6e | ||
|
|
df0fb423ed | ||
|
|
4c3156f290 | ||
|
|
ecdf374902 | ||
|
|
3e924e0cde | ||
|
|
6fb71e12c8 | ||
|
|
6138aa5489 | ||
|
|
61e865d3a6 | ||
|
|
febcbf6242 | ||
|
|
6a2fac6a9e | ||
|
|
b60c5467fc | ||
|
|
ecd563406e | ||
|
|
d5b66315e2 | ||
|
|
5b1719fc6e | ||
|
|
add22cf2e9 | ||
|
|
21509191fa | ||
|
|
1a73cccf0d | ||
|
|
407d68250a | ||
|
|
38b7bd18bb | ||
|
|
a00e944a35 | ||
|
|
481569804e | ||
|
|
a1d7e270ff | ||
|
|
225ccf1d2f | ||
|
|
4a5e1f9f3f | ||
|
|
b27b7210fd | ||
|
|
acd5181449 | ||
|
|
b6b2d03a80 | ||
|
|
7aee2b7cb7 | ||
|
|
df1914cb7a | ||
|
|
6706d5904d | ||
|
|
221aefd764 | ||
|
|
670057e8e6 | ||
|
|
427e46201c | ||
|
|
fd1240f335 | ||
|
|
aa7670cb59 | ||
|
|
468139229c | ||
|
|
39752f0e3f | ||
|
|
4d850d067f | ||
|
|
bcae64df88 | ||
|
|
690fd5a061 | ||
|
|
ac56c6df9a |
6
.github/workflows/codeql-analysis.yml
vendored
6
.github/workflows/codeql-analysis.yml
vendored
@@ -36,14 +36,14 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
uses: github/codeql-action/init@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
|
|
||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
uses: github/codeql-action/autobuild@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -57,4 +57,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5
|
uses: github/codeql-action/analyze@e12f0178983d466f2f6028f5cc7a6d786fd97f4b # v4.31.4
|
||||||
|
|||||||
2
.github/workflows/nightly.yaml
vendored
2
.github/workflows/nightly.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/relative-ci.yaml
vendored
2
.github/workflows/relative-ci.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Send bundle stats and build information to RelativeCI
|
- name: Send bundle stats and build information to RelativeCI
|
||||||
uses: relative-ci/agent-action@c45aaa919ef85620af54242a241ac17a8fa35983 # v3.2.1
|
uses: relative-ci/agent-action@feb19ddc698445db27401f1490f6ac182da0816f # v3.2.0
|
||||||
with:
|
with:
|
||||||
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
|
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|||||||
10
.github/workflows/release.yaml
vendored
10
.github/workflows/release.yaml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||||
|
|
||||||
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
- name: Set up Python ${{ env.PYTHON_VERSION }}
|
||||||
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
|
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.PYTHON_VERSION }}
|
python-version: ${{ env.PYTHON_VERSION }}
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ jobs:
|
|||||||
script/release
|
script/release
|
||||||
|
|
||||||
- name: Upload release assets
|
- name: Upload release assets
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
dist/*.whl
|
dist/*.whl
|
||||||
@@ -75,7 +75,7 @@ jobs:
|
|||||||
|
|
||||||
# home-assistant/wheels doesn't support SHA pinning
|
# home-assistant/wheels doesn't support SHA pinning
|
||||||
- name: Build wheels
|
- name: Build wheels
|
||||||
uses: home-assistant/wheels@2025.11.0
|
uses: home-assistant/wheels@2025.10.0
|
||||||
with:
|
with:
|
||||||
abi: cp313
|
abi: cp313
|
||||||
tag: musllinux_1_2
|
tag: musllinux_1_2
|
||||||
@@ -108,7 +108,7 @@ jobs:
|
|||||||
- name: Tar folder
|
- name: Tar folder
|
||||||
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
|
run: tar -czf landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz -C landing-page/dist .
|
||||||
- name: Upload release asset
|
- name: Upload release asset
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||||
with:
|
with:
|
||||||
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
|
files: landing-page/home_assistant_frontend_landingpage-${{ github.event.release.tag_name }}.tar.gz
|
||||||
|
|
||||||
@@ -137,6 +137,6 @@ jobs:
|
|||||||
- name: Tar folder
|
- name: Tar folder
|
||||||
run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build .
|
run: tar -czf hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz -C hassio/build .
|
||||||
- name: Upload release asset
|
- name: Upload release asset
|
||||||
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||||
with:
|
with:
|
||||||
files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz
|
files: hassio/home_assistant_frontend_supervisor-${{ github.event.release.tag_name }}.tar.gz
|
||||||
|
|||||||
@@ -156,7 +156,9 @@ const createTestTranslation = () =>
|
|||||||
*/
|
*/
|
||||||
const createMasterTranslation = () =>
|
const createMasterTranslation = () =>
|
||||||
gulp
|
gulp
|
||||||
.src([EN_SRC, ...(mergeBackend ? [`${inBackendDir}/en.json`] : [])])
|
.src([EN_SRC, ...(mergeBackend ? [`${inBackendDir}/en.json`] : [])], {
|
||||||
|
allowEmpty: true,
|
||||||
|
})
|
||||||
.pipe(new CustomJSON(lokaliseTransform))
|
.pipe(new CustomJSON(lokaliseTransform))
|
||||||
.pipe(new MergeJSON("en"))
|
.pipe(new MergeJSON("en"))
|
||||||
.pipe(gulp.dest(workDir));
|
.pipe(gulp.dest(workDir));
|
||||||
|
|||||||
@@ -305,8 +305,9 @@ export class HcMain extends HassElement {
|
|||||||
await llColl.refresh();
|
await llColl.refresh();
|
||||||
this._unsubLovelace = llColl.subscribe(async (rawConfig) => {
|
this._unsubLovelace = llColl.subscribe(async (rawConfig) => {
|
||||||
if (isStrategyDashboard(rawConfig)) {
|
if (isStrategyDashboard(rawConfig)) {
|
||||||
const { generateLovelaceDashboardStrategy } =
|
const { generateLovelaceDashboardStrategy } = await import(
|
||||||
await import("../../../../src/panels/lovelace/strategies/get-strategy");
|
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||||
|
);
|
||||||
const config = await generateLovelaceDashboardStrategy(
|
const config = await generateLovelaceDashboardStrategy(
|
||||||
rawConfig,
|
rawConfig,
|
||||||
this.hass!
|
this.hass!
|
||||||
@@ -346,8 +347,9 @@ export class HcMain extends HassElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _generateDefaultLovelaceConfig() {
|
private async _generateDefaultLovelaceConfig() {
|
||||||
const { generateLovelaceDashboardStrategy } =
|
const { generateLovelaceDashboardStrategy } = await import(
|
||||||
await import("../../../../src/panels/lovelace/strategies/get-strategy");
|
"../../../../src/panels/lovelace/strategies/get-strategy"
|
||||||
|
);
|
||||||
this._handleNewLovelaceConfig(
|
this._handleNewLovelaceConfig(
|
||||||
await generateLovelaceDashboardStrategy(DEFAULT_CONFIG, this.hass!)
|
await generateLovelaceDashboardStrategy(DEFAULT_CONFIG, this.hass!)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -44,18 +44,24 @@ export const mockEnergy = (hass: MockHomeAssistant) => {
|
|||||||
number_energy_price: null,
|
number_energy_price: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
power: [
|
||||||
|
{ stat_rate: "sensor.power_grid" },
|
||||||
|
{ stat_rate: "sensor.power_grid_return" },
|
||||||
|
],
|
||||||
cost_adjustment_day: 0,
|
cost_adjustment_day: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "solar",
|
type: "solar",
|
||||||
stat_energy_from: "sensor.solar_production",
|
stat_energy_from: "sensor.solar_production",
|
||||||
|
stat_rate: "sensor.power_solar",
|
||||||
config_entry_solar_forecast: ["solar_forecast"],
|
config_entry_solar_forecast: ["solar_forecast"],
|
||||||
},
|
},
|
||||||
/* {
|
{
|
||||||
type: "battery",
|
type: "battery",
|
||||||
stat_energy_from: "sensor.battery_output",
|
stat_energy_from: "sensor.battery_output",
|
||||||
stat_energy_to: "sensor.battery_input",
|
stat_energy_to: "sensor.battery_input",
|
||||||
}, */
|
stat_rate: "sensor.power_battery",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "gas",
|
type: "gas",
|
||||||
stat_energy_from: "sensor.energy_gas",
|
stat_energy_from: "sensor.energy_gas",
|
||||||
@@ -63,28 +69,48 @@ export const mockEnergy = (hass: MockHomeAssistant) => {
|
|||||||
entity_energy_price: null,
|
entity_energy_price: null,
|
||||||
number_energy_price: null,
|
number_energy_price: null,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "water",
|
||||||
|
stat_energy_from: "sensor.energy_water",
|
||||||
|
stat_cost: "sensor.energy_water_cost",
|
||||||
|
entity_energy_price: null,
|
||||||
|
number_energy_price: null,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
device_consumption: [
|
device_consumption: [
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_car",
|
stat_consumption: "sensor.energy_car",
|
||||||
|
stat_rate: "sensor.power_car",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_ac",
|
stat_consumption: "sensor.energy_ac",
|
||||||
|
stat_rate: "sensor.power_ac",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_washing_machine",
|
stat_consumption: "sensor.energy_washing_machine",
|
||||||
|
stat_rate: "sensor.power_washing_machine",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_dryer",
|
stat_consumption: "sensor.energy_dryer",
|
||||||
|
stat_rate: "sensor.power_dryer",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_heat_pump",
|
stat_consumption: "sensor.energy_heat_pump",
|
||||||
|
stat_rate: "sensor.power_heat_pump",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
stat_consumption: "sensor.energy_boiler",
|
stat_consumption: "sensor.energy_boiler",
|
||||||
|
stat_rate: "sensor.power_boiler",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
device_consumption_water: [
|
||||||
|
{
|
||||||
|
stat_consumption: "sensor.water_kitchen",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stat_consumption: "sensor.water_garden",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
device_consumption_water: [],
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
hass.mockWS(
|
hass.mockWS(
|
||||||
|
|||||||
@@ -154,6 +154,38 @@ export const energyEntities = () =>
|
|||||||
unit_of_measurement: "EUR",
|
unit_of_measurement: "EUR",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"sensor.power_grid": {
|
||||||
|
entity_id: "sensor.power_grid",
|
||||||
|
state: "500",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
unit_of_measurement: "W",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.power_grid_return": {
|
||||||
|
entity_id: "sensor.power_grid_return",
|
||||||
|
state: "-100",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
unit_of_measurement: "W",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.power_solar": {
|
||||||
|
entity_id: "sensor.power_solar",
|
||||||
|
state: "200",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
unit_of_measurement: "W",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.power_battery": {
|
||||||
|
entity_id: "sensor.power_battery",
|
||||||
|
state: "100",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
unit_of_measurement: "W",
|
||||||
|
},
|
||||||
|
},
|
||||||
"sensor.energy_gas_cost": {
|
"sensor.energy_gas_cost": {
|
||||||
entity_id: "sensor.energy_gas_cost",
|
entity_id: "sensor.energy_gas_cost",
|
||||||
state: "2",
|
state: "2",
|
||||||
@@ -171,6 +203,15 @@ export const energyEntities = () =>
|
|||||||
unit_of_measurement: "m³",
|
unit_of_measurement: "m³",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"sensor.energy_water": {
|
||||||
|
entity_id: "sensor.energy_water",
|
||||||
|
state: "4000",
|
||||||
|
attributes: {
|
||||||
|
last_reset: "1970-01-01T00:00:00:00+00",
|
||||||
|
friendly_name: "Water",
|
||||||
|
unit_of_measurement: "L",
|
||||||
|
},
|
||||||
|
},
|
||||||
"sensor.energy_car": {
|
"sensor.energy_car": {
|
||||||
entity_id: "sensor.energy_car",
|
entity_id: "sensor.energy_car",
|
||||||
state: "4",
|
state: "4",
|
||||||
@@ -225,4 +266,58 @@ export const energyEntities = () =>
|
|||||||
unit_of_measurement: "kWh",
|
unit_of_measurement: "kWh",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"sensor.power_car": {
|
||||||
|
entity_id: "sensor.power_car",
|
||||||
|
state: "40",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
friendly_name: "Electric car",
|
||||||
|
unit_of_measurement: "W",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.power_ac": {
|
||||||
|
entity_id: "sensor.power_ac",
|
||||||
|
state: "30",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
friendly_name: "Air conditioning",
|
||||||
|
unit_of_measurement: "W",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.power_washing_machine": {
|
||||||
|
entity_id: "sensor.power_washing_machine",
|
||||||
|
state: "60",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
friendly_name: "Washing machine",
|
||||||
|
unit_of_measurement: "W",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.power_dryer": {
|
||||||
|
entity_id: "sensor.power_dryer",
|
||||||
|
state: "55",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
friendly_name: "Dryer",
|
||||||
|
unit_of_measurement: "W",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.power_heat_pump": {
|
||||||
|
entity_id: "sensor.power_heat_pump",
|
||||||
|
state: "60",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
friendly_name: "Heat pump",
|
||||||
|
unit_of_measurement: "W",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"sensor.power_boiler": {
|
||||||
|
entity_id: "sensor.power_boiler",
|
||||||
|
state: "70",
|
||||||
|
attributes: {
|
||||||
|
state_class: "measurement",
|
||||||
|
friendly_name: "Boiler",
|
||||||
|
unit_of_measurement: "W",
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,17 +17,15 @@ const generateMeanStatistics = (
|
|||||||
end: Date,
|
end: Date,
|
||||||
// eslint-disable-next-line default-param-last
|
// eslint-disable-next-line default-param-last
|
||||||
period: "5minute" | "hour" | "day" | "month" = "hour",
|
period: "5minute" | "hour" | "day" | "month" = "hour",
|
||||||
initValue: number,
|
|
||||||
maxDiff: number
|
maxDiff: number
|
||||||
): StatisticValue[] => {
|
): StatisticValue[] => {
|
||||||
const statistics: StatisticValue[] = [];
|
const statistics: StatisticValue[] = [];
|
||||||
let currentDate = new Date(start);
|
let currentDate = new Date(start);
|
||||||
currentDate.setMinutes(0, 0, 0);
|
currentDate.setMinutes(0, 0, 0);
|
||||||
let lastVal = initValue;
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
while (end > currentDate && currentDate < now) {
|
while (end > currentDate && currentDate < now) {
|
||||||
const delta = Math.random() * maxDiff;
|
const delta = Math.random() * maxDiff;
|
||||||
const mean = lastVal + delta;
|
const mean = delta;
|
||||||
statistics.push({
|
statistics.push({
|
||||||
start: currentDate.getTime(),
|
start: currentDate.getTime(),
|
||||||
end: currentDate.getTime(),
|
end: currentDate.getTime(),
|
||||||
@@ -38,7 +36,6 @@ const generateMeanStatistics = (
|
|||||||
state: mean,
|
state: mean,
|
||||||
sum: null,
|
sum: null,
|
||||||
});
|
});
|
||||||
lastVal = mean;
|
|
||||||
currentDate =
|
currentDate =
|
||||||
period === "day"
|
period === "day"
|
||||||
? addDays(currentDate, 1)
|
? addDays(currentDate, 1)
|
||||||
@@ -336,7 +333,6 @@ export const mockRecorder = (mockHass: MockHomeAssistant) => {
|
|||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
period,
|
period,
|
||||||
state,
|
|
||||||
state * (state > 80 ? 0.05 : 0.1)
|
state * (state > 80 ? 0.05 : 0.1)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -381,10 +381,6 @@ export class DemoHaWaDialog extends LitElement {
|
|||||||
<td><code>--dialog-z-index</code></td>
|
<td><code>--dialog-z-index</code></td>
|
||||||
<td>Z-index for the dialog.</td>
|
<td>Z-index for the dialog.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td><code>--dialog-surface-position</code></td>
|
|
||||||
<td>CSS position of the dialog surface.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>--dialog-surface-margin-top</code></td>
|
<td><code>--dialog-surface-margin-top</code></td>
|
||||||
<td>Top margin for the dialog surface.</td>
|
<td>Top margin for the dialog surface.</td>
|
||||||
|
|||||||
@@ -88,8 +88,8 @@ class HassioRegistriesDialog extends LitElement {
|
|||||||
<ha-button
|
<ha-button
|
||||||
?disabled=${Boolean(
|
?disabled=${Boolean(
|
||||||
!this._input.registry ||
|
!this._input.registry ||
|
||||||
!this._input.username ||
|
!this._input.username ||
|
||||||
!this._input.password
|
!this._input.password
|
||||||
)}
|
)}
|
||||||
@click=${this._addNewRegistry}
|
@click=${this._addNewRegistry}
|
||||||
appearance="filled"
|
appearance="filled"
|
||||||
|
|||||||
18
package.json
18
package.json
@@ -89,8 +89,8 @@
|
|||||||
"@thomasloven/round-slider": "0.6.0",
|
"@thomasloven/round-slider": "0.6.0",
|
||||||
"@tsparticles/engine": "3.9.1",
|
"@tsparticles/engine": "3.9.1",
|
||||||
"@tsparticles/preset-links": "3.2.0",
|
"@tsparticles/preset-links": "3.2.0",
|
||||||
"@vaadin/combo-box": "24.9.6",
|
"@vaadin/combo-box": "24.9.5",
|
||||||
"@vaadin/vaadin-themable-mixin": "24.9.6",
|
"@vaadin/vaadin-themable-mixin": "24.9.5",
|
||||||
"@vibrant/color": "4.0.0",
|
"@vibrant/color": "4.0.0",
|
||||||
"@vue/web-component-wrapper": "1.3.0",
|
"@vue/web-component-wrapper": "1.3.0",
|
||||||
"@webcomponents/scoped-custom-element-registry": "0.0.10",
|
"@webcomponents/scoped-custom-element-registry": "0.0.10",
|
||||||
@@ -152,13 +152,13 @@
|
|||||||
"@babel/helper-define-polyfill-provider": "0.6.5",
|
"@babel/helper-define-polyfill-provider": "0.6.5",
|
||||||
"@babel/plugin-transform-runtime": "7.28.5",
|
"@babel/plugin-transform-runtime": "7.28.5",
|
||||||
"@babel/preset-env": "7.28.5",
|
"@babel/preset-env": "7.28.5",
|
||||||
"@bundle-stats/plugin-webpack-filter": "4.21.7",
|
"@bundle-stats/plugin-webpack-filter": "4.21.6",
|
||||||
"@lokalise/node-api": "15.4.0",
|
"@lokalise/node-api": "15.4.0",
|
||||||
"@octokit/auth-oauth-device": "8.0.3",
|
"@octokit/auth-oauth-device": "8.0.3",
|
||||||
"@octokit/plugin-retry": "8.0.3",
|
"@octokit/plugin-retry": "8.0.3",
|
||||||
"@octokit/rest": "22.0.1",
|
"@octokit/rest": "22.0.1",
|
||||||
"@rsdoctor/rspack-plugin": "1.3.11",
|
"@rsdoctor/rspack-plugin": "1.3.11",
|
||||||
"@rspack/core": "1.6.5",
|
"@rspack/core": "1.6.4",
|
||||||
"@rspack/dev-server": "1.1.4",
|
"@rspack/dev-server": "1.1.4",
|
||||||
"@types/babel__plugin-transform-runtime": "7.9.5",
|
"@types/babel__plugin-transform-runtime": "7.9.5",
|
||||||
"@types/chromecast-caf-receiver": "6.0.22",
|
"@types/chromecast-caf-receiver": "6.0.22",
|
||||||
@@ -178,7 +178,7 @@
|
|||||||
"@types/tar": "6.1.13",
|
"@types/tar": "6.1.13",
|
||||||
"@types/ua-parser-js": "0.7.39",
|
"@types/ua-parser-js": "0.7.39",
|
||||||
"@types/webspeechapi": "0.0.29",
|
"@types/webspeechapi": "0.0.29",
|
||||||
"@vitest/coverage-v8": "4.0.14",
|
"@vitest/coverage-v8": "4.0.13",
|
||||||
"babel-loader": "10.0.0",
|
"babel-loader": "10.0.0",
|
||||||
"babel-plugin-template-html-minifier": "4.1.0",
|
"babel-plugin-template-html-minifier": "4.1.0",
|
||||||
"browserslist-useragent-regexp": "4.1.3",
|
"browserslist-useragent-regexp": "4.1.3",
|
||||||
@@ -194,7 +194,7 @@
|
|||||||
"eslint-plugin-wc": "3.0.2",
|
"eslint-plugin-wc": "3.0.2",
|
||||||
"fancy-log": "2.0.0",
|
"fancy-log": "2.0.0",
|
||||||
"fs-extra": "11.3.2",
|
"fs-extra": "11.3.2",
|
||||||
"glob": "13.0.0",
|
"glob": "12.0.0",
|
||||||
"gulp": "5.0.1",
|
"gulp": "5.0.1",
|
||||||
"gulp-brotli": "3.0.0",
|
"gulp-brotli": "3.0.0",
|
||||||
"gulp-json-transform": "0.5.0",
|
"gulp-json-transform": "0.5.0",
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
"lodash.template": "4.5.0",
|
"lodash.template": "4.5.0",
|
||||||
"map-stream": "0.0.7",
|
"map-stream": "0.0.7",
|
||||||
"pinst": "3.0.0",
|
"pinst": "3.0.0",
|
||||||
"prettier": "3.7.2",
|
"prettier": "3.6.2",
|
||||||
"rspack-manifest-plugin": "5.2.0",
|
"rspack-manifest-plugin": "5.2.0",
|
||||||
"serve": "14.2.5",
|
"serve": "14.2.5",
|
||||||
"sinon": "21.0.0",
|
"sinon": "21.0.0",
|
||||||
@@ -217,9 +217,9 @@
|
|||||||
"terser-webpack-plugin": "5.3.14",
|
"terser-webpack-plugin": "5.3.14",
|
||||||
"ts-lit-plugin": "2.0.2",
|
"ts-lit-plugin": "2.0.2",
|
||||||
"typescript": "5.9.3",
|
"typescript": "5.9.3",
|
||||||
"typescript-eslint": "8.48.0",
|
"typescript-eslint": "8.47.0",
|
||||||
"vite-tsconfig-paths": "5.1.4",
|
"vite-tsconfig-paths": "5.1.4",
|
||||||
"vitest": "4.0.14",
|
"vitest": "4.0.13",
|
||||||
"webpack-stats-plugin": "1.1.3",
|
"webpack-stats-plugin": "1.1.3",
|
||||||
"webpackbar": "7.0.0",
|
"webpackbar": "7.0.0",
|
||||||
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "home-assistant-frontend"
|
name = "home-assistant-frontend"
|
||||||
version = "20251029.0"
|
version = "20251203.3"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
license-files = ["LICENSE*"]
|
license-files = ["LICENSE*"]
|
||||||
description = "The Home Assistant frontend"
|
description = "The Home Assistant frontend"
|
||||||
|
|||||||
@@ -45,8 +45,9 @@ export const computeFormatFunctions = async (
|
|||||||
formatEntityAttributeName: FormatEntityAttributeNameFunc;
|
formatEntityAttributeName: FormatEntityAttributeNameFunc;
|
||||||
formatEntityName: FormatEntityNameFunc;
|
formatEntityName: FormatEntityNameFunc;
|
||||||
}> => {
|
}> => {
|
||||||
const { computeStateDisplay } =
|
const { computeStateDisplay } = await import(
|
||||||
await import("../entity/compute_state_display");
|
"../entity/compute_state_display"
|
||||||
|
);
|
||||||
const { computeAttributeValueDisplay, computeAttributeNameDisplay } =
|
const { computeAttributeValueDisplay, computeAttributeNameDisplay } =
|
||||||
await import("../entity/compute_attribute_display");
|
await import("../entity/compute_attribute_display");
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { html, LitElement } from "lit";
|
import { html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import type { VisualMapComponentOption } from "echarts/components";
|
import type { VisualMapComponentOption } from "echarts/components";
|
||||||
import type { LineSeriesOption } from "echarts/charts";
|
import type { LineSeriesOption } from "echarts/charts";
|
||||||
import type { YAXisOption } from "echarts/types/dist/shared";
|
import type { YAXisOption } from "echarts/types/dist/shared";
|
||||||
@@ -27,7 +27,6 @@ const safeParseFloat = (value) => {
|
|||||||
return isFinite(parsed) ? parsed : null;
|
return isFinite(parsed) ? parsed : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@customElement("state-history-chart-line")
|
|
||||||
export class StateHistoryChartLine extends LitElement {
|
export class StateHistoryChartLine extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -796,6 +795,7 @@ export class StateHistoryChartLine extends LitElement {
|
|||||||
return Math.abs(value) < 1 ? value : roundingFn(value);
|
return Math.abs(value) < 1 ? value : roundingFn(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
customElements.define("state-history-chart-line", StateHistoryChartLine);
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
|
|||||||
@@ -373,6 +373,7 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
itemName: 3,
|
itemName: 3,
|
||||||
},
|
},
|
||||||
renderItem: this._renderItem,
|
renderItem: this._renderItem,
|
||||||
|
progressive: 0,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -838,10 +838,10 @@ export class HaDataTable extends LitElement {
|
|||||||
} else if (this.sortDirection === "asc") {
|
} else if (this.sortDirection === "asc") {
|
||||||
this.sortDirection = "desc";
|
this.sortDirection = "desc";
|
||||||
} else {
|
} else {
|
||||||
this.sortDirection = "asc";
|
this.sortDirection = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sortColumn = columnId;
|
this.sortColumn = this.sortDirection === null ? undefined : columnId;
|
||||||
|
|
||||||
fireEvent(this, "sorting-changed", {
|
fireEvent(this, "sorting-changed", {
|
||||||
column: columnId,
|
column: columnId,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { mdiAlert } from "@mdi/js";
|
|||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { ifDefined } from "lit/directives/if-defined";
|
import { ifDefined } from "lit/directives/if-defined";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
@@ -17,7 +17,6 @@ import { CLIMATE_HVAC_ACTION_TO_MODE } from "../../data/climate";
|
|||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "../ha-state-icon";
|
import "../ha-state-icon";
|
||||||
|
|
||||||
@customElement("state-badge")
|
|
||||||
export class StateBadge extends LitElement {
|
export class StateBadge extends LitElement {
|
||||||
public hass?: HomeAssistant;
|
public hass?: HomeAssistant;
|
||||||
|
|
||||||
@@ -266,3 +265,5 @@ declare global {
|
|||||||
"state-badge": StateBadge;
|
"state-badge": StateBadge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("state-badge", StateBadge);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { LitElement, html } from "lit";
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { getAreaContext } from "../common/entity/context/get_area_context";
|
import { getAreaContext } from "../common/entity/context/get_area_context";
|
||||||
import { areaCompare } from "../data/area_registry";
|
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import "./ha-expansion-panel";
|
import "./ha-expansion-panel";
|
||||||
import "./ha-items-display-editor";
|
import "./ha-items-display-editor";
|
||||||
@@ -37,11 +36,7 @@ export class HaAreasDisplayEditor extends LitElement {
|
|||||||
public showNavigationButton = false;
|
public showNavigationButton = false;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const compare = areaCompare(this.hass.areas);
|
const areas = Object.values(this.hass.areas);
|
||||||
|
|
||||||
const areas = Object.values(this.hass.areas).sort((areaA, areaB) =>
|
|
||||||
compare(areaA.area_id, areaB.area_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
const items: DisplayItem[] = areas.map((area) => {
|
const items: DisplayItem[] = areas.map((area) => {
|
||||||
const { floor } = getAreaContext(area, this.hass.floors);
|
const { floor } = getAreaContext(area, this.hass.floors);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import memoizeOne from "memoize-one";
|
|||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { computeFloorName } from "../common/entity/compute_floor_name";
|
import { computeFloorName } from "../common/entity/compute_floor_name";
|
||||||
import { getAreaContext } from "../common/entity/context/get_area_context";
|
import { getAreaContext } from "../common/entity/context/get_area_context";
|
||||||
import { areaCompare } from "../data/area_registry";
|
|
||||||
import type { FloorRegistryEntry } from "../data/floor_registry";
|
import type { FloorRegistryEntry } from "../data/floor_registry";
|
||||||
import { getFloors } from "../panels/lovelace/strategies/areas/helpers/areas-strategy-helper";
|
import { getFloors } from "../panels/lovelace/strategies/areas/helpers/areas-strategy-helper";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
@@ -131,11 +130,8 @@ export class HaAreasFloorsDisplayEditor extends LitElement {
|
|||||||
// update items if floors change
|
// update items if floors change
|
||||||
_hassFloors: HomeAssistant["floors"]
|
_hassFloors: HomeAssistant["floors"]
|
||||||
): Record<string, DisplayItem[]> => {
|
): Record<string, DisplayItem[]> => {
|
||||||
const compare = areaCompare(hassAreas);
|
const areas = Object.values(hassAreas);
|
||||||
|
|
||||||
const areas = Object.values(hassAreas).sort((areaA, areaB) =>
|
|
||||||
compare(areaA.area_id, areaB.area_id)
|
|
||||||
);
|
|
||||||
const groupedItems: Record<string, DisplayItem[]> = areas.reduce(
|
const groupedItems: Record<string, DisplayItem[]> = areas.reduce(
|
||||||
(acc, area) => {
|
(acc, area) => {
|
||||||
const { floor } = getAreaContext(area, this.hass.floors);
|
const { floor } = getAreaContext(area, this.hass.floors);
|
||||||
|
|||||||
@@ -659,7 +659,7 @@ export class HaAssistChat extends LitElement {
|
|||||||
--markdown-table-border-color: var(--divider-color);
|
--markdown-table-border-color: var(--divider-color);
|
||||||
--markdown-code-background-color: var(--primary-background-color);
|
--markdown-code-background-color: var(--primary-background-color);
|
||||||
--markdown-code-text-color: var(--primary-text-color);
|
--markdown-code-text-color: var(--primary-text-color);
|
||||||
--markdown-list-indent: 1rem;
|
--markdown-list-indent: 1.15em;
|
||||||
&:not(:has(ha-markdown-element)) {
|
&:not(:has(ha-markdown-element)) {
|
||||||
min-height: 1lh;
|
min-height: 1lh;
|
||||||
min-width: 1lh;
|
min-width: 1lh;
|
||||||
|
|||||||
@@ -167,30 +167,33 @@ export class HaFilterLabels extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _labelSelected(ev: CustomEvent<SelectedDetail<Set<number>>>) {
|
private async _labelSelected(ev: CustomEvent<SelectedDetail<Set<number>>>) {
|
||||||
if (!ev.detail.index.size) {
|
|
||||||
fireEvent(this, "data-table-filter-changed", {
|
|
||||||
value: [],
|
|
||||||
items: undefined,
|
|
||||||
});
|
|
||||||
this.value = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const value: string[] = [];
|
|
||||||
const filteredLabels = this._filteredLabels(
|
const filteredLabels = this._filteredLabels(
|
||||||
this._labels,
|
this._labels,
|
||||||
this._filter,
|
this._filter,
|
||||||
this.value
|
this.value
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const filteredLabelIds = new Set(filteredLabels.map((l) => l.label_id));
|
||||||
|
|
||||||
|
// Keep previously selected labels that are not in the current filtered view
|
||||||
|
const preservedLabels = (this.value || []).filter(
|
||||||
|
(id) => !filteredLabelIds.has(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build the new selection from the filtered labels based on selected indices
|
||||||
|
const newlySelectedLabels: string[] = [];
|
||||||
for (const index of ev.detail.index) {
|
for (const index of ev.detail.index) {
|
||||||
const labelId = filteredLabels[index].label_id;
|
const labelId = filteredLabels[index]?.label_id;
|
||||||
value.push(labelId);
|
if (labelId) {
|
||||||
|
newlySelectedLabels.push(labelId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.value = value;
|
|
||||||
|
const value = [...preservedLabels, ...newlySelectedLabels];
|
||||||
|
this.value = value.length ? value : [];
|
||||||
|
|
||||||
fireEvent(this, "data-table-filter-changed", {
|
fireEvent(this, "data-table-filter-changed", {
|
||||||
value,
|
value: value.length ? value : undefined,
|
||||||
items: undefined,
|
items: undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -344,7 +344,10 @@ export class HaGenericPicker extends LitElement {
|
|||||||
|
|
||||||
wa-popover::part(body) {
|
wa-popover::part(body) {
|
||||||
width: max(var(--body-width), 250px);
|
width: max(var(--body-width), 250px);
|
||||||
max-width: max(var(--body-width), 250px);
|
max-width: var(
|
||||||
|
--ha-generic-picker-max-width,
|
||||||
|
max(var(--body-width), 250px)
|
||||||
|
);
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
height: 70vh;
|
height: 70vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -99,10 +99,7 @@ class HaMarkdownElement extends ReactiveElement {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
render(
|
render(h(unsafeHTML(elements.join(""))), this.renderRoot);
|
||||||
elements.map((e) => h(unsafeHTML(e))),
|
|
||||||
this.renderRoot
|
|
||||||
);
|
|
||||||
|
|
||||||
this._resize();
|
this._resize();
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ export class HaMarkdown extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public cache = false;
|
@property({ type: Boolean }) public cache = false;
|
||||||
|
|
||||||
@query("ha-markdown-element") private _markdownElement!: ReactiveElement;
|
@query("ha-markdown-element") private _markdownElement?: ReactiveElement;
|
||||||
|
|
||||||
protected async getUpdateComplete() {
|
protected async getUpdateComplete() {
|
||||||
const result = await super.getUpdateComplete();
|
const result = await super.getUpdateComplete();
|
||||||
await this._markdownElement.updateComplete;
|
await this._markdownElement?.updateComplete;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +74,6 @@ export class HaMarkdown extends LitElement {
|
|||||||
background-color: var(--markdown-image-background-color);
|
background-color: var(--markdown-image-background-color);
|
||||||
border-radius: var(--markdown-image-border-radius);
|
border-radius: var(--markdown-image-border-radius);
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
|
||||||
width: auto;
|
|
||||||
transition: height 0.2s ease-in-out;
|
|
||||||
}
|
}
|
||||||
p:first-child > img:first-child {
|
p:first-child > img:first-child {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
@@ -84,8 +81,7 @@ export class HaMarkdown extends LitElement {
|
|||||||
p:first-child > img:last-child {
|
p:first-child > img:last-child {
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
:host > ul,
|
ha-markdown-element > :is(ol, ul) {
|
||||||
:host > ol {
|
|
||||||
padding-inline-start: var(--markdown-list-indent, revert);
|
padding-inline-start: var(--markdown-list-indent, revert);
|
||||||
}
|
}
|
||||||
li {
|
li {
|
||||||
@@ -136,6 +132,18 @@ export class HaMarkdown extends LitElement {
|
|||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
margin: var(--ha-space-4) 0;
|
margin: var(--ha-space-4) 0;
|
||||||
}
|
}
|
||||||
|
table[role="presentation"] {
|
||||||
|
--markdown-table-border-collapse: separate;
|
||||||
|
--markdown-table-border-width: attr(border, 0);
|
||||||
|
--markdown-table-padding-inline: 0;
|
||||||
|
--markdown-table-padding-block: 0;
|
||||||
|
th {
|
||||||
|
vertical-align: attr(align, center);
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
vertical-align: attr(align, left);
|
||||||
|
}
|
||||||
|
}
|
||||||
table {
|
table {
|
||||||
border-collapse: var(--markdown-table-border-collapse, collapse);
|
border-collapse: var(--markdown-table-border-collapse, collapse);
|
||||||
}
|
}
|
||||||
@@ -143,14 +151,15 @@ export class HaMarkdown extends LitElement {
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
text-align: start;
|
text-align: var(--markdown-table-text-align, start);
|
||||||
}
|
}
|
||||||
td,
|
td,
|
||||||
th {
|
th {
|
||||||
border-width: var(--markdown-table-border-width, 1px);
|
border-width: var(--markdown-table-border-width, 1px);
|
||||||
border-style: var(--markdown-table-border-style, solid);
|
border-style: var(--markdown-table-border-style, solid);
|
||||||
border-color: var(--markdown-table-border-color, var(--divider-color));
|
border-color: var(--markdown-table-border-color, var(--divider-color));
|
||||||
padding: 0.25em 0.5em;
|
padding-inline: var(--markdown-table-padding-inline, 0.5em);
|
||||||
|
padding-block: var(--markdown-table-padding-block, 0.25em);
|
||||||
}
|
}
|
||||||
blockquote {
|
blockquote {
|
||||||
border-left: 4px solid var(--divider-color);
|
border-left: 4px solid var(--divider-color);
|
||||||
|
|||||||
@@ -103,8 +103,8 @@ export class HaPickerField extends LitElement {
|
|||||||
--md-list-item-two-line-container-height: 56px;
|
--md-list-item-two-line-container-height: 56px;
|
||||||
--md-list-item-top-space: 0px;
|
--md-list-item-top-space: 0px;
|
||||||
--md-list-item-bottom-space: 0px;
|
--md-list-item-bottom-space: 0px;
|
||||||
--md-list-item-leading-space: var(--ha-space-4);
|
--md-list-item-leading-space: 8px;
|
||||||
--md-list-item-trailing-space: var(--ha-space-2);
|
--md-list-item-trailing-space: 8px;
|
||||||
--ha-md-list-item-gap: var(--ha-space-2);
|
--ha-md-list-item-gap: var(--ha-space-2);
|
||||||
/* Remove the default focus ring */
|
/* Remove the default focus ring */
|
||||||
--md-focus-ring-width: 0px;
|
--md-focus-ring-width: 0px;
|
||||||
|
|||||||
@@ -450,7 +450,7 @@ export class HaServiceControl extends LitElement {
|
|||||||
|
|
||||||
const hasOptional = Boolean(
|
const hasOptional = Boolean(
|
||||||
!shouldRenderServiceDataYaml &&
|
!shouldRenderServiceDataYaml &&
|
||||||
serviceData?.flatFields.some((field) => showOptionalToggle(field))
|
serviceData?.flatFields.some((field) => showOptionalToggle(field))
|
||||||
);
|
);
|
||||||
|
|
||||||
const targetEntities = this._getTargetedEntities(
|
const targetEntities = this._getTargetedEntities(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import { subscribeLabFeatures } from "../data/labs";
|
import { subscribeLabFeature } from "../data/labs";
|
||||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
||||||
|
|
||||||
interface Snowflake {
|
interface Snowflake {
|
||||||
@@ -10,7 +10,7 @@ interface Snowflake {
|
|||||||
size: number;
|
size: number;
|
||||||
duration: number;
|
duration: number;
|
||||||
delay: number;
|
delay: number;
|
||||||
blur: number;
|
rotation: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-snowflakes")
|
@customElement("ha-snowflakes")
|
||||||
@@ -27,13 +27,14 @@ export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
public hassSubscribe() {
|
public hassSubscribe() {
|
||||||
return [
|
return [
|
||||||
subscribeLabFeatures(this.hass!.connection, (features) => {
|
subscribeLabFeature(
|
||||||
this._enabled =
|
this.hass!.connection,
|
||||||
features.find(
|
"frontend",
|
||||||
(f) =>
|
"winter_mode",
|
||||||
f.domain === "frontend" && f.preview_feature === "winter_mode"
|
(feature) => {
|
||||||
)?.enabled ?? false;
|
this._enabled = feature.enabled;
|
||||||
}),
|
}
|
||||||
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +52,7 @@ export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|||||||
size: Math.random() * 12 + 8, // Random size between 8-20px
|
size: Math.random() * 12 + 8, // Random size between 8-20px
|
||||||
duration: Math.random() * 8 + 8, // Random duration between 8-16s
|
duration: Math.random() * 8 + 8, // Random duration between 8-16s
|
||||||
delay: Math.random() * 8, // Random delay between 0-8s
|
delay: Math.random() * 8, // Random delay between 0-8s
|
||||||
blur: Math.random() * 1, // Random blur between 0-1px
|
rotation: Math.random() * 720 - 360, // Random starting rotation -360 to 360deg
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._snowflakes = snowflakes;
|
this._snowflakes = snowflakes;
|
||||||
@@ -75,20 +76,27 @@ export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|||||||
<div class="snowflakes ${isDark ? "dark" : "light"}" aria-hidden="true">
|
<div class="snowflakes ${isDark ? "dark" : "light"}" aria-hidden="true">
|
||||||
${this._snowflakes.map(
|
${this._snowflakes.map(
|
||||||
(flake) => html`
|
(flake) => html`
|
||||||
<div
|
<svg
|
||||||
class="snowflake ${this.narrow && flake.id >= 30
|
class="snowflake ${this.narrow && flake.id >= 30
|
||||||
? "hide-narrow"
|
? "hide-narrow"
|
||||||
: ""}"
|
: ""}"
|
||||||
style="
|
style="
|
||||||
left: ${flake.left}%;
|
left: ${flake.left}%;
|
||||||
font-size: ${flake.size}px;
|
width: ${flake.size}px;
|
||||||
|
height: ${flake.size}px;
|
||||||
animation-duration: ${flake.duration}s;
|
animation-duration: ${flake.duration}s;
|
||||||
animation-delay: ${flake.delay}s;
|
animation-delay: ${flake.delay}s;
|
||||||
filter: blur(${flake.blur}px);
|
--rotation: ${flake.rotation}deg;
|
||||||
"
|
"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
❄
|
<path
|
||||||
</div>
|
d="M7.991 0a.644.644 0 0 1 .283 1.221v2.553l.986-.988a.645.645 0 0 1 .612-.839.644.644 0 1 1-.222 1.247l-1.376 1.38V7.52l1.65-.954.466-1.879a.645.645 0 0 1 .1-1.042.643.643 0 1 1 .445 1.189l-.363 1.356 3.145-1.82a.643.643 0 1 1 .282.49l-2.205 1.277 1.347.361a.643.643 0 1 1-.158.543l-1.88-.505L8.573 8l1.632.945 1.858-.535a.64.64 0 0 1 .95-.434.643.643 0 1 1-.805.98l-1.354.364L14 11.14a.641.641 0 0 1 .914.855.643.643 0 0 1-1.197-.366l-2.205-1.276.36 1.35a.642.642 0 0 1 .419.95.643.643 0 1 1-.967-.816l-.503-1.884L8.273 8.48v1.909l1.39 1.344a.644.644 0 1 1 .208 1.252.644.644 0 0 1-.606-.852l-.991-.994v3.64A.644.644 0 0 1 7.99 16a.644.644 0 0 1-.282-1.221v-2.553l-.986.988a.645.645 0 0 1-.612.839.644.644 0 1 1 .222-1.247l1.376-1.38V8.5l-1.632.945-.467 1.879q.079.068.134.163a.643.643 0 1 1-.68-.31l.364-1.357-3.145 1.82A.643.643 0 1 1 2 11.15l2.205-1.276-1.347-.361a.643.643 0 1 1 .158-.543l1.88.505L7.444 8l-1.65-.954-1.857.534a.64.64 0 0 1-.95.434.643.643 0 1 1 .805-.98l1.354-.364L2 4.85a.641.641 0 0 1-.914-.855.643.643 0 0 1 1.197.366l2.205 1.276-.36-1.35a.642.642 0 0 1-.419-.95.643.643 0 1 1 .967.816l.503 1.884L7.71 7.5V5.611L6.32 4.267a.644.644 0 1 1-.208-1.252.644.644 0 0 1 .607.852l.991.994V1.22A.644.644 0 0 1 7.991 0"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -128,16 +136,10 @@ export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
.light .snowflake {
|
.light .snowflake {
|
||||||
color: #00bcd4;
|
color: #00bcd4;
|
||||||
text-shadow:
|
|
||||||
0 0 5px #00bcd4,
|
|
||||||
0 0 10px #00e5ff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .snowflake {
|
.dark .snowflake {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
text-shadow:
|
|
||||||
0 0 5px rgba(255, 255, 255, 0.8),
|
|
||||||
0 0 10px rgba(255, 255, 255, 0.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.snowflake.hide-narrow {
|
.snowflake.hide-narrow {
|
||||||
@@ -146,19 +148,23 @@ export class HaSnowflakes extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@keyframes fall {
|
@keyframes fall {
|
||||||
0% {
|
0% {
|
||||||
transform: translateY(-10vh) translateX(0);
|
transform: translateY(-10vh) translateX(0) rotate(var(--rotation));
|
||||||
}
|
}
|
||||||
25% {
|
25% {
|
||||||
transform: translateY(30vh) translateX(10px);
|
transform: translateY(30vh) translateX(10px)
|
||||||
|
rotate(calc(var(--rotation) + 25deg));
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
transform: translateY(60vh) translateX(-10px);
|
transform: translateY(60vh) translateX(-10px)
|
||||||
|
rotate(calc(var(--rotation) + 50deg));
|
||||||
}
|
}
|
||||||
75% {
|
75% {
|
||||||
transform: translateY(85vh) translateX(10px);
|
transform: translateY(85vh) translateX(10px)
|
||||||
|
rotate(calc(var(--rotation) + 75deg));
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: translateY(120vh) translateX(0);
|
transform: translateY(120vh) translateX(0)
|
||||||
|
rotate(calc(var(--rotation) + 100deg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -952,10 +952,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
|
|||||||
let hasFloor = false;
|
let hasFloor = false;
|
||||||
let rtl = false;
|
let rtl = false;
|
||||||
let showEntityId = false;
|
let showEntityId = false;
|
||||||
|
|
||||||
if (type === "area" || type === "floor") {
|
if (type === "area" || type === "floor") {
|
||||||
item.id = item[type]?.[`${type}_id`];
|
|
||||||
|
|
||||||
rtl = computeRTL(this.hass);
|
rtl = computeRTL(this.hass);
|
||||||
hasFloor =
|
hasFloor =
|
||||||
type === "area" && !!(item as FloorComboBoxItem).area?.floor_id;
|
type === "area" && !!(item as FloorComboBoxItem).area?.floor_id;
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export class HaToast extends Snackbar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mdc-snackbar {
|
.mdc-snackbar {
|
||||||
|
z-index: 10;
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
right: calc(8px + var(--safe-area-inset-right));
|
right: calc(8px + var(--safe-area-inset-right));
|
||||||
bottom: calc(8px + var(--safe-area-inset-bottom));
|
bottom: calc(8px + var(--safe-area-inset-bottom));
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ export type DialogWidth = "small" | "medium" | "large" | "full";
|
|||||||
* @cssprop --ha-dialog-surface-background - Dialog background color.
|
* @cssprop --ha-dialog-surface-background - Dialog background color.
|
||||||
* @cssprop --ha-dialog-border-radius - Border radius of the dialog surface.
|
* @cssprop --ha-dialog-border-radius - Border radius of the dialog surface.
|
||||||
* @cssprop --dialog-z-index - Z-index for the dialog.
|
* @cssprop --dialog-z-index - Z-index for the dialog.
|
||||||
* @cssprop --dialog-surface-position - CSS position of the dialog surface.
|
|
||||||
* @cssprop --dialog-surface-margin-top - Top margin for the dialog surface.
|
* @cssprop --dialog-surface-margin-top - Top margin for the dialog surface.
|
||||||
*
|
*
|
||||||
* @attr {boolean} open - Controls the dialog open state.
|
* @attr {boolean} open - Controls the dialog open state.
|
||||||
@@ -244,7 +243,6 @@ export class HaWaDialog extends LitElement {
|
|||||||
calc(var(--safe-height) - var(--ha-space-20))
|
calc(var(--safe-height) - var(--ha-space-20))
|
||||||
);
|
);
|
||||||
min-height: var(--ha-dialog-min-height);
|
min-height: var(--ha-dialog-min-height);
|
||||||
position: var(--dialog-surface-position, relative);
|
|
||||||
margin-top: var(--dialog-surface-margin-top, auto);
|
margin-top: var(--dialog-surface-margin-top, auto);
|
||||||
/* Used to offset the dialog from the safe areas when space is limited */
|
/* Used to offset the dialog from the safe areas when space is limited */
|
||||||
transform: translate(
|
transform: translate(
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { LitElement, html, css } from "lit";
|
import { LitElement, html, css } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { property } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import "../ha-state-icon";
|
import "../ha-state-icon";
|
||||||
|
|
||||||
@customElement("ha-entity-marker")
|
|
||||||
class HaEntityMarker extends LitElement {
|
class HaEntityMarker extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -90,6 +89,8 @@ class HaEntityMarker extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-entity-marker", HaEntityMarker);
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-entity-marker": HaEntityMarker;
|
"ha-entity-marker": HaEntityMarker;
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ const getAreasAndFloorsItems = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
let outputAreas = areas;
|
let outputAreas = areas;
|
||||||
|
let outputFloors = floors;
|
||||||
|
|
||||||
let areaIds: string[] | undefined;
|
let areaIds: string[] | undefined;
|
||||||
|
|
||||||
@@ -254,9 +255,29 @@ const getAreasAndFloorsItems = (
|
|||||||
outputAreas = outputAreas.filter(
|
outputAreas = outputAreas.filter(
|
||||||
(area) => !area.floor_id || !excludeFloors!.includes(area.floor_id)
|
(area) => !area.floor_id || !excludeFloors!.includes(area.floor_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
outputFloors = outputFloors.filter(
|
||||||
|
(floor) => !excludeFloors!.includes(floor.floor_id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const hierarchy = getAreasFloorHierarchy(floors, outputAreas);
|
if (
|
||||||
|
entityFilter ||
|
||||||
|
deviceFilter ||
|
||||||
|
includeDomains ||
|
||||||
|
excludeDomains ||
|
||||||
|
includeDeviceClasses
|
||||||
|
) {
|
||||||
|
// Ensure we only include floors that have areas with the filtered entities/devices
|
||||||
|
const validFloorIds = new Set(
|
||||||
|
outputAreas.map((area) => area.floor_id).filter((id) => id)
|
||||||
|
);
|
||||||
|
outputFloors = outputFloors.filter((floor) =>
|
||||||
|
validFloorIds.has(floor.floor_id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hierarchy = getAreasFloorHierarchy(outputFloors, outputAreas);
|
||||||
|
|
||||||
const items: (
|
const items: (
|
||||||
| FloorComboBoxItem
|
| FloorComboBoxItem
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { stringCompare } from "../common/string/compare";
|
|
||||||
import type { HomeAssistant } from "../types";
|
import type { HomeAssistant } from "../types";
|
||||||
import type { DeviceRegistryEntry } from "./device_registry";
|
import type { DeviceRegistryEntry } from "./device_registry";
|
||||||
import type {
|
import type {
|
||||||
@@ -105,22 +104,3 @@ export const getAreaDeviceLookup = (
|
|||||||
}
|
}
|
||||||
return areaDeviceLookup;
|
return areaDeviceLookup;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const areaCompare =
|
|
||||||
(entries?: HomeAssistant["areas"], order?: string[]) =>
|
|
||||||
(a: string, b: string) => {
|
|
||||||
const indexA = order ? order.indexOf(a) : -1;
|
|
||||||
const indexB = order ? order.indexOf(b) : -1;
|
|
||||||
if (indexA === -1 && indexB === -1) {
|
|
||||||
const nameA = entries?.[a]?.name ?? a;
|
|
||||||
const nameB = entries?.[b]?.name ?? b;
|
|
||||||
return stringCompare(nameA, nameB);
|
|
||||||
}
|
|
||||||
if (indexA === -1) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (indexB === -1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return indexA - indexB;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
isLastDayOfMonth,
|
isLastDayOfMonth,
|
||||||
addYears,
|
addYears,
|
||||||
} from "date-fns";
|
} from "date-fns";
|
||||||
import type { Collection } from "home-assistant-js-websocket";
|
import type { Collection, HassEntity } from "home-assistant-js-websocket";
|
||||||
import { getCollection } from "home-assistant-js-websocket";
|
import { getCollection } from "home-assistant-js-websocket";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import {
|
import {
|
||||||
@@ -1361,3 +1361,37 @@ export const calculateSolarConsumedGauge = (
|
|||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current power value from entity state, normalized to kW
|
||||||
|
* @param stateObj - The entity state object to get power value from
|
||||||
|
* @returns Power value in kW, or 0 if entity not found or invalid
|
||||||
|
*/
|
||||||
|
export const getPowerFromState = (stateObj: HassEntity): number | undefined => {
|
||||||
|
if (!stateObj) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const value = parseFloat(stateObj.state);
|
||||||
|
if (isNaN(value)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize to kW based on unit of measurement (case-sensitive)
|
||||||
|
// Supported units: GW, kW, MW, mW, TW, W
|
||||||
|
const unit = stateObj.attributes.unit_of_measurement;
|
||||||
|
switch (unit) {
|
||||||
|
case "W":
|
||||||
|
return value / 1000;
|
||||||
|
case "mW":
|
||||||
|
return value / 1000000;
|
||||||
|
case "MW":
|
||||||
|
return value * 1000;
|
||||||
|
case "GW":
|
||||||
|
return value * 1000000;
|
||||||
|
case "TW":
|
||||||
|
return value * 1000000000;
|
||||||
|
default:
|
||||||
|
// Assume kW if no unit or unit is kW
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ export interface HassioFullBackupCreateParams {
|
|||||||
confirm_password?: string;
|
confirm_password?: string;
|
||||||
background?: boolean;
|
background?: boolean;
|
||||||
}
|
}
|
||||||
export interface HassioPartialBackupCreateParams extends HassioFullBackupCreateParams {
|
export interface HassioPartialBackupCreateParams
|
||||||
|
extends HassioFullBackupCreateParams {
|
||||||
folders?: string[];
|
folders?: string[];
|
||||||
addons?: string[];
|
addons?: string[];
|
||||||
homeassistant?: boolean;
|
homeassistant?: boolean;
|
||||||
|
|||||||
@@ -18,6 +18,11 @@ export interface LabPreviewFeaturesResponse {
|
|||||||
features: LabPreviewFeature[];
|
features: LabPreviewFeature[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all lab features
|
||||||
|
* @param hass - The Home Assistant instance
|
||||||
|
* @returns A promise to fetch the lab features
|
||||||
|
*/
|
||||||
export const fetchLabFeatures = async (
|
export const fetchLabFeatures = async (
|
||||||
hass: HomeAssistant
|
hass: HomeAssistant
|
||||||
): Promise<LabPreviewFeature[]> => {
|
): Promise<LabPreviewFeature[]> => {
|
||||||
@@ -27,6 +32,15 @@ export const fetchLabFeatures = async (
|
|||||||
return response.features;
|
return response.features;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a specific lab feature
|
||||||
|
* @param hass - The Home Assistant instance
|
||||||
|
* @param domain - The domain of the lab feature
|
||||||
|
* @param preview_feature - The preview feature of the lab feature
|
||||||
|
* @param enabled - Whether the lab feature is enabled
|
||||||
|
* @param create_backup - Whether to create a backup of the lab feature
|
||||||
|
* @returns A promise to update the lab feature
|
||||||
|
*/
|
||||||
export const labsUpdatePreviewFeature = (
|
export const labsUpdatePreviewFeature = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
domain: string,
|
domain: string,
|
||||||
@@ -65,6 +79,12 @@ const subscribeLabUpdates = (
|
|||||||
"labs_updated"
|
"labs_updated"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to a collection of lab features
|
||||||
|
* @param conn - The connection to the Home Assistant instance
|
||||||
|
* @param onChange - The function to call when the lab features change
|
||||||
|
* @returns The unsubscribe function
|
||||||
|
*/
|
||||||
export const subscribeLabFeatures = (
|
export const subscribeLabFeatures = (
|
||||||
conn: Connection,
|
conn: Connection,
|
||||||
onChange: (features: LabPreviewFeature[]) => void
|
onChange: (features: LabPreviewFeature[]) => void
|
||||||
@@ -76,3 +96,23 @@ export const subscribeLabFeatures = (
|
|||||||
conn,
|
conn,
|
||||||
onChange
|
onChange
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe to a specific lab feature
|
||||||
|
* @param conn - The connection to the Home Assistant instance
|
||||||
|
* @param domain - The domain of the lab feature
|
||||||
|
* @param previewFeature - The preview feature identifier
|
||||||
|
* @param onChange - The function to call when the lab feature changes
|
||||||
|
* @returns A promise that resolves to the unsubscribe function
|
||||||
|
*/
|
||||||
|
export const subscribeLabFeature = (
|
||||||
|
conn: Connection,
|
||||||
|
domain: string,
|
||||||
|
previewFeature: string,
|
||||||
|
onChange: (feature: LabPreviewFeature) => void
|
||||||
|
): Promise<() => void> =>
|
||||||
|
conn.subscribeMessage<LabPreviewFeature>(onChange, {
|
||||||
|
type: "labs/subscribe",
|
||||||
|
domain,
|
||||||
|
preview_feature: previewFeature,
|
||||||
|
});
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export const enum LawnMowerEntityFeature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface LawnMowerEntityAttributes
|
interface LawnMowerEntityAttributes
|
||||||
extends HassEntityAttributeBase, Record<string, any> {}
|
extends HassEntityAttributeBase,
|
||||||
|
Record<string, any> {}
|
||||||
|
|
||||||
export interface LawnMowerEntity extends HassEntityBase {
|
export interface LawnMowerEntity extends HassEntityBase {
|
||||||
attributes: LawnMowerEntityAttributes;
|
attributes: LawnMowerEntityAttributes;
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ export interface LovelaceSectionConfig extends LovelaceBaseSectionConfig {
|
|||||||
cards?: LovelaceCardConfig[];
|
cards?: LovelaceCardConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceStrategySectionConfig extends LovelaceBaseSectionConfig {
|
export interface LovelaceStrategySectionConfig
|
||||||
|
extends LovelaceBaseSectionConfig {
|
||||||
strategy: LovelaceStrategyConfig;
|
strategy: LovelaceStrategyConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ export interface LovelaceConfig extends LovelaceDashboardBaseConfig {
|
|||||||
views: LovelaceViewRawConfig[];
|
views: LovelaceViewRawConfig[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceDashboardStrategyConfig extends LovelaceDashboardBaseConfig {
|
export interface LovelaceDashboardStrategyConfig
|
||||||
|
extends LovelaceDashboardBaseConfig {
|
||||||
strategy: LovelaceStrategyConfig;
|
strategy: LovelaceStrategyConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export interface LovelaceDashboardMutableParams {
|
|||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LovelaceDashboardCreateParams extends LovelaceDashboardMutableParams {
|
export interface LovelaceDashboardCreateParams
|
||||||
|
extends LovelaceDashboardMutableParams {
|
||||||
url_path: string;
|
url_path: string;
|
||||||
mode: "storage";
|
mode: "storage";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,12 +220,12 @@ const tryDescribeAction = <T extends ActionType>(
|
|||||||
if (config.action) {
|
if (config.action) {
|
||||||
const [domain, serviceName] = config.action.split(".", 2);
|
const [domain, serviceName] = config.action.split(".", 2);
|
||||||
const descriptionPlaceholders =
|
const descriptionPlaceholders =
|
||||||
hass.services[domain][serviceName].description_placeholders;
|
hass.services[domain]?.[serviceName]?.description_placeholders;
|
||||||
const service =
|
const service =
|
||||||
hass.localize(
|
hass.localize(
|
||||||
`component.${domain}.services.${serviceName}.name`,
|
`component.${domain}.services.${serviceName}.name`,
|
||||||
descriptionPlaceholders
|
descriptionPlaceholders
|
||||||
) || hass.services[domain][serviceName]?.name;
|
) || hass.services[domain]?.[serviceName]?.name;
|
||||||
|
|
||||||
if (config.metadata) {
|
if (config.metadata) {
|
||||||
return hass.localize(
|
return hass.localize(
|
||||||
|
|||||||
@@ -106,7 +106,8 @@ export interface AutomationTrace extends BaseTrace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface AutomationTraceExtended
|
export interface AutomationTraceExtended
|
||||||
extends AutomationTrace, BaseTraceExtended {
|
extends AutomationTrace,
|
||||||
|
BaseTraceExtended {
|
||||||
config: ManualAutomationConfig;
|
config: ManualAutomationConfig;
|
||||||
blueprint_inputs?: BlueprintAutomationConfig;
|
blueprint_inputs?: BlueprintAutomationConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ export interface PromptDialogParams extends BaseDialogBoxParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DialogBoxParams
|
export interface DialogBoxParams
|
||||||
extends ConfirmationDialogParams, PromptDialogParams {
|
extends ConfirmationDialogParams,
|
||||||
|
PromptDialogParams {
|
||||||
confirm?: (out?: string) => void;
|
confirm?: (out?: string) => void;
|
||||||
confirmation?: boolean;
|
confirmation?: boolean;
|
||||||
prompt?: boolean;
|
prompt?: boolean;
|
||||||
|
|||||||
@@ -19,9 +19,8 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HassDialog<
|
export interface HassDialog<T = HASSDomEvents[ValidHassDomEvent]>
|
||||||
T = HASSDomEvents[ValidHassDomEvent],
|
extends HTMLElement {
|
||||||
> extends HTMLElement {
|
|
||||||
showDialog(params: T);
|
showDialog(params: T);
|
||||||
closeDialog?: (historyState?: any) => boolean;
|
closeDialog?: (historyState?: any) => boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { slugify } from "../../../common/string/slugify";
|
import { slugify } from "../../../common/string/slugify";
|
||||||
import "../../../components/buttons/ha-progress-button";
|
import "../../../components/buttons/ha-progress-button";
|
||||||
import "../../../components/ha-camera-stream";
|
import "../../../components/ha-camera-stream";
|
||||||
@@ -9,7 +9,6 @@ import type { HomeAssistant } from "../../../types";
|
|||||||
import { fileDownload } from "../../../util/file_download";
|
import { fileDownload } from "../../../util/file_download";
|
||||||
import { showToast } from "../../../util/toast";
|
import { showToast } from "../../../util/toast";
|
||||||
|
|
||||||
@customElement("more-info-camera")
|
|
||||||
class MoreInfoCamera extends LitElement {
|
class MoreInfoCamera extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -113,6 +112,8 @@ class MoreInfoCamera extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("more-info-camera", MoreInfoCamera);
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"more-info-camera": MoreInfoCamera;
|
"more-info-camera": MoreInfoCamera;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-attribute-icon";
|
import "../../../components/ha-attribute-icon";
|
||||||
@@ -32,7 +32,6 @@ import { moreInfoControlStyle } from "../components/more-info-control-style";
|
|||||||
|
|
||||||
type MainControl = "temperature" | "humidity";
|
type MainControl = "temperature" | "humidity";
|
||||||
|
|
||||||
@customElement("more-info-climate")
|
|
||||||
class MoreInfoClimate extends LitElement {
|
class MoreInfoClimate extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -568,6 +567,8 @@ class MoreInfoClimate extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("more-info-climate", MoreInfoClimate);
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"more-info-climate": MoreInfoClimate;
|
"more-info-climate": MoreInfoClimate;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { dynamicElement } from "../../../common/dom/dynamic-element-directive";
|
import { dynamicElement } from "../../../common/dom/dynamic-element-directive";
|
||||||
import type { GroupEntity } from "../../../data/group";
|
import type { GroupEntity } from "../../../data/group";
|
||||||
import { computeGroupDomain } from "../../../data/group";
|
import { computeGroupDomain } from "../../../data/group";
|
||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
importMoreInfoControl,
|
importMoreInfoControl,
|
||||||
} from "../state_more_info_control";
|
} from "../state_more_info_control";
|
||||||
|
|
||||||
@customElement("more-info-group")
|
|
||||||
class MoreInfoGroup extends LitElement {
|
class MoreInfoGroup extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -107,6 +106,8 @@ class MoreInfoGroup extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("more-info-group", MoreInfoGroup);
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"more-info-group": MoreInfoGroup;
|
"more-info-group": MoreInfoGroup;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { mdiPower, mdiTuneVariant } from "@mdi/js";
|
import { mdiPower, mdiTuneVariant } from "@mdi/js";
|
||||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-control-select-menu";
|
import "../../../components/ha-control-select-menu";
|
||||||
@@ -15,7 +15,6 @@ import type { HomeAssistant } from "../../../types";
|
|||||||
import "../components/ha-more-info-control-select-container";
|
import "../components/ha-more-info-control-select-container";
|
||||||
import { moreInfoControlStyle } from "../components/more-info-control-style";
|
import { moreInfoControlStyle } from "../components/more-info-control-style";
|
||||||
|
|
||||||
@customElement("more-info-humidifier")
|
|
||||||
class MoreInfoHumidifier extends LitElement {
|
class MoreInfoHumidifier extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -250,6 +249,8 @@ class MoreInfoHumidifier extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("more-info-humidifier", MoreInfoHumidifier);
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"more-info-humidifier": MoreInfoHumidifier;
|
"more-info-humidifier": MoreInfoHumidifier;
|
||||||
|
|||||||
@@ -302,7 +302,9 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _goToAddEntityTo(ev) {
|
private _goToAddEntityTo(ev) {
|
||||||
if (!shouldHandleRequestSelectedEvent(ev)) return;
|
// Only check for request-selected events (from menu items), not regular clicks (from icon button)
|
||||||
|
if (ev.type === "request-selected" && !shouldHandleRequestSelectedEvent(ev))
|
||||||
|
return;
|
||||||
this._setView("add_to");
|
this._setView("add_to");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,7 +552,18 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
: nothing}
|
: nothing}
|
||||||
</ha-button-menu>
|
</ha-button-menu>
|
||||||
`
|
`
|
||||||
: nothing}
|
: !__DEMO__ && this._shouldShowAddEntityTo()
|
||||||
|
? html`
|
||||||
|
<ha-icon-button
|
||||||
|
slot="actionItems"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.dialogs.more_info_control.add_entity_to"
|
||||||
|
)}
|
||||||
|
.path=${mdiPlusBoxMultipleOutline}
|
||||||
|
@click=${this._goToAddEntityTo}
|
||||||
|
></ha-icon-button>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
`
|
`
|
||||||
: isSpecificInitialView
|
: isSpecificInitialView
|
||||||
? html`
|
? html`
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { css, html, LitElement } from "lit";
|
import { css, html, LitElement } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import "../components/ha-spinner";
|
import "../components/ha-spinner";
|
||||||
import "../components/ha-button";
|
import "../components/ha-button";
|
||||||
|
|
||||||
@customElement("ha-init-page")
|
|
||||||
class HaInitPage extends LitElement {
|
class HaInitPage extends LitElement {
|
||||||
@property({ type: Boolean }) public error = false;
|
@property({ type: Boolean }) public error = false;
|
||||||
|
|
||||||
@@ -121,6 +120,8 @@ class HaInitPage extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-init-page", HaInitPage);
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-init-page": HaInitPage;
|
"ha-init-page": HaInitPage;
|
||||||
|
|||||||
@@ -128,8 +128,6 @@ class HassSubpage extends LitElement {
|
|||||||
ha-menu-button,
|
ha-menu-button,
|
||||||
ha-icon-button-arrow-prev,
|
ha-icon-button-arrow-prev,
|
||||||
::slotted([slot="toolbar-icon"]) {
|
::slotted([slot="toolbar-icon"]) {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
color: var(--sidebar-icon-color);
|
color: var(--sidebar-icon-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -621,9 +621,9 @@ export class HaTabsSubpageDataTable extends KeyboardShortcutMixin(LitElement) {
|
|||||||
} else if (this._sortDirection === "asc") {
|
} else if (this._sortDirection === "asc") {
|
||||||
this._sortDirection = "desc";
|
this._sortDirection = "desc";
|
||||||
} else {
|
} else {
|
||||||
this._sortDirection = "asc";
|
this._sortDirection = null;
|
||||||
}
|
}
|
||||||
this._sortColumn = columnId;
|
this._sortColumn = this._sortDirection === null ? undefined : columnId;
|
||||||
|
|
||||||
fireEvent(this, "sorting-changed", {
|
fireEvent(this, "sorting-changed", {
|
||||||
column: columnId,
|
column: columnId,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { mdiClose } from "@mdi/js";
|
import { mdiClose } from "@mdi/js";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { property, query, state } from "lit/decorators";
|
||||||
import type { LocalizeKeys } from "../common/translations/localize";
|
import type { LocalizeKeys } from "../common/translations/localize";
|
||||||
import "../components/ha-button";
|
import "../components/ha-button";
|
||||||
import "../components/ha-icon-button";
|
import "../components/ha-icon-button";
|
||||||
@@ -26,7 +26,6 @@ export interface ToastActionParams {
|
|||||||
| { translationKey: LocalizeKeys; args?: Record<string, string> };
|
| { translationKey: LocalizeKeys; args?: Record<string, string> };
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("notification-manager")
|
|
||||||
class NotificationManager extends LitElement {
|
class NotificationManager extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -116,6 +115,8 @@ class NotificationManager extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("notification-manager", NotificationManager);
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"notification-manager": NotificationManager;
|
"notification-manager": NotificationManager;
|
||||||
|
|||||||
@@ -90,7 +90,9 @@ class OnboardingRestoreBackupCloudLogin extends LitElement {
|
|||||||
this._email = this._cloudLoginElement.emailField.value;
|
this._email = this._cloudLoginElement.emailField.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
await import("../../panels/config/cloud/forgot-password/cloud-forgot-password-card");
|
await import(
|
||||||
|
"../../panels/config/cloud/forgot-password/cloud-forgot-password-card"
|
||||||
|
);
|
||||||
this._view = "forgot-password";
|
this._view = "forgot-password";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { TZDate } from "@date-fns/tz";
|
|||||||
import { addDays, isSameDay } from "date-fns";
|
import { addDays, isSameDay } from "date-fns";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { formatDate } from "../../common/datetime/format_date";
|
import { formatDate } from "../../common/datetime/format_date";
|
||||||
import { formatDateTime } from "../../common/datetime/format_date_time";
|
import { formatDateTime } from "../../common/datetime/format_date_time";
|
||||||
import { formatTime } from "../../common/datetime/format_time";
|
import { formatTime } from "../../common/datetime/format_time";
|
||||||
@@ -26,7 +26,6 @@ import type { CalendarEventDetailDialogParams } from "./show-dialog-calendar-eve
|
|||||||
import { showCalendarEventEditDialog } from "./show-dialog-calendar-event-editor";
|
import { showCalendarEventEditDialog } from "./show-dialog-calendar-event-editor";
|
||||||
import { resolveTimeZone } from "../../common/datetime/resolve-time-zone";
|
import { resolveTimeZone } from "../../common/datetime/resolve-time-zone";
|
||||||
|
|
||||||
@customElement("dialog-calendar-event-detail")
|
|
||||||
class DialogCalendarEventDetail extends LitElement {
|
class DialogCalendarEventDetail extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -272,3 +271,8 @@ declare global {
|
|||||||
"dialog-calendar-event-detail": DialogCalendarEventDetail;
|
"dialog-calendar-event-detail": DialogCalendarEventDetail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define(
|
||||||
|
"dialog-calendar-event-detail",
|
||||||
|
DialogCalendarEventDetail
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { HassEntity } from "home-assistant-js-websocket";
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import "../../../components/entity/ha-entity-picker";
|
import "../../../components/entity/ha-entity-picker";
|
||||||
import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker";
|
import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker";
|
||||||
@@ -40,7 +40,6 @@ const SENSOR_DOMAINS = ["sensor"];
|
|||||||
const TEMPERATURE_DEVICE_CLASSES = [SENSOR_DEVICE_CLASS_TEMPERATURE];
|
const TEMPERATURE_DEVICE_CLASSES = [SENSOR_DEVICE_CLASS_TEMPERATURE];
|
||||||
const HUMIDITY_DEVICE_CLASSES = [SENSOR_DEVICE_CLASS_HUMIDITY];
|
const HUMIDITY_DEVICE_CLASSES = [SENSOR_DEVICE_CLASS_HUMIDITY];
|
||||||
|
|
||||||
@customElement("dialog-area-registry-detail")
|
|
||||||
class DialogAreaDetail extends LitElement {
|
class DialogAreaDetail extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -427,3 +426,5 @@ declare global {
|
|||||||
"dialog-area-registry-detail": DialogAreaDetail;
|
"dialog-area-registry-detail": DialogAreaDetail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("dialog-area-registry-detail", DialogAreaDetail);
|
||||||
|
|||||||
@@ -81,8 +81,11 @@ class DialogAreasFloorsOrder extends LitElement {
|
|||||||
return nothing;
|
return nothing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasFloors = this._hierarchy.floors.length > 0;
|
||||||
const dialogTitle = this.hass.localize(
|
const dialogTitle = this.hass.localize(
|
||||||
"ui.panel.config.areas.dialog.reorder_title"
|
hasFloors
|
||||||
|
? "ui.panel.config.areas.dialog.reorder_floors_areas_title"
|
||||||
|
: "ui.panel.config.areas.dialog.reorder_areas_title"
|
||||||
);
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
@@ -172,7 +175,7 @@ class DialogAreasFloorsOrder extends LitElement {
|
|||||||
? html`<div class="floor-header">
|
? html`<div class="floor-header">
|
||||||
<span class="floor-name">
|
<span class="floor-name">
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.areas.dialog.unassigned_areas"
|
"ui.panel.config.areas.dialog.other_areas"
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>`
|
</div>`
|
||||||
@@ -418,7 +421,6 @@ class DialogAreasFloorsOrder extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.floor.unassigned {
|
.floor.unassigned {
|
||||||
border-style: dashed;
|
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { mdiTextureBox } from "@mdi/js";
|
import { mdiTextureBox } from "@mdi/js";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { repeat } from "lit/directives/repeat";
|
import { repeat } from "lit/directives/repeat";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
@@ -27,7 +27,6 @@ import type { HomeAssistant } from "../../../types";
|
|||||||
import { showAreaRegistryDetailDialog } from "./show-dialog-area-registry-detail";
|
import { showAreaRegistryDetailDialog } from "./show-dialog-area-registry-detail";
|
||||||
import type { FloorRegistryDetailDialogParams } from "./show-dialog-floor-registry-detail";
|
import type { FloorRegistryDetailDialogParams } from "./show-dialog-floor-registry-detail";
|
||||||
|
|
||||||
@customElement("dialog-floor-registry-detail")
|
|
||||||
class DialogFloorDetail extends LitElement {
|
class DialogFloorDetail extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -362,3 +361,5 @@ declare global {
|
|||||||
"dialog-floor-registry-detail": DialogFloorDetail;
|
"dialog-floor-registry-detail": DialogFloorDetail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("dialog-floor-registry-detail", DialogFloorDetail);
|
||||||
|
|||||||
@@ -175,21 +175,12 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
has-fab
|
has-fab
|
||||||
>
|
>
|
||||||
<ha-button-menu slot="toolbar-icon">
|
<ha-icon-button
|
||||||
<ha-icon-button
|
slot="toolbar-icon"
|
||||||
slot="trigger"
|
.label=${this.hass.localize("ui.common.help")}
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.path=${mdiHelpCircle}
|
||||||
.path=${mdiDotsVertical}
|
@click=${this._showHelp}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-list-item graphic="icon" @click=${this._showReorderDialog}>
|
|
||||||
<ha-svg-icon .path=${mdiSort} slot="graphic"></ha-svg-icon>
|
|
||||||
${this.hass.localize("ui.panel.config.areas.picker.reorder")}
|
|
||||||
</ha-list-item>
|
|
||||||
<ha-list-item graphic="icon" @click=${this._showHelp}>
|
|
||||||
<ha-svg-icon .path=${mdiHelpCircle} slot="graphic"></ha-svg-icon>
|
|
||||||
${this.hass.localize("ui.common.help")}
|
|
||||||
</ha-list-item>
|
|
||||||
</ha-button-menu>
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="floors">
|
<div class="floors">
|
||||||
${this._hierarchy.floors.map(({ areas, id }) => {
|
${this._hierarchy.floors.map(({ areas, id }) => {
|
||||||
@@ -213,6 +204,16 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
slot="trigger"
|
slot="trigger"
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
<ha-list-item graphic="icon"
|
||||||
|
><ha-svg-icon
|
||||||
|
.path=${mdiSort}
|
||||||
|
slot="graphic"
|
||||||
|
></ha-svg-icon
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.areas.picker.reorder"
|
||||||
|
)}</ha-list-item
|
||||||
|
>
|
||||||
|
<li divider role="separator"></li>
|
||||||
<ha-list-item graphic="icon"
|
<ha-list-item graphic="icon"
|
||||||
><ha-svg-icon
|
><ha-svg-icon
|
||||||
.path=${mdiPencil}
|
.path=${mdiPencil}
|
||||||
@@ -266,9 +267,30 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<h2>
|
<h2>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.panel.config.areas.picker.unassigned_areas"
|
this._hierarchy.floors.length
|
||||||
|
? "ui.panel.config.areas.picker.other_areas"
|
||||||
|
: "ui.panel.config.areas.picker.header"
|
||||||
)}
|
)}
|
||||||
</h2>
|
</h2>
|
||||||
|
<div class="actions">
|
||||||
|
<ha-button-menu
|
||||||
|
@action=${this._handleUnassignedAreasAction}
|
||||||
|
>
|
||||||
|
<ha-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
.path=${mdiDotsVertical}
|
||||||
|
></ha-icon-button>
|
||||||
|
<ha-list-item graphic="icon"
|
||||||
|
><ha-svg-icon
|
||||||
|
.path=${mdiSort}
|
||||||
|
slot="graphic"
|
||||||
|
></ha-svg-icon
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.areas.picker.reorder"
|
||||||
|
)}</ha-list-item
|
||||||
|
>
|
||||||
|
</ha-button-menu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ha-sortable
|
<ha-sortable
|
||||||
handle-selector="a"
|
handle-selector="a"
|
||||||
@@ -515,14 +537,23 @@ export class HaConfigAreasDashboard extends LitElement {
|
|||||||
const floor = (ev.currentTarget as any).floor;
|
const floor = (ev.currentTarget as any).floor;
|
||||||
switch (ev.detail.index) {
|
switch (ev.detail.index) {
|
||||||
case 0:
|
case 0:
|
||||||
this._editFloor(floor);
|
this._showReorderDialog();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
this._editFloor(floor);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
this._deleteFloor(floor);
|
this._deleteFloor(floor);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleUnassignedAreasAction(ev: CustomEvent<ActionDetail>) {
|
||||||
|
if (ev.detail.index === 0) {
|
||||||
|
this._showReorderDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _createFloor() {
|
private _createFloor() {
|
||||||
this._openFloorDialog();
|
this._openFloorDialog();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ import {
|
|||||||
fetchIntegrationManifests,
|
fetchIntegrationManifests,
|
||||||
} from "../../../data/integration";
|
} from "../../../data/integration";
|
||||||
import type { LabelRegistryEntry } from "../../../data/label_registry";
|
import type { LabelRegistryEntry } from "../../../data/label_registry";
|
||||||
import { subscribeLabFeatures } from "../../../data/labs";
|
import { subscribeLabFeature } from "../../../data/labs";
|
||||||
import {
|
import {
|
||||||
TARGET_SEPARATOR,
|
TARGET_SEPARATOR,
|
||||||
getConditionsForTarget,
|
getConditionsForTarget,
|
||||||
@@ -228,7 +228,7 @@ class DialogAddAutomationElement
|
|||||||
|
|
||||||
private _unsub?: Promise<UnsubscribeFunc>;
|
private _unsub?: Promise<UnsubscribeFunc>;
|
||||||
|
|
||||||
private _unsubscribeLabFeatures?: UnsubscribeFunc;
|
private _unsubscribeLabFeatures?: Promise<UnsubscribeFunc>;
|
||||||
|
|
||||||
private _configEntryLookup: Record<string, ConfigEntry> = {};
|
private _configEntryLookup: Record<string, ConfigEntry> = {};
|
||||||
|
|
||||||
@@ -281,15 +281,12 @@ class DialogAddAutomationElement
|
|||||||
this._fetchManifests();
|
this._fetchManifests();
|
||||||
this._calculateUsedDomains();
|
this._calculateUsedDomains();
|
||||||
|
|
||||||
this._unsubscribeLabFeatures = subscribeLabFeatures(
|
this._unsubscribeLabFeatures = subscribeLabFeature(
|
||||||
this.hass.connection,
|
this.hass.connection,
|
||||||
(features) => {
|
"automation",
|
||||||
this._newTriggersAndConditions =
|
"new_triggers_conditions",
|
||||||
features.find(
|
(feature) => {
|
||||||
(feature) =>
|
this._newTriggersAndConditions = feature.enabled;
|
||||||
feature.domain === "automation" &&
|
|
||||||
feature.preview_feature === "new_triggers_conditions"
|
|
||||||
)?.enabled ?? false;
|
|
||||||
this._tab = this._newTriggersAndConditions ? "targets" : "groups";
|
this._tab = this._newTriggersAndConditions ? "targets" : "groups";
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -425,7 +422,7 @@ class DialogAddAutomationElement
|
|||||||
this._unsub = undefined;
|
this._unsub = undefined;
|
||||||
}
|
}
|
||||||
if (this._unsubscribeLabFeatures) {
|
if (this._unsubscribeLabFeatures) {
|
||||||
this._unsubscribeLabFeatures();
|
this._unsubscribeLabFeatures.then((unsub) => unsub());
|
||||||
this._unsubscribeLabFeatures = undefined;
|
this._unsubscribeLabFeatures = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -686,6 +683,7 @@ class DialogAddAutomationElement
|
|||||||
<ha-automation-add-items
|
<ha-automation-add-items
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.items=${this._getItems()}
|
.items=${this._getItems()}
|
||||||
|
.scrollable=${!this._narrow}
|
||||||
.error=${this._tab === "targets" && this._loadItemsError
|
.error=${this._tab === "targets" && this._loadItemsError
|
||||||
? this.hass.localize(
|
? this.hass.localize(
|
||||||
"ui.panel.config.automation.editor.load_target_items_failed"
|
"ui.panel.config.automation.editor.load_target_items_failed"
|
||||||
@@ -2146,7 +2144,7 @@ class DialogAddAutomationElement
|
|||||||
min-height: 160px;
|
min-height: 160px;
|
||||||
}
|
}
|
||||||
.content.column ha-automation-add-from-target {
|
.content.column ha-automation-add-from-target {
|
||||||
overflow: hidden;
|
overflow: clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-wa-dialog ha-automation-add-items {
|
ha-wa-dialog ha-automation-add-items {
|
||||||
|
|||||||
@@ -911,6 +911,10 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
|||||||
const services: Record<string, Level3Entries> = {};
|
const services: Record<string, Level3Entries> = {};
|
||||||
|
|
||||||
unassignedDevices.forEach(({ id: deviceId, entry_type }) => {
|
unassignedDevices.forEach(({ id: deviceId, entry_type }) => {
|
||||||
|
const device = this.devices[deviceId];
|
||||||
|
if (!device || device.disabled_by) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const deviceEntry = {
|
const deviceEntry = {
|
||||||
open: false,
|
open: false,
|
||||||
entities:
|
entities:
|
||||||
@@ -1012,6 +1016,10 @@ export default class HaAutomationAddFromTarget extends LitElement {
|
|||||||
const devices: Record<string, Level3Entries> = {};
|
const devices: Record<string, Level3Entries> = {};
|
||||||
|
|
||||||
referenced_devices.forEach(({ id: deviceId }) => {
|
referenced_devices.forEach(({ id: deviceId }) => {
|
||||||
|
const device = this.devices[deviceId];
|
||||||
|
if (!device || device.disabled_by) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
devices[deviceId] = {
|
devices[deviceId] = {
|
||||||
open: false,
|
open: false,
|
||||||
entities:
|
entities:
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ export class HaAutomationAddItems extends LitElement {
|
|||||||
@property({ type: Boolean, attribute: "tooltip-description" })
|
@property({ type: Boolean, attribute: "tooltip-description" })
|
||||||
public tooltipDescription = false;
|
public tooltipDescription = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, reflect: true }) scrollable = false;
|
||||||
|
|
||||||
@state() private _itemsScrolled = false;
|
@state() private _itemsScrolled = false;
|
||||||
|
|
||||||
@query(".items")
|
@query(".items")
|
||||||
@@ -260,11 +262,12 @@ export class HaAutomationAddItems extends LitElement {
|
|||||||
:host {
|
:host {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
:host([scrollable]) .items {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
.items {
|
.items {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: auto;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.items.blank {
|
.items.blank {
|
||||||
|
|||||||
@@ -321,40 +321,39 @@ export class HaAutomationAddSearch extends LitElement {
|
|||||||
></ha-tree-indicator>
|
></ha-tree-indicator>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
${item.icon
|
${(item as AutomationItemComboBoxItem).renderedIcon
|
||||||
? html`<ha-icon slot="start" .icon=${item.icon}></ha-icon>`
|
? html`<div slot="start">
|
||||||
: item.icon_path
|
${(item as AutomationItemComboBoxItem).renderedIcon}
|
||||||
? html`<ha-svg-icon
|
</div>`
|
||||||
slot="start"
|
: item.icon
|
||||||
.path=${item.icon_path}
|
? html`<ha-icon slot="start" .icon=${item.icon}></ha-icon>`
|
||||||
></ha-svg-icon>`
|
: item.icon_path || type === "area"
|
||||||
: type === "entity" && (item as EntityComboBoxItem).stateObj
|
? html`<ha-svg-icon
|
||||||
? html`
|
slot="start"
|
||||||
<state-badge
|
.path=${item.icon_path || mdiTextureBox}
|
||||||
slot="start"
|
></ha-svg-icon>`
|
||||||
.stateObj=${(item as EntityComboBoxItem).stateObj}
|
: type === "entity" && (item as EntityComboBoxItem).stateObj
|
||||||
.hass=${this.hass}
|
|
||||||
></state-badge>
|
|
||||||
`
|
|
||||||
: type === "device" && (item as DevicePickerItem).domain
|
|
||||||
? html`
|
? html`
|
||||||
<ha-domain-icon
|
<state-badge
|
||||||
slot="start"
|
slot="start"
|
||||||
|
.stateObj=${(item as EntityComboBoxItem).stateObj}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.domain=${(item as DevicePickerItem).domain!}
|
></state-badge>
|
||||||
brand-fallback
|
|
||||||
></ha-domain-icon>
|
|
||||||
`
|
`
|
||||||
: type === "floor"
|
: type === "device" && (item as DevicePickerItem).domain
|
||||||
? html`<ha-floor-icon
|
? html`
|
||||||
slot="start"
|
<ha-domain-icon
|
||||||
.floor=${(item as FloorComboBoxItem).floor!}
|
|
||||||
></ha-floor-icon>`
|
|
||||||
: type === "area"
|
|
||||||
? html`<ha-svg-icon
|
|
||||||
slot="start"
|
slot="start"
|
||||||
.path=${item.icon_path || mdiTextureBox}
|
.hass=${this.hass}
|
||||||
></ha-svg-icon>`
|
.domain=${(item as DevicePickerItem).domain!}
|
||||||
|
brand-fallback
|
||||||
|
></ha-domain-icon>
|
||||||
|
`
|
||||||
|
: type === "floor"
|
||||||
|
? html`<ha-floor-icon
|
||||||
|
slot="start"
|
||||||
|
.floor=${(item as FloorComboBoxItem).floor!}
|
||||||
|
></ha-floor-icon>`
|
||||||
: nothing}
|
: nothing}
|
||||||
<span slot="headline">${item.primary}</span>
|
<span slot="headline">${item.primary}</span>
|
||||||
${item.secondary
|
${item.secondary
|
||||||
@@ -784,7 +783,7 @@ export class HaAutomationAddSearch extends LitElement {
|
|||||||
id: key,
|
id: key,
|
||||||
primary: name,
|
primary: name,
|
||||||
secondary: description,
|
secondary: description,
|
||||||
iconPath,
|
icon_path: iconPath,
|
||||||
renderedIcon: icon,
|
renderedIcon: icon,
|
||||||
type,
|
type,
|
||||||
search_labels: [key, name, description],
|
search_labels: [key, name, description],
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import {
|
|||||||
CONDITION_BUILDING_BLOCKS,
|
CONDITION_BUILDING_BLOCKS,
|
||||||
subscribeConditions,
|
subscribeConditions,
|
||||||
} from "../../../../data/condition";
|
} from "../../../../data/condition";
|
||||||
import { subscribeLabFeatures } from "../../../../data/labs";
|
import { subscribeLabFeature } from "../../../../data/labs";
|
||||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||||
import type { HomeAssistant } from "../../../../types";
|
import type { HomeAssistant } from "../../../../types";
|
||||||
import {
|
import {
|
||||||
@@ -90,14 +90,14 @@ export default class HaAutomationCondition extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
protected hassSubscribe() {
|
protected hassSubscribe() {
|
||||||
return [
|
return [
|
||||||
subscribeLabFeatures(this.hass!.connection, (features) => {
|
subscribeLabFeature(
|
||||||
this._newTriggersAndConditions =
|
this.hass!.connection,
|
||||||
features.find(
|
"automation",
|
||||||
(feature) =>
|
"new_triggers_conditions",
|
||||||
feature.domain === "automation" &&
|
(feature) => {
|
||||||
feature.preview_feature === "new_triggers_conditions"
|
this._newTriggersAndConditions = feature.enabled;
|
||||||
)?.enabled ?? false;
|
}
|
||||||
}),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -124,9 +124,9 @@ export class HaPlatformCondition extends LitElement {
|
|||||||
|
|
||||||
const hasOptional = Boolean(
|
const hasOptional = Boolean(
|
||||||
conditionDesc?.fields &&
|
conditionDesc?.fields &&
|
||||||
Object.values(conditionDesc.fields).some((field) =>
|
Object.values(conditionDesc.fields).some((field) =>
|
||||||
showOptionalToggle(field)
|
showOptionalToggle(field)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import {
|
|||||||
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import type { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { property, query, state } from "lit/decorators";
|
||||||
import { classMap } from "lit/directives/class-map";
|
import { classMap } from "lit/directives/class-map";
|
||||||
import { transform } from "../../../common/decorators/transform";
|
import { transform } from "../../../common/decorators/transform";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
@@ -112,7 +112,6 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-automation-editor")
|
|
||||||
export class HaAutomationEditor extends PreventUnsavedMixin(
|
export class HaAutomationEditor extends PreventUnsavedMixin(
|
||||||
KeyboardShortcutMixin(LitElement)
|
KeyboardShortcutMixin(LitElement)
|
||||||
) {
|
) {
|
||||||
@@ -1340,3 +1339,5 @@ export class HaAutomationEditor extends PreventUnsavedMixin(
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-automation-editor", HaAutomationEditor);
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
|||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import { slugify } from "../../../common/string/slugify";
|
|
||||||
import "../../../components/ha-tooltip";
|
|
||||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
import {
|
import {
|
||||||
hasRejectedItems,
|
hasRejectedItems,
|
||||||
@@ -329,19 +327,14 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
|
|||||||
const date = new Date(automation.last_triggered);
|
const date = new Date(automation.last_triggered);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const dayDifference = differenceInDays(now, date);
|
const dayDifference = differenceInDays(now, date);
|
||||||
const formattedTime = formatShortDateTimeWithConditionalYear(
|
|
||||||
date,
|
|
||||||
this.hass.locale,
|
|
||||||
this.hass.config
|
|
||||||
);
|
|
||||||
const elementId = "last-triggered-" + slugify(automation.entity_id);
|
|
||||||
return html`
|
return html`
|
||||||
${dayDifference > 3
|
${dayDifference > 3
|
||||||
? formattedTime
|
? formatShortDateTimeWithConditionalYear(
|
||||||
: html`
|
date,
|
||||||
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
|
this.hass.locale,
|
||||||
<span id=${elementId}>${relativeTime(date, locale)}</span>
|
this.hass.config
|
||||||
`}
|
)
|
||||||
|
: relativeTime(date, locale)}
|
||||||
`;
|
`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -97,9 +97,9 @@ export default class HaAutomationSidebarAction extends LitElement {
|
|||||||
title = `${domainToName(this.hass.localize, domain)}: ${
|
title = `${domainToName(this.hass.localize, domain)}: ${
|
||||||
this.hass.localize(
|
this.hass.localize(
|
||||||
`component.${domain}.services.${service}.name`,
|
`component.${domain}.services.${service}.name`,
|
||||||
this.hass.services[domain][service].description_placeholders
|
this.hass.services[domain]?.[service]?.description_placeholders
|
||||||
) ||
|
) ||
|
||||||
this.hass.services[domain][service]?.name ||
|
this.hass.services[domain]?.[service]?.name ||
|
||||||
title
|
title
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
type Trigger,
|
type Trigger,
|
||||||
type TriggerList,
|
type TriggerList,
|
||||||
} from "../../../../data/automation";
|
} from "../../../../data/automation";
|
||||||
import { subscribeLabFeatures } from "../../../../data/labs";
|
import { subscribeLabFeature } from "../../../../data/labs";
|
||||||
import type { TriggerDescriptions } from "../../../../data/trigger";
|
import type { TriggerDescriptions } from "../../../../data/trigger";
|
||||||
import { isTriggerList, subscribeTriggers } from "../../../../data/trigger";
|
import { isTriggerList, subscribeTriggers } from "../../../../data/trigger";
|
||||||
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../../../../mixins/subscribe-mixin";
|
||||||
@@ -85,14 +85,14 @@ export default class HaAutomationTrigger extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
protected hassSubscribe() {
|
protected hassSubscribe() {
|
||||||
return [
|
return [
|
||||||
subscribeLabFeatures(this.hass!.connection, (features) => {
|
subscribeLabFeature(
|
||||||
this._newTriggersAndConditions =
|
this.hass!.connection,
|
||||||
features.find(
|
"automation",
|
||||||
(feature) =>
|
"new_triggers_conditions",
|
||||||
feature.domain === "automation" &&
|
(feature) => {
|
||||||
feature.preview_feature === "new_triggers_conditions"
|
this._newTriggersAndConditions = feature.enabled;
|
||||||
)?.enabled ?? false;
|
}
|
||||||
}),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,9 +160,9 @@ export class HaPlatformTrigger extends LitElement {
|
|||||||
|
|
||||||
const hasOptional = Boolean(
|
const hasOptional = Boolean(
|
||||||
triggerDesc?.fields &&
|
triggerDesc?.fields &&
|
||||||
Object.values(triggerDesc.fields).some((field) =>
|
Object.values(triggerDesc.fields).some((field) =>
|
||||||
showOptionalToggle(field)
|
showOptionalToggle(field)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { mdiOpenInNew } from "@mdi/js";
|
import { mdiOpenInNew } from "@mdi/js";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||||
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
|
||||||
@@ -13,7 +13,6 @@ import type { WebhookDialogParams } from "./show-dialog-manage-cloudhook";
|
|||||||
import "../../../../components/ha-button";
|
import "../../../../components/ha-button";
|
||||||
import "../../../../components/ha-copy-textfield";
|
import "../../../../components/ha-copy-textfield";
|
||||||
|
|
||||||
@customElement("dialog-manage-cloudhook")
|
|
||||||
export class DialogManageCloudhook extends LitElement {
|
export class DialogManageCloudhook extends LitElement {
|
||||||
protected hass?: HomeAssistant;
|
protected hass?: HomeAssistant;
|
||||||
|
|
||||||
@@ -156,3 +155,5 @@ declare global {
|
|||||||
"dialog-manage-cloudhook": DialogManageCloudhook;
|
"dialog-manage-cloudhook": DialogManageCloudhook;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("dialog-manage-cloudhook", DialogManageCloudhook);
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
import {
|
import type { RequestSelectedDetail } from "@material/mwc-list/mwc-list-item";
|
||||||
mdiCheckboxBlankOutline,
|
import { mdiDotsVertical, mdiRefresh } from "@mdi/js";
|
||||||
mdiCheckboxMarked,
|
|
||||||
mdiDotsVertical,
|
|
||||||
mdiLocationEnter,
|
|
||||||
mdiLocationExit,
|
|
||||||
mdiRefresh,
|
|
||||||
} from "@mdi/js";
|
|
||||||
import type { HassEntities } from "home-assistant-js-websocket";
|
import type { HassEntities } from "home-assistant-js-websocket";
|
||||||
import type { TemplateResult } from "lit";
|
import type { TemplateResult } from "lit";
|
||||||
import { LitElement, css, html } from "lit";
|
import { LitElement, css, html } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
|
import { shouldHandleRequestSelectedEvent } from "../../../common/mwc/handle-request-selected-event";
|
||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
import "../../../components/ha-bar";
|
import "../../../components/ha-bar";
|
||||||
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
|
import "../../../components/ha-check-list-item";
|
||||||
|
import "../../../components/ha-list-item";
|
||||||
import "../../../components/ha-metric";
|
import "../../../components/ha-metric";
|
||||||
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
import { extractApiErrorMessage } from "../../../data/hassio/common";
|
||||||
import type {
|
import type {
|
||||||
@@ -35,9 +33,6 @@ import "../../../layouts/hass-subpage";
|
|||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import "../dashboard/ha-config-updates";
|
import "../dashboard/ha-config-updates";
|
||||||
import { showJoinBetaDialog } from "./updates/show-dialog-join-beta";
|
import { showJoinBetaDialog } from "./updates/show-dialog-join-beta";
|
||||||
import "../../../components/ha-dropdown";
|
|
||||||
import "../../../components/ha-dropdown-item";
|
|
||||||
import "@home-assistant/webawesome/dist/components/divider/divider";
|
|
||||||
|
|
||||||
@customElement("ha-config-section-updates")
|
@customElement("ha-config-section-updates")
|
||||||
class HaConfigSectionUpdates extends LitElement {
|
class HaConfigSectionUpdates extends LitElement {
|
||||||
@@ -78,44 +73,35 @@ class HaConfigSectionUpdates extends LitElement {
|
|||||||
.path=${mdiRefresh}
|
.path=${mdiRefresh}
|
||||||
@click=${this._checkUpdates}
|
@click=${this._checkUpdates}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
<ha-dropdown @wa-select=${this._handleOverflowAction}>
|
<ha-button-menu multi>
|
||||||
<ha-icon-button
|
<ha-icon-button
|
||||||
slot="trigger"
|
slot="trigger"
|
||||||
.label=${this.hass.localize("ui.common.menu")}
|
.label=${this.hass.localize("ui.common.menu")}
|
||||||
.path=${mdiDotsVertical}
|
.path=${mdiDotsVertical}
|
||||||
></ha-icon-button>
|
></ha-icon-button>
|
||||||
|
<ha-check-list-item
|
||||||
<ha-dropdown-item value="show_skipped">
|
left
|
||||||
<ha-svg-icon
|
@request-selected=${this._toggleSkipped}
|
||||||
.path=${this._showSkipped
|
.selected=${this._showSkipped}
|
||||||
? mdiCheckboxMarked
|
>
|
||||||
: mdiCheckboxBlankOutline}
|
|
||||||
slot="icon"
|
|
||||||
></ha-svg-icon>
|
|
||||||
${this.hass.localize("ui.panel.config.updates.show_skipped")}
|
${this.hass.localize("ui.panel.config.updates.show_skipped")}
|
||||||
</ha-dropdown-item>
|
</ha-check-list-item>
|
||||||
${this._supervisorInfo
|
${this._supervisorInfo
|
||||||
? html`
|
? html`
|
||||||
<wa-divider></wa-divider>
|
<li divider role="separator"></li>
|
||||||
<ha-dropdown-item
|
<ha-list-item
|
||||||
value="toggle_beta"
|
@request-selected=${this._toggleBeta}
|
||||||
.disabled=${this._supervisorInfo.channel === "dev"}
|
.disabled=${this._supervisorInfo.channel === "dev"}
|
||||||
>
|
>
|
||||||
<ha-svg-icon
|
|
||||||
.path=${this._supervisorInfo.channel === "stable"
|
|
||||||
? mdiLocationEnter
|
|
||||||
: mdiLocationExit}
|
|
||||||
slot="icon"
|
|
||||||
></ha-svg-icon>
|
|
||||||
${this._supervisorInfo.channel === "stable"
|
${this._supervisorInfo.channel === "stable"
|
||||||
? this.hass.localize("ui.panel.config.updates.join_beta")
|
? this.hass.localize("ui.panel.config.updates.join_beta")
|
||||||
: this.hass.localize(
|
: this.hass.localize(
|
||||||
"ui.panel.config.updates.leave_beta"
|
"ui.panel.config.updates.leave_beta"
|
||||||
)}
|
)}
|
||||||
</ha-dropdown-item>
|
</ha-list-item>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-dropdown>
|
</ha-button-menu>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<ha-card outlined>
|
<ha-card outlined>
|
||||||
@@ -147,19 +133,27 @@ class HaConfigSectionUpdates extends LitElement {
|
|||||||
this._supervisorInfo = await fetchHassioSupervisorInfo(this.hass);
|
this._supervisorInfo = await fetchHassioSupervisorInfo(this.hass);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _handleOverflowAction(
|
private _toggleSkipped(ev: CustomEvent<RequestSelectedDetail>): void {
|
||||||
ev: CustomEvent<{ item: { value: string } }>
|
if (ev.detail.source !== "property") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._showSkipped = !this._showSkipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _toggleBeta(
|
||||||
|
ev: CustomEvent<RequestSelectedDetail>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (ev.detail.item.value === "toggle_beta") {
|
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||||
if (this._supervisorInfo!.channel === "stable") {
|
return;
|
||||||
showJoinBetaDialog(this, {
|
}
|
||||||
join: async () => this._setChannel("beta"),
|
|
||||||
});
|
if (this._supervisorInfo!.channel === "stable") {
|
||||||
} else {
|
showJoinBetaDialog(this, {
|
||||||
this._setChannel("stable");
|
join: async () => this._setChannel("beta"),
|
||||||
}
|
});
|
||||||
} else if (ev.detail.item.value === "show_skipped") {
|
} else {
|
||||||
this._showSkipped = !this._showSkipped;
|
this._setChannel("stable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1138,20 +1138,23 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (domains.includes("mqtt")) {
|
if (domains.includes("mqtt")) {
|
||||||
const mqtt =
|
const mqtt = await import(
|
||||||
await import("./device-detail/integration-elements/mqtt/device-actions");
|
"./device-detail/integration-elements/mqtt/device-actions"
|
||||||
|
);
|
||||||
const actions = mqtt.getMQTTDeviceActions(this, device);
|
const actions = mqtt.getMQTTDeviceActions(this, device);
|
||||||
deviceActions.push(...actions);
|
deviceActions.push(...actions);
|
||||||
}
|
}
|
||||||
if (domains.includes("zha")) {
|
if (domains.includes("zha")) {
|
||||||
const zha =
|
const zha = await import(
|
||||||
await import("./device-detail/integration-elements/zha/device-actions");
|
"./device-detail/integration-elements/zha/device-actions"
|
||||||
|
);
|
||||||
const actions = await zha.getZHADeviceActions(this, this.hass, device);
|
const actions = await zha.getZHADeviceActions(this, this.hass, device);
|
||||||
deviceActions.push(...actions);
|
deviceActions.push(...actions);
|
||||||
}
|
}
|
||||||
if (domains.includes("zwave_js")) {
|
if (domains.includes("zwave_js")) {
|
||||||
const zwave =
|
const zwave = await import(
|
||||||
await import("./device-detail/integration-elements/zwave_js/device-actions");
|
"./device-detail/integration-elements/zwave_js/device-actions"
|
||||||
|
);
|
||||||
const actions = await zwave.getZwaveDeviceActions(
|
const actions = await zwave.getZwaveDeviceActions(
|
||||||
this,
|
this,
|
||||||
this.hass,
|
this.hass,
|
||||||
@@ -1160,8 +1163,9 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
deviceActions.push(...actions);
|
deviceActions.push(...actions);
|
||||||
}
|
}
|
||||||
if (domains.includes("esphome")) {
|
if (domains.includes("esphome")) {
|
||||||
const esphome =
|
const esphome = await import(
|
||||||
await import("./device-detail/integration-elements/esphome/device-actions");
|
"./device-detail/integration-elements/esphome/device-actions"
|
||||||
|
);
|
||||||
const actions = await esphome.getESPHomeDeviceActions(
|
const actions = await esphome.getESPHomeDeviceActions(
|
||||||
this,
|
this,
|
||||||
this.hass,
|
this.hass,
|
||||||
@@ -1170,8 +1174,9 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
deviceActions.push(...actions);
|
deviceActions.push(...actions);
|
||||||
}
|
}
|
||||||
if (domains.includes("matter")) {
|
if (domains.includes("matter")) {
|
||||||
const matter =
|
const matter = await import(
|
||||||
await import("./device-detail/integration-elements/matter/device-actions");
|
"./device-detail/integration-elements/matter/device-actions"
|
||||||
|
);
|
||||||
const defaultActions = matter.getMatterDeviceDefaultActions(
|
const defaultActions = matter.getMatterDeviceDefaultActions(
|
||||||
this,
|
this,
|
||||||
this.hass,
|
this.hass,
|
||||||
@@ -1215,8 +1220,9 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
).map((int) => int.domain);
|
).map((int) => int.domain);
|
||||||
|
|
||||||
if (domains.includes("zwave_js")) {
|
if (domains.includes("zwave_js")) {
|
||||||
const zwave =
|
const zwave = await import(
|
||||||
await import("./device-detail/integration-elements/zwave_js/device-alerts");
|
"./device-detail/integration-elements/zwave_js/device-alerts"
|
||||||
|
);
|
||||||
|
|
||||||
const alerts = await zwave.getZwaveDeviceAlerts(this.hass, device);
|
const alerts = await zwave.getZwaveDeviceAlerts(this.hass, device);
|
||||||
deviceAlerts.push(...alerts);
|
deviceAlerts.push(...alerts);
|
||||||
@@ -1298,7 +1304,9 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
if (domains.includes("zwave_js")) {
|
if (domains.includes("zwave_js")) {
|
||||||
import("./device-detail/integration-elements/zwave_js/ha-device-info-zwave_js");
|
import(
|
||||||
|
"./device-detail/integration-elements/zwave_js/ha-device-info-zwave_js"
|
||||||
|
);
|
||||||
deviceInfo.push(html`
|
deviceInfo.push(html`
|
||||||
<ha-device-info-zwave_js
|
<ha-device-info-zwave_js
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -1307,7 +1315,9 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
if (domains.includes("matter")) {
|
if (domains.includes("matter")) {
|
||||||
import("./device-detail/integration-elements/matter/ha-device-info-matter");
|
import(
|
||||||
|
"./device-detail/integration-elements/matter/ha-device-info-matter"
|
||||||
|
);
|
||||||
deviceInfo.push(html`
|
deviceInfo.push(html`
|
||||||
<ha-device-info-matter
|
<ha-device-info-matter
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
|||||||
@@ -1012,6 +1012,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
|||||||
? html`<ha-area-picker
|
? html`<ha-area-picker
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.value=${this._areaId}
|
.value=${this._areaId}
|
||||||
|
.placeholder=${this._device?.area_id}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
@value-changed=${this._areaPicked}
|
@value-changed=${this._areaPicked}
|
||||||
></ha-area-picker>`
|
></ha-area-picker>`
|
||||||
|
|||||||
@@ -116,10 +116,8 @@ import { showAddIntegrationDialog } from "../integrations/show-add-integration-d
|
|||||||
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
import { showLabelDetailDialog } from "../labels/show-dialog-label-detail";
|
||||||
import { slugify } from "../../../common/string/slugify";
|
import { slugify } from "../../../common/string/slugify";
|
||||||
|
|
||||||
export interface StateEntity extends Omit<
|
export interface StateEntity
|
||||||
EntityRegistryEntry,
|
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
|
||||||
"id" | "unique_id"
|
|
||||||
> {
|
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
selectable?: boolean;
|
selectable?: boolean;
|
||||||
id?: string;
|
id?: string;
|
||||||
|
|||||||
@@ -530,7 +530,9 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
|
|||||||
zha: {
|
zha: {
|
||||||
tag: "zha-config-dashboard-router",
|
tag: "zha-config-dashboard-router",
|
||||||
load: () =>
|
load: () =>
|
||||||
import("./integrations/integration-panels/zha/zha-config-dashboard-router"),
|
import(
|
||||||
|
"./integrations/integration-panels/zha/zha-config-dashboard-router"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
mqtt: {
|
mqtt: {
|
||||||
tag: "mqtt-config-panel",
|
tag: "mqtt-config-panel",
|
||||||
@@ -540,22 +542,30 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
|
|||||||
zwave_js: {
|
zwave_js: {
|
||||||
tag: "zwave_js-config-router",
|
tag: "zwave_js-config-router",
|
||||||
load: () =>
|
load: () =>
|
||||||
import("./integrations/integration-panels/zwave_js/zwave_js-config-router"),
|
import(
|
||||||
|
"./integrations/integration-panels/zwave_js/zwave_js-config-router"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
matter: {
|
matter: {
|
||||||
tag: "matter-config-panel",
|
tag: "matter-config-panel",
|
||||||
load: () =>
|
load: () =>
|
||||||
import("./integrations/integration-panels/matter/matter-config-panel"),
|
import(
|
||||||
|
"./integrations/integration-panels/matter/matter-config-panel"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
thread: {
|
thread: {
|
||||||
tag: "thread-config-panel",
|
tag: "thread-config-panel",
|
||||||
load: () =>
|
load: () =>
|
||||||
import("./integrations/integration-panels/thread/thread-config-panel"),
|
import(
|
||||||
|
"./integrations/integration-panels/thread/thread-config-panel"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
bluetooth: {
|
bluetooth: {
|
||||||
tag: "bluetooth-config-dashboard-router",
|
tag: "bluetooth-config-dashboard-router",
|
||||||
load: () =>
|
load: () =>
|
||||||
import("./integrations/integration-panels/bluetooth/bluetooth-config-dashboard-router"),
|
import(
|
||||||
|
"./integrations/integration-panels/bluetooth/bluetooth-config-dashboard-router"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
dhcp: {
|
dhcp: {
|
||||||
tag: "dhcp-config-panel",
|
tag: "dhcp-config-panel",
|
||||||
@@ -570,7 +580,9 @@ class HaPanelConfig extends SubscribeMixin(HassRouterPage) {
|
|||||||
zeroconf: {
|
zeroconf: {
|
||||||
tag: "zeroconf-config-panel",
|
tag: "zeroconf-config-panel",
|
||||||
load: () =>
|
load: () =>
|
||||||
import("./integrations/integration-panels/zeroconf/zeroconf-config-panel"),
|
import(
|
||||||
|
"./integrations/integration-panels/zeroconf/zeroconf-config-panel"
|
||||||
|
),
|
||||||
},
|
},
|
||||||
application_credentials: {
|
application_credentials: {
|
||||||
tag: "ha-config-application-credentials",
|
tag: "ha-config-application-credentials",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { html, LitElement, nothing } from "lit";
|
import { html, LitElement, nothing } from "lit";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { createCloseHeading } from "../../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../../components/ha-dialog";
|
||||||
import "../../../../components/ha-form/ha-form";
|
import "../../../../components/ha-form/ha-form";
|
||||||
@@ -14,7 +14,6 @@ import type {
|
|||||||
} from "./show-dialog-schedule-block-info";
|
} from "./show-dialog-schedule-block-info";
|
||||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||||
|
|
||||||
@customElement("dialog-schedule-block-info")
|
|
||||||
class DialogScheduleBlockInfo extends LitElement {
|
class DialogScheduleBlockInfo extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -157,3 +156,5 @@ declare global {
|
|||||||
"dialog-schedule-block-info": DialogScheduleBlockInfo;
|
"dialog-schedule-block-info": DialogScheduleBlockInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("dialog-schedule-block-info", DialogScheduleBlockInfo);
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ export class MatterAddDevice extends HTMLElement {
|
|||||||
public hass!: HomeAssistant;
|
public hass!: HomeAssistant;
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
showMatterAddDeviceDialog(this);
|
navigate("/config/devices/dashboard", {
|
||||||
navigate(`/config/devices`, {
|
|
||||||
replace: true,
|
replace: true,
|
||||||
});
|
});
|
||||||
|
showMatterAddDeviceDialog(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { CSSResultGroup, PropertyValues } from "lit";
|
import type { CSSResultGroup, PropertyValues } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||||
import "../../../../../components/buttons/ha-call-service-button";
|
import "../../../../../components/buttons/ha-call-service-button";
|
||||||
import "../../../../../components/ha-card";
|
import "../../../../../components/ha-card";
|
||||||
@@ -15,7 +15,6 @@ import type { HomeAssistant } from "../../../../../types";
|
|||||||
import { formatAsPaddedHex } from "./functions";
|
import { formatAsPaddedHex } from "./functions";
|
||||||
import type { IssueCommandServiceData } from "./types";
|
import type { IssueCommandServiceData } from "./types";
|
||||||
|
|
||||||
@customElement("zha-cluster-commands")
|
|
||||||
export class ZHAClusterCommands extends LitElement {
|
export class ZHAClusterCommands extends LitElement {
|
||||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@@ -260,3 +259,5 @@ declare global {
|
|||||||
"zha-cluster-commands": ZHAClusterCommands;
|
"zha-cluster-commands": ZHAClusterCommands;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("zha-cluster-commands", ZHAClusterCommands);
|
||||||
|
|||||||
@@ -192,9 +192,9 @@ export class ZHAGroupBindingControl extends LitElement {
|
|||||||
private get _canBind(): boolean {
|
private get _canBind(): boolean {
|
||||||
return Boolean(
|
return Boolean(
|
||||||
this._groupToBind &&
|
this._groupToBind &&
|
||||||
this._clustersToBind &&
|
this._clustersToBind &&
|
||||||
this._clustersToBind?.length > 0 &&
|
this._clustersToBind?.length > 0 &&
|
||||||
this.device
|
this.device
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { mdiClose, mdiContentCopy } from "@mdi/js";
|
import { mdiClose, mdiContentCopy } from "@mdi/js";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
@@ -26,7 +26,6 @@ import { showToast } from "../../../util/toast";
|
|||||||
import type { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail";
|
import type { SystemLogDetailDialogParams } from "./show-dialog-system-log-detail";
|
||||||
import { formatSystemLogTime } from "./util";
|
import { formatSystemLogTime } from "./util";
|
||||||
|
|
||||||
@customElement("dialog-system-log-detail")
|
|
||||||
class DialogSystemLogDetail extends LitElement {
|
class DialogSystemLogDetail extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -273,3 +272,5 @@ declare global {
|
|||||||
"dialog-system-log-detail": DialogSystemLogDetail;
|
"dialog-system-log-detail": DialogSystemLogDetail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("dialog-system-log-detail", DialogSystemLogDetail);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { mdiPencil } from "@mdi/js";
|
import { mdiPencil } from "@mdi/js";
|
||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import "../../../components/entity/ha-entities-picker";
|
import "../../../components/entity/ha-entities-picker";
|
||||||
@@ -43,7 +43,6 @@ const cropOptions: CropOptions = {
|
|||||||
aspectRatio: 1,
|
aspectRatio: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
@customElement("dialog-person-detail")
|
|
||||||
class DialogPersonDetail extends LitElement implements HassDialog {
|
class DialogPersonDetail extends LitElement implements HassDialog {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -568,3 +567,5 @@ declare global {
|
|||||||
"dialog-person-detail": DialogPersonDetail;
|
"dialog-person-detail": DialogPersonDetail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("dialog-person-detail", DialogPersonDetail);
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import { LitElement, css, html, nothing } from "lit";
|
|||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { computeCssColor } from "../../../common/color/compute-color";
|
import { computeCssColor } from "../../../common/color/compute-color";
|
||||||
import { formatShortDateTimeWithConditionalYear } from "../../../common/datetime/format_date_time";
|
import { formatShortDateTime } from "../../../common/datetime/format_date_time";
|
||||||
import { relativeTime } from "../../../common/datetime/relative_time";
|
import { relativeTime } from "../../../common/datetime/relative_time";
|
||||||
import { storage } from "../../../common/decorators/storage";
|
import { storage } from "../../../common/decorators/storage";
|
||||||
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
@@ -301,21 +301,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
|
|||||||
const date = new Date(scene.state);
|
const date = new Date(scene.state);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const dayDifference = differenceInDays(now, date);
|
const dayDifference = differenceInDays(now, date);
|
||||||
const formattedTime = formatShortDateTimeWithConditionalYear(
|
|
||||||
date,
|
|
||||||
this.hass.locale,
|
|
||||||
this.hass.config
|
|
||||||
);
|
|
||||||
const elementId = "last-activated-" + slugify(scene.entity_id);
|
|
||||||
return html`
|
return html`
|
||||||
${dayDifference > 3
|
${dayDifference > 3
|
||||||
? formattedTime
|
? formatShortDateTime(date, this.hass.locale, this.hass.config)
|
||||||
: html`
|
: relativeTime(date, this.hass.locale)}
|
||||||
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
|
|
||||||
<span id=${elementId}
|
|
||||||
>${relativeTime(date, this.hass.locale)}</span
|
|
||||||
>
|
|
||||||
`}
|
|
||||||
`;
|
`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
|
||||||
import { LitElement, css, html, nothing } from "lit";
|
import { LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { property, query, state } 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 { goBack, navigate } from "../../../common/navigate";
|
import { goBack, navigate } from "../../../common/navigate";
|
||||||
@@ -79,7 +79,6 @@ import "./manual-script-editor";
|
|||||||
import type { HaManualScriptEditor } from "./manual-script-editor";
|
import type { HaManualScriptEditor } from "./manual-script-editor";
|
||||||
import { showAutomationSaveTimeoutDialog } from "../automation/automation-save-timeout-dialog/show-dialog-automation-save-timeout";
|
import { showAutomationSaveTimeoutDialog } from "../automation/automation-save-timeout-dialog/show-dialog-automation-save-timeout";
|
||||||
|
|
||||||
@customElement("ha-script-editor")
|
|
||||||
export class HaScriptEditor extends SubscribeMixin(
|
export class HaScriptEditor extends SubscribeMixin(
|
||||||
PreventUnsavedMixin(KeyboardShortcutMixin(LitElement))
|
PreventUnsavedMixin(KeyboardShortcutMixin(LitElement))
|
||||||
) {
|
) {
|
||||||
@@ -1279,6 +1278,8 @@ export class HaScriptEditor extends SubscribeMixin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-script-editor", HaScriptEditor);
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-script-editor": HaScriptEditor;
|
"ha-script-editor": HaScriptEditor;
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ import type { HASSDomEvent } from "../../../common/dom/fire_event";
|
|||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import { slugify } from "../../../common/string/slugify";
|
|
||||||
import "../../../components/ha-tooltip";
|
|
||||||
import type { LocalizeFunc } from "../../../common/translations/localize";
|
import type { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
import {
|
import {
|
||||||
hasRejectedItems,
|
hasRejectedItems,
|
||||||
@@ -304,27 +302,19 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
title: localize("ui.card.automation.last_triggered"),
|
title: localize("ui.card.automation.last_triggered"),
|
||||||
template: (script) => {
|
template: (script) => {
|
||||||
if (!script.last_triggered) {
|
|
||||||
return this.hass.localize("ui.components.relative_time.never");
|
|
||||||
}
|
|
||||||
const date = new Date(script.last_triggered);
|
const date = new Date(script.last_triggered);
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const dayDifference = differenceInDays(now, date);
|
const dayDifference = differenceInDays(now, date);
|
||||||
const formattedTime = formatShortDateTimeWithConditionalYear(
|
|
||||||
date,
|
|
||||||
this.hass.locale,
|
|
||||||
this.hass.config
|
|
||||||
);
|
|
||||||
const elementId = "last-triggered-" + slugify(script.entity_id);
|
|
||||||
return html`
|
return html`
|
||||||
${dayDifference > 3
|
${script.last_triggered
|
||||||
? formattedTime
|
? dayDifference > 3
|
||||||
: html`
|
? formatShortDateTimeWithConditionalYear(
|
||||||
<ha-tooltip for=${elementId}>${formattedTime}</ha-tooltip>
|
date,
|
||||||
<span id=${elementId}
|
this.hass.locale,
|
||||||
>${relativeTime(date, this.hass.locale)}</span
|
this.hass.config
|
||||||
>
|
)
|
||||||
`}
|
: relativeTime(date, this.hass.locale)
|
||||||
|
: this.hass.localize("ui.components.relative_time.never")}
|
||||||
`;
|
`;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { mdiHelpCircle } from "@mdi/js";
|
import { mdiHelpCircle } from "@mdi/js";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { isEmptyEntityDomainFilter } from "../../../common/entity/entity_domain_filter";
|
import { isEmptyEntityDomainFilter } from "../../../common/entity/entity_domain_filter";
|
||||||
@@ -20,7 +20,6 @@ import {
|
|||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { brandsUrl } from "../../../util/brands-url";
|
import { brandsUrl } from "../../../util/brands-url";
|
||||||
|
|
||||||
@customElement("cloud-alexa-pref")
|
|
||||||
export class CloudAlexaPref extends LitElement {
|
export class CloudAlexaPref extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -298,3 +297,5 @@ declare global {
|
|||||||
"cloud-alexa-pref": CloudAlexaPref;
|
"cloud-alexa-pref": CloudAlexaPref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("cloud-alexa-pref", CloudAlexaPref);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { mdiHelpCircle } from "@mdi/js";
|
import { mdiHelpCircle } from "@mdi/js";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { isEmptyEntityDomainFilter } from "../../../common/entity/entity_domain_filter";
|
import { isEmptyEntityDomainFilter } from "../../../common/entity/entity_domain_filter";
|
||||||
@@ -23,7 +23,6 @@ import type { HomeAssistant } from "../../../types";
|
|||||||
import { brandsUrl } from "../../../util/brands-url";
|
import { brandsUrl } from "../../../util/brands-url";
|
||||||
import { showSaveSuccessToast } from "../../../util/toast-saved-success";
|
import { showSaveSuccessToast } from "../../../util/toast-saved-success";
|
||||||
|
|
||||||
@customElement("cloud-google-pref")
|
|
||||||
export class CloudGooglePref extends LitElement {
|
export class CloudGooglePref extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -368,3 +367,5 @@ declare global {
|
|||||||
"cloud-google-pref": CloudGooglePref;
|
"cloud-google-pref": CloudGooglePref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("cloud-google-pref", CloudGooglePref);
|
||||||
|
|||||||
@@ -215,57 +215,62 @@ export class AssistPipelineDebug extends LitElement {
|
|||||||
? html`
|
? html`
|
||||||
<div class="messages">
|
<div class="messages">
|
||||||
${messages.map((content) =>
|
${messages.map((content) =>
|
||||||
content.role === "system" || content.role === "tool_result"
|
content.role === "system"
|
||||||
? html`
|
? content.content
|
||||||
<ha-expansion-panel
|
? html`
|
||||||
class="content-expansion ${content.role}"
|
<ha-expansion-panel
|
||||||
>
|
class="content-expansion ${content.role}"
|
||||||
<div slot="header">
|
>
|
||||||
${content.role === "system"
|
<div slot="header">System</div>
|
||||||
? "System"
|
<pre>${content.content}</pre>
|
||||||
: `Result for ${content.tool_name}`}
|
</ha-expansion-panel>
|
||||||
</div>
|
`
|
||||||
${content.role === "system"
|
: nothing
|
||||||
? html`<pre>${content.content}</pre>`
|
: content.role === "tool_result"
|
||||||
: html`
|
? html`
|
||||||
<ha-yaml-editor
|
<ha-expansion-panel
|
||||||
read-only
|
class="content-expansion ${content.role}"
|
||||||
auto-update
|
>
|
||||||
.value=${content}
|
<div slot="header">
|
||||||
></ha-yaml-editor>
|
Result for ${content.tool_name}
|
||||||
`}
|
</div>
|
||||||
</ha-expansion-panel>
|
<ha-yaml-editor
|
||||||
`
|
read-only
|
||||||
: html`
|
auto-update
|
||||||
${content.content
|
.value=${content}
|
||||||
? html`
|
></ha-yaml-editor>
|
||||||
<div class=${`message ${content.role}`}>
|
</ha-expansion-panel>
|
||||||
${content.content}
|
`
|
||||||
</div>
|
: html`
|
||||||
`
|
${content.content
|
||||||
: nothing}
|
? html`
|
||||||
${content.role === "assistant" &&
|
<div class=${`message ${content.role}`}>
|
||||||
content.tool_calls?.length
|
${content.content}
|
||||||
? html`
|
</div>
|
||||||
<ha-expansion-panel
|
`
|
||||||
class="content-expansion assistant"
|
: nothing}
|
||||||
>
|
${content.role === "assistant" &&
|
||||||
<span slot="header">
|
content.tool_calls?.length
|
||||||
Call
|
? html`
|
||||||
${content.tool_calls.length === 1
|
<ha-expansion-panel
|
||||||
? content.tool_calls[0].tool_name
|
class="content-expansion assistant"
|
||||||
: `${content.tool_calls.length} tools`}
|
>
|
||||||
</span>
|
<span slot="header">
|
||||||
|
Call
|
||||||
|
${content.tool_calls.length === 1
|
||||||
|
? content.tool_calls[0].tool_name
|
||||||
|
: `${content.tool_calls.length} tools`}
|
||||||
|
</span>
|
||||||
|
|
||||||
<ha-yaml-editor
|
<ha-yaml-editor
|
||||||
read-only
|
read-only
|
||||||
auto-update
|
auto-update
|
||||||
.value=${content.tool_calls}
|
.value=${content.tool_calls}
|
||||||
></ha-yaml-editor>
|
></ha-yaml-editor>
|
||||||
</ha-expansion-panel>
|
</ha-expansion-panel>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div style="clear:both"></div>
|
<div style="clear:both"></div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { createCloseHeading } from "../../../components/ha-dialog";
|
import { createCloseHeading } from "../../../components/ha-dialog";
|
||||||
@@ -19,7 +19,6 @@ const SCHEMA = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("dialog-home-zone-detail")
|
|
||||||
class DialogHomeZoneDetail extends LitElement {
|
class DialogHomeZoneDetail extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -152,3 +151,5 @@ declare global {
|
|||||||
"dialog-home-zone-detail": DialogHomeZoneDetail;
|
"dialog-home-zone-detail": DialogHomeZoneDetail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("dialog-home-zone-detail", DialogHomeZoneDetail);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { CSSResultGroup } from "lit";
|
import type { CSSResultGroup } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { fireEvent } from "../../../common/dom/fire_event";
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
import { addDistanceToCoord } from "../../../common/location/add_distance_to_coord";
|
import { addDistanceToCoord } from "../../../common/location/add_distance_to_coord";
|
||||||
@@ -14,7 +14,6 @@ import { haStyleDialog } from "../../../resources/styles";
|
|||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import type { ZoneDetailDialogParams } from "./show-dialog-zone-detail";
|
import type { ZoneDetailDialogParams } from "./show-dialog-zone-detail";
|
||||||
|
|
||||||
@customElement("dialog-zone-detail")
|
|
||||||
class DialogZoneDetail extends LitElement {
|
class DialogZoneDetail extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -242,3 +241,5 @@ declare global {
|
|||||||
"dialog-zone-detail": DialogZoneDetail;
|
"dialog-zone-detail": DialogZoneDetail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("dialog-zone-detail", DialogZoneDetail);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { ReactiveElement } from "lit";
|
import { ReactiveElement } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { property } from "lit/decorators";
|
||||||
import type { NavigateOptions } from "../../common/navigate";
|
import type { NavigateOptions } from "../../common/navigate";
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
import { deepEqual } from "../../common/util/deep-equal";
|
import { deepEqual } from "../../common/util/deep-equal";
|
||||||
@@ -22,7 +22,6 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-panel-custom")
|
|
||||||
export class HaPanelCustom extends ReactiveElement {
|
export class HaPanelCustom extends ReactiveElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -172,3 +171,5 @@ export class HaPanelCustom extends ReactiveElement {
|
|||||||
iframeDoc.close();
|
iframeDoc.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-panel-custom", HaPanelCustom);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { mdiHelpCircle } from "@mdi/js";
|
import { mdiHelpCircle } from "@mdi/js";
|
||||||
import type { HassService } from "home-assistant-js-websocket";
|
import type { HassService } from "home-assistant-js-websocket";
|
||||||
import { ERR_CONNECTION_LOST } from "home-assistant-js-websocket";
|
import { ERR_CONNECTION_LOST } from "home-assistant-js-websocket";
|
||||||
import { dump, load } from "js-yaml";
|
import { load } from "js-yaml";
|
||||||
import type { CSSResultGroup, TemplateResult } from "lit";
|
import type { CSSResultGroup, TemplateResult } from "lit";
|
||||||
import { css, html, LitElement, nothing } from "lit";
|
import { css, html, LitElement, nothing } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
@@ -320,10 +320,7 @@ class HaPanelDevAction extends LitElement {
|
|||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
`component.${domain}.services.${serviceName}.fields.${field.key}.example`,
|
`component.${domain}.services.${serviceName}.fields.${field.key}.example`,
|
||||||
descriptionPlaceholders
|
descriptionPlaceholders
|
||||||
) ||
|
) || field.example}
|
||||||
(typeof field.example === "object"
|
|
||||||
? html`<pre>${dump(field.example)}</pre>`
|
|
||||||
: field.example)}
|
|
||||||
</td>
|
</td>
|
||||||
</tr>`
|
</tr>`
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -547,9 +547,9 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
|
|||||||
} else if (this._sortDirection === "asc") {
|
} else if (this._sortDirection === "asc") {
|
||||||
this._sortDirection = "desc";
|
this._sortDirection = "desc";
|
||||||
} else {
|
} else {
|
||||||
this._sortDirection = "asc";
|
this._sortDirection = null;
|
||||||
}
|
}
|
||||||
this._sortColumn = columnId;
|
this._sortColumn = this._sortDirection === null ? undefined : columnId;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleGroupBy(ev) {
|
private _handleGroupBy(ev) {
|
||||||
|
|||||||
@@ -268,8 +268,10 @@ class PanelEnergy extends LitElement {
|
|||||||
(source) => source.type === "gas"
|
(source) => source.type === "gas"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hasDeviceConsumption = this._prefs.device_consumption.length > 0;
|
||||||
|
|
||||||
const views: LovelaceViewConfig[] = [];
|
const views: LovelaceViewConfig[] = [];
|
||||||
if (hasEnergy) {
|
if (hasEnergy || hasDeviceConsumption) {
|
||||||
views.push(ENERGY_VIEW);
|
views.push(ENERGY_VIEW);
|
||||||
}
|
}
|
||||||
if (hasGas) {
|
if (hasGas) {
|
||||||
|
|||||||
@@ -2,20 +2,12 @@ import { ReactiveElement } from "lit";
|
|||||||
import { customElement } from "lit/decorators";
|
import { customElement } from "lit/decorators";
|
||||||
import type { GridSourceTypeEnergyPreference } from "../../../data/energy";
|
import type { GridSourceTypeEnergyPreference } from "../../../data/energy";
|
||||||
import { getEnergyDataCollection } from "../../../data/energy";
|
import { getEnergyDataCollection } from "../../../data/energy";
|
||||||
import type { HomeAssistant } from "../../../types";
|
|
||||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
|
||||||
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
|
||||||
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
|
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
|
||||||
|
import type { LovelaceStrategyConfig } from "../../../data/lovelace/config/strategy";
|
||||||
|
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||||
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
import { DEFAULT_ENERGY_COLLECTION_KEY } from "../ha-panel-energy";
|
||||||
|
|
||||||
const sourceHasCost = (source: Record<string, any>): boolean =>
|
|
||||||
Boolean(
|
|
||||||
source.stat_cost ||
|
|
||||||
source.stat_compensation ||
|
|
||||||
source.entity_energy_price ||
|
|
||||||
source.number_energy_price
|
|
||||||
);
|
|
||||||
|
|
||||||
@customElement("energy-overview-view-strategy")
|
@customElement("energy-overview-view-strategy")
|
||||||
export class EnergyOverviewViewStrategy extends ReactiveElement {
|
export class EnergyOverviewViewStrategy extends ReactiveElement {
|
||||||
static async generate(
|
static async generate(
|
||||||
@@ -35,6 +27,9 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
|||||||
const energyCollection = getEnergyDataCollection(hass, {
|
const energyCollection = getEnergyDataCollection(hass, {
|
||||||
key: collectionKey,
|
key: collectionKey,
|
||||||
});
|
});
|
||||||
|
if (!energyCollection.prefs) {
|
||||||
|
await energyCollection.refresh();
|
||||||
|
}
|
||||||
const prefs = energyCollection.prefs;
|
const prefs = energyCollection.prefs;
|
||||||
|
|
||||||
// No energy sources available
|
// No energy sources available
|
||||||
@@ -68,13 +63,6 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
|||||||
(source.type === "battery" && source.stat_rate) ||
|
(source.type === "battery" && source.stat_rate) ||
|
||||||
(source.type === "grid" && source.power?.length)
|
(source.type === "grid" && source.power?.length)
|
||||||
);
|
);
|
||||||
const hasCost = prefs.energy_sources.some(
|
|
||||||
(source) =>
|
|
||||||
sourceHasCost(source) ||
|
|
||||||
(source.type === "grid" &&
|
|
||||||
(source.flow_from?.some(sourceHasCost) ||
|
|
||||||
source.flow_to?.some(sourceHasCost)))
|
|
||||||
);
|
|
||||||
|
|
||||||
const overviewSection: LovelaceSectionConfig = {
|
const overviewSection: LovelaceSectionConfig = {
|
||||||
type: "grid",
|
type: "grid",
|
||||||
@@ -95,7 +83,7 @@ export class EnergyOverviewViewStrategy extends ReactiveElement {
|
|||||||
}
|
}
|
||||||
view.sections!.push(overviewSection);
|
view.sections!.push(overviewSection);
|
||||||
|
|
||||||
if (hasCost) {
|
if (prefs.energy_sources.length) {
|
||||||
view.sections!.push({
|
view.sections!.push({
|
||||||
type: "grid",
|
type: "grid",
|
||||||
cards: [
|
cards: [
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ export class EnergyViewStrategy extends ReactiveElement {
|
|||||||
const energyCollection = getEnergyDataCollection(hass, {
|
const energyCollection = getEnergyDataCollection(hass, {
|
||||||
key: collectionKey,
|
key: collectionKey,
|
||||||
});
|
});
|
||||||
|
if (!energyCollection.prefs) {
|
||||||
|
await energyCollection.refresh();
|
||||||
|
}
|
||||||
const prefs = energyCollection.prefs;
|
const prefs = energyCollection.prefs;
|
||||||
|
|
||||||
// No energy sources available
|
// No energy sources available
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ export class GasViewStrategy extends ReactiveElement {
|
|||||||
const energyCollection = getEnergyDataCollection(hass, {
|
const energyCollection = getEnergyDataCollection(hass, {
|
||||||
key: collectionKey,
|
key: collectionKey,
|
||||||
});
|
});
|
||||||
|
if (!energyCollection.prefs) {
|
||||||
|
await energyCollection.refresh();
|
||||||
|
}
|
||||||
const prefs = energyCollection.prefs;
|
const prefs = energyCollection.prefs;
|
||||||
|
|
||||||
const hasGasSources = prefs?.energy_sources.some(
|
const hasGasSources = prefs?.energy_sources.some(
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ export class WaterViewStrategy extends ReactiveElement {
|
|||||||
const energyCollection = getEnergyDataCollection(hass, {
|
const energyCollection = getEnergyDataCollection(hass, {
|
||||||
key: collectionKey,
|
key: collectionKey,
|
||||||
});
|
});
|
||||||
|
if (!energyCollection.prefs) {
|
||||||
|
await energyCollection.refresh();
|
||||||
|
}
|
||||||
const prefs = energyCollection.prefs;
|
const prefs = energyCollection.prefs;
|
||||||
|
|
||||||
const hasWaterSources = prefs?.energy_sources.some(
|
const hasWaterSources = prefs?.energy_sources.some(
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import type {
|
|||||||
} from "home-assistant-js-websocket/dist/types";
|
} from "home-assistant-js-websocket/dist/types";
|
||||||
import type { PropertyValues } from "lit";
|
import type { PropertyValues } from "lit";
|
||||||
import { LitElement, css, html } from "lit";
|
import { LitElement, css, html } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { property, query, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { ensureArray } from "../../common/array/ensure-array";
|
import { ensureArray } from "../../common/array/ensure-array";
|
||||||
import { storage } from "../../common/decorators/storage";
|
import { storage } from "../../common/decorators/storage";
|
||||||
@@ -52,7 +52,6 @@ import type { HomeAssistant } from "../../types";
|
|||||||
import { fileDownload } from "../../util/file_download";
|
import { fileDownload } from "../../util/file_download";
|
||||||
import { addEntitiesToLovelaceView } from "../lovelace/editor/add-entities-to-view";
|
import { addEntitiesToLovelaceView } from "../lovelace/editor/add-entities-to-view";
|
||||||
|
|
||||||
@customElement("ha-panel-history")
|
|
||||||
class HaPanelHistory extends LitElement {
|
class HaPanelHistory extends LitElement {
|
||||||
@property({ attribute: false }) hass!: HomeAssistant;
|
@property({ attribute: false }) hass!: HomeAssistant;
|
||||||
|
|
||||||
@@ -632,6 +631,7 @@ class HaPanelHistory extends LitElement {
|
|||||||
|
|
||||||
:host([virtualize]) {
|
:host([virtualize]) {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
--ha-generic-picker-max-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-wrapper {
|
.progress-wrapper {
|
||||||
@@ -680,6 +680,8 @@ class HaPanelHistory extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-panel-history", HaPanelHistory);
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-panel-history": HaPanelHistory;
|
"ha-panel-history": HaPanelHistory;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user