Compare commits
1 Commits
20220624.0
...
add-Use-UU
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1f6243145f |
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -51,7 +51,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
|
|||||||
<!--
|
<!--
|
||||||
Provide details about the versions you are using, which helps us reproducing
|
Provide details about the versions you are using, which helps us reproducing
|
||||||
and finding the issue quicker. Version information is found in the
|
and finding the issue quicker. Version information is found in the
|
||||||
Home Assistant frontend: Settings -> About.
|
Home Assistant frontend: Configuration -> Info.
|
||||||
|
|
||||||
Browser version and operating system is important! Please try to replicate
|
Browser version and operating system is important! Please try to replicate
|
||||||
your issue in a different browser and be sure to include your findings.
|
your issue in a different browser and be sure to include your findings.
|
||||||
|
6
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Report a bug with the UI / Dashboards
|
name: Report a bug with the UI, Frontend or Lovelace
|
||||||
description: Report an issue related to the Home Assistant frontend.
|
description: Report an issue related to the Home Assistant frontend.
|
||||||
labels: bug
|
labels: bug
|
||||||
body:
|
body:
|
||||||
@@ -9,7 +9,7 @@ body:
|
|||||||
|
|
||||||
If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue.
|
If you have a feature or enhancement request for the frontend, please [start an discussion][fr] instead of creating an issue.
|
||||||
|
|
||||||
**Please not not report issues for custom cards.**
|
**Please not not report issues for custom Lovelace cards.**
|
||||||
|
|
||||||
[fr]: https://github.com/home-assistant/frontend/discussions
|
[fr]: https://github.com/home-assistant/frontend/discussions
|
||||||
[releases]: https://github.com/home-assistant/home-assistant/releases
|
[releases]: https://github.com/home-assistant/home-assistant/releases
|
||||||
@@ -64,7 +64,7 @@ body:
|
|||||||
label: What version of Home Assistant Core has the issue?
|
label: What version of Home Assistant Core has the issue?
|
||||||
placeholder: core-
|
placeholder: core-
|
||||||
description: >
|
description: >
|
||||||
Can be found in: [Settings -> About](https://my.home-assistant.io/redirect/info/).
|
Can be found in the Configuration panel -> Info.
|
||||||
- type: input
|
- type: input
|
||||||
attributes:
|
attributes:
|
||||||
label: What was the last working version of Home Assistant Core?
|
label: What was the last working version of Home Assistant Core?
|
||||||
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,17 +1,17 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Request a feature for the UI / Dashboards
|
- name: Request a feature for the UI, Frontend or Lovelace
|
||||||
url: https://github.com/home-assistant/frontend/discussions/category_choices
|
url: https://github.com/home-assistant/frontend/discussions/category_choices
|
||||||
about: Request an new feature for the Home Assistant frontend.
|
about: Request an new feature for the Home Assistant frontend.
|
||||||
- name: Report a bug that is NOT related to the UI / Dashboards
|
- name: Report a bug that is NOT related to the UI, Frontend or Lovelace
|
||||||
url: https://github.com/home-assistant/core/issues
|
url: https://github.com/home-assistant/core/issues
|
||||||
about: This is the issue tracker for our frontend. Please report other issues in the backend ("core") repository.
|
about: This is the issue tracker for our frontend. Please report other issues with the backend repository.
|
||||||
- name: Report incorrect or missing information on our website
|
- name: Report incorrect or missing information on our website
|
||||||
url: https://github.com/home-assistant/home-assistant.io/issues
|
url: https://github.com/home-assistant/home-assistant.io/issues
|
||||||
about: Our documentation has its own issue tracker. Please report issues with the website there.
|
about: Our documentation has its own issue tracker. Please report issues with the website there.
|
||||||
- name: I have a question or need support
|
- name: I have a question or need support
|
||||||
url: https://www.home-assistant.io/help
|
url: https://www.home-assistant.io/help
|
||||||
about: We use GitHub for tracking bugs. Check our website for resources on getting help.
|
about: We use GitHub for tracking bugs, check our website for resources on getting help.
|
||||||
- name: I'm unsure where to go
|
- name: I'm unsure where to go
|
||||||
url: https://www.home-assistant.io/join-chat
|
url: https://www.home-assistant.io/join-chat
|
||||||
about: If you are unsure where to go, then joining our chat is recommended; Just ask!
|
about: If you are unsure where to go, then joining our chat is recommended; Just ask!
|
||||||
|
32
.github/workflows/release.yaml
vendored
@@ -74,11 +74,33 @@ jobs:
|
|||||||
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
|
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
|
||||||
echo "home-assistant-frontend==$version" > ./requirements.txt
|
echo "home-assistant-frontend==$version" > ./requirements.txt
|
||||||
|
|
||||||
- name: Build wheels
|
- name: Upload requirements.txt
|
||||||
uses: home-assistant/wheels@2022.06.7
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
abi: cp310
|
name: requirements
|
||||||
tag: musllinux_1_2
|
path: ./requirements.txt
|
||||||
arch: amd64
|
|
||||||
|
build-wheels:
|
||||||
|
name: Build wheels for ${{ matrix.arch }}
|
||||||
|
needs: wheels-init
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
|
||||||
|
tag:
|
||||||
|
- "3.9-alpine3.14"
|
||||||
|
steps:
|
||||||
|
- name: Download requirements.txt
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: requirements
|
||||||
|
|
||||||
|
- name: Build wheels
|
||||||
|
uses: home-assistant/wheels@master
|
||||||
|
with:
|
||||||
|
tag: ${{ matrix.tag }}
|
||||||
|
arch: ${{ matrix.arch }}
|
||||||
|
wheels-host: ${{ secrets.WHEELS_HOST }}
|
||||||
wheels-key: ${{ secrets.WHEELS_KEY }}
|
wheels-key: ${{ secrets.WHEELS_KEY }}
|
||||||
|
wheels-user: wheels
|
||||||
requirements: "requirements.txt"
|
requirements: "requirements.txt"
|
||||||
|
2
.vscode/tasks.json
vendored
@@ -181,7 +181,7 @@
|
|||||||
{
|
{
|
||||||
"label": "Run HA Core for Supervisor in devcontainer",
|
"label": "Run HA Core for Supervisor in devcontainer",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "SUPERVISOR=${input:supervisorHost} SUPERVISOR_TOKEN=${input:supervisorToken} script/core",
|
"command": "HASSIO=${input:supervisorHost} HASSIO_TOKEN=${input:supervisorToken} script/core",
|
||||||
"isBackground": true,
|
"isBackground": true,
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
|
@@ -26,8 +26,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
version() {
|
version() {
|
||||||
const version = fs
|
const version = fs
|
||||||
.readFileSync(path.resolve(paths.polymer_dir, "pyproject.toml"), "utf8")
|
.readFileSync(path.resolve(paths.polymer_dir, "setup.cfg"), "utf8")
|
||||||
.match(/version\W+=\W"(\d{8}\.\d)"/);
|
.match(/version\W+=\W(\d{8}\.\d)/);
|
||||||
if (!version) {
|
if (!version) {
|
||||||
throw Error("Version not found");
|
throw Error("Version not found");
|
||||||
}
|
}
|
||||||
|
@@ -3,10 +3,10 @@ const webpack = require("webpack");
|
|||||||
const path = require("path");
|
const path = require("path");
|
||||||
const TerserPlugin = require("terser-webpack-plugin");
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
const { WebpackManifestPlugin } = require("webpack-manifest-plugin");
|
||||||
const log = require("fancy-log");
|
|
||||||
const WebpackBar = require("webpackbar");
|
|
||||||
const paths = require("./paths.js");
|
const paths = require("./paths.js");
|
||||||
const bundle = require("./bundle.js");
|
const bundle = require("./bundle.js");
|
||||||
|
const log = require("fancy-log");
|
||||||
|
const WebpackBar = require("webpackbar");
|
||||||
|
|
||||||
class LogStartCompilePlugin {
|
class LogStartCompilePlugin {
|
||||||
ignoredFirst = false;
|
ignoredFirst = false;
|
||||||
@@ -138,8 +138,6 @@ const createWebpackConfig = ({
|
|||||||
"lit/directives/cache$": "lit/directives/cache.js",
|
"lit/directives/cache$": "lit/directives/cache.js",
|
||||||
"lit/directives/repeat$": "lit/directives/repeat.js",
|
"lit/directives/repeat$": "lit/directives/repeat.js",
|
||||||
"lit/polyfill-support$": "lit/polyfill-support.js",
|
"lit/polyfill-support$": "lit/polyfill-support.js",
|
||||||
"@lit-labs/virtualizer/layouts/grid":
|
|
||||||
"@lit-labs/virtualizer/layouts/grid.js",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
|
@@ -1,9 +0,0 @@
|
|||||||
# These redirects are handled by Netlify
|
|
||||||
#
|
|
||||||
|
|
||||||
# Some custom cards are not prefixing the instance URL when fetching data
|
|
||||||
# and can end up fetching the data from the Cast domain instead of HA.
|
|
||||||
# This will make sure that some common ones are replaced with a placeholder.
|
|
||||||
/api/camera_proxy/* /images/google-nest-hub.png
|
|
||||||
/api/camera_proxy_stream/* /images/google-nest-hub.png
|
|
||||||
/api/media_player_proxy/* /images/google-nest-hub.png
|
|
@@ -194,7 +194,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
|
|||||||
type: "state-icon",
|
type: "state-icon",
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: "call-service",
|
action: "call-service",
|
||||||
data: {
|
service_data: {
|
||||||
entity_id: "group.downstairs_lights",
|
entity_id: "group.downstairs_lights",
|
||||||
},
|
},
|
||||||
service: "homeassistant.toggle",
|
service: "homeassistant.toggle",
|
||||||
|
@@ -137,7 +137,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
state: "73",
|
state: "73",
|
||||||
attributes: {
|
attributes: {
|
||||||
unit_of_measurement: "%",
|
unit_of_measurement: "%",
|
||||||
friendly_name: "Oskar battery",
|
friendly_name: "oskar batteri",
|
||||||
device_class: "battery",
|
device_class: "battery",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -146,7 +146,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
state: "88",
|
state: "88",
|
||||||
attributes: {
|
attributes: {
|
||||||
unit_of_measurement: "%",
|
unit_of_measurement: "%",
|
||||||
friendly_name: "Bella battery",
|
friendly_name: "bella batteri",
|
||||||
device_class: "battery",
|
device_class: "battery",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -154,7 +154,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
entity_id: "binary_sensor.unifi_camera",
|
entity_id: "binary_sensor.unifi_camera",
|
||||||
state: "off",
|
state: "off",
|
||||||
attributes: {
|
attributes: {
|
||||||
friendly_name: "Motion sensor camera",
|
friendly_name: "R\u00f6relsesensor kamera",
|
||||||
icon: "mdi:walk",
|
icon: "mdi:walk",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -707,7 +707,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
cloudiness: 25,
|
cloudiness: 25,
|
||||||
friendly_name: "Weather",
|
friendly_name: "V\u00e4der",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"binary_sensor.ubiquiti_switch": {
|
"binary_sensor.ubiquiti_switch": {
|
||||||
@@ -731,7 +731,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
round_trip_time_max: "0.626",
|
round_trip_time_max: "0.626",
|
||||||
round_trip_time_mdev: "",
|
round_trip_time_mdev: "",
|
||||||
round_trip_time_min: "0.358",
|
round_trip_time_min: "0.358",
|
||||||
friendly_name: "Entrance camera",
|
friendly_name: "Entr\u00e9 kamera",
|
||||||
device_class: "connectivity",
|
device_class: "connectivity",
|
||||||
icon: "mdi:cctv",
|
icon: "mdi:cctv",
|
||||||
},
|
},
|
||||||
@@ -807,7 +807,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
|
|||||||
attributes: {
|
attributes: {
|
||||||
battery_level: 88,
|
battery_level: 88,
|
||||||
on: true,
|
on: true,
|
||||||
friendly_name: "Back door sensor",
|
friendly_name: "Altand\u00f6rren sensor",
|
||||||
device_class: "opening",
|
device_class: "opening",
|
||||||
icon: "mdi:door",
|
icon: "mdi:door",
|
||||||
},
|
},
|
||||||
|
@@ -377,7 +377,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
name: "AC bed",
|
name: "AC bed",
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: "call-service",
|
action: "call-service",
|
||||||
data: {
|
service_data: {
|
||||||
entity_id: "script.air_cleaner_quiet",
|
entity_id: "script.air_cleaner_quiet",
|
||||||
},
|
},
|
||||||
service: "script.turn_on",
|
service: "script.turn_on",
|
||||||
@@ -390,7 +390,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
name: "AC bed",
|
name: "AC bed",
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: "call-service",
|
action: "call-service",
|
||||||
data: {
|
service_data: {
|
||||||
entity_id: "script.air_cleaner_auto",
|
entity_id: "script.air_cleaner_auto",
|
||||||
},
|
},
|
||||||
service: "script.turn_on",
|
service: "script.turn_on",
|
||||||
@@ -403,7 +403,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
name: "AC bed",
|
name: "AC bed",
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: "call-service",
|
action: "call-service",
|
||||||
data: {
|
service_data: {
|
||||||
entity_id: "script.air_cleaner_turbo",
|
entity_id: "script.air_cleaner_turbo",
|
||||||
},
|
},
|
||||||
service: "script.turn_on",
|
service: "script.turn_on",
|
||||||
@@ -416,7 +416,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
name: "AC",
|
name: "AC",
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: "call-service",
|
action: "call-service",
|
||||||
data: {
|
service_data: {
|
||||||
entity_id: "script.ac_off",
|
entity_id: "script.ac_off",
|
||||||
},
|
},
|
||||||
service: "script.turn_on",
|
service: "script.turn_on",
|
||||||
@@ -429,7 +429,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
name: "AC",
|
name: "AC",
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: "call-service",
|
action: "call-service",
|
||||||
data: {
|
service_data: {
|
||||||
entity_id: "script.ac_on",
|
entity_id: "script.ac_on",
|
||||||
},
|
},
|
||||||
service: "script.turn_on",
|
service: "script.turn_on",
|
||||||
@@ -629,7 +629,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
entity: "scene.morning_lights",
|
entity: "scene.morning_lights",
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: "call-service",
|
action: "call-service",
|
||||||
data: {
|
service_data: {
|
||||||
entity_id: "scene.morning_lights",
|
entity_id: "scene.morning_lights",
|
||||||
},
|
},
|
||||||
service: "scene.turn_on",
|
service: "scene.turn_on",
|
||||||
@@ -641,7 +641,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
entity: "scene.movie_time",
|
entity: "scene.movie_time",
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: "call-service",
|
action: "call-service",
|
||||||
data: {
|
service_data: {
|
||||||
entity_id: "scene.movie_time",
|
entity_id: "scene.movie_time",
|
||||||
},
|
},
|
||||||
service: "scene.turn_on",
|
service: "scene.turn_on",
|
||||||
@@ -702,7 +702,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
entity: "light.downstairs_lights",
|
entity: "light.downstairs_lights",
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: "call-service",
|
action: "call-service",
|
||||||
data: {
|
service_data: {
|
||||||
entity_id: "light.downstairs_lights",
|
entity_id: "light.downstairs_lights",
|
||||||
},
|
},
|
||||||
service: "light.toggle",
|
service: "light.toggle",
|
||||||
@@ -714,7 +714,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
|
|||||||
entity: "light.upstairs_lights",
|
entity: "light.upstairs_lights",
|
||||||
tap_action: {
|
tap_action: {
|
||||||
action: "call-service",
|
action: "call-service",
|
||||||
data: {
|
service_data: {
|
||||||
entity_id: "light.upstairs_lights",
|
entity_id: "light.upstairs_lights",
|
||||||
},
|
},
|
||||||
service: "light.toggle",
|
service: "light.toggle",
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
export const mockConfig = (hass: MockHomeAssistant) => {
|
export const mockConfig = (hass: MockHomeAssistant) => {
|
||||||
hass.mockAPI("config/config_entries/entry?domain=co2signal", () => [
|
hass.mockAPI("config/config_entries/entry", () => [
|
||||||
{
|
{
|
||||||
entry_id: "co2signal",
|
entry_id: "co2signal",
|
||||||
domain: "co2signal",
|
domain: "co2signal",
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { format, startOfToday, startOfTomorrow } from "date-fns/esm";
|
import { format, startOfToday, startOfTomorrow } from "date-fns";
|
||||||
import { EnergySolarForecasts } from "../../../src/data/energy";
|
import { EnergySolarForecasts } from "../../../src/data/energy";
|
||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ export const mockHassioSupervisor = (hass: MockHomeAssistant) => {
|
|||||||
version_latest: "3.6.2",
|
version_latest: "3.6.2",
|
||||||
update_available: false,
|
update_available: false,
|
||||||
repository: "a0d7b954",
|
repository: "a0d7b954",
|
||||||
icon: false,
|
icon: true,
|
||||||
logo: true,
|
logo: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -4,7 +4,7 @@ import {
|
|||||||
addMonths,
|
addMonths,
|
||||||
differenceInHours,
|
differenceInHours,
|
||||||
endOfDay,
|
endOfDay,
|
||||||
} from "date-fns/esm";
|
} from "date-fns";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { StatisticValue } from "../../../src/data/history";
|
import { StatisticValue } from "../../../src/data/history";
|
||||||
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
import { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
|
||||||
@@ -466,7 +466,6 @@ export const mockHistory = (mockHass: MockHomeAssistant) => {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
mockHass.mockWS("recorder/get_statistics_metadata", () => []);
|
|
||||||
mockHass.mockWS("history/list_statistic_ids", () => []);
|
mockHass.mockWS("history/list_statistic_ids", () => []);
|
||||||
mockHass.mockWS(
|
mockHass.mockWS(
|
||||||
"history/statistics_during_period",
|
"history/statistics_during_period",
|
||||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 32 KiB |
@@ -53,19 +53,13 @@ class DemoBlackWhiteRow extends LitElement {
|
|||||||
|
|
||||||
firstUpdated(changedProps) {
|
firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
applyThemesOnElement(
|
applyThemesOnElement(this.shadowRoot!.querySelector(".dark"), {
|
||||||
this.shadowRoot!.querySelector(".dark"),
|
default_theme: "default",
|
||||||
{
|
default_dark_theme: "default",
|
||||||
default_theme: "default",
|
themes: {},
|
||||||
default_dark_theme: "default",
|
darkMode: true,
|
||||||
themes: {},
|
theme: "default",
|
||||||
darkMode: true,
|
});
|
||||||
theme: "default",
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(ev) {
|
handleSubmit(ev) {
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
export const LONG_TEXT = `
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc laoreet velit ut elit volutpat, eget ultrices odio lacinia. In imperdiet malesuada est, nec sagittis metus ultricies quis. Sed nisl ex, convallis porttitor ante quis, hendrerit tristique justo. Mauris pharetra venenatis augue, eu maximus sem cursus in. Quisque sed consequat risus. Suspendisse facilisis ligula a odio consectetur condimentum. Curabitur vehicula elit nec augue mollis, et volutpat massa dictum.
|
|
||||||
|
|
||||||
Nam pellentesque auctor rutrum. Suspendisse elit est, sodales vel diam nec, porttitor faucibus massa. Ut pretium ac orci eu pharetra. Praesent in nibh at magna viverra rutrum eu vitae tortor. Etiam eget sem ex. Fusce tristique odio nec lacus mattis, vitae tempor nunc malesuada. Maecenas faucibus magna vel libero maximus egestas. Vestibulum luctus semper velit, in lobortis risus tempus non. Curabitur bibendum ornare commodo. Quisque commodo neque sit amet tincidunt lacinia. Proin elementum ante velit, eu congue nulla semper quis. Pellentesque consequat vel nunc at scelerisque. Mauris sit amet venenatis diam, blandit viverra leo. Integer commodo laoreet orci.
|
|
||||||
|
|
||||||
Curabitur ipsum tortor, sodales ut augue sed, commodo porttitor libero. Pellentesque molestie vitae mi consectetur tempor. In sed lectus consequat, lobortis neque non, semper ipsum. Etiam eget ex et nibh sagittis pulvinar lacinia ac mauris. Aenean ligula eros, viverra ac nibh at, venenatis semper quam. Sed interdum ligula sit amet massa tincidunt tincidunt. Suspendisse potenti. Aliquam egestas facilisis est, sed faucibus erat scelerisque id. Duis dolor quam, viverra vitae orci euismod, laoreet pellentesque justo. Nunc malesuada non erat at ullamcorper. Mauris eget posuere odio. Vestibulum turpis nunc, pharetra eget ante in, feugiat mollis justo. Proin porttitor, diam nec vulputate pretium, tellus arcu rhoncus turpis, a blandit nisi nulla quis arcu. Nunc ac ullamcorper ligula, nec facilisis leo.
|
|
||||||
|
|
||||||
In vitae eros sollicitudin, iaculis ex eget, egestas orci. Etiam sed pretium lorem. Nam nisi enim, consectetur sit amet semper ac, semper pharetra diam. In pulvinar neque sapien, ac ullamcorper est lacinia a. Etiam tincidunt velit sed diam malesuada, eu ornare ex consectetur. Phasellus in imperdiet tellus. Sed bibendum, dui sit amet fringilla aliquet, enim odio sollicitudin lorem, vel semper turpis mauris vel mauris. Aenean congue magna ac massa cursus, in dictum orci commodo. Pellentesque mollis velit in sollicitudin tincidunt. Vestibulum et efficitur nulla.
|
|
||||||
|
|
||||||
Quisque posuere, velit sed porttitor dapibus, neque augue fringilla felis, eu luctus nisi nisl nec ipsum. Curabitur pellentesque ac lectus eget ultricies. Vestibulum est dolor, lacinia pharetra vulputate a, facilisis a magna. Nam vitae arcu nibh. Praesent finibus blandit ante, ac gravida ex mollis eget. Donec quam est, pulvinar vitae neque ut, bibendum aliquam erat. Nullam mollis arcu at sem tincidunt, in tristique lectus facilisis. Aenean ut lacus vel nisl finibus iaculis non a turpis. Integer eget ipsum ante. Donec nunc neque, vestibulum ac magna ac, posuere scelerisque dui. Pellentesque massa nibh, rhoncus id dolor quis, placerat posuere turpis. Donec aliquet augue nisi, eu finibus dui auctor et. Vestibulum eu varius lorem. Quisque lectus ante, malesuada pretium risus eget, interdum mattis enim.
|
|
||||||
`;
|
|
@@ -119,7 +119,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
params: {
|
params: {
|
||||||
domain: "input_boolean",
|
domain: "input_boolean",
|
||||||
service: "toggle",
|
service: "toggle",
|
||||||
data: {},
|
service_data: {},
|
||||||
target: {
|
target: {
|
||||||
entity_id: ["input_boolean.toggle_4"],
|
entity_id: ["input_boolean.toggle_4"],
|
||||||
},
|
},
|
||||||
@@ -164,7 +164,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
params: {
|
params: {
|
||||||
domain: "input_boolean",
|
domain: "input_boolean",
|
||||||
service: "toggle",
|
service: "toggle",
|
||||||
data: {},
|
service_data: {},
|
||||||
target: {
|
target: {
|
||||||
entity_id: ["input_boolean.toggle_2"],
|
entity_id: ["input_boolean.toggle_2"],
|
||||||
},
|
},
|
||||||
@@ -182,7 +182,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
params: {
|
params: {
|
||||||
domain: "input_boolean",
|
domain: "input_boolean",
|
||||||
service: "toggle",
|
service: "toggle",
|
||||||
data: {},
|
service_data: {},
|
||||||
target: {
|
target: {
|
||||||
entity_id: ["input_boolean.toggle_3"],
|
entity_id: ["input_boolean.toggle_3"],
|
||||||
},
|
},
|
||||||
@@ -200,7 +200,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
params: {
|
params: {
|
||||||
domain: "input_boolean",
|
domain: "input_boolean",
|
||||||
service: "toggle",
|
service: "toggle",
|
||||||
data: {},
|
service_data: {},
|
||||||
target: {
|
target: {
|
||||||
entity_id: ["input_boolean.toggle_4"],
|
entity_id: ["input_boolean.toggle_4"],
|
||||||
},
|
},
|
||||||
@@ -298,11 +298,11 @@ export const basicTrace: DemoTrace = {
|
|||||||
source: "state of input_boolean.toggle_1",
|
source: "state of input_boolean.toggle_1",
|
||||||
entity_id: "automation.toggle_toggles",
|
entity_id: "automation.toggle_toggles",
|
||||||
context_id: "6cfcae368e7b3686fad6c59e83ae76c9",
|
context_id: "6cfcae368e7b3686fad6c59e83ae76c9",
|
||||||
when: 1616647011.240832,
|
when: "2021-03-25T04:36:51.240832+00:00",
|
||||||
domain: "automation",
|
domain: "automation",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
when: 1616647011.249828,
|
when: "2021-03-25T04:36:51.249828+00:00",
|
||||||
name: "Toggle 4",
|
name: "Toggle 4",
|
||||||
state: "on",
|
state: "on",
|
||||||
entity_id: "input_boolean.toggle_4",
|
entity_id: "input_boolean.toggle_4",
|
||||||
@@ -313,7 +313,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
context_name: "Ensure Party mode",
|
context_name: "Ensure Party mode",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
when: 1616647011.258947,
|
when: "2021-03-25T04:36:51.258947+00:00",
|
||||||
name: "Toggle 2",
|
name: "Toggle 2",
|
||||||
state: "on",
|
state: "on",
|
||||||
entity_id: "input_boolean.toggle_2",
|
entity_id: "input_boolean.toggle_2",
|
||||||
@@ -324,7 +324,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
context_name: "Ensure Party mode",
|
context_name: "Ensure Party mode",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
when: 1616647011.261806,
|
when: "2021-03-25T04:36:51.261806+00:00",
|
||||||
name: "Toggle 3",
|
name: "Toggle 3",
|
||||||
state: "off",
|
state: "off",
|
||||||
entity_id: "input_boolean.toggle_3",
|
entity_id: "input_boolean.toggle_3",
|
||||||
@@ -335,7 +335,7 @@ export const basicTrace: DemoTrace = {
|
|||||||
context_name: "Ensure Party mode",
|
context_name: "Ensure Party mode",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
when: 1616647011.265246,
|
when: "2021-03-25T04:36:51.265246+00:00",
|
||||||
name: "Toggle 4",
|
name: "Toggle 4",
|
||||||
state: "off",
|
state: "off",
|
||||||
entity_id: "input_boolean.toggle_4",
|
entity_id: "input_boolean.toggle_4",
|
||||||
|
@@ -185,11 +185,11 @@ export const motionLightTrace: DemoTrace = {
|
|||||||
"has been triggered by state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
"has been triggered by state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
source: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
source: "state of binary_sensor.pauluss_macbook_pro_camera_in_use",
|
||||||
entity_id: "automation.auto_elgato",
|
entity_id: "automation.auto_elgato",
|
||||||
when: 1615702021.768492,
|
when: "2021-03-14T06:07:01.768492+00:00",
|
||||||
domain: "automation",
|
domain: "automation",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
when: 1615702021.872187,
|
when: "2021-03-14T06:07:01.872187+00:00",
|
||||||
name: "Elgato Key Light Air",
|
name: "Elgato Key Light Air",
|
||||||
state: "on",
|
state: "on",
|
||||||
entity_id: "light.elgato_key_light_air",
|
entity_id: "light.elgato_key_light_air",
|
||||||
@@ -200,7 +200,7 @@ export const motionLightTrace: DemoTrace = {
|
|||||||
context_name: "Auto Elgato",
|
context_name: "Auto Elgato",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
when: 1615702073.284505,
|
when: "2021-03-14T06:07:53.284505+00:00",
|
||||||
name: "Elgato Key Light Air",
|
name: "Elgato Key Light Air",
|
||||||
state: "off",
|
state: "off",
|
||||||
entity_id: "light.elgato_key_light_air",
|
entity_id: "light.elgato_key_light_air",
|
||||||
|
@@ -62,45 +62,6 @@ const ACTIONS = [
|
|||||||
entity_id: "input_boolean.toggle_4",
|
entity_id: "input_boolean.toggle_4",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
parallel: [
|
|
||||||
{ scene: "scene.kitchen_morning" },
|
|
||||||
{
|
|
||||||
service: "media_player.play_media",
|
|
||||||
target: { entity_id: "media_player.living_room" },
|
|
||||||
data: { media_content_id: "", media_content_type: "" },
|
|
||||||
metadata: { title: "Happy Song" },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
stop: "No one is home!",
|
|
||||||
},
|
|
||||||
{ repeat: { count: 3, sequence: [{ delay: "00:00:01" }] } },
|
|
||||||
{
|
|
||||||
repeat: {
|
|
||||||
for_each: ["bread", "butter", "cheese"],
|
|
||||||
sequence: [{ delay: "00:00:01" }],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
if: [{ condition: "state" }],
|
|
||||||
then: [{ delay: "00:00:01" }],
|
|
||||||
else: [{ delay: "00:00:05" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
choose: [
|
|
||||||
{
|
|
||||||
conditions: [{ condition: "state" }],
|
|
||||||
sequence: [{ delay: "00:00:01" }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
conditions: [{ condition: "sun" }],
|
|
||||||
sequence: [{ delay: "00:00:05" }],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
default: [{ delay: "00:00:03" }],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("demo-automation-describe-action")
|
@customElement("demo-automation-describe-action")
|
||||||
|
@@ -20,10 +20,6 @@ import { HaWaitForTriggerAction } from "../../../../src/panels/config/automation
|
|||||||
import { HaWaitAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-wait_template";
|
import { HaWaitAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-wait_template";
|
||||||
import { Action } from "../../../../src/data/script";
|
import { Action } from "../../../../src/data/script";
|
||||||
import { HaConditionAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-condition";
|
import { HaConditionAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-condition";
|
||||||
import { HaParallelAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-parallel";
|
|
||||||
import { HaIfAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-if";
|
|
||||||
import { HaStopAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-stop";
|
|
||||||
import { HaPlayMediaAction } from "../../../../src/panels/config/automation/action/types/ha-automation-action-play_media";
|
|
||||||
|
|
||||||
const SCHEMAS: { name: string; actions: Action[] }[] = [
|
const SCHEMAS: { name: string; actions: Action[] }[] = [
|
||||||
{ name: "Event", actions: [HaEventAction.defaultConfig] },
|
{ name: "Event", actions: [HaEventAction.defaultConfig] },
|
||||||
@@ -32,15 +28,11 @@ const SCHEMAS: { name: string; actions: Action[] }[] = [
|
|||||||
{ name: "Condition", actions: [HaConditionAction.defaultConfig] },
|
{ name: "Condition", actions: [HaConditionAction.defaultConfig] },
|
||||||
{ name: "Delay", actions: [HaDelayAction.defaultConfig] },
|
{ name: "Delay", actions: [HaDelayAction.defaultConfig] },
|
||||||
{ name: "Scene", actions: [HaSceneAction.defaultConfig] },
|
{ name: "Scene", actions: [HaSceneAction.defaultConfig] },
|
||||||
{ name: "Play media", actions: [HaPlayMediaAction.defaultConfig] },
|
|
||||||
{ name: "Wait", actions: [HaWaitAction.defaultConfig] },
|
{ name: "Wait", actions: [HaWaitAction.defaultConfig] },
|
||||||
{ name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] },
|
{ name: "WaitForTrigger", actions: [HaWaitForTriggerAction.defaultConfig] },
|
||||||
{ name: "Repeat", actions: [HaRepeatAction.defaultConfig] },
|
{ name: "Repeat", actions: [HaRepeatAction.defaultConfig] },
|
||||||
{ name: "If-Then", actions: [HaIfAction.defaultConfig] },
|
|
||||||
{ name: "Choose", actions: [HaChooseAction.defaultConfig] },
|
{ name: "Choose", actions: [HaChooseAction.defaultConfig] },
|
||||||
{ name: "Variables", actions: [{ variables: { hello: "1" } }] },
|
{ name: "Variables", actions: [{ variables: { hello: "1" } }] },
|
||||||
{ name: "Parallel", actions: [HaParallelAction.defaultConfig] },
|
|
||||||
{ name: "Stop", actions: [HaStopAction.defaultConfig] },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("demo-automation-editor-action")
|
@customElement("demo-automation-editor-action")
|
||||||
@@ -94,6 +86,6 @@ class DemoHaAutomationEditorAction extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"demo-automation-editor-action": DemoHaAutomationEditorAction;
|
"demo-ha-automation-editor-action": DemoHaAutomationEditorAction;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@ import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
|||||||
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
||||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||||
import type { ConditionWithShorthand } from "../../../../src/data/automation";
|
import type { Condition } from "../../../../src/data/automation";
|
||||||
import "../../../../src/panels/config/automation/condition/ha-automation-condition";
|
import "../../../../src/panels/config/automation/condition/ha-automation-condition";
|
||||||
import { HaDeviceCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-device";
|
import { HaDeviceCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-device";
|
||||||
import { HaLogicalCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-logical";
|
import { HaLogicalCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-logical";
|
||||||
@@ -20,7 +20,7 @@ import { HaTimeCondition } from "../../../../src/panels/config/automation/condit
|
|||||||
import { HaTriggerCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-trigger";
|
import { HaTriggerCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-trigger";
|
||||||
import { HaZoneCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-zone";
|
import { HaZoneCondition } from "../../../../src/panels/config/automation/condition/types/ha-automation-condition-zone";
|
||||||
|
|
||||||
const SCHEMAS: { name: string; conditions: ConditionWithShorthand[] }[] = [
|
const SCHEMAS: { name: string; conditions: Condition[] }[] = [
|
||||||
{
|
{
|
||||||
name: "State",
|
name: "State",
|
||||||
conditions: [{ condition: "state", ...HaStateCondition.defaultConfig }],
|
conditions: [{ condition: "state", ...HaStateCondition.defaultConfig }],
|
||||||
@@ -69,14 +69,6 @@ const SCHEMAS: { name: string; conditions: ConditionWithShorthand[] }[] = [
|
|||||||
name: "Trigger",
|
name: "Trigger",
|
||||||
conditions: [{ condition: "trigger", ...HaTriggerCondition.defaultConfig }],
|
conditions: [{ condition: "trigger", ...HaTriggerCondition.defaultConfig }],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Shorthand",
|
|
||||||
conditions: [
|
|
||||||
{ and: HaLogicalCondition.defaultConfig.conditions },
|
|
||||||
{ or: HaLogicalCondition.defaultConfig.conditions },
|
|
||||||
{ not: HaLogicalCondition.defaultConfig.conditions },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("demo-automation-editor-condition")
|
@customElement("demo-automation-editor-condition")
|
||||||
|
@@ -159,19 +159,13 @@ export class DemoHaAlert extends LitElement {
|
|||||||
|
|
||||||
firstUpdated(changedProps) {
|
firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
applyThemesOnElement(
|
applyThemesOnElement(this.shadowRoot!.querySelector(".dark"), {
|
||||||
this.shadowRoot!.querySelector(".dark"),
|
default_theme: "default",
|
||||||
{
|
default_dark_theme: "default",
|
||||||
default_theme: "default",
|
themes: {},
|
||||||
default_dark_theme: "default",
|
darkMode: true,
|
||||||
themes: {},
|
theme: "default",
|
||||||
darkMode: true,
|
});
|
||||||
theme: "default",
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
|
@@ -3,7 +3,18 @@ import { customElement } from "lit/decorators";
|
|||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-faded";
|
import "../../../../src/components/ha-faded";
|
||||||
import "../../../../src/components/ha-markdown";
|
import "../../../../src/components/ha-markdown";
|
||||||
import { LONG_TEXT } from "../../data/text";
|
|
||||||
|
const LONG_TEXT = `
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc laoreet velit ut elit volutpat, eget ultrices odio lacinia. In imperdiet malesuada est, nec sagittis metus ultricies quis. Sed nisl ex, convallis porttitor ante quis, hendrerit tristique justo. Mauris pharetra venenatis augue, eu maximus sem cursus in. Quisque sed consequat risus. Suspendisse facilisis ligula a odio consectetur condimentum. Curabitur vehicula elit nec augue mollis, et volutpat massa dictum.
|
||||||
|
|
||||||
|
Nam pellentesque auctor rutrum. Suspendisse elit est, sodales vel diam nec, porttitor faucibus massa. Ut pretium ac orci eu pharetra. Praesent in nibh at magna viverra rutrum eu vitae tortor. Etiam eget sem ex. Fusce tristique odio nec lacus mattis, vitae tempor nunc malesuada. Maecenas faucibus magna vel libero maximus egestas. Vestibulum luctus semper velit, in lobortis risus tempus non. Curabitur bibendum ornare commodo. Quisque commodo neque sit amet tincidunt lacinia. Proin elementum ante velit, eu congue nulla semper quis. Pellentesque consequat vel nunc at scelerisque. Mauris sit amet venenatis diam, blandit viverra leo. Integer commodo laoreet orci.
|
||||||
|
|
||||||
|
Curabitur ipsum tortor, sodales ut augue sed, commodo porttitor libero. Pellentesque molestie vitae mi consectetur tempor. In sed lectus consequat, lobortis neque non, semper ipsum. Etiam eget ex et nibh sagittis pulvinar lacinia ac mauris. Aenean ligula eros, viverra ac nibh at, venenatis semper quam. Sed interdum ligula sit amet massa tincidunt tincidunt. Suspendisse potenti. Aliquam egestas facilisis est, sed faucibus erat scelerisque id. Duis dolor quam, viverra vitae orci euismod, laoreet pellentesque justo. Nunc malesuada non erat at ullamcorper. Mauris eget posuere odio. Vestibulum turpis nunc, pharetra eget ante in, feugiat mollis justo. Proin porttitor, diam nec vulputate pretium, tellus arcu rhoncus turpis, a blandit nisi nulla quis arcu. Nunc ac ullamcorper ligula, nec facilisis leo.
|
||||||
|
|
||||||
|
In vitae eros sollicitudin, iaculis ex eget, egestas orci. Etiam sed pretium lorem. Nam nisi enim, consectetur sit amet semper ac, semper pharetra diam. In pulvinar neque sapien, ac ullamcorper est lacinia a. Etiam tincidunt velit sed diam malesuada, eu ornare ex consectetur. Phasellus in imperdiet tellus. Sed bibendum, dui sit amet fringilla aliquet, enim odio sollicitudin lorem, vel semper turpis mauris vel mauris. Aenean congue magna ac massa cursus, in dictum orci commodo. Pellentesque mollis velit in sollicitudin tincidunt. Vestibulum et efficitur nulla.
|
||||||
|
|
||||||
|
Quisque posuere, velit sed porttitor dapibus, neque augue fringilla felis, eu luctus nisi nisl nec ipsum. Curabitur pellentesque ac lectus eget ultricies. Vestibulum est dolor, lacinia pharetra vulputate a, facilisis a magna. Nam vitae arcu nibh. Praesent finibus blandit ante, ac gravida ex mollis eget. Donec quam est, pulvinar vitae neque ut, bibendum aliquam erat. Nullam mollis arcu at sem tincidunt, in tristique lectus facilisis. Aenean ut lacus vel nisl finibus iaculis non a turpis. Integer eget ipsum ante. Donec nunc neque, vestibulum ac magna ac, posuere scelerisque dui. Pellentesque massa nibh, rhoncus id dolor quis, placerat posuere turpis. Donec aliquet augue nisi, eu finibus dui auctor et. Vestibulum eu varius lorem. Quisque lectus ante, malesuada pretium risus eget, interdum mattis enim.
|
||||||
|
`;
|
||||||
|
|
||||||
const SMALL_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
|
const SMALL_TEXT = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
|
||||||
|
|
||||||
|
@@ -1,18 +1,18 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
/* eslint-disable lit/no-template-arrow */
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { html, LitElement, TemplateResult } from "lit";
|
import { LitElement, TemplateResult, html } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
|
import { computeInitialHaFormData } from "../../../../src/components/ha-form/compute-initial-ha-form-data";
|
||||||
|
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
||||||
|
import "../../../../src/components/ha-form/ha-form";
|
||||||
|
import "../../components/demo-black-white-row";
|
||||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||||
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
||||||
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||||
import { computeInitialHaFormData } from "../../../../src/components/ha-form/compute-initial-ha-form-data";
|
|
||||||
import "../../../../src/components/ha-form/ha-form";
|
|
||||||
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
|
||||||
import { getEntity } from "../../../../src/fake_data/entity";
|
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
import "../../components/demo-black-white-row";
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
|
|
||||||
const ENTITIES = [
|
const ENTITIES = [
|
||||||
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
||||||
@@ -147,9 +147,7 @@ const SCHEMAS: {
|
|||||||
{ name: "target", selector: { target: {} } },
|
{ name: "target", selector: { target: {} } },
|
||||||
{ name: "number", selector: { number: { min: 0, max: 10 } } },
|
{ name: "number", selector: { number: { min: 0, max: 10 } } },
|
||||||
{ name: "boolean", selector: { boolean: {} } },
|
{ name: "boolean", selector: { boolean: {} } },
|
||||||
{ name: "time", required: true, selector: { time: {} } },
|
{ name: "time", selector: { time: {} } },
|
||||||
{ name: "datetime", required: true, selector: { datetime: {} } },
|
|
||||||
{ name: "date", required: true, selector: { date: {} } },
|
|
||||||
{ name: "action", selector: { action: {} } },
|
{ name: "action", selector: { action: {} } },
|
||||||
{ name: "text", selector: { text: { multiline: false } } },
|
{ name: "text", selector: { text: { multiline: false } } },
|
||||||
{ name: "text_multiline", selector: { text: { multiline: true } } },
|
{ name: "text_multiline", selector: { text: { multiline: true } } },
|
||||||
|
@@ -1,20 +1,20 @@
|
|||||||
/* eslint-disable lit/no-template-arrow */
|
/* eslint-disable lit/no-template-arrow */
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { css, html, LitElement, TemplateResult } from "lit";
|
import { LitElement, TemplateResult, css, html } from "lit";
|
||||||
import { customElement, state } from "lit/decorators";
|
import { customElement, state } from "lit/decorators";
|
||||||
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
|
||||||
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
|
||||||
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
|
||||||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
|
||||||
import "../../../../src/components/ha-selector/ha-selector";
|
import "../../../../src/components/ha-selector/ha-selector";
|
||||||
import "../../../../src/components/ha-settings-row";
|
import "../../../../src/components/ha-settings-row";
|
||||||
import { BlueprintInput } from "../../../../src/data/blueprint";
|
|
||||||
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
|
|
||||||
import { getEntity } from "../../../../src/fake_data/entity";
|
|
||||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||||
import { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin";
|
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import "../../components/demo-black-white-row";
|
import "../../components/demo-black-white-row";
|
||||||
|
import { BlueprintInput } from "../../../../src/data/blueprint";
|
||||||
|
import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
||||||
|
import { mockDeviceRegistry } from "../../../../demo/src/stubs/device_registry";
|
||||||
|
import { mockAreaRegistry } from "../../../../demo/src/stubs/area_registry";
|
||||||
|
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||||
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
|
import { ProvideHassElement } from "../../../../src/mixins/provide-hass-lit-mixin";
|
||||||
|
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||||
|
|
||||||
const ENTITIES = [
|
const ENTITIES = [
|
||||||
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
getEntity("alarm_control_panel", "alarm", "disarmed", {
|
||||||
@@ -109,7 +109,7 @@ const AREAS = [
|
|||||||
|
|
||||||
const SCHEMAS: {
|
const SCHEMAS: {
|
||||||
name: string;
|
name: string;
|
||||||
input: Record<string, (BlueprintInput & { required?: boolean }) | null>;
|
input: Record<string, BlueprintInput | null>;
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
{
|
||||||
name: "One of each",
|
name: "One of each",
|
||||||
@@ -166,11 +166,8 @@ const SCHEMAS: {
|
|||||||
object: { name: "Object", selector: { object: {} } },
|
object: { name: "Object", selector: { object: {} } },
|
||||||
select_radio: {
|
select_radio: {
|
||||||
name: "Select (Radio)",
|
name: "Select (Radio)",
|
||||||
selector: {
|
selector: { select: { options: ["Option 1", "Option 2"] } },
|
||||||
select: { options: ["Option 1", "Option 2"], mode: "list" },
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
template: { name: "Template", selector: { template: {} } },
|
|
||||||
select: {
|
select: {
|
||||||
name: "Select",
|
name: "Select",
|
||||||
selector: {
|
selector: {
|
||||||
@@ -186,22 +183,6 @@ const SCHEMAS: {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
select_custom: {
|
|
||||||
name: "Select (Custom)",
|
|
||||||
selector: {
|
|
||||||
select: {
|
|
||||||
custom_value: true,
|
|
||||||
options: [
|
|
||||||
"Option 1",
|
|
||||||
"Option 2",
|
|
||||||
"Option 3",
|
|
||||||
"Option 4",
|
|
||||||
"Option 5",
|
|
||||||
"Option 6",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
icon: { name: "Icon", selector: { icon: {} } },
|
icon: { name: "Icon", selector: { icon: {} } },
|
||||||
media: { name: "Media", selector: { media: {} } },
|
media: { name: "Media", selector: { media: {} } },
|
||||||
location: { name: "Location", selector: { location: {} } },
|
location: { name: "Location", selector: { location: {} } },
|
||||||
@@ -221,35 +202,6 @@ const SCHEMAS: {
|
|||||||
input: {
|
input: {
|
||||||
entity: { name: "Entity", selector: { entity: { multiple: true } } },
|
entity: { name: "Entity", selector: { entity: { multiple: true } } },
|
||||||
device: { name: "Device", selector: { device: { multiple: true } } },
|
device: { name: "Device", selector: { device: { multiple: true } } },
|
||||||
area: { name: "Area", selector: { area: { multiple: true } } },
|
|
||||||
select: {
|
|
||||||
name: "Select Multiple",
|
|
||||||
selector: {
|
|
||||||
select: {
|
|
||||||
multiple: true,
|
|
||||||
custom_value: true,
|
|
||||||
options: [
|
|
||||||
"Option 1",
|
|
||||||
"Option 2",
|
|
||||||
"Option 3",
|
|
||||||
"Option 4",
|
|
||||||
"Option 5",
|
|
||||||
"Option 6",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
select_checkbox: {
|
|
||||||
name: "Select Multiple (Checkbox)",
|
|
||||||
required: false,
|
|
||||||
selector: {
|
|
||||||
select: {
|
|
||||||
mode: "list",
|
|
||||||
multiple: true,
|
|
||||||
options: ["Option 1", "Option 2", "Option 3", "Option 4"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -258,14 +210,6 @@ const SCHEMAS: {
|
|||||||
class DemoHaSelector extends LitElement implements ProvideHassElement {
|
class DemoHaSelector extends LitElement implements ProvideHassElement {
|
||||||
@state() public hass!: HomeAssistant;
|
@state() public hass!: HomeAssistant;
|
||||||
|
|
||||||
@state() private _disabled = false;
|
|
||||||
|
|
||||||
@state() private _required = false;
|
|
||||||
|
|
||||||
@state() private _helper = false;
|
|
||||||
|
|
||||||
@state() private _label = true;
|
|
||||||
|
|
||||||
private data = SCHEMAS.map(() => ({}));
|
private data = SCHEMAS.map(() => ({}));
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -399,36 +343,6 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="options">
|
|
||||||
<ha-formfield label="Labels">
|
|
||||||
<ha-switch
|
|
||||||
.name=${"label"}
|
|
||||||
.checked=${this._label}
|
|
||||||
@change=${this._handleOptionChange}
|
|
||||||
></ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
<ha-formfield label="Required">
|
|
||||||
<ha-switch
|
|
||||||
.name=${"required"}
|
|
||||||
.checked=${this._required}
|
|
||||||
@change=${this._handleOptionChange}
|
|
||||||
></ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
<ha-formfield label="Disabled">
|
|
||||||
<ha-switch
|
|
||||||
.name=${"disabled"}
|
|
||||||
.checked=${this._disabled}
|
|
||||||
@change=${this._handleOptionChange}
|
|
||||||
></ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
<ha-formfield label="Helper text">
|
|
||||||
<ha-switch
|
|
||||||
.name=${"helper"}
|
|
||||||
.checked=${this._helper}
|
|
||||||
@change=${this._handleOptionChange}
|
|
||||||
></ha-switch>
|
|
||||||
</ha-formfield>
|
|
||||||
</div>
|
|
||||||
${SCHEMAS.map((info, idx) => {
|
${SCHEMAS.map((info, idx) => {
|
||||||
const data = this.data[idx];
|
const data = this.data[idx];
|
||||||
const valueChanged = (ev) => {
|
const valueChanged = (ev) => {
|
||||||
@@ -451,12 +365,8 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
|||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.selector=${value!.selector}
|
.selector=${value!.selector}
|
||||||
.key=${key}
|
.key=${key}
|
||||||
.label=${this._label ? value!.name : undefined}
|
|
||||||
.value=${data[key] ?? value!.default}
|
.value=${data[key] ?? value!.default}
|
||||||
.disabled=${this._disabled}
|
|
||||||
.required=${this._required}
|
|
||||||
@value-changed=${valueChanged}
|
@value-changed=${valueChanged}
|
||||||
.helper=${this._helper ? "Helper text" : undefined}
|
|
||||||
></ha-selector>
|
></ha-selector>
|
||||||
</ha-settings-row>
|
</ha-settings-row>
|
||||||
`
|
`
|
||||||
@@ -468,21 +378,10 @@ class DemoHaSelector extends LitElement implements ProvideHassElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleOptionChange(ev) {
|
|
||||||
this[`_${ev.target.name}`] = ev.target.checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
static styles = css`
|
static styles = css`
|
||||||
ha-selector {
|
ha-selector {
|
||||||
width: 60;
|
width: 60;
|
||||||
}
|
}
|
||||||
.options {
|
|
||||||
max-width: 800px;
|
|
||||||
margin: 16px auto;
|
|
||||||
}
|
|
||||||
.options ha-formfield {
|
|
||||||
margin-right: 16px;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
---
|
|
||||||
title: Tips
|
|
||||||
---
|
|
@@ -1,73 +0,0 @@
|
|||||||
import { html, css, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement } from "lit/decorators";
|
|
||||||
import "../../../../src/components/ha-tip";
|
|
||||||
import "../../../../src/components/ha-card";
|
|
||||||
import { applyThemesOnElement } from "../../../../src/common/dom/apply_themes_on_element";
|
|
||||||
|
|
||||||
const tips: (string | TemplateResult)[] = [
|
|
||||||
"Test tip",
|
|
||||||
"Bigger test tip, with some random text just to fill up as much space as possible without it looking like I'm really trying to to that",
|
|
||||||
html`<i>Tip</i> <b>with</b> <sub>HTML</sub>`,
|
|
||||||
];
|
|
||||||
|
|
||||||
@customElement("demo-components-ha-tip")
|
|
||||||
export class DemoHaTip extends LitElement {
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html` ${["light", "dark"].map(
|
|
||||||
(mode) => html`
|
|
||||||
<div class=${mode}>
|
|
||||||
<ha-card header="ha-tip ${mode} demo">
|
|
||||||
<div class="card-content">
|
|
||||||
${tips.map((tip) => html`<ha-tip>${tip}</ha-tip>`)}
|
|
||||||
</div>
|
|
||||||
</ha-card>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
firstUpdated(changedProps) {
|
|
||||||
super.firstUpdated(changedProps);
|
|
||||||
applyThemesOnElement(
|
|
||||||
this.shadowRoot!.querySelector(".dark"),
|
|
||||||
{
|
|
||||||
default_theme: "default",
|
|
||||||
default_dark_theme: "default",
|
|
||||||
themes: {},
|
|
||||||
darkMode: true,
|
|
||||||
theme: "default",
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles() {
|
|
||||||
return css`
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.dark,
|
|
||||||
.light {
|
|
||||||
display: block;
|
|
||||||
background-color: var(--primary-background-color);
|
|
||||||
padding: 0 50px;
|
|
||||||
}
|
|
||||||
ha-tip {
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
margin: 24px auto;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
"demo-components-ha-tip": DemoHaTip;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -249,7 +249,7 @@ const CONFIGS = [
|
|||||||
name: Bed light
|
name: Bed light
|
||||||
action_name: Toggle light
|
action_name: Toggle light
|
||||||
service: light.toggle
|
service: light.toggle
|
||||||
data:
|
service_data:
|
||||||
entity_id: light.bed_light
|
entity_id: light.bed_light
|
||||||
- type: section
|
- type: section
|
||||||
label: Links
|
label: Links
|
||||||
|
@@ -199,7 +199,7 @@ const CONFIGS = [
|
|||||||
tap_action:
|
tap_action:
|
||||||
action: call-service
|
action: call-service
|
||||||
service: light.turn_on
|
service: light.turn_on
|
||||||
data:
|
service_data:
|
||||||
entity_id: light.ceiling_lights
|
entity_id: light.ceiling_lights
|
||||||
- entity: sun.sun
|
- entity: sun.sun
|
||||||
name: Regular
|
name: Regular
|
||||||
|
@@ -9,7 +9,7 @@ const CONFIGS = [
|
|||||||
heading: "markdown-it demo",
|
heading: "markdown-it demo",
|
||||||
config: `
|
config: `
|
||||||
- type: markdown
|
- type: markdown
|
||||||
content: >-
|
content: >
|
||||||
# h1 Heading 8-)
|
# h1 Heading 8-)
|
||||||
|
|
||||||
## h2 Heading
|
## h2 Heading
|
||||||
@@ -249,17 +249,6 @@ const CONFIGS = [
|
|||||||
::: warning
|
::: warning
|
||||||
*here be dragons*
|
*here be dragons*
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### ha-alert
|
|
||||||
|
|
||||||
You can use our [\`ha-alert\`](https://design.home-assistant.io/#components/ha-alert) component in markdown content rendered in the Home Assistant Frontend.
|
|
||||||
|
|
||||||
<ha-alert alert-type="error">This is an error alert — check it out!</ha-alert>
|
|
||||||
<ha-alert alert-type="warning">This is a warning alert — check it out!</ha-alert>
|
|
||||||
<ha-alert alert-type="info">This is an info alert — check it out!</ha-alert>
|
|
||||||
<ha-alert alert-type="success">This is a success alert — check it out!</ha-alert>
|
|
||||||
<ha-alert title="Test alert">This is an alert with a title</ha-alert>
|
|
||||||
|
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@@ -40,7 +40,7 @@ const CONFIGS = [
|
|||||||
left: 90%
|
left: 90%
|
||||||
padding: 0px
|
padding: 0px
|
||||||
service: light.turn_off
|
service: light.turn_off
|
||||||
data:
|
service_data:
|
||||||
entity_id: group.all_lights
|
entity_id: group.all_lights
|
||||||
- type: icon
|
- type: icon
|
||||||
icon: mdi:cctv
|
icon: mdi:cctv
|
||||||
@@ -88,7 +88,7 @@ const CONFIGS = [
|
|||||||
left: 90%
|
left: 90%
|
||||||
padding: 0px
|
padding: 0px
|
||||||
service: light.turn_off
|
service: light.turn_off
|
||||||
data:
|
service_data:
|
||||||
entity_id: group.all_lights
|
entity_id: group.all_lights
|
||||||
- type: icon
|
- type: icon
|
||||||
icon: mdi:cctv
|
icon: mdi:cctv
|
||||||
|
@@ -5,7 +5,6 @@ import {
|
|||||||
UPDATE_SUPPORT_BACKUP,
|
UPDATE_SUPPORT_BACKUP,
|
||||||
UPDATE_SUPPORT_PROGRESS,
|
UPDATE_SUPPORT_PROGRESS,
|
||||||
UPDATE_SUPPORT_INSTALL,
|
UPDATE_SUPPORT_INSTALL,
|
||||||
UPDATE_SUPPORT_RELEASE_NOTES,
|
|
||||||
} from "../../../../src/data/update";
|
} from "../../../../src/data/update";
|
||||||
import "../../../../src/dialogs/more-info/more-info-content";
|
import "../../../../src/dialogs/more-info/more-info-content";
|
||||||
import { getEntity } from "../../../../src/fake_data/entity";
|
import { getEntity } from "../../../../src/fake_data/entity";
|
||||||
@@ -14,11 +13,10 @@ import {
|
|||||||
provideHass,
|
provideHass,
|
||||||
} from "../../../../src/fake_data/provide_hass";
|
} from "../../../../src/fake_data/provide_hass";
|
||||||
import "../../components/demo-more-infos";
|
import "../../components/demo-more-infos";
|
||||||
import { LONG_TEXT } from "../../data/text";
|
|
||||||
|
|
||||||
const base_attributes = {
|
const base_attributes = {
|
||||||
title: "Awesome",
|
title: "Awesome",
|
||||||
installed_version: "1.2.2",
|
current_version: "1.2.2",
|
||||||
latest_version: "1.2.3",
|
latest_version: "1.2.3",
|
||||||
release_url: "https://home-assistant.io",
|
release_url: "https://home-assistant.io",
|
||||||
supported_features: UPDATE_SUPPORT_INSTALL,
|
supported_features: UPDATE_SUPPORT_INSTALL,
|
||||||
@@ -50,7 +48,7 @@ const ENTITIES = [
|
|||||||
}),
|
}),
|
||||||
getEntity("update", "update5", "off", {
|
getEntity("update", "update5", "off", {
|
||||||
...base_attributes,
|
...base_attributes,
|
||||||
installed_version: "1.2.3",
|
current_version: "1.2.3",
|
||||||
friendly_name: "No update",
|
friendly_name: "No update",
|
||||||
}),
|
}),
|
||||||
getEntity("update", "update6", "off", {
|
getEntity("update", "update6", "off", {
|
||||||
@@ -102,43 +100,14 @@ const ENTITIES = [
|
|||||||
}),
|
}),
|
||||||
getEntity("update", "update14", "off", {
|
getEntity("update", "update14", "off", {
|
||||||
...base_attributes,
|
...base_attributes,
|
||||||
installed_version: null,
|
current_version: null,
|
||||||
friendly_name: "Update without installed_version",
|
friendly_name: "Update without current_version",
|
||||||
}),
|
}),
|
||||||
getEntity("update", "update15", "off", {
|
getEntity("update", "update15", "off", {
|
||||||
...base_attributes,
|
...base_attributes,
|
||||||
latest_version: null,
|
latest_version: null,
|
||||||
friendly_name: "Update without latest_version",
|
friendly_name: "Update without latest_version",
|
||||||
}),
|
}),
|
||||||
getEntity("update", "update16", "off", {
|
|
||||||
...base_attributes,
|
|
||||||
friendly_name: "Update with release notes",
|
|
||||||
supported_features:
|
|
||||||
base_attributes.supported_features + UPDATE_SUPPORT_RELEASE_NOTES,
|
|
||||||
}),
|
|
||||||
getEntity("update", "update17", "off", {
|
|
||||||
...base_attributes,
|
|
||||||
friendly_name: "Update with release notes error",
|
|
||||||
supported_features:
|
|
||||||
base_attributes.supported_features + UPDATE_SUPPORT_RELEASE_NOTES,
|
|
||||||
}),
|
|
||||||
getEntity("update", "update18", "off", {
|
|
||||||
...base_attributes,
|
|
||||||
friendly_name: "Update with release notes loading",
|
|
||||||
supported_features:
|
|
||||||
base_attributes.supported_features + UPDATE_SUPPORT_RELEASE_NOTES,
|
|
||||||
}),
|
|
||||||
getEntity("update", "update19", "on", {
|
|
||||||
...base_attributes,
|
|
||||||
friendly_name: "Update with auto update",
|
|
||||||
auto_update: true,
|
|
||||||
}),
|
|
||||||
getEntity("update", "update20", "on", {
|
|
||||||
...base_attributes,
|
|
||||||
in_progress: true,
|
|
||||||
title: undefined,
|
|
||||||
friendly_name: "Installing without title",
|
|
||||||
}),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
@customElement("demo-more-info-update")
|
@customElement("demo-more-info-update")
|
||||||
@@ -161,24 +130,6 @@ class DemoMoreInfoUpdate extends LitElement {
|
|||||||
const hass = provideHass(this._demoRoot);
|
const hass = provideHass(this._demoRoot);
|
||||||
hass.updateTranslations(null, "en");
|
hass.updateTranslations(null, "en");
|
||||||
hass.addEntities(ENTITIES);
|
hass.addEntities(ENTITIES);
|
||||||
hass.mockWS(
|
|
||||||
"update/release_notes",
|
|
||||||
(msg: { type: string; entity_id: string }) => {
|
|
||||||
if (msg.entity_id === "update.update16") {
|
|
||||||
return LONG_TEXT;
|
|
||||||
}
|
|
||||||
if (msg.entity_id === "update.update17") {
|
|
||||||
return Promise.reject({
|
|
||||||
code: "error",
|
|
||||||
message: "Could not fetch release notes",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (msg.entity_id === "update.update18") {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,8 +6,10 @@ import { atLeastVersion } from "../../../src/common/config/version";
|
|||||||
import { navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
import { caseInsensitiveStringCompare } from "../../../src/common/string/compare";
|
import { caseInsensitiveStringCompare } from "../../../src/common/string/compare";
|
||||||
import "../../../src/components/ha-card";
|
import "../../../src/components/ha-card";
|
||||||
import { HassioAddonRepository } from "../../../src/data/hassio/addon";
|
import {
|
||||||
import { StoreAddon } from "../../../src/data/supervisor/store";
|
HassioAddonInfo,
|
||||||
|
HassioAddonRepository,
|
||||||
|
} from "../../../src/data/hassio/addon";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import "../components/hassio-card-content";
|
import "../components/hassio-card-content";
|
||||||
@@ -21,16 +23,20 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public repo!: HassioAddonRepository;
|
@property({ attribute: false }) public repo!: HassioAddonRepository;
|
||||||
|
|
||||||
@property({ attribute: false }) public addons!: StoreAddon[];
|
@property({ attribute: false }) public addons!: HassioAddonInfo[];
|
||||||
|
|
||||||
@property() public filter!: string;
|
@property() public filter!: string;
|
||||||
|
|
||||||
private _getAddons = memoizeOne((addons: StoreAddon[], filter?: string) => {
|
private _getAddons = memoizeOne(
|
||||||
if (filter) {
|
(addons: HassioAddonInfo[], filter?: string) => {
|
||||||
return filterAndSort(addons, filter);
|
if (filter) {
|
||||||
|
return filterAndSort(addons, filter);
|
||||||
|
}
|
||||||
|
return addons.sort((a, b) =>
|
||||||
|
caseInsensitiveStringCompare(a.name, b.name)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return addons.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name));
|
);
|
||||||
});
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const repo = this.repo;
|
const repo = this.repo;
|
||||||
@@ -62,7 +68,6 @@ class HassioAddonRepositoryEl extends LitElement {
|
|||||||
${addons.map(
|
${addons.map(
|
||||||
(addon) => html`
|
(addon) => html`
|
||||||
<ha-card
|
<ha-card
|
||||||
outlined
|
|
||||||
.addon=${addon}
|
.addon=${addon}
|
||||||
class=${addon.available ? "" : "not_available"}
|
class=${addon.available ? "" : "not_available"}
|
||||||
@click=${this._addonTapped}
|
@click=${this._addonTapped}
|
||||||
|
@@ -14,15 +14,15 @@ import memoizeOne from "memoize-one";
|
|||||||
import { atLeastVersion } from "../../../src/common/config/version";
|
import { atLeastVersion } from "../../../src/common/config/version";
|
||||||
import { fireEvent } from "../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../src/common/dom/fire_event";
|
||||||
import { navigate } from "../../../src/common/navigate";
|
import { navigate } from "../../../src/common/navigate";
|
||||||
|
import "../../../src/components/search-input";
|
||||||
import { extractSearchParam } from "../../../src/common/url/search-params";
|
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||||
import "../../../src/components/ha-button-menu";
|
import "../../../src/components/ha-button-menu";
|
||||||
import "../../../src/components/ha-icon-button";
|
import "../../../src/components/ha-icon-button";
|
||||||
import "../../../src/components/search-input";
|
|
||||||
import {
|
import {
|
||||||
|
HassioAddonInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
reloadHassioAddons,
|
reloadHassioAddons,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
import { StoreAddon } from "../../../src/data/supervisor/store";
|
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import "../../../src/layouts/hass-loading-screen";
|
import "../../../src/layouts/hass-loading-screen";
|
||||||
import "../../../src/layouts/hass-subpage";
|
import "../../../src/layouts/hass-subpage";
|
||||||
@@ -66,10 +66,10 @@ class HassioAddonStore extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
let repos: TemplateResult[] = [];
|
let repos: TemplateResult[] = [];
|
||||||
|
|
||||||
if (this.supervisor.store.repositories) {
|
if (this.supervisor.addon.repositories) {
|
||||||
repos = this.addonRepositories(
|
repos = this.addonRepositories(
|
||||||
this.supervisor.store.repositories,
|
this.supervisor.addon.repositories,
|
||||||
this.supervisor.store.addons,
|
this.supervisor.addon.addons,
|
||||||
this._filter
|
this._filter
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ class HassioAddonStore extends LitElement {
|
|||||||
private addonRepositories = memoizeOne(
|
private addonRepositories = memoizeOne(
|
||||||
(
|
(
|
||||||
repositories: HassioAddonRepository[],
|
repositories: HassioAddonRepository[],
|
||||||
addons: StoreAddon[],
|
addons: HassioAddonInfo[],
|
||||||
filter?: string
|
filter?: string
|
||||||
) =>
|
) =>
|
||||||
repositories.sort(sortRepos).map((repo) => {
|
repositories.sort(sortRepos).map((repo) => {
|
||||||
|
@@ -50,7 +50,6 @@ class HassioAddonAudio extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
outlined
|
|
||||||
.header=${this.supervisor.localize("addon.configuration.audio.header")}
|
.header=${this.supervisor.localize("addon.configuration.audio.header")}
|
||||||
>
|
>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
|
@@ -39,14 +39,7 @@ import type { HomeAssistant } from "../../../../src/types";
|
|||||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
|
|
||||||
const SUPPORTED_UI_TYPES = [
|
const SUPPORTED_UI_TYPES = ["string", "select", "boolean", "integer", "float"];
|
||||||
"string",
|
|
||||||
"select",
|
|
||||||
"boolean",
|
|
||||||
"integer",
|
|
||||||
"float",
|
|
||||||
"schema",
|
|
||||||
];
|
|
||||||
|
|
||||||
const ADDON_YAML_SCHEMA = DEFAULT_SCHEMA.extend([
|
const ADDON_YAML_SCHEMA = DEFAULT_SCHEMA.extend([
|
||||||
new Type("!secret", {
|
new Type("!secret", {
|
||||||
@@ -55,8 +48,6 @@ const ADDON_YAML_SCHEMA = DEFAULT_SCHEMA.extend([
|
|||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const MASKED_FIELDS = ["password", "secret", "token"];
|
|
||||||
|
|
||||||
@customElement("hassio-addon-config")
|
@customElement("hassio-addon-config")
|
||||||
class HassioAddonConfig extends LitElement {
|
class HassioAddonConfig extends LitElement {
|
||||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
@@ -84,66 +75,19 @@ class HassioAddonConfig extends LitElement {
|
|||||||
public computeLabel = (entry: HaFormSchema): string =>
|
public computeLabel = (entry: HaFormSchema): string =>
|
||||||
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
|
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
|
||||||
?.name ||
|
?.name ||
|
||||||
this.addon.translations.en?.configuration?.[entry.name]?.name ||
|
this.addon.translations.en?.configuration?.[entry.name].name ||
|
||||||
entry.name;
|
entry.name;
|
||||||
|
|
||||||
public computeHelper = (entry: HaFormSchema): string =>
|
private _schema = memoizeOne((schema: HaFormSchema[]): HaFormSchema[] =>
|
||||||
this.addon.translations[this.hass.language]?.configuration?.[entry.name]
|
// @ts-expect-error supervisor does not implement [string, string] for select.options[]
|
||||||
?.description ||
|
schema.map((entry) =>
|
||||||
this.addon.translations.en?.configuration?.[entry.name]?.description ||
|
entry.type === "select"
|
||||||
"";
|
? {
|
||||||
|
...entry,
|
||||||
private _convertSchema = memoizeOne(
|
options: entry.options.map((option) => [option, option]),
|
||||||
// Convert supervisor schema to selectors
|
}
|
||||||
(schema: Record<string, any>): HaFormSchema[] =>
|
: entry
|
||||||
schema.map((entry) =>
|
)
|
||||||
entry.type === "select"
|
|
||||||
? {
|
|
||||||
name: entry.name,
|
|
||||||
required: entry.required,
|
|
||||||
selector: { select: { options: entry.options } },
|
|
||||||
}
|
|
||||||
: entry.type === "string"
|
|
||||||
? entry.multiple
|
|
||||||
? {
|
|
||||||
name: entry.name,
|
|
||||||
required: entry.required,
|
|
||||||
selector: {
|
|
||||||
select: { options: [], multiple: true, custom_value: true },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
name: entry.name,
|
|
||||||
required: entry.required,
|
|
||||||
selector: {
|
|
||||||
text: {
|
|
||||||
type:
|
|
||||||
entry.format || MASKED_FIELDS.includes(entry.name)
|
|
||||||
? "password"
|
|
||||||
: "text",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: entry.type === "boolean"
|
|
||||||
? {
|
|
||||||
name: entry.name,
|
|
||||||
required: entry.required,
|
|
||||||
selector: { boolean: {} },
|
|
||||||
}
|
|
||||||
: entry.type === "schema"
|
|
||||||
? {
|
|
||||||
name: entry.name,
|
|
||||||
required: entry.required,
|
|
||||||
selector: { object: {} },
|
|
||||||
}
|
|
||||||
: entry.type === "float" || entry.type === "integer"
|
|
||||||
? {
|
|
||||||
name: entry.name,
|
|
||||||
required: entry.required,
|
|
||||||
selector: { number: { mode: "box" } },
|
|
||||||
}
|
|
||||||
: entry
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
private _filteredShchema = memoizeOne(
|
private _filteredShchema = memoizeOne(
|
||||||
@@ -162,7 +106,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
);
|
);
|
||||||
return html`
|
return html`
|
||||||
<h1>${this.addon.name}</h1>
|
<h1>${this.addon.name}</h1>
|
||||||
<ha-card outlined>
|
<ha-card>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h2>
|
<h2>
|
||||||
${this.supervisor.localize("addon.configuration.options.header")}
|
${this.supervisor.localize("addon.configuration.options.header")}
|
||||||
@@ -196,8 +140,7 @@ class HassioAddonConfig extends LitElement {
|
|||||||
.data=${this._options!}
|
.data=${this._options!}
|
||||||
@value-changed=${this._configChanged}
|
@value-changed=${this._configChanged}
|
||||||
.computeLabel=${this.computeLabel}
|
.computeLabel=${this.computeLabel}
|
||||||
.computeHelper=${this.computeHelper}
|
.schema=${this._schema(
|
||||||
.schema=${this._convertSchema(
|
|
||||||
this._showOptional
|
this._showOptional
|
||||||
? this.addon.schema!
|
? this.addon.schema!
|
||||||
: this._filteredShchema(
|
: this._filteredShchema(
|
||||||
@@ -254,9 +197,8 @@ class HassioAddonConfig extends LitElement {
|
|||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
this._canShowSchema = !this.addon.schema!.find(
|
this._canShowSchema = !this.addon.schema!.find(
|
||||||
(entry) =>
|
// @ts-ignore
|
||||||
// @ts-ignore
|
(entry) => !SUPPORTED_UI_TYPES.includes(entry.type) || entry.multiple
|
||||||
!SUPPORTED_UI_TYPES.includes(entry.type)
|
|
||||||
);
|
);
|
||||||
this._yamlMode = !this._canShowSchema;
|
this._yamlMode = !this._canShowSchema;
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@@ -7,13 +8,10 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
|
||||||
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
import { fireEvent } from "../../../../src/common/dom/fire_event";
|
||||||
import "../../../../src/components/buttons/ha-progress-button";
|
import "../../../../src/components/buttons/ha-progress-button";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import "../../../../src/components/ha-form/ha-form";
|
|
||||||
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
|
||||||
import {
|
import {
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
HassioAddonSetOptionParams,
|
HassioAddonSetOptionParams,
|
||||||
@@ -26,6 +24,16 @@ import { HomeAssistant } from "../../../../src/types";
|
|||||||
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
import { suggestAddonRestart } from "../../dialogs/suggestAddonRestart";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
|
|
||||||
|
interface NetworkItem {
|
||||||
|
description: string;
|
||||||
|
container: string;
|
||||||
|
host: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NetworkItemInput extends PaperInputElement {
|
||||||
|
container: string;
|
||||||
|
}
|
||||||
|
|
||||||
@customElement("hassio-addon-network")
|
@customElement("hassio-addon-network")
|
||||||
class HassioAddonNetwork extends LitElement {
|
class HassioAddonNetwork extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -34,13 +42,9 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
|
|
||||||
@state() private _showOptional = false;
|
|
||||||
|
|
||||||
@state() private _configHasChanged = false;
|
|
||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
@state() private _config?: Record<string, any>;
|
@state() private _config?: NetworkItem[];
|
||||||
|
|
||||||
public connectedCallback(): void {
|
public connectedCallback(): void {
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
@@ -52,61 +56,59 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasHiddenOptions = Object.keys(this._config).find(
|
|
||||||
(entry) => this._config![entry] === null
|
|
||||||
);
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
outlined
|
|
||||||
.header=${this.supervisor.localize(
|
.header=${this.supervisor.localize(
|
||||||
"addon.configuration.network.header"
|
"addon.configuration.network.header"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<p>
|
|
||||||
${this.supervisor.localize(
|
|
||||||
"addon.configuration.network.introduction"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
${this._error
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-form
|
<table>
|
||||||
.data=${this._config}
|
<tbody>
|
||||||
@value-changed=${this._configChanged}
|
<tr>
|
||||||
.computeLabel=${this._computeLabel}
|
<th>
|
||||||
.computeHelper=${this._computeHelper}
|
${this.supervisor.localize(
|
||||||
.schema=${this._createSchema(
|
"addon.configuration.network.container"
|
||||||
this._config,
|
)}
|
||||||
this._showOptional,
|
</th>
|
||||||
this.hass.userData?.showAdvanced || false
|
<th>
|
||||||
)}
|
${this.supervisor.localize(
|
||||||
></ha-form>
|
"addon.configuration.network.host"
|
||||||
</div>
|
)}
|
||||||
${hasHiddenOptions
|
</th>
|
||||||
? html`<ha-formfield
|
<th>${this.supervisor.localize("common.description")}</th>
|
||||||
class="show-optional"
|
</tr>
|
||||||
.label=${this.supervisor.localize(
|
${this._config!.map(
|
||||||
"addon.configuration.network.show_disabled"
|
(item) => html`
|
||||||
|
<tr>
|
||||||
|
<td>${item.container}</td>
|
||||||
|
<td>
|
||||||
|
<paper-input
|
||||||
|
@value-changed=${this._configChanged}
|
||||||
|
placeholder=${this.supervisor.localize(
|
||||||
|
"addon.configuration.network.disabled"
|
||||||
|
)}
|
||||||
|
.value=${item.host ? String(item.host) : ""}
|
||||||
|
.container=${item.container}
|
||||||
|
no-label-float
|
||||||
|
></paper-input>
|
||||||
|
</td>
|
||||||
|
<td>${this._computeDescription(item)}</td>
|
||||||
|
</tr>
|
||||||
|
`
|
||||||
)}
|
)}
|
||||||
>
|
</tbody>
|
||||||
<ha-switch
|
</table>
|
||||||
@change=${this._toggleOptional}
|
</div>
|
||||||
.checked=${this._showOptional}
|
|
||||||
>
|
|
||||||
</ha-switch>
|
|
||||||
</ha-formfield>`
|
|
||||||
: ""}
|
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-progress-button class="warning" @click=${this._resetTapped}>
|
<ha-progress-button class="warning" @click=${this._resetTapped}>
|
||||||
${this.supervisor.localize("common.reset_defaults")}
|
${this.supervisor.localize("common.reset_defaults")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
<ha-progress-button
|
<ha-progress-button @click=${this._saveTapped}>
|
||||||
@click=${this._saveTapped}
|
|
||||||
.disabled=${!this._configHasChanged}
|
|
||||||
>
|
|
||||||
${this.supervisor.localize("common.save")}
|
${this.supervisor.localize("common.save")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -121,60 +123,50 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createSchema = memoizeOne(
|
private _computeDescription = (item: NetworkItem): string =>
|
||||||
(
|
this.addon.translations[this.hass.language]?.network?.[item.container]
|
||||||
config: Record<string, number>,
|
?.description ||
|
||||||
showOptional: boolean,
|
this.addon.translations.en?.network?.[item.container]?.description ||
|
||||||
advanced: boolean
|
item.description;
|
||||||
): HaFormSchema[] =>
|
|
||||||
(showOptional
|
|
||||||
? Object.keys(config)
|
|
||||||
: Object.keys(config).filter((entry) => config[entry] !== null)
|
|
||||||
).map((entry) => ({
|
|
||||||
name: entry,
|
|
||||||
selector: {
|
|
||||||
number: {
|
|
||||||
mode: "box",
|
|
||||||
min: 0,
|
|
||||||
max: 65535,
|
|
||||||
unit_of_measurement: advanced ? entry : undefined,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
|
|
||||||
private _computeLabel = (_: HaFormSchema): string => "";
|
|
||||||
|
|
||||||
private _computeHelper = (item: HaFormSchema): string =>
|
|
||||||
this.addon.translations[this.hass.language]?.network?.[item.name] ||
|
|
||||||
this.addon.translations.en?.network?.[item.name] ||
|
|
||||||
this.addon.network_description?.[item.name] ||
|
|
||||||
item.name;
|
|
||||||
|
|
||||||
private _setNetworkConfig(): void {
|
private _setNetworkConfig(): void {
|
||||||
this._config = this.addon.network || {};
|
const network = this.addon.network || {};
|
||||||
|
const description = this.addon.network_description || {};
|
||||||
|
const items: NetworkItem[] = Object.keys(network).map((key) => ({
|
||||||
|
container: key,
|
||||||
|
host: network[key],
|
||||||
|
description: description[key],
|
||||||
|
}));
|
||||||
|
this._config = items.sort((a, b) => (a.container > b.container ? 1 : -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _configChanged(ev: CustomEvent): Promise<void> {
|
private async _configChanged(ev: Event): Promise<void> {
|
||||||
this._configHasChanged = true;
|
const target = ev.target as NetworkItemInput;
|
||||||
this._config! = ev.detail.value;
|
this._config!.forEach((item) => {
|
||||||
|
if (
|
||||||
|
item.container === target.container &&
|
||||||
|
item.host !== parseInt(String(target.value), 10)
|
||||||
|
) {
|
||||||
|
item.host = target.value ? parseInt(String(target.value), 10) : null;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _resetTapped(ev: CustomEvent): Promise<void> {
|
private async _resetTapped(ev: CustomEvent): Promise<void> {
|
||||||
const button = ev.currentTarget as any;
|
const button = ev.currentTarget as any;
|
||||||
|
button.progress = true;
|
||||||
|
|
||||||
const data: HassioAddonSetOptionParams = {
|
const data: HassioAddonSetOptionParams = {
|
||||||
network: null,
|
network: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||||
this._configHasChanged = false;
|
|
||||||
const eventdata = {
|
const eventdata = {
|
||||||
success: true,
|
success: true,
|
||||||
response: undefined,
|
response: undefined,
|
||||||
path: "option",
|
path: "option",
|
||||||
};
|
};
|
||||||
button.actionSuccess();
|
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
if (this.addon?.state === "started") {
|
if (this.addon?.state === "started") {
|
||||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||||
@@ -185,21 +177,19 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
"error",
|
"error",
|
||||||
extractApiErrorMessage(err)
|
extractApiErrorMessage(err)
|
||||||
);
|
);
|
||||||
button.actionError();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private _toggleOptional() {
|
button.progress = false;
|
||||||
this._showOptional = !this._showOptional;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _saveTapped(ev: CustomEvent): Promise<void> {
|
private async _saveTapped(ev: CustomEvent): Promise<void> {
|
||||||
const button = ev.currentTarget as any;
|
const button = ev.currentTarget as any;
|
||||||
|
button.progress = true;
|
||||||
|
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
const networkconfiguration = {};
|
const networkconfiguration = {};
|
||||||
Object.entries(this._config!).forEach(([key, value]) => {
|
this._config!.forEach((item) => {
|
||||||
networkconfiguration[key] = value ?? null;
|
networkconfiguration[item.container] = parseInt(String(item.host), 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
const data: HassioAddonSetOptionParams = {
|
const data: HassioAddonSetOptionParams = {
|
||||||
@@ -208,13 +198,11 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||||
this._configHasChanged = false;
|
|
||||||
const eventdata = {
|
const eventdata = {
|
||||||
success: true,
|
success: true,
|
||||||
response: undefined,
|
response: undefined,
|
||||||
path: "option",
|
path: "option",
|
||||||
};
|
};
|
||||||
button.actionSuccess();
|
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
fireEvent(this, "hass-api-called", eventdata);
|
||||||
if (this.addon?.state === "started") {
|
if (this.addon?.state === "started") {
|
||||||
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
await suggestAddonRestart(this, this.hass, this.supervisor, this.addon);
|
||||||
@@ -225,8 +213,8 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
"error",
|
"error",
|
||||||
extractApiErrorMessage(err)
|
extractApiErrorMessage(err)
|
||||||
);
|
);
|
||||||
button.actionError();
|
|
||||||
}
|
}
|
||||||
|
button.progress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
@@ -244,9 +232,6 @@ class HassioAddonNetwork extends LitElement {
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.show-optional {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,7 @@ class HassioAddonDocumentationDashboard extends LitElement {
|
|||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<ha-card outlined>
|
<ha-card>
|
||||||
${this._error
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
|
@@ -12,19 +12,12 @@ import { navigate } from "../../../src/common/navigate";
|
|||||||
import { extractSearchParam } from "../../../src/common/url/search-params";
|
import { extractSearchParam } from "../../../src/common/url/search-params";
|
||||||
import "../../../src/components/ha-circular-progress";
|
import "../../../src/components/ha-circular-progress";
|
||||||
import {
|
import {
|
||||||
fetchAddonInfo,
|
|
||||||
fetchHassioAddonInfo,
|
fetchHassioAddonInfo,
|
||||||
fetchHassioAddonsInfo,
|
fetchHassioAddonsInfo,
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||||
import {
|
|
||||||
addStoreRepository,
|
|
||||||
fetchSupervisorStore,
|
|
||||||
StoreAddonDetails,
|
|
||||||
} from "../../../src/data/supervisor/store";
|
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import { showConfirmationDialog } from "../../../src/dialogs/generic/show-dialog-box";
|
|
||||||
import "../../../src/layouts/hass-error-screen";
|
import "../../../src/layouts/hass-error-screen";
|
||||||
import "../../../src/layouts/hass-loading-screen";
|
import "../../../src/layouts/hass-loading-screen";
|
||||||
import "../../../src/layouts/hass-tabs-subpage";
|
import "../../../src/layouts/hass-tabs-subpage";
|
||||||
@@ -47,9 +40,7 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public route!: Route;
|
@property({ attribute: false }) public route!: Route;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon?:
|
@property({ attribute: false }) public addon?: HassioAddonDetails;
|
||||||
| HassioAddonDetails
|
|
||||||
| StoreAddonDetails;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public narrow!: boolean;
|
@property({ type: Boolean }) public narrow!: boolean;
|
||||||
|
|
||||||
@@ -175,39 +166,6 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
protected async firstUpdated(): Promise<void> {
|
protected async firstUpdated(): Promise<void> {
|
||||||
if (this.route.path === "") {
|
if (this.route.path === "") {
|
||||||
const requestedAddon = extractSearchParam("addon");
|
const requestedAddon = extractSearchParam("addon");
|
||||||
const requestedAddonRepository = extractSearchParam("repository_url");
|
|
||||||
if (requestedAddonRepository) {
|
|
||||||
const storeInfo = await fetchSupervisorStore(this.hass);
|
|
||||||
if (
|
|
||||||
!storeInfo.repositories.find(
|
|
||||||
(repo) => repo.source === requestedAddonRepository
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
!(await showConfirmationDialog(this, {
|
|
||||||
title: this.supervisor.localize("my.add_addon_repository_title"),
|
|
||||||
text: this.supervisor.localize(
|
|
||||||
"my.add_addon_repository_description",
|
|
||||||
{ addon: requestedAddon, repository: requestedAddonRepository }
|
|
||||||
),
|
|
||||||
confirmText: this.supervisor.localize("common.add"),
|
|
||||||
dismissText: this.supervisor.localize("common.cancel"),
|
|
||||||
}))
|
|
||||||
) {
|
|
||||||
this._error = this.supervisor.localize(
|
|
||||||
"my.error_repository_not_found"
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await addStoreRepository(this.hass, requestedAddonRepository);
|
|
||||||
} catch (err: any) {
|
|
||||||
this._error = extractApiErrorMessage(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (requestedAddon) {
|
if (requestedAddon) {
|
||||||
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
|
const addonsInfo = await fetchHassioAddonsInfo(this.hass);
|
||||||
const validAddon = addonsInfo.addons.some(
|
const validAddon = addonsInfo.addons.some(
|
||||||
@@ -244,8 +202,6 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
|
|
||||||
if (path === "uninstall") {
|
if (path === "uninstall") {
|
||||||
window.history.back();
|
window.history.back();
|
||||||
} else if (path === "install") {
|
|
||||||
this.addon = await fetchHassioAddonInfo(this.hass, this.addon!.slug);
|
|
||||||
} else {
|
} else {
|
||||||
await this._routeDataChanged();
|
await this._routeDataChanged();
|
||||||
}
|
}
|
||||||
@@ -263,7 +219,8 @@ class HassioAddonDashboard extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.addon = await fetchAddonInfo(this.hass, this.supervisor, addon);
|
const addoninfo = await fetchHassioAddonInfo(this.hass, addon);
|
||||||
|
this.addon = addoninfo;
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
|
this._error = `Error fetching addon info: ${extractApiErrorMessage(err)}`;
|
||||||
this.addon = undefined;
|
this.addon = undefined;
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
|
import { HassioAddonDetails } from "../../../src/data/hassio/addon";
|
||||||
import { StoreAddonDetails } from "../../../src/data/supervisor/store";
|
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
HassRouterPage,
|
HassRouterPage,
|
||||||
@@ -21,9 +20,7 @@ class HassioAddonRouter extends HassRouterPage {
|
|||||||
|
|
||||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon!:
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
| HassioAddonDetails
|
|
||||||
| StoreAddonDetails;
|
|
||||||
|
|
||||||
protected routerOptions: RouterOptions = {
|
protected routerOptions: RouterOptions = {
|
||||||
defaultPage: "info",
|
defaultPage: "info",
|
||||||
|
@@ -59,10 +59,7 @@ import {
|
|||||||
fetchHassioStats,
|
fetchHassioStats,
|
||||||
HassioStats,
|
HassioStats,
|
||||||
} from "../../../../src/data/hassio/common";
|
} from "../../../../src/data/hassio/common";
|
||||||
import {
|
import { StoreAddon } from "../../../../src/data/supervisor/store";
|
||||||
StoreAddon,
|
|
||||||
StoreAddonDetails,
|
|
||||||
} from "../../../../src/data/supervisor/store";
|
|
||||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
@@ -103,9 +100,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public addon!:
|
@property({ attribute: false }) public addon!: HassioAddonDetails;
|
||||||
| HassioAddonDetails
|
|
||||||
| StoreAddonDetails;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public supervisor!: Supervisor;
|
@property({ attribute: false }) public supervisor!: Supervisor;
|
||||||
|
|
||||||
@@ -148,7 +143,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
></update-available-card>
|
></update-available-card>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${"protected" in this.addon && !this.addon.protected
|
${!this.addon.protected
|
||||||
? html`
|
? html`
|
||||||
<ha-alert
|
<ha-alert
|
||||||
alert-type="error"
|
alert-type="error"
|
||||||
@@ -171,7 +166,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
|
||||||
<ha-card outlined>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="addon-header">
|
<div class="addon-header">
|
||||||
${!this.narrow ? this.addon.name : ""}
|
${!this.narrow ? this.addon.name : ""}
|
||||||
@@ -523,7 +518,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
${this.addon.version && this.addon.state === "started"
|
${this.addon.state === "started"
|
||||||
? html`<ha-settings-row ?three-line=${this.narrow}>
|
? html`<ha-settings-row ?three-line=${this.narrow}>
|
||||||
<span slot="heading">
|
<span slot="heading">
|
||||||
${this.supervisor.localize("addon.dashboard.hostname")}
|
${this.supervisor.localize("addon.dashboard.hostname")}
|
||||||
@@ -654,7 +649,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
|
|
||||||
${this.addon.long_description
|
${this.addon.long_description
|
||||||
? html`
|
? html`
|
||||||
<ha-card outlined>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<ha-markdown
|
<ha-markdown
|
||||||
.content=${this.addon.long_description}
|
.content=${this.addon.long_description}
|
||||||
@@ -674,7 +669,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _loadData(): Promise<void> {
|
private async _loadData(): Promise<void> {
|
||||||
if ("state" in this.addon && this.addon.state === "started") {
|
if (this.addon.state === "started") {
|
||||||
this._metrics = await fetchHassioStats(
|
this._metrics = await fetchHassioStats(
|
||||||
this.hass,
|
this.hass,
|
||||||
`addons/${this.addon.slug}`
|
`addons/${this.addon.slug}`
|
||||||
@@ -722,22 +717,18 @@ class HassioAddonInfo extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get _computeIsRunning(): boolean {
|
private get _computeIsRunning(): boolean {
|
||||||
return (this.addon as HassioAddonDetails)?.state === "started";
|
return this.addon?.state === "started";
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _pathWebui(): string | null {
|
private get _pathWebui(): string | null {
|
||||||
return (this.addon as HassioAddonDetails).webui!.replace(
|
return (
|
||||||
"[HOST]",
|
this.addon.webui &&
|
||||||
document.location.hostname
|
this.addon.webui.replace("[HOST]", document.location.hostname)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private get _computeShowWebUI(): boolean | "" | null {
|
private get _computeShowWebUI(): boolean | "" | null {
|
||||||
return (
|
return !this.addon.ingress && this.addon.webui && this._computeIsRunning;
|
||||||
!this.addon.ingress &&
|
|
||||||
(this.addon as HassioAddonDetails).webui &&
|
|
||||||
this._computeIsRunning
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _openIngress(): void {
|
private _openIngress(): void {
|
||||||
@@ -763,8 +754,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
private async _startOnBootToggled(): Promise<void> {
|
private async _startOnBootToggled(): Promise<void> {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
const data: HassioAddonSetOptionParams = {
|
const data: HassioAddonSetOptionParams = {
|
||||||
boot:
|
boot: this.addon.boot === "auto" ? "manual" : "auto",
|
||||||
(this.addon as HassioAddonDetails).boot === "auto" ? "manual" : "auto",
|
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||||
@@ -786,7 +776,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
private async _watchdogToggled(): Promise<void> {
|
private async _watchdogToggled(): Promise<void> {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
const data: HassioAddonSetOptionParams = {
|
const data: HassioAddonSetOptionParams = {
|
||||||
watchdog: !(this.addon as HassioAddonDetails).watchdog,
|
watchdog: !this.addon.watchdog,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||||
@@ -808,7 +798,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
private async _autoUpdateToggled(): Promise<void> {
|
private async _autoUpdateToggled(): Promise<void> {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
const data: HassioAddonSetOptionParams = {
|
const data: HassioAddonSetOptionParams = {
|
||||||
auto_update: !(this.addon as HassioAddonDetails).auto_update,
|
auto_update: !this.addon.auto_update,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||||
@@ -830,7 +820,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
private async _protectionToggled(): Promise<void> {
|
private async _protectionToggled(): Promise<void> {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
const data: HassioAddonSetSecurityParams = {
|
const data: HassioAddonSetSecurityParams = {
|
||||||
protected: !(this.addon as HassioAddonDetails).protected,
|
protected: !this.addon.protected,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
await setHassioAddonSecurity(this.hass, this.addon.slug, data);
|
await setHassioAddonSecurity(this.hass, this.addon.slug, data);
|
||||||
@@ -852,7 +842,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
private async _panelToggled(): Promise<void> {
|
private async _panelToggled(): Promise<void> {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
const data: HassioAddonSetOptionParams = {
|
const data: HassioAddonSetOptionParams = {
|
||||||
ingress_panel: !(this.addon as HassioAddonDetails).ingress_panel,
|
ingress_panel: !this.addon.ingress_panel,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
await setHassioAddonOption(this.hass, this.addon.slug, data);
|
||||||
@@ -880,7 +870,7 @@ class HassioAddonInfo extends LitElement {
|
|||||||
|
|
||||||
showHassioMarkdownDialog(this, {
|
showHassioMarkdownDialog(this, {
|
||||||
title: this.supervisor.localize("addon.dashboard.changelog"),
|
title: this.supervisor.localize("addon.dashboard.changelog"),
|
||||||
content: extractChangelog(this.addon as HassioAddonDetails, content),
|
content: extractChangelog(this.addon, content),
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
|
@@ -2,7 +2,6 @@ import "@material/mwc-button";
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import "../../../../src/components/ha-alert";
|
import "../../../../src/components/ha-alert";
|
||||||
import "../../../../src/components/ha-ansi-to-html";
|
|
||||||
import "../../../../src/components/ha-card";
|
import "../../../../src/components/ha-card";
|
||||||
import {
|
import {
|
||||||
fetchHassioAddonLogs,
|
fetchHassioAddonLogs,
|
||||||
@@ -12,6 +11,7 @@ import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
|||||||
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../../../src/data/supervisor/supervisor";
|
||||||
import { haStyle } from "../../../../src/resources/styles";
|
import { haStyle } from "../../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../../src/types";
|
import { HomeAssistant } from "../../../../src/types";
|
||||||
|
import "../../components/hassio-ansi-to-html";
|
||||||
import { hassioStyle } from "../../resources/hassio-style";
|
import { hassioStyle } from "../../resources/hassio-style";
|
||||||
|
|
||||||
@customElement("hassio-addon-logs")
|
@customElement("hassio-addon-logs")
|
||||||
@@ -34,15 +34,15 @@ class HassioAddonLogs extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<h1>${this.addon.name}</h1>
|
<h1>${this.addon.name}</h1>
|
||||||
<ha-card outlined>
|
<ha-card>
|
||||||
${this._error
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
${this._content
|
${this._content
|
||||||
? html`<ha-ansi-to-html
|
? html`<hassio-ansi-to-html
|
||||||
.content=${this._content}
|
.content=${this._content}
|
||||||
></ha-ansi-to-html>`
|
></hassio-ansi-to-html>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { ActionDetail } from "@material/mwc-list";
|
import { ActionDetail } from "@material/mwc-list";
|
||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import { mdiBackupRestore, mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
|
import { mdiDelete, mdiDotsVertical, mdiPlus } from "@mdi/js";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@@ -98,8 +98,9 @@ export class HassioBackups extends LitElement {
|
|||||||
if (backup.content.addons.length !== 0) {
|
if (backup.content.addons.length !== 0) {
|
||||||
for (const addon of backup.content.addons) {
|
for (const addon of backup.content.addons) {
|
||||||
content.push(
|
content.push(
|
||||||
this.supervisor.addon.addons.find((entry) => entry.slug === addon)
|
this.supervisor.supervisor.addons.find(
|
||||||
?.name || addon
|
(entry) => entry.slug === addon
|
||||||
|
)?.name || addon
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,15 +166,7 @@ export class HassioBackups extends LitElement {
|
|||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage-data-table
|
<hass-tabs-subpage-data-table
|
||||||
.tabs=${atLeastVersion(this.hass.config.version, 2022, 5)
|
.tabs=${supervisorTabs(this.hass)}
|
||||||
? [
|
|
||||||
{
|
|
||||||
translationKey: "panel.backups",
|
|
||||||
path: `/hassio/backups`,
|
|
||||||
iconPath: mdiBackupRestore,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: supervisorTabs(this.hass)}
|
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.localizeFunc=${this.supervisor.localize}
|
.localizeFunc=${this.supervisor.localize}
|
||||||
.searchLabel=${this.supervisor.localize("search")}
|
.searchLabel=${this.supervisor.localize("search")}
|
||||||
@@ -189,9 +182,7 @@ export class HassioBackups extends LitElement {
|
|||||||
selectable
|
selectable
|
||||||
hasFab
|
hasFab
|
||||||
.mainPage=${!atLeastVersion(this.hass.config.version, 2021, 12)}
|
.mainPage=${!atLeastVersion(this.hass.config.version, 2021, 12)}
|
||||||
back-path=${atLeastVersion(this.hass.config.version, 2022, 5)
|
back-path="/config"
|
||||||
? "/config/system"
|
|
||||||
: "/config"}
|
|
||||||
supervisor
|
supervisor
|
||||||
>
|
>
|
||||||
<ha-button-menu
|
<ha-button-menu
|
||||||
|
@@ -10,8 +10,8 @@ interface State {
|
|||||||
backgroundColor: null | string;
|
backgroundColor: null | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-ansi-to-html")
|
@customElement("hassio-ansi-to-html")
|
||||||
class HaAnsiToHtml extends LitElement {
|
class HassioAnsiToHtml extends LitElement {
|
||||||
@property() public content!: string;
|
@property() public content!: string;
|
||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
@@ -241,6 +241,6 @@ class HaAnsiToHtml extends LitElement {
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
"ha-ansi-to-html": HaAnsiToHtml;
|
"hassio-ansi-to-html": HassioAnsiToHtml;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,8 +1,8 @@
|
|||||||
import Fuse from "fuse.js";
|
import Fuse from "fuse.js";
|
||||||
import { StoreAddon } from "../../../src/data/supervisor/store";
|
import { HassioAddonInfo } from "../../../src/data/hassio/addon";
|
||||||
|
|
||||||
export function filterAndSort(addons: StoreAddon[], filter: string) {
|
export function filterAndSort(addons: HassioAddonInfo[], filter: string) {
|
||||||
const options: Fuse.IFuseOptions<StoreAddon> = {
|
const options: Fuse.IFuseOptions<HassioAddonInfo> = {
|
||||||
keys: ["name", "description", "slug"],
|
keys: ["name", "description", "slug"],
|
||||||
isCaseSensitive: false,
|
isCaseSensitive: false,
|
||||||
minMatchCharLength: 2,
|
minMatchCharLength: 2,
|
||||||
|
@@ -32,6 +32,13 @@ interface AddonCheckboxItem extends CheckboxItem {
|
|||||||
|
|
||||||
const _computeFolders = (folders): CheckboxItem[] => {
|
const _computeFolders = (folders): CheckboxItem[] => {
|
||||||
const list: CheckboxItem[] = [];
|
const list: CheckboxItem[] = [];
|
||||||
|
if (folders.includes("homeassistant")) {
|
||||||
|
list.push({
|
||||||
|
slug: "homeassistant",
|
||||||
|
name: "Home Assistant configuration",
|
||||||
|
checked: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (folders.includes("ssl")) {
|
if (folders.includes("ssl")) {
|
||||||
list.push({ slug: "ssl", name: "SSL", checked: false });
|
list.push({ slug: "ssl", name: "SSL", checked: false });
|
||||||
}
|
}
|
||||||
@@ -93,10 +100,10 @@ export class SupervisorBackupContent extends LitElement {
|
|||||||
this.folders = _computeFolders(
|
this.folders = _computeFolders(
|
||||||
this.backup
|
this.backup
|
||||||
? this.backup.folders
|
? this.backup.folders
|
||||||
: ["ssl", "share", "media", "addons/local"]
|
: ["homeassistant", "ssl", "share", "media", "addons/local"]
|
||||||
);
|
);
|
||||||
this.addons = _computeAddons(
|
this.addons = _computeAddons(
|
||||||
this.backup ? this.backup.addons : this.supervisor?.addon.addons
|
this.backup ? this.backup.addons : this.supervisor?.supervisor.addons
|
||||||
);
|
);
|
||||||
this.backupType = this.backup?.type || "full";
|
this.backupType = this.backup?.type || "full";
|
||||||
this.backupName = this.backup?.name || "";
|
this.backupName = this.backup?.name || "";
|
||||||
@@ -180,7 +187,7 @@ export class SupervisorBackupContent extends LitElement {
|
|||||||
>
|
>
|
||||||
<ha-checkbox
|
<ha-checkbox
|
||||||
.checked=${this.homeAssistant}
|
.checked=${this.homeAssistant}
|
||||||
@change=${this.toggleHomeAssistant}
|
@click=${this.toggleHomeAssistant}
|
||||||
>
|
>
|
||||||
</ha-checkbox>
|
</ha-checkbox>
|
||||||
</ha-formfield>
|
</ha-formfield>
|
||||||
|
@@ -24,9 +24,9 @@ class HassioAddons extends LitElement {
|
|||||||
? html` <h1>${this.supervisor.localize("dashboard.addons")}</h1> `
|
? html` <h1>${this.supervisor.localize("dashboard.addons")}</h1> `
|
||||||
: ""}
|
: ""}
|
||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
${!this.supervisor.addon.addons.length
|
${!this.supervisor.supervisor.addons?.length
|
||||||
? html`
|
? html`
|
||||||
<ha-card outlined>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<button class="link" @click=${this._openStore}>
|
<button class="link" @click=${this._openStore}>
|
||||||
${this.supervisor.localize("dashboard.no_addons")}
|
${this.supervisor.localize("dashboard.no_addons")}
|
||||||
@@ -34,15 +34,11 @@ class HassioAddons extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`
|
`
|
||||||
: this.supervisor.addon.addons
|
: this.supervisor.supervisor.addons
|
||||||
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name))
|
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name))
|
||||||
.map(
|
.map(
|
||||||
(addon) => html`
|
(addon) => html`
|
||||||
<ha-card
|
<ha-card .addon=${addon} @click=${this._addonTapped}>
|
||||||
outlined
|
|
||||||
.addon=${addon}
|
|
||||||
@click=${this._addonTapped}
|
|
||||||
>
|
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<hassio-card-content
|
<hassio-card-content
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
|
@@ -10,7 +10,6 @@ import { HomeAssistant, Route } from "../../../src/types";
|
|||||||
import { supervisorTabs } from "../hassio-tabs";
|
import { supervisorTabs } from "../hassio-tabs";
|
||||||
import "./hassio-addons";
|
import "./hassio-addons";
|
||||||
import "./hassio-update";
|
import "./hassio-update";
|
||||||
import "../../../src/layouts/hass-subpage";
|
|
||||||
|
|
||||||
@customElement("hassio-dashboard")
|
@customElement("hassio-dashboard")
|
||||||
class HassioDashboard extends LitElement {
|
class HassioDashboard extends LitElement {
|
||||||
@@ -23,31 +22,6 @@ class HassioDashboard extends LitElement {
|
|||||||
@property({ attribute: false }) public route!: Route;
|
@property({ attribute: false }) public route!: Route;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (atLeastVersion(this.hass.config.version, 2022, 5)) {
|
|
||||||
return html`<hass-subpage
|
|
||||||
.hass=${this.hass}
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
.route=${this.route}
|
|
||||||
.header=${this.supervisor.localize("panel.addons")}
|
|
||||||
>
|
|
||||||
<hassio-addons
|
|
||||||
.hass=${this.hass}
|
|
||||||
.supervisor=${this.supervisor}
|
|
||||||
></hassio-addons>
|
|
||||||
<a href="/hassio/store">
|
|
||||||
<ha-fab
|
|
||||||
.label=${this.supervisor.localize("panel.store")}
|
|
||||||
extended
|
|
||||||
class="non-tabs"
|
|
||||||
>
|
|
||||||
<ha-svg-icon
|
|
||||||
slot="icon"
|
|
||||||
.path=${mdiStorePlus}
|
|
||||||
></ha-svg-icon> </ha-fab
|
|
||||||
></a>
|
|
||||||
</hass-subpage>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@@ -100,12 +74,6 @@ class HassioDashboard extends LitElement {
|
|||||||
.content {
|
.content {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
ha-fab.non-tabs {
|
|
||||||
position: fixed;
|
|
||||||
right: calc(16px + env(safe-area-inset-right));
|
|
||||||
bottom: calc(16px + env(safe-area-inset-bottom));
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@@ -85,7 +85,7 @@ export class HassioUpdate extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<ha-card outlined>
|
<ha-card>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
|
||||||
|
@@ -15,18 +15,15 @@ import "../../../../src/components/ha-circular-progress";
|
|||||||
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
import { createCloseHeading } from "../../../../src/components/ha-dialog";
|
||||||
import "../../../../src/components/ha-icon-button";
|
import "../../../../src/components/ha-icon-button";
|
||||||
import {
|
import {
|
||||||
|
fetchHassioAddonsInfo,
|
||||||
HassioAddonInfo,
|
HassioAddonInfo,
|
||||||
HassioAddonRepository,
|
HassioAddonRepository,
|
||||||
} from "../../../../src/data/hassio/addon";
|
} from "../../../../src/data/hassio/addon";
|
||||||
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
|
||||||
|
import { setSupervisorOption } from "../../../../src/data/hassio/supervisor";
|
||||||
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
import { haStyle, haStyleDialog } from "../../../../src/resources/styles";
|
||||||
import type { HomeAssistant } from "../../../../src/types";
|
import type { HomeAssistant } from "../../../../src/types";
|
||||||
import { HassioRepositoryDialogParams } from "./show-dialog-repositories";
|
import { HassioRepositoryDialogParams } from "./show-dialog-repositories";
|
||||||
import {
|
|
||||||
addStoreRepository,
|
|
||||||
fetchStoreRepositories,
|
|
||||||
removeStoreRepository,
|
|
||||||
} from "../../../../src/data/supervisor/store";
|
|
||||||
|
|
||||||
@customElement("dialog-hassio-repositories")
|
@customElement("dialog-hassio-repositories")
|
||||||
class HassioRepositoriesDialog extends LitElement {
|
class HassioRepositoriesDialog extends LitElement {
|
||||||
@@ -61,13 +58,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
|
|
||||||
private _filteredRepositories = memoizeOne((repos: HassioAddonRepository[]) =>
|
private _filteredRepositories = memoizeOne((repos: HassioAddonRepository[]) =>
|
||||||
repos
|
repos
|
||||||
.filter(
|
.filter((repo) => repo.slug !== "core" && repo.slug !== "local")
|
||||||
(repo) =>
|
|
||||||
repo.slug !== "core" && // The core add-ons repository
|
|
||||||
repo.slug !== "local" && // Locally managed add-ons
|
|
||||||
repo.slug !== "a0d7b954" && // Home Assistant Community Add-ons
|
|
||||||
repo.slug !== "5c53de3b" // The ESPHome repository
|
|
||||||
)
|
|
||||||
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name))
|
.sort((a, b) => caseInsensitiveStringCompare(a.name, b.name))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -87,7 +78,7 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
const repositories = this._filteredRepositories(this._repositories);
|
const repositories = this._filteredRepositories(this._repositories);
|
||||||
const usedRepositories = this._filteredUsedRepositories(
|
const usedRepositories = this._filteredUsedRepositories(
|
||||||
repositories,
|
repositories,
|
||||||
this._dialogParams.supervisor.addon.addons
|
this._dialogParams.supervisor.supervisor.addons
|
||||||
);
|
);
|
||||||
return html`
|
return html`
|
||||||
<ha-dialog
|
<ha-dialog
|
||||||
@@ -224,7 +215,9 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
|
|
||||||
private async _loadData(): Promise<void> {
|
private async _loadData(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
this._repositories = await fetchStoreRepositories(this.hass);
|
const addonsinfo = await fetchHassioAddonsInfo(this.hass);
|
||||||
|
|
||||||
|
this._repositories = addonsinfo.repositories;
|
||||||
|
|
||||||
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
|
fireEvent(this, "supervisor-collection-refresh", { collection: "addon" });
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -238,9 +231,14 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._processing = true;
|
this._processing = true;
|
||||||
|
const repositories = this._filteredRepositories(this._repositories!);
|
||||||
|
const newRepositories = repositories.map((repo) => repo.source);
|
||||||
|
newRepositories.push(input.value);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await addStoreRepository(this.hass, input.value);
|
await setSupervisorOption(this.hass, {
|
||||||
|
addons_repositories: newRepositories,
|
||||||
|
});
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
|
|
||||||
input.value = "";
|
input.value = "";
|
||||||
@@ -252,8 +250,19 @@ class HassioRepositoriesDialog extends LitElement {
|
|||||||
|
|
||||||
private async _removeRepository(ev: Event) {
|
private async _removeRepository(ev: Event) {
|
||||||
const slug = (ev.currentTarget as any).slug;
|
const slug = (ev.currentTarget as any).slug;
|
||||||
|
const repositories = this._filteredRepositories(this._repositories!);
|
||||||
|
const repository = repositories.find((repo) => repo.slug === slug);
|
||||||
|
if (!repository) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const newRepositories = repositories
|
||||||
|
.map((repo) => repo.source)
|
||||||
|
.filter((repo) => repo !== repository.source);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await removeStoreRepository(this.hass, slug);
|
await setSupervisorOption(this.hass, {
|
||||||
|
addons_repositories: newRepositories,
|
||||||
|
});
|
||||||
await this._loadData();
|
await this._loadData();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this._error = extractApiErrorMessage(err);
|
this._error = extractApiErrorMessage(err);
|
||||||
|
@@ -3,8 +3,8 @@ import { customElement, property } from "lit/decorators";
|
|||||||
import { atLeastVersion } from "../../src/common/config/version";
|
import { atLeastVersion } from "../../src/common/config/version";
|
||||||
import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element";
|
import { applyThemesOnElement } from "../../src/common/dom/apply_themes_on_element";
|
||||||
import { fireEvent } from "../../src/common/dom/fire_event";
|
import { fireEvent } from "../../src/common/dom/fire_event";
|
||||||
import { mainWindow } from "../../src/common/dom/get_main_window";
|
|
||||||
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
|
import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
|
||||||
|
import { mainWindow } from "../../src/common/dom/get_main_window";
|
||||||
import { navigate } from "../../src/common/navigate";
|
import { navigate } from "../../src/common/navigate";
|
||||||
import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
|
import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
|
||||||
import { Supervisor } from "../../src/data/supervisor/supervisor";
|
import { Supervisor } from "../../src/data/supervisor/supervisor";
|
||||||
@@ -73,18 +73,6 @@ export class HassioMain extends SupervisorBaseElement {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Forward keydown events to the main window for quickbar access
|
|
||||||
document.body.addEventListener("keydown", (ev: KeyboardEvent) => {
|
|
||||||
if (ev.altKey || ev.ctrlKey || ev.shiftKey || ev.metaKey) {
|
|
||||||
// Ignore if modifier keys are pressed
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// @ts-ignore
|
|
||||||
fireEvent(mainWindow, "hass-quick-bar-trigger", ev, {
|
|
||||||
bubbles: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
makeDialogManager(this, this.shadowRoot!);
|
makeDialogManager(this, this.shadowRoot!);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,7 +15,7 @@ import {
|
|||||||
} from "../../src/panels/my/ha-panel-my";
|
} from "../../src/panels/my/ha-panel-my";
|
||||||
import { HomeAssistant, Route } from "../../src/types";
|
import { HomeAssistant, Route } from "../../src/types";
|
||||||
|
|
||||||
export const REDIRECTS: Redirects = {
|
const REDIRECTS: Redirects = {
|
||||||
supervisor: {
|
supervisor: {
|
||||||
redirect: "/hassio/dashboard",
|
redirect: "/hassio/dashboard",
|
||||||
},
|
},
|
||||||
@@ -42,9 +42,6 @@ export const REDIRECTS: Redirects = {
|
|||||||
params: {
|
params: {
|
||||||
addon: "string",
|
addon: "string",
|
||||||
},
|
},
|
||||||
optional_params: {
|
|
||||||
repository_url: "url",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
supervisor_ingress: {
|
supervisor_ingress: {
|
||||||
redirect: "/hassio/ingress",
|
redirect: "/hassio/ingress",
|
||||||
@@ -127,14 +124,6 @@ class HassioMyRedirect extends LitElement {
|
|||||||
}
|
}
|
||||||
resultParams[key] = params[key];
|
resultParams[key] = params[key];
|
||||||
});
|
});
|
||||||
Object.entries(redirect.optional_params || {}).forEach(([key, type]) => {
|
|
||||||
if (params[key]) {
|
|
||||||
if (!this._checkParamType(type, params[key])) {
|
|
||||||
throw Error();
|
|
||||||
}
|
|
||||||
resultParams[key] = params[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return `?${createSearchParam(resultParams)}`;
|
return `?${createSearchParam(resultParams)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,27 +8,24 @@ import { atLeastVersion } from "../../src/common/config/version";
|
|||||||
import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
|
import type { PageNavigation } from "../../src/layouts/hass-tabs-subpage";
|
||||||
import { HomeAssistant } from "../../src/types";
|
import { HomeAssistant } from "../../src/types";
|
||||||
|
|
||||||
export const supervisorTabs = (hass: HomeAssistant): PageNavigation[] =>
|
export const supervisorTabs = (hass: HomeAssistant): PageNavigation[] => [
|
||||||
atLeastVersion(hass.config.version, 2022, 5)
|
{
|
||||||
? []
|
translationKey: atLeastVersion(hass.config.version, 2021, 12)
|
||||||
: [
|
? "panel.addons"
|
||||||
{
|
: "panel.dashboard",
|
||||||
translationKey: atLeastVersion(hass.config.version, 2021, 12)
|
path: `/hassio/dashboard`,
|
||||||
? "panel.addons"
|
iconPath: atLeastVersion(hass.config.version, 2021, 12)
|
||||||
: "panel.dashboard",
|
? mdiPuzzle
|
||||||
path: `/hassio/dashboard`,
|
: mdiViewDashboard,
|
||||||
iconPath: atLeastVersion(hass.config.version, 2021, 12)
|
},
|
||||||
? mdiPuzzle
|
{
|
||||||
: mdiViewDashboard,
|
translationKey: "panel.backups",
|
||||||
},
|
path: `/hassio/backups`,
|
||||||
{
|
iconPath: mdiBackupRestore,
|
||||||
translationKey: "panel.backups",
|
},
|
||||||
path: `/hassio/backups`,
|
{
|
||||||
iconPath: mdiBackupRestore,
|
translationKey: "panel.system",
|
||||||
},
|
path: `/hassio/system`,
|
||||||
{
|
iconPath: mdiCogs,
|
||||||
translationKey: "panel.system",
|
},
|
||||||
path: `/hassio/system`,
|
];
|
||||||
iconPath: mdiCogs,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
@@ -48,7 +48,7 @@ class HassioCoreInfo extends LitElement {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card header="Core" outlined>
|
<ha-card header="Core">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div>
|
<div>
|
||||||
<ha-settings-row>
|
<ha-settings-row>
|
||||||
|
@@ -66,7 +66,7 @@ class HassioHostInfo extends LitElement {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
return html`
|
return html`
|
||||||
<ha-card header="Host" outlined>
|
<ha-card header="Host">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div>
|
<div>
|
||||||
${this.supervisor.host.features.includes("hostname")
|
${this.supervisor.host.features.includes("hostname")
|
||||||
|
@@ -23,10 +23,6 @@ import {
|
|||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
} from "../../../src/dialogs/generic/show-dialog-box";
|
} from "../../../src/dialogs/generic/show-dialog-box";
|
||||||
import {
|
|
||||||
UNHEALTHY_REASON_URL,
|
|
||||||
UNSUPPORTED_REASON_URL,
|
|
||||||
} from "../../../src/panels/config/system-health/ha-config-system-health";
|
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import { bytesToString } from "../../../src/util/bytes-to-string";
|
import { bytesToString } from "../../../src/util/bytes-to-string";
|
||||||
@@ -34,6 +30,11 @@ import { documentationUrl } from "../../../src/util/documentation-url";
|
|||||||
import "../components/supervisor-metric";
|
import "../components/supervisor-metric";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
|
const UNSUPPORTED_REASON_URL = {};
|
||||||
|
const UNHEALTHY_REASON_URL = {
|
||||||
|
privileged: "/more-info/unsupported/privileged",
|
||||||
|
};
|
||||||
|
|
||||||
@customElement("hassio-supervisor-info")
|
@customElement("hassio-supervisor-info")
|
||||||
class HassioSupervisorInfo extends LitElement {
|
class HassioSupervisorInfo extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@@ -57,7 +58,7 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
return html`
|
return html`
|
||||||
<ha-card header="Supervisor" outlined>
|
<ha-card header="Supervisor">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div>
|
<div>
|
||||||
<ha-settings-row>
|
<ha-settings-row>
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "../../../src/components/ha-ansi-to-html";
|
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
@@ -12,6 +11,7 @@ import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
|||||||
import "../../../src/layouts/hass-loading-screen";
|
import "../../../src/layouts/hass-loading-screen";
|
||||||
import { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
import "../components/hassio-ansi-to-html";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
|
||||||
interface LogProvider {
|
interface LogProvider {
|
||||||
@@ -65,7 +65,7 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
|
|
||||||
protected render(): TemplateResult | void {
|
protected render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<ha-card outlined>
|
<ha-card>
|
||||||
${this._error
|
${this._error
|
||||||
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
? html`<ha-alert alert-type="error">${this._error}</ha-alert>`
|
||||||
: ""}
|
: ""}
|
||||||
@@ -89,8 +89,8 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
|
|
||||||
<div class="card-content" id="content">
|
<div class="card-content" id="content">
|
||||||
${this._content
|
${this._content
|
||||||
? html`<ha-ansi-to-html .content=${this._content}>
|
? html`<hassio-ansi-to-html .content=${this._content}>
|
||||||
</ha-ansi-to-html>`
|
</hassio-ansi-to-html>`
|
||||||
: html`<hass-loading-screen no-toolbar></hass-loading-screen>`}
|
: html`<hass-loading-screen no-toolbar></hass-loading-screen>`}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
|
@@ -128,7 +128,6 @@ class UpdateAvailableCard extends LitElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
<ha-card
|
||||||
outlined
|
|
||||||
.header=${this.supervisor.localize("update_available.update_name", {
|
.header=${this.supervisor.localize("update_available.update_name", {
|
||||||
name: this._name,
|
name: this._name,
|
||||||
})}
|
})}
|
||||||
|
10
package.json
@@ -72,8 +72,8 @@
|
|||||||
"@material/mwc-textfield": "0.25.3",
|
"@material/mwc-textfield": "0.25.3",
|
||||||
"@material/mwc-top-app-bar-fixed": "^0.25.3",
|
"@material/mwc-top-app-bar-fixed": "^0.25.3",
|
||||||
"@material/top-app-bar": "14.0.0-canary.261f2db59.0",
|
"@material/top-app-bar": "14.0.0-canary.261f2db59.0",
|
||||||
"@mdi/js": "6.7.96",
|
"@mdi/js": "6.6.95",
|
||||||
"@mdi/svg": "6.7.96",
|
"@mdi/svg": "6.6.95",
|
||||||
"@polymer/app-layout": "^3.1.0",
|
"@polymer/app-layout": "^3.1.0",
|
||||||
"@polymer/iron-flex-layout": "^3.0.1",
|
"@polymer/iron-flex-layout": "^3.0.1",
|
||||||
"@polymer/iron-icon": "^3.0.1",
|
"@polymer/iron-icon": "^3.0.1",
|
||||||
@@ -89,8 +89,8 @@
|
|||||||
"@polymer/paper-tooltip": "^3.0.1",
|
"@polymer/paper-tooltip": "^3.0.1",
|
||||||
"@polymer/polymer": "3.4.1",
|
"@polymer/polymer": "3.4.1",
|
||||||
"@thomasloven/round-slider": "0.5.4",
|
"@thomasloven/round-slider": "0.5.4",
|
||||||
"@vaadin/combo-box": "^23.0.10",
|
"@vaadin/combo-box": "^22.0.4",
|
||||||
"@vaadin/vaadin-themable-mixin": "^23.0.10",
|
"@vaadin/vaadin-themable-mixin": "^22.0.4",
|
||||||
"@vibrant/color": "^3.2.1-alpha.1",
|
"@vibrant/color": "^3.2.1-alpha.1",
|
||||||
"@vibrant/core": "^3.2.1-alpha.1",
|
"@vibrant/core": "^3.2.1-alpha.1",
|
||||||
"@vibrant/quantizer-mmcq": "^3.2.1-alpha.1",
|
"@vibrant/quantizer-mmcq": "^3.2.1-alpha.1",
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
"fuse.js": "^6.0.0",
|
"fuse.js": "^6.0.0",
|
||||||
"google-timezones-json": "^1.0.2",
|
"google-timezones-json": "^1.0.2",
|
||||||
"hls.js": "^1.1.5",
|
"hls.js": "^1.1.5",
|
||||||
"home-assistant-js-websocket": "^7.1.0",
|
"home-assistant-js-websocket": "^7.0.1",
|
||||||
"idb-keyval": "^5.1.3",
|
"idb-keyval": "^5.1.3",
|
||||||
"intl-messageformat": "^9.9.1",
|
"intl-messageformat": "^9.9.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
|
@@ -1,30 +1,3 @@
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools~=62.3", "wheel~=0.37.1"]
|
requires = ["setuptools~=60.5", "wheel~=0.37.1"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
|
||||||
name = "home-assistant-frontend"
|
|
||||||
version = "20220624.0"
|
|
||||||
license = {text = "Apache-2.0"}
|
|
||||||
description = "The Home Assistant frontend"
|
|
||||||
readme = "README.md"
|
|
||||||
authors = [
|
|
||||||
{name = "The Home Assistant Authors", email = "hello@home-assistant.io"}
|
|
||||||
]
|
|
||||||
requires-python = ">=3.4.0"
|
|
||||||
|
|
||||||
[project.urls]
|
|
||||||
"Homepage" = "https://github.com/home-assistant/frontend"
|
|
||||||
|
|
||||||
[tool.setuptools]
|
|
||||||
platforms = ["any"]
|
|
||||||
zip-safe = false
|
|
||||||
include-package-data = true
|
|
||||||
|
|
||||||
[tool.setuptools.packages.find]
|
|
||||||
include = ["hass_frontend*"]
|
|
||||||
|
|
||||||
[tool.mypy]
|
|
||||||
python_version = 3.4
|
|
||||||
show_error_codes = true
|
|
||||||
strict = true
|
|
||||||
|
@@ -15,7 +15,7 @@ if [ -z $(which hass) ]; then
|
|||||||
echo "Installing Home Asstant core from dev."
|
echo "Installing Home Asstant core from dev."
|
||||||
python3 -m pip install --upgrade \
|
python3 -m pip install --upgrade \
|
||||||
colorlog \
|
colorlog \
|
||||||
git+https://github.com/home-assistant/home-assistant.git@dev
|
git+git://github.com/home-assistant/home-assistant.git@dev
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d "${WD}/config" ]; then
|
if [ ! -d "${WD}/config" ]; then
|
||||||
|
@@ -50,14 +50,14 @@ async function main(args) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const setup = fs.readFileSync("pyproject.toml", "utf8");
|
const setup = fs.readFileSync("setup.cfg", "utf8");
|
||||||
const version = setup.match(/version\W+=\W"(\d{8}\.\d)"/)[1];
|
const version = setup.match(/\d{8}\.\d+/)[0];
|
||||||
const newVersion = method(version);
|
const newVersion = method(version);
|
||||||
|
|
||||||
console.log("Current version:", version);
|
console.log("Current version:", version);
|
||||||
console.log("New version:", newVersion);
|
console.log("New version:", newVersion);
|
||||||
|
|
||||||
fs.writeFileSync("pyproject.toml", setup.replace(version, newVersion), "utf-8");
|
fs.writeFileSync("setup.cfg", setup.replace(version, newVersion), "utf-8");
|
||||||
|
|
||||||
if (!commit) {
|
if (!commit) {
|
||||||
return;
|
return;
|
||||||
|
26
setup.cfg
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
[metadata]
|
||||||
|
name = home-assistant-frontend
|
||||||
|
version = 20220322.0
|
||||||
|
author = The Home Assistant Authors
|
||||||
|
author_email = hello@home-assistant.io
|
||||||
|
license = Apache-2.0
|
||||||
|
platforms = any
|
||||||
|
description = The Home Assistant frontend
|
||||||
|
long_description = file: README.md
|
||||||
|
long_description_content_type = text/markdown
|
||||||
|
url = https://github.com/home-assistant/frontend
|
||||||
|
|
||||||
|
[options]
|
||||||
|
packages = find:
|
||||||
|
zip_safe = False
|
||||||
|
include_package_data = True
|
||||||
|
python_requires = >= 3.4.0
|
||||||
|
|
||||||
|
[options.packages.find]
|
||||||
|
include =
|
||||||
|
hass_frontend*
|
||||||
|
|
||||||
|
[mypy]
|
||||||
|
python_version = 3.4
|
||||||
|
show_error_codes = True
|
||||||
|
strict = True
|
@@ -1,16 +0,0 @@
|
|||||||
import secondsToDuration from "./seconds_to_duration";
|
|
||||||
|
|
||||||
const DAY_IN_SECONDS = 86400;
|
|
||||||
const HOUR_IN_SECONDS = 3600;
|
|
||||||
const MINUTE_IN_SECONDS = 60;
|
|
||||||
|
|
||||||
export const UNIT_TO_SECOND_CONVERT = {
|
|
||||||
s: 1,
|
|
||||||
min: MINUTE_IN_SECONDS,
|
|
||||||
h: HOUR_IN_SECONDS,
|
|
||||||
d: DAY_IN_SECONDS,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const formatDuration = (duration: string, units: string): string =>
|
|
||||||
secondsToDuration(parseFloat(duration) * UNIT_TO_SECOND_CONVERT[units]) ||
|
|
||||||
"0";
|
|
@@ -1,41 +0,0 @@
|
|||||||
const DEFAULT_OWN = true;
|
|
||||||
|
|
||||||
// Finds the closest ancestor of an element that has a specific optionally owned property,
|
|
||||||
// traversing slot and shadow root boundaries until the body element is reached
|
|
||||||
export const closestWithProperty = (
|
|
||||||
element: Element | null,
|
|
||||||
property: string | symbol,
|
|
||||||
own = DEFAULT_OWN
|
|
||||||
) => {
|
|
||||||
if (!element || element === document.body) return null;
|
|
||||||
|
|
||||||
element = element.assignedSlot ?? element;
|
|
||||||
if (element.parentElement) {
|
|
||||||
element = element.parentElement;
|
|
||||||
} else {
|
|
||||||
const root = element.getRootNode();
|
|
||||||
element = root instanceof ShadowRoot ? root.host : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
own
|
|
||||||
? Object.prototype.hasOwnProperty.call(element, property)
|
|
||||||
: element && property in element
|
|
||||||
)
|
|
||||||
return element;
|
|
||||||
return closestWithProperty(element, property, own);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Finds the set of all such ancestors and includes starting element as first in the set
|
|
||||||
export const ancestorsWithProperty = (
|
|
||||||
element: Element | null,
|
|
||||||
property: string | symbol,
|
|
||||||
own = DEFAULT_OWN
|
|
||||||
) => {
|
|
||||||
const ancestors: Set<Element> = new Set();
|
|
||||||
while (element) {
|
|
||||||
ancestors.add(element);
|
|
||||||
element = closestWithProperty(element, property, own);
|
|
||||||
}
|
|
||||||
return ancestors;
|
|
||||||
};
|
|
@@ -12,7 +12,7 @@ export const isNavigationClick = (e: MouseEvent) => {
|
|||||||
|
|
||||||
const anchor = e
|
const anchor = e
|
||||||
.composedPath()
|
.composedPath()
|
||||||
.find((n) => (n as HTMLElement).tagName === "A") as
|
.filter((n) => (n as HTMLElement).tagName === "A")[0] as
|
||||||
| HTMLAnchorElement
|
| HTMLAnchorElement
|
||||||
| undefined;
|
| undefined;
|
||||||
if (
|
if (
|
||||||
|
@@ -29,11 +29,8 @@ import {
|
|||||||
mdiPowerPlug,
|
mdiPowerPlug,
|
||||||
mdiPowerPlugOff,
|
mdiPowerPlugOff,
|
||||||
mdiRadioboxBlank,
|
mdiRadioboxBlank,
|
||||||
|
mdiSmoke,
|
||||||
mdiSnowflake,
|
mdiSnowflake,
|
||||||
mdiSmokeDetector,
|
|
||||||
mdiSmokeDetectorAlert,
|
|
||||||
mdiSmokeDetectorVariant,
|
|
||||||
mdiSmokeDetectorVariantAlert,
|
|
||||||
mdiSquare,
|
mdiSquare,
|
||||||
mdiSquareOutline,
|
mdiSquareOutline,
|
||||||
mdiStop,
|
mdiStop,
|
||||||
@@ -55,8 +52,6 @@ export const binarySensorIcon = (state?: string, stateObj?: HassEntity) => {
|
|||||||
return is_off ? mdiBattery : mdiBatteryOutline;
|
return is_off ? mdiBattery : mdiBatteryOutline;
|
||||||
case "battery_charging":
|
case "battery_charging":
|
||||||
return is_off ? mdiBattery : mdiBatteryCharging;
|
return is_off ? mdiBattery : mdiBatteryCharging;
|
||||||
case "carbon_monoxide":
|
|
||||||
return is_off ? mdiSmokeDetector : mdiSmokeDetectorAlert;
|
|
||||||
case "cold":
|
case "cold":
|
||||||
return is_off ? mdiThermometer : mdiSnowflake;
|
return is_off ? mdiThermometer : mdiSnowflake;
|
||||||
case "connectivity":
|
case "connectivity":
|
||||||
@@ -73,7 +68,7 @@ export const binarySensorIcon = (state?: string, stateObj?: HassEntity) => {
|
|||||||
case "tamper":
|
case "tamper":
|
||||||
return is_off ? mdiCheckCircle : mdiAlertCircle;
|
return is_off ? mdiCheckCircle : mdiAlertCircle;
|
||||||
case "smoke":
|
case "smoke":
|
||||||
return is_off ? mdiSmokeDetectorVariant : mdiSmokeDetectorVariantAlert;
|
return is_off ? mdiCheckCircle : mdiSmoke;
|
||||||
case "heat":
|
case "heat":
|
||||||
return is_off ? mdiThermometer : mdiFire;
|
return is_off ? mdiThermometer : mdiFire;
|
||||||
case "light":
|
case "light":
|
||||||
|
@@ -1,11 +1,6 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { UNAVAILABLE_STATES } from "../../data/entity";
|
|
||||||
|
|
||||||
export const computeActiveState = (stateObj: HassEntity): string => {
|
export const computeActiveState = (stateObj: HassEntity): string => {
|
||||||
if (UNAVAILABLE_STATES.includes(stateObj.state)) {
|
|
||||||
return stateObj.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
const domain = stateObj.entity_id.split(".")[0];
|
const domain = stateObj.entity_id.split(".")[0];
|
||||||
let state = stateObj.state;
|
let state = stateObj.state;
|
||||||
|
|
||||||
|
@@ -2,74 +2,50 @@ import { HassEntity } from "home-assistant-js-websocket";
|
|||||||
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
||||||
import { FrontendLocaleData } from "../../data/translation";
|
import { FrontendLocaleData } from "../../data/translation";
|
||||||
import {
|
import {
|
||||||
|
updateIsInstalling,
|
||||||
|
UpdateEntity,
|
||||||
UPDATE_SUPPORT_PROGRESS,
|
UPDATE_SUPPORT_PROGRESS,
|
||||||
updateIsInstallingFromAttributes,
|
|
||||||
} from "../../data/update";
|
} from "../../data/update";
|
||||||
import { formatDate } from "../datetime/format_date";
|
import { formatDate } from "../datetime/format_date";
|
||||||
import { formatDateTime } from "../datetime/format_date_time";
|
import { formatDateTime } from "../datetime/format_date_time";
|
||||||
import { formatTime } from "../datetime/format_time";
|
import { formatTime } from "../datetime/format_time";
|
||||||
import { formatNumber, isNumericFromAttributes } from "../number/format_number";
|
import { formatNumber, isNumericState } from "../number/format_number";
|
||||||
import { LocalizeFunc } from "../translations/localize";
|
import { LocalizeFunc } from "../translations/localize";
|
||||||
import { supportsFeatureFromAttributes } from "./supports-feature";
|
import { computeStateDomain } from "./compute_state_domain";
|
||||||
import { formatDuration, UNIT_TO_SECOND_CONVERT } from "../datetime/duration";
|
import { supportsFeature } from "./supports-feature";
|
||||||
import { computeDomain } from "./compute_domain";
|
|
||||||
|
|
||||||
export const computeStateDisplay = (
|
export const computeStateDisplay = (
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
stateObj: HassEntity,
|
stateObj: HassEntity,
|
||||||
locale: FrontendLocaleData,
|
locale: FrontendLocaleData,
|
||||||
state?: string
|
state?: string
|
||||||
): string =>
|
|
||||||
computeStateDisplayFromEntityAttributes(
|
|
||||||
localize,
|
|
||||||
locale,
|
|
||||||
stateObj.entity_id,
|
|
||||||
stateObj.attributes,
|
|
||||||
state !== undefined ? state : stateObj.state
|
|
||||||
);
|
|
||||||
|
|
||||||
export const computeStateDisplayFromEntityAttributes = (
|
|
||||||
localize: LocalizeFunc,
|
|
||||||
locale: FrontendLocaleData,
|
|
||||||
entityId: string,
|
|
||||||
attributes: any,
|
|
||||||
state: string
|
|
||||||
): string => {
|
): string => {
|
||||||
if (state === UNKNOWN || state === UNAVAILABLE) {
|
const compareState = state !== undefined ? state : stateObj.state;
|
||||||
return localize(`state.default.${state}`);
|
|
||||||
|
if (compareState === UNKNOWN || compareState === UNAVAILABLE) {
|
||||||
|
return localize(`state.default.${compareState}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entities with a `unit_of_measurement` or `state_class` are numeric values and should use `formatNumber`
|
// Entities with a `unit_of_measurement` or `state_class` are numeric values and should use `formatNumber`
|
||||||
if (isNumericFromAttributes(attributes)) {
|
if (isNumericState(stateObj)) {
|
||||||
// state is duration
|
if (stateObj.attributes.device_class === "monetary") {
|
||||||
if (
|
|
||||||
attributes.device_class === "duration" &&
|
|
||||||
attributes.unit_of_measurement &&
|
|
||||||
UNIT_TO_SECOND_CONVERT[attributes.unit_of_measurement]
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
return formatDuration(state, attributes.unit_of_measurement);
|
return formatNumber(compareState, locale, {
|
||||||
} catch (_err) {
|
|
||||||
// fallback to default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (attributes.device_class === "monetary") {
|
|
||||||
try {
|
|
||||||
return formatNumber(state, locale, {
|
|
||||||
style: "currency",
|
style: "currency",
|
||||||
currency: attributes.unit_of_measurement,
|
currency: stateObj.attributes.unit_of_measurement,
|
||||||
minimumFractionDigits: 2,
|
|
||||||
});
|
});
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
// fallback to default
|
// fallback to default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return `${formatNumber(state, locale)}${
|
return `${formatNumber(compareState, locale)}${
|
||||||
attributes.unit_of_measurement ? " " + attributes.unit_of_measurement : ""
|
stateObj.attributes.unit_of_measurement
|
||||||
|
? " " + stateObj.attributes.unit_of_measurement
|
||||||
|
: ""
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const domain = computeDomain(entityId);
|
const domain = computeStateDomain(stateObj);
|
||||||
|
|
||||||
if (domain === "input_datetime") {
|
if (domain === "input_datetime") {
|
||||||
if (state !== undefined) {
|
if (state !== undefined) {
|
||||||
@@ -104,32 +80,36 @@ export const computeStateDisplayFromEntityAttributes = (
|
|||||||
} else {
|
} else {
|
||||||
// If not trying to display an explicit state, create `Date` object from `stateObj`'s attributes then format.
|
// If not trying to display an explicit state, create `Date` object from `stateObj`'s attributes then format.
|
||||||
let date: Date;
|
let date: Date;
|
||||||
if (attributes.has_date && attributes.has_time) {
|
if (stateObj.attributes.has_date && stateObj.attributes.has_time) {
|
||||||
date = new Date(
|
date = new Date(
|
||||||
attributes.year,
|
stateObj.attributes.year,
|
||||||
attributes.month - 1,
|
stateObj.attributes.month - 1,
|
||||||
attributes.day,
|
stateObj.attributes.day,
|
||||||
attributes.hour,
|
stateObj.attributes.hour,
|
||||||
attributes.minute
|
stateObj.attributes.minute
|
||||||
);
|
);
|
||||||
return formatDateTime(date, locale);
|
return formatDateTime(date, locale);
|
||||||
}
|
}
|
||||||
if (attributes.has_date) {
|
if (stateObj.attributes.has_date) {
|
||||||
date = new Date(attributes.year, attributes.month - 1, attributes.day);
|
date = new Date(
|
||||||
|
stateObj.attributes.year,
|
||||||
|
stateObj.attributes.month - 1,
|
||||||
|
stateObj.attributes.day
|
||||||
|
);
|
||||||
return formatDate(date, locale);
|
return formatDate(date, locale);
|
||||||
}
|
}
|
||||||
if (attributes.has_time) {
|
if (stateObj.attributes.has_time) {
|
||||||
date = new Date();
|
date = new Date();
|
||||||
date.setHours(attributes.hour, attributes.minute);
|
date.setHours(stateObj.attributes.hour, stateObj.attributes.minute);
|
||||||
return formatTime(date, locale);
|
return formatTime(date, locale);
|
||||||
}
|
}
|
||||||
return state;
|
return stateObj.state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain === "humidifier") {
|
if (domain === "humidifier") {
|
||||||
if (state === "on" && attributes.humidity) {
|
if (compareState === "on" && stateObj.attributes.humidity) {
|
||||||
return `${attributes.humidity} %`;
|
return `${stateObj.attributes.humidity} %`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +119,7 @@ export const computeStateDisplayFromEntityAttributes = (
|
|||||||
domain === "number" ||
|
domain === "number" ||
|
||||||
domain === "input_number"
|
domain === "input_number"
|
||||||
) {
|
) {
|
||||||
return formatNumber(state, locale);
|
return formatNumber(compareState, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// state of button is a timestamp
|
// state of button is a timestamp
|
||||||
@@ -147,12 +127,12 @@ export const computeStateDisplayFromEntityAttributes = (
|
|||||||
domain === "button" ||
|
domain === "button" ||
|
||||||
domain === "input_button" ||
|
domain === "input_button" ||
|
||||||
domain === "scene" ||
|
domain === "scene" ||
|
||||||
(domain === "sensor" && attributes.device_class === "timestamp")
|
(domain === "sensor" && stateObj.attributes.device_class === "timestamp")
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return formatDateTime(new Date(state), locale);
|
return formatDateTime(new Date(compareState), locale);
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
return state;
|
return compareState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,28 +143,30 @@ export const computeStateDisplayFromEntityAttributes = (
|
|||||||
// When the latest version is skipped, show the latest version
|
// When the latest version is skipped, show the latest version
|
||||||
// When update is not available, show "Up-to-date"
|
// When update is not available, show "Up-to-date"
|
||||||
// When update is not available and there is no latest_version show "Unavailable"
|
// When update is not available and there is no latest_version show "Unavailable"
|
||||||
return state === "on"
|
return compareState === "on"
|
||||||
? updateIsInstallingFromAttributes(attributes)
|
? updateIsInstalling(stateObj as UpdateEntity)
|
||||||
? supportsFeatureFromAttributes(attributes, UPDATE_SUPPORT_PROGRESS)
|
? supportsFeature(stateObj, UPDATE_SUPPORT_PROGRESS)
|
||||||
? localize("ui.card.update.installing_with_progress", {
|
? localize("ui.card.update.installing_with_progress", {
|
||||||
progress: attributes.in_progress,
|
progress: stateObj.attributes.in_progress,
|
||||||
})
|
})
|
||||||
: localize("ui.card.update.installing")
|
: localize("ui.card.update.installing")
|
||||||
: attributes.latest_version
|
: stateObj.attributes.latest_version
|
||||||
: attributes.skipped_version === attributes.latest_version
|
: stateObj.attributes.skipped_version ===
|
||||||
? attributes.latest_version ?? localize("state.default.unavailable")
|
stateObj.attributes.latest_version
|
||||||
|
? stateObj.attributes.latest_version ??
|
||||||
|
localize("state.default.unavailable")
|
||||||
: localize("ui.card.update.up_to_date");
|
: localize("ui.card.update.up_to_date");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// Return device class translation
|
// Return device class translation
|
||||||
(attributes.device_class &&
|
(stateObj.attributes.device_class &&
|
||||||
localize(
|
localize(
|
||||||
`component.${domain}.state.${attributes.device_class}.${state}`
|
`component.${domain}.state.${stateObj.attributes.device_class}.${compareState}`
|
||||||
)) ||
|
)) ||
|
||||||
// Return default translation
|
// Return default translation
|
||||||
localize(`component.${domain}.state._.${state}`) ||
|
localize(`component.${domain}.state._.${compareState}`) ||
|
||||||
// We don't know! Return the raw state.
|
// We don't know! Return the raw state.
|
||||||
state
|
compareState
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -1,13 +1,7 @@
|
|||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { computeObjectId } from "./compute_object_id";
|
import { computeObjectId } from "./compute_object_id";
|
||||||
|
|
||||||
export const computeStateNameFromEntityAttributes = (
|
|
||||||
entityId: string,
|
|
||||||
attributes: { [key: string]: any }
|
|
||||||
): string =>
|
|
||||||
attributes.friendly_name === undefined
|
|
||||||
? computeObjectId(entityId).replace(/_/g, " ")
|
|
||||||
: attributes.friendly_name || "";
|
|
||||||
|
|
||||||
export const computeStateName = (stateObj: HassEntity): string =>
|
export const computeStateName = (stateObj: HassEntity): string =>
|
||||||
computeStateNameFromEntityAttributes(stateObj.entity_id, stateObj.attributes);
|
stateObj.attributes.friendly_name === undefined
|
||||||
|
? computeObjectId(stateObj.entity_id).replace(/_/g, " ")
|
||||||
|
: stateObj.attributes.friendly_name || "";
|
||||||
|
@@ -8,29 +8,29 @@ import {
|
|||||||
mdiCalendar,
|
mdiCalendar,
|
||||||
mdiCast,
|
mdiCast,
|
||||||
mdiCastConnected,
|
mdiCastConnected,
|
||||||
mdiCheckCircleOutline,
|
|
||||||
mdiClock,
|
mdiClock,
|
||||||
mdiCloseCircleOutline,
|
|
||||||
mdiGestureTapButton,
|
mdiGestureTapButton,
|
||||||
mdiLanConnect,
|
mdiLanConnect,
|
||||||
mdiLanDisconnect,
|
mdiLanDisconnect,
|
||||||
|
mdiLightSwitch,
|
||||||
mdiLock,
|
mdiLock,
|
||||||
mdiLockAlert,
|
mdiLockAlert,
|
||||||
mdiLockClock,
|
mdiLockClock,
|
||||||
mdiLockOpen,
|
mdiLockOpen,
|
||||||
mdiPackage,
|
|
||||||
mdiPackageDown,
|
|
||||||
mdiPackageUp,
|
mdiPackageUp,
|
||||||
mdiPowerPlug,
|
mdiPowerPlug,
|
||||||
mdiPowerPlugOff,
|
mdiPowerPlugOff,
|
||||||
mdiRestart,
|
mdiRestart,
|
||||||
mdiToggleSwitchVariant,
|
mdiToggleSwitch,
|
||||||
mdiToggleSwitchVariantOff,
|
mdiToggleSwitchOff,
|
||||||
|
mdiCheckCircleOutline,
|
||||||
|
mdiCloseCircleOutline,
|
||||||
mdiWeatherNight,
|
mdiWeatherNight,
|
||||||
|
mdiPackage,
|
||||||
|
mdiPackageDown,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { UpdateEntity, updateIsInstalling } from "../../data/update";
|
import { updateIsInstalling, UpdateEntity } from "../../data/update";
|
||||||
import { weatherIcon } from "../../data/weather";
|
|
||||||
/**
|
/**
|
||||||
* Return the icon to be used for a domain.
|
* Return the icon to be used for a domain.
|
||||||
*
|
*
|
||||||
@@ -47,20 +47,6 @@ export const domainIcon = (
|
|||||||
stateObj?: HassEntity,
|
stateObj?: HassEntity,
|
||||||
state?: string
|
state?: string
|
||||||
): string => {
|
): string => {
|
||||||
const icon = domainIconWithoutDefault(domain, stateObj, state);
|
|
||||||
if (icon) {
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line
|
|
||||||
console.warn(`Unable to find icon for domain ${domain}`);
|
|
||||||
return DEFAULT_DOMAIN_ICON;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const domainIconWithoutDefault = (
|
|
||||||
domain: string,
|
|
||||||
stateObj?: HassEntity,
|
|
||||||
state?: string
|
|
||||||
): string | undefined => {
|
|
||||||
const compareState = state !== undefined ? state : stateObj?.state;
|
const compareState = state !== undefined ? state : stateObj?.state;
|
||||||
|
|
||||||
switch (domain) {
|
switch (domain) {
|
||||||
@@ -102,15 +88,6 @@ export const domainIconWithoutDefault = (
|
|||||||
? mdiCheckCircleOutline
|
? mdiCheckCircleOutline
|
||||||
: mdiCloseCircleOutline;
|
: mdiCloseCircleOutline;
|
||||||
|
|
||||||
case "input_datetime":
|
|
||||||
if (!stateObj?.attributes.has_date) {
|
|
||||||
return mdiClock;
|
|
||||||
}
|
|
||||||
if (!stateObj.attributes.has_time) {
|
|
||||||
return mdiCalendar;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "lock":
|
case "lock":
|
||||||
switch (compareState) {
|
switch (compareState) {
|
||||||
case "unlocked":
|
case "unlocked":
|
||||||
@@ -132,11 +109,9 @@ export const domainIconWithoutDefault = (
|
|||||||
case "outlet":
|
case "outlet":
|
||||||
return compareState === "on" ? mdiPowerPlug : mdiPowerPlugOff;
|
return compareState === "on" ? mdiPowerPlug : mdiPowerPlugOff;
|
||||||
case "switch":
|
case "switch":
|
||||||
return compareState === "on"
|
return compareState === "on" ? mdiToggleSwitch : mdiToggleSwitchOff;
|
||||||
? mdiToggleSwitchVariant
|
|
||||||
: mdiToggleSwitchVariantOff;
|
|
||||||
default:
|
default:
|
||||||
return mdiToggleSwitchVariant;
|
return mdiLightSwitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "sensor": {
|
case "sensor": {
|
||||||
@@ -148,6 +123,15 @@ export const domainIconWithoutDefault = (
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "input_datetime":
|
||||||
|
if (!stateObj?.attributes.has_date) {
|
||||||
|
return mdiClock;
|
||||||
|
}
|
||||||
|
if (!stateObj.attributes.has_time) {
|
||||||
|
return mdiCalendar;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case "sun":
|
case "sun":
|
||||||
return stateObj?.state === "above_horizon"
|
return stateObj?.state === "above_horizon"
|
||||||
? FIXED_DOMAIN_ICONS[domain]
|
? FIXED_DOMAIN_ICONS[domain]
|
||||||
@@ -159,14 +143,13 @@ export const domainIconWithoutDefault = (
|
|||||||
? mdiPackageDown
|
? mdiPackageDown
|
||||||
: mdiPackageUp
|
: mdiPackageUp
|
||||||
: mdiPackage;
|
: mdiPackage;
|
||||||
|
|
||||||
case "weather":
|
|
||||||
return weatherIcon(stateObj?.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (domain in FIXED_DOMAIN_ICONS) {
|
if (domain in FIXED_DOMAIN_ICONS) {
|
||||||
return FIXED_DOMAIN_ICONS[domain];
|
return FIXED_DOMAIN_ICONS[domain];
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
// eslint-disable-next-line
|
||||||
|
console.warn(`Unable to find icon for domain ${domain}`);
|
||||||
|
return DEFAULT_DOMAIN_ICON;
|
||||||
};
|
};
|
||||||
|
@@ -3,13 +3,6 @@ import { HassEntity } from "home-assistant-js-websocket";
|
|||||||
export const supportsFeature = (
|
export const supportsFeature = (
|
||||||
stateObj: HassEntity,
|
stateObj: HassEntity,
|
||||||
feature: number
|
feature: number
|
||||||
): boolean => supportsFeatureFromAttributes(stateObj.attributes, feature);
|
|
||||||
|
|
||||||
export const supportsFeatureFromAttributes = (
|
|
||||||
attributes: {
|
|
||||||
[key: string]: any;
|
|
||||||
},
|
|
||||||
feature: number
|
|
||||||
): boolean =>
|
): boolean =>
|
||||||
// eslint-disable-next-line no-bitwise
|
// eslint-disable-next-line no-bitwise
|
||||||
(attributes.supported_features! & feature) !== 0;
|
(stateObj.attributes.supported_features! & feature) !== 0;
|
||||||
|
@@ -5,6 +5,6 @@ export const clamp = (value: number, min: number, max: number) =>
|
|||||||
export const conditionalClamp = (value: number, min?: number, max?: number) => {
|
export const conditionalClamp = (value: number, min?: number, max?: number) => {
|
||||||
let result: number;
|
let result: number;
|
||||||
result = min ? Math.max(value, min) : value;
|
result = min ? Math.max(value, min) : value;
|
||||||
result = max ? Math.min(result, max) : result;
|
result = max ? Math.min(value, max) : value;
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@@ -7,11 +7,8 @@ import { round } from "./round";
|
|||||||
* @param stateObj The entity state object
|
* @param stateObj The entity state object
|
||||||
*/
|
*/
|
||||||
export const isNumericState = (stateObj: HassEntity): boolean =>
|
export const isNumericState = (stateObj: HassEntity): boolean =>
|
||||||
isNumericFromAttributes(stateObj.attributes);
|
!!stateObj.attributes.unit_of_measurement ||
|
||||||
|
!!stateObj.attributes.state_class;
|
||||||
export const isNumericFromAttributes = (attributes: {
|
|
||||||
[key: string]: any;
|
|
||||||
}): boolean => !!attributes.unit_of_measurement || !!attributes.state_class;
|
|
||||||
|
|
||||||
export const numberFormatToLocale = (
|
export const numberFormatToLocale = (
|
||||||
localeOptions: FrontendLocaleData
|
localeOptions: FrontendLocaleData
|
||||||
|
@@ -7,7 +7,6 @@ export const iconColorCSS = css`
|
|||||||
ha-state-icon[data-domain="calendar"][data-state="on"],
|
ha-state-icon[data-domain="calendar"][data-state="on"],
|
||||||
ha-state-icon[data-domain="camera"][data-state="streaming"],
|
ha-state-icon[data-domain="camera"][data-state="streaming"],
|
||||||
ha-state-icon[data-domain="cover"][data-state="open"],
|
ha-state-icon[data-domain="cover"][data-state="open"],
|
||||||
ha-state-icon[data-domain="device_tracker"][data-state="home"],
|
|
||||||
ha-state-icon[data-domain="fan"][data-state="on"],
|
ha-state-icon[data-domain="fan"][data-state="on"],
|
||||||
ha-state-icon[data-domain="humidifier"][data-state="on"],
|
ha-state-icon[data-domain="humidifier"][data-state="on"],
|
||||||
ha-state-icon[data-domain="light"][data-state="on"],
|
ha-state-icon[data-domain="light"][data-state="on"],
|
||||||
@@ -70,9 +69,7 @@ export const iconColorCSS = css`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-state-icon[data-domain="plant"][data-state="problem"] {
|
ha-state-icon[data-domain="plant"][data-state="problem"],
|
||||||
color: var(--state-icon-error-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Color the icon if unavailable */
|
/* Color the icon if unavailable */
|
||||||
ha-state-icon[data-state="unavailable"] {
|
ha-state-icon[data-state="unavailable"] {
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import { LitElement } from "lit";
|
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
export function computeRTL(hass: HomeAssistant) {
|
export function computeRTL(hass: HomeAssistant) {
|
||||||
@@ -16,21 +15,3 @@ export function computeRTLDirection(hass: HomeAssistant) {
|
|||||||
export function emitRTLDirection(rtl: boolean) {
|
export function emitRTLDirection(rtl: boolean) {
|
||||||
return rtl ? "rtl" : "ltr";
|
return rtl ? "rtl" : "ltr";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function computeDirectionStyles(isRTL: boolean, element: LitElement) {
|
|
||||||
const direction: string = emitRTLDirection(isRTL);
|
|
||||||
setDirectionStyles(direction, element);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setDirectionStyles(direction: string, element: LitElement) {
|
|
||||||
element.style.direction = direction;
|
|
||||||
element.style.setProperty("--direction", direction);
|
|
||||||
element.style.setProperty(
|
|
||||||
"--float-start",
|
|
||||||
direction === "ltr" ? "left" : "right"
|
|
||||||
);
|
|
||||||
element.style.setProperty(
|
|
||||||
"--float-end",
|
|
||||||
direction === "ltr" ? "right" : "left"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
export const promiseTimeout = (ms: number, promise: Promise<any> | any) => {
|
export const promiseTimeout = (ms: number, promise: Promise<any>) => {
|
||||||
const timeout = new Promise((_resolve, reject) => {
|
const timeout = new Promise((_resolve, reject) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
reject(`Timed out in ${ms} ms.`);
|
reject(`Timed out in ${ms} ms.`);
|
||||||
|
@@ -1,18 +0,0 @@
|
|||||||
import { HomeAssistant } from "../../types";
|
|
||||||
|
|
||||||
export const subscribePollingCollection = (
|
|
||||||
hass: HomeAssistant,
|
|
||||||
updateData: (hass: HomeAssistant) => void,
|
|
||||||
interval: number
|
|
||||||
) => {
|
|
||||||
let timeout;
|
|
||||||
const fetchData = async () => {
|
|
||||||
try {
|
|
||||||
await updateData(hass);
|
|
||||||
} finally {
|
|
||||||
timeout = setTimeout(() => fetchData(), interval);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fetchData();
|
|
||||||
return () => clearTimeout(timeout);
|
|
||||||
};
|
|
@@ -13,7 +13,7 @@ export const throttle = <T extends any[]>(
|
|||||||
) => {
|
) => {
|
||||||
let timeout: number | undefined;
|
let timeout: number | undefined;
|
||||||
let previous = 0;
|
let previous = 0;
|
||||||
const throttledFunc = (...args: T): void => {
|
return (...args: T): void => {
|
||||||
const later = () => {
|
const later = () => {
|
||||||
previous = leading === false ? 0 : Date.now();
|
previous = leading === false ? 0 : Date.now();
|
||||||
timeout = undefined;
|
timeout = undefined;
|
||||||
@@ -35,10 +35,4 @@ export const throttle = <T extends any[]>(
|
|||||||
timeout = window.setTimeout(later, remaining);
|
timeout = window.setTimeout(later, remaining);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
throttledFunc.cancel = () => {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
timeout = undefined;
|
|
||||||
previous = 0;
|
|
||||||
};
|
|
||||||
return throttledFunc;
|
|
||||||
};
|
};
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
import { HomeAssistant } from "../../types";
|
|
||||||
|
|
||||||
interface ResultCache<T> {
|
|
||||||
[entityId: string]: Promise<T> | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call a function with result caching per entity.
|
|
||||||
* @param cacheKey key to store the cache on hass object
|
|
||||||
* @param cacheTime time to cache the results
|
|
||||||
* @param func function to fetch the data
|
|
||||||
* @param hass Home Assistant object
|
|
||||||
* @param entityId entity to fetch data for
|
|
||||||
* @param args extra arguments to pass to the function to fetch the data
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const timeCacheEntityPromiseFunc = async <T>(
|
|
||||||
cacheKey: string,
|
|
||||||
cacheTime: number,
|
|
||||||
func: (hass: HomeAssistant, entityId: string, ...args: any[]) => Promise<T>,
|
|
||||||
hass: HomeAssistant,
|
|
||||||
entityId: string,
|
|
||||||
...args: any[]
|
|
||||||
): Promise<T> => {
|
|
||||||
let cache: ResultCache<T> | undefined = (hass as any)[cacheKey];
|
|
||||||
|
|
||||||
if (!cache) {
|
|
||||||
cache = hass[cacheKey] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastResult = cache[entityId];
|
|
||||||
|
|
||||||
if (lastResult) {
|
|
||||||
return lastResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = func(hass, entityId, ...args);
|
|
||||||
cache[entityId] = result;
|
|
||||||
|
|
||||||
result.then(
|
|
||||||
// When successful, set timer to clear cache
|
|
||||||
() =>
|
|
||||||
setTimeout(() => {
|
|
||||||
cache![entityId] = undefined;
|
|
||||||
}, cacheTime),
|
|
||||||
// On failure, clear cache right away
|
|
||||||
() => {
|
|
||||||
cache![entityId] = undefined;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
@@ -1,80 +1,43 @@
|
|||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
interface CacheResult<T> {
|
interface ResultCache<T> {
|
||||||
result: T;
|
[entityId: string]: Promise<T> | undefined;
|
||||||
cacheKey: any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Caches a result of a promise for X time. Allows optional extra validation
|
|
||||||
* check to invalidate the cache.
|
|
||||||
* @param cacheKey the key to store the cache
|
|
||||||
* @param cacheTime the time to cache the result
|
|
||||||
* @param func the function to fetch the data
|
|
||||||
* @param generateCacheKey optional function to generate a cache key based on current hass + cached result. Cache is invalid if generates a different cache key.
|
|
||||||
* @param hass Home Assistant object
|
|
||||||
* @param args extra arguments to pass to the function to fetch the data
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const timeCachePromiseFunc = async <T>(
|
export const timeCachePromiseFunc = async <T>(
|
||||||
cacheKey: string,
|
cacheKey: string,
|
||||||
cacheTime: number,
|
cacheTime: number,
|
||||||
func: (hass: HomeAssistant, ...args: any[]) => Promise<T>,
|
func: (hass: HomeAssistant, entityId: string, ...args: any[]) => Promise<T>,
|
||||||
generateCacheKey:
|
|
||||||
| ((hass: HomeAssistant, lastResult: T) => unknown)
|
|
||||||
| undefined,
|
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
entityId: string,
|
||||||
...args: any[]
|
...args: any[]
|
||||||
): Promise<T> => {
|
): Promise<T> => {
|
||||||
const anyHass = hass as any;
|
let cache: ResultCache<T> | undefined = (hass as any)[cacheKey];
|
||||||
const lastResult: Promise<CacheResult<T>> | CacheResult<T> | undefined =
|
|
||||||
anyHass[cacheKey];
|
|
||||||
|
|
||||||
const checkCachedResult = (result: CacheResult<T>): T | Promise<T> => {
|
if (!cache) {
|
||||||
if (
|
cache = hass[cacheKey] = {};
|
||||||
!generateCacheKey ||
|
|
||||||
generateCacheKey(hass, result.result) === result.cacheKey
|
|
||||||
) {
|
|
||||||
return result.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
anyHass[cacheKey] = undefined;
|
|
||||||
return timeCachePromiseFunc(
|
|
||||||
cacheKey,
|
|
||||||
cacheTime,
|
|
||||||
func,
|
|
||||||
generateCacheKey,
|
|
||||||
hass,
|
|
||||||
...args
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// If we have a cached result, return it if it's still valid
|
|
||||||
if (lastResult) {
|
|
||||||
return lastResult instanceof Promise
|
|
||||||
? lastResult.then(checkCachedResult)
|
|
||||||
: checkCachedResult(lastResult);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const resultPromise = func(hass, ...args);
|
const lastResult = cache[entityId];
|
||||||
anyHass[cacheKey] = resultPromise;
|
|
||||||
|
|
||||||
resultPromise.then(
|
if (lastResult) {
|
||||||
|
return lastResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = func(hass, entityId, ...args);
|
||||||
|
cache[entityId] = result;
|
||||||
|
|
||||||
|
result.then(
|
||||||
// When successful, set timer to clear cache
|
// When successful, set timer to clear cache
|
||||||
(result) => {
|
() =>
|
||||||
anyHass[cacheKey] = {
|
|
||||||
result,
|
|
||||||
cacheKey: generateCacheKey?.(hass, result),
|
|
||||||
};
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
anyHass[cacheKey] = undefined;
|
cache![entityId] = undefined;
|
||||||
}, cacheTime);
|
}, cacheTime),
|
||||||
},
|
|
||||||
// On failure, clear cache right away
|
// On failure, clear cache right away
|
||||||
() => {
|
() => {
|
||||||
anyHass[cacheKey] = undefined;
|
cache![entityId] = undefined;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return resultPromise;
|
return result;
|
||||||
};
|
};
|
||||||
|
@@ -34,7 +34,7 @@ import {
|
|||||||
endOfMonth,
|
endOfMonth,
|
||||||
endOfQuarter,
|
endOfQuarter,
|
||||||
endOfYear,
|
endOfYear,
|
||||||
} from "date-fns/esm";
|
} from "date-fns";
|
||||||
import {
|
import {
|
||||||
formatDate,
|
formatDate,
|
||||||
formatDateMonth,
|
formatDateMonth,
|
||||||
|
@@ -11,8 +11,6 @@ import { classMap } from "lit/directives/class-map";
|
|||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { clamp } from "../../common/number/clamp";
|
import { clamp } from "../../common/number/clamp";
|
||||||
|
|
||||||
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
|
||||||
|
|
||||||
interface Tooltip extends TooltipModel<any> {
|
interface Tooltip extends TooltipModel<any> {
|
||||||
top: string;
|
top: string;
|
||||||
left: string;
|
left: string;
|
||||||
@@ -39,26 +37,6 @@ export default class HaChartBase extends LitElement {
|
|||||||
|
|
||||||
@state() private _hiddenDatasets: Set<number> = new Set();
|
@state() private _hiddenDatasets: Set<number> = new Set();
|
||||||
|
|
||||||
private _releaseCanvas() {
|
|
||||||
// release the canvas memory to prevent
|
|
||||||
// safari from running out of memory.
|
|
||||||
if (this.chart) {
|
|
||||||
this.chart.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public disconnectedCallback() {
|
|
||||||
this._releaseCanvas();
|
|
||||||
super.disconnectedCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
public connectedCallback() {
|
|
||||||
super.connectedCallback();
|
|
||||||
if (this.hasUpdated) {
|
|
||||||
this._setupChart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated() {
|
||||||
this._setupChart();
|
this._setupChart();
|
||||||
this.data.datasets.forEach((dataset, index) => {
|
this.data.datasets.forEach((dataset, index) => {
|
||||||
|
@@ -8,7 +8,7 @@ import {
|
|||||||
} from "../../common/number/format_number";
|
} from "../../common/number/format_number";
|
||||||
import { LineChartEntity, LineChartState } from "../../data/history";
|
import { LineChartEntity, LineChartState } from "../../data/history";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
import "./ha-chart-base";
|
||||||
|
|
||||||
const safeParseFloat = (value) => {
|
const safeParseFloat = (value) => {
|
||||||
const parsed = parseFloat(value);
|
const parsed = parseFloat(value);
|
||||||
@@ -28,13 +28,11 @@ class StateHistoryChartLine extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public isSingleDevice = false;
|
@property({ type: Boolean }) public isSingleDevice = false;
|
||||||
|
|
||||||
@property({ attribute: false }) public endTime!: Date;
|
@property({ attribute: false }) public endTime?: Date;
|
||||||
|
|
||||||
@state() private _chartData?: ChartData<"line">;
|
@state() private _chartData?: ChartData<"line">;
|
||||||
|
|
||||||
@state() private _chartOptions?: ChartOptions;
|
@state() private _chartOptions?: ChartOptions<"line">;
|
||||||
|
|
||||||
private _chartTime: Date = new Date();
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
@@ -59,7 +57,6 @@ class StateHistoryChartLine extends LitElement {
|
|||||||
locale: this.hass.locale,
|
locale: this.hass.locale,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
suggestedMax: this.endTime,
|
|
||||||
ticks: {
|
ticks: {
|
||||||
maxRotation: 0,
|
maxRotation: 0,
|
||||||
sampleSize: 5,
|
sampleSize: 5,
|
||||||
@@ -123,13 +120,7 @@ class StateHistoryChartLine extends LitElement {
|
|||||||
locale: numberFormatToLocale(this.hass.locale),
|
locale: numberFormatToLocale(this.hass.locale),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (
|
if (changedProps.has("data")) {
|
||||||
changedProps.has("data") ||
|
|
||||||
this._chartTime <
|
|
||||||
new Date(this.endTime.getTime() - MIN_TIME_BETWEEN_UPDATES)
|
|
||||||
) {
|
|
||||||
// If the line is more than 5 minutes old, re-gen it
|
|
||||||
// so the X axis grows even if there is no new data
|
|
||||||
this._generateData();
|
this._generateData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,12 +130,28 @@ class StateHistoryChartLine extends LitElement {
|
|||||||
const computedStyles = getComputedStyle(this);
|
const computedStyles = getComputedStyle(this);
|
||||||
const entityStates = this.data;
|
const entityStates = this.data;
|
||||||
const datasets: ChartDataset<"line">[] = [];
|
const datasets: ChartDataset<"line">[] = [];
|
||||||
|
let endTime: Date;
|
||||||
|
|
||||||
if (entityStates.length === 0) {
|
if (entityStates.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._chartTime = new Date();
|
endTime =
|
||||||
const endTime = this.endTime;
|
this.endTime ||
|
||||||
|
// Get the highest date from the last date of each device
|
||||||
|
new Date(
|
||||||
|
Math.max(
|
||||||
|
...entityStates.map((devSts) =>
|
||||||
|
new Date(
|
||||||
|
devSts.states[devSts.states.length - 1].last_changed
|
||||||
|
).getTime()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (endTime > new Date()) {
|
||||||
|
endTime = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
const names = this.names || {};
|
const names = this.names || {};
|
||||||
entityStates.forEach((states) => {
|
entityStates.forEach((states) => {
|
||||||
const domain = states.domain;
|
const domain = states.domain;
|
||||||
|
@@ -9,7 +9,7 @@ import { numberFormatToLocale } from "../../common/number/format_number";
|
|||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
import { TimelineEntity } from "../../data/history";
|
import { TimelineEntity } from "../../data/history";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { MIN_TIME_BETWEEN_UPDATES } from "./ha-chart-base";
|
import "./ha-chart-base";
|
||||||
import type { TimeLineData } from "./timeline-chart/const";
|
import type { TimeLineData } from "./timeline-chart/const";
|
||||||
|
|
||||||
/** Binary sensor device classes for which the static colors for on/off are NOT inverted.
|
/** Binary sensor device classes for which the static colors for on/off are NOT inverted.
|
||||||
@@ -83,8 +83,6 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public data: TimelineEntity[] = [];
|
@property({ attribute: false }) public data: TimelineEntity[] = [];
|
||||||
|
|
||||||
@property() public narrow!: boolean;
|
|
||||||
|
|
||||||
@property() public names: boolean | Record<string, string> = false;
|
@property() public names: boolean | Record<string, string> = false;
|
||||||
|
|
||||||
@property() public unit?: string;
|
@property() public unit?: string;
|
||||||
@@ -93,18 +91,12 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public isSingleDevice = false;
|
@property({ type: Boolean }) public isSingleDevice = false;
|
||||||
|
|
||||||
@property({ type: Boolean }) public chunked = false;
|
@property({ attribute: false }) public endTime?: Date;
|
||||||
|
|
||||||
@property({ attribute: false }) public startTime!: Date;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public endTime!: Date;
|
|
||||||
|
|
||||||
@state() private _chartData?: ChartData<"timeline">;
|
@state() private _chartData?: ChartData<"timeline">;
|
||||||
|
|
||||||
@state() private _chartOptions?: ChartOptions<"timeline">;
|
@state() private _chartOptions?: ChartOptions<"timeline">;
|
||||||
|
|
||||||
private _chartTime: Date = new Date();
|
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<ha-chart-base
|
<ha-chart-base
|
||||||
@@ -118,7 +110,6 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
|
|
||||||
public willUpdate(changedProps: PropertyValues) {
|
public willUpdate(changedProps: PropertyValues) {
|
||||||
if (!this.hasUpdated) {
|
if (!this.hasUpdated) {
|
||||||
const narrow = this.narrow;
|
|
||||||
this._chartOptions = {
|
this._chartOptions = {
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
parsing: false,
|
parsing: false,
|
||||||
@@ -132,8 +123,6 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
locale: this.hass.locale,
|
locale: this.hass.locale,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
suggestedMin: this.startTime,
|
|
||||||
suggestedMax: this.endTime,
|
|
||||||
ticks: {
|
ticks: {
|
||||||
autoSkip: true,
|
autoSkip: true,
|
||||||
maxRotation: 0,
|
maxRotation: 0,
|
||||||
@@ -164,18 +153,11 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
drawTicks: false,
|
drawTicks: false,
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
display:
|
display: this.data.length !== 1,
|
||||||
this.chunked || !this.isSingleDevice || this.data.length !== 1,
|
|
||||||
},
|
},
|
||||||
afterSetDimensions: (y) => {
|
afterSetDimensions: (y) => {
|
||||||
y.maxWidth = y.chart.width * 0.18;
|
y.maxWidth = y.chart.width * 0.18;
|
||||||
},
|
},
|
||||||
afterFit: (scaleInstance) => {
|
|
||||||
if (this.chunked) {
|
|
||||||
// ensure all the chart labels are the same width
|
|
||||||
scaleInstance.width = narrow ? 105 : 185;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
position: computeRTL(this.hass) ? "right" : "left",
|
position: computeRTL(this.hass) ? "right" : "left",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -213,13 +195,7 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
locale: numberFormatToLocale(this.hass.locale),
|
locale: numberFormatToLocale(this.hass.locale),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (
|
if (changedProps.has("data")) {
|
||||||
changedProps.has("data") ||
|
|
||||||
this._chartTime <
|
|
||||||
new Date(this.endTime.getTime() - MIN_TIME_BETWEEN_UPDATES)
|
|
||||||
) {
|
|
||||||
// If the line is more than 5 minutes old, re-gen it
|
|
||||||
// so the X axis grows even if there is no new data
|
|
||||||
this._generateData();
|
this._generateData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,9 +208,34 @@ export class StateHistoryChartTimeline extends LitElement {
|
|||||||
stateHistory = [];
|
stateHistory = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this._chartTime = new Date();
|
const startTime = new Date(
|
||||||
const startTime = this.startTime;
|
stateHistory.reduce(
|
||||||
const endTime = this.endTime;
|
(minTime, stateInfo) =>
|
||||||
|
Math.min(minTime, new Date(stateInfo.data[0].last_changed).getTime()),
|
||||||
|
new Date().getTime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// end time is Math.max(startTime, last_event)
|
||||||
|
let endTime =
|
||||||
|
this.endTime ||
|
||||||
|
new Date(
|
||||||
|
stateHistory.reduce(
|
||||||
|
(maxTime, stateInfo) =>
|
||||||
|
Math.max(
|
||||||
|
maxTime,
|
||||||
|
new Date(
|
||||||
|
stateInfo.data[stateInfo.data.length - 1].last_changed
|
||||||
|
).getTime()
|
||||||
|
),
|
||||||
|
startTime.getTime()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (endTime > new Date()) {
|
||||||
|
endTime = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
const labels: string[] = [];
|
const labels: string[] = [];
|
||||||
const datasets: ChartDataset<"timeline">[] = [];
|
const datasets: ChartDataset<"timeline">[] = [];
|
||||||
const names = this.names || {};
|
const names = this.names || {};
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import "@lit-labs/virtualizer";
|
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResultGroup,
|
CSSResultGroup,
|
||||||
@@ -7,29 +6,12 @@ import {
|
|||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state, eventOptions } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import {
|
import { HistoryResult } from "../../data/history";
|
||||||
HistoryResult,
|
|
||||||
LineChartUnit,
|
|
||||||
TimelineEntity,
|
|
||||||
} from "../../data/history";
|
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import "./state-history-chart-line";
|
import "./state-history-chart-line";
|
||||||
import "./state-history-chart-timeline";
|
import "./state-history-chart-timeline";
|
||||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
|
||||||
|
|
||||||
const CANVAS_TIMELINE_ROWS_CHUNK = 10; // Split up the canvases to avoid hitting the render limit
|
|
||||||
|
|
||||||
const chunkData = (inputArray: any[], chunks: number) =>
|
|
||||||
inputArray.reduce((results, item, idx) => {
|
|
||||||
const chunkIdx = Math.floor(idx / chunks);
|
|
||||||
if (!results[chunkIdx]) {
|
|
||||||
results[chunkIdx] = [];
|
|
||||||
}
|
|
||||||
results[chunkIdx].push(item);
|
|
||||||
return results;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
@customElement("state-history-charts")
|
@customElement("state-history-charts")
|
||||||
class StateHistoryCharts extends LitElement {
|
class StateHistoryCharts extends LitElement {
|
||||||
@@ -37,13 +19,8 @@ class StateHistoryCharts extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public historyData!: HistoryResult;
|
@property({ attribute: false }) public historyData!: HistoryResult;
|
||||||
|
|
||||||
@property() public narrow!: boolean;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public names = false;
|
@property({ type: Boolean }) public names = false;
|
||||||
|
|
||||||
@property({ type: Boolean, attribute: "virtualize", reflect: true })
|
|
||||||
public virtualize = false;
|
|
||||||
|
|
||||||
@property({ attribute: false }) public endTime?: Date;
|
@property({ attribute: false }) public endTime?: Date;
|
||||||
|
|
||||||
@property({ type: Boolean, attribute: "up-to-now" }) public upToNow = false;
|
@property({ type: Boolean, attribute: "up-to-now" }) public upToNow = false;
|
||||||
@@ -52,104 +29,59 @@ class StateHistoryCharts extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public isLoadingData = false;
|
@property({ type: Boolean }) public isLoadingData = false;
|
||||||
|
|
||||||
@state() private _computedStartTime!: Date;
|
|
||||||
|
|
||||||
@state() private _computedEndTime!: Date;
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
@restoreScroll(".container") private _savedScrollPos?: number;
|
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!isComponentLoaded(this.hass, "history")) {
|
if (!isComponentLoaded(this.hass, "history")) {
|
||||||
return html`<div class="info">
|
return html` <div class="info">
|
||||||
${this.hass.localize("ui.components.history_charts.history_disabled")}
|
${this.hass.localize("ui.components.history_charts.history_disabled")}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isLoadingData && !this.historyData) {
|
if (this.isLoadingData && !this.historyData) {
|
||||||
return html`<div class="info">
|
return html` <div class="info">
|
||||||
${this.hass.localize("ui.components.history_charts.loading_history")}
|
${this.hass.localize("ui.components.history_charts.loading_history")}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._isHistoryEmpty()) {
|
if (this._isHistoryEmpty()) {
|
||||||
return html`<div class="info">
|
return html` <div class="info">
|
||||||
${this.hass.localize("ui.components.history_charts.no_history_found")}
|
${this.hass.localize("ui.components.history_charts.no_history_found")}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const now = new Date();
|
const computedEndTime = this.upToNow
|
||||||
|
? new Date()
|
||||||
|
: this.endTime || new Date();
|
||||||
|
|
||||||
this._computedEndTime =
|
return html`
|
||||||
this.upToNow || !this.endTime || this.endTime > now ? now : this.endTime;
|
${this.historyData.timeline.length
|
||||||
|
? html`
|
||||||
this._computedStartTime = new Date(
|
<state-history-chart-timeline
|
||||||
this.historyData.timeline.reduce(
|
.hass=${this.hass}
|
||||||
(minTime, stateInfo) =>
|
.data=${this.historyData.timeline}
|
||||||
Math.min(minTime, new Date(stateInfo.data[0].last_changed).getTime()),
|
.endTime=${computedEndTime}
|
||||||
new Date().getTime()
|
.noSingle=${this.noSingle}
|
||||||
)
|
.names=${this.names}
|
||||||
);
|
></state-history-chart-timeline>
|
||||||
|
`
|
||||||
const combinedItems = this.historyData.timeline.length
|
: html``}
|
||||||
? (this.virtualize
|
${this.historyData.line.map(
|
||||||
? chunkData(this.historyData.timeline, CANVAS_TIMELINE_ROWS_CHUNK)
|
(line) => html`
|
||||||
: [this.historyData.timeline]
|
<state-history-chart-line
|
||||||
).concat(this.historyData.line)
|
.hass=${this.hass}
|
||||||
: this.historyData.line;
|
.unit=${line.unit}
|
||||||
|
.data=${line.data}
|
||||||
return this.virtualize
|
.identifier=${line.identifier}
|
||||||
? html`<div class="container ha-scrollbar" @scroll=${this._saveScrollPos}>
|
.isSingleDevice=${!this.noSingle &&
|
||||||
<lit-virtualizer
|
line.data &&
|
||||||
scroller
|
line.data.length === 1}
|
||||||
class="ha-scrollbar"
|
.endTime=${computedEndTime}
|
||||||
.items=${combinedItems}
|
.names=${this.names}
|
||||||
.renderItem=${this._renderHistoryItem}
|
></state-history-chart-line>
|
||||||
>
|
`
|
||||||
</lit-virtualizer>
|
)}
|
||||||
</div>`
|
`;
|
||||||
: html`${combinedItems.map((item, index) =>
|
|
||||||
this._renderHistoryItem(item, index)
|
|
||||||
)}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _renderHistoryItem = (
|
|
||||||
item: TimelineEntity[] | LineChartUnit,
|
|
||||||
index: number
|
|
||||||
): TemplateResult => {
|
|
||||||
if (!item || index === undefined) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
if (!Array.isArray(item)) {
|
|
||||||
return html`<div class="entry-container">
|
|
||||||
<state-history-chart-line
|
|
||||||
.hass=${this.hass}
|
|
||||||
.unit=${item.unit}
|
|
||||||
.data=${item.data}
|
|
||||||
.identifier=${item.identifier}
|
|
||||||
.isSingleDevice=${!this.noSingle &&
|
|
||||||
this.historyData.line?.length === 1}
|
|
||||||
.endTime=${this._computedEndTime}
|
|
||||||
.names=${this.names}
|
|
||||||
></state-history-chart-line>
|
|
||||||
</div> `;
|
|
||||||
}
|
|
||||||
return html`<div class="entry-container">
|
|
||||||
<state-history-chart-timeline
|
|
||||||
.hass=${this.hass}
|
|
||||||
.data=${item}
|
|
||||||
.startTime=${this._computedStartTime}
|
|
||||||
.endTime=${this._computedEndTime}
|
|
||||||
.isSingleDevice=${!this.noSingle &&
|
|
||||||
this.historyData.timeline?.length === 1}
|
|
||||||
.names=${this.names}
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
.chunked=${this.virtualize}
|
|
||||||
></state-history-chart-timeline>
|
|
||||||
</div> `;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||||
return !(changedProps.size === 1 && changedProps.has("hass"));
|
return !(changedProps.size === 1 && changedProps.has("hass"));
|
||||||
}
|
}
|
||||||
@@ -164,11 +96,6 @@ class StateHistoryCharts extends LitElement {
|
|||||||
return !this.isLoadingData && historyDataEmpty;
|
return !this.isLoadingData && historyDataEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@eventOptions({ passive: true })
|
|
||||||
private _saveScrollPos(e: Event) {
|
|
||||||
this._savedScrollPos = (e.target as HTMLDivElement).scrollTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
@@ -176,47 +103,11 @@ class StateHistoryCharts extends LitElement {
|
|||||||
/* height of single timeline chart = 60px */
|
/* height of single timeline chart = 60px */
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([virtualize]) {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 60px;
|
line-height: 60px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
.container {
|
|
||||||
max-height: var(--history-max-height);
|
|
||||||
}
|
|
||||||
|
|
||||||
.entry-container {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.entry-container:hover {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([virtualize]) .entry-container {
|
|
||||||
padding-left: 1px;
|
|
||||||
padding-right: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container,
|
|
||||||
lit-virtualizer {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
lit-virtualizer {
|
|
||||||
contain: size layout !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
state-history-chart-timeline,
|
|
||||||
state-history-chart-line {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -347,8 +347,8 @@ class StatisticsChart extends LitElement {
|
|||||||
statTypes.forEach((type) => {
|
statTypes.forEach((type) => {
|
||||||
let val: number | null;
|
let val: number | null;
|
||||||
if (type === "sum") {
|
if (type === "sum") {
|
||||||
if (initVal === null) {
|
if (!initVal) {
|
||||||
initVal = val = stat.state || 0;
|
initVal = val = stat.state;
|
||||||
prevSum = stat.sum;
|
prevSum = stat.sum;
|
||||||
} else {
|
} else {
|
||||||
val = initVal + ((stat.sum || 0) - prevSum!);
|
val = initVal + ((stat.sum || 0) - prevSum!);
|
||||||
|
@@ -1,167 +1,165 @@
|
|||||||
export const currencies = [
|
|
||||||
"AED",
|
|
||||||
"AFN",
|
|
||||||
"ALL",
|
|
||||||
"AMD",
|
|
||||||
"ANG",
|
|
||||||
"AOA",
|
|
||||||
"ARS",
|
|
||||||
"AUD",
|
|
||||||
"AWG",
|
|
||||||
"AZN",
|
|
||||||
"BAM",
|
|
||||||
"BBD",
|
|
||||||
"BDT",
|
|
||||||
"BGN",
|
|
||||||
"BHD",
|
|
||||||
"BIF",
|
|
||||||
"BMD",
|
|
||||||
"BND",
|
|
||||||
"BOB",
|
|
||||||
"BRL",
|
|
||||||
"BSD",
|
|
||||||
"BTN",
|
|
||||||
"BWP",
|
|
||||||
"BYN",
|
|
||||||
"BYR",
|
|
||||||
"BZD",
|
|
||||||
"CAD",
|
|
||||||
"CDF",
|
|
||||||
"CHF",
|
|
||||||
"CLP",
|
|
||||||
"CNY",
|
|
||||||
"COP",
|
|
||||||
"CRC",
|
|
||||||
"CUP",
|
|
||||||
"CVE",
|
|
||||||
"CZK",
|
|
||||||
"DJF",
|
|
||||||
"DKK",
|
|
||||||
"DOP",
|
|
||||||
"DZD",
|
|
||||||
"EGP",
|
|
||||||
"ERN",
|
|
||||||
"ETB",
|
|
||||||
"EUR",
|
|
||||||
"FJD",
|
|
||||||
"FKP",
|
|
||||||
"GBP",
|
|
||||||
"GEL",
|
|
||||||
"GHS",
|
|
||||||
"GIP",
|
|
||||||
"GMD",
|
|
||||||
"GNF",
|
|
||||||
"GTQ",
|
|
||||||
"GYD",
|
|
||||||
"HKD",
|
|
||||||
"HNL",
|
|
||||||
"HRK",
|
|
||||||
"HTG",
|
|
||||||
"HUF",
|
|
||||||
"IDR",
|
|
||||||
"ILS",
|
|
||||||
"INR",
|
|
||||||
"IQD",
|
|
||||||
"IRR",
|
|
||||||
"ISK",
|
|
||||||
"JMD",
|
|
||||||
"JOD",
|
|
||||||
"JPY",
|
|
||||||
"KES",
|
|
||||||
"KGS",
|
|
||||||
"KHR",
|
|
||||||
"KMF",
|
|
||||||
"KPW",
|
|
||||||
"KRW",
|
|
||||||
"KWD",
|
|
||||||
"KYD",
|
|
||||||
"KZT",
|
|
||||||
"LAK",
|
|
||||||
"LBP",
|
|
||||||
"LKR",
|
|
||||||
"LRD",
|
|
||||||
"LSL",
|
|
||||||
"LTL",
|
|
||||||
"LYD",
|
|
||||||
"MAD",
|
|
||||||
"MDL",
|
|
||||||
"MGA",
|
|
||||||
"MKD",
|
|
||||||
"MMK",
|
|
||||||
"MNT",
|
|
||||||
"MOP",
|
|
||||||
"MRO",
|
|
||||||
"MUR",
|
|
||||||
"MVR",
|
|
||||||
"MWK",
|
|
||||||
"MXN",
|
|
||||||
"MYR",
|
|
||||||
"MZN",
|
|
||||||
"NAD",
|
|
||||||
"NGN",
|
|
||||||
"NIO",
|
|
||||||
"NOK",
|
|
||||||
"NPR",
|
|
||||||
"NZD",
|
|
||||||
"OMR",
|
|
||||||
"PAB",
|
|
||||||
"PEN",
|
|
||||||
"PGK",
|
|
||||||
"PHP",
|
|
||||||
"PKR",
|
|
||||||
"PLN",
|
|
||||||
"PYG",
|
|
||||||
"QAR",
|
|
||||||
"RON",
|
|
||||||
"RSD",
|
|
||||||
"RUB",
|
|
||||||
"RWF",
|
|
||||||
"SAR",
|
|
||||||
"SBD",
|
|
||||||
"SCR",
|
|
||||||
"SDG",
|
|
||||||
"SEK",
|
|
||||||
"SGD",
|
|
||||||
"SHP",
|
|
||||||
"SLL",
|
|
||||||
"SOS",
|
|
||||||
"SRD",
|
|
||||||
"SSP",
|
|
||||||
"STD",
|
|
||||||
"SYP",
|
|
||||||
"SZL",
|
|
||||||
"THB",
|
|
||||||
"TJS",
|
|
||||||
"TMT",
|
|
||||||
"TND",
|
|
||||||
"TOP",
|
|
||||||
"TRY",
|
|
||||||
"TTD",
|
|
||||||
"TWD",
|
|
||||||
"TZS",
|
|
||||||
"UAH",
|
|
||||||
"UGX",
|
|
||||||
"USD",
|
|
||||||
"UYU",
|
|
||||||
"UZS",
|
|
||||||
"VEF",
|
|
||||||
"VND",
|
|
||||||
"VUV",
|
|
||||||
"WST",
|
|
||||||
"XAF",
|
|
||||||
"XCD",
|
|
||||||
"XOF",
|
|
||||||
"XPF",
|
|
||||||
"YER",
|
|
||||||
"ZAR",
|
|
||||||
"ZMK",
|
|
||||||
"ZWL",
|
|
||||||
];
|
|
||||||
|
|
||||||
export const createCurrencyListEl = () => {
|
export const createCurrencyListEl = () => {
|
||||||
const list = document.createElement("datalist");
|
const list = document.createElement("datalist");
|
||||||
list.id = "currencies";
|
list.id = "currencies";
|
||||||
for (const currency of currencies) {
|
for (const currency of [
|
||||||
|
"AED",
|
||||||
|
"AFN",
|
||||||
|
"ALL",
|
||||||
|
"AMD",
|
||||||
|
"ANG",
|
||||||
|
"AOA",
|
||||||
|
"ARS",
|
||||||
|
"AUD",
|
||||||
|
"AWG",
|
||||||
|
"AZN",
|
||||||
|
"BAM",
|
||||||
|
"BBD",
|
||||||
|
"BDT",
|
||||||
|
"BGN",
|
||||||
|
"BHD",
|
||||||
|
"BIF",
|
||||||
|
"BMD",
|
||||||
|
"BND",
|
||||||
|
"BOB",
|
||||||
|
"BRL",
|
||||||
|
"BSD",
|
||||||
|
"BTN",
|
||||||
|
"BWP",
|
||||||
|
"BYN",
|
||||||
|
"BYR",
|
||||||
|
"BZD",
|
||||||
|
"CAD",
|
||||||
|
"CDF",
|
||||||
|
"CHF",
|
||||||
|
"CLP",
|
||||||
|
"CNY",
|
||||||
|
"COP",
|
||||||
|
"CRC",
|
||||||
|
"CUP",
|
||||||
|
"CVE",
|
||||||
|
"CZK",
|
||||||
|
"DJF",
|
||||||
|
"DKK",
|
||||||
|
"DOP",
|
||||||
|
"DZD",
|
||||||
|
"EGP",
|
||||||
|
"ERN",
|
||||||
|
"ETB",
|
||||||
|
"EUR",
|
||||||
|
"FJD",
|
||||||
|
"FKP",
|
||||||
|
"GBP",
|
||||||
|
"GEL",
|
||||||
|
"GHS",
|
||||||
|
"GIP",
|
||||||
|
"GMD",
|
||||||
|
"GNF",
|
||||||
|
"GTQ",
|
||||||
|
"GYD",
|
||||||
|
"HKD",
|
||||||
|
"HNL",
|
||||||
|
"HRK",
|
||||||
|
"HTG",
|
||||||
|
"HUF",
|
||||||
|
"IDR",
|
||||||
|
"ILS",
|
||||||
|
"INR",
|
||||||
|
"IQD",
|
||||||
|
"IRR",
|
||||||
|
"ISK",
|
||||||
|
"JMD",
|
||||||
|
"JOD",
|
||||||
|
"JPY",
|
||||||
|
"KES",
|
||||||
|
"KGS",
|
||||||
|
"KHR",
|
||||||
|
"KMF",
|
||||||
|
"KPW",
|
||||||
|
"KRW",
|
||||||
|
"KWD",
|
||||||
|
"KYD",
|
||||||
|
"KZT",
|
||||||
|
"LAK",
|
||||||
|
"LBP",
|
||||||
|
"LKR",
|
||||||
|
"LRD",
|
||||||
|
"LSL",
|
||||||
|
"LTL",
|
||||||
|
"LYD",
|
||||||
|
"MAD",
|
||||||
|
"MDL",
|
||||||
|
"MGA",
|
||||||
|
"MKD",
|
||||||
|
"MMK",
|
||||||
|
"MNT",
|
||||||
|
"MOP",
|
||||||
|
"MRO",
|
||||||
|
"MUR",
|
||||||
|
"MVR",
|
||||||
|
"MWK",
|
||||||
|
"MXN",
|
||||||
|
"MYR",
|
||||||
|
"MZN",
|
||||||
|
"NAD",
|
||||||
|
"NGN",
|
||||||
|
"NIO",
|
||||||
|
"NOK",
|
||||||
|
"NPR",
|
||||||
|
"NZD",
|
||||||
|
"OMR",
|
||||||
|
"PAB",
|
||||||
|
"PEN",
|
||||||
|
"PGK",
|
||||||
|
"PHP",
|
||||||
|
"PKR",
|
||||||
|
"PLN",
|
||||||
|
"PYG",
|
||||||
|
"QAR",
|
||||||
|
"RON",
|
||||||
|
"RSD",
|
||||||
|
"RUB",
|
||||||
|
"RWF",
|
||||||
|
"SAR",
|
||||||
|
"SBD",
|
||||||
|
"SCR",
|
||||||
|
"SDG",
|
||||||
|
"SEK",
|
||||||
|
"SGD",
|
||||||
|
"SHP",
|
||||||
|
"SLL",
|
||||||
|
"SOS",
|
||||||
|
"SRD",
|
||||||
|
"SSP",
|
||||||
|
"STD",
|
||||||
|
"SYP",
|
||||||
|
"SZL",
|
||||||
|
"THB",
|
||||||
|
"TJS",
|
||||||
|
"TMT",
|
||||||
|
"TND",
|
||||||
|
"TOP",
|
||||||
|
"TRY",
|
||||||
|
"TTD",
|
||||||
|
"TWD",
|
||||||
|
"TZS",
|
||||||
|
"UAH",
|
||||||
|
"UGX",
|
||||||
|
"USD",
|
||||||
|
"UYU",
|
||||||
|
"UZS",
|
||||||
|
"VEF",
|
||||||
|
"VND",
|
||||||
|
"VUV",
|
||||||
|
"WST",
|
||||||
|
"XAF",
|
||||||
|
"XCD",
|
||||||
|
"XOF",
|
||||||
|
"XPF",
|
||||||
|
"YER",
|
||||||
|
"ZAR",
|
||||||
|
"ZMK",
|
||||||
|
"ZWL",
|
||||||
|
]) {
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.value = currency;
|
option.value = currency;
|
||||||
option.innerHTML = currency;
|
option.innerHTML = currency;
|
||||||
|
@@ -269,8 +269,8 @@ export class HaDataTable extends LitElement {
|
|||||||
@change=${this._handleHeaderRowCheckboxClick}
|
@change=${this._handleHeaderRowCheckboxClick}
|
||||||
.indeterminate=${this._checkedRows.length &&
|
.indeterminate=${this._checkedRows.length &&
|
||||||
this._checkedRows.length !== this._checkableRowsCount}
|
this._checkedRows.length !== this._checkableRowsCount}
|
||||||
.checked=${this._checkedRows.length &&
|
.checked=${this._checkedRows.length ===
|
||||||
this._checkedRows.length === this._checkableRowsCount}
|
this._checkableRowsCount}
|
||||||
>
|
>
|
||||||
</ha-checkbox>
|
</ha-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -5,7 +5,6 @@ import { fireEvent } from "../../common/dom/fire_event";
|
|||||||
import {
|
import {
|
||||||
DeviceAutomation,
|
DeviceAutomation,
|
||||||
deviceAutomationsEqual,
|
deviceAutomationsEqual,
|
||||||
sortDeviceAutomations,
|
|
||||||
} from "../../data/device_automation";
|
} from "../../data/device_automation";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "../ha-select";
|
import "../ha-select";
|
||||||
@@ -128,9 +127,7 @@ export abstract class HaDeviceAutomationPicker<
|
|||||||
|
|
||||||
private async _updateDeviceInfo() {
|
private async _updateDeviceInfo() {
|
||||||
this._automations = this.deviceId
|
this._automations = this.deviceId
|
||||||
? (await this._fetchDeviceAutomations(this.hass, this.deviceId)).sort(
|
? await this._fetchDeviceAutomations(this.hass, this.deviceId)
|
||||||
sortDeviceAutomations
|
|
||||||
)
|
|
||||||
: // No device, clear the list of automations
|
: // No device, clear the list of automations
|
||||||
[];
|
[];
|
||||||
|
|
||||||
@@ -164,9 +161,8 @@ export abstract class HaDeviceAutomationPicker<
|
|||||||
if (this.value && deviceAutomationsEqual(automation, this.value)) {
|
if (this.value && deviceAutomationsEqual(automation, this.value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const value = { ...automation };
|
fireEvent(this, "change");
|
||||||
delete value.metadata;
|
fireEvent(this, "value-changed", { value: automation });
|
||||||
fireEvent(this, "value-changed", { value });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
|
@@ -52,8 +52,6 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@property() public value?: string;
|
@property() public value?: string;
|
||||||
|
|
||||||
@property() public helper?: string;
|
|
||||||
|
|
||||||
@property() public devices?: DeviceRegistryEntry[];
|
@property() public devices?: DeviceRegistryEntry[];
|
||||||
|
|
||||||
@property() public areas?: AreaRegistryEntry[];
|
@property() public areas?: AreaRegistryEntry[];
|
||||||
@@ -88,8 +86,6 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@property({ type: Boolean }) public disabled?: boolean;
|
@property({ type: Boolean }) public disabled?: boolean;
|
||||||
|
|
||||||
@property({ type: Boolean }) public required?: boolean;
|
|
||||||
|
|
||||||
@state() private _opened?: boolean;
|
@state() private _opened?: boolean;
|
||||||
|
|
||||||
@query("ha-combo-box", true) public comboBox!: HaComboBox;
|
@query("ha-combo-box", true) public comboBox!: HaComboBox;
|
||||||
@@ -198,10 +194,9 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
this.hass,
|
this.hass,
|
||||||
deviceEntityLookup[device.id]
|
deviceEntityLookup[device.id]
|
||||||
),
|
),
|
||||||
area:
|
area: device.area_id
|
||||||
device.area_id && areaLookup[device.area_id]
|
? areaLookup[device.area_id].name
|
||||||
? areaLookup[device.area_id].name
|
: this.hass.localize("ui.components.device-picker.no_area"),
|
||||||
: this.hass.localize("ui.components.device-picker.no_area"),
|
|
||||||
}));
|
}));
|
||||||
if (!outputDevices.length) {
|
if (!outputDevices.length) {
|
||||||
return [
|
return [
|
||||||
@@ -272,10 +267,8 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
|
|||||||
? this.hass.localize("ui.components.device-picker.device")
|
? this.hass.localize("ui.components.device-picker.device")
|
||||||
: this.label}
|
: this.label}
|
||||||
.value=${this._value}
|
.value=${this._value}
|
||||||
.helper=${this.helper}
|
|
||||||
.renderer=${rowRenderer}
|
.renderer=${rowRenderer}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
.required=${this.required}
|
|
||||||
item-value-path="id"
|
item-value-path="id"
|
||||||
item-label-path="name"
|
item-label-path="name"
|
||||||
@opened-changed=${this._openedChanged}
|
@opened-changed=${this._openedChanged}
|
||||||
|
@@ -4,7 +4,6 @@ import { fireEvent } from "../../common/dom/fire_event";
|
|||||||
import { PolymerChangedEvent } from "../../polymer-types";
|
import { PolymerChangedEvent } from "../../polymer-types";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import "./ha-device-picker";
|
import "./ha-device-picker";
|
||||||
import type { HaDevicePickerDeviceFilterFunc } from "./ha-device-picker";
|
|
||||||
|
|
||||||
@customElement("ha-devices-picker")
|
@customElement("ha-devices-picker")
|
||||||
class HaDevicesPicker extends LitElement {
|
class HaDevicesPicker extends LitElement {
|
||||||
@@ -12,12 +11,6 @@ class HaDevicesPicker extends LitElement {
|
|||||||
|
|
||||||
@property() public value?: string[];
|
@property() public value?: string[];
|
||||||
|
|
||||||
@property() public helper?: string;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public disabled?: boolean;
|
|
||||||
|
|
||||||
@property({ type: Boolean }) public required?: boolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show entities from specific domains.
|
* Show entities from specific domains.
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@@ -42,8 +35,6 @@ class HaDevicesPicker extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: "pick-device-label" }) public pickDeviceLabel?: string;
|
@property({ attribute: "pick-device-label" }) public pickDeviceLabel?: string;
|
||||||
|
|
||||||
@property() public deviceFilter?: HaDevicePickerDeviceFilterFunc;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.hass) {
|
if (!this.hass) {
|
||||||
return html``;
|
return html``;
|
||||||
@@ -58,13 +49,11 @@ class HaDevicesPicker extends LitElement {
|
|||||||
allow-custom-entity
|
allow-custom-entity
|
||||||
.curValue=${entityId}
|
.curValue=${entityId}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.deviceFilter=${this.deviceFilter}
|
|
||||||
.includeDomains=${this.includeDomains}
|
.includeDomains=${this.includeDomains}
|
||||||
.excludeDomains=${this.excludeDomains}
|
.excludeDomains=${this.excludeDomains}
|
||||||
.includeDeviceClasses=${this.includeDeviceClasses}
|
.includeDeviceClasses=${this.includeDeviceClasses}
|
||||||
.value=${entityId}
|
.value=${entityId}
|
||||||
.label=${this.pickedDeviceLabel}
|
.label=${this.pickedDeviceLabel}
|
||||||
.disabled=${this.disabled}
|
|
||||||
@value-changed=${this._deviceChanged}
|
@value-changed=${this._deviceChanged}
|
||||||
></ha-device-picker>
|
></ha-device-picker>
|
||||||
</div>
|
</div>
|
||||||
@@ -72,16 +61,11 @@ class HaDevicesPicker extends LitElement {
|
|||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
<ha-device-picker
|
<ha-device-picker
|
||||||
allow-custom-entity
|
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.helper=${this.helper}
|
|
||||||
.deviceFilter=${this.deviceFilter}
|
|
||||||
.includeDomains=${this.includeDomains}
|
.includeDomains=${this.includeDomains}
|
||||||
.excludeDomains=${this.excludeDomains}
|
.excludeDomains=${this.excludeDomains}
|
||||||
.includeDeviceClasses=${this.includeDeviceClasses}
|
.includeDeviceClasses=${this.includeDeviceClasses}
|
||||||
.label=${this.pickDeviceLabel}
|
.label=${this.pickDeviceLabel}
|
||||||
.disabled=${this.disabled}
|
|
||||||
.required=${this.required && !currentDevices.length}
|
|
||||||
@value-changed=${this._addDevice}
|
@value-changed=${this._addDevice}
|
||||||
></ha-device-picker>
|
></ha-device-picker>
|
||||||
</div>
|
</div>
|
||||||
|