diff --git a/.babelrc b/.babelrc
old mode 100755
new mode 100644
diff --git a/.eslintrc.yaml b/.eslintrc.yaml
old mode 100755
new mode 100644
index 6020c9cb..889d79b0
--- a/.eslintrc.yaml
+++ b/.eslintrc.yaml
@@ -14,3 +14,4 @@ globals:
window: true
Event: true
customElements: true
+ document: true
diff --git a/.gitignore b/.gitignore
old mode 100755
new mode 100644
diff --git a/package-lock.json b/package-lock.json
index cee6e1cf..22d7d50b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"license": "MIT",
"dependencies": {
"@kalkih/lz-string": "^1.4.5",
+ "@mdi/js": "^7.4.47",
"custom-card-helpers": "^1.6.6",
"d3-interpolate": "^3.0.1",
"lit-element": "^2.2.1",
@@ -1331,6 +1332,11 @@
"resolved": "https://registry.npmjs.org/@kalkih/lz-string/-/lz-string-1.4.5.tgz",
"integrity": "sha512-F5eIc4ER1sYItRMyIjpmvbV9hb25ydPCOB+2YN+jwljugEf0s+jfEqSpvnmrPYvSRogNkJtVHc2g+NucmLvGHg=="
},
+ "node_modules/@mdi/js": {
+ "version": "7.4.47",
+ "resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz",
+ "integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ=="
+ },
"node_modules/@nicolo-ribaudo/chokidar-2": {
"version": "2.1.8-no-fsevents",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz",
@@ -16864,6 +16870,11 @@
"resolved": "https://registry.npmjs.org/@kalkih/lz-string/-/lz-string-1.4.5.tgz",
"integrity": "sha512-F5eIc4ER1sYItRMyIjpmvbV9hb25ydPCOB+2YN+jwljugEf0s+jfEqSpvnmrPYvSRogNkJtVHc2g+NucmLvGHg=="
},
+ "@mdi/js": {
+ "version": "7.4.47",
+ "resolved": "https://registry.npmjs.org/@mdi/js/-/js-7.4.47.tgz",
+ "integrity": "sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ=="
+ },
"@nicolo-ribaudo/chokidar-2": {
"version": "2.1.8-no-fsevents",
"resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.tgz",
diff --git a/package.json b/package.json
index 4e76eced..2616199a 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"license": "MIT",
"dependencies": {
"@kalkih/lz-string": "^1.4.5",
+ "@mdi/js": "^7.4.47",
"custom-card-helpers": "^1.6.6",
"d3-interpolate": "^3.0.1",
"lit-element": "^2.2.1",
diff --git a/rollup.config.js b/rollup.config.js
index e251f483..ff5dbf1b 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -25,7 +25,7 @@ export default {
plugins: [
commonjs(),
json({
- include: 'package.json',
+ include: ['package.json', 'src/localize/languages/*'],
preferConst: true,
}),
resolve(),
diff --git a/scripts/update_readme.sh b/scripts/update_readme.sh
old mode 100755
new mode 100644
diff --git a/src/editor/components/colorSelector.js b/src/editor/components/colorSelector.js
new file mode 100644
index 00000000..1a12835f
--- /dev/null
+++ b/src/editor/components/colorSelector.js
@@ -0,0 +1,130 @@
+import { mdiClose } from '@mdi/js';
+import { fireEvent } from 'custom-card-helpers';
+import { css, html, LitElement } from 'lit-element';
+import { isValidHex, convertColorNameToHex } from '../editorUtils';
+
+export const colorSelector = {
+ hex_color: {},
+};
+
+export class CustomColorSelector extends LitElement {
+ static get properties() {
+ return {
+ hass: { attribute: false },
+ selector: { attribute: false },
+ value: {},
+ label: {},
+ };
+ }
+
+ render() {
+ const isHex = isValidHex(this.value);
+ const colorValue = isHex ? this.value : convertColorNameToHex(this.value);
+ return html`
+
+ `;
+ }
+
+ return html`
+
+
+
+
+ ${this.data.map((row, index) => html`
+
+
+
+
+ `)}
+
+ `;
+ }
+
+ valueChanged(ev) {
+ ev.stopPropagation();
+ if (!this.data || !this.hass) {
+ return;
+ }
+ const value = ev.detail.value || '';
+ const index = (ev.target).index || 0;
+ const newListValue = this.data.concat();
+
+ newListValue[index] = value;
+
+ fireEvent(this, 'value-changed', { value: newListValue });
+ }
+
+ addRow(ev) {
+ ev.stopPropagation();
+ const value = {};
+ if (!this.data) {
+ fireEvent(this, 'value-changed', { value: [value] });
+ return;
+ }
+
+ fireEvent(this, 'value-changed', { value: [...this.data, value] });
+ }
+
+ removeRow(ev) {
+ ev.stopPropagation();
+ if (!this.data || !this.hass) {
+ return;
+ }
+ const index = (ev.currentTarget).index || 0;
+ const newListValue = this.data.concat();
+
+ newListValue.splice(index, 1);
+
+ fireEvent(this, 'value-changed', { value: newListValue });
+ }
+
+ static get styles() {
+ return css`
+ .list-entry {
+ display: flex;
+ align-items: center;
+ margin-bottom: 8px;
+ }
+ .list-entry ha-form {
+ flex-grow: 1;
+ }
+ `;
+ }
+}
+
+customElements.define('ha-form-mgc-list', MGCList);
diff --git a/src/editor/components/subPageHeader.js b/src/editor/components/subPageHeader.js
new file mode 100644
index 00000000..0acecd5e
--- /dev/null
+++ b/src/editor/components/subPageHeader.js
@@ -0,0 +1,58 @@
+import { fireEvent } from 'custom-card-helpers';
+import { LitElement, html, css } from 'lit-element';
+import { mdiPlus, mdiArrowLeft } from '@mdi/js';
+
+class SubPageHeader extends LitElement {
+ static get properties() {
+ return {
+ name: {},
+ adding: { reflect: true },
+ };
+ }
+
+ render() {
+ return html`
+
+ `;
+ }
+
+ goBack() {
+ fireEvent(this, 'go-back');
+ }
+
+ addRow() {
+ fireEvent(this, 'add-row');
+ }
+
+ static get styles() {
+ return css`
+ .header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .back-title {
+ display: flex;
+ align-items: center;
+ font-size: 18px;
+ }
+ `;
+ }
+}
+
+customElements.define('mini-graph-card-subpage-header', SubPageHeader);
diff --git a/src/editor/editor.js b/src/editor/editor.js
new file mode 100644
index 00000000..7ee98524
--- /dev/null
+++ b/src/editor/editor.js
@@ -0,0 +1,172 @@
+import { fireEvent } from 'custom-card-helpers';
+import { LitElement, html } from 'lit-element';
+import './components/entitiesEditor';
+import './components/entityEditor';
+import './components/mgc_list';
+import { setupTranslations } from '../localize/localize';
+import { MAINSCHEMA, BOOLEANS } from './editorConst';
+import { booleanToString, stringToBoolean } from './editorUtils';
+
+class MiniGraphCardEditor extends LitElement {
+ static get properties() {
+ return {
+ hass: {},
+ _config: {},
+ subElementEditorConfig: {},
+ };
+ }
+
+ setConfig(config) {
+ this._config = config;
+ this._entities = config.entities;
+ setupTranslations(this.hass);
+ }
+
+ valueChanged(ev) {
+ ev.stopPropagation();
+ const newConfig = ev.detail.value || '';
+ const newShow = {};
+
+ if (typeof newConfig.show !== 'undefined') {
+ Object.keys(newConfig.show).forEach((key) => {
+ if (typeof newConfig.show[key] !== 'undefined') {
+ newShow[key] = stringToBoolean(newConfig.show[key]);
+ }
+ });
+ }
+
+ if (!this._config || !this.hass) {
+ return;
+ }
+
+ fireEvent(this, 'config-changed', {
+ config:
+ {
+ ...newConfig,
+ show: Object.keys(newShow).length !== 0 ? newShow : undefined,
+ },
+ });
+ }
+
+ buildShowObject(showObject) {
+ if (typeof showObject === 'undefined') {
+ return undefined;
+ }
+ const show = {};
+ Object.keys(showObject).forEach((key) => {
+ if (!BOOLEANS.includes(key)) {
+ show[key] = booleanToString(showObject[key]);
+ } else {
+ show[key] = showObject[key];
+ }
+ });
+ return show;
+ }
+
+ entitiesChanged(ev) {
+ ev.stopPropagation();
+ if (!this._config || !this.hass) {
+ return;
+ }
+ fireEvent(this, 'config-changed', { config: { ...this._config, entities: ev.detail } });
+ }
+
+ computeLabel(schema) {
+ let localized = this.hass.localize(`ui.panel.lovelace.editor.card.generic.${schema.name}`);
+ if (localized !== '') {
+ return localized;
+ }
+ localized = this.hass.localize(`ui.panel.lovelace.editor.card.mgc.${schema.name}`);
+ if (localized !== '') {
+ return localized;
+ }
+ return schema.name;
+ }
+
+ localizeValue(key) {
+ const localized = this.hass.localize(`ui.panel.lovelace.editor.card.mgc.values.${key}`);
+ if (localized !== '') {
+ return localized;
+ }
+ return key;
+ }
+
+ render() {
+ if (!this.hass || !this._config) {
+ return html``;
+ }
+
+ if (this.subElementEditorConfig !== undefined) {
+ return this.renderSubElement();
+ }
+
+ const SHOW = this._config.show;
+ const DATA = {
+ ...this._config,
+ show: this.buildShowObject(SHOW),
+ };
+
+ return html`
+
+
+
+
+ `;
+ }
+
+ goBack() {
+ this.subElementEditorConfig = undefined;
+ }
+
+ renderSubElement() {
+ switch (this.subElementEditorConfig.type) {
+ case 'entity':
+ return html`
+
+ `;
+ default:
+ return html``;
+ }
+ }
+
+ subElementChanged(ev) {
+ ev.stopPropagation();
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const index = this.subElementEditorConfig.index || 0;
+ if (index !== undefined) {
+ const configentities = [...this._entities];
+ configentities[index] = ev.detail;
+ fireEvent(this, 'config-changed', { config: { ...this._config, entities: configentities } });
+ }
+ }
+
+ editRow(ev) {
+ ev.stopPropagation();
+ if (!this._config || !this.hass) {
+ return;
+ }
+ const id = ev.detail;
+ this.subElementEditorConfig = { type: 'entity', index: id };
+ }
+}
+
+customElements.define('mini-graph-card-editor', MiniGraphCardEditor);
diff --git a/src/editor/editorConst.js b/src/editor/editorConst.js
new file mode 100644
index 00000000..582e272d
--- /dev/null
+++ b/src/editor/editorConst.js
@@ -0,0 +1,546 @@
+import {
+ mdiAlignHorizontalLeft, mdiArrowExpandVertical, mdiEye,
+ mdiFormatColorFill,
+ mdiPalette,
+ mdiStateMachine,
+} from '@mdi/js';
+
+const MAINSCHEMA = [
+ {
+ name: 'appearance',
+ type: 'expandable',
+ iconPath: mdiPalette,
+ flatten: true,
+ schema: [
+ {
+ name: '',
+ type: 'grid',
+ schema: [
+ {
+ name: 'name',
+ label: 'Name',
+ selector: { text: {} },
+ },
+ {
+ name: 'icon',
+ selector: { icon: {} },
+ },
+ {
+ name: 'unit',
+ selector: { text: {} },
+ },
+ {
+ name: 'hour24',
+ selector: { boolean: {} },
+ },
+ {
+ name: 'hours_to_show',
+ selector: { number: { min: 1 } },
+ },
+ {
+ name: 'points_per_hour',
+ selector: { number: { min: 0.1, step: 0.1 } },
+ },
+ {
+ name: 'aggregate_func',
+ selector: {
+ select: {
+ options: [
+ { label: 'Average', value: 'avg' },
+ { label: 'Median', value: 'median' },
+ { label: 'Minimum', value: 'min' },
+ { label: 'Maximum', value: 'max' },
+ { label: 'First', value: 'first' },
+ { label: 'Last', value: 'last' },
+ { label: 'Sum', value: 'sum' },
+ { label: 'Delta', value: 'delta' },
+ { label: 'Diff', value: 'diff' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'aggregate_func',
+ },
+ },
+ },
+ {
+ name: 'group_by',
+ selector: {
+ select: {
+ options: [
+ { label: 'Interval', value: 'interval' },
+ { label: 'Date', value: 'date' },
+ { label: 'Hour', value: 'hour' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'group_by',
+ },
+ },
+ },
+ {
+ name: 'value_factor',
+ selector: { number: {} },
+ },
+ {
+ name: 'bar_spacing',
+ selector: { number: { min: 0.1, step: 0.1 } },
+ },
+ {
+ name: 'line_width',
+ selector: { number: { min: 0.1, step: 0.1 } },
+ },
+ {
+ name: 'color_thresholds_transition',
+ selector: {
+ select: {
+ options: [
+ { label: 'Smooth', value: 'smooth' },
+ { label: 'Hard', value: 'hard' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'transition',
+ },
+ },
+ },
+ {
+ name: 'animate',
+ selector: { boolean: {} },
+ },
+ {
+ name: 'logarithmic',
+ selector: { boolean: {} },
+ },
+ ],
+ },
+ {
+ name: 'bounds',
+ type: 'expandable',
+ iconPath: mdiArrowExpandVertical,
+ flatten: true,
+ schema: [
+ {
+ name: '',
+ type: 'grid',
+ schema: [
+ {
+ name: 'lower_bound',
+ selector: { text: {} },
+ },
+ {
+ name: 'upper_bound',
+ selector: { text: {} },
+ },
+ {
+ name: 'min_bound_range',
+ selector: { number: { step: 0.1 } },
+ },
+ ],
+ },
+ {
+ name: '',
+ type: 'grid',
+ schema: [
+ {
+ name: 'lower_bound_secondary',
+ selector: { text: {} },
+ },
+ {
+ name: 'upper_bound_secondary',
+ selector: { text: {} },
+ },
+ {
+ name: 'min_bound_range_secondary',
+ selector: { number: { step: 0.1 } },
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: 'alignment',
+ type: 'expandable',
+ iconPath: mdiAlignHorizontalLeft,
+ flatten: true,
+ schema: [
+ {
+ name: '',
+ type: 'grid',
+ schema: [
+ {
+ name: 'align_header',
+ selector: {
+ select: {
+ options: [
+ { label: 'Default', value: 'default' },
+ { label: 'Left', value: 'left' },
+ { label: 'Right', value: 'right' },
+ { label: 'Center', value: 'center' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'alignment',
+ },
+ },
+ },
+ {
+ name: 'align_icon',
+ selector: {
+ select: {
+ options: [
+ { label: 'Left', value: 'left' },
+ { label: 'Right', value: 'right' },
+ { label: 'State', value: 'state' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'alignment',
+ },
+ },
+ },
+ {
+ name: 'align_state',
+ selector: {
+ select: {
+ options: [
+ { label: 'Left', value: 'left' },
+ { label: 'Right', value: 'right' },
+ { label: 'Center', value: 'center' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'alignment',
+ },
+ },
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: 'show',
+ type: 'expandable',
+ iconPath: mdiEye,
+ schema: [
+ {
+ name: '',
+ type: 'grid',
+ schema: [
+ {
+ name: 'name',
+ default: true,
+ selector: { boolean: {} },
+ },
+ {
+ name: 'icon',
+ default: true,
+ selector: { boolean: {} },
+ },
+ {
+ name: 'state',
+ selector: {
+ select: {
+ options: [
+ { label: 'Show', value: 'show' },
+ { label: 'Hide', value: 'hide' },
+ { label: 'Last', value: 'last' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'state',
+ },
+ },
+ },
+ {
+ name: 'graph',
+ selector: {
+ select: {
+ options: [
+ { label: 'Line', value: 'line' },
+ { label: 'Bar', value: 'bar' },
+ { label: 'Hide', value: 'hide' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'graph',
+ },
+ },
+ },
+ {
+ name: 'fill',
+ selector: {
+ select: {
+ options: [
+ { label: 'Show', value: 'show' },
+ { label: 'Hide', value: 'hide' },
+ { label: 'Fade', value: 'fade' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'fill',
+ },
+ },
+ },
+ {
+ name: 'points',
+ selector: {
+ select: {
+ options: [
+ { label: 'Show', value: 'show' },
+ { label: 'Hide', value: 'hide' },
+ { label: 'Hover', value: 'hover' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'points',
+ },
+ },
+ },
+ {
+ name: 'labels',
+ selector: {
+ select: {
+ options: [
+ { label: 'Show', value: 'show' },
+ { label: 'Hide', value: 'hide' },
+ { label: 'Hover', value: 'hover' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'labels',
+ },
+ },
+ },
+ {
+ name: 'labels_secondary',
+ selector: {
+ select: {
+ options: [
+ { label: 'Show', value: 'show' },
+ { label: 'Hide', value: 'hide' },
+ { label: 'Hover', value: 'hover' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'labels',
+ },
+ },
+ },
+ {
+ name: 'legend',
+ default: true,
+ selector: { boolean: {} },
+ },
+ {
+ name: 'average',
+ selector: { boolean: {} },
+ },
+ {
+ name: 'extrema',
+ selector: { boolean: {} },
+ },
+
+ {
+ name: 'name_adaptive_color',
+ selector: { boolean: {} },
+ },
+ {
+ name: 'icon_adaptive_color',
+ selector: { boolean: {} },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: 'color_thresholds',
+ type: 'expandable',
+ flatten: true,
+ iconPath: mdiFormatColorFill,
+ schema: [
+ {
+ name: 'color_thresholds',
+ type: 'mgc-list',
+ schema: [
+ {
+ name: '',
+ type: 'grid',
+ column_min_width: '100px',
+ schema: [
+ {
+ name: 'value',
+ selector: { number: { step: 0.1 } },
+ },
+ {
+ name: 'color',
+ selector: { hex_color: {} },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: 'state_map',
+ type: 'expandable',
+ flatten: true,
+ iconPath: mdiStateMachine,
+ schema: [
+ {
+ name: 'state_map',
+ type: 'mgc-list',
+ schema: [
+ {
+ name: '',
+ type: 'grid',
+ column_min_width: '100px',
+ schema: [
+ {
+ name: 'value',
+ selector: { text: {} },
+ },
+ {
+ name: 'label',
+ selector: { text: {} },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: 'tap_action',
+ selector: { ui_action: {} },
+ },
+];
+
+const ENTITYSCHEMA = [
+ {
+ name: '',
+ type: 'grid',
+ schema: [
+ {
+ name: 'entity',
+ selector: { entity: {} },
+ },
+ {
+ name: 'attribute',
+ selector: { attribute: {} },
+ context: { filter_entity: 'entity' },
+ },
+ {
+ name: 'name',
+ selector: { text: {} },
+ },
+ {
+ name: 'unit',
+ selector: { text: {} },
+ },
+ {
+ name: 'color',
+ selector: { hex_color: { clearable: true } },
+ },
+ {
+ name: 'state_adaptive_color',
+ selector: { boolean: {} },
+ },
+ {
+ name: 'aggregate_func',
+ selector: {
+ select: {
+ options: [
+ { label: 'Average', value: 'avg' },
+ { label: 'Median', value: 'median' },
+ { label: 'Minimum', value: 'min' },
+ { label: 'Maximum', value: 'max' },
+ { label: 'First', value: 'first' },
+ { label: 'Last', value: 'last' },
+ { label: 'Sum', value: 'sum' },
+ { label: 'Delta', value: 'delta' },
+ { label: 'Diff', value: 'diff' },
+ ],
+ mode: 'dropdown',
+ translation_key: 'aggregate_func',
+ },
+ },
+ },
+ ],
+ },
+ {
+ name: 'show',
+ type: 'expandable',
+ iconPath: mdiEye,
+ flatten: true,
+ schema: [
+ {
+ name: '',
+ type: 'grid',
+ schema: [
+ {
+ name: 'show_state',
+ default: true,
+ selector: { boolean: {} },
+ },
+ {
+ name: 'show_indicator',
+ selector: { boolean: {} },
+ },
+ {
+ name: 'show_graph',
+ default: true,
+ selector: { boolean: {} },
+ },
+ {
+ name: 'show_line',
+ default: true,
+ selector: { boolean: {} },
+ },
+ {
+ name: 'show_fill',
+ default: true,
+ selector: { boolean: {} },
+ },
+ {
+ name: 'show_points',
+ default: true,
+ selector: { boolean: {} },
+ },
+ {
+ name: 'show_legend',
+ default: true,
+ selector: { boolean: {} },
+ },
+ {
+ name: 'show_adaptive_color',
+ selector: { boolean: {} },
+ },
+ {
+ name: 'smoothing',
+ default: true,
+ selector: { boolean: {} },
+ },
+ ],
+ },
+ ],
+ },
+ {
+ name: 'y_axis',
+ selector: {
+ select: {
+ options: [
+ { label: 'Primary', value: 'primary' },
+ { label: 'Secondary', value: 'secondary' },
+ ],
+ translation_key: 'y_axis',
+ },
+ },
+ },
+];
+
+const BOOLEANS = [
+ 'name',
+ 'icon',
+ 'legend',
+ 'average',
+ 'extrema',
+ 'name_adaptive_color',
+ 'icon_adaptive_color',
+];
+
+export {
+ MAINSCHEMA,
+ ENTITYSCHEMA,
+ BOOLEANS,
+};
diff --git a/src/editor/editorUtils.js b/src/editor/editorUtils.js
new file mode 100644
index 00000000..5159c20d
--- /dev/null
+++ b/src/editor/editorUtils.js
@@ -0,0 +1,34 @@
+
+const stringToBoolean = (string) => {
+ if (/^show$/i.test(string)) {
+ return true;
+ } else if (/^hide$/i.test(string)) {
+ return false;
+ }
+ return string;
+};
+
+const booleanToString = (bool) => {
+ if (typeof bool === 'boolean') {
+ if (bool) {
+ return 'show';
+ } else {
+ return 'hide';
+ }
+ }
+
+ return bool;
+};
+
+const convertColorNameToHex = (color) => {
+ const canvas = document.createElement('canvas').getContext('2d');
+ canvas.fillStyle = color;
+ return canvas.fillStyle;
+};
+
+const isValidHex = hex => /^#([0-9A-F]{3}){1,2}$/i.test(hex);
+
+export {
+ stringToBoolean, booleanToString,
+ convertColorNameToHex, isValidHex,
+};
diff --git a/src/localize/languages/de.json b/src/localize/languages/de.json
new file mode 100644
index 00000000..dd1e0a75
--- /dev/null
+++ b/src/localize/languages/de.json
@@ -0,0 +1,49 @@
+{
+ "edit_entity": "Entity bearbeiten",
+ "hour24": "Zeit im 24-Stunden-Format anzeigen",
+ "points_per_hour": "Datenpunkte pro Stunde",
+ "group_by": "Daten gruppieren",
+ "aggregate_func": "Aggregatfunktion",
+ "align_header": "Kopfzeile Ausrichtung",
+ "align_icon": "Icon Ausreichtung",
+ "align_state": "Status Ausreichtung",
+ "graph": "Graph",
+ "fill": "Füllung",
+ "points": "Punkte",
+ "legend": "Legende",
+ "average": "Durchschnitt",
+ "extrema": "Extrema",
+ "labels": "Beschriftungen",
+ "labels_secondary": "Sekundäre Beschriftungen",
+ "name_adaptive_color": "Name adaptive Farbe",
+ "icon_adaptive_color": "Icon adaptive Farbe",
+ "animate": "Animieren",
+ "bar_spacing": "Balkenabstand",
+ "line_width": "Linienbreite",
+ "color_thresholds_transition": "Farbschwellen Übergang",
+ "logarithmic": "Logarithmisch",
+ "lower_bound": "Untergrenze",
+ "upper_bound": "Obergrenze",
+ "min_bound_range": "Min Grenzbereich",
+ "lower_bound_secondary": "Sekundäre Untergrenze",
+ "upper_bound_secondary": "Sekundäre Obergrenze",
+ "min_bound_range_secondary": "Min sekundärer Grenzbereich",
+ "value_factor": "Faktor",
+ "color_thresholds": "Farbschwellen",
+ "state_map": "Statuszuordnung",
+ "color": "Farbe",
+ "value": "Wert",
+ "label": "Beschriftung",
+ "use_color_thresholds": "Farbschwellen verwenden",
+ "state_adaptive_color": "Status adaptive Farbe",
+ "show_state": "Status anzeigen",
+ "show_indicator": "Indicator anzeigen",
+ "show_graph": "Graph anzeigen",
+ "show_line": "Linie anzeigen",
+ "show_fill": "Füllung anzeigen",
+ "show_points": "Punkte anzeigen",
+ "show_legend": "Legende anzeigen",
+ "show_adaptive_color": "Adaptive Farbe verwenden",
+ "smoothing": "Glätten",
+ "y_axis": "Achse"
+}
\ No newline at end of file
diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json
new file mode 100644
index 00000000..f52946f0
--- /dev/null
+++ b/src/localize/languages/en.json
@@ -0,0 +1,131 @@
+{
+ "edit_entity": "Edit entity",
+ "hour24": "Display time in 24-hour format",
+ "points_per_hour": "Data points per hour",
+ "group_by": "Data grouping",
+ "aggregate_func": "Aggregate function",
+ "align_header": "Header alignment",
+ "align_icon": "Icon alignment",
+ "align_state": "State alignment",
+ "graph": "Graph",
+ "fill": "Fill",
+ "points": "Points",
+ "legend": "Legend",
+ "average": "Average",
+ "extrema": "Extrema",
+ "labels": "Labels",
+ "labels_secondary": "Secondary labels",
+ "name_adaptive_color": "Name adaptive color",
+ "icon_adaptive_color": "Icon adaptive color",
+ "animate": "Animate",
+ "bar_spacing": "Bar spacing",
+ "line_width": "Line width",
+ "color_thresholds_transition": "Color thresholds transition",
+ "logarithmic": "Logarithmic",
+ "lower_bound": "Lower bound",
+ "upper_bound": "Upper bound",
+ "min_bound_range": "Min bound range",
+ "lower_bound_secondary": "Secondary lower bound",
+ "upper_bound_secondary": "Secondary upper bound",
+ "min_bound_range_secondary": "Min secondary bound range",
+ "value_factor": "Value factor",
+ "color_thresholds": "Color thresholds",
+ "state_map": "State map",
+ "color": "Color",
+ "value": "Value",
+ "label": "Label",
+ "use_color_thresholds": "Use color thresholds",
+ "state_adaptive_color": "State adaptive color",
+ "show_state": "Show state",
+ "show_indicator": "Show indicator",
+ "show_graph": "Show graph",
+ "show_line": "Show line",
+ "show_fill": "Show fill",
+ "show_points": "Show points",
+ "show_legend": "Show legends",
+ "show_adaptive_color": "Show adaptive color",
+ "smoothing": "Smoothing",
+ "y_axis": "Axis",
+ "appearance": "Appearance",
+ "bounds": "Bounds",
+ "alignment": "Alignment",
+ "show": "Display",
+ "values": {
+ "aggregate_func": {
+ "options": {
+ "avg": "Average",
+ "median": "Median",
+ "min": "Minimum",
+ "max": "Maximum",
+ "first": "First",
+ "last": "Last",
+ "sum": "Sum",
+ "delta": "Delta",
+ "diff": "Diff"
+ }
+ },
+ "group_by": {
+ "options": {
+ "interval": "Interval",
+ "date": "Date",
+ "hour": "Hour"
+ }
+ },
+ "transition": {
+ "options": {
+ "smooth": "Smooth",
+ "hard": "Hard"
+ }
+ },
+ "alignment": {
+ "options": {
+ "default": "Default",
+ "left": "Left",
+ "right": "Right",
+ "center": "Center",
+ "state": "State"
+ }
+ },
+ "state": {
+ "options": {
+ "show": "Show",
+ "hide": "Hide",
+ "last": "Last"
+ }
+ },
+ "graph": {
+ "options": {
+ "line": "Line",
+ "bar": "Bar",
+ "hide": "Hide"
+ }
+ },
+ "fill": {
+ "options": {
+ "show": "Show",
+ "hide": "Hide",
+ "fade": "Fade"
+ }
+ },
+ "points": {
+ "options": {
+ "show": "Show",
+ "hide": "Hide",
+ "hover": "Hover"
+ }
+ },
+ "labels": {
+ "options": {
+ "show": "Show",
+ "hide": "Hide",
+ "hover": "Hover"
+ }
+ },
+ "y_axis": {
+ "options":{
+ "primary": "Primary",
+ "secondary": "Secondary"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/localize/localize.js b/src/localize/localize.js
new file mode 100644
index 00000000..39da4168
--- /dev/null
+++ b/src/localize/localize.js
@@ -0,0 +1,44 @@
+import * as en from './languages/en.json';
+import * as de from './languages/de.json';
+
+const languages = {
+ en,
+ de,
+};
+
+const DEFAULT_LANG = 'en';
+
+function processTranslations(obj) {
+ let keys = [];
+ Object.keys(obj).forEach((key) => {
+ if (typeof obj[key] === 'object') {
+ const subKeys = processTranslations(obj[key]);
+ keys = keys.concat(subKeys.map(subkey => `${key}.${subkey}`));
+ } else {
+ keys.push(key);
+ }
+ });
+ return keys;
+}
+
+export default function setupTranslations(hass) {
+ const lang = hass.locale.language || DEFAULT_LANG;
+ const hassObject = hass;
+ const resources = hassObject.resources[hass.locale.language];
+ const languageObject = languages[lang] || languages[DEFAULT_LANG];
+
+ const keys = processTranslations(languageObject);
+
+ keys.forEach((key) => {
+ if (!key.startsWith('default')) {
+ const nestedKeys = key.split('.');
+ const value = nestedKeys.reduce((a, c) => a[c], languageObject);
+
+ if (value) {
+ resources[`ui.panel.lovelace.editor.card.mgc.${key}`] = value;
+ }
+ }
+ });
+}
+
+export { setupTranslations };
diff --git a/src/main.js b/src/main.js
index 46496c4a..1d4abb0b 100644
--- a/src/main.js
+++ b/src/main.js
@@ -7,6 +7,7 @@ import Graph from './graph';
import style from './style';
import handleClick from './handleClick';
import buildConfig from './buildConfig';
+import './editor/editor';
import './initialize';
import { version } from '../package.json';
@@ -50,6 +51,10 @@ class MiniGraphCard extends LitElement {
this._md5Config = undefined;
}
+ static getConfigElement() {
+ return document.createElement('mini-graph-card-editor');
+ }
+
static get styles() {
return style;
}