diff --git a/README.md b/README.md index 32c7725..1753e5d 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ The card stricly validates all the options available (but not for the `apex_conf | `graph_span` | string | `24h` | v1.1.0 | The span of the graph as a time interval. Valid values are any time string, eg: `1h`, `12min`, `1d`, `1h25`, `10sec`, ... | | `span` | object | | v1.2.0 | See [span](#span-options) | | `show` | object | | v1.0.0 | See [show](#main-show-options) | -| `hours_12` | boolean | `false` | v1.5.0 | Display time in 12h format instead of the default 24h format | +| ~~`hours_12`~~ | ~~boolean~~ | ~~`false`~~ | ~~v1.5.0~~ | **Deprecated since NEXT_VERSION** ~~Display time in 12h format instead of the default 24h format~~ | | `cache` | boolean | `true` | v1.0.0 | Use in-browser data caching to reduce the load on Home Assistant's server | | `stacked` | boolean | `false` | v1.0.0 | Enable if you want the data to be stacked on the graph | | `layout` | string | | v1.0.0 | See [layouts](#layouts) | @@ -141,6 +141,7 @@ The card stricly validates all the options available (but not for the `apex_conf | `y_axis_precision` | numnber | `1` | v1.2.0 | The float precision used to display numbers on the Y axis | | `apex_config`| object | | v1.0.0 | Apexcharts API 1:1 mapping. You call see all the options [here](https://apexcharts.com/docs/installation/) --> `Options (Reference)` in the Menu. See [Apex Charts](#apex-charts-options-example) | | `experimental` | object | | v1.6.0 | See [experimental](#experimental-features) | +| `locale` | string | | NEXT_VERSION | Default is to inherit from Home-Assistant's user configuration. This overrides it and forces the locale. Eg: `en`, or `fr`. Reverts to `en` if locale is unknown. | diff --git a/src/apex-layouts.ts b/src/apex-layouts.ts index 6c4e434..25c46cb 100644 --- a/src/apex-layouts.ts +++ b/src/apex-layouts.ts @@ -5,18 +5,89 @@ import { DEFAULT_FLOAT_PRECISION, DEFAULT_SERIE_TYPE, HOUR_24, - moment, NO_VALUE, PLAIN_COLOR_TYPES, TIMESERIES_TYPES, } from './const'; import { ChartCardConfig } from './types'; -import { computeName, computeUom, mergeDeep, prettyPrintTime } from './utils'; +import { computeName, computeUom, is12Hour, mergeDeep, prettyPrintTime } from './utils'; import { layoutMinimal } from './layouts/minimal'; +import * as ca from 'apexcharts/dist/locales/ca.json'; +import * as cs from 'apexcharts/dist/locales/cs.json'; +import * as de from 'apexcharts/dist/locales/de.json'; +import * as el from 'apexcharts/dist/locales/el.json'; +import * as en from 'apexcharts/dist/locales/en.json'; +import * as es from 'apexcharts/dist/locales/es.json'; +import * as fi from 'apexcharts/dist/locales/fi.json'; +import * as fr from 'apexcharts/dist/locales/fr.json'; +import * as he from 'apexcharts/dist/locales/he.json'; +import * as hi from 'apexcharts/dist/locales/hi.json'; +import * as hr from 'apexcharts/dist/locales/hr.json'; +import * as hy from 'apexcharts/dist/locales/hy.json'; +import * as id from 'apexcharts/dist/locales/id.json'; +import * as it from 'apexcharts/dist/locales/it.json'; +import * as ka from 'apexcharts/dist/locales/ka.json'; +import * as ko from 'apexcharts/dist/locales/ko.json'; +import * as lt from 'apexcharts/dist/locales/lt.json'; +import * as nb from 'apexcharts/dist/locales/nb.json'; +import * as nl from 'apexcharts/dist/locales/nl.json'; +import * as pl from 'apexcharts/dist/locales/pl.json'; +import * as pt_br from 'apexcharts/dist/locales/pt-br.json'; +import * as pt from 'apexcharts/dist/locales/pt.json'; +import * as rs from 'apexcharts/dist/locales/rs.json'; +import * as ru from 'apexcharts/dist/locales/ru.json'; +import * as se from 'apexcharts/dist/locales/se.json'; +import * as sk from 'apexcharts/dist/locales/sk.json'; +import * as sl from 'apexcharts/dist/locales/sl.json'; +import * as sq from 'apexcharts/dist/locales/sq.json'; +import * as th from 'apexcharts/dist/locales/th.json'; +import * as tr from 'apexcharts/dist/locales/tr.json'; +import * as ua from 'apexcharts/dist/locales/ua.json'; +import * as zh_cn from 'apexcharts/dist/locales/zh-cn.json'; export function getLayoutConfig(config: ChartCardConfig, hass: HomeAssistant | undefined = undefined): unknown { + const locales = { + ca: ca, + cs: cs, + de: de, + el: el, + en: en, + es: es, + fi: fi, + fr: fr, + he: he, + hi: hi, + hr: hr, + hy: hy, + id: id, + it: it, + ka: ka, + ko: ko, + lt: lt, + nb: nb, + nl: nl, + pl: pl, + 'pt-br': pt_br, + pt: pt, + rs: rs, + ru: ru, + se: se, + sk: sk, + sl: sl, + sq: sq, + th: th, + tr: tr, + ua: ua, + 'zh-cn': zh_cn, + }; + console.log(hass?.language); const def = { chart: { + locales: [(config.locale && locales[config.locale]) || (hass?.language && locales[hass.language]) || en], + defaultLocale: + (config.locale && locales[config.locale] && config.locale) || + (hass?.language && locales[hass.language] && hass.language) || + 'en', type: config.chart_type || DEFAULT_SERIE_TYPE, stacked: config?.stacked, foreColor: 'var(--primary-text-color)', @@ -37,11 +108,11 @@ export function getLayoutConfig(config: ChartCardConfig, hass: HomeAssistant | u }, series: getSeries(config, hass), labels: getLabels(config, hass), - xaxis: getXAxis(config), + xaxis: getXAxis(config, hass), yaxis: getYAxis(config), tooltip: { x: { - formatter: getXTooltipFormatter(config), + formatter: getXTooltipFormatter(config, config.locale || hass?.language || 'en'), }, y: { formatter: getYTooltipFormatter(config, hass), @@ -118,14 +189,15 @@ function getLabels(config: ChartCardConfig, hass: HomeAssistant | undefined) { } } -function getXAxis(config: ChartCardConfig) { +function getXAxis(config: ChartCardConfig, hass: HomeAssistant | undefined) { if (TIMESERIES_TYPES.includes(config.chart_type)) { + const hours12 = is12Hour(config.locale || hass?.language || 'en'); return { type: 'datetime', // range: getMilli(config.hours_to_show), labels: { datetimeUTC: false, - datetimeFormatter: getDateTimeFormatter(config.hours_12), + datetimeFormatter: getDateTimeFormatter(hours12), }, }; } else { @@ -161,21 +233,17 @@ function getDateTimeFormatter(hours12: boolean | undefined): unknown { } } -function getXTooltipFormatter(config: ChartCardConfig): ((val: number) => string) | undefined { +function getXTooltipFormatter(config: ChartCardConfig, lang: string): ((val: number) => string) | undefined { if (config.apex_config?.tooltip?.x?.format) return undefined; - let hours = 'HH:mm:ss'; - let days = 'MMM Do, HH:mm:ss'; - if (config.hours_12) { - hours = 'hh:mm:ss a'; - days = 'MMM Do, hh:mm:ss'; - } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return parse(config.graph_span)! < HOUR_24 && !config.span?.offset ? function (val) { - return moment(new Date(val)).format(hours); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return new Intl.DateTimeFormat(lang, { timeStyle: 'medium' } as any).format(val); } : function (val) { - return moment(new Date(val)).format(days); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return new Intl.DateTimeFormat(lang, { dateStyle: 'medium', timeStyle: 'medium' } as any).format(val); }; } diff --git a/src/graphEntry.ts b/src/graphEntry.ts index bac82b2..9ee8b4f 100644 --- a/src/graphEntry.ts +++ b/src/graphEntry.ts @@ -33,8 +33,6 @@ export default class GraphEntry { private _config: ChartCardSeriesConfig; - private _timeRange: DateRange; - private _func: (item: EntityCachePoints) => number; private _realStart: Date; @@ -72,7 +70,6 @@ export default class GraphEntry { const now2 = new Date(now); this._func = aggregateFuncMap[config.group_by.func]; now2.setTime(now2.getTime() - HOUR_24); - this._timeRange = moment.range(now, now2); this._realEnd = new Date(); this._realStart = new Date(); // Valid because tested during init; @@ -142,7 +139,6 @@ export default class GraphEntry { } if (!this._entityState || this._updating) return false; this._updating = true; - this._timeRange = moment.range(startHistory, end); let history: EntityEntryCache | undefined = undefined; if (this._config.data_generator) { @@ -256,7 +252,7 @@ export default class GraphEntry { return false; } if (this._config.group_by.func !== 'raw') { - this._computedHistory = this._dataBucketer(history).map((bucket) => { + this._computedHistory = this._dataBucketer(history, moment.range(startHistory, end)).map((bucket) => { return [bucket.timestamp, this._func(bucket.data)]; }); } else { @@ -317,8 +313,8 @@ export default class GraphEntry { }; } - private _dataBucketer(history: EntityEntryCache): HistoryBuckets { - const ranges = Array.from(this._timeRange.reverseBy('milliseconds', { step: this._groupByDurationMs })).reverse(); + private _dataBucketer(history: EntityEntryCache, timeRange: DateRange): HistoryBuckets { + const ranges = Array.from(timeRange.reverseBy('milliseconds', { step: this._groupByDurationMs })).reverse(); // const res: EntityCachePoints[] = [[]]; let buckets: HistoryBuckets = []; ranges.forEach((range, index) => { diff --git a/src/types-config-ti.ts b/src/types-config-ti.ts index f62d5f7..e0d8764 100644 --- a/src/types-config-ti.ts +++ b/src/types-config-ti.ts @@ -8,6 +8,7 @@ export const ChartCardExternalConfig = t.iface([], { "type": t.lit('custom:apexcharts-card'), "config_templates": t.opt(t.array("string")), "color_list": t.opt(t.array("string")), + "locale": t.opt("string"), "experimental": t.opt(t.iface([], { "color_threshold": t.opt("boolean"), "disable_config_validation": t.opt("boolean"), @@ -19,7 +20,6 @@ export const ChartCardExternalConfig = t.iface([], { "all_series_config": t.opt("ChartCardAllSeriesExternalConfig"), "series": t.array("ChartCardSeriesExternalConfig"), "graph_span": t.opt("string"), - "hours_12": t.opt("boolean"), "span": t.opt("ChartCardSpanExtConfig"), "y_axis_precision": t.opt("number"), "now": t.opt(t.iface([], { diff --git a/src/types-config.ts b/src/types-config.ts index 1150a3c..5fa2db4 100644 --- a/src/types-config.ts +++ b/src/types-config.ts @@ -2,6 +2,7 @@ export interface ChartCardExternalConfig { type: 'custom:apexcharts-card'; config_templates?: string[]; color_list?: string[]; + locale?: string; experimental?: { color_threshold?: boolean; disable_config_validation?: boolean; @@ -13,7 +14,6 @@ export interface ChartCardExternalConfig { all_series_config?: ChartCardAllSeriesExternalConfig; series: ChartCardSeriesExternalConfig[]; graph_span?: string; - hours_12?: boolean; span?: ChartCardSpanExtConfig; y_axis_precision?: number; now?: { diff --git a/src/utils.ts b/src/utils.ts index 115fcca..eda091c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -231,3 +231,7 @@ export function mergeDeepConfig(target: any, source: any): any { return target; } + +export function is12Hour(locale: string): boolean { + return !(new Date(2021, 1, 1, 15, 0, 0, 0).toLocaleTimeString(locale).indexOf('15') > -1); +}