Compare commits

..

16 Commits

Author SHA1 Message Date
Franck Nijhof
f08f455698 Merge branch 'dev' 2024-08-06 12:10:50 +02:00
Bram Kragten
735560c552 Merge branch 'dev' 2024-08-05 17:16:34 +02:00
Bram Kragten
b5c2b555bc Merge branch 'dev' 2024-08-05 16:37:14 +02:00
Bram Kragten
b368f886f9 20240802.0 (#21562) 2024-08-02 16:11:50 +02:00
Bram Kragten
da75eecfa5 Merge branch 'dev' 2024-08-01 11:15:27 +02:00
Bram Kragten
fdf829bc81 20240731.0 (#21510) 2024-07-31 16:21:25 +02:00
Paul Bottein
0447247add 20240710.0 (#21350) 2024-07-10 08:34:04 +02:00
Bram Kragten
895333aa05 20240705.0 (#21306) 2024-07-05 13:40:27 +02:00
Bram Kragten
58ba9f628a 20240703.0 (#21264) 2024-07-03 14:27:49 +02:00
Bram Kragten
28ced4bfd3 20240702.0 (#21255) 2024-07-02 21:37:23 +02:00
Paul Bottein
fd6a192db1 20240628.0 (#21223) 2024-06-28 22:04:49 +02:00
Bram Kragten
d72e8c35d8 20240627.0 (#21192) 2024-06-27 20:02:18 +02:00
Paul Bottein
5bc3ad4c63 20240626.2 (#21181) 2024-06-26 23:19:57 +02:00
Paul Bottein
530745d20d Revert "20240626.1" (#21180)
Revert "20240626.1 (#21179)"

This reverts commit a16cae0671.
2024-06-26 23:18:27 +02:00
Paul Bottein
a16cae0671 20240626.1 (#21179)
* Fix undefined value in search (#21175)

* Fix hass object in nested hui-card (#21178)

* Bumped version to 20240626.1
2024-06-26 23:09:42 +02:00
Bram Kragten
8d0c4e4a52 20240626.0 (#21171) 2024-06-26 12:49:50 +02:00
54 changed files with 1293 additions and 1021 deletions

View File

@@ -89,7 +89,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.3.6
uses: actions/upload-artifact@v4.3.5
with:
name: frontend-bundle-stats
path: build/stats/*.json
@@ -113,7 +113,7 @@ jobs:
env:
IS_TEST: "true"
- name: Upload bundle stats
uses: actions/upload-artifact@v4.3.6
uses: actions/upload-artifact@v4.3.5
with:
name: supervisor-bundle-stats
path: build/stats/*.json

View File

@@ -57,14 +57,14 @@ jobs:
run: tar -czvf translations.tar.gz translations
- name: Upload build artifacts
uses: actions/upload-artifact@v4.3.6
uses: actions/upload-artifact@v4.3.5
with:
name: wheels
path: dist/home_assistant_frontend*.whl
if-no-files-found: error
- name: Upload translations
uses: actions/upload-artifact@v4.3.6
uses: actions/upload-artifact@v4.3.5
with:
name: translations
path: translations.tar.gz

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send bundle stats and build information to RelativeCI
uses: relative-ci/agent-action@v2.1.12
uses: relative-ci/agent-action@v2.1.11
with:
key: ${{ secrets[format('RELATIVE_CI_KEY_{0}_{1}', matrix.bundle, matrix.build)] }}
token: ${{ github.token }}

View File

@@ -1,76 +1,35 @@
// Tasks to generate entry HTML
import {
applyVersionsToRegexes,
compileRegex,
getPreUserAgentRegexes,
} from "browserslist-useragent-regexp";
import { getUserAgentRegex } from "browserslist-useragent-regexp";
import fs from "fs-extra";
import gulp from "gulp";
import { minify } from "html-minifier-terser";
import template from "lodash.template";
import { dirname, extname, resolve } from "node:path";
import path from "path";
import { htmlMinifierOptions, terserOptions } from "../bundle.cjs";
import env from "../env.cjs";
import paths from "../paths.cjs";
// macOS companion app has no way to obtain the Safari version used by WKWebView,
// and it is not in the default user agent string. So we add an additional regex
// to serve modern based on a minimum macOS version. We take the minimum Safari
// major version from browserslist and manually map that to a supported macOS
// version. Note this assumes the user has kept Safari updated.
const HA_MACOS_REGEX =
/Home Assistant\/[\d.]+ \(.+; macOS (\d+)\.(\d+)(?:\.(\d+))?\)/;
const SAFARI_TO_MACOS = {
15: [10, 15, 0],
16: [11, 0, 0],
17: [12, 0, 0],
18: [13, 0, 0],
};
const getCommonTemplateVars = () => {
const browserRegexes = getPreUserAgentRegexes({
env: "modern",
allowHigherVersions: true,
mobileToDesktop: true,
throwOnMissing: true,
});
const minSafariVersion = browserRegexes.find(
(regex) => regex.family === "safari"
)?.matchedVersions[0][0];
const minMacOSVersion = SAFARI_TO_MACOS[minSafariVersion];
if (!minMacOSVersion) {
throw Error(
`Could not find minimum MacOS version for Safari ${minSafariVersion}.`
);
}
const haMacOSRegex = applyVersionsToRegexes(
[
{
family: "ha_macos",
regex: HA_MACOS_REGEX,
matchedVersions: [minMacOSVersion],
requestVersions: [minMacOSVersion],
},
],
{ ignorePatch: true, allowHigherVersions: true }
);
return {
useRollup: env.useRollup(),
useWDS: env.useWDS(),
modernRegex: compileRegex(browserRegexes.concat(haMacOSRegex)).toString(),
};
};
const renderTemplate = (templateFile, data = {}) => {
const compiled = template(
fs.readFileSync(templateFile, { encoding: "utf-8" })
);
return compiled({
...data,
useRollup: env.useRollup(),
useWDS: env.useWDS(),
modernRegex: getUserAgentRegex({
env: "modern",
allowHigherVersions: true,
mobileToDesktop: true,
throwOnMissing: true,
}).toString(),
// Resolve any child/nested templates relative to the parent and pass the same data
renderTemplate: (childTemplate) =>
renderTemplate(resolve(dirname(templateFile), childTemplate), data),
renderTemplate(
path.resolve(path.dirname(templateFile), childTemplate),
data
),
});
};
@@ -104,12 +63,10 @@ const genPagesDevTask =
publicRoot = ""
) =>
async () => {
const commonVars = getCommonTemplateVars();
for (const [page, entries] of Object.entries(pageEntries)) {
const content = renderTemplate(
resolve(inputRoot, inputSub, `${page}.template`),
path.resolve(inputRoot, inputSub, `${page}.template`),
{
...commonVars,
latestEntryJS: entries.map((entry) =>
useWDS
? `http://localhost:8000/src/entrypoints/${entry}.ts`
@@ -124,7 +81,7 @@ const genPagesDevTask =
es5CustomPanelJS: `${publicRoot}/frontend_es5/custom-panel.js`,
}
);
fs.outputFileSync(resolve(outputRoot, page), content);
fs.outputFileSync(path.resolve(outputRoot, page), content);
}
};
@@ -141,18 +98,16 @@ const genPagesProdTask =
) =>
async () => {
const latestManifest = fs.readJsonSync(
resolve(outputLatest, "manifest.json")
path.resolve(outputLatest, "manifest.json")
);
const es5Manifest = outputES5
? fs.readJsonSync(resolve(outputES5, "manifest.json"))
? fs.readJsonSync(path.resolve(outputES5, "manifest.json"))
: {};
const commonVars = getCommonTemplateVars();
const minifiedHTML = [];
for (const [page, entries] of Object.entries(pageEntries)) {
const content = renderTemplate(
resolve(inputRoot, inputSub, `${page}.template`),
path.resolve(inputRoot, inputSub, `${page}.template`),
{
...commonVars,
latestEntryJS: entries.map((entry) => latestManifest[`${entry}.js`]),
es5EntryJS: entries.map((entry) => es5Manifest[`${entry}.js`]),
latestCustomPanelJS: latestManifest["custom-panel.js"],
@@ -160,8 +115,8 @@ const genPagesProdTask =
}
);
minifiedHTML.push(
minifyHtml(content, extname(page)).then((minified) =>
fs.outputFileSync(resolve(outputRoot, page), minified)
minifyHtml(content, path.extname(page)).then((minified) =>
fs.outputFileSync(path.resolve(outputRoot, page), minified)
)
);
}

View File

@@ -63,42 +63,33 @@
align-items: center;
}
#ha-launch-screen svg {
width: 112px;
width: 170px;
flex-shrink: 0;
}
#ha-launch-screen .ha-launch-screen-spacer-top {
#ha-launch-screen .ha-launch-screen-spacer {
flex: 1;
margin-top: calc( 2 * max(env(safe-area-inset-bottom), 48px) + 46px );
padding-top: 48px;
}
#ha-launch-screen .ha-launch-screen-spacer-bottom {
flex: 1;
padding-top: 48px;
}
.ohf-logo {
margin: max(env(safe-area-inset-bottom), 48px) 0;
color: grey;
font-size: 12px;
margin-bottom: 16px;
display: flex;
flex-direction: column;
align-items: center;
opacity: .66;
}
@media (prefers-color-scheme: dark) {
.ohf-logo {
filter: invert(1);
}
}
</style>
</head>
<body>
<div id="ha-launch-screen">
<div class="ha-launch-screen-spacer-top"></div>
<div class="ha-launch-screen-spacer"></div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
<path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
</svg>
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer-bottom"></div>
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
<div class="ohf-logo">
<img src="/static/images/ohf-badge.svg" alt="Home Assistant is a project by the Open Home Foundation" height="46">
a project from
<img src="/static/icons/ohf.svg" alt="Open Home Foundation" height="32">
</div>
</div>
<ha-demo></ha-demo>

View File

@@ -532,6 +532,15 @@ export default {
last_changed: "2018-07-19T10:44:46.200946+00:00",
last_updated: "2018-07-19T10:44:46.200946+00:00",
},
"mailbox.demomailbox": {
entity_id: "mailbox.demomailbox",
state: "10",
attributes: {
friendly_name: "DemoMailbox",
},
last_changed: "2018-07-19T10:45:16.555210+00:00",
last_updated: "2018-07-19T10:45:16.555210+00:00",
},
"input_select.living_room_preset": {
entity_id: "input_select.living_room_preset",
state: "Visitors",

View File

@@ -25,15 +25,15 @@
"license": "Apache-2.0",
"type": "module",
"dependencies": {
"@babel/runtime": "7.25.4",
"@babel/runtime": "7.25.0",
"@braintree/sanitize-url": "7.1.0",
"@codemirror/autocomplete": "6.18.0",
"@codemirror/autocomplete": "6.17.0",
"@codemirror/commands": "6.6.0",
"@codemirror/language": "6.10.2",
"@codemirror/legacy-modes": "6.4.1",
"@codemirror/legacy-modes": "6.4.0",
"@codemirror/search": "6.5.6",
"@codemirror/state": "6.4.1",
"@codemirror/view": "6.32.0",
"@codemirror/view": "6.29.1",
"@egjs/hammerjs": "2.0.17",
"@formatjs/intl-datetimeformat": "6.12.5",
"@formatjs/intl-displaynames": "6.6.8",
@@ -49,11 +49,11 @@
"@fullcalendar/list": "6.1.15",
"@fullcalendar/luxon3": "6.1.15",
"@fullcalendar/timegrid": "6.1.15",
"@lezer/highlight": "1.2.1",
"@lezer/highlight": "1.2.0",
"@lit-labs/context": "0.4.1",
"@lit-labs/motion": "1.0.7",
"@lit-labs/observers": "2.0.2",
"@lit-labs/virtualizer": "2.0.14",
"@lit-labs/virtualizer": "2.0.13",
"@lrnwebcomponents/simple-tooltip": "8.0.2",
"@material/chips": "=14.0.0-canary.53b3cad2f.0",
"@material/data-table": "=14.0.0-canary.53b3cad2f.0",
@@ -80,7 +80,7 @@
"@material/mwc-top-app-bar": "0.27.0",
"@material/mwc-top-app-bar-fixed": "0.27.0",
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
"@material/web": "2.1.0",
"@material/web": "2.0.0",
"@mdi/js": "7.4.47",
"@mdi/svg": "7.4.47",
"@polymer/paper-item": "3.0.1",
@@ -88,8 +88,8 @@
"@polymer/paper-tabs": "3.1.0",
"@polymer/polymer": "3.5.1",
"@thomasloven/round-slider": "0.6.0",
"@vaadin/combo-box": "24.4.6",
"@vaadin/vaadin-themable-mixin": "24.4.6",
"@vaadin/combo-box": "24.4.5",
"@vaadin/vaadin-themable-mixin": "24.4.5",
"@vibrant/color": "3.2.1-alpha.1",
"@vibrant/core": "3.2.1-alpha.1",
"@vibrant/quantizer-mmcq": "3.2.1-alpha.1",
@@ -97,10 +97,10 @@
"@webcomponents/scoped-custom-element-registry": "0.0.9",
"@webcomponents/webcomponentsjs": "2.8.0",
"app-datepicker": "5.1.1",
"chart.js": "4.4.4",
"chart.js": "4.4.3",
"color-name": "2.0.0",
"comlink": "4.4.1",
"core-js": "3.38.1",
"core-js": "3.37.1",
"cropperjs": "1.6.2",
"date-fns": "3.6.0",
"date-fns-tz": "3.1.3",
@@ -117,20 +117,20 @@
"leaflet": "1.9.4",
"leaflet-draw": "1.0.4",
"lit": "2.8.0",
"luxon": "3.5.0",
"marked": "14.0.0",
"luxon": "3.4.4",
"marked": "13.0.3",
"memoize-one": "6.0.0",
"node-vibrant": "3.2.1-alpha.1",
"proxy-polyfill": "0.3.2",
"punycode": "2.3.1",
"qr-scanner": "1.4.2",
"qrcode": "1.5.4",
"qrcode": "1.5.3",
"roboto-fontface": "0.10.0",
"rrule": "2.8.1",
"sortablejs": "1.15.2",
"stacktrace-js": "2.0.2",
"superstruct": "2.0.2",
"tinykeys": "3.0.0",
"tinykeys": "2.1.0",
"tsparticles-engine": "2.12.0",
"tsparticles-preset-links": "2.12.0",
"ua-parser-js": "1.0.38",
@@ -152,15 +152,15 @@
"@babel/core": "7.25.2",
"@babel/helper-define-polyfill-provider": "0.6.2",
"@babel/plugin-proposal-decorators": "7.24.7",
"@babel/plugin-transform-runtime": "7.25.4",
"@babel/preset-env": "7.25.4",
"@babel/plugin-transform-runtime": "7.24.7",
"@babel/preset-env": "7.25.3",
"@babel/preset-typescript": "7.24.7",
"@bundle-stats/plugin-webpack-filter": "4.14.2",
"@bundle-stats/plugin-webpack-filter": "4.13.4",
"@koa/cors": "5.0.0",
"@lokalise/node-api": "12.7.0",
"@octokit/auth-oauth-device": "7.1.1",
"@octokit/plugin-retry": "7.1.1",
"@octokit/rest": "21.0.2",
"@octokit/rest": "21.0.1",
"@open-wc/dev-server-hmr": "0.1.4",
"@rollup/plugin-babel": "6.0.4",
"@rollup/plugin-commonjs": "26.0.1",
@@ -168,7 +168,7 @@
"@rollup/plugin-node-resolve": "15.2.3",
"@rollup/plugin-replace": "5.0.7",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.17",
"@types/chromecast-caf-receiver": "6.0.16",
"@types/chromecast-caf-sender": "1.0.10",
"@types/color-name": "1.1.4",
"@types/glob": "8.1.0",
@@ -202,8 +202,8 @@
"eslint-plugin-import": "2.29.1",
"eslint-plugin-lit": "1.14.0",
"eslint-plugin-lit-a11y": "4.1.4",
"eslint-plugin-unused-imports": "4.1.3",
"eslint-plugin-wc": "2.1.1",
"eslint-plugin-unused-imports": "4.0.1",
"eslint-plugin-wc": "2.1.0",
"fancy-log": "2.0.0",
"fs-extra": "11.2.0",
"glob": "11.0.0",
@@ -213,10 +213,10 @@
"gulp-rename": "2.0.0",
"gulp-zopfli-green": "6.0.2",
"html-minifier-terser": "7.2.0",
"husky": "9.1.5",
"husky": "9.1.4",
"instant-mocha": "1.5.2",
"jszip": "3.10.1",
"lint-staged": "15.2.9",
"lint-staged": "15.2.8",
"lit-analyzer": "2.0.3",
"lodash.merge": "4.6.2",
"lodash.template": "4.5.0",
@@ -239,7 +239,7 @@
"transform-async-modules-webpack-plugin": "1.1.1",
"ts-lit-plugin": "2.0.2",
"typescript": "5.5.4",
"webpack": "5.94.0",
"webpack": "5.93.0",
"webpack-cli": "5.1.4",
"webpack-dev-server": "5.0.4",
"webpack-manifest-plugin": "5.0.0",

View File

@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="b" data-name="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760.69 138.69">
<g id="c" data-name="Layer_1">
<g>
<g>
<path d="M136.22,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM136.27,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
<path d="M184.16,80.53c0,3.47-1.06,6.27-3.18,8.41s-4.98,3.21-8.59,3.21h-7.45v12h-6.56v-35.18h14.06c3.64,0,6.5,1.04,8.59,3.11s3.13,4.89,3.13,8.45ZM177.25,80.39c0-1.64-.52-2.98-1.56-4.03s-2.52-1.57-4.44-1.57h-6.3v11.65h6.26c1.95,0,3.45-.55,4.49-1.65s1.56-2.57,1.56-4.39Z"/>
<path d="M210.82,98.02v6.14h-22.03v-35.18h21.98v6.19h-15.42v8.3h13.78v5.81h-13.78v8.74h15.47Z"/>
<path d="M246.95,68.98v35.18h-6.49l-16.08-24.77v24.77h-6.52v-35.18h6.52l16.08,24.77v-24.77h6.49Z"/>
<path d="M266.45,68.98h6.56v14.44l14.7.05v-14.48h6.63v35.18h-6.63v-14.84l-14.7-.09v14.93h-6.56v-35.18Z"/>
<path d="M316.41,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM316.46,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
<path d="M373.66,68.98v35.18h-6.45v-20.55l-8.11,20.55h-6.23l-8.02-20.39v20.39h-6.28v-35.18h6.28l11.13,27.54,11.23-27.54h6.45Z"/>
<path d="M402.87,98.02v6.14h-22.03v-35.18h21.98v6.19h-15.42v8.3h13.78v5.81h-13.78v8.74h15.47Z"/>
<path d="M427.83,75.12v8.93h13.01l-.05,5.91h-12.96v14.2h-6.52v-35.18h21.98l-.05,6.14h-15.42Z"/>
<path d="M463.16,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM463.21,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
<path d="M485,68.98h6.56v22.12c0,2.31.72,4.12,2.16,5.43s3.3,1.96,5.58,1.96,4.08-.67,5.58-2.02,2.25-3.13,2.25-5.37v-22.12h6.52v22.31c0,2.08-.38,3.98-1.14,5.7s-1.79,3.14-3.09,4.25-2.82,1.98-4.56,2.59-3.59.91-5.55.91c-2.59,0-4.96-.52-7.1-1.55s-3.88-2.58-5.2-4.65-1.99-4.49-1.99-7.25v-22.31Z"/>
<path d="M549.63,68.98v35.18h-6.49l-16.08-24.77v24.77h-6.52v-35.18h6.52l16.08,24.77v-24.77h6.49Z"/>
<path d="M586.9,86.58c.05,3.34-.71,6.37-2.27,9.08s-3.7,4.82-6.42,6.32-5.73,2.23-9.02,2.18h-12.42v-35.18h12.42c2.45-.03,4.78.39,6.98,1.28s4.1,2.1,5.68,3.66,2.84,3.43,3.75,5.64,1.35,4.55,1.3,7.03ZM579.99,86.58c0-3.39-1-6.16-3.01-8.3s-4.62-3.21-7.84-3.21h-5.81v23.04h5.81c3.27,0,5.89-1.06,7.88-3.19s2.98-4.91,2.98-8.34Z"/>
<path d="M609.16,96.19h-12.73l-2.79,7.97h-6.82l12.68-35.18h6.63l12.66,35.18h-6.96l-2.67-7.97ZM607.24,90.73l-4.43-12.87-4.45,12.87h8.88Z"/>
<path d="M642.87,75.17h-9.89v28.99h-6.56v-28.99h-9.94v-6.19h26.39v6.19Z"/>
<path d="M647.06,104.16v-35.18h6.56v35.18h-6.56Z"/>
<path d="M675.71,68.35c3.31-.05,6.29.72,8.92,2.31s4.67,3.77,6.11,6.55,2.14,5.89,2.11,9.33c.03,3.44-.68,6.55-2.12,9.34s-3.49,4.98-6.13,6.56-5.62,2.36-8.93,2.31c-3.33,0-6.29-.78-8.88-2.33s-4.6-3.71-6.02-6.48-2.13-5.9-2.13-9.4c-.03-2.56.38-4.98,1.22-7.24s2.01-4.21,3.5-5.82,3.31-2.88,5.45-3.8,4.45-1.36,6.91-1.32ZM675.76,98.35c3.09,0,5.56-1.07,7.41-3.2s2.77-5,2.77-8.61-.91-6.52-2.73-8.65-4.3-3.19-7.44-3.19-5.63,1.06-7.46,3.19-2.75,5.01-2.75,8.65.91,6.52,2.74,8.64,4.32,3.18,7.48,3.18Z"/>
<path d="M726.96,68.98v35.18h-6.49l-16.08-24.77v24.77h-6.52v-35.18h6.52l16.08,24.77v-24.77h6.49Z"/>
</g>
<g>
<path d="M94.34,79.34c0,2.75-2.25,5-5,5h-50c-2.75,0-5-2.25-5-5v-20c0-2.75,1.59-6.59,3.54-8.54l22.93-22.93c1.94-1.94,5.13-1.94,7.07,0l22.93,22.93c1.94,1.94,3.54,5.79,3.54,8.54v20Z"/>
<g>
<rect x="34.34" y="94.34" width="60" height="10" rx="2.5" ry="2.5"/>
<rect x="34.34" y="94.34" width="10" height="20" rx="1.56" ry="1.56"/>
<rect x="84.34" y="94.34" width="10" height="20" rx="1.56" ry="1.56"/>
</g>
</g>
<path d="M735.34,3c12.32,0,22.34,10.02,22.34,22.34v88c0,12.32-10.02,22.34-22.34,22.34H25.34c-12.32,0-22.34-10.02-22.34-22.34V25.34C3,13.02,13.02,3,25.34,3h710M735.34,0H25.34C11.37,0,0,11.37,0,25.34v88c0,13.98,11.37,25.34,25.34,25.34h710c13.97,0,25.34-11.37,25.34-25.34V25.34c0-13.98-11.37-25.34-25.34-25.34h0Z"/>
<g>
<path d="M120.98,36.79h2.95v7.26l7.66.02v-7.29h2.97v17.37h-2.97v-7.47l-7.66-.02v7.49h-2.95v-17.37Z"/>
<path d="M146.97,36.47c1.63,0,3.09.39,4.37,1.16s2.28,1.84,2.99,3.2,1.06,2.9,1.06,4.61c.02,1.7-.32,3.24-1.04,4.62s-1.72,2.47-3.02,3.25-2.75,1.16-4.36,1.14c-1.62.02-3.08-.36-4.37-1.14s-2.29-1.86-3-3.24-1.05-2.91-1.03-4.61c0-1.27.2-2.47.61-3.58s.99-2.08,1.72-2.88,1.63-1.42,2.68-1.88,2.18-.67,3.39-.66ZM146.99,51.57c1.6,0,2.89-.56,3.85-1.67s1.45-2.6,1.45-4.45-.48-3.32-1.45-4.43-2.25-1.66-3.85-1.66-2.89.55-3.86,1.66-1.45,2.58-1.45,4.43.48,3.34,1.44,4.46,2.25,1.67,3.88,1.67Z"/>
<path d="M176.51,36.79v17.37h-2.89v-10.78l-4.29,10.78h-2.81l-4.25-10.71v10.71h-2.84v-17.37h2.84l5.66,13.92,5.69-13.92h2.89Z"/>
<path d="M192.41,51.37v2.79h-10.78v-17.37h10.78v2.81h-7.83v4.5h7v2.61h-7v4.66h7.83Z"/>
<path d="M213.93,50.11h-6.61l-1.43,4.04h-3.04l6.27-17.37h3.04l6.29,17.37h-3.11l-1.41-4.04ZM213.07,47.62l-2.43-6.95-2.45,6.95h4.88Z"/>
<path d="M226.96,36.47c1.59,0,2.91.39,3.96,1.16s1.7,1.81,1.94,3.1l-2.78.76c-.16-.74-.52-1.32-1.09-1.72s-1.27-.6-2.11-.6c-.9,0-1.61.21-2.14.64s-.79,1-.79,1.71c0,1.12.7,1.85,2.09,2.18l2.84.71c1.46.38,2.56.98,3.29,1.81s1.09,1.84,1.09,3.05c0,1.55-.56,2.8-1.68,3.76s-2.63,1.44-4.51,1.44c-1.7,0-3.13-.4-4.3-1.2-1.15-.83-1.84-1.92-2.05-3.28l2.78-.72c.1.77.48,1.37,1.14,1.8s1.5.65,2.53.65,1.76-.21,2.32-.62.84-.98.84-1.69c0-1.12-.7-1.85-2.09-2.21l-2.84-.69c-1.46-.33-2.55-.92-3.28-1.77s-1.1-1.88-1.1-3.11c0-1.53.54-2.78,1.63-3.74s2.52-1.44,4.29-1.44Z"/>
<path d="M242.38,36.47c1.59,0,2.91.39,3.96,1.16s1.7,1.81,1.94,3.1l-2.78.76c-.16-.74-.52-1.32-1.09-1.72s-1.27-.6-2.11-.6c-.9,0-1.61.21-2.14.64s-.79,1-.79,1.71c0,1.12.7,1.85,2.09,2.18l2.84.71c1.46.38,2.56.98,3.29,1.81s1.09,1.84,1.09,3.05c0,1.55-.56,2.8-1.68,3.76s-2.63,1.44-4.51,1.44c-1.7,0-3.13-.4-4.3-1.2-1.15-.83-1.84-1.92-2.05-3.28l2.78-.72c.1.77.48,1.37,1.14,1.8s1.5.65,2.53.65,1.76-.21,2.32-.62.84-.98.84-1.69c0-1.12-.7-1.85-2.09-2.21l-2.84-.69c-1.46-.33-2.55-.92-3.28-1.77s-1.1-1.88-1.1-3.11c0-1.53.54-2.78,1.63-3.74s2.52-1.44,4.29-1.44Z"/>
<path d="M252.68,54.16v-17.37h2.95v17.37h-2.95Z"/>
<path d="M265.82,36.47c1.59,0,2.91.39,3.96,1.16s1.7,1.81,1.94,3.1l-2.78.76c-.16-.74-.52-1.32-1.09-1.72s-1.27-.6-2.11-.6c-.9,0-1.61.21-2.14.64s-.79,1-.79,1.71c0,1.12.7,1.85,2.09,2.18l2.84.71c1.46.38,2.56.98,3.29,1.81s1.09,1.84,1.09,3.05c0,1.55-.56,2.8-1.68,3.76s-2.63,1.44-4.51,1.44c-1.7,0-3.13-.4-4.3-1.2-1.15-.83-1.84-1.92-2.05-3.28l2.78-.72c.1.77.48,1.37,1.14,1.8s1.5.65,2.53.65,1.76-.21,2.32-.62.84-.98.84-1.69c0-1.12-.7-1.85-2.09-2.21l-2.84-.69c-1.46-.33-2.55-.92-3.28-1.77s-1.1-1.88-1.1-3.11c0-1.53.54-2.78,1.63-3.74s2.52-1.44,4.29-1.44Z"/>
<path d="M287.47,39.57h-4.97v14.58h-2.95v-14.58h-4.97v-2.79h12.9v2.79Z"/>
<path d="M298.87,50.11h-6.61l-1.43,4.04h-3.04l6.27-17.37h3.04l6.29,17.37h-3.11l-1.41-4.04ZM298.01,47.62l-2.43-6.95-2.45,6.95h4.88Z"/>
<path d="M320.89,36.79v17.37h-2.93l-8.25-12.67v12.67h-2.93v-17.37h2.93l8.25,12.65v-12.65h2.93Z"/>
<path d="M337.31,39.57h-4.97v14.58h-2.95v-14.58h-4.97v-2.79h12.9v2.79Z"/>
<path d="M348.75,54.16v-17.14h2.05v17.14h-2.05Z"/>
<path d="M360.95,36.72c1.55,0,2.82.38,3.81,1.14,1,.74,1.61,1.72,1.82,2.95l-1.95.52c-.16-.87-.56-1.54-1.23-2.02s-1.5-.71-2.5-.71c-1.08,0-1.95.27-2.6.8s-.97,1.24-.97,2.13c0,1.36.84,2.26,2.52,2.71l2.9.73c1.37.34,2.41.9,3.11,1.68s1.05,1.74,1.05,2.9c0,1.46-.53,2.64-1.6,3.54s-2.49,1.36-4.28,1.36c-1.61,0-2.95-.37-4.03-1.12s-1.72-1.76-1.95-3.06l1.98-.55c.13.88.55,1.56,1.25,2.06s1.63.75,2.77.75,2.12-.26,2.79-.77,1.02-1.22,1.02-2.14c0-1.44-.84-2.36-2.52-2.77l-2.88-.71c-1.38-.34-2.42-.9-3.12-1.69s-1.05-1.75-1.05-2.9c0-1.44.52-2.61,1.56-3.51s2.4-1.35,4.09-1.35Z"/>
<path d="M388.35,49.75h-7.54l-1.59,4.4h-2.07l6.25-17.14h2.36l6.31,17.14h-2.15l-1.57-4.4ZM387.73,48.05l-3.09-8.71-3.2,8.71h6.29Z"/>
<path d="M415.46,42.47c0,1.6-.5,2.91-1.5,3.95s-2.32,1.56-3.97,1.56h-4.53v6.18h-2.05v-17.14h6.6c1.67,0,3,.49,3.98,1.47s1.47,2.31,1.47,3.98ZM413.31,42.42c0-1.07-.32-1.92-.95-2.56s-1.51-.96-2.64-.96h-4.26v7.24h4.17c1.15,0,2.06-.34,2.71-1.02s.98-1.58.98-2.7Z"/>
<path d="M428.37,46.9l3.43,7.26h-2.31l-3.18-6.95h-4.76v6.95h-2.05v-17.14h6.54c1.81,0,3.22.45,4.24,1.35s1.53,2.14,1.53,3.72c0,1.22-.3,2.26-.9,3.1s-1.44,1.41-2.53,1.7ZM429.64,42.12c0-1.01-.32-1.81-.95-2.38s-1.52-.86-2.66-.86h-4.5v6.47h4.53c1.15,0,2.03-.28,2.64-.85s.92-1.36.92-2.38Z"/>
<path d="M443.34,36.74c1.18-.02,2.28.2,3.31.65s1.9,1.07,2.62,1.85,1.28,1.73,1.69,2.83.6,2.27.59,3.52c.02,1.67-.33,3.19-1.03,4.54s-1.68,2.42-2.95,3.19-2.68,1.14-4.25,1.12c-1.59,0-3-.38-4.25-1.13s-2.21-1.81-2.89-3.15-1.03-2.87-1.03-4.57c-.02-1.67.32-3.18,1.02-4.53s1.67-2.42,2.93-3.2,2.68-1.15,4.24-1.13ZM443.34,52.45c1.8,0,3.26-.64,4.38-1.91s1.68-2.92,1.68-4.95-.56-3.71-1.68-4.98-2.58-1.9-4.38-1.9-3.28.63-4.4,1.9-1.68,2.93-1.68,4.98.56,3.69,1.68,4.96,2.59,1.9,4.39,1.9Z"/>
<path d="M464.3,37.02v12.42c0,1.49-.47,2.71-1.41,3.64s-2.17,1.39-3.71,1.39c-1.56,0-2.76-.46-3.61-1.37s-1.27-2.13-1.27-3.69v-.55h1.98v.55c0,1.18.29,1.99.86,2.45.59.45,1.26.67,2.02.67.93,0,1.68-.26,2.24-.78.57-.52.85-1.32.85-2.39v-12.35h2.05Z"/>
<path d="M479.86,52.23v1.93h-10.35v-17.14h10.33v1.95h-8.31v5.67h7.53v1.8h-7.53v5.79h8.33Z"/>
<path d="M496.97,42.42c-.36-1.15-1.01-2.06-1.93-2.71s-2.02-.98-3.3-.98c-1.79,0-3.24.63-4.35,1.89s-1.66,2.92-1.66,4.96.55,3.72,1.66,4.96,2.55,1.86,4.33,1.86c1.27,0,2.39-.31,3.35-.94.95-.63,1.61-1.44,1.98-2.45l1.93.83c-.55,1.41-1.48,2.53-2.78,3.36-1.31.81-2.81,1.22-4.51,1.22-2.4,0-4.35-.81-5.84-2.43s-2.24-3.75-2.24-6.4c0-1.74.34-3.28,1.02-4.62s1.64-2.39,2.88-3.13,2.65-1.11,4.25-1.11c1.8,0,3.33.46,4.59,1.38,1.26.91,2.12,2.1,2.57,3.59l-1.93.71Z"/>
<path d="M512.92,38.95h-5.12v15.21h-2.05v-15.21h-5.16v-1.93h12.33v1.93Z"/>
<path d="M536.4,49.66c0,1.39-.49,2.48-1.46,3.29s-2.27,1.21-3.9,1.21h-6.72v-17.12h6.58c1.65,0,2.95.41,3.89,1.24s1.41,1.93,1.41,3.32c0,.92-.21,1.72-.64,2.4s-1.02,1.2-1.79,1.57c.81.37,1.45.91,1.92,1.62s.7,1.53.7,2.47ZM526.3,38.81v5.97h4.54c1.03,0,1.85-.27,2.46-.81s.92-1.24.92-2.09c0-.91-.31-1.65-.93-2.22s-1.45-.85-2.5-.85h-4.5ZM534.45,49.39c0-.91-.32-1.64-.95-2.17s-1.44-.8-2.46-.8h-4.74v5.95h4.74c1.04,0,1.86-.27,2.48-.81s.92-1.26.92-2.16Z"/>
<path d="M541.07,37.02l4.54,8.1,4.54-8.1h2.24l-5.76,10.05v7.09h-2.05v-7.09l-5.83-10.05h2.31Z"/>
<path d="M574.27,38.95h-5.12v15.21h-2.05v-15.21h-5.16v-1.93h12.33v1.93Z"/>
<path d="M577.95,37.02h2.05v7.55l8.74.02v-7.58h2.05v17.14h-2.05v-7.76l-8.74-.02v7.79h-2.05v-17.14Z"/>
<path d="M606.55,52.23v1.93h-10.35v-17.14h10.33v1.95h-8.31v5.67h7.53v1.8h-7.53v5.79h8.33Z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20240809.0"
version = "20240806.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"

View File

@@ -40,6 +40,7 @@ import {
mdiImageFilterFrames,
mdiLightbulb,
mdiLightningBolt,
mdiMailbox,
mdiMapMarkerRadius,
mdiMeterGas,
mdiMicrophoneMessage,
@@ -118,6 +119,7 @@ export const FIXED_DOMAIN_ICONS = {
input_text: mdiFormTextbox,
lawn_mower: mdiRobotMower,
light: mdiLightbulb,
mailbox: mdiMailbox,
notify: mdiCommentAlert,
number: mdiRayVertex,
persistent_notification: mdiBell,

View File

@@ -26,7 +26,7 @@ export const FIXED_DOMAIN_STATES = {
humidifier: ["on", "off"],
input_boolean: ["on", "off"],
input_button: [],
lawn_mower: ["error", "paused", "mowing", "returning", "docked"],
lawn_mower: ["error", "paused", "mowing", "docked"],
light: ["on", "off"],
lock: [
"jammed",

View File

@@ -28,11 +28,6 @@ const LAWN_MOWER_ACTIONS: Partial<
service: "start_mowing",
feature: LawnMowerEntityFeature.START_MOWING,
},
returning: {
action: "pause",
service: "pause",
feature: LawnMowerEntityFeature.PAUSE,
},
paused: {
action: "resume_mowing",
service: "start_mowing",

View File

@@ -1,19 +1,15 @@
import { css, CSSResultGroup, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import { BooleanSelector } from "../../data/selector";
import { HomeAssistant } from "../../types";
import "../ha-checkbox";
import "../ha-formfield";
import "../ha-input-helper-text";
import "../ha-switch";
import "../ha-input-helper-text";
@customElement("ha-selector-boolean")
export class HaBooleanSelector extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public selector!: BooleanSelector;
@property({ type: Boolean }) public value = false;
@property() public placeholder?: any;
@@ -25,24 +21,13 @@ export class HaBooleanSelector extends LitElement {
@property({ type: Boolean }) public disabled = false;
protected render() {
const checkbox = this.selector.boolean?.mode === "checkbox";
return html`
<ha-formfield .alignEnd=${!checkbox} spaceBetween .label=${this.label}>
${checkbox
? html`
<ha-checkbox
.checked=${this.value ?? this.placeholder === true}
@change=${this._handleChange}
.disabled=${this.disabled}
></ha-checkbox>
`
: html`
<ha-switch
.checked=${this.value ?? this.placeholder === true}
@change=${this._handleChange}
.disabled=${this.disabled}
></ha-switch>
`}
<ha-formfield alignEnd spaceBetween .label=${this.label}>
<ha-switch
.checked=${this.value ?? this.placeholder === true}
@change=${this._handleChange}
.disabled=${this.disabled}
></ha-switch>
</ha-formfield>
${this.helper
? html`<ha-input-helper-text>${this.helper}</ha-input-helper-text>`

View File

@@ -81,16 +81,15 @@ export class HaTargetSelector extends LitElement {
return nothing;
}
return html` ${this.label ? html`<label>${this.label}</label>` : nothing}
<ha-target-picker
.hass=${this.hass}
.value=${this.value}
.helper=${this.helper}
.deviceFilter=${this._filterDevices}
.entityFilter=${this._filterEntities}
.disabled=${this.disabled}
.createDomains=${this._createDomains}
></ha-target-picker>`;
return html`<ha-target-picker
.hass=${this.hass}
.value=${this.value}
.helper=${this.helper}
.deviceFilter=${this._filterDevices}
.entityFilter=${this._filterEntities}
.disabled=${this.disabled}
.createDomains=${this._createDomains}
></ha-target-picker>`;
}
private _filterEntities = (entity: HassEntity): boolean => {

View File

@@ -497,11 +497,6 @@ export class HaServiceControl extends LitElement {
dataField.name ||
dataField.key}
>
${this._renderSectionDescription(
dataField,
domain,
serviceName
)}
${Object.entries(dataField.fields).map(([key, field]) =>
this._renderField(
{ key, ...field },
@@ -522,22 +517,6 @@ export class HaServiceControl extends LitElement {
)} `;
}
private _renderSectionDescription(
dataField: ExtHassService["fields"][number],
domain: string | undefined,
serviceName: string | undefined
) {
const description = this.hass!.localize(
`component.${domain}.services.${serviceName}.sections.${dataField.key}.description`
);
if (!description) {
return nothing;
}
return html`<p>${description}</p>`;
}
private _renderField = (
dataField: ExtHassService["fields"][number],
hasOptional: boolean,

View File

@@ -4,12 +4,7 @@ import {
} from "home-assistant-js-websocket";
import { UNAVAILABLE } from "./entity";
export type LawnMowerEntityState =
| "paused"
| "mowing"
| "returning"
| "docked"
| "error";
export type LawnMowerEntityState = "paused" | "mowing" | "docked" | "error";
export const enum LawnMowerEntityFeature {
START_MOWING = 1,

View File

@@ -101,9 +101,8 @@ export interface AttributeSelector {
}
export interface BooleanSelector {
boolean: {
mode?: "checkbox" | "switch";
} | null;
// eslint-disable-next-line @typescript-eslint/ban-types
boolean: {} | null;
}
export interface ColorRGBSelector {

View File

@@ -140,12 +140,10 @@ export class HaVoiceCommandDialog extends LitElement {
const controlHA = !this._pipeline
? false
: this.hass.states[this._pipeline?.conversation_engine]
? supportsFeature(
this.hass.states[this._pipeline?.conversation_engine],
ConversationEntityFeature.CONTROL
)
: true;
: supportsFeature(
this.hass.states[this._pipeline?.conversation_engine],
ConversationEntityFeature.CONTROL
);
const supportsMicrophone = AudioRecorder.isSupported;
const supportsSTT = this._pipeline?.stt_engine;

View File

@@ -42,39 +42,30 @@
width: 112px;
flex-shrink: 0;
}
#ha-launch-screen .ha-launch-screen-spacer-top {
#ha-launch-screen .ha-launch-screen-spacer {
flex: 1;
margin-top: calc( 2 * max(env(safe-area-inset-bottom), 48px) + 46px );
padding-top: 48px;
}
#ha-launch-screen .ha-launch-screen-spacer-bottom {
flex: 1;
padding-top: 48px;
}
.ohf-logo {
margin: max(env(safe-area-inset-bottom), 48px) 0;
color: grey;
font-size: 12px;
margin-bottom: 16px;
display: flex;
flex-direction: column;
align-items: center;
opacity: .66;
}
@media (prefers-color-scheme: dark) {
.ohf-logo {
filter: invert(1);
}
}
</style>
</head>
<body>
<div id="ha-launch-screen">
<div class="ha-launch-screen-spacer-top"></div>
<div class="ha-launch-screen-spacer"></div>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240">
<path fill="#18BCF2" d="M240 224.762a15 15 0 0 1-15 15H15a15 15 0 0 1-15-15v-90c0-8.25 4.77-19.769 10.61-25.609l98.78-98.7805c5.83-5.83 15.38-5.83 21.21 0l98.79 98.7895c5.83 5.83 10.61 17.36 10.61 25.61v90-.01Z"/>
<path fill="#F2F4F9" d="m107.27 239.762-40.63-40.63c-2.09.72-4.32 1.13-6.64 1.13-11.3 0-20.5-9.2-20.5-20.5s9.2-20.5 20.5-20.5 20.5 9.2 20.5 20.5c0 2.33-.41 4.56-1.13 6.65l31.63 31.63v-115.88c-6.8-3.3395-11.5-10.3195-11.5-18.3895 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5c0 8.07-4.7 15.05-11.5 18.3895v81.27l31.46-31.46c-.62-1.96-.96-4.04-.96-6.2 0-11.3 9.2-20.5 20.5-20.5s20.5 9.2 20.5 20.5-9.2 20.5-20.5 20.5c-2.5 0-4.88-.47-7.09-1.29L129 208.892v30.88z"/>
</svg>
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer-bottom"></div>
<div id="ha-launch-screen-info-box" class="ha-launch-screen-spacer"></div>
<div class="ohf-logo">
<img src="/static/images/ohf-badge.svg" alt="Home Assistant is a project by the Open Home Foundation" height="46">
a project from
<img src="/static/icons/ohf.svg" alt="Open Home Foundation" height="32">
</div>
</div>
<home-assistant></home-assistant>

View File

@@ -29,6 +29,7 @@ const COMPONENTS = {
history: () => import("../panels/history/ha-panel-history"),
iframe: () => import("../panels/iframe/ha-panel-iframe"),
logbook: () => import("../panels/logbook/ha-panel-logbook"),
mailbox: () => import("../panels/mailbox/ha-panel-mailbox"),
map: () => import("../panels/map/ha-panel-map"),
my: () => import("../panels/my/ha-panel-my"),
profile: () => import("../panels/profile/ha-panel-profile"),

View File

@@ -53,7 +53,7 @@ export class HaServiceAction extends LitElement implements ActionElement {
);
public static get defaultConfig() {
return { action: "", data: {} };
return { service: "", data: {} };
}
protected willUpdate(changedProperties: PropertyValues) {

View File

@@ -353,7 +353,6 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) {
},
actions: {
title: "",
label: this.hass.localize("ui.panel.config.generic.headers.actions"),
type: "icon-button",
showNarrow: true,
moveable: false,

View File

@@ -217,7 +217,6 @@ export class HaWebhookTrigger extends LitElement {
ha-textfield > ha-icon-button {
--mdc-icon-button-size: 24px;
--mdc-icon-size: 18px;
color: var(--secondary-text-color);
}
ha-button-menu {

View File

@@ -198,7 +198,6 @@ class HaBlueprintOverview extends LitElement {
},
actions: {
title: "",
label: this.hass.localize("ui.panel.config.generic.headers.actions"),
type: "overflow-menu",
showNarrow: true,
moveable: false,

View File

@@ -0,0 +1,29 @@
const documentContainer = document.createElement("template");
documentContainer.setAttribute("style", "display: none;");
documentContainer.innerHTML = `<dom-module id="ha-form-style">
<template>
<style>
.form-group {
@apply --layout-horizontal;
@apply --layout-center;
padding: 8px 16px;
}
.form-group label {
@apply --layout-flex-2;
}
.form-group .form-control {
@apply --layout-flex;
}
.form-group.vertical {
@apply --layout-vertical;
@apply --layout-start;
}
</style>
</template>
</dom-module>`;
document.head.appendChild(documentContainer.content);

View File

@@ -349,7 +349,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
},
actions: {
title: "",
label: this.hass.localize("ui.panel.config.generic.headers.actions"),
label: "Actions",
type: "overflow-menu",
hideable: false,
moveable: false,

View File

@@ -13,7 +13,6 @@ import {
mdiDevices,
mdiDotsVertical,
mdiDownload,
mdiFileCodeOutline,
mdiHandExtendedOutline,
mdiOpenInNew,
mdiPackageVariant,
@@ -330,22 +329,6 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) {
)}</ha-alert
>`
: ""}
${normalEntries.length === 0 &&
this._manifest &&
!this._manifest.config_flow &&
this.hass.config.components.find(
(comp) => comp.split(".")[0] === this.domain
)
? html`<ha-alert alert-type="info"
><ha-svg-icon
slot="icon"
path=${mdiFileCodeOutline}
></ha-svg-icon
>${this.hass.localize(
"ui.panel.config.integrations.config_entry.no_config_flow"
)}</ha-alert
>`
: ""}
</div>
<div class="card-actions">

View File

@@ -311,9 +311,6 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
},
only_editable: {
title: "",
label: this.hass.localize(
"ui.panel.config.scene.picker.headers.editable"
),
type: "icon",
showNarrow: true,
template: (scene) =>
@@ -333,7 +330,6 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) {
},
actions: {
title: "",
label: this.hass.localize("ui.panel.config.generic.headers.actions"),
type: "overflow-menu",
showNarrow: true,
moveable: false,

View File

@@ -488,9 +488,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
if (value && !Array.isArray(value)) {
config.sequence = [value];
}
if (config.sequence) {
config.sequence = migrateAutomationAction(config.sequence);
}
config.sequence = migrateAutomationAction(config.sequence);
return config;
}

View File

@@ -321,7 +321,6 @@ class HaScriptPicker extends SubscribeMixin(LitElement) {
},
actions: {
title: "",
label: this.hass.localize("ui.panel.config.generic.headers.actions"),
type: "overflow-menu",
showNarrow: true,
moveable: false,

View File

@@ -38,7 +38,7 @@ export class AssistPipelineEvents extends LitElement {
</ha-card>`;
}
return html`<ha-alert alert-type="warning"
>There were no events in this run.</ha-alert
>There where no events in this run.</ha-alert
>`;
}
return html`

View File

@@ -308,7 +308,7 @@ class HaPanelDevAction extends LitElement {
private async _copyTemplate(): Promise<void> {
await copyToClipboard(
`{% set ${this._serviceData?.response_variable || "action_response"} = ${JSON.stringify(this._response)} %}`
`{% set action_response = ${JSON.stringify(this._response)} %}`
);
showToast(this, {
message: this.hass.localize("ui.common.copied_clipboard"),

View File

@@ -88,8 +88,13 @@ class HaPanelDevTemplate extends LitElement {
: "dict"
: type;
return html`
<div class="content">
<div class="description">
<div
class="content ${classMap({
layout: !this.narrow,
horizontal: !this.narrow,
})}"
>
<div class="edit-pane">
<p>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.description"
@@ -121,143 +126,123 @@ class HaPanelDevTemplate extends LitElement {
>
</li>
</ul>
<p>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.editor"
)}
</p>
<ha-code-editor
mode="jinja2"
.hass=${this.hass}
.value=${this._template}
.error=${this._error}
autofocus
autocomplete-entities
autocomplete-icons
@value-changed=${this._templateChanged}
dir="ltr"
></ha-code-editor>
<mwc-button @click=${this._restoreDemo}>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.reset"
)}
</mwc-button>
<mwc-button @click=${this._clear}>
${this.hass.localize("ui.common.clear")}
</mwc-button>
</div>
</div>
<div
class="content ${classMap({
layout: !this.narrow,
horizontal: !this.narrow,
})}"
>
<ha-card
class="edit-pane"
header=${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.editor"
)}
>
<div class="card-content">
<ha-code-editor
mode="jinja2"
.hass=${this.hass}
.value=${this._template}
.error=${this._error}
autofocus
autocomplete-entities
autocomplete-icons
@value-changed=${this._templateChanged}
dir="ltr"
></ha-code-editor>
</div>
<div class="card-actions">
<mwc-button @click=${this._restoreDemo}>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.reset"
)}
</mwc-button>
<mwc-button @click=${this._clear}>
${this.hass.localize("ui.common.clear")}
</mwc-button>
</div>
</ha-card>
<ha-card
class="render-pane"
header=${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.result"
)}
>
<div class="card-content">
${this._rendering
? html`<ha-circular-progress
class="render-spinner"
indeterminate
size="small"
></ha-circular-progress>`
: ""}
${this._error
? html`<ha-alert
alert-type=${this._errorLevel?.toLowerCase() || "error"}
>${this._error}</ha-alert
>`
: nothing}
${this._templateResult
? html`${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.result_type"
)}:
${resultType}
<!-- prettier-ignore -->
<pre class="rendered ${classMap({
[resultType]: resultType,
})}"
>${type === "object"
? JSON.stringify(this._templateResult.result, null, 2)
: this._templateResult.result}</pre>
${this._templateResult.listeners.time
<div class="render-pane">
${this._rendering
? html`<ha-circular-progress
class="render-spinner"
indeterminate
size="small"
></ha-circular-progress>`
: ""}
${this._error
? html`<ha-alert
alert-type=${this._errorLevel?.toLowerCase() || "error"}
>${this._error}</ha-alert
>`
: nothing}
${this._templateResult
? html`${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.result_type"
)}:
${resultType}
<!-- prettier-ignore -->
<pre class="rendered ${classMap({
[resultType]: resultType,
})}"
>${type === "object"
? JSON.stringify(this._templateResult.result, null, 2)
: this._templateResult.result}</pre>
${this._templateResult.listeners.time
? html`
<p>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.time"
)}
</p>
`
: ""}
${!this._templateResult.listeners
? nothing
: this._templateResult.listeners.all
? html`
<p>
<p class="all_listeners">
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.time"
"ui.panel.developer-tools.tabs.templates.all_listeners"
)}
</p>
`
: ""}
${!this._templateResult.listeners
? nothing
: this._templateResult.listeners.all
: this._templateResult.listeners.domains.length ||
this._templateResult.listeners.entities.length
? html`
<p class="all_listeners">
<p>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.all_listeners"
"ui.panel.developer-tools.tabs.templates.listeners"
)}
</p>
<ul>
${this._templateResult.listeners.domains
.sort()
.map(
(domain) => html`
<li>
<b
>${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.domain"
)}</b
>: ${domain}
</li>
`
)}
${this._templateResult.listeners.entities
.sort()
.map(
(entity_id) => html`
<li>
<b
>${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.entity"
)}</b
>: ${entity_id}
</li>
`
)}
</ul>
`
: this._templateResult.listeners.domains.length ||
this._templateResult.listeners.entities.length
? html`
<p>
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.listeners"
)}
</p>
<ul>
${this._templateResult.listeners.domains
.sort()
.map(
(domain) => html`
<li>
<b
>${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.domain"
)}</b
>: ${domain}
</li>
`
)}
${this._templateResult.listeners.entities
.sort()
.map(
(entity_id) => html`
<li>
<b
>${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.entity"
)}</b
>: ${entity_id}
</li>
`
)}
</ul>
`
: !this._templateResult.listeners.time
? html`<span class="all_listeners">
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.no_listeners"
)}
</span>`
: nothing}`
: nothing}
</div>
</ha-card>
: !this._templateResult.listeners.time
? html`<span class="all_listeners">
${this.hass.localize(
"ui.panel.developer-tools.tabs.templates.no_listeners"
)}
</span>`
: nothing}`
: nothing}
</div>
</div>
`;
}
@@ -273,7 +258,6 @@ class HaPanelDevTemplate extends LitElement {
}
.content {
gap: 16px;
padding: 16px;
padding: max(16px, env(safe-area-inset-top))
max(16px, env(safe-area-inset-right))
@@ -281,11 +265,10 @@ class HaPanelDevTemplate extends LitElement {
max(16px, env(safe-area-inset-left));
}
ha-card {
margin-bottom: 16px;
}
.edit-pane {
margin-right: 16px;
margin-inline-start: initial;
margin-inline-end: 16px;
direction: var(--direction);
}
@@ -297,6 +280,12 @@ class HaPanelDevTemplate extends LitElement {
max-width: 50%;
}
.render-pane {
position: relative;
max-width: 50%;
flex: 1;
}
.render-spinner {
position: absolute;
top: 8px;

View File

@@ -57,6 +57,9 @@ export class HuiBadge extends ReactiveElement {
}
private _updateElement(config: LovelaceBadgeConfig) {
if (config.type === "state-label") {
config = { display_type: "complete", ...config, type: "entity" };
}
if (!this._element) {
return;
}
@@ -66,6 +69,9 @@ export class HuiBadge extends ReactiveElement {
}
private _loadElement(config: LovelaceBadgeConfig) {
if (config.type === "state-label") {
config = { display_type: "complete", ...config, type: "entity" };
}
this._element = createBadgeElement(config);
this._elementConfig = config;
if (this.hass) {

View File

@@ -5,7 +5,6 @@ import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { styleMap } from "lit/directives/style-map";
import memoizeOne from "memoize-one";
import { mdiAlertCircle } from "@mdi/js";
import { computeCssColor } from "../../../common/color/compute-color";
import { hsv2rgb, rgb2hex, rgb2hsv } from "../../../common/color/convert-color";
import { computeDomain } from "../../../common/entity/compute_domain";
@@ -13,7 +12,6 @@ import { stateActive } from "../../../common/entity/state_active";
import { stateColorCss } from "../../../common/entity/state_color";
import "../../../components/ha-ripple";
import "../../../components/ha-state-icon";
import "../../../components/ha-svg-icon";
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive";
@@ -131,17 +129,7 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
const stateObj = entityId ? this.hass.states[entityId] : undefined;
if (!stateObj) {
return html`
<div class="badge error">
<ha-svg-icon .hass=${this.hass} .path=${mdiAlertCircle}></ha-svg-icon>
<span class="content">
<span class="name">${entityId}</span>
<span class="state">
${this.hass.localize("ui.badge.entity.not_found")}
</span>
</span>
</div>
`;
return nothing;
}
const active = stateActive(stateObj);
@@ -218,9 +206,6 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
--badge-color: var(--state-inactive-color);
-webkit-tap-highlight-color: transparent;
}
.badge.error {
--badge-color: var(--red-color);
}
.badge {
position: relative;
--ha-ripple-color: var(--badge-color);
@@ -240,14 +225,8 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
box-sizing: border-box;
width: auto;
border-radius: 18px;
background: var(
--ha-card-background,
var(--card-background-color, white)
);
-webkit-backdrop-filter: var(--ha-card-backdrop-filter, none);
backdrop-filter: var(--ha-card-backdrop-filter, none);
background-color: var(--card-background-color, white);
border-width: var(--ha-card-border-width, 1px);
box-shadow: var(--ha-card-box-shadow, none);
border-style: solid;
border-color: var(
--ha-card-border-color,
@@ -298,8 +277,7 @@ export class HuiEntityBadge extends LitElement implements LovelaceBadge {
letter-spacing: 0.1px;
color: var(--primary-text-color);
}
ha-state-icon,
ha-svg-icon {
ha-state-icon {
color: var(--badge-color);
line-height: 0;
}

View File

@@ -1,25 +1,71 @@
import { customElement } from "lit/decorators";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import "../../../components/entity/ha-state-label-badge";
import { HuiStateLabelBadgeEditor } from "../editor/config-elements/hui-state-label-badge-editor";
import { HuiEntityBadge } from "./hui-entity-badge";
import { EntityBadgeConfig, StateLabelBadgeConfig } from "./types";
import { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { handleAction } from "../common/handle-action";
import { hasAction } from "../common/has-action";
import { LovelaceBadge } from "../types";
import { StateLabelBadgeConfig } from "./types";
@customElement("hui-state-label-badge")
export class HuiStateLabelBadge extends HuiEntityBadge {
public static async getConfigElement(): Promise<HuiStateLabelBadgeEditor> {
await import("../editor/config-elements/hui-state-label-badge-editor");
return document.createElement("hui-state-label-badge-editor");
export class HuiStateLabelBadge extends LitElement implements LovelaceBadge {
@property({ attribute: false }) public hass?: HomeAssistant;
@state() protected _config?: StateLabelBadgeConfig;
public setConfig(config: StateLabelBadgeConfig): void {
this._config = config;
}
// @ts-ignore
public override setConfig(config: StateLabelBadgeConfig): void {
const entityBadgeConfig: EntityBadgeConfig = {
type: "entity",
entity: config.entity,
display_type: config.show_name === false ? "standard" : "complete",
};
protected render() {
if (!this._config || !this.hass) {
return nothing;
}
this._config = entityBadgeConfig;
const stateObj = this.hass.states[this._config.entity!];
return html`
<ha-state-label-badge
.hass=${this.hass}
.state=${stateObj}
.name=${this._config.name}
.icon=${this._config.icon}
.image=${this._config.image}
.showName=${this._config.show_name ?? true}
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(this._config!.hold_action),
hasDoubleClick: hasAction(this._config!.double_tap_action),
})}
tabindex=${ifDefined(
hasAction(this._config.tap_action) || this._config.entity
? "0"
: undefined
)}
></ha-state-label-badge>
`;
}
private _handleAction(ev: ActionHandlerEvent) {
handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
static get styles(): CSSResultGroup {
return css`
ha-state-label-badge:focus {
outline: none;
background: var(--divider-color);
border-radius: 4px;
}
ha-state-label-badge {
display: inline-block;
padding: 4px 2px 4px 2px;
margin: -4px -2px -4px -2px;
}
`;
}
}

View File

@@ -96,12 +96,7 @@ class HuiAlarmModeCardFeature
}
private async _setMode(mode: AlarmMode) {
await setProtectedAlarmControlPanelMode(
this,
this.hass!,
this.stateObj!,
mode
);
setProtectedAlarmControlPanelMode(this, this.hass!, this.stateObj!, mode);
}
protected render(): TemplateResult | null {

View File

@@ -430,14 +430,6 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
this._config?.show_current !== false &&
this._config?.show_forecast !== false
) {
return {
grid_columns: 4,
grid_min_columns: 2,
grid_rows: 4,
grid_min_rows: 4,
};
}
if (this._config?.show_forecast !== false) {
return {
grid_columns: 4,
grid_min_columns: 2,
@@ -449,7 +441,7 @@ class HuiWeatherForecastCard extends LitElement implements LovelaceCard {
grid_columns: 4,
grid_min_columns: 2,
grid_rows: 2,
grid_min_rows: 2,
grid_min_rows: 1,
};
}

View File

@@ -44,7 +44,6 @@ const HIDE_DOMAIN = new Set([
"persistent_notification",
"script",
"sun",
"tag",
"todo",
"zone",
...ASSIST_ENTITIES,

View File

@@ -226,15 +226,8 @@ export class HuiActionEditor extends LitElement {
if (!this.hass) {
return;
}
let action = this.config?.action;
if (action === "call-service") {
action = "perform-action";
}
const value = ev.target.value;
if (action === value) {
if (this.config?.action === value) {
return;
}
if (value === "default") {
@@ -299,7 +292,6 @@ export class HuiActionEditor extends LitElement {
ev.stopPropagation();
const value = {
...this.config!,
action: "perform-action",
perform_action: ev.detail.value.action || "",
data: ev.detail.value.data,
target: ev.detail.value.target || {},

View File

@@ -1,15 +1,16 @@
import { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import "../cards/hui-entity-card";
import "../cards/hui-entities-card";
import "../cards/hui-button-card";
import "../cards/hui-calendar-card";
import "../cards/hui-entities-card";
import "../cards/hui-entity-button-card";
import "../cards/hui-entity-card";
import "../cards/hui-glance-card";
import "../cards/hui-grid-card";
import "../cards/hui-light-card";
import "../cards/hui-sensor-card";
import "../cards/hui-thermostat-card";
import "../cards/hui-weather-forecast-card";
import "../cards/hui-tile-card";
import "../cards/hui-weather-forecast-card";
import {
createLovelaceElement,
getLovelaceElementClass,

View File

@@ -109,6 +109,10 @@ export class HuiDialogEditBadge
this._badgeConfig = badge != null ? ensureBadgeConfig(badge) : badge;
}
if (this._badgeConfig?.type === "state-label") {
this._badgeConfig = { ...this._badgeConfig, type: "entity" };
}
this.large = false;
if (this._badgeConfig && !Object.isFrozen(this._badgeConfig)) {
this._badgeConfig = deepFreeze(this._badgeConfig);

View File

@@ -235,11 +235,9 @@ export class HaCardConditionEditor extends LitElement {
? this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_pass"
)
: this._testingResult === false
? this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_error"
)
: nothing
: this.hass.localize(
"ui.panel.lovelace.editor.condition-editor.testing_error"
)
}
</div>
</ha-card>

View File

@@ -102,7 +102,7 @@ export class HaCardConditionState extends LitElement {
const data: StateConditionData = {
...content,
entity: this.condition.entity,
invert: this.condition.state_not !== undefined ? "true" : "false",
invert: this.condition.state_not ? "true" : "false",
state: this.condition.state_not ?? this.condition.state,
};

View File

@@ -1,45 +0,0 @@
import { customElement } from "lit/decorators";
import { assert, assign, boolean, object, optional, string } from "superstruct";
import "../../../../components/ha-form/ha-form";
import { EntityBadgeConfig } from "../../badges/types";
import "../hui-sub-element-editor";
import { actionConfigStruct } from "../structs/action-struct";
import { baseLovelaceBadgeConfig } from "../structs/base-badge-struct";
import "./hui-card-features-editor";
import { HuiEntityBadgeEditor } from "./hui-entity-badge-editor";
const badgeConfigStruct = assign(
baseLovelaceBadgeConfig,
object({
entity: optional(string()),
name: optional(string()),
icon: optional(string()),
show_entity_picture: optional(boolean()),
tap_action: optional(actionConfigStruct),
show_name: optional(boolean()),
image: optional(string()),
})
);
@customElement("hui-state-label-badge-editor")
export class HuiStateLabelBadgeEditor extends HuiEntityBadgeEditor {
// @ts-ignore
public override setConfig(config: StateLabelBadgeConfig): void {
assert(config, badgeConfigStruct);
const entityBadgeConfig: EntityBadgeConfig = {
type: "entity",
entity: config.entity,
display_type: config.show_name === false ? "standard" : "complete",
};
// @ts-ignore
this._config = entityBadgeConfig;
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-state-label-badge-editor": HuiStateLabelBadgeEditor;
}
}

View File

@@ -41,8 +41,6 @@ const actionConfigStructService = object({
entity_id: optional(union([string(), array(string())])),
device_id: optional(union([string(), array(string())])),
area_id: optional(union([string(), array(string())])),
floor_id: optional(union([string(), array(string())])),
label_id: optional(union([string(), array(string())])),
})
),
confirmation: optional(actionConfigStructConfirmation),

View File

@@ -231,15 +231,14 @@ class LovelaceFullConfigEditor extends LitElement {
if (!value) {
showConfirmationDialog(this, {
title: this.hass.localize(
"ui.panel.lovelace.editor.raw_editor.confirm_delete_config_title"
"ui.panel.lovelace.editor.raw_editor.confirm_remove_config_title"
),
text: this.hass.localize(
"ui.panel.lovelace.editor.raw_editor.confirm_delete_config_text"
"ui.panel.lovelace.editor.raw_editor.confirm_remove_config_text"
),
confirmText: this.hass.localize("ui.common.delete"),
confirmText: this.hass.localize("ui.common.remove"),
dismissText: this.hass.localize("ui.common.cancel"),
confirm: () => this._removeConfig(),
destructive: true,
});
return;
}

View File

@@ -12,8 +12,6 @@ import { fireEvent } from "../../../common/dom/fire_event";
import type { LovelaceViewElement } from "../../../data/lovelace";
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
import type { HomeAssistant } from "../../../types";
import { HuiBadge } from "../badges/hui-badge";
import "../badges/hui-view-badges";
import { HuiCard } from "../cards/hui-card";
import { HuiCardOptions } from "../components/hui-card-options";
import { replaceCard } from "../editor/config-util";
@@ -30,8 +28,6 @@ export class SideBarView extends LitElement implements LovelaceViewElement {
@property({ attribute: false }) public cards: HuiCard[] = [];
@property({ attribute: false }) public badges: HuiBadge[] = [];
@state() private _config?: LovelaceViewConfig;
private _mqlListenerRef?: () => void;
@@ -89,12 +85,6 @@ export class SideBarView extends LitElement implements LovelaceViewElement {
protected render(): TemplateResult {
return html`
<hui-view-badges
.hass=${this.hass}
.badges=${this.badges}
.lovelace=${this.lovelace}
.viewIndex=${this.index}
></hui-view-badges>
<div
class="container ${this.lovelace?.editMode ? "edit-mode" : ""}"
></div>
@@ -201,12 +191,6 @@ export class SideBarView extends LitElement implements LovelaceViewElement {
padding-top: 4px;
}
hui-view-badges {
display: block;
margin: 12px 8px 20px 8px;
font-size: 85%;
}
.container {
display: flex;
justify-content: center;

View File

@@ -0,0 +1,153 @@
import "@material/mwc-button";
import "../../components/ha-dialog";
import "../../components/ha-circular-progress";
import "../../components/ha-icon";
import "../../components/ha-icon-button";
import {
CSSResultGroup,
LitElement,
TemplateResult,
css,
html,
nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { HomeAssistant } from "../../types";
import { haStyleDialog } from "../../resources/styles";
@customElement("ha-dialog-show-audio-message")
class HaDialogShowAudioMessage extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _currentMessage?: any;
@state() private _errorMsg?: string;
@state() private _loading: boolean = false;
@state() private _opened: boolean = false;
@state() private _blobUrl?: string;
protected render(): TemplateResult {
return html`
<ha-dialog
.open=${this._opened}
@closed=${this._closeDialog}
heading=${this.hass.localize("ui.panel.mailbox.playback_title")}
>
${this._loading
? html`<ha-circular-progress indeterminate></ha-circular-progress>`
: html`<div class="icon">
<ha-icon-button id="delicon" @click=${this._openDeleteDialog}>
<ha-icon icon="hass:delete"></ha-icon>
</ha-icon-button>
</div>
${
this._currentMessage
? html`<div id="transcribe">
${this._currentMessage?.message}
</div>`
: nothing
}
${
this._errorMsg
? html`<div class="error">${this._errorMsg}</div>`
: nothing
}
${
this._blobUrl
? html` <audio id="mp3" preload="none" controls autoplay>
<source
id="mp3src"
src=${this._blobUrl}
type="audio/mpeg"
/>
</audio>`
: nothing
}
</div>`}
</ha-dialog>
`;
}
showDialog({ hass, message }) {
this.hass = hass;
this._errorMsg = undefined;
this._currentMessage = message;
this._opened = true;
const platform = message.platform;
if (platform.has_media) {
this._loading = true;
const url = `/api/mailbox/media/${platform.name}/${message.sha}`;
this.hass
.fetchWithAuth(url)
.then((response) => {
if (response.ok) {
return response.blob();
}
return Promise.reject({
status: response.status,
statusText: response.statusText,
});
})
.then((blob) => {
this._loading = false;
this._blobUrl = window.URL.createObjectURL(blob);
})
.catch((err) => {
this._loading = false;
this._errorMsg = `Error loading audio: ${err.statusText}`;
});
} else {
this._loading = false;
}
}
private _openDeleteDialog() {
if (confirm(this.hass.localize("ui.panel.mailbox.delete_prompt"))) {
this._deleteSelected();
}
}
private _deleteSelected() {
const msg = this._currentMessage;
this.hass.callApi(
"DELETE",
`mailbox/delete/${msg.platform.name}/${msg.sha}`
);
this._closeDialog();
}
private _closeDialog() {
const mp3 = this.shadowRoot!.querySelector("#mp3")! as any;
mp3.pause();
this._currentMessage = undefined;
this._errorMsg = undefined;
this._loading = false;
this._opened = false;
}
static get styles(): CSSResultGroup {
return [
haStyleDialog,
css`
.error {
color: red;
}
p {
color: var(--secondary-text-color);
}
.icon {
text-align: var(--float-end);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-dialog-show-audio-message": HaDialogShowAudioMessage;
}
}

View File

@@ -0,0 +1,275 @@
import {
CSSResultGroup,
LitElement,
TemplateResult,
css,
html,
nothing,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import "@material/mwc-button";
import { formatDateTime } from "../../common/datetime/format_date_time";
import "../../components/ha-card";
import "../../components/ha-menu-button";
import "../../components/ha-tabs";
import "@polymer/paper-tabs/paper-tab";
import { HomeAssistant } from "../../types";
import { fireEvent } from "../../common/dom/fire_event";
import { haStyle } from "../../resources/styles";
import "../../components/ha-top-app-bar-fixed";
import { formatDuration } from "../../common/datetime/format_duration";
let registeredDialog = false;
interface MailboxMessage {
info: {
origtime: number;
callerid: string;
duration: string;
};
text: string;
sha: string;
}
@customElement("ha-panel-mailbox")
class HaPanelMailbox extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public narrow!: boolean;
@property({ attribute: false }) public platforms?: any[];
@state() private _messages?: any[];
@state() private _currentPlatform: number = 0;
private _unsubEvents?;
protected render(): TemplateResult {
return html`
<ha-top-app-bar-fixed>
<ha-menu-button
slot="navigationIcon"
.hass=${this.hass}
.narrow=${this.narrow}
></ha-menu-button>
<div slot="title">${this.hass.localize("panel.mailbox")}</div>
${!this._areTabsHidden(this.platforms)
? html`<div sticky>
<ha-tabs
scrollable
.selected=${this._currentPlatform}
@iron-activate=${this._handlePlatformSelected}
>
${this.platforms?.map(
(platform) =>
html` <paper-tab data-entity=${platform}>
${this._getPlatformName(platform)}
</paper-tab>`
)}
</ha-tabs>
</div>`
: ""}
</ha-top-app-bar-fixed>
<div class="content">
<ha-card>
${!this._messages?.length
? html`<div class="card-content empty">
${this.hass.localize("ui.panel.mailbox.empty")}
</div>`
: nothing}
${this._messages?.map(
(message) =>
html` <ha-list-item
.message=${message}
@click=${this._openMP3Dialog}
twoline
>
<span>
<span>${message.caller}</span>
<span class="tip">
${formatDuration(this.hass.locale, {
seconds: message.duration,
})}
</span>
</span>
<span slot="secondary">
<span class="date">${message.timestamp}</span> -
${message.message}
</span>
</ha-list-item>`
)}
</ha-card>
</div>
`;
}
connectedCallback() {
super.connectedCallback();
if (!registeredDialog) {
registeredDialog = true;
fireEvent(this, "register-dialog", {
dialogShowEvent: "show-audio-message-dialog",
dialogTag: "ha-dialog-show-audio-message",
dialogImport: () => import("./ha-dialog-show-audio-message"),
});
}
this.hassChanged = this.hassChanged.bind(this);
this.hass.connection
.subscribeEvents(this.hassChanged, "mailbox_updated")
.then((unsub) => {
this._unsubEvents = unsub;
});
this._computePlatforms().then((platforms) => {
this.platforms = platforms;
this.hassChanged();
});
}
disconnectedCallback() {
super.disconnectedCallback();
if (this._unsubEvents) this._unsubEvents();
}
hassChanged() {
if (!this._messages) {
this._messages = [];
}
this._getMessages().then((items) => {
this._messages = items;
});
}
private _openMP3Dialog(ev) {
const message: any = (ev.currentTarget! as any).message;
fireEvent(this, "show-audio-message-dialog", {
hass: this.hass,
message: message,
});
}
private _getMessages() {
const platform = this.platforms![this._currentPlatform];
return this.hass
.callApi<MailboxMessage[]>("GET", `mailbox/messages/${platform.name}`)
.then((values) => {
const platformItems: any[] = [];
const arrayLength = values.length;
for (let i = 0; i < arrayLength; i++) {
const datetime = formatDateTime(
new Date(values[i].info.origtime * 1000),
this.hass.locale,
this.hass.config
);
platformItems.push({
timestamp: datetime,
caller: values[i].info.callerid,
message: values[i].text,
sha: values[i].sha,
duration: values[i].info.duration,
platform: platform,
});
}
return platformItems.sort((a, b) => b.timestamp - a.timestamp);
});
}
private _computePlatforms(): Promise<any[]> {
return this.hass.callApi<any[]>("GET", "mailbox/platforms");
}
private _handlePlatformSelected(ev) {
const newPlatform = ev.detail.selected;
if (newPlatform !== this._currentPlatform) {
this._currentPlatform = newPlatform;
this.hassChanged();
}
}
private _areTabsHidden(platforms) {
return !platforms || platforms.length < 2;
}
private _getPlatformName(item) {
const entity = `mailbox.${item.name}`;
const stateObj = this.hass.states[entity.toLowerCase()];
return stateObj.attributes.friendly_name;
}
static get styles(): CSSResultGroup {
return [
haStyle,
css`
:host {
-ms-user-select: initial;
-webkit-user-select: initial;
-moz-user-select: initial;
}
.content {
padding: 16px;
max-width: 600px;
margin: 0 auto;
}
ha-card {
overflow: hidden;
}
ha-tabs {
margin-left: max(env(safe-area-inset-left), 24px);
margin-right: max(env(safe-area-inset-right), 24px);
margin-inline-start: max(env(safe-area-inset-left), 24px);
margin-inline-end: max(env(safe-area-inset-right), 24px);
--paper-tabs-selection-bar-color: #fff;
text-transform: uppercase;
}
.empty {
text-align: center;
color: var(--secondary-text-color);
}
.header {
@apply --paper-font-title;
}
.row {
display: flex;
justify-content: space-between;
}
@media all and (max-width: 450px) {
.content {
width: auto;
padding: 0;
}
}
.tip {
color: var(--secondary-text-color);
font-size: 14px;
}
.date {
color: var(--primary-text-color);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-panel-mailbox": HaPanelMailbox;
}
}
declare global {
// for fire event
interface HASSDomEvents {
"show-audio-message-dialog": {
hass: HomeAssistant;
message: string;
};
}
}

View File

@@ -222,7 +222,6 @@ class HaProfileSectionGeneral extends LitElement {
text: this.hass.localize("ui.panel.profile.logout_text"),
confirmText: this.hass.localize("ui.panel.profile.logout"),
confirm: () => fireEvent(this, "hass-logout"),
destructive: true,
});
}

View File

@@ -364,9 +364,6 @@ class DialogTodoItemEditor extends LitElement {
"ui.components.todo.item.confirm_delete.delete"
),
text: this.hass.localize("ui.components.todo.item.confirm_delete.prompt"),
destructive: true,
confirmText: this.hass.localize("ui.common.delete"),
dismissText: this.hass.localize("ui.common.cancel"),
});
if (!confirm) {
// Cancel

View File

@@ -7,6 +7,7 @@
"map": "Map",
"logbook": "Logbook",
"history": "History",
"mailbox": "Mailbox",
"todo": "To-do lists",
"developer_tools": "Developer tools",
"media_browser": "Media",
@@ -70,11 +71,6 @@
"backup": {
"upload_backup": "Upload backup"
},
"badge": {
"entity": {
"not_found": "[%key:ui::card::tile::not_found%]"
}
},
"card": {
"common": {
"turn_on": "Turn on",
@@ -175,7 +171,6 @@
"actions": {
"resume_mowing": "Resume mowing",
"start_mowing": "Start mowing",
"pause": "Pause",
"dock": "Return to dock"
}
},
@@ -3749,7 +3744,6 @@
"name": "Name",
"last_activated": "Last activated",
"category": "Category",
"editable": "[%key:ui::panel::config::helpers::picker::headers::editable%]",
"area": "Area",
"icon": "Icon"
},
@@ -5497,8 +5491,8 @@
"saved": "Saved",
"reload": "Reload",
"lovelace_changed": "Your dashboard was updated, do you want to load the updated config in the editor and lose your current changes?",
"confirm_delete_config_title": "Delete dashboard configuration?",
"confirm_delete_config_text": "This dashboard will be permanently deleted. The dashboard will be automatically regenerated to display your areas, devices and entities.",
"confirm_remove_config_title": "Are you sure you want to remove your dashboard configuration?",
"confirm_remove_config_text": "We will automatically generate your dashboard views with your areas and devices if you remove your dashboard configuration.",
"confirm_unsaved_changes": "You have unsaved changes, are you sure you want to exit?",
"confirm_unsaved_comments": "Your configuration might contains comment(s), these will not be saved. Do you want to continue?",
"error_parse_yaml": "Unable to parse YAML: {error}",
@@ -6360,6 +6354,12 @@
},
"reload_lovelace": "Reload UI"
},
"mailbox": {
"empty": "You do not have any messages",
"playback_title": "Message playback",
"delete_prompt": "Delete this message?",
"delete_button": "Delete"
},
"media-browser": {
"error": {
"player_not_exist": "Media player {name} does not exist"
@@ -6856,7 +6856,6 @@
"title": "Template",
"description": "Templates are rendered using the Jinja2 template engine with some Home Assistant specific extensions.",
"editor": "Template editor",
"result": "Result",
"reset": "Reset to demo template",
"confirm_reset": "Do you want to reset your current template back to the demo template?",
"confirm_clear": "Do you want to clear your current template?",

936
yarn.lock

File diff suppressed because it is too large Load Diff