diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 49839b27f660..af8a33890396 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -64,7 +64,7 @@ jobs:
- name: Install dependencies
run: yarn install --immutable
- name: Build resources
- run: ./node_modules/.bin/gulp build-translations build-locale-data
+ run: ./node_modules/.bin/gulp gen-icons-json build-translations build-locale-data
- name: Run Tests
run: yarn run test
build:
diff --git a/hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts b/hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts
index 8a1c3c670221..4dcfde2ec2b4 100644
--- a/hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts
+++ b/hassio/src/dialogs/datadisk/dialog-hassio-datadisk.ts
@@ -4,7 +4,6 @@ import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../../src/common/dom/fire_event";
import "../../../../src/components/ha-circular-progress";
-import "../../../../src/components/ha-markdown";
import "../../../../src/components/ha-select";
import {
extractApiErrorMessage,
diff --git a/src/components/ha-markdown-element.ts b/src/components/ha-markdown-element.ts
index e00bfceb9477..0e806b7c7dcf 100644
--- a/src/components/ha-markdown-element.ts
+++ b/src/components/ha-markdown-element.ts
@@ -95,6 +95,15 @@ class HaMarkdownElement extends ReactiveElement {
}
node.firstElementChild!.replaceWith(alertNote);
}
+ } else if (
+ node instanceof HTMLElement &&
+ ["ha-alert", "ha-qr-code", "ha-icon", "ha-svg-icon"].includes(
+ node.localName
+ )
+ ) {
+ import(
+ /* webpackInclude: /(ha-alert)|(ha-qr-code)|(ha-icon)|(ha-svg-icon)/ */ `./${node.localName}`
+ );
}
}
}
diff --git a/src/components/ha-markdown.ts b/src/components/ha-markdown.ts
index c248a320e819..f81ddd286037 100644
--- a/src/components/ha-markdown.ts
+++ b/src/components/ha-markdown.ts
@@ -2,11 +2,6 @@ import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import "./ha-markdown-element";
-// Import components that are allwoed to be defined.
-import "./ha-alert";
-import "./ha-icon";
-import "./ha-svg-icon";
-
@customElement("ha-markdown")
export class HaMarkdown extends LitElement {
@property() public content?;
diff --git a/src/components/ha-qr-code.ts b/src/components/ha-qr-code.ts
new file mode 100644
index 000000000000..f6a686d06823
--- /dev/null
+++ b/src/components/ha-qr-code.ts
@@ -0,0 +1,114 @@
+import { LitElement, PropertyValues, css, html, nothing } from "lit";
+import { customElement, property, query, state } from "lit/decorators";
+import QRCode from "qrcode";
+
+@customElement("ha-qr-code")
+export class HaQrCode extends LitElement {
+ @property() public data?: string;
+
+ @property({ attribute: "error-correction-level" })
+ public errorCorrectionLevel: "low" | "medium" | "quartile" | "high" =
+ "medium";
+
+ @property({ type: Number })
+ public width = 4;
+
+ @property({ type: Number })
+ public scale = 4;
+
+ @property({ type: Number })
+ public margin = 4;
+
+ @property({ type: Number }) public maskPattern?:
+ | 0
+ | 1
+ | 2
+ | 3
+ | 4
+ | 5
+ | 6
+ | 7;
+
+ @property({ attribute: "center-image" }) public centerImage?: string;
+
+ @state() private _error?: string;
+
+ @query("canvas") private _canvas?: HTMLCanvasElement;
+
+ protected willUpdate(changedProperties: PropertyValues): void {
+ super.willUpdate(changedProperties);
+ if (
+ (changedProperties.has("data") ||
+ changedProperties.has("scale") ||
+ changedProperties.has("width") ||
+ changedProperties.has("margin") ||
+ changedProperties.has("maskPattern") ||
+ changedProperties.has("errorCorrectionLevel")) &&
+ this._error
+ ) {
+ this._error = undefined;
+ }
+ }
+
+ updated(changedProperties: PropertyValues) {
+ const canvas = this._canvas;
+ if (
+ canvas &&
+ this.data &&
+ (changedProperties.has("data") ||
+ changedProperties.has("scale") ||
+ changedProperties.has("width") ||
+ changedProperties.has("margin") ||
+ changedProperties.has("maskPattern") ||
+ changedProperties.has("errorCorrectionLevel") ||
+ changedProperties.has("centerImage"))
+ ) {
+ const computedStyles = getComputedStyle(this);
+
+ QRCode.toCanvas(canvas, this.data, {
+ errorCorrectionLevel: this.errorCorrectionLevel,
+ width: this.width,
+ scale: this.scale,
+ margin: this.margin,
+ maskPattern: this.maskPattern,
+ color: {
+ light: computedStyles.getPropertyValue("--card-background-color"),
+ dark: computedStyles.getPropertyValue("--primary-text-color"),
+ },
+ }).catch((err) => {
+ this._error = err.message;
+ });
+
+ if (this.centerImage) {
+ const context = this._canvas!.getContext("2d");
+ const imageObj = new Image();
+ imageObj.src = this.centerImage;
+ imageObj.onload = () => {
+ context?.drawImage(
+ imageObj,
+ canvas.width * 0.375,
+ canvas.height * 0.375,
+ canvas.width / 4,
+ canvas.height / 4
+ );
+ };
+ }
+ }
+ }
+
+ render() {
+ if (!this.data) {
+ return nothing;
+ }
+ if (this._error) {
+ return html`