Compare commits

..

25 Commits

Author SHA1 Message Date
Zack Barett
d3765987c4 Merge branch 'dev' into Better-row-editor 2020-11-30 20:07:26 -06:00
Zack Arnett
259cb6a2d2 Review updates 2020-11-30 20:03:54 -06:00
Zack Arnett
68c3825c7b lint 2020-11-20 12:24:06 -06:00
Zack Arnett
2e3c3ded96 Update the rest of the editors 2020-11-20 12:19:08 -06:00
Zack Arnett
b6a93b439e more lint 2020-11-19 19:32:27 -06:00
Zack Arnett
ea4a5e4f1d lint 2020-11-19 19:24:27 -06:00
Zack Arnett
de71c348a5 Add More cards editors to config template 2020-11-19 19:20:31 -06:00
Zack Arnett
b27fe7e703 Clean up 2020-11-18 20:29:04 -06:00
Zack Arnett
06db9c4bee Show Code editor to only advanced 2020-11-18 20:16:53 -06:00
Zack Arnett
ae5018b501 lint 2020-11-18 18:13:56 -06:00
Zack Arnett
9ef3d02636 Fix header footer editor 2020-11-18 17:05:23 -06:00
Zack Arnett
9f2723deb8 fix enlarge 2020-11-18 17:02:02 -06:00
Zack Arnett
e9845e1009 Add row do something at least 2020-11-18 16:57:08 -06:00
Zack Arnett
40d8a76b4d Update Header footer 2020-11-17 22:23:41 -06:00
Zack Arnett
7a282ede59 Fix menu 2020-11-17 19:55:15 -06:00
Zack Arnett
48455c767f lint and wording 2020-11-17 17:22:01 -06:00
Zack Arnett
4385dd5c44 Convert entities card to advanced template 2020-11-17 17:17:30 -06:00
Zack Arnett
7733a5f831 Add handle back 2020-11-17 16:35:50 -06:00
Zack Arnett
b6c9676930 Lint 2020-11-17 16:18:21 -06:00
Zack Arnett
3692e36f12 use ha-settings-row 2020-11-17 16:13:15 -06:00
Zack Arnett
30e5fdb6e5 Advancded 2020-11-17 11:52:08 -06:00
Zack Arnett
99f4afec8d console.die 2020-11-15 16:25:30 -06:00
Zack Arnett
5ae6c05fe6 Update scrollbar on Dialog 2020-11-15 15:29:29 -06:00
Zack Arnett
effd5e4982 More changes 2020-11-15 14:50:00 -06:00
Zack Arnett
5592ea80a9 Make this place a little bit better 2020-11-14 20:46:14 -06:00
613 changed files with 9923 additions and 25922 deletions

View File

@@ -26,9 +26,6 @@
"[typescript]": { "[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"files.trimTrailingWhitespace": true "files.trimTrailingWhitespace": true
} }
} }

View File

@@ -74,12 +74,12 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
``` ```
## Problem-relevant frontend configuration ## Problem-relevant configuration
<!-- <!--
An example configuration that caused the problem for you, e.g. the YAML configuration An example configuration that caused the problem for you. Fill this out even
of the used cards. Fill this out even if it seems unimportant to you. Please be sure if it seems unimportant to you. Please be sure to remove personal information
to remove personal information like passwords, private URLs and other credentials. like passwords, private URLs and other credentials.
--> -->
```yaml ```yaml
@@ -89,7 +89,7 @@ DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed w
## Javascript errors shown in your browser console/inspector ## Javascript errors shown in your browser console/inspector
<!-- <!--
If you come across any Javascript or other error logs, e.g. in your browser If you come across any javascript or other error logs, e.g., in your browser
console/inspector please provide them. console/inspector please provide them.
--> -->

View File

@@ -1,19 +0,0 @@
name: Netlify
on:
schedule:
- cron: "0 0 * * *"
jobs:
trigger_builds:
name: Trigger netlify build preview
runs-on: "ubuntu-latest"
steps:
- name: Trigger Cast build
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_CAST_DEV_BUILD_HOOK }}
- name: Trigger Demo build
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_DEMO_DEV_BUILD_HOOK }}
- name: Trigger Gallery build
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_GALLERY_DEV_BUILD_HOOK }}

View File

@@ -1,81 +0,0 @@
name: Release
on:
release:
types:
- published
env:
WHEELS_TAG: 3.7-alpine3.11
PYTHON_VERSION: 3.7
NODE_VERSION: 12.1
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Verify version
uses: home-assistant/actions/helpers/verify-version@master
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v2
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
- name: Build and release package
run: |
python3 -m pip install twine
export TWINE_USERNAME="__token__"
export TWINE_PASSWORD="${{ secrets.TWINE_TOKEN }}"
script/release
wheels-init:
name: Init wheels build
needs: release
runs-on: ubuntu-latest
steps:
- name: Generate requirements.txt
run: |
# Sleep to give pypi time to populate the new version across mirrors
sleep 240
version=$(echo "${{ github.ref }}" | awk -F"/" '{print $NF}' )
echo "home-assistant-frontend==$version" > ./requirements.txt
- name: Upload requirements.txt
uses: actions/upload-artifact@v2
with:
name: requirements
path: ./requirements.txt
build-wheels:
name: Build wheels for ${{ matrix.arch }}
needs: wheels-init
runs-on: ubuntu-latest
strategy:
matrix:
arch: ["aarch64", "armhf", "armv7", "amd64", "i386"]
steps:
- name: Download requirements.txt
uses: actions/download-artifact@v2
with:
name: requirements
- name: Build wheels
uses: home-assistant/wheels@master
with:
tag: ${{ env.WHEELS_TAG }}
arch: ${{ matrix.arch }}
wheels-host: ${{ secrets.WHEELS_HOST }}
wheels-key: ${{ secrets.WHEELS_KEY }}
wheels-user: wheels
requirements: "requirements.txt"

View File

@@ -1,65 +0,0 @@
name: Translations
on:
schedule:
- cron: "30 0 * * *"
push:
branches:
- dev
paths:
- translations/en.json
env:
NODE_VERSION: 12
jobs:
upload:
name: Upload
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
- name: Upload Translations
run: |
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
./script/translations_upload_base
download:
name: Download
needs: upload
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2
- name: Set up Node ${{ env.NODE_VERSION }}
uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
- name: Download Translations
run: |
export LOKALISE_TOKEN="${{ secrets.LOKALISE_TOKEN }}"
npm install
./script/translations_download
- name: Initialize git
uses: home-assistant/actions/helpers/git-init@master
with:
name: GitHub Action
email: github-action@users.noreply.github.com
- name: Update translation
run: |
git add translations
git commit -am "Translation update"
git push

View File

@@ -14,7 +14,7 @@ This is the repository for the official [Home Assistant](https://home-assistant.
- Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/) - Development: [Instructions](https://developers.home-assistant.io/docs/frontend/development/)
- Production build: `script/build_frontend` - Production build: `script/build_frontend`
- Gallery: `cd gallery && script/develop_gallery` - Gallery: `cd gallery && script/develop_gallery`
- Supervisor: [Instructions](https://developers.home-assistant.io/docs/supervisor/developing) - Hass.io: [Instructions](https://developers.home-assistant.io/docs/en/hassio_hass.html)
## Frontend development ## Frontend development

View File

@@ -0,0 +1,30 @@
# https://dev.azure.com/home-assistant
trigger: none
pr: none
schedules:
- cron: "0 0 * * *"
displayName: "build preview"
branches:
include:
- dev
always: true
variables:
- group: netlify
jobs:
- job: 'Netlify_preview'
pool:
vmImage: 'ubuntu-latest'
steps:
- script: |
# Cast
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_CAST}
# Demo
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_DEMO}
# Gallery
curl -X POST -d {} https://api.netlify.com/build_hooks/${NETLIFY_GALLERY}
displayName: 'Trigger netlify build preview'

View File

@@ -0,0 +1,59 @@
# https://dev.azure.com/home-assistant
trigger:
batch: true
tags:
include:
- "*"
pr: none
variables:
- name: versionWheels
value: '1.10.1-3.7-alpine3.11'
- name: versionNode
value: '12.1'
- group: twine
resources:
repositories:
- repository: azure
type: github
name: 'home-assistant/ci-azure'
endpoint: 'home-assistant'
stages:
- stage: "Validate"
jobs:
- template: templates/azp-job-version.yaml@azure
- stage: "Build"
jobs:
- job: "ReleasePython"
pool:
vmImage: "ubuntu-latest"
steps:
- task: UsePythonVersion@0
displayName: "Use Python 3.7"
inputs:
versionSpec: "3.7"
- task: NodeTool@0
displayName: "Use Node $(versionNode)"
inputs:
versionSpec: "$(versionNode)"
- script: pip install twine wheel
displayName: "Install tools"
- script: |
export TWINE_USERNAME="$(twineUser)"
export TWINE_PASSWORD="$(twinePassword)"
script/release
displayName: "Build and release package"
- stage: "Wheels"
jobs:
- template: templates/azp-job-wheels.yaml@azure
parameters:
builderVersion: '$(versionWheels)'
wheelsRequirement: 'requirement.txt'
preBuild:
- script: |
sleep 240
echo "home-assistant-frontend==$(Build.SourceBranchName)" > requirement.txt

View File

@@ -0,0 +1,70 @@
# https://dev.azure.com/home-assistant
trigger:
batch: true
branches:
include:
- dev
paths:
include:
- translations/en.json
pr: none
schedules:
- cron: "30 0 * * *"
displayName: "frontend translation update"
branches:
include:
- dev
always: true
variables:
- group: translation
resources:
repositories:
- repository: azure
type: github
name: 'home-assistant/ci-azure'
endpoint: 'home-assistant'
jobs:
- job: 'Upload'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: '12.x'
- script: |
export LOKALISE_TOKEN="$(lokaliseToken)"
export AZURE_BRANCH="$(Build.SourceBranchName)"
./script/translations_upload_base
displayName: 'Upload Translation'
- job: 'Download'
dependsOn:
- 'Upload'
condition: or(eq(variables['Build.Reason'], 'Schedule'), eq(variables['Build.Reason'], 'Manual'))
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Use Node 12.x'
inputs:
versionSpec: '12.x'
- template: templates/azp-step-git-init.yaml@azure
- script: |
export LOKALISE_TOKEN="$(lokaliseToken)"
export AZURE_BRANCH="$(Build.SourceBranchName)"
npm install
./script/translations_download
displayName: 'Download Translation'
- script: |
git checkout dev
git add translation
git commit -am "[ci skip] Translation update"
git push
displayName: 'Update translation'

View File

@@ -36,7 +36,6 @@ const createWebpackConfig = ({
const ignorePackages = bundle.ignorePackages({ latestBuild }); const ignorePackages = bundle.ignorePackages({ latestBuild });
return { return {
mode: isProdBuild ? "production" : "development", mode: isProdBuild ? "production" : "development",
target: ["web", latestBuild ? "es2017" : "es5"],
devtool: isProdBuild devtool: isProdBuild
? "cheap-module-source-map" ? "cheap-module-source-map"
: "eval-cheap-module-source-map", : "eval-cheap-module-source-map",
@@ -132,6 +131,22 @@ const createWebpackConfig = ({
} }
return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`; return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
}, },
environment: {
// The environment supports arrow functions ('() => { ... }').
arrowFunction: latestBuild,
// The environment supports BigInt as literal (123n).
bigIntLiteral: false,
// The environment supports const and let for variable declarations.
const: latestBuild,
// The environment supports destructuring ('{ a, b } = obj').
destructuring: latestBuild,
// The environment supports an async import() function to import EcmaScript modules.
dynamicImport: latestBuild,
// The environment supports 'for of' iteration ('for (const x of array) { ... }').
forOf: latestBuild,
// The environment supports ECMAScript Module syntax to import ECMAScript modules (import ... from '...').
module: latestBuild,
},
chunkFilename: chunkFilename:
isProdBuild && !isStatsBuild isProdBuild && !isStatsBuild
? "chunk.[chunkhash].js" ? "chunk.[chunkhash].js"

View File

@@ -1,8 +1,8 @@
import { import {
customElement, customElement,
html, html,
internalProperty,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { mockHistory } from "../../../../demo/src/stubs/history"; import { mockHistory } from "../../../../demo/src/stubs/history";

View File

@@ -1,4 +1,4 @@
import "web-animations-js/web-animations-next-lite.min"; import "web-animations-js/web-animations-next-lite.min";
import "../../../src/resources/ha-style";
import "../../../src/resources/roboto"; import "../../../src/resources/roboto";
import "../../../src/resources/ha-style";
import "./layout/hc-lovelace"; import "./layout/hc-lovelace";

View File

@@ -54,8 +54,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
state: "21", state: "21",
attributes: { attributes: {
friendly_name: "Living room temperature", friendly_name: "Living room temperature",
device_class: "temperature",
unit_of_measurement: "°C",
}, },
}, },
"sensor.study_temp_rounded": { "sensor.study_temp_rounded": {
@@ -63,8 +61,6 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
state: "23", state: "23",
attributes: { attributes: {
friendly_name: "Study temperature", friendly_name: "Study temperature",
device_class: "temperature",
unit_of_measurement: "°C",
}, },
}, },
"sensor.living_room": { "sensor.living_room": {
@@ -265,7 +261,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
entity_id: "light.kitchen_lights", entity_id: "light.kitchen_lights",
state: "off", state: "off",
attributes: { attributes: {
friendly_name: "Kitchen Lights", friendly_name: "Kitchen lights",
supported_features: 1, supported_features: 1,
}, },
}, },
@@ -488,7 +484,7 @@ export const demoEntitiesArsaboo: DemoConfig["entities"] = (localize) =>
attributes: { attributes: {
min_mireds: 111, min_mireds: 111,
max_mireds: 400, max_mireds: 400,
friendly_name: "Garage Lights", friendly_name: "Garage lights",
supported_features: 55, supported_features: 55,
}, },
}, },

View File

@@ -12,7 +12,6 @@ export const demoLovelaceArsaboo: DemoConfig["lovelace"] = (localize) => ({
{ {
type: "entities", type: "entities",
title: localize("ui.panel.page-demo.config.arsaboo.labels.lights"), title: localize("ui.panel.page-demo.config.arsaboo.labels.lights"),
state_color: true,
entities: [ entities: [
{ {
entity: "light.kitchen_lights", entity: "light.kitchen_lights",

View File

@@ -653,7 +653,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
entity_id: "binary_sensor.smoke_sensor_158d0001b8ddc7", entity_id: "binary_sensor.smoke_sensor_158d0001b8ddc7",
state: "off", state: "off",
attributes: { attributes: {
density: 0, Density: 0,
battery_level: 59, battery_level: 59,
friendly_name: "Downstairs Smoke Detector", friendly_name: "Downstairs Smoke Detector",
device_class: "smoke", device_class: "smoke",
@@ -663,7 +663,7 @@ export const demoEntitiesJimpower: DemoConfig["entities"] = () =>
entity_id: "binary_sensor.smoke_sensor_158d0001b8deba", entity_id: "binary_sensor.smoke_sensor_158d0001b8deba",
state: "off", state: "off",
attributes: { attributes: {
density: 0, Density: 0,
battery_level: 65, battery_level: 65,
friendly_name: "Upstairs Smoke Detector", friendly_name: "Upstairs Smoke Detector",
device_class: "smoke", device_class: "smoke",

View File

@@ -3,7 +3,49 @@ import { DemoConfig } from "../types";
export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
name: "Kingia Castle", name: "Kingia Castle",
resources: [], resources: [
// {
// url: "/local/custom_ui/dark-sky-weather-card.js?v=4",
// type: "js",
// },
// {
// url: "/local/custom_ui/mini-media-player-bundle.js?v=0.9.8",
// type: "module",
// },
// {
// url: "/local/custom_ui/tracker-card.js?v=0.1.5",
// type: "js",
// },
// {
// url: "/local/custom_ui/surveillance-card.js?v=0.0.1",
// type: "module",
// },
// {
// url: "/local/custom_ui/mini-graph-card-bundle.js?v=0.1.0",
// type: "module",
// },
// {
// url: "/local/custom_ui/slider-entity-row.js?v=d6da75",
// type: "js",
// },
// {
// url:
// "/local/custom_ui/compact-custom-header/compact-custom-header.js?v=0.2.7",
// type: "js",
// },
// {
// url: "/local/custom_ui/waze-card.js?v=1.1.1",
// type: "js",
// },
// {
// url: "/local/custom_ui/circle-sensor-card.js?v=1.2.0",
// type: "module",
// },
// {
// url: "/local/custom_ui/monster-card.js?v=0.2.3",
// type: "js",
// },
],
views: [ views: [
{ {
cards: [ cards: [
@@ -561,6 +603,89 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
}, },
{ {
cards: [ cards: [
// {
// style: {
// "background-image": 'url("/assets/jimpower/cardbackK.png")',
// "background-size": "100% 400px",
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// "background-repeat": "no-repeat",
// color: "#999999",
// "border-radius": "20px",
// border: "solid 1px rgba(100,100,100,0.3)",
// "background-color": "rgba(50,50,50,0.3)",
// },
// type: "custom:card-modder",
// card: {
// entity_visibility: "sensor.dark_sky_visibility",
// entity_sun: "sun.sun",
// entity_daily_summary:
// "sensor.bom_gc_forecast_detailed_summary_0",
// entity_temperature: "sensor.bom_temp",
// entity_forecast_high_temp_3:
// "sensor.bom_gc_forecast_max_temp_c_3",
// entity_forecast_high_temp_2:
// "sensor.bom_gc_forecast_max_temp_c_2",
// entity_forecast_high_temp_5:
// "sensor.bom_gc_forecast_max_temp_c_5",
// entity_forecast_high_temp_4:
// "sensor.bom_gc_forecast_max_temp_c_4",
// entity_wind_speed: "sensor.bom_wind_sp",
// entity_forecast_icon_4: "sensor.dark_sky_icon_4",
// entity_forecast_icon_5: "sensor.dark_sky_icon_5",
// entity_forecast_icon_2: "sensor.dark_sky_icon_2",
// entity_forecast_icon_3: "sensor.dark_sky_icon_3",
// entity_forecast_icon_1: "sensor.dark_sky_icon_1",
// entity_forecast_high_temp_1:
// "sensor.bom_gc_forecast_max_temp_c_1",
// entity_wind_bearing: "sensor.bom_wind_bear",
// entity_forecast_low_temp_2:
// "sensor.bom_gc_forecast_min_temp_c_2",
// entity_forecast_low_temp_3:
// "sensor.bom_gc_forecast_min_temp_c_3",
// entity_pressure: "sensor.bom_pres",
// entity_forecast_low_temp_1:
// "sensor.bom_gc_forecast_min_temp_c_1",
// entity_forecast_low_temp_4:
// "sensor.bom_gc_forecast_min_temp_c_4",
// entity_forecast_low_temp_5:
// "sensor.bom_gc_forecast_min_temp_c_5",
// entity_humidity: "sensor.bom_humd",
// type: "custom:dark-sky-weather-card",
// entity_current_conditions: "sensor.dark_sky_icon",
// },
// },
// {
// style: {
// "background-image": 'url("/assets/jimpower/home/waze_5.png")',
// "background-size": "100% 400px",
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// "background-repeat": "no-repeat",
// "border-radius": "20px",
// border: "solid 1px rgba(100,100,100,0.3)",
// "background-color": "rgba(50,50,50,0.3)",
// },
// type: "custom:card-modder",
// card: {
// entities: [
// {
// name: "James",
// zone: "zone.home",
// entity: "sensor.james_to_home",
// },
// {
// name: "Tina",
// zone: "zone.home",
// entity: "sensor.tina_to_home",
// },
// {
// name: "Work",
// zone: "zone.powertec",
// entity: "sensor.commute_to_work",
// },
// ],
// type: "custom:waze-card",
// },
// },
{ {
style: { style: {
"border-radius": "20px", "border-radius": "20px",
@@ -597,8 +722,46 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
], ],
type: "vertical-stack", type: "vertical-stack",
}, },
// {
// cards: [
// {
// style: {
// "border-radius": "20px",
// color: "#999999",
// "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// border: "solid 1px rgba(100,100,100,0.3)",
// },
// type: "custom:card-modder",
// card: {
// type: "picture-entity",
// entity: "camera.bom_radar",
// },
// },
// // {
// // style: {
// // "background-image": 'url("/assets/jimpower/cardbackK.png")',
// // "background-size": "100% 525px",
// // "box-shadow": "3px 3px rgba(0,0,0,0.4)",
// // "background-repeat": "no-repeat",
// // color: "#999999",
// // "border-radius": "20px",
// // border: "solid 1px rgba(100,100,100,0.3)",
// // "background-color": "rgba(50,50,50,0.3)",
// // },
// // type: "custom:card-modder",
// // card: {
// // title: null,
// // type: "custom:tracker-card",
// // trackers: [
// // "sensor.custom_card_tracker",
// // "sensor.custom_component_tracker",
// // ],
// // },
// // },
// ],
// type: "vertical-stack",
// },
], ],
path: "home",
icon: "mdi:castle", icon: "mdi:castle",
name: "Home", name: "Home",
background: background:
@@ -718,13 +881,26 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
card: { card: {
image: "/assets/jimpower/security/air_8.jpg", image: "/assets/jimpower/security/air_8.jpg",
elements: [ elements: [
{
image:
"https://www.airvisual.com/assets/aqi/ic-face-1-green.svg",
type: "image",
style: {
width: "80px",
top: "30%",
left: "12%",
transform: "none",
height: "80px",
},
entity: "sensor.us_air_pollution_level_2",
},
{ {
style: { style: {
color: "hsl(120, 41%, 39%)", color: "hsl(120, 41%, 39%)",
top: "50%", top: "50%",
"font-weight": 600, "font-weight": 600,
"font-size": "50px", "font-size": "20px",
left: "30%", left: "44%",
}, },
type: "state-label", type: "state-label",
entity: "sensor.us_air_pollution_level_2", entity: "sensor.us_air_pollution_level_2",
@@ -744,7 +920,7 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
style: { style: {
color: "white", color: "white",
top: "80%", top: "80%",
left: "48%", left: "52%",
}, },
type: "state-icon", type: "state-icon",
entity: "sensor.us_main_pollutant_2", entity: "sensor.us_main_pollutant_2",
@@ -1235,7 +1411,6 @@ export const demoLovelaceJimpower: DemoConfig["lovelace"] = () => ({
type: "vertical-stack", type: "vertical-stack",
}, },
], ],
path: "security",
icon: "hass:shield-home", icon: "hass:shield-home",
name: "Security", name: "Security",
background: background:

View File

@@ -101,12 +101,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
"sensor.zwave_battery_front_door": { "sensor.zwave_battery_front_door": {
entity_id: "sensor.zwave_battery_front_door", entity_id: "sensor.zwave_battery_front_door",
state: "63", state: "63",
attributes: { attributes: { friendly_name: "Battery", icon: "mdi:battery-60" },
friendly_name: "Battery",
icon: "mdi:battery-60",
unit_of_measurement: "%",
device_class: "battery",
},
}, },
"sensor.oskar_devices": { "sensor.oskar_devices": {
entity_id: "sensor.oskar_devices", entity_id: "sensor.oskar_devices",
@@ -169,7 +164,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
}, },
"input_select.christmas_pattern": { "input_select.christmas_pattern": {
entity_id: "input_select.christmas_pattern", entity_id: "input_select.christmas_pattern",
state: "Rainbow", state: "None",
attributes: { attributes: {
options: [ options: [
"None", "None",
@@ -191,7 +186,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
}, },
"input_select.christmas_palette": { "input_select.christmas_palette": {
entity_id: "input_select.christmas_palette", entity_id: "input_select.christmas_palette",
state: "Party", state: "None",
attributes: { attributes: {
options: [ options: [
"None", "None",
@@ -462,7 +457,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
state: "0.0", state: "0.0",
attributes: { attributes: {
unit_of_measurement: "kB/s", unit_of_measurement: "kB/s",
friendly_name: "Downloading", friendly_name: "Nedladdning",
icon: "mdi:file-download", icon: "mdi:file-download",
}, },
}, },
@@ -476,7 +471,7 @@ export const demoEntitiesKernehed: DemoConfig["entities"] = () =>
state: "0.0", state: "0.0",
attributes: { attributes: {
unit_of_measurement: "kB/s", unit_of_measurement: "kB/s",
friendly_name: "Uploading", friendly_name: "Uppladdning",
icon: "mdi:file-upload", icon: "mdi:file-upload",
}, },
}, },

View File

@@ -2,7 +2,44 @@ import { DemoConfig } from "../types";
export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
name: "Hem", name: "Hem",
resources: [], resources: [
// {
// url: "/local/custom-lovelace/monster-card.js",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/mini-media-player-bundle.js?v=0.9.8",
// type: "module",
// },
// {
// url: "/local/custom-lovelace/slideshow-card.js?=1.1.0",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/fold-entity-row.js?v=3ae2c4",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/swipe-card/swipe-card.js?v=2.0.0",
// type: "module",
// },
// {
// url: "/local/custom-lovelace/upcoming-media-card/upcoming-media-card.js",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/tracker-card.js?v=0.1.5",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/card-tools.js?v=6ce5d0",
// type: "js",
// },
// {
// url: "/local/custom-lovelace/krisinfo.js?=0.0.1",
// type: "js",
// },
],
views: [ views: [
{ {
cards: [ cards: [
@@ -27,7 +64,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
style: { style: {
color: "white", color: "white",
top: "93%", top: "93%",
left: "85%", left: "90%",
}, },
type: "state-label", type: "state-label",
entity: "sensor.battery_oskar", entity: "sensor.battery_oskar",
@@ -50,7 +87,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
{ {
style: { style: {
color: "white", color: "white",
top: "93%", top: "92%",
left: "20%", left: "20%",
}, },
type: "state-label", type: "state-label",
@@ -59,8 +96,8 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
{ {
style: { style: {
color: "white", color: "white",
top: "93%", top: "92%",
left: "85%", left: "90%",
}, },
type: "state-label", type: "state-label",
entity: "sensor.battery_bella", entity: "sensor.battery_bella",
@@ -68,7 +105,7 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
{ {
style: { style: {
color: "white", color: "white",
top: "93%", top: "92%",
left: "55%", left: "55%",
}, },
type: "state-label", type: "state-label",
@@ -94,6 +131,78 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
type: "entities", type: "entities",
title: "Lock", title: "Lock",
}, },
// {
// filter: {
// exclude: [
// {
// state: "not_home",
// },
// ],
// include: [
// {
// entity_id: "device_tracker.annasiphone",
// },
// {
// entity_id: "device_tracker.iphone_2",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "G\u00e4ster",
// },
// show_empty: false,
// },
// {
// filter: {
// exclude: [
// {
// state: "Inget",
// },
// {
// state: "i.u.",
// },
// ],
// include: [
// {
// entity_id: "sensor.pollen_al",
// },
// {
// entity_id: "sensor.pollen_alm",
// },
// {
// entity_id: "sensor.pollen_salg_vide",
// },
// {
// entity_id: "sensor.pollen_bjork",
// },
// {
// entity_id: "sensor.pollen_bok",
// },
// {
// entity_id: "sensor.pollen_ek",
// },
// {
// entity_id: "sensor.pollen_grabo",
// },
// {
// entity_id: "sensor.pollen_gras",
// },
// {
// entity_id: "sensor.pollen_hassel",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "Pollenniv\u00e5er",
// },
// show_empty: false,
// },
{ {
cards: [ cards: [
{ {
@@ -117,6 +226,10 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
], ],
type: "vertical-stack", type: "vertical-stack",
}, },
// {
// url: "https://embed.windy.com/embed2.html",
// type: "iframe",
// },
{ {
entities: [ entities: [
{ {
@@ -150,7 +263,6 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
], ],
type: "glance", type: "glance",
show_state: false, show_state: false,
columns: 4,
}, },
{ {
entities: ["sensor.oskar_bluetooth"], entities: ["sensor.oskar_bluetooth"],
@@ -158,6 +270,32 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
type: "entities", type: "entities",
title: "Occupancy", title: "Occupancy",
}, },
// {
// filter: {
// exclude: [
// {
// state: false,
// },
// ],
// include: [
// {
// entity_id:
// "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_2",
// },
// {
// entity_id:
// "binary_sensor.fibaro_system_unknown_type0c02_id1003_sensor_3",
// },
// ],
// },
// type: "custom:monster-card",
// card: {
// show_header_toggle: false,
// type: "entities",
// title: "Brandvarnare",
// },
// show_empty: false,
// },
{ {
type: "weather-forecast", type: "weather-forecast",
entity: "weather.smhi_vader", entity: "weather.smhi_vader",
@@ -240,9 +378,41 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
"binary_sensor.windows_server", "binary_sensor.windows_server",
"binary_sensor.teamspeak", "binary_sensor.teamspeak",
"binary_sensor.harmony_hub", "binary_sensor.harmony_hub",
// {
// style: {
// height: "1px",
// width: "85%",
// "margin-left": "auto",
// background: "#62717b",
// "margin-right": "auto",
// },
// type: "divider",
// },
// {
// items: ["sensor.uptime_router", "sensor.installerad_routeros"],
// head: {
// entity: "binary_sensor.router",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "mdi:router",
// },
// },
// {
// items: [
// "sensor.uptime_router_server",
// "sensor.installerad_routeros_server",
// ],
// head: {
// entity: "binary_sensor.router_server",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "mdi:router",
// },
// },
], ],
show_header_toggle: false, show_header_toggle: false,
state_color: true,
type: "entities", type: "entities",
title: "Network", title: "Network",
}, },
@@ -252,10 +422,29 @@ export const demoLovelaceKernehed: DemoConfig["lovelace"] = () => ({
"binary_sensor.ubiquiti_switch", "binary_sensor.ubiquiti_switch",
"binary_sensor.ubiquiti_nvr", "binary_sensor.ubiquiti_nvr",
"binary_sensor.entre_kamera", "binary_sensor.entre_kamera",
// {
// items: ["sensor.uptime_ap_1"],
// head: {
// entity: "binary_sensor.accesspunkt_1",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "router-wireless",
// },
// },
// {
// items: ["sensor.uptime_ap_2"],
// head: {
// entity: "binary_sensor.accesspunkt_2",
// },
// type: "custom:fold-entity-row",
// group_config: {
// icon: "router-wireless",
// },
// },
"sensor.total_clients_wireless", "sensor.total_clients_wireless",
], ],
show_header_toggle: false, show_header_toggle: false,
state_color: true,
type: "entities", type: "entities",
title: "Ubiquiti", title: "Ubiquiti",
}, },

View File

@@ -215,7 +215,6 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
card: { card: {
type: "glance", type: "glance",
show_state: false, show_state: false,
columns: 4,
}, },
state_filter: ["on"], state_filter: ["on"],
}, },
@@ -809,6 +808,67 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
], ],
type: "vertical-stack", type: "vertical-stack",
}, },
// {
// cards: [
// {
// entities: [
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Bedside",
// entity: "light.bedside_lamp",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Bedroom",
// entity: "light.bedroom_ceiling_light",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Isa",
// entity: "light.isa_ceiling_light",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Upstairs hallway",
// entity: "light.upstairs_hallway_ceiling_light_level",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Nightlight",
// entity: "light.gateway_light_34ce008bfc4b",
// },
// {
// hide_when_off: true,
// toggle: true,
// type: "custom:slider-entity-row",
// name: "Walk in closet",
// entity: "light.walk_in_closet_lights",
// },
// {
// hide_when_off: true,
// toggle: false,
// type: "custom:slider-entity-row",
// name: "Stefan",
// entity: "light.stefan_lightstrip",
// },
// ],
// show_header_toggle: false,
// type: "entities",
// title: "Upstairs",
// },
// ],
// type: "vertical-stack",
// },
], ],
path: "lights", path: "lights",
title: "Lights", title: "Lights",
@@ -858,6 +918,10 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
name: "Dafang", name: "Dafang",
icon: "mdi:webcam", icon: "mdi:webcam",
}, },
{
name: "IR Hallway",
entity: "sensor.system_ir_blaster",
},
{ {
name: "IR Bedroom", name: "IR Bedroom",
entity: "sensor.system_ir_blaster_bedroom", entity: "sensor.system_ir_blaster_bedroom",
@@ -876,7 +940,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
"sensor.system_ring_chime", "sensor.system_ring_chime",
], ],
type: "glance", type: "glance",
columns: 4, columns: 5,
show_state: false, show_state: false,
}, },
{ {

View File

@@ -3,8 +3,8 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { CastManager } from "../../../src/cast/cast_manager"; import { CastManager } from "../../../src/cast/cast_manager";

View File

@@ -3,9 +3,9 @@ import {
css, css,
CSSResult, CSSResult,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { until } from "lit-html/directives/until"; import { until } from "lit-html/directives/until";

View File

@@ -1,8 +1,8 @@
import "../../src/resources/safari-14-attachshadow-patch";
import "@polymer/polymer/lib/elements/dom-if"; import "@polymer/polymer/lib/elements/dom-if";
import "@polymer/polymer/lib/elements/dom-repeat"; import "@polymer/polymer/lib/elements/dom-repeat";
import "../../src/resources/ha-style"; import "../../src/resources/ha-style";
import "../../src/resources/roboto"; import "../../src/resources/roboto";
import "../../src/resources/safari-14-attachshadow-patch";
import "./ha-demo"; import "./ha-demo";
/* polyfill for paper-dropdown */ /* polyfill for paper-dropdown */

View File

@@ -1,4 +1,3 @@
// Compat needs to be first import
import "../../src/resources/compatibility"; import "../../src/resources/compatibility";
import { isNavigationClick } from "../../src/common/dom/is-navigation-click"; import { isNavigationClick } from "../../src/common/dom/is-navigation-click";
import { navigate } from "../../src/common/navigate"; import { navigate } from "../../src/common/navigate";

View File

@@ -2,10 +2,10 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-switch"; import "../../../src/components/ha-switch";
import "../../../src/components/ha-formfield";
import "./demo-card"; import "./demo-card";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
class DemoCards extends PolymerElement { class DemoCards extends PolymerElement {
static get template() { static get template() {

View File

@@ -2,36 +2,37 @@ import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/dialogs/more-info/more-info-content";
import "../../../src/state-summary/state-card-content"; import "../../../src/state-summary/state-card-content";
import "../../../src/dialogs/more-info/more-info-content";
class DemoMoreInfo extends PolymerElement { class DemoMoreInfo extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
.root { :host {
display: flex; display: flex;
align-items: start;
} }
#card {
max-width: 400px;
width: 100vw;
}
ha-card { ha-card {
width: 352px; width: 333px;
padding: 20px 24px; padding: 20px 24px;
} }
state-card-content { state-card-content {
display: block; display: block;
margin-bottom: 16px; margin-bottom: 16px;
} }
pre { pre {
width: 400px; width: 400px;
margin: 0 16px; margin: 0 16px;
overflow: auto; overflow: auto;
color: var(--primary-text-color); color: var(--primary-text-color);
} }
@media only screen and (max-width: 800px) { @media only screen and (max-width: 800px) {
.root { :host {
flex-direction: column; flex-direction: column;
} }
pre { pre {
@@ -39,25 +40,21 @@ class DemoMoreInfo extends PolymerElement {
} }
} }
</style> </style>
<div class="root"> <ha-card>
<div id="card"> <state-card-content
<ha-card> state-obj="[[_stateObj]]"
<state-card-content hass="[[hass]]"
state-obj="[[_stateObj]]" in-dialog
hass="[[hass]]" ></state-card-content>
in-dialog
></state-card-content>
<more-info-content <more-info-content
hass="[[hass]]" hass="[[hass]]"
state-obj="[[_stateObj]]" state-obj="[[_stateObj]]"
></more-info-content> ></more-info-content>
</ha-card> </ha-card>
</div> <template is="dom-if" if="[[showConfig]]">
<template is="dom-if" if="[[showConfig]]"> <pre>[[_jsonEntity(_stateObj)]]</pre>
<pre>[[_jsonEntity(_stateObj)]]</pre> </template>
</template>
</div>
`; `;
} }

View File

@@ -2,8 +2,6 @@ import "@polymer/app-layout/app-toolbar/app-toolbar";
import { html } from "@polymer/polymer/lib/utils/html-tag"; import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */ /* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element"; import { PolymerElement } from "@polymer/polymer/polymer-element";
import { applyThemesOnElement } from "../../../src/common/dom/apply_themes_on_element";
import "../../../src/components/ha-formfield";
import "../../../src/components/ha-switch"; import "../../../src/components/ha-switch";
import "./demo-more-info"; import "./demo-more-info";
@@ -11,10 +9,6 @@ class DemoMoreInfos extends PolymerElement {
static get template() { static get template() {
return html` return html`
<style> <style>
#container {
min-height: calc(100vh - 128px);
background: var(--primary-background-color);
}
.cards { .cards {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -29,31 +23,20 @@ class DemoMoreInfos extends PolymerElement {
.filters { .filters {
margin-left: 60px; margin-left: 60px;
} }
ha-formfield {
margin-right: 16px;
}
</style> </style>
<app-toolbar> <app-toolbar>
<div class="filters"> <div class="filters">
<ha-formfield label="Show entities"> <ha-switch checked="{{_showConfig}}">Show entity</ha-switch>
<ha-switch checked="[[_showConfig]]" on-change="_showConfigToggled">
</ha-switch>
</ha-formfield>
<ha-formfield label="Dark theme">
<ha-switch on-change="_darkThemeToggled"> </ha-switch>
</ha-formfield>
</div> </div>
</app-toolbar> </app-toolbar>
<div id="container"> <div class="cards">
<div class="cards"> <template is="dom-repeat" items="[[entities]]">
<template is="dom-repeat" items="[[entities]]"> <demo-more-info
<demo-more-info entity-id="[[item]]"
entity-id="[[item]]" show-config="[[_showConfig]]"
show-config="[[_showConfig]]" hass="[[hass]]"
hass="[[hass]]" ></demo-more-info>
></demo-more-info> </template>
</template>
</div>
</div> </div>
`; `;
} }
@@ -68,16 +51,6 @@ class DemoMoreInfos extends PolymerElement {
}, },
}; };
} }
_showConfigToggled(ev) {
this._showConfig = ev.target.checked;
}
_darkThemeToggled(ev) {
applyThemesOnElement(this.$.container, { themes: {} }, "default", {
dark: ev.target.checked,
});
}
} }
customElements.define("demo-more-infos", DemoMoreInfos); customElements.define("demo-more-infos", DemoMoreInfos);

View File

@@ -7,8 +7,8 @@ export const createMediaPlayerEntities = () => [
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead", media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media + // Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set // Select Source + Stop + Clear + Play + Shuffle Set + Browse Media
supported_features: 64063, supported_features: 195135,
entity_picture: "/images/album_cover_2.jpg", entity_picture: "/images/album_cover_2.jpg",
media_duration: 300, media_duration: 300,
media_position: 50, media_position: 50,
@@ -24,8 +24,8 @@ export const createMediaPlayerEntities = () => [
media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)", media_title: "I Wanna Be A Hippy (Flamman & Abraxas Radio Mix)",
media_artist: "Technohead", media_artist: "Technohead",
// Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media + // Pause + Seek + Volume Set + Volume Mute + Previous Track + Next Track + Play Media +
// Select Source + Stop + Clear + Play + Shuffle Set + Browse Media // Select Source + Stop + Clear + Play + Shuffle Set
supported_features: 195135, supported_features: 64063,
entity_picture: "/images/album_cover.jpg", entity_picture: "/images/album_cover.jpg",
media_duration: 300, media_duration: 300,
media_position: 0, media_position: 0,

View File

@@ -1,72 +0,0 @@
import { getEntity } from "../../../src/fake_data/entity";
export const createPlantEntities = () => [
getEntity("plant", "lemon_tree", "ok", {
problem: "none",
sensors: {
moisture: "sensor.lemon_tree_moisture",
battery: "sensor.lemon_tree_battery",
temperature: "sensor.lemon_tree_temperature",
conductivity: "sensor.lemon_tree_conductivity",
brightness: "sensor.lemon_tree_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 95,
temperature: 15.6,
conductivity: 1,
brightness: 12,
max_brightness: 20,
friendly_name: "Lemon Tree",
}),
getEntity("plant", "apple_tree", "ok", {
problem: "brightness",
sensors: {
moisture: "sensor.apple_tree_moisture",
battery: "sensor.apple_tree_battery",
temperature: "sensor.apple_tree_temperature",
conductivity: "sensor.apple_tree_conductivity",
brightness: "sensor.apple_tree_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
battery: "%",
conductivity: "μS/cm",
},
moisture: 54,
battery: 2,
temperature: 15.6,
conductivity: 1,
brightness: 25,
max_brightness: 20,
friendly_name: "Apple Tree",
}),
getEntity("plant", "sunflowers", "ok", {
problem: "moisture, temperature, conductivity",
sensors: {
moisture: "sensor.sunflowers_moisture",
temperature: "sensor.sunflowers_temperature",
conductivity: "sensor.sunflowers_conductivity",
brightness: "sensor.sunflowers_brightness",
},
unit_of_measurement_dict: {
temperature: "°C",
moisture: "%",
brightness: "lx",
conductivity: "μS/cm",
},
moisture: 54,
temperature: 15.6,
conductivity: 1,
brightness: 25,
entity_picture: "/images/sunflowers.jpg",
}),
];

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -76,19 +71,28 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-alarm-panel-card") class DemoAlarmPanelEntity extends PolymerElement {
class DemoAlarmPanelEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
hass.updateTranslations(null, "en"); type: Object,
hass.updateTranslations("lovelace", "en"); value: CONFIGS,
},
};
}
public ready() {
super.ready();
this._setupDemo();
}
private async _setupDemo() {
const hass = provideHass(this.$.demos);
await hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -58,19 +53,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-conditional-card") class DemoConditional extends PolymerElement {
class DemoConditional extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -222,19 +217,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-entities-card") class DemoEntities extends PolymerElement {
class DemoEntities extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -53,7 +48,7 @@ const CONFIGS = [
config: ` config: `
- type: button - type: button
entity: light.bed_light entity: light.bed_light
tap_action: tap_action:
action: toggle action: toggle
`, `,
}, },
@@ -74,19 +69,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-entity-button-card") class DemoButtonEntity extends PolymerElement {
class DemoButtonEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -48,7 +43,7 @@ const ENTITIES = [
const CONFIGS = [ const CONFIGS = [
{ {
heading: "Unfiltered controller", heading: "Controller",
config: ` config: `
- type: entities - type: entities
entities: entities:
@@ -58,7 +53,7 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "Filtered entities card", heading: "Basic",
config: ` config: `
- type: entity-filter - type: entity-filter
entities: entities:
@@ -74,27 +69,7 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: 'With "entities" card config', heading: "With card config",
config: `
- type: entity-filter
entities:
- device_tracker.demo_anne_therese
- device_tracker.demo_home_boy
- device_tracker.demo_paulus
- light.bed_light
- light.ceiling_lights
- light.kitchen_lights
state_filter:
- "on"
- not_home
card:
type: entities
title: Custom Title
show_header_toggle: false
`,
},
{
heading: 'With "glance" card config',
config: ` config: `
- type: entity-filter - type: entity-filter
entities: entities:
@@ -109,27 +84,31 @@ const CONFIGS = [
- not_home - not_home
card: card:
type: glance type: glance
show_state: true show_state: false
title: Custom Title
`, `,
}, },
]; ];
@customElement("demo-hui-entity-filter-card") class DemoFilter extends PolymerElement {
class DemoEntityFilter extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
customElements.define("demo-hui-entity-filter-card", DemoEntityFilter); customElements.define("demo-hui-entity-filter-card", DemoFilter);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -112,19 +107,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-gauge-card") class DemoGaugeEntity extends PolymerElement {
class DemoGaugeEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -82,8 +77,7 @@ const CONFIGS = [
heading: "With title", heading: "With title",
config: ` config: `
- type: glance - type: glance
title: Custom title title: This is glance
columns: 4
entities: entities:
- device_tracker.demo_paulus - device_tracker.demo_paulus
- media_player.living_room - media_player.living_room
@@ -110,10 +104,9 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "No entity names", heading: "No name",
config: ` config: `
- type: glance - type: glance
columns: 4
show_name: false show_name: false
entities: entities:
- device_tracker.demo_paulus - device_tracker.demo_paulus
@@ -126,10 +119,9 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "No state labels", heading: "No state",
config: ` config: `
- type: glance - type: glance
columns: 4
show_state: false show_state: false
entities: entities:
- device_tracker.demo_paulus - device_tracker.demo_paulus
@@ -142,10 +134,9 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "No names and no state labels", heading: "No name and no state",
config: ` config: `
- type: glance - type: glance
columns: 4
show_name: false show_name: false
show_state: false show_state: false
entities: entities:
@@ -159,24 +150,47 @@ const CONFIGS = [
`, `,
}, },
{ {
heading: "Custom name + custom icon", heading: "Custom name, custom icon",
config: ` config: `
- type: glance - type: glance
columns: 4
entities: entities:
- entity: device_tracker.demo_paulus - entity: device_tracker.demo_paulus
name: ¯\\_(ツ)_/¯ name: ¯\\_(ツ)_/¯
icon: mdi:home-assistant icon: mdi:home-assistant
- entity: media_player.living_room - media_player.living_room
name: ¯\\_(ツ)_/¯ - sun.sun
icon: mdi:home-assistant - cover.kitchen_window
- entity: light.kitchen_lights
icon: mdi:alarm-light
- lock.kitchen_door
- light.ceiling_lights
`,
},
{
heading: "Custom tap action",
config: `
- type: glance
entities:
- entity: lock.kitchen_door
tap_action:
type: toggle
- entity: light.ceiling_lights
tap_action:
action: call-service
service: light.turn_on
service_data:
entity_id: light.ceiling_lights
- device_tracker.demo_paulus
- media_player.living_room
- sun.sun
- cover.kitchen_window
- light.kitchen_lights
`, `,
}, },
{ {
heading: "Selectively hidden name", heading: "Selectively hidden name",
config: ` config: `
- type: glance - type: glance
columns: 4
entities: entities:
- device_tracker.demo_paulus - device_tracker.demo_paulus
- entity: media_player.living_room - entity: media_player.living_room
@@ -185,51 +199,45 @@ const CONFIGS = [
- entity: cover.kitchen_window - entity: cover.kitchen_window
name: name:
- light.kitchen_lights - light.kitchen_lights
- entity: lock.kitchen_door
name:
- light.ceiling_lights
`, `,
}, },
{ {
heading: "Custom tap action", heading: "Primary theme",
config: ` config: `
- type: glance - type: glance
columns: 4 theming: primary
entities: entities:
- entity: lock.kitchen_door - device_tracker.demo_paulus
name: Custom - media_player.living_room
tap_action: - sun.sun
type: toggle - cover.kitchen_window
- entity: light.ceiling_lights - light.kitchen_lights
name: Custom - lock.kitchen_door
tap_action: - light.ceiling_lights
action: call-service
service: light.turn_on
service_data:
entity_id: light.ceiling_lights
- entity: sun.sun
name: Regular
- entity: light.kitchen_lights
name: Regular
`, `,
}, },
]; ];
@customElement("demo-hui-glance-card") class DemoPicEntity extends PolymerElement {
class DemoGlanceEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
customElements.define("demo-hui-glance-card", DemoGlanceEntity); customElements.define("demo-hui-glance-card", DemoPicEntity);

View File

@@ -1,4 +1,6 @@
import { customElement, html, LitElement, TemplateResult } from "lit-element"; import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../components/demo-cards"; import "../components/demo-cards";
const CONFIGS = [ const CONFIGS = [
@@ -35,10 +37,18 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-iframe-card") class DemoIframe extends PolymerElement {
class DemoIframe extends LitElement { static get template() {
protected render(): TemplateResult { return html` <demo-cards configs="[[_configs]]"></demo-cards> `;
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`; }
static get properties() {
return {
_configs: {
type: Object,
value: CONFIGS,
},
};
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -68,19 +63,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-light-card") class DemoLightEntity extends PolymerElement {
class DemoLightEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -166,19 +161,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-map-card") class DemoMap extends PolymerElement {
class DemoMap extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockTemplate } from "../../../demo/src/stubs/template"; import { mockTemplate } from "../../../demo/src/stubs/template";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -259,19 +254,23 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-markdown-card") class DemoMarkdown extends PolymerElement {
class DemoMarkdown extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
hass.updateTranslations(null, "en"); type: Object,
hass.updateTranslations("lovelace", "en"); value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
mockTemplate(hass); mockTemplate(hass);
} }
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players"; import { createMediaPlayerEntities } from "../data/media_players";
@@ -151,33 +146,28 @@ const CONFIGS = [
entity: media_player.receiver_off entity: media_player.receiver_off
`, `,
}, },
{
heading: "Grid Full Size",
config: `
- type: grid
columns: 1
cards:
- type: media-control
entity: media_player.music_paused
`,
},
]; ];
@customElement("demo-hui-media-control-card") class DemoHuiMediControlCard extends PolymerElement {
class DemoHuiMediaControlCard extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createMediaPlayerEntities()); hass.addEntities(createMediaPlayerEntities());
} }
} }
customElements.define("demo-hui-media-control-card", DemoHuiMediaControlCard); customElements.define("demo-hui-media-control-card", DemoHuiMediControlCard);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
import { createMediaPlayerEntities } from "../data/media_players"; import { createMediaPlayerEntities } from "../data/media_players";
@@ -31,9 +26,9 @@ const CONFIGS = [
- entity: media_player.android_cast - entity: media_player.android_cast
name: Screen casting name: Screen casting
- entity: media_player.image_display - entity: media_player.image_display
name: Digital Picture Frame name: Digital Picture Frame
- entity: media_player.sonos_idle - entity: media_player.sonos_idle
name: Sonos Idle name: Sonos Idle
- entity: media_player.idle_browse_media - entity: media_player.idle_browse_media
name: Idle waiting for Browse Media name: Idle waiting for Browse Media
- entity: media_player.theater_off - entity: media_player.theater_off
@@ -43,7 +38,7 @@ const CONFIGS = [
- entity: media_player.theater_off_static - entity: media_player.theater_off_static
name: Player Off (cannot be switched on) name: Player Off (cannot be switched on)
- entity: media_player.theater_on_static - entity: media_player.theater_on_static
name: Player On (cannot be switched off) name: Player On (cannot be switched off)
- entity: media_player.idle - entity: media_player.idle
name: Player Idle name: Player Idle
- entity: media_player.playing - entity: media_player.playing
@@ -60,21 +55,26 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-media-player-row") class DemoHuiMediaPlayerRows extends PolymerElement {
class DemoHuiMediaPlayerRow extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createMediaPlayerEntities()); hass.addEntities(createMediaPlayerEntities());
} }
} }
customElements.define("demo-hui-media-player-row", DemoHuiMediaPlayerRow); customElements.define("demo-hui-media-player-rows", DemoHuiMediaPlayerRows);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -130,21 +125,26 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-picture-elements-card") class DemoPicElements extends PolymerElement {
class DemoPictureElements extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
customElements.define("demo-hui-picture-elements-card", DemoPictureElements); customElements.define("demo-hui-picture-elements-card", DemoPicElements);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -85,21 +80,26 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-picture-entity-card") class DemoPicEntity extends PolymerElement {
class DemoPictureEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
customElements.define("demo-hui-picture-entity-card", DemoPictureEntity); customElements.define("demo-hui-picture-entity-card", DemoPicEntity);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -126,21 +121,26 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-picture-glance-card") class DemoPicGlance extends PolymerElement {
class DemoPictureGlance extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }
customElements.define("demo-hui-picture-glance-card", DemoPictureGlance); customElements.define("demo-hui-picture-glance-card", DemoPicGlance);

View File

@@ -1,55 +0,0 @@
import {
customElement,
html,
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards";
import { createPlantEntities } from "../data/plants";
const CONFIGS = [
{
heading: "Basic example",
config: `
- type: plant-status
entity: plant.lemon_tree
`,
},
{
heading: "Problem (too bright) + low battery",
config: `
- type: plant-status
entity: plant.apple_tree
`,
},
{
heading: "With picture + multiple problems",
config: `
- type: plant-status
entity: plant.sunflowers
name: Sunflowers Name Overwrite
`,
},
];
@customElement("demo-hui-plant-card")
export class DemoPlantEntity extends LitElement {
@query("#demos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
}
protected firstUpdated(changedProperties: PropertyValues) {
super.firstUpdated(changedProperties);
const hass = provideHass(this._demoRoot);
hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(createPlantEntities());
}
}
customElements.define("demo-hui-plant-card", DemoPlantEntity);

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -25,19 +20,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-shopping-list-card") class DemoShoppingListEntity extends PolymerElement {
class DemoShoppingListEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.mockAPI("shopping_list", () => [ hass.mockAPI("shopping_list", () => [
{ name: "list", id: 1, complete: false }, { name: "list", id: 1, complete: false },

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { mockHistory } from "../../../demo/src/stubs/history"; import { mockHistory } from "../../../demo/src/stubs/history";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
@@ -137,19 +132,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-stack-card") class DemoStack extends PolymerElement {
class DemoStack extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
mockHistory(hass); mockHistory(hass);
} }

View File

@@ -1,11 +1,6 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { provideHass } from "../../../src/fake_data/provide_hass"; import { provideHass } from "../../../src/fake_data/provide_hass";
import "../components/demo-cards"; import "../components/demo-cards";
@@ -79,19 +74,24 @@ const CONFIGS = [
}, },
]; ];
@customElement("demo-hui-thermostat-card") class DemoThermostatEntity extends PolymerElement {
class DemoThermostatEntity extends LitElement { static get template() {
@query("#demos") private _demoRoot!: HTMLElement; return html` <demo-cards id="demos" configs="[[_configs]]"></demo-cards> `;
protected render(): TemplateResult {
return html`<demo-cards id="demos" .configs=${CONFIGS}></demo-cards>`;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _configs: {
type: Object,
value: CONFIGS,
},
};
}
public ready() {
super.ready();
const hass = provideHass(this.$.demos);
hass.updateTranslations(null, "en"); hass.updateTranslations(null, "en");
hass.updateTranslations("lovelace", "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,29 +1,12 @@
import { import { html } from "@polymer/polymer/lib/utils/html-tag";
customElement, /* eslint-plugin-disable lit */
html, import { PolymerElement } from "@polymer/polymer/polymer-element";
LitElement,
property,
PropertyValues,
query,
TemplateResult,
} from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { import { SUPPORT_BRIGHTNESS } from "../../../src/data/light";
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
SUPPORT_EFFECT,
SUPPORT_FLASH,
SUPPORT_TRANSITION,
SUPPORT_WHITE_VALUE,
} from "../../../src/data/light";
import "../../../src/dialogs/more-info/more-info-content";
import { getEntity } from "../../../src/fake_data/entity"; import { getEntity } from "../../../src/fake_data/entity";
import { import { provideHass } from "../../../src/fake_data/provide_hass";
MockHomeAssistant,
provideHass,
} from "../../../src/fake_data/provide_hass";
import "../components/demo-more-infos"; import "../components/demo-more-infos";
import "../../../src/dialogs/more-info/more-info-content";
const ENTITIES = [ const ENTITIES = [
getEntity("light", "bed_light", "on", { getEntity("light", "bed_light", "on", {
@@ -31,52 +14,38 @@ const ENTITIES = [
}), }),
getEntity("light", "kitchen_light", "on", { getEntity("light", "kitchen_light", "on", {
friendly_name: "Brightness Light", friendly_name: "Brightness Light",
brightness: 200, brightness: 80,
supported_features: SUPPORT_BRIGHTNESS, supported_features: SUPPORT_BRIGHTNESS,
}), }),
getEntity("light", "color_temperature_light", "on", {
friendly_name: "White Color Temperature Light",
brightness: 128,
color_temp: 75,
min_mireds: 30,
max_mireds: 150,
supported_features: SUPPORT_BRIGHTNESS + SUPPORT_COLOR_TEMP,
}),
getEntity("light", "color_effectslight", "on", {
friendly_name: "Color Effets Light",
brightness: 255,
hs_color: [30, 100],
white_value: 36,
supported_features:
SUPPORT_BRIGHTNESS +
SUPPORT_EFFECT +
SUPPORT_FLASH +
SUPPORT_COLOR +
SUPPORT_TRANSITION +
SUPPORT_WHITE_VALUE,
effect_list: ["random", "colorloop"],
}),
]; ];
@customElement("demo-more-info-light") class DemoMoreInfoLight extends PolymerElement {
class DemoMoreInfoLight extends LitElement { static get template() {
@property() public hass!: MockHomeAssistant;
@query("demo-more-infos") private _demoRoot!: HTMLElement;
protected render(): TemplateResult {
return html` return html`
<demo-more-infos <demo-more-infos
.hass=${this.hass} hass="[[hass]]"
.entities=${ENTITIES.map((ent) => ent.entityId)} entities="[[_entities]]"
></demo-more-infos> ></demo-more-infos>
`; `;
} }
protected firstUpdated(changedProperties: PropertyValues) { static get properties() {
super.firstUpdated(changedProperties); return {
const hass = provideHass(this._demoRoot); _entities: {
hass.updateTranslations(null, "en"); type: Array,
value: ENTITIES.map((ent) => ent.entityId),
},
};
}
public ready() {
super.ready();
this._setupDemo();
}
private async _setupDemo() {
const hass = provideHass(this);
await hass.updateTranslations(null, "en");
hass.addEntities(ENTITIES); hass.addEntities(ENTITIES);
} }
} }

View File

@@ -1,10 +1,9 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { customElement, html, LitElement, TemplateResult } from "lit-element"; import { html, LitElement, TemplateResult } from "lit-element";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { ActionHandlerEvent } from "../../../src/data/lovelace"; import { ActionHandlerEvent } from "../../../src/data/lovelace";
import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive"; import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
@customElement("demo-util-long-press")
export class DemoUtilLongPress extends LitElement { export class DemoUtilLongPress extends LitElement {
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
@@ -21,7 +20,7 @@ export class DemoUtilLongPress extends LitElement {
<textarea></textarea> <textarea></textarea>
<div>Try pressing and scrolling too!</div> <div>(try pressing and scrolling too!)</div>
</ha-card> </ha-card>
` `
)} )}
@@ -63,3 +62,5 @@ export class DemoUtilLongPress extends LitElement {
`; `;
} }
} }
customElements.define("demo-util-long-press", DemoUtilLongPress);

View File

@@ -14,6 +14,8 @@ import "../../src/styles/polymer-ha-style";
// eslint-disable-next-line import/extensions // eslint-disable-next-line import/extensions
import { DEMOS } from "../build/import-demos"; import { DEMOS } from "../build/import-demos";
const fixPath = (path) => path.substr(2, path.length - 5);
class HaGallery extends PolymerElement { class HaGallery extends PolymerElement {
static get template() { static get template() {
return html` return html`

View File

@@ -12,7 +12,6 @@ import {
} from "lit-element"; } from "lit-element";
import { html, TemplateResult } from "lit-html"; import { html, TemplateResult } from "lit-html";
import { atLeastVersion } from "../../../src/common/config/version"; import { atLeastVersion } from "../../../src/common/config/version";
import { fireEvent } from "../../../src/common/dom/fire_event";
import "../../../src/common/search/search-input"; import "../../../src/common/search/search-input";
import "../../../src/components/ha-button-menu"; import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-svg-icon"; import "../../../src/components/ha-svg-icon";
@@ -23,7 +22,6 @@ import {
reloadHassioAddons, reloadHassioAddons,
} 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 { fetchHassioSupervisorInfo } from "../../../src/data/hassio/supervisor";
import "../../../src/layouts/hass-loading-screen"; import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
@@ -192,11 +190,7 @@ class HassioAddonStore extends LitElement {
private async _loadData() { private async _loadData() {
try { try {
const [addonsInfo, supervisor] = await Promise.all([ const addonsInfo = await fetchHassioAddonsInfo(this.hass);
fetchHassioAddonsInfo(this.hass),
fetchHassioSupervisorInfo(this.hass),
]);
fireEvent(this, "supervisor-update", { supervisor });
this._repos = addonsInfo.repositories; this._repos = addonsInfo.repositories;
this._repos.sort(sortRepos); this._repos.sort(sortRepos);
this._addons = addonsInfo.addons; this._addons = addonsInfo.addons;

View File

@@ -7,14 +7,13 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "web-animations-js/web-animations-next-lite.min"; import "web-animations-js/web-animations-next-lite.min";
import "../../../../src/components/buttons/ha-progress-button";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
import { import {
HassioAddonDetails, HassioAddonDetails,
@@ -29,6 +28,7 @@ import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; 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";
import "../../../../src/components/buttons/ha-progress-button";
@customElement("hassio-addon-audio") @customElement("hassio-addon-audio")
class HassioAddonAudio extends LitElement { class HassioAddonAudio extends LitElement {

View File

@@ -7,11 +7,11 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
import "../../../../src/components/ha-circular-progress";
import "./hassio-addon-audio"; import "./hassio-addon-audio";
import "./hassio-addon-config"; import "./hassio-addon-config";
import "./hassio-addon-network"; import "./hassio-addon-network";
@@ -26,41 +26,28 @@ class HassioAddonConfigDashboard extends LitElement {
if (!this.addon) { if (!this.addon) {
return html`<ha-circular-progress active></ha-circular-progress>`; return html`<ha-circular-progress active></ha-circular-progress>`;
} }
const hasOptions =
this.addon.options && Object.keys(this.addon.options).length;
const hasSchema =
this.addon.schema && Object.keys(this.addon.schema).length;
return html` return html`
<div class="content"> <div class="content">
${hasOptions || hasSchema || this.addon.network || this.addon.audio <hassio-addon-config
.hass=${this.hass}
.addon=${this.addon}
></hassio-addon-config>
${this.addon.network
? html` ? html`
${hasOptions || hasSchema <hassio-addon-network
? html` .hass=${this.hass}
<hassio-addon-config .addon=${this.addon}
.hass=${this.hass} ></hassio-addon-network>
.addon=${this.addon}
></hassio-addon-config>
`
: ""}
${this.addon.network
? html`
<hassio-addon-network
.hass=${this.hass}
.addon=${this.addon}
></hassio-addon-network>
`
: ""}
${this.addon.audio
? html`
<hassio-addon-audio
.hass=${this.hass}
.addon=${this.addon}
></hassio-addon-audio>
`
: ""}
` `
: "This add-on does not expose configuration for you to mess with.... 👋"} : ""}
${this.addon.audio
? html`
<hassio-addon-audio
.hass=${this.hass}
.addon=${this.addon}
></hassio-addon-audio>
`
: ""}
</div> </div>
`; `;
} }

View File

@@ -1,7 +1,4 @@
import "@material/mwc-button"; import "@material/mwc-button";
import { ActionDetail } from "@material/mwc-list";
import "@material/mwc-list/mwc-list-item";
import { mdiDotsVertical } from "@mdi/js";
import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea"; import "@polymer/iron-autogrow-textarea/iron-autogrow-textarea";
import { import {
css, css,
@@ -17,9 +14,7 @@ import {
} from "lit-element"; } from "lit-element";
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-button-menu";
import "../../../../src/components/ha-card"; import "../../../../src/components/ha-card";
import "../../../../src/components/ha-form/ha-form";
import "../../../../src/components/ha-yaml-editor"; import "../../../../src/components/ha-yaml-editor";
import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor"; import type { HaYamlEditor } from "../../../../src/components/ha-yaml-editor";
import { import {
@@ -34,67 +29,35 @@ 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 = ["string", "select", "boolean", "integer", "float"];
@customElement("hassio-addon-config") @customElement("hassio-addon-config")
class HassioAddonConfig extends LitElement { class HassioAddonConfig extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@property({ attribute: false }) public hass!: HomeAssistant; @internalProperty() private _error?: string;
@property({ type: Boolean }) private _configHasChanged = false; @property({ type: Boolean }) private _configHasChanged = false;
@property({ type: Boolean }) private _valid = true; @property({ type: Boolean }) private _valid = true;
@internalProperty() private _canShowSchema = false; @query("ha-yaml-editor", true) private _editor!: HaYamlEditor;
@internalProperty() private _error?: string;
@internalProperty() private _options?: Record<string, unknown>;
@internalProperty() private _yamlMode = false;
@query("ha-yaml-editor") private _editor?: HaYamlEditor;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<h1>${this.addon.name}</h1> <h1>${this.addon.name}</h1>
<ha-card> <ha-card header="Configuration">
<div class="header">
<h2>Configuration</h2>
<div class="card-menu">
<ha-button-menu corner="BOTTOM_START" @action=${this._handleAction}>
<mwc-icon-button slot="trigger">
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
</mwc-icon-button>
<mwc-list-item .disabled=${!this._canShowSchema}>
${this._yamlMode ? "Edit in UI" : "Edit in YAML"}
</mwc-list-item>
<mwc-list-item class="warning">
Reset to defaults
</mwc-list-item>
</ha-button-menu>
</div>
</div>
<div class="card-content"> <div class="card-content">
${!this._yamlMode && this._canShowSchema && this.addon.schema <ha-yaml-editor
? html`<ha-form @value-changed=${this._configChanged}
.data=${this._options!} ></ha-yaml-editor>
@value-changed=${this._configChanged}
.schema=${this.addon.schema}
></ha-form>`
: html` <ha-yaml-editor
@value-changed=${this._configChanged}
></ha-yaml-editor>`}
${this._error ? html` <div class="errors">${this._error}</div> ` : ""} ${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
${!this._yamlMode || ${this._valid ? "" : html` <div class="errors">Invalid YAML</div> `}
(this._canShowSchema && this.addon.schema) ||
this._valid
? ""
: html` <div class="errors">Invalid YAML</div> `}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<ha-progress-button class="warning" @click=${this._resetTapped}>
Reset to defaults
</ha-progress-button>
<ha-progress-button <ha-progress-button
@click=${this._saveTapped} @click=${this._saveTapped}
.disabled=${!this._configHasChanged || !this._valid} .disabled=${!this._configHasChanged || !this._valid}
@@ -106,55 +69,16 @@ class HassioAddonConfig extends LitElement {
`; `;
} }
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this._canShowSchema = !this.addon.schema.find(
// @ts-ignore
(entry) => !SUPPORTED_UI_TYPES.includes(entry.type) || entry.multiple
);
this._yamlMode = !this._canShowSchema;
}
protected updated(changedProperties: PropertyValues): void { protected updated(changedProperties: PropertyValues): void {
if (changedProperties.has("addon")) {
this._options = { ...this.addon.options };
}
super.updated(changedProperties); super.updated(changedProperties);
if ( if (changedProperties.has("addon")) {
changedProperties.has("_yamlMode") || this._editor.setValue(this.addon.options);
changedProperties.has("_options")
) {
if (this._yamlMode) {
const editor = this._editor;
if (editor) {
editor.setValue(this._options!);
}
}
}
}
private _handleAction(ev: CustomEvent<ActionDetail>) {
switch (ev.detail.index) {
case 0:
this._yamlMode = !this._yamlMode;
break;
case 1:
this._resetTapped(ev);
break;
} }
} }
private _configChanged(ev): void { private _configChanged(ev): void {
if (this.addon.schema && this._canShowSchema && !this._yamlMode) { this._configHasChanged = true;
this._valid = true; this._valid = ev.detail.isValid;
this._configHasChanged = true;
} else {
this._configHasChanged = true;
this._valid = ev.detail.isValid;
}
if (this._valid) {
this._options! = ev.detail.value;
}
} }
private async _resetTapped(ev: CustomEvent): Promise<void> { private async _resetTapped(ev: CustomEvent): Promise<void> {
@@ -198,12 +122,18 @@ class HassioAddonConfig extends LitElement {
const button = ev.currentTarget as any; const button = ev.currentTarget as any;
button.progress = true; button.progress = true;
let data: HassioAddonSetOptionParams;
this._error = undefined; this._error = undefined;
try { try {
await setHassioAddonOption(this.hass, this.addon.slug, { data = {
options: this._options!, options: this._editor.value,
}); };
} catch (err) {
this._error = err;
return;
}
try {
await setHassioAddonOption(this.hass, this.addon.slug, data);
this._configHasChanged = false; this._configHasChanged = false;
const eventdata = { const eventdata = {
success: true, success: true,
@@ -248,29 +178,6 @@ class HassioAddonConfig extends LitElement {
.syntaxerror { .syntaxerror {
color: var(--error-color); color: var(--error-color);
} }
.card-menu {
float: right;
z-index: 3;
--mdc-theme-text-primary-on-background: var(--primary-text-color);
}
mwc-list-item[disabled] {
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
}
.header {
display: flex;
justify-content: space-between;
}
.header h2 {
color: var(--ha-card-header-color, --primary-text-color);
font-family: var(--ha-card-header-font-family, inherit);
font-size: var(--ha-card-header-font-size, 24px);
letter-spacing: -0.012em;
line-height: 48px;
padding: 12px 16px 16px;
display: block;
margin-block: 0px;
font-weight: normal;
}
`, `,
]; ];
} }

View File

@@ -14,12 +14,12 @@ import {
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import "../../../src/components/ha-circular-progress";
import { import {
fetchHassioAddonInfo, fetchHassioAddonInfo,
HassioAddonDetails, HassioAddonDetails,
} from "../../../src/data/hassio/addon"; } from "../../../src/data/hassio/addon";
import "../../../src/layouts/hass-tabs-subpage"; import "../../../src/layouts/hass-tabs-subpage";
import "../../../src/components/ha-circular-progress";
import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage"; import type { PageNavigation } from "../../../src/layouts/hass-tabs-subpage";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";

View File

@@ -7,8 +7,8 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import "../../../../src/components/ha-circular-progress";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";

View File

@@ -49,20 +49,14 @@ import {
uninstallHassioAddon, uninstallHassioAddon,
validateHassioAddonOption, validateHassioAddonOption,
} from "../../../../src/data/hassio/addon"; } from "../../../../src/data/hassio/addon";
import { import { extractApiErrorMessage } from "../../../../src/data/hassio/common";
extractApiErrorMessage,
fetchHassioStats,
HassioStats,
} from "../../../../src/data/hassio/common";
import { import {
showAlertDialog, showAlertDialog,
showConfirmationDialog, showConfirmationDialog,
} from "../../../../src/dialogs/generic/show-dialog-box"; } from "../../../../src/dialogs/generic/show-dialog-box";
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 "../../components/hassio-card-content"; import "../../components/hassio-card-content";
import "../../components/supervisor-metric";
import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown"; import { showHassioMarkdownDialog } from "../../dialogs/markdown/show-dialog-hassio-markdown";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";
@@ -75,7 +69,7 @@ const STAGE_ICON = {
const PERMIS_DESC = { const PERMIS_DESC = {
stage: { stage: {
title: "Add-on Stage", title: "Add-on Stage",
description: `Add-ons can have one of three stages:\n\n<ha-svg-icon path="${STAGE_ICON.stable}"></ha-svg-icon> **Stable**: These are add-ons ready to be used in production.\n\n<ha-svg-icon path="${STAGE_ICON.experimental}"></ha-svg-icon> **Experimental**: These may contain bugs, and may be unfinished.\n\n<ha-svg-icon path="${STAGE_ICON.deprecated}"></ha-svg-icon> **Deprecated**: These add-ons will no longer receive any updates.`, description: `Add-ons can have one of three stages:\n\n<ha-svg-icon .path='${STAGE_ICON.stable}'></ha-svg-icon> **Stable**: These are add-ons ready to be used in production.\n\n<ha-svg-icon .path='${STAGE_ICON.experimental}'></ha-svg-icon> **Experimental**: These may contain bugs, and may be unfinished.\n\n<ha-svg-icon .path='${STAGE_ICON.deprecated}'></ha-svg-icon> **Deprecated**: These add-ons will no longer receive any updates.`,
}, },
rating: { rating: {
title: "Add-on Security Rating", title: "Add-on Security Rating",
@@ -137,24 +131,9 @@ class HassioAddonInfo extends LitElement {
@property({ attribute: false }) public addon!: HassioAddonDetails; @property({ attribute: false }) public addon!: HassioAddonDetails;
@internalProperty() private _metrics?: HassioStats;
@internalProperty() private _error?: string; @internalProperty() private _error?: string;
protected render(): TemplateResult { protected render(): TemplateResult {
const metrics = [
{
description: "Add-on CPU Usage",
value: this._metrics?.cpu_percent,
},
{
description: "Add-on RAM Usage",
value: this._metrics?.memory_percent,
tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
this._metrics?.memory_limit
)}`,
},
];
return html` return html`
${this.addon.update_available ${this.addon.update_available
? html` ? html`
@@ -258,281 +237,249 @@ class HassioAddonInfo extends LitElement {
> >
for details. for details.
</div> </div>
<div class="addon-container"> ${this.addon.logo
<div> ? html`
${this.addon.logo <img
? html` class="logo"
<img src="/api/hassio/addons/${this.addon.slug}/logo"
class="logo" />
src="/api/hassio/addons/${this.addon.slug}/logo" `
/> : ""}
` <div class="security">
: ""} ${this.addon.stage !== "stable"
<div class="security"> ? html` <ha-label-badge
${this.addon.stage !== "stable"
? html` <ha-label-badge
class=${classMap({
yellow: this.addon.stage === "experimental",
red: this.addon.stage === "deprecated",
})}
@click=${this._showMoreInfo}
id="stage"
label="stage"
description=""
>
<ha-svg-icon
.path=${STAGE_ICON[this.addon.stage]}
></ha-svg-icon>
</ha-label-badge>`
: ""}
<ha-label-badge
class=${classMap({ class=${classMap({
green: [5, 6].includes(Number(this.addon.rating)), yellow: this.addon.stage === "experimental",
yellow: [3, 4].includes(Number(this.addon.rating)), red: this.addon.stage === "deprecated",
red: [1, 2].includes(Number(this.addon.rating)),
})} })}
@click=${this._showMoreInfo} @click=${this._showMoreInfo}
id="rating" id="stage"
.value=${this.addon.rating} label="stage"
label="rating"
description="" description=""
></ha-label-badge> >
${this.addon.host_network <ha-svg-icon
? html` .path=${STAGE_ICON[this.addon.stage]}
<ha-label-badge ></ha-svg-icon>
@click=${this._showMoreInfo} </ha-label-badge>`
id="host_network" : ""}
label="host"
description=""
>
<ha-svg-icon .path=${mdiNetwork}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.full_access
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="full_access"
label="hardware"
description=""
>
<ha-svg-icon .path=${mdiChip}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.homeassistant_api
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="homeassistant_api"
label="hass"
description=""
>
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this._computeHassioApi
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="hassio_api"
label="hassio"
.description=${this.addon.hassio_role}
>
<ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.docker_api
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="docker_api"
label="docker"
description=""
>
<ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.host_pid
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="host_pid"
label="host pid"
description=""
>
<ha-svg-icon .path=${mdiPound}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.apparmor
? html`
<ha-label-badge
@click=${this._showMoreInfo}
class=${this._computeApparmorClassName}
id="apparmor"
label="apparmor"
description=""
>
<ha-svg-icon .path=${mdiShield}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.auth_api
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="auth_api"
label="auth"
description=""
>
<ha-svg-icon .path=${mdiKey}></ha-svg-icon>
</ha-label-badge>
`
: ""}
${this.addon.ingress
? html`
<ha-label-badge
@click=${this._showMoreInfo}
id="ingress"
label="ingress"
description=""
>
<ha-svg-icon
.path=${mdiCursorDefaultClickOutline}
></ha-svg-icon>
</ha-label-badge>
`
: ""}
</div>
${this.addon.version <ha-label-badge
? html` class=${classMap({
<div green: [5, 6].includes(Number(this.addon.rating)),
class="${classMap({ yellow: [3, 4].includes(Number(this.addon.rating)),
"addon-options": true, red: [1, 2].includes(Number(this.addon.rating)),
started: this.addon.state === "started", })}
})}" @click=${this._showMoreInfo}
> id="rating"
<ha-settings-row ?three-line=${this.narrow}> .value=${this.addon.rating}
<span slot="heading"> label="rating"
Start on boot description=""
</span> ></ha-label-badge>
<span slot="description"> ${this.addon.host_network
Make the add-on start during a system boot ? html`
</span> <ha-label-badge
<ha-switch @click=${this._showMoreInfo}
@change=${this._startOnBootToggled} id="host_network"
.checked=${this.addon.boot === "auto"} label="host"
haptic description=""
></ha-switch> >
</ha-settings-row> <ha-svg-icon .path=${mdiNetwork}></ha-svg-icon>
</ha-label-badge>
${this.addon.startup !== "once" `
? html` : ""}
<ha-settings-row ?three-line=${this.narrow}> ${this.addon.full_access
<span slot="heading"> ? html`
Watchdog <ha-label-badge
</span> @click=${this._showMoreInfo}
<span slot="description"> id="full_access"
This will start the add-on if it crashes label="hardware"
</span> description=""
<ha-switch >
@change=${this._watchdogToggled} <ha-svg-icon .path=${mdiChip}></ha-svg-icon>
.checked=${this.addon.watchdog} </ha-label-badge>
haptic `
></ha-switch> : ""}
</ha-settings-row> ${this.addon.homeassistant_api
` ? html`
: ""} <ha-label-badge
${this.addon.auto_update || @click=${this._showMoreInfo}
this.hass.userData?.showAdvanced id="homeassistant_api"
? html` label="hass"
<ha-settings-row ?three-line=${this.narrow}> description=""
<span slot="heading"> >
Auto update <ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
</span> </ha-label-badge>
<span slot="description"> `
Auto update the add-on when there is a new : ""}
version available ${this._computeHassioApi
</span> ? html`
<ha-switch <ha-label-badge
@change=${this._autoUpdateToggled} @click=${this._showMoreInfo}
.checked=${this.addon.auto_update} id="hassio_api"
haptic label="hassio"
></ha-switch> .description=${this.addon.hassio_role}
</ha-settings-row> >
` <ha-svg-icon .path=${mdiHomeAssistant}></ha-svg-icon>
: ""} </ha-label-badge>
${this.addon.ingress `
? html` : ""}
<ha-settings-row ?three-line=${this.narrow}> ${this.addon.docker_api
<span slot="heading"> ? html`
Show in sidebar <ha-label-badge
</span> @click=${this._showMoreInfo}
<span slot="description"> id="docker_api"
${this._computeCannotIngressSidebar label="docker"
? "This option requires Home Assistant 0.92 or later." description=""
: "Add this add-on to your sidebar"} >
</span> <ha-svg-icon .path=${mdiDocker}></ha-svg-icon>
<ha-switch </ha-label-badge>
@change=${this._panelToggled} `
.checked=${this.addon.ingress_panel} : ""}
.disabled=${this._computeCannotIngressSidebar} ${this.addon.host_pid
haptic ? html`
></ha-switch> <ha-label-badge
</ha-settings-row> @click=${this._showMoreInfo}
` id="host_pid"
: ""} label="host pid"
${this._computeUsesProtectedOptions description=""
? html` >
<ha-settings-row ?three-line=${this.narrow}> <ha-svg-icon .path=${mdiPound}></ha-svg-icon>
<span slot="heading"> </ha-label-badge>
Protection mode `
</span> : ""}
<span slot="description"> ${this.addon.apparmor
Blocks elevated system access from the add-on ? html`
</span> <ha-label-badge
<ha-switch @click=${this._showMoreInfo}
@change=${this._protectionToggled} class=${this._computeApparmorClassName}
.checked=${this.addon.protected} id="apparmor"
haptic label="apparmor"
></ha-switch> description=""
</ha-settings-row> >
` <ha-svg-icon .path=${mdiShield}></ha-svg-icon>
: ""} </ha-label-badge>
</div> `
` : ""}
: ""} ${this.addon.auth_api
</div> ? html`
<div> <ha-label-badge
${this.addon.state === "started" @click=${this._showMoreInfo}
? html`<ha-settings-row ?three-line=${this.narrow}> id="auth_api"
<span slot="heading"> label="auth"
Hostname description=""
</span> >
<code slot="description"> <ha-svg-icon .path=${mdiKey}></ha-svg-icon>
${this.addon.hostname} </ha-label-badge>
</code> `
</ha-settings-row> : ""}
${metrics.map( ${this.addon.ingress
(metric) => ? html`
html` <ha-label-badge
<supervisor-metric @click=${this._showMoreInfo}
.description=${metric.description} id="ingress"
.value=${metric.value ?? 0} label="ingress"
.tooltip=${metric.tooltip} description=""
></supervisor-metric> >
` <ha-svg-icon
)}` .path=${mdiCursorDefaultClickOutline}
: ""} ></ha-svg-icon>
</div> </ha-label-badge>
`
: ""}
</div> </div>
${this.addon.version
? html`
<div class="addon-options">
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Start on boot
</span>
<span slot="description">
Make the add-on start during a system boot
</span>
<ha-switch
@change=${this._startOnBootToggled}
.checked=${this.addon.boot === "auto"}
haptic
></ha-switch>
</ha-settings-row>
${this.addon.startup !== "once"
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Watchdog
</span>
<span slot="description">
This will start the add-on if it crashes
</span>
<ha-switch
@change=${this._watchdogToggled}
.checked=${this.addon.watchdog}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
${this.addon.auto_update || this.hass.userData?.showAdvanced
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Auto update
</span>
<span slot="description">
Auto update the add-on when there is a new version
available
</span>
<ha-switch
@change=${this._autoUpdateToggled}
.checked=${this.addon.auto_update}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
${this.addon.ingress
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Show in sidebar
</span>
<span slot="description">
${this._computeCannotIngressSidebar
? "This option requires Home Assistant 0.92 or later."
: "Add this add-on to your sidebar"}
</span>
<ha-switch
@change=${this._panelToggled}
.checked=${this.addon.ingress_panel}
.disabled=${this._computeCannotIngressSidebar}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
${this._computeUsesProtectedOptions
? html`
<ha-settings-row ?three-line=${this.narrow}>
<span slot="heading">
Protection mode
</span>
<span slot="description">
Blocks elevated system access from the add-on
</span>
<ha-switch
@change=${this._protectionToggled}
.checked=${this.addon.protected}
haptic
></ha-switch>
</ha-settings-row>
`
: ""}
</div>
`
: ""}
${this._error ? html` <div class="errors">${this._error}</div> ` : ""} ${this._error ? html` <div class="errors">${this._error}</div> ` : ""}
</div> </div>
<div class="card-actions"> <div class="card-actions">
@@ -632,22 +579,6 @@ class HassioAddonInfo extends LitElement {
`; `;
} }
protected updated(changedProps) {
super.updated(changedProps);
if (changedProps.has("addon")) {
this._loadData();
}
}
private async _loadData(): Promise<void> {
if (this.addon.state === "started") {
this._metrics = await fetchHassioStats(
this.hass,
`addons/${this.addon.slug}`
);
}
}
private get _computeHassioApi(): boolean { private get _computeHassioApi(): boolean {
return ( return (
this.addon.hassio_api && this.addon.hassio_api &&
@@ -1057,26 +988,10 @@ class HassioAddonInfo extends LitElement {
.addon-options { .addon-options {
max-width: 50%; max-width: 50%;
} }
.addon-options.started {
max-width: 90%;
}
.addon-container {
display: grid;
grid-auto-flow: column;
grid-template-columns: 1fr auto;
}
.addon-container div:last-of-type {
align-self: end;
}
@media (max-width: 720px) { @media (max-width: 720px) {
.addon-options { .addon-options {
max-width: 100%; max-width: 100%;
} }
.addon-container {
display: block;
}
} }
`, `,
]; ];

View File

@@ -7,8 +7,8 @@ import {
property, property,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import "../../../../src/components/ha-circular-progress";
import { HassioAddonDetails } from "../../../../src/data/hassio/addon"; import { HassioAddonDetails } from "../../../../src/data/hassio/addon";
import "../../../../src/components/ha-circular-progress";
import { haStyle } from "../../../../src/resources/styles"; import { haStyle } from "../../../../src/resources/styles";
import { HomeAssistant } from "../../../../src/types"; import { HomeAssistant } from "../../../../src/types";
import { hassioStyle } from "../../resources/hassio-style"; import { hassioStyle } from "../../resources/hassio-style";

View File

@@ -1,87 +0,0 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import "../../../src/components/ha-bar";
import "../../../src/components/ha-settings-row";
import { roundWithOneDecimal } from "../../../src/util/calculate";
@customElement("supervisor-metric")
class SupervisorMetric extends LitElement {
@property({ type: Number }) public value!: number;
@property({ type: String }) public description!: string;
@property({ type: String }) public tooltip?: string;
protected render(): TemplateResult {
const roundedValue = roundWithOneDecimal(this.value);
return html`<ha-settings-row>
<span slot="heading">
${this.description}
</span>
<div slot="description" title="${this.tooltip ?? ""}">
<span class="value">
${roundedValue}%
</span>
<ha-bar
class="${classMap({
"target-warning": roundedValue > 50,
"target-critical": roundedValue > 85,
})}"
.value=${this.value}
></ha-bar>
</div>
</ha-settings-row>`;
}
static get styles(): CSSResult {
return css`
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row > div[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
display: flex;
justify-content: space-between;
}
ha-bar {
--ha-bar-primary-color: var(
--hassio-bar-ok-color,
var(--success-color)
);
}
.target-warning {
--ha-bar-primary-color: var(
--hassio-bar-warning-color,
var(--warning-color)
);
}
.target-critical {
--ha-bar-primary-color: var(
--hassio-bar-critical-color,
var(--error-color)
);
}
.value {
width: 42px;
padding-right: 4px;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"supervisor-metric": SupervisorMetric;
}
}

View File

@@ -74,7 +74,9 @@ export class HassioUpdate extends LitElement {
"Supervisor", "Supervisor",
this.supervisor.supervisor, this.supervisor.supervisor,
"hassio/supervisor/update", "hassio/supervisor/update",
`https://github.com//home-assistant/hassio/releases/tag/${this.supervisor.supervisor.version_latest}` `https://github.com//home-assistant/hassio/releases/tag/${
this.supervisor.supervisor.version_latest
}`
)} )}
${this.supervisor.host.features.includes("hassos") ${this.supervisor.host.features.includes("hassos")
? this._renderUpdateCard( ? this._renderUpdateCard(

View File

@@ -3,9 +3,9 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { createCloseHeading } from "../../../../src/components/ha-dialog"; import { createCloseHeading } from "../../../../src/components/ha-dialog";

View File

@@ -1,7 +1,6 @@
// Compat needs to be first import
import "../../src/resources/compatibility"; import "../../src/resources/compatibility";
import "../../src/resources/roboto";
import "../../src/resources/safari-14-attachshadow-patch"; import "../../src/resources/safari-14-attachshadow-patch";
import "../../src/resources/roboto";
import "./hassio-main"; import "./hassio-main";
const styleEl = document.createElement("style"); const styleEl = document.createElement("style");

View File

@@ -1,11 +1,11 @@
import { customElement, html, property, PropertyValues } from "lit-element"; import { html, PropertyValues, customElement, property } from "lit-element";
import { atLeastVersion } from "../../src/common/config/version"; import "./hassio-router";
import { HomeAssistant, Route } from "../../src/types";
import { HassioPanelInfo } from "../../src/data/hassio/supervisor";
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 { HassioPanelInfo } from "../../src/data/hassio/supervisor";
import { makeDialogManager } from "../../src/dialogs/make-dialog-manager"; import { makeDialogManager } from "../../src/dialogs/make-dialog-manager";
import { HomeAssistant, Route } from "../../src/types"; import { atLeastVersion } from "../../src/common/config/version";
import "./hassio-router";
import { SupervisorBaseElement } from "./supervisor-base-element"; import { SupervisorBaseElement } from "./supervisor-base-element";
@customElement("hassio-main") @customElement("hassio-main")

View File

@@ -1,17 +1,14 @@
import { mdiMenu } from "@mdi/js";
import { import {
css, css,
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { fireEvent } from "../../../src/common/dom/fire_event";
import { navigate } from "../../../src/common/navigate";
import { import {
fetchHassioAddonInfo, fetchHassioAddonInfo,
HassioAddonDetails, HassioAddonDetails,
@@ -20,10 +17,13 @@ import {
createHassioSession, createHassioSession,
validateHassioSession, validateHassioSession,
} from "../../../src/data/hassio/ingress"; } from "../../../src/data/hassio/ingress";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-loading-screen"; import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-subpage"; import "../../../src/layouts/hass-subpage";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import { navigate } from "../../../src/common/navigate";
import { mdiMenu } from "@mdi/js";
import { fireEvent } from "../../../src/common/dom/fire_event";
@customElement("hassio-ingress-view") @customElement("hassio-ingress-view")
class HassioIngressView extends LitElement { class HassioIngressView extends LitElement {

View File

@@ -264,7 +264,7 @@ class HassioSnapshots extends LitElement {
} }
protected updated(changedProps: PropertyValues) { protected updated(changedProps: PropertyValues) {
if (changedProps.has("supervisor")) { if (changedProps.has("supervisorInfo")) {
this._addonList = this.supervisor.supervisor.addons this._addonList = this.supervisor.supervisor.addons
.map((addon) => ({ .map((addon) => ({
slug: addon.slug, slug: addon.slug,

View File

@@ -1,246 +0,0 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row";
import {
extractApiErrorMessage,
fetchHassioStats,
HassioStats,
} from "../../../src/data/hassio/common";
import { restartCore, updateCore } from "../../../src/data/supervisor/core";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
import { bytesToString } from "../../../src/util/bytes-to-string";
import "../components/supervisor-metric";
import { hassioStyle } from "../resources/hassio-style";
@customElement("hassio-core-info")
class HassioCoreInfo extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _metrics?: HassioStats;
protected render(): TemplateResult | void {
const metrics = [
{
description: "Core CPU Usage",
value: this._metrics?.cpu_percent,
},
{
description: "Core RAM Usage",
value: this._metrics?.memory_percent,
tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
this._metrics?.memory_limit
)}`,
},
];
return html`
<ha-card header="Core">
<div class="card-content">
<div>
<ha-settings-row>
<span slot="heading">
Version
</span>
<span slot="description">
core-${this.supervisor.core.version}
</span>
</ha-settings-row>
<ha-settings-row>
<span slot="heading">
Newest Version
</span>
<span slot="description">
core-${this.supervisor.core.version_latest}
</span>
${this.supervisor.core.update_available
? html`
<ha-progress-button
title="Update the core"
@click=${this._coreUpdate}
>
Update
</ha-progress-button>
`
: ""}
</ha-settings-row>
</div>
<div>
${metrics.map(
(metric) =>
html`
<supervisor-metric
.description=${metric.description}
.value=${metric.value ?? 0}
.tooltip=${metric.tooltip}
></supervisor-metric>
`
)}
</div>
</div>
<div class="card-actions">
<ha-progress-button
slot="primaryAction"
class="warning"
@click=${this._coreRestart}
title="Restart Home Assistant Core"
>
Restart Core
</ha-progress-button>
</div>
</ha-card>
`;
}
protected firstUpdated(): void {
this._loadData();
}
private async _loadData(): Promise<void> {
this._metrics = await fetchHassioStats(this.hass, "core");
}
private async _coreRestart(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any;
button.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: "Restart Home Assistant Core",
text: "Are you sure you want to restart Home Assistant Core",
confirmText: "restart",
dismissText: "cancel",
});
if (!confirmed) {
button.progress = false;
return;
}
try {
await restartCore(this.hass);
} catch (err) {
showAlertDialog(this, {
title: "Failed to restart Home Assistant Core",
text: extractApiErrorMessage(err),
});
} finally {
button.progress = false;
}
}
private async _coreUpdate(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any;
button.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: "Update Home Assistant Core",
text: `Are you sure you want to update Home Assistant Core to version ${this.supervisor.core.version_latest}?`,
confirmText: "update",
dismissText: "cancel",
});
if (!confirmed) {
button.progress = false;
return;
}
try {
await updateCore(this.hass);
} catch (err) {
showAlertDialog(this, {
title: "Failed to update Home Assistant Core",
text: extractApiErrorMessage(err),
});
} finally {
button.progress = false;
}
}
static get styles(): CSSResult[] {
return [
haStyle,
hassioStyle,
css`
ha-card {
height: 100%;
justify-content: space-between;
flex-direction: column;
display: flex;
}
.card-actions {
height: 48px;
border-top: none;
display: flex;
justify-content: flex-end;
align-items: center;
}
.card-content {
display: flex;
flex-direction: column;
height: calc(100% - 124px);
justify-content: space-between;
}
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row[three-line] {
height: 74px;
}
ha-settings-row > span[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
}
.warning {
--mdc-theme-primary: var(--error-color);
}
ha-button-menu {
color: var(--secondary-text-color);
--mdc-menu-min-width: 200px;
}
@media (min-width: 563px) {
paper-listbox {
max-height: 150px;
overflow: auto;
}
}
paper-item {
cursor: pointer;
min-height: 35px;
}
mwc-list-item ha-svg-icon {
color: var(--secondary-text-color);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"hassio-core-info": HassioCoreInfo;
}
}

View File

@@ -43,11 +43,6 @@ import {
} from "../../../src/dialogs/generic/show-dialog-box"; } from "../../../src/dialogs/generic/show-dialog-box";
import { haStyle } from "../../../src/resources/styles"; import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types"; import { HomeAssistant } from "../../../src/types";
import {
getValueInPercentage,
roundWithOneDecimal,
} from "../../../src/util/calculate";
import "../components/supervisor-metric";
import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown"; import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown";
import { showNetworkDialog } from "../dialogs/network/show-dialog-network"; import { showNetworkDialog } from "../dialogs/network/show-dialog-network";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
@@ -62,105 +57,80 @@ class HassioHostInfo extends LitElement {
const primaryIpAddress = this.supervisor.host.features.includes("network") const primaryIpAddress = this.supervisor.host.features.includes("network")
? this._primaryIpAddress(this.supervisor.network!) ? this._primaryIpAddress(this.supervisor.network!)
: ""; : "";
const metrics = [
{
description: "Used Space",
value: this._getUsedSpace(
this.supervisor.host.disk_used,
this.supervisor.host.disk_total
),
tooltip: `${this.supervisor.host.disk_used} GB/${this.supervisor.host.disk_total} GB`,
},
];
return html` return html`
<ha-card header="Host"> <ha-card header="Host System">
<div class="card-content"> <div class="card-content">
<div> ${this.supervisor.host.features.includes("hostname")
${this.supervisor.host.features.includes("hostname") ? html`<ha-settings-row>
? html`<ha-settings-row> <span slot="heading">
<span slot="heading"> Hostname
Hostname </span>
</span> <span slot="description">
<span slot="description"> ${this.supervisor.host.hostname}
${this.supervisor.host.hostname} </span>
</span> <mwc-button
<mwc-button title="Change the hostname"
title="Change the hostname" label="Change"
label="Change" @click=${this._changeHostnameClicked}
@click=${this._changeHostnameClicked} >
> </mwc-button>
</mwc-button> </ha-settings-row>`
</ha-settings-row>` : ""}
: ""} ${this.supervisor.host.features.includes("network")
${this.supervisor.host.features.includes("network") ? html` <ha-settings-row>
? html` <ha-settings-row> <span slot="heading">
<span slot="heading"> IP Address
IP Address </span>
</span> <span slot="description">
<span slot="description"> ${primaryIpAddress}
${primaryIpAddress} </span>
</span> <mwc-button
<mwc-button title="Change the network"
title="Change the network" label="Change"
label="Change" @click=${this._changeNetworkClicked}
@click=${this._changeNetworkClicked} >
> </mwc-button>
</mwc-button> </ha-settings-row>`
</ha-settings-row>` : ""}
: ""}
<ha-settings-row> <ha-settings-row>
<span slot="heading"> <span slot="heading">
Operating System Operating System
</span> </span>
<span slot="description"> <span slot="description">
${this.supervisor.host.operating_system} ${this.supervisor.host.operating_system}
</span> </span>
${this.supervisor.os.update_available ${this.supervisor.os.update_available
? html` ? html`
<ha-progress-button <ha-progress-button
title="Update the host OS" title="Update the host OS"
@click=${this._osUpdate} @click=${this._osUpdate}
> >
Update Update
</ha-progress-button> </ha-progress-button>
`
: ""}
</ha-settings-row>
${!this.supervisor.host.features.includes("hassos")
? html`<ha-settings-row>
<span slot="heading">
Docker version
</span>
<span slot="description">
${this.supervisor.info.docker}
</span>
</ha-settings-row>`
: ""}
${this.supervisor.host.deployment
? html`<ha-settings-row>
<span slot="heading">
Deployment
</span>
<span slot="description">
${this.supervisor.host.deployment}
</span>
</ha-settings-row>`
: ""}
</div>
<div>
${metrics.map(
(metric) =>
html`
<supervisor-metric
.description=${metric.description}
.value=${metric.value ?? 0}
.tooltip=${metric.tooltip}
></supervisor-metric>
` `
)} : ""}
</div> </ha-settings-row>
${!this.supervisor.host.features.includes("hassos")
? html`<ha-settings-row>
<span slot="heading">
Docker version
</span>
<span slot="description">
${this.supervisor.info.docker}
</span>
</ha-settings-row>`
: ""}
${this.supervisor.host.deployment
? html`<ha-settings-row>
<span slot="heading">
Deployment
</span>
<span slot="description">
${this.supervisor.host.deployment}
</span>
</ha-settings-row>`
: ""}
</div> </div>
<div class="card-actions"> <div class="card-actions">
${this.supervisor.host.features.includes("reboot") ${this.supervisor.host.features.includes("reboot")
@@ -170,7 +140,7 @@ class HassioHostInfo extends LitElement {
class="warning" class="warning"
@click=${this._hostReboot} @click=${this._hostReboot}
> >
Reboot Host Reboot
</ha-progress-button> </ha-progress-button>
` `
: ""} : ""}
@@ -181,7 +151,7 @@ class HassioHostInfo extends LitElement {
class="warning" class="warning"
@click=${this._hostShutdown} @click=${this._hostShutdown}
> >
Shutdown Host Shutdown
</ha-progress-button> </ha-progress-button>
` `
: ""} : ""}
@@ -213,10 +183,6 @@ class HassioHostInfo extends LitElement {
this._loadData(); this._loadData();
} }
private _getUsedSpace = memoizeOne((used: number, total: number) =>
roundWithOneDecimal(getValueInPercentage(used, 0, total))
);
private _primaryIpAddress = memoizeOne((network_info: NetworkInfo) => { private _primaryIpAddress = memoizeOne((network_info: NetworkInfo) => {
if (!network_info || !network_info.interfaces) { if (!network_info || !network_info.interfaces) {
return ""; return "";
@@ -403,12 +369,6 @@ class HassioHostInfo extends LitElement {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.card-content {
display: flex;
flex-direction: column;
height: calc(100% - 124px);
justify-content: space-between;
}
ha-settings-row { ha-settings-row {
padding: 0; padding: 0;
height: 54px; height: 54px;

View File

@@ -3,7 +3,6 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
TemplateResult, TemplateResult,
@@ -13,11 +12,7 @@ import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row"; import "../../../src/components/ha-settings-row";
import "../../../src/components/ha-switch"; import "../../../src/components/ha-switch";
import { import { extractApiErrorMessage } from "../../../src/data/hassio/common";
extractApiErrorMessage,
fetchHassioStats,
HassioStats,
} from "../../../src/data/hassio/common";
import { import {
fetchHassioSupervisorInfo, fetchHassioSupervisorInfo,
reloadSupervisor, reloadSupervisor,
@@ -33,9 +28,7 @@ import {
} from "../../../src/dialogs/generic/show-dialog-box"; } from "../../../src/dialogs/generic/show-dialog-box";
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 { documentationUrl } from "../../../src/util/documentation-url"; import { documentationUrl } from "../../../src/util/documentation-url";
import "../components/supervisor-metric";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
const UNSUPPORTED_REASON = { const UNSUPPORTED_REASON = {
@@ -94,164 +87,127 @@ class HassioSupervisorInfo extends LitElement {
@property({ attribute: false }) public supervisor!: Supervisor; @property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _metrics?: HassioStats;
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
const metrics = [
{
description: "Supervisor CPU Usage",
value: this._metrics?.cpu_percent,
},
{
description: "Supervisor RAM Usage",
value: this._metrics?.memory_percent,
tooltip: `${bytesToString(this._metrics?.memory_usage)}/${bytesToString(
this._metrics?.memory_limit
)}`,
},
];
return html` return html`
<ha-card header="Supervisor"> <ha-card header="Supervisor">
<div class="card-content"> <div class="card-content">
<div> <ha-settings-row>
<ha-settings-row> <span slot="heading">
<span slot="heading"> Version
Version </span>
</span> <span slot="description">
<span slot="description"> ${this.supervisor.supervisor.version}
supervisor-${this.supervisor.supervisor.version} </span>
</span> </ha-settings-row>
</ha-settings-row> <ha-settings-row>
<ha-settings-row> <span slot="heading">
<span slot="heading"> Newest Version
Newest Version </span>
</span> <span slot="description">
<span slot="description"> ${this.supervisor.supervisor.version_latest}
supervisor-${this.supervisor.supervisor.version_latest} </span>
</span> ${this.supervisor.supervisor.update_available
${this.supervisor.supervisor.update_available ? html`
? html` <ha-progress-button
<ha-progress-button title="Update the supervisor"
title="Update the supervisor" @click=${this._supervisorUpdate}
@click=${this._supervisorUpdate}
>
Update
</ha-progress-button>
`
: ""}
</ha-settings-row>
<ha-settings-row>
<span slot="heading">
Channel
</span>
<span slot="description">
${this.supervisor.supervisor.channel}
</span>
${this.supervisor.supervisor.channel === "beta"
? html`
<ha-progress-button
@click=${this._toggleBeta}
title="Get stable updates for Home Assistant, supervisor and host"
>
Leave beta channel
</ha-progress-button>
`
: this.supervisor.supervisor.channel === "stable"
? html`
<ha-progress-button
@click=${this._toggleBeta}
title="Get beta updates for Home Assistant (RCs), supervisor and host"
>
Join beta channel
</ha-progress-button>
`
: ""}
</ha-settings-row>
${this.supervisor.supervisor.supported
? html` <ha-settings-row three-line>
<span slot="heading">
Share Diagnostics
</span>
<div slot="description" class="diagnostics-description">
Share crash reports and diagnostic information.
<button
class="link"
title="Show more information about this"
@click=${this._diagnosticsInformationDialog}
>
Learn more
</button>
</div>
<ha-switch
haptic
.checked=${this.supervisor.supervisor.diagnostics}
@change=${this._toggleDiagnostics}
></ha-switch>
</ha-settings-row>`
: html`<div class="error">
You are running an unsupported installation.
<button
class="link"
title="Learn more about how you can make your system compliant"
@click=${this._unsupportedDialog}
> >
Learn more Update
</button> </ha-progress-button>
</div>`}
${!this.supervisor.supervisor.healthy
? html`<div class="error">
Your installation is running in an unhealthy state.
<button
class="link"
title="Learn more about why your system is marked as unhealthy"
@click=${this._unhealthyDialog}
>
Learn more
</button>
</div>`
: ""}
</div>
<div class="metrics-block">
${metrics.map(
(metric) =>
html`
<supervisor-metric
.description=${metric.description}
.value=${metric.value ?? 0}
.tooltip=${metric.tooltip}
></supervisor-metric>
` `
)} : ""}
</div> </ha-settings-row>
<ha-settings-row>
<span slot="heading">
Channel
</span>
<span slot="description">
${this.supervisor.supervisor.channel}
</span>
${this.supervisor.supervisor.channel === "beta"
? html`
<ha-progress-button
@click=${this._toggleBeta}
title="Get stable updates for Home Assistant, supervisor and host"
>
Leave beta channel
</ha-progress-button>
`
: this.supervisor.supervisor.channel === "stable"
? html`
<ha-progress-button
@click=${this._toggleBeta}
title="Get beta updates for Home Assistant (RCs), supervisor and host"
>
Join beta channel
</ha-progress-button>
`
: ""}
</ha-settings-row>
${this.supervisor.supervisor.supported
? html` <ha-settings-row three-line>
<span slot="heading">
Share Diagnostics
</span>
<div slot="description" class="diagnostics-description">
Share crash reports and diagnostic information.
<button
class="link"
title="Show more information about this"
@click=${this._diagnosticsInformationDialog}
>
Learn more
</button>
</div>
<ha-switch
haptic
.checked=${this.supervisor.supervisor.diagnostics}
@change=${this._toggleDiagnostics}
></ha-switch>
</ha-settings-row>`
: html`<div class="error">
You are running an unsupported installation.
<button
class="link"
title="Learn more about how you can make your system compliant"
@click=${this._unsupportedDialog}
>
Learn more
</button>
</div>`}
${!this.supervisor.supervisor.healthy
? html`<div class="error">
Your installtion is running in an unhealthy state.
<button
class="link"
title="Learn more about why your system is marked as unhealthy"
@click=${this._unhealthyDialog}
>
Learn more
</button>
</div>`
: ""}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<ha-progress-button <ha-progress-button
@click=${this._supervisorReload} @click=${this._supervisorReload}
title="Reload parts of the Supervisor" title="Reload parts of the Supervisor"
> >
Reload Supervisor Reload
</ha-progress-button> </ha-progress-button>
<ha-progress-button <ha-progress-button
class="warning" class="warning"
@click=${this._supervisorRestart} @click=${this._supervisorRestart}
title="Restart the Supervisor" title="Restart the Supervisor"
> >
Restart Supervisor Restart
</ha-progress-button> </ha-progress-button>
</div> </div>
</ha-card> </ha-card>
`; `;
} }
protected firstUpdated(): void {
this._loadData();
}
private async _loadData(): Promise<void> {
this._metrics = await fetchHassioStats(this.hass, "supervisor");
}
private async _toggleBeta(ev: CustomEvent): Promise<void> { private async _toggleBeta(ev: CustomEvent): Promise<void> {
const button = ev.currentTarget as any; const button = ev.currentTarget as any;
button.progress = true; button.progress = true;
@@ -326,18 +282,6 @@ class HassioSupervisorInfo extends LitElement {
const button = ev.currentTarget as any; const button = ev.currentTarget as any;
button.progress = true; button.progress = true;
const confirmed = await showConfirmationDialog(this, {
title: "Restart the Supervisor",
text: "Are you sure you want to restart the Supervisor",
confirmText: "restart",
dismissText: "cancel",
});
if (!confirmed) {
button.progress = false;
return;
}
try { try {
await restartSupervisor(this.hass); await restartSupervisor(this.hass);
} catch (err) { } catch (err) {
@@ -482,15 +426,6 @@ class HassioSupervisorInfo extends LitElement {
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
} }
.card-content {
display: flex;
flex-direction: column;
height: calc(100% - 124px);
justify-content: space-between;
}
.metrics-block {
margin-top: 16px;
}
button.link { button.link {
color: var(--primary-color); color: var(--primary-color);
} }

View File

@@ -0,0 +1,185 @@
import "@material/mwc-button";
import "@material/mwc-list/mwc-list-item";
import {
css,
CSSResult,
customElement,
html,
internalProperty,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { classMap } from "lit-html/directives/class-map";
import memoizeOne from "memoize-one";
import "../../../src/components/buttons/ha-progress-button";
import "../../../src/components/ha-bar";
import "../../../src/components/ha-button-menu";
import "../../../src/components/ha-card";
import "../../../src/components/ha-settings-row";
import { fetchHassioStats, HassioStats } from "../../../src/data/hassio/common";
import { HassioHostInfo } from "../../../src/data/hassio/host";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant } from "../../../src/types";
import { bytesToString } from "../../../src/util/bytes-to-string";
import {
getValueInPercentage,
roundWithOneDecimal,
} from "../../../src/util/calculate";
import { hassioStyle } from "../resources/hassio-style";
@customElement("hassio-system-metrics")
class HassioSystemMetrics extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public supervisor!: Supervisor;
@internalProperty() private _supervisorMetrics?: HassioStats;
@internalProperty() private _coreMetrics?: HassioStats;
protected render(): TemplateResult | void {
const metrics = [
{
description: "Core CPU Usage",
value: this._coreMetrics?.cpu_percent,
},
{
description: "Core RAM Usage",
value: this._coreMetrics?.memory_percent,
tooltip: `${bytesToString(
this._coreMetrics?.memory_usage
)}/${bytesToString(this._coreMetrics?.memory_limit)}`,
},
{
description: "Supervisor CPU Usage",
value: this._supervisorMetrics?.cpu_percent,
},
{
description: "Supervisor RAM Usage",
value: this._supervisorMetrics?.memory_percent,
tooltip: `${bytesToString(
this._supervisorMetrics?.memory_usage
)}/${bytesToString(this._supervisorMetrics?.memory_limit)}`,
},
{
description: "Used Space",
value: this._getUsedSpace(this.supervisor.host),
tooltip: `${this.supervisor.host.disk_used} GB/${this.supervisor.host.disk_total} GB`,
},
];
return html`
<ha-card header="System Metrics">
<div class="card-content">
${metrics.map((metric) =>
this._renderMetric(
metric.description,
metric.value ?? 0,
metric.tooltip
)
)}
</div>
</ha-card>
`;
}
protected firstUpdated(): void {
this._loadData();
}
private _renderMetric(
description: string,
value: number,
tooltip?: string
): TemplateResult {
const roundedValue = roundWithOneDecimal(value);
return html`<ha-settings-row>
<span slot="heading">
${description}
</span>
<div slot="description" title="${tooltip ?? ""}">
<span class="value">
${roundedValue}%
</span>
<ha-bar
class="${classMap({
"target-warning": roundedValue > 50,
"target-critical": roundedValue > 85,
})}"
.value=${value}
></ha-bar>
</div>
</ha-settings-row>`;
}
private _getUsedSpace = memoizeOne((hostInfo: HassioHostInfo) =>
roundWithOneDecimal(
getValueInPercentage(hostInfo.disk_used, 0, hostInfo.disk_total)
)
);
private async _loadData(): Promise<void> {
const [supervisor, core] = await Promise.all([
fetchHassioStats(this.hass, "supervisor"),
fetchHassioStats(this.hass, "core"),
]);
this._supervisorMetrics = supervisor;
this._coreMetrics = core;
}
static get styles(): CSSResult[] {
return [
haStyle,
hassioStyle,
css`
ha-card {
height: 100%;
justify-content: space-between;
flex-direction: column;
display: flex;
}
ha-settings-row {
padding: 0;
height: 54px;
width: 100%;
}
ha-settings-row > div[slot="description"] {
white-space: normal;
color: var(--secondary-text-color);
display: flex;
justify-content: space-between;
}
ha-bar {
--ha-bar-primary-color: var(
--hassio-bar-ok-color,
var(--success-color)
);
}
.target-warning {
--ha-bar-primary-color: var(
--hassio-bar-warning-color,
var(--warning-color)
);
}
.target-critical {
--ha-bar-primary-color: var(
--hassio-bar-critical-color,
var(--error-color)
);
}
.value {
width: 42px;
padding-right: 4px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"hassio-system-metrics": HassioSystemMetrics;
}
}

View File

@@ -13,10 +13,10 @@ import { haStyle } from "../../../src/resources/styles";
import { HomeAssistant, Route } from "../../../src/types"; import { HomeAssistant, Route } from "../../../src/types";
import { supervisorTabs } from "../hassio-tabs"; import { supervisorTabs } from "../hassio-tabs";
import { hassioStyle } from "../resources/hassio-style"; import { hassioStyle } from "../resources/hassio-style";
import "./hassio-core-info";
import "./hassio-host-info"; import "./hassio-host-info";
import "./hassio-supervisor-info"; import "./hassio-supervisor-info";
import "./hassio-supervisor-log"; import "./hassio-supervisor-log";
import "./hassio-system-metrics";
@customElement("hassio-system") @customElement("hassio-system")
class HassioSystem extends LitElement { class HassioSystem extends LitElement {
@@ -41,10 +41,6 @@ class HassioSystem extends LitElement {
<span slot="header">System</span> <span slot="header">System</span>
<div class="content"> <div class="content">
<div class="card-group"> <div class="card-group">
<hassio-core-info
.hass=${this.hass}
.supervisor=${this.supervisor}
></hassio-core-info>
<hassio-supervisor-info <hassio-supervisor-info
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor} .supervisor=${this.supervisor}
@@ -53,6 +49,10 @@ class HassioSystem extends LitElement {
.hass=${this.hass} .hass=${this.hass}
.supervisor=${this.supervisor} .supervisor=${this.supervisor}
></hassio-host-info> ></hassio-host-info>
<hassio-system-metrics
.hass=${this.hass}
.supervisor=${this.supervisor}
></hassio-system-metrics>
</div> </div>
<hassio-supervisor-log .hass=${this.hass}></hassio-supervisor-log> <hassio-supervisor-log .hass=${this.hass}></hassio-supervisor-log>
</div> </div>

View File

@@ -29,22 +29,22 @@
"@fullcalendar/daygrid": "5.1.0", "@fullcalendar/daygrid": "5.1.0",
"@fullcalendar/interaction": "5.1.0", "@fullcalendar/interaction": "5.1.0",
"@fullcalendar/list": "5.1.0", "@fullcalendar/list": "5.1.0",
"@material/chips": "=9.0.0-canary.1c156d69d.0", "@material/chips": "=8.0.0-canary.774dcfc8e.0",
"@material/mwc-button": "^0.20.0", "@material/mwc-button": "^0.19.0",
"@material/mwc-checkbox": "^0.20.0", "@material/mwc-checkbox": "^0.19.0",
"@material/mwc-circular-progress": "^0.20.0", "@material/mwc-circular-progress": "^0.19.0",
"@material/mwc-dialog": "^0.20.0", "@material/mwc-dialog": "^0.19.0",
"@material/mwc-fab": "^0.20.0", "@material/mwc-fab": "^0.19.0",
"@material/mwc-formfield": "^0.20.0", "@material/mwc-formfield": "^0.19.0",
"@material/mwc-icon-button": "^0.20.0", "@material/mwc-icon-button": "^0.19.0",
"@material/mwc-list": "^0.20.0", "@material/mwc-list": "^0.19.0",
"@material/mwc-menu": "^0.20.0", "@material/mwc-menu": "^0.19.0",
"@material/mwc-radio": "^0.20.0", "@material/mwc-radio": "^0.19.0",
"@material/mwc-ripple": "^0.20.0", "@material/mwc-ripple": "^0.19.0",
"@material/mwc-switch": "^0.20.0", "@material/mwc-switch": "^0.19.0",
"@material/mwc-tab": "^0.20.0", "@material/mwc-tab": "^0.19.0",
"@material/mwc-tab-bar": "^0.20.0", "@material/mwc-tab-bar": "^0.19.0",
"@material/top-app-bar": "=9.0.0-canary.1c156d69d.0", "@material/top-app-bar": "=8.0.0-canary.774dcfc8e.0",
"@mdi/js": "5.6.55", "@mdi/js": "5.6.55",
"@mdi/svg": "5.6.55", "@mdi/svg": "5.6.55",
"@polymer/app-layout": "^3.0.2", "@polymer/app-layout": "^3.0.2",
@@ -120,7 +120,7 @@
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"roboto-fontface": "^0.10.0", "roboto-fontface": "^0.10.0",
"sortablejs": "^1.10.2", "sortablejs": "^1.10.2",
"superstruct": "^0.10.13", "superstruct": "^0.10.12",
"tinykeys": "^1.1.1", "tinykeys": "^1.1.1",
"unfetch": "^4.1.0", "unfetch": "^4.1.0",
"vis-data": "^7.1.1", "vis-data": "^7.1.1",

View File

@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
setup( setup(
name="home-assistant-frontend", name="home-assistant-frontend",
version="20210127.1", version="20201126.0",
description="The Home Assistant frontend", description="The Home Assistant frontend",
url="https://github.com/home-assistant/home-assistant-polymer", url="https://github.com/home-assistant/home-assistant-polymer",
author="The Home Assistant Authors", author="The Home Assistant Authors",

View File

@@ -3,9 +3,9 @@ import {
css, css,
CSSResult, CSSResult,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";

View File

@@ -2,25 +2,21 @@ import {
css, css,
CSSResult, CSSResult,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
} from "lit-element"; } from "lit-element";
import punycode from "punycode";
import { extractSearchParamsObject } from "../common/url/search-params";
import { import {
AuthProvider, AuthProvider,
AuthUrlSearchParams,
fetchAuthProviders, fetchAuthProviders,
AuthUrlSearchParams,
} from "../data/auth"; } from "../data/auth";
import {
DiscoveryInformation,
fetchDiscoveryInformation,
} from "../data/discovery";
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin"; import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
import { registerServiceWorker } from "../util/register-service-worker"; import { registerServiceWorker } from "../util/register-service-worker";
import "./ha-auth-flow"; import "./ha-auth-flow";
import { extractSearchParamsObject } from "../common/url/search-params";
import punycode from "punycode";
import("./ha-pick-auth-provider"); import("./ha-pick-auth-provider");
@@ -35,8 +31,6 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
@internalProperty() private _authProviders?: AuthProvider[]; @internalProperty() private _authProviders?: AuthProvider[];
@internalProperty() private _discovery?: DiscoveryInformation;
constructor() { constructor() {
super(); super();
this.translationFragment = "page-authorize"; this.translationFragment = "page-authorize";
@@ -64,17 +58,14 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
// the name with a bold tag. // the name with a bold tag.
const loggingInWith = document.createElement("div"); const loggingInWith = document.createElement("div");
loggingInWith.innerText = this.localize( loggingInWith.innerText = this.localize(
this._discovery?.location_name "ui.panel.page-authorize.logging_in_with",
? "ui.panel.page-authorize.logging_in_to_with"
: "ui.panel.page-authorize.logging_in_with",
"locationName",
"LOCATION",
"authProviderName", "authProviderName",
"NAME" "NAME"
); );
loggingInWith.innerHTML = loggingInWith.innerHTML loggingInWith.innerHTML = loggingInWith.innerHTML.replace(
.replace("**LOCATION**", `<b>${this._discovery?.location_name}</b>`) "**NAME**",
.replace("**NAME**", `<b>${this._authProvider!.name}</b>`); `<b>${this._authProvider!.name}</b>`
);
const inactiveProviders = this._authProviders.filter( const inactiveProviders = this._authProviders.filter(
(prv) => prv !== this._authProvider (prv) => prv !== this._authProvider
@@ -114,7 +105,6 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
protected firstUpdated(changedProps: PropertyValues) { protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
this._fetchAuthProviders(); this._fetchAuthProviders();
this._fetchDiscoveryInfo();
if (!this.redirectUri) { if (!this.redirectUri) {
return; return;
@@ -136,10 +126,6 @@ class HaAuthorize extends litLocalizeLiteMixin(LitElement) {
} }
} }
private async _fetchDiscoveryInfo() {
this._discovery = await fetchDiscoveryInformation();
}
private async _fetchAuthProviders() { private async _fetchAuthProviders() {
// Fetch auth providers // Fetch auth providers
try { try {

View File

@@ -1,6 +1,6 @@
import { isComponentLoaded } from "./is_component_loaded";
import { PageNavigation } from "../../layouts/hass-tabs-subpage"; import { PageNavigation } from "../../layouts/hass-tabs-subpage";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { isComponentLoaded } from "./is_component_loaded";
export const canShowPage = (hass: HomeAssistant, page: PageNavigation) => { export const canShowPage = (hass: HomeAssistant, page: PageNavigation) => {
return ( return (

View File

@@ -4,4 +4,4 @@ import { HomeAssistant } from "../../types";
export const isComponentLoaded = ( export const isComponentLoaded = (
hass: HomeAssistant, hass: HomeAssistant,
component: string component: string
): boolean => hass && hass.config.components.includes(component); ): boolean => hass && hass.config.components.indexOf(component) !== -1;

View File

@@ -34,7 +34,6 @@ export const FIXED_DOMAIN_ICONS = {
light: "hass:lightbulb", light: "hass:lightbulb",
mailbox: "hass:mailbox", mailbox: "hass:mailbox",
notify: "hass:comment-alert", notify: "hass:comment-alert",
number: "hass:ray-vertex",
persistent_notification: "hass:bell", persistent_notification: "hass:bell",
person: "hass:account", person: "hass:account",
plant: "hass:flower", plant: "hass:flower",
@@ -78,7 +77,6 @@ export const DOMAINS_WITH_CARD = [
"input_text", "input_text",
"lock", "lock",
"media_player", "media_player",
"number",
"scene", "scene",
"script", "script",
"timer", "timer",
@@ -116,7 +114,6 @@ export const DOMAINS_HIDE_MORE_INFO = [
"input_number", "input_number",
"input_select", "input_select",
"input_text", "input_text",
"number",
"scene", "scene",
]; ];
@@ -141,9 +138,6 @@ export const DOMAINS_TOGGLE = new Set([
"humidifier", "humidifier",
]); ]);
/** Domains that have a dynamic entity image / picture. */
export const DOMAINS_WITH_DYNAMIC_PICTURE = new Set(["camera", "media_player"]);
/** Temperature units. */ /** Temperature units. */
export const UNIT_C = "°C"; export const UNIT_C = "°C";
export const UNIT_F = "°F"; export const UNIT_F = "°F";

View File

@@ -1,7 +0,0 @@
export default function checkValidDate(date?: Date): boolean {
if (!date) {
return false;
}
return date instanceof Date && !isNaN(date.valueOf());
}

View File

@@ -9,12 +9,3 @@ export const formatDate = toLocaleDateStringSupportsOptions
day: "numeric", day: "numeric",
}) })
: (dateObj: Date) => format(dateObj, "longDate"); : (dateObj: Date) => format(dateObj, "longDate");
export const formatDateWeekday = toLocaleDateStringSupportsOptions
? (dateObj: Date, locales: string) =>
dateObj.toLocaleDateString(locales, {
weekday: "long",
month: "short",
day: "numeric",
})
: (dateObj: Date) => format(dateObj, "dddd, MMM D");

View File

@@ -17,12 +17,3 @@ export const formatTimeWithSeconds = toLocaleTimeStringSupportsOptions
second: "2-digit", second: "2-digit",
}) })
: (dateObj: Date) => format(dateObj, "mediumTime"); : (dateObj: Date) => format(dateObj, "mediumTime");
export const formatTimeWeekday = toLocaleTimeStringSupportsOptions
? (dateObj: Date, locales: string) =>
dateObj.toLocaleTimeString(locales, {
weekday: "long",
hour: "numeric",
minute: "2-digit",
})
: (dateObj: Date) => format(dateObj, "dddd, HH:mm");

View File

@@ -1,6 +0,0 @@
export const ensureArray = (value?: any) => {
if (!value || Array.isArray(value)) {
return value;
}
return [value];
};

View File

@@ -3,9 +3,9 @@ import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
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 } from "../string/format_number";
import { LocalizeFunc } from "../translations/localize"; import { LocalizeFunc } from "../translations/localize";
import { computeStateDomain } from "./compute_state_domain"; import { computeStateDomain } from "./compute_state_domain";
import { formatNumber } from "../string/format_number";
export const computeStateDisplay = ( export const computeStateDisplay = (
localize: LocalizeFunc, localize: LocalizeFunc,
@@ -63,14 +63,10 @@ export const computeStateDisplay = (
if (domain === "humidifier") { if (domain === "humidifier") {
if (compareState === "on" && stateObj.attributes.humidity) { if (compareState === "on" && stateObj.attributes.humidity) {
return `${stateObj.attributes.humidity} %`; return `${stateObj.attributes.humidity}%`;
} }
} }
if (domain === "counter") {
return formatNumber(compareState, language);
}
return ( return (
// Return device class translation // Return device class translation
(stateObj.attributes.device_class && (stateObj.attributes.device_class &&

View File

@@ -1,9 +1,9 @@
import { HassEntity } from "home-assistant-js-websocket"; import { HassEntity } from "home-assistant-js-websocket";
import { UNAVAILABLE } from "../../data/entity";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import { DOMAINS_WITH_CARD } from "../const"; import { DOMAINS_WITH_CARD } from "../const";
import { canToggleState } from "./can_toggle_state"; import { canToggleState } from "./can_toggle_state";
import { computeStateDomain } from "./compute_state_domain"; import { computeStateDomain } from "./compute_state_domain";
import { UNAVAILABLE } from "../../data/entity";
export const stateCardType = (hass: HomeAssistant, stateObj: HassEntity) => { export const stateCardType = (hass: HomeAssistant, stateObj: HassEntity) => {
if (stateObj.state === UNAVAILABLE) { if (stateObj.state === UNAVAILABLE) {

View File

@@ -1,7 +1,7 @@
// We import the minified bundle because the unminified bundle import Vibrant from "node-vibrant/lib/browser";
// has some quirks that break wds. See #7784 for unminified version. import MMCQ from "@vibrant/quantizer-mmcq";
import Vibrant from "node-vibrant/dist/vibrant"; import { BasicPipeline } from "@vibrant/core/lib/pipeline";
import type { Swatch, Vec3 } from "@vibrant/color"; import { Swatch, Vec3 } from "@vibrant/color";
import { getRGBContrastRatio } from "../color/rgb"; import { getRGBContrastRatio } from "../color/rgb";
const CONTRAST_RATIO = 4.5; const CONTRAST_RATIO = 4.5;
@@ -104,15 +104,23 @@ const customGenerator = (colors: Swatch[]) => {
} }
return { return {
// We can't import Swatch constructor from the minified bundle, take it from background color. foreground: new Swatch(foregroundColor, 0),
// @ts-expect-error
foreground: new backgroundColor.constructor(foregroundColor, 0),
background: backgroundColor, background: backgroundColor,
}; };
}; };
// Set our custom generator as the default. Vibrant.use(
Vibrant._pipeline.generator.register("default", customGenerator); new BasicPipeline().filter
.register(
"default",
(r: number, g: number, b: number, a: number) =>
a >= 125 && !(r > 250 && g > 250 && b > 250)
)
.quantizer.register("mmcq", MMCQ)
// Our generator has different output
// @ts-expect-error
.generator.register("default", customGenerator)
);
export const extractColors = (url: string, downsampleColors = 16) => export const extractColors = (url: string, downsampleColors = 16) =>
new Vibrant(url, { new Vibrant(url, {

View File

@@ -1,5 +1,3 @@
import "@material/mwc-icon-button/mwc-icon-button";
import { mdiClose, mdiMagnify } from "@mdi/js";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import { import {
css, css,
@@ -12,6 +10,8 @@ import { html, TemplateResult } from "lit-html";
import { classMap } from "lit-html/directives/class-map"; import { classMap } from "lit-html/directives/class-map";
import "../../components/ha-svg-icon"; import "../../components/ha-svg-icon";
import { fireEvent } from "../dom/fire_event"; import { fireEvent } from "../dom/fire_event";
import { mdiMagnify, mdiClose } from "@mdi/js";
import "@material/mwc-icon-button/mwc-icon-button";
@customElement("search-input") @customElement("search-input")
class SearchInput extends LitElement { class SearchInput extends LitElement {

View File

@@ -1,45 +0,0 @@
import { StructError } from "superstruct";
import type { HomeAssistant } from "../../types";
export const handleStructError = (
hass: HomeAssistant,
err: Error
): { warnings: string[]; errors?: string[] } => {
if (!(err instanceof StructError)) {
return { warnings: [err.message], errors: undefined };
}
const errors: string[] = [];
const warnings: string[] = [];
for (const failure of err.failures()) {
if (failure.value === undefined) {
errors.push(
hass.localize(
"ui.errors.config.key_missing",
"key",
failure.path.join(".")
)
);
} else if (failure.type === "never") {
warnings.push(
hass.localize(
"ui.errors.config.key_not_expected",
"key",
failure.path.join(".")
)
);
} else {
warnings.push(
hass.localize(
"ui.errors.config.key_wrong_type",
"key",
failure.path.join("."),
"type_correct",
failure.type,
"type_wrong",
JSON.stringify(failure.value)
)
);
}
}
return { warnings, errors };
};

View File

@@ -1,30 +0,0 @@
import { struct, StructContext, StructResult } from "superstruct";
const isEntityId = (value: unknown, context: StructContext): StructResult => {
if (typeof value !== "string") {
return [context.fail({ type: "string" })];
}
if (!value.includes(".")) {
return [
context.fail({
type: "Entity ID should be in the format 'domain.entity'",
}),
];
}
return true;
};
export const EntityId = struct("entity-id", isEntityId);
const isEntityIdOrAll = (
value: unknown,
context: StructContext
): StructResult => {
if (typeof value === "string" && value === "all") {
return true;
}
return isEntityId(value, context);
};
export const EntityIdOrAll = struct("entity-id-all", isEntityIdOrAll);

View File

@@ -1,5 +1,5 @@
import { shouldPolyfill } from "@formatjs/intl-pluralrules/should-polyfill";
import IntlMessageFormat from "intl-messageformat"; import IntlMessageFormat from "intl-messageformat";
import { shouldPolyfill } from "@formatjs/intl-pluralrules/should-polyfill";
import { Resources } from "../../types"; import { Resources } from "../../types";
export type LocalizeFunc = (key: string, ...args: any[]) => string; export type LocalizeFunc = (key: string, ...args: any[]) => string;

View File

@@ -1,13 +1,4 @@
export const copyToClipboard = async (str) => { export const copyToClipboard = (str) => {
if (navigator.clipboard) {
try {
await navigator.clipboard.writeText(str);
return;
} catch {
// just continue with the fallback coding below
}
}
const el = document.createElement("textarea"); const el = document.createElement("textarea");
el.value = str; el.value = str;
document.body.appendChild(el); document.body.appendChild(el);

View File

@@ -7,9 +7,10 @@ import {
html, html,
LitElement, LitElement,
property, property,
query,
TemplateResult, TemplateResult,
query,
} from "lit-element"; } from "lit-element";
import "../ha-circular-progress"; import "../ha-circular-progress";
@customElement("ha-progress-button") @customElement("ha-progress-button")

View File

@@ -98,12 +98,6 @@ export class HaDataTable extends LitElement {
@property({ type: Boolean }) public hasFab = false; @property({ type: Boolean }) public hasFab = false;
/**
* Add an extra row at the bottom of the data table
* @type {TemplateResult}
*/
@property({ attribute: false }) public appendRow?;
@property({ type: Boolean, attribute: "auto-height" }) @property({ type: Boolean, attribute: "auto-height" })
public autoHeight = false; public autoHeight = false;
@@ -132,8 +126,6 @@ export class HaDataTable extends LitElement {
@query("slot[name='header']") private _header!: HTMLSlotElement; @query("slot[name='header']") private _header!: HTMLSlotElement;
private _items: DataTableRowData[] = [];
private _checkableRowsCount?: number; private _checkableRowsCount?: number;
private _checkedRows: string[] = []; private _checkedRows: string[] = [];
@@ -326,13 +318,10 @@ export class HaDataTable extends LitElement {
@scroll=${this._saveScrollPos} @scroll=${this._saveScrollPos}
> >
${scroll({ ${scroll({
items: this._items, items: !this.hasFab
? this._filteredData
: [...this._filteredData, ...[{ empty: true }]],
renderItem: (row: DataTableRowData, index) => { renderItem: (row: DataTableRowData, index) => {
if (row.append) {
return html`
<div class="mdc-data-table__row">${row.content}</div>
`;
}
if (row.empty) { if (row.empty) {
return html` <div class="mdc-data-table__row"></div> `; return html` <div class="mdc-data-table__row"></div> `;
} }
@@ -458,20 +447,6 @@ export class HaDataTable extends LitElement {
if (this.curRequest !== curRequest) { if (this.curRequest !== curRequest) {
return; return;
} }
if (this.appendRow || this.hasFab) {
this._items = [...data];
if (this.appendRow) {
this._items.push({ append: true, content: this.appendRow });
}
if (this.hasFab) {
this._items.push({ empty: true });
}
} else {
this._items = data;
}
this._filteredData = data; this._filteredData = data;
} }

View File

@@ -1,4 +1,5 @@
import { wrap } from "comlink"; import { wrap } from "comlink";
import type { api } from "./sort_filter_worker"; import type { api } from "./sort_filter_worker";
type FilterDataType = api["filterData"]; type FilterDataType = api["filterData"];

View File

@@ -58,14 +58,6 @@ const sortData = (
valB = valB.toUpperCase(); valB = valB.toUpperCase();
} }
// Ensure "undefined" is always sorted to the bottom
if (valA === undefined && valB !== undefined) {
return 1;
}
if (valB === undefined && valA !== undefined) {
return -1;
}
if (valA < valB) { if (valA < valB) {
return sort * -1; return sort * -1;
} }

View File

@@ -1,11 +1,11 @@
// @ts-nocheck // @ts-nocheck
import wrap from "@vue/web-component-wrapper";
import { customElement } from "lit-element/lib/decorators";
import Vue from "vue"; import Vue from "vue";
import wrap from "@vue/web-component-wrapper";
import DateRangePicker from "vue2-daterange-picker"; import DateRangePicker from "vue2-daterange-picker";
import dateRangePickerStyles from "vue2-daterange-picker/dist/vue2-daterange-picker.css"; import dateRangePickerStyles from "vue2-daterange-picker/dist/vue2-daterange-picker.css";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { Constructor } from "../types"; import { Constructor } from "../types";
import { customElement } from "lit-element/lib/decorators";
const Component = Vue.extend({ const Component = Vue.extend({
props: { props: {
@@ -210,7 +210,7 @@ class DateRangePickerElement extends WrappedElement {
} }
.calendar-table { .calendar-table {
padding: 0 !important; padding: 0 !important;
} }
`; `;
const shadowRoot = this.shadowRoot!; const shadowRoot = this.shadowRoot!;
shadowRoot.appendChild(style); shadowRoot.appendChild(style);

View File

@@ -1,6 +1,5 @@
import "@material/mwc-button/mwc-button";
import "@material/mwc-icon-button/mwc-icon-button"; import "@material/mwc-icon-button/mwc-icon-button";
import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js"; import "@material/mwc-button/mwc-button";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
@@ -12,9 +11,9 @@ import {
CSSResult, CSSResult,
customElement, customElement,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
@@ -38,8 +37,9 @@ import {
import { SubscribeMixin } from "../../mixins/subscribe-mixin"; import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { PolymerChangedEvent } from "../../polymer-types"; import { PolymerChangedEvent } from "../../polymer-types";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import "../ha-svg-icon";
import "./ha-devices-picker"; import "./ha-devices-picker";
import "../ha-svg-icon";
import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
interface DevicesByArea { interface DevicesByArea {
[areaId: string]: AreaDevices; [areaId: string]: AreaDevices;
@@ -139,7 +139,7 @@ export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
private _filteredDevices: DeviceRegistryEntry[] = []; private _filteredDevices: DeviceRegistryEntry[] = [];
private _getAreasWithDevices = memoizeOne( private _getDevices = memoizeOne(
( (
devices: DeviceRegistryEntry[], devices: DeviceRegistryEntry[],
areas: AreaRegistryEntry[], areas: AreaRegistryEntry[],
@@ -277,7 +277,7 @@ export class HaAreaDevicesPicker extends SubscribeMixin(LitElement) {
if (!this._devices || !this._areas || !this._entities) { if (!this._devices || !this._areas || !this._entities) {
return html``; return html``;
} }
const areas = this._getAreasWithDevices( const areas = this._getDevices(
this._devices, this._devices,
this._areas, this._areas,
this._entities, this._entities,

View File

@@ -6,9 +6,9 @@ import {
css, css,
CSSResult, CSSResult,
html, html,
internalProperty,
LitElement, LitElement,
property, property,
internalProperty,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";

View File

@@ -1,5 +1,4 @@
import "@material/mwc-icon-button/mwc-icon-button"; import "../ha-icon-button";
import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
import "@polymer/paper-input/paper-input"; import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item"; import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body"; import "@polymer/paper-item/paper-item-body";
@@ -13,8 +12,6 @@ import {
html, html,
LitElement, LitElement,
property, property,
PropertyValues,
query,
TemplateResult, TemplateResult,
} from "lit-element"; } from "lit-element";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
@@ -38,7 +35,6 @@ import {
import { SubscribeMixin } from "../../mixins/subscribe-mixin"; import { SubscribeMixin } from "../../mixins/subscribe-mixin";
import { PolymerChangedEvent } from "../../polymer-types"; import { PolymerChangedEvent } from "../../polymer-types";
import { HomeAssistant } from "../../types"; import { HomeAssistant } from "../../types";
import "../ha-svg-icon";
interface Device { interface Device {
name: string; name: string;
@@ -115,10 +111,6 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
@property({ type: Boolean }) @property({ type: Boolean })
private _opened?: boolean; private _opened?: boolean;
@query("vaadin-combo-box-light", true) private _comboBox!: HTMLElement;
private _init = false;
private _getDevices = memoizeOne( private _getDevices = memoizeOne(
( (
devices: DeviceRegistryEntry[], devices: DeviceRegistryEntry[],
@@ -130,27 +122,18 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
deviceFilter: this["deviceFilter"] deviceFilter: this["deviceFilter"]
): Device[] => { ): Device[] => {
if (!devices.length) { if (!devices.length) {
return [ return [];
{
id: "",
area: "",
name: this.hass.localize("ui.components.device-picker.no_devices"),
},
];
} }
const deviceEntityLookup: DeviceEntityLookup = {}; const deviceEntityLookup: DeviceEntityLookup = {};
for (const entity of entities) {
if (includeDomains || excludeDomains || includeDeviceClasses) { if (!entity.device_id) {
for (const entity of entities) { continue;
if (!entity.device_id) {
continue;
}
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
} }
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
} }
const areaLookup: { [areaId: string]: AreaRegistryEntry } = {}; const areaLookup: { [areaId: string]: AreaRegistryEntry } = {};
@@ -158,9 +141,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
areaLookup[area.area_id] = area; areaLookup[area.area_id] = area;
} }
let inputDevices = devices.filter( let inputDevices = [...devices];
(device) => device.id === this.value || !device.disabled_by
);
if (includeDomains) { if (includeDomains) {
inputDevices = inputDevices.filter((device) => { inputDevices = inputDevices.filter((device) => {
@@ -227,15 +208,6 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
: this.hass.localize("ui.components.device-picker.no_area"), : this.hass.localize("ui.components.device-picker.no_area"),
}; };
}); });
if (!outputDevices.length) {
return [
{
id: "",
area: "",
name: this.hass.localize("ui.components.device-picker.no_match"),
},
];
}
if (outputDevices.length === 1) { if (outputDevices.length === 1) {
return outputDevices; return outputDevices;
} }
@@ -243,18 +215,6 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
} }
); );
public open() {
this.updateComplete.then(() => {
(this.shadowRoot?.querySelector("vaadin-combo-box-light") as any)?.open();
});
}
public focus() {
this.updateComplete.then(() => {
this.shadowRoot?.querySelector("paper-input")?.focus();
});
}
public hassSubscribe(): UnsubscribeFunc[] { public hassSubscribe(): UnsubscribeFunc[] {
return [ return [
subscribeDeviceRegistry(this.hass.connection!, (devices) => { subscribeDeviceRegistry(this.hass.connection!, (devices) => {
@@ -269,33 +229,25 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
]; ];
} }
protected updated(changedProps: PropertyValues) {
if (
(!this._init && this.devices && this.areas && this.entities) ||
(changedProps.has("_opened") && this._opened)
) {
this._init = true;
(this._comboBox as any).items = this._getDevices(
this.devices!,
this.areas!,
this.entities!,
this.includeDomains,
this.excludeDomains,
this.includeDeviceClasses,
this.deviceFilter
);
}
}
protected render(): TemplateResult { protected render(): TemplateResult {
if (!this.devices || !this.areas || !this.entities) { if (!this.devices || !this.areas || !this.entities) {
return html``; return html``;
} }
const devices = this._getDevices(
this.devices,
this.areas,
this.entities,
this.includeDomains,
this.excludeDomains,
this.includeDeviceClasses,
this.deviceFilter
);
return html` return html`
<vaadin-combo-box-light <vaadin-combo-box-light
item-value-path="id" item-value-path="id"
item-id-path="id" item-id-path="id"
item-label-path="name" item-label-path="name"
.items=${devices}
.value=${this._value} .value=${this._value}
.renderer=${rowRenderer} .renderer=${rowRenderer}
@opened-changed=${this._openedChanged} @opened-changed=${this._openedChanged}
@@ -313,30 +265,34 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
> >
${this.value ${this.value
? html` ? html`
<mwc-icon-button <ha-icon-button
.label=${this.hass.localize( aria-label=${this.hass.localize(
"ui.components.device-picker.clear" "ui.components.device-picker.clear"
)} )}
slot="suffix" slot="suffix"
class="clear-button" class="clear-button"
icon="hass:close"
@click=${this._clearValue} @click=${this._clearValue}
no-ripple
> >
<ha-svg-icon .path=${mdiClose}></ha-svg-icon> Clear
</mwc-icon-button> </ha-icon-button>
`
: ""}
${devices.length > 0
? html`
<ha-icon-button
aria-label=${this.hass.localize(
"ui.components.device-picker.show_devices"
)}
slot="suffix"
class="toggle-button"
.icon=${this._opened ? "hass:menu-up" : "hass:menu-down"}
>
Toggle
</ha-icon-button>
` `
: ""} : ""}
<mwc-icon-button
.label=${this.hass.localize(
"ui.components.device-picker.show_devices"
)}
slot="suffix"
class="toggle-button"
>
<ha-svg-icon
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
></ha-svg-icon>
</mwc-icon-button>
</paper-input> </paper-input>
</vaadin-combo-box-light> </vaadin-combo-box-light>
`; `;
@@ -373,7 +329,7 @@ export class HaDevicePicker extends SubscribeMixin(LitElement) {
static get styles(): CSSResult { static get styles(): CSSResult {
return css` return css`
paper-input > mwc-icon-button { paper-input > ha-icon-button {
--mdc-icon-button-size: 24px; --mdc-icon-button-size: 24px;
padding: 2px; padding: 2px;
color: var(--secondary-text-color); color: var(--secondary-text-color);

View File

@@ -1,6 +1,6 @@
import { customElement, html, LitElement, property } from "lit-element";
import { batteryIcon } from "../../common/entity/battery_icon"; import { batteryIcon } from "../../common/entity/battery_icon";
import "../ha-icon"; import "../ha-icon";
import { customElement, html, property, LitElement } from "lit-element";
@customElement("ha-battery-icon") @customElement("ha-battery-icon")
export class HaBatteryIcon extends LitElement { export class HaBatteryIcon extends LitElement {

View File

@@ -638,14 +638,8 @@ class HaChartBase extends mixinBehaviors(
const name = data[3]; const name = data[3];
if (name === null) return Color().hsl(0, 40, 38); if (name === null) return Color().hsl(0, 40, 38);
if (name === undefined) return Color().hsl(120, 40, 38); if (name === undefined) return Color().hsl(120, 40, 38);
let name1 = name.toLowerCase(); const name1 = name.toLowerCase();
if (ret === undefined) { if (ret === undefined) {
if (data[4]) {
// Invert on/off if data[4] is true. Required for some binary_sensor device classes
// (BINARY_SENSOR_DEVICE_CLASS_COLOR_INVERTED) where "off" is the good (= green color) value.
name1 = name1 === "on" ? "off" : name1 === "off" ? "on" : name1;
}
ret = colorDict[name1]; ret = colorDict[name1];
} }
if (ret === undefined) { if (ret === undefined) {

Some files were not shown because too many files have changed in this diff Show More