├── .gitignore ├── .prettierrc ├── README.md ├── dist └── chartjs-card.js ├── hacs.json ├── img ├── example1.png ├── example2.png └── example3.png ├── info.md ├── package.json ├── rollup.config.js ├── src └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | yarn-error.log -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "trailingComma": "es5", 4 | "tabWidth": 2, 5 | "semi": false, 6 | "singleQuote": true, 7 | "bracketSpacing": true, 8 | "jsxBracketSameLine": false, 9 | "arrowParens": "always", 10 | "endOfLine": "auto", 11 | "jsxSingleQuote": true, 12 | "proseWrap": "preserve", 13 | "quoteProps": "as-needed", 14 | "useTabs": false, 15 | "htmlWhitespaceSensitivity": "css" 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chartjs-card 2 | 3 | [![](https://img.shields.io/github/v/release/ricreis394/chartjs-card.svg?style=flat)](https://github.com/ricreis394/chartjs-card/releases/latest) 4 | [![hacs_badge](https://img.shields.io/badge/HACS-Custom-41BDF5.svg)](https://github.com/hacs/integration) 5 | 6 | Chart.js card for Home Assistant 7 | Allows to create highly customized graphs with support for templating 8 | 9 | ## [Chart.js 3.7.0 documentation](https://www.chartjs.org/docs/3.7.0/) 10 | 11 | ![](./img/example1.png) 12 | 13 | ## Instalation through HACS 14 | 15 | This card isn't in HACS, but you can add it manually through `Custom repositories` 16 | 17 | To do that just follow these steps: **HACS -> Frontend -> 3 dots (upper right corner) -> Custom repositories -> (Paste this github page url)** 18 | 19 | ## Config 20 | 21 | | Name | Type | Default | Description | 22 | | -------------- | ------- | ------- | ------------------------------------------------------------------ | 23 | | chart | string | | chart type | 24 | | data | | | just like chart.js documentation, accepts Templates for all fields | 25 | | options | | | just like chart.js documentation | 26 | | plugins | | | just like chart.js documentation | 27 | | entitiy_row | boolean | false | if is entity row or not | 28 | | custom_options | object | | TODO | 29 | | register_plugins | array | | registers plugins to be added to graph | 30 | 31 | ## Templating 32 | 33 | Everything between Curly braces and started with a Dollar sign, will be evaluated as Javascript code 34 | 35 | You can easily access states through: 36 | 37 | ```js 38 | ${states["sensor.example"].state} 39 | ``` 40 | 41 | or you can convert a Date to a week day label: 42 | 43 | ```js 44 | ${new Date(new Date().setDate(new Date().getDate()-2)).toLocaleString("pt-PT", {weekday: "short"})} 45 | ``` 46 | 47 | or more simple for today 48 | 49 | ```js 50 | ${new Date().toLocaleString("pt-PT", {weekday: "short"})} 51 | ``` 52 | 53 | and you can convert array string to a real array like so 54 | 55 | ```js 56 | ${'[12, 14, 2, 4]'} 57 | ``` 58 | 59 | can be useful when you grab a lot of data from the DB and want to display all in the graph 60 | 61 | ## Additional plugins 62 | 63 | Some plugins can be added to the graph 64 | 65 | | Name | Link | 66 | |--|--| 67 | | zoom | [https://www.chartjs.org/chartjs-plugin-zoom/latest/](https://www.chartjs.org/chartjs-plugin-zoom/latest/) | 68 | | annotation | [https://www.chartjs.org/chartjs-plugin-annotation/latest/](https://www.chartjs.org/chartjs-plugin-annotation/latest/) | 69 | 70 | To use the plugins you need to register before using, to do that add the name into `register_plugins` like so: 71 | 72 | ```yaml 73 | register_plugins: 74 | - zoom 75 | - annotation 76 | ``` 77 | 78 | ## Examples 79 | 80 | ### example 1 81 | 82 | ![](./img/example1.png) 83 | 84 | ```yaml 85 | chart: bar 86 | data: 87 | datasets: 88 | - backgroundColor: ${states["sensor.chartjs_energy_last_30_days"].attributes.colors} 89 | borderWidth: 1 90 | data: ${states["sensor.chartjs_energy_last_30_days"].attributes.data} 91 | label: Eletricidade 92 | labels: ${states["sensor.chartjs_energy_last_30_days"].attributes.labels} 93 | custom_options: 94 | showLegend: false 95 | options: 96 | plugins: 97 | title: 98 | display: true 99 | text: Consumo energético (últimos 30 dias) 100 | entity_row: false 101 | type: custom:chartjs-card 102 | ``` 103 | 104 | ### example 2 105 | 106 | ![](./img/example2.png) 107 | 108 | ```yaml 109 | type: custom:chartjs-card 110 | chart: bar 111 | custom_options: 112 | showLegend: false 113 | data: 114 | datasets: 115 | - backgroundColor: '#2fabe0' 116 | borderWidth: 1 117 | data: 118 | - 10 119 | - 11 120 | - 10 121 | - 9 122 | - 9.6 123 | - 9 124 | - 11 125 | label: Lavar roupa 126 | - backgroundColor: '#fcba03' 127 | borderWidth: 1 128 | data: 129 | - 2 130 | - 3 131 | - 5 132 | - 2 133 | - 3 134 | - 3 135 | - 5 136 | label: Frigorífico 137 | - backgroundColor: '#c8ed11' 138 | borderWidth: 1 139 | data: 140 | - 5 141 | - 6 142 | - 5 143 | - 7 144 | - 3 145 | - 4 146 | - 5 147 | label: Placa 148 | labels: 149 | - >- 150 | ${new Date(new Date().setDate(new 151 | Date().getDate()-6)).toLocaleString("pt-PT", {weekday: "short"})} 152 | - >- 153 | ${new Date(new Date().setDate(new 154 | Date().getDate()-5)).toLocaleString("pt-PT", {weekday: "short"})} 155 | - >- 156 | ${new Date(new Date().setDate(new 157 | Date().getDate()-4)).toLocaleString("pt-PT", {weekday: "short"})} 158 | - >- 159 | ${new Date(new Date().setDate(new 160 | Date().getDate()-3)).toLocaleString("pt-PT", {weekday: "short"})} 161 | - >- 162 | ${new Date(new Date().setDate(new 163 | Date().getDate()-2)).toLocaleString("pt-PT", {weekday: "short"})} 164 | - >- 165 | ${new Date(new Date().setDate(new 166 | Date().getDate()-1)).toLocaleString("pt-PT", {weekday: "short"})} 167 | - '${new Date().toLocaleString("pt-PT", {weekday: "short"})}' 168 | entity_row: false 169 | options: 170 | scales: 171 | x: 172 | scaleLabel: 173 | display: true 174 | fontStyle: initial 175 | labelString: Dias 176 | 'y': 177 | scaleLabel: 178 | display: true 179 | fontStyle: initial 180 | labelString: Horas 181 | ticks: 182 | beginAtZero: true 183 | plugins: 184 | title: 185 | display: true 186 | text: | 187 | Consumo por semana 188 | ``` 189 | 190 | ### example 3 191 | 192 | ![](./img/example3.png) 193 | 194 | ```yaml 195 | chart: doughnut 196 | custom_options: 197 | showLegend: true 198 | data: 199 | datasets: 200 | - backgroundColor: 201 | - '#32a852' 202 | - '#3271a8' 203 | - '#9044db' 204 | - '#dbbd44' 205 | - '#6533ab' 206 | - '#364534' 207 | - '#db330d' 208 | borderColor: var(--paper-card-background-color) 209 | borderWidth: 1 210 | data: 211 | - ${states["sensor.energy_daily_placa"].state} 212 | - ${states["sensor.energy_daily_fridge"].state} 213 | - ${states["sensor.energy_daily_oven"].state} 214 | - ${states["sensor.energy_daily_microwave"].state} 215 | - ${states["sensor.energy_daily_dishwasher"].state} 216 | - ${states["sensor.energy_daily_washing_machine"].state} 217 | - ${states["sensor.energy_daily_drying_machine"].state} 218 | hoverBorderColor: var(--paper-card-background-color) 219 | labels: 220 | - Placa 221 | - Frigorifico 222 | - Forno 223 | - Micro-ondas 224 | - Máq. lavar loiça 225 | - Máq. lavar roupa 226 | - Máq. secar roupa 227 | options: 228 | plugins: 229 | legend: 230 | position: left 231 | title: 232 | display: true 233 | text: Consumo energético (hoje) 234 | type: custom:chartjs-card 235 | ``` 236 | -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Chart.js Card", 3 | "description": "Allows to create highly customized graphs with support for templating", 4 | "render_readme": false, 5 | "filename": "chartjs-card.js" 6 | } 7 | -------------------------------------------------------------------------------- /img/example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plckr/chartjs-card/07606edec64ea7279f5d59e9ebc2baf7bf8cdac0/img/example1.png -------------------------------------------------------------------------------- /img/example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plckr/chartjs-card/07606edec64ea7279f5d59e9ebc2baf7bf8cdac0/img/example2.png -------------------------------------------------------------------------------- /img/example3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plckr/chartjs-card/07606edec64ea7279f5d59e9ebc2baf7bf8cdac0/img/example3.png -------------------------------------------------------------------------------- /info.md: -------------------------------------------------------------------------------- 1 | # chartjs-card 2 | 3 | [![](https://img.shields.io/github/v/release/ricreis394/chartjs-card.svg?style=flat)](https://github.com/ricreis394/chartjs-card/releases/latest) 4 | [![hacs_badge](https://img.shields.io/badge/HACS-Custom-41BDF5.svg)](https://github.com/hacs/integration) 5 | 6 | Chart.js card for Home Assistant 7 | 8 | Allows to create highly customized graphs with support for templating 9 | 10 | ![](https://raw.githubusercontent.com/ricreis394/chartjs-card/master/img/example1.png) 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chartjs-card", 3 | "version": "1.1.1", 4 | "description": "Chart.js card to Home Assistant", 5 | "main": "src/index.js", 6 | "keywords": [ 7 | "chartjs", 8 | "homeassistant", 9 | "home-assistant" 10 | ], 11 | "dependencies": { 12 | "chart.js": "^3.7.0", 13 | "chartjs-plugin-annotation": "^1.2.2", 14 | "chartjs-plugin-zoom": "^1.2.0", 15 | "lit": "^2.1.1", 16 | "lodash": "^4.17.15" 17 | }, 18 | "devDependencies": { 19 | "@rollup/plugin-commonjs": "^21.0.1", 20 | "@rollup/plugin-json": "^4.1.0", 21 | "@rollup/plugin-node-resolve": "^13.1.3", 22 | "prettier": "^2.5.1", 23 | "rollup": "^2.63.0", 24 | "rollup-plugin-serve": "^1.1.0" 25 | }, 26 | "scripts": { 27 | "test": "echo \"Error: no test specified\" && exit 1", 28 | "build": "npm run rollup", 29 | "rollup": "rollup -c", 30 | "start": "rollup -c --watch" 31 | }, 32 | "author": "Ricardo Reis", 33 | "license": "ISC" 34 | } 35 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from '@rollup/plugin-commonjs' 2 | import { nodeResolve } from '@rollup/plugin-node-resolve' 3 | import json from '@rollup/plugin-json' 4 | import serve from 'rollup-plugin-serve' 5 | 6 | const dev = process.env.ROLLUP_WATCH 7 | 8 | const serveopts = { 9 | contentBase: ['./dist'], 10 | host: '0.0.0.0', 11 | port: 5000, 12 | allowCrossOrigin: true, 13 | headers: { 14 | 'Access-Control-Allow-Origin': '*', 15 | }, 16 | } 17 | 18 | const plugins = [ 19 | nodeResolve(), 20 | commonjs(), 21 | json({ 22 | include: 'package.json', 23 | preferConst: true, 24 | }), 25 | dev && serve(serveopts), 26 | ] 27 | 28 | export default { 29 | input: 'src/index.js', 30 | output: { 31 | file: 'dist/chartjs-card.js', 32 | format: 'umd', 33 | name: 'Chartjs-card', 34 | }, 35 | plugins: [...plugins], 36 | } 37 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import pkg from '../package.json' 2 | import Chart from 'chart.js/auto' 3 | import { LitElement, html } from 'lit' 4 | import _ from 'lodash' 5 | 6 | import zoomPlugin from 'chartjs-plugin-zoom' 7 | import annotationPlugin from 'chartjs-plugin-annotation' 8 | 9 | class Card extends LitElement { 10 | static get properties() { 11 | return { 12 | hass: { type: Object }, 13 | _config: { type: Object }, 14 | } 15 | } 16 | 17 | constructor() { 18 | super() 19 | this._initialized = false 20 | this.chart = {} 21 | this._updateFromEntities = [] 22 | this.chartConfig = {} 23 | 24 | // Set chart defaults 25 | Chart.defaults.color = this._evaluateCssVariable('var(--primary-text-color)') 26 | Chart.defaults.title = { 27 | fontSize: 14, 28 | fontStyle: 'normal', 29 | } 30 | } 31 | 32 | shouldUpdate(changedProps) { 33 | if (changedProps.has('_config')) { 34 | return true 35 | } 36 | 37 | if (this._config) { 38 | const oldHass = changedProps.get('hass') || undefined 39 | 40 | if (oldHass) { 41 | let changed = false 42 | this._updateFromEntities.forEach((entity) => { 43 | changed = changed || Boolean(this.hass && oldHass.states[entity] !== this.hass.states[entity]) 44 | }) 45 | 46 | return changed 47 | } 48 | } 49 | 50 | return false 51 | } 52 | 53 | firstUpdated() { 54 | this._initialize() 55 | } 56 | 57 | updated(changedProps) { 58 | super.updated() 59 | 60 | if (this._initialized && changedProps.has('_config')) { 61 | this._initialize() 62 | return 63 | } 64 | 65 | this._updateChart() 66 | } 67 | 68 | _initialize() { 69 | // Register zoom plugin 70 | if (Array.isArray(this._config.register_plugins)) { 71 | if (this._config.register_plugins.includes('zoom')) { 72 | Chart.register(zoomPlugin) 73 | } 74 | if (this._config.register_plugins.includes('annotation')) { 75 | Chart.register(annotationPlugin) 76 | } 77 | } 78 | 79 | if (this._initialized) this.chart.destroy() 80 | this.chartConfig = this._generateChartConfig(this._config) 81 | const ctx = this.renderRoot.querySelector('canvas').getContext('2d') 82 | this.chart = new Chart(ctx, this.chartConfig) 83 | this._initialized = true 84 | } 85 | 86 | _updateChart() { 87 | if (!this._initialized) return 88 | const chartConfig = this._generateChartConfig(this._config) 89 | this.chart.data = chartConfig.data 90 | this.chart.options = chartConfig.options 91 | this.chart.plugins = chartConfig.plugins 92 | this.chart.update('none') 93 | } 94 | 95 | _generateChartConfig(config) { 96 | // Reset dependency entities 97 | this._updateFromEntities = [] 98 | 99 | let chartconfig = { 100 | type: config.chart, 101 | data: this._evaluateConfig(config.data), 102 | options: this._evaluateConfig(config.options), 103 | plugins: this._evaluateConfig(config.plugins), 104 | } 105 | 106 | if (typeof config.custom_options === 'object') { 107 | if (typeof config.custom_options.showLegend === 'boolean') { 108 | // chartconfig.options.legend.display = config.options.showLegend; // Defaults to True 109 | _.set(chartconfig, 'options.plugins.legend.display', config.custom_options.showLegend) 110 | } 111 | } 112 | 113 | return chartconfig 114 | } 115 | 116 | _evaluateConfig(config) { 117 | // Only allow Object as input 118 | if (typeof config === 'object') { 119 | let newObj = _.cloneDeepWith(config, (v) => { 120 | if (!_.isObject(v)) { 121 | if (this._evaluateTemplate(v) !== v) { 122 | // Search for entities inputs 123 | const regexEntity = /states\[["|'](.+?)["|']\]/g 124 | const matches = v.trim().matchAll(regexEntity) 125 | for (const match of matches) { 126 | if (!this._updateFromEntities.includes(match[1])) { 127 | this._updateFromEntities.push(match[1]) 128 | } 129 | } 130 | 131 | return this._evaluateTemplate(v) 132 | } 133 | if (this._evaluateCssVariable(v) !== v) { 134 | return this._evaluateCssVariable(v) 135 | } 136 | return v 137 | } 138 | }) 139 | return newObj 140 | } else { 141 | return config 142 | } 143 | } 144 | 145 | _evaluateCssVariable(variable) { 146 | if (typeof variable !== 'string') return variable 147 | 148 | const regexCssVar = /var[(](--[^-].+)[)]/ 149 | var r = _.words(variable, regexCssVar)[1] 150 | 151 | if (!r) { 152 | return variable 153 | } 154 | 155 | return getComputedStyle(document.documentElement).getPropertyValue(r) 156 | } 157 | 158 | _evaluateTemplate(template) { 159 | if (typeof template === 'string') { 160 | const regexTemplate = /^\${(.+)}$/g 161 | if (_.includes(template, '${') && template.match(regexTemplate)) { 162 | ;('use strict') 163 | 164 | const user = this.hass?.user 165 | const states = this.hass?.states 166 | const hass = this.hass 167 | 168 | // Workaround to avoid rollup to remove above variables 169 | if (!user || !states || !hass) console.log('this never executes') 170 | 171 | const evaluated = eval(template.trim().substring(2, template.length - 1)) 172 | 173 | if (Array.isArray(evaluated)) { 174 | return evaluated.map((r) => this._evaluateCssVariable(r)) 175 | } 176 | 177 | const regexArray = /^\[[^\]]+\]$/g 178 | if (typeof evaluated === 'string' && evaluated.match(regexArray)) { 179 | try { 180 | return eval(evaluated).map((r) => this._evaluateCssVariable(r)) 181 | } catch (e) { 182 | return evaluated 183 | } 184 | } 185 | return evaluated 186 | } 187 | } 188 | return template 189 | } 190 | 191 | setConfig(config) { 192 | // Deep clone 193 | this._config = JSON.parse(JSON.stringify(config)) 194 | 195 | const availableTypes = ['line', 'bar', 'radar', 'doughnut', 'pie', 'polarArea', 'bubble', 'scatter'] 196 | if (!this._config.chart) { 197 | throw new Error('You need to define type of chart') 198 | } else if (!availableTypes.includes(this._config.chart)) { 199 | throw new Error("Invalid config for 'chart'. Available options are: " + availableTypes.join(', ')) 200 | } 201 | 202 | // Entity row 203 | if (typeof config.entity_row === 'undefined') { 204 | this._config.entity_row = false 205 | } else if (typeof this._config.entity_row !== 'boolean') { 206 | throw new Error('entity_row must be true or false') 207 | } 208 | } 209 | 210 | getCardSize() { 211 | return 4 212 | } 213 | 214 | render() { 215 | return html` 216 | 217 | Your browser does not support the canvas element. 218 | 219 | ` 220 | } 221 | } 222 | customElements.define(pkg.name, Card) 223 | console.info( 224 | `%c ${pkg.name} ${pkg.version} \n%c chart.js ${pkg.dependencies['chart.js']}`, 225 | 'color: white; font-weight: bold; background: #ff6384', 226 | 'color: #ff6384; font-weight: bold' 227 | ) 228 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@lit/reactive-element@^1.1.0": 6 | version "1.1.1" 7 | resolved "https://registry.yarnpkg.com/@lit/reactive-element/-/reactive-element-1.1.1.tgz#523b29e529e881fce47bab764ea1b8058fd45796" 8 | integrity sha512-B2JdRMwCGv+VpIRj3CYVQBx3muPDeE8y+HPgWqzrAHsO5/40BpwDFZeplIV790BaTqDVUDvZOKMSbuFM9zWC0w== 9 | 10 | "@rollup/plugin-commonjs@^21.0.1": 11 | version "21.0.1" 12 | resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.1.tgz#1e57c81ae1518e4df0954d681c642e7d94588fee" 13 | integrity sha512-EA+g22lbNJ8p5kuZJUYyhhDK7WgJckW5g4pNN7n4mAFUM96VuwUnNT3xr2Db2iCZPI1pJPbGyfT5mS9T1dHfMg== 14 | dependencies: 15 | "@rollup/pluginutils" "^3.1.0" 16 | commondir "^1.0.1" 17 | estree-walker "^2.0.1" 18 | glob "^7.1.6" 19 | is-reference "^1.2.1" 20 | magic-string "^0.25.7" 21 | resolve "^1.17.0" 22 | 23 | "@rollup/plugin-json@^4.1.0": 24 | version "4.1.0" 25 | resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" 26 | integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== 27 | dependencies: 28 | "@rollup/pluginutils" "^3.0.8" 29 | 30 | "@rollup/plugin-node-resolve@^13.1.3": 31 | version "13.1.3" 32 | resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz#2ed277fb3ad98745424c1d2ba152484508a92d79" 33 | integrity sha512-BdxNk+LtmElRo5d06MGY4zoepyrXX1tkzX2hrnPEZ53k78GuOMWLqmJDGIIOPwVRIFZrLQOo+Yr6KtCuLIA0AQ== 34 | dependencies: 35 | "@rollup/pluginutils" "^3.1.0" 36 | "@types/resolve" "1.17.1" 37 | builtin-modules "^3.1.0" 38 | deepmerge "^4.2.2" 39 | is-module "^1.0.0" 40 | resolve "^1.19.0" 41 | 42 | "@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": 43 | version "3.1.0" 44 | resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" 45 | integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== 46 | dependencies: 47 | "@types/estree" "0.0.39" 48 | estree-walker "^1.0.1" 49 | picomatch "^2.2.2" 50 | 51 | "@types/estree@*": 52 | version "0.0.50" 53 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83" 54 | integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw== 55 | 56 | "@types/estree@0.0.39": 57 | version "0.0.39" 58 | resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" 59 | integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== 60 | 61 | "@types/node@*": 62 | version "14.0.13" 63 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.0.13.tgz#ee1128e881b874c371374c1f72201893616417c9" 64 | integrity sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA== 65 | 66 | "@types/resolve@1.17.1": 67 | version "1.17.1" 68 | resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" 69 | integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== 70 | dependencies: 71 | "@types/node" "*" 72 | 73 | "@types/trusted-types@^2.0.2": 74 | version "2.0.2" 75 | resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" 76 | integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg== 77 | 78 | balanced-match@^1.0.0: 79 | version "1.0.2" 80 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 81 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 82 | 83 | brace-expansion@^1.1.7: 84 | version "1.1.11" 85 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 86 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 87 | dependencies: 88 | balanced-match "^1.0.0" 89 | concat-map "0.0.1" 90 | 91 | builtin-modules@^3.1.0: 92 | version "3.1.0" 93 | resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484" 94 | integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw== 95 | 96 | chart.js@^3.7.0: 97 | version "3.7.0" 98 | resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-3.7.0.tgz#7a19c93035341df801d613993c2170a1fcf1d882" 99 | integrity sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg== 100 | 101 | chartjs-plugin-annotation@^1.2.2: 102 | version "1.2.2" 103 | resolved "https://registry.yarnpkg.com/chartjs-plugin-annotation/-/chartjs-plugin-annotation-1.2.2.tgz#0f66e7a9ca3d4062a4b6629b74fa2e1b6f308700" 104 | integrity sha512-Yazdr1cGUrHZf/Dj0G+uY05QRXNiboZa/QgCmx9qgYDtpz66NczVYYIR75vH2sKO3ezHP3IRF0Cf7bXMxOEf1w== 105 | 106 | chartjs-plugin-zoom@^1.2.0: 107 | version "1.2.0" 108 | resolved "https://registry.yarnpkg.com/chartjs-plugin-zoom/-/chartjs-plugin-zoom-1.2.0.tgz#dad0861b2d171bca1f6d11b3e3e917bc12b950ff" 109 | integrity sha512-cLYKUHHx4bevuZQDpEKdjpvZ6HGu6NF8laTThgA0I9af+PV1N4qVTRZmyDNh0SAzsHZPtDOhuO3I7B4CF1lstw== 110 | dependencies: 111 | hammerjs "^2.0.8" 112 | 113 | commondir@^1.0.1: 114 | version "1.0.1" 115 | resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" 116 | integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= 117 | 118 | concat-map@0.0.1: 119 | version "0.0.1" 120 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 121 | integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= 122 | 123 | deepmerge@^4.2.2: 124 | version "4.2.2" 125 | resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" 126 | integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== 127 | 128 | estree-walker@^1.0.1: 129 | version "1.0.1" 130 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" 131 | integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== 132 | 133 | estree-walker@^2.0.1: 134 | version "2.0.2" 135 | resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" 136 | integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== 137 | 138 | fs.realpath@^1.0.0: 139 | version "1.0.0" 140 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 141 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 142 | 143 | fsevents@~2.3.2: 144 | version "2.3.2" 145 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" 146 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== 147 | 148 | function-bind@^1.1.1: 149 | version "1.1.1" 150 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 151 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 152 | 153 | glob@^7.1.6: 154 | version "7.2.0" 155 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" 156 | integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== 157 | dependencies: 158 | fs.realpath "^1.0.0" 159 | inflight "^1.0.4" 160 | inherits "2" 161 | minimatch "^3.0.4" 162 | once "^1.3.0" 163 | path-is-absolute "^1.0.0" 164 | 165 | hammerjs@^2.0.8: 166 | version "2.0.8" 167 | resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" 168 | integrity sha1-BO93hiz/K7edMPdpIJWTAiK/YPE= 169 | 170 | has@^1.0.3: 171 | version "1.0.3" 172 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 173 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 174 | dependencies: 175 | function-bind "^1.1.1" 176 | 177 | inflight@^1.0.4: 178 | version "1.0.6" 179 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 180 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 181 | dependencies: 182 | once "^1.3.0" 183 | wrappy "1" 184 | 185 | inherits@2: 186 | version "2.0.4" 187 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" 188 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== 189 | 190 | is-core-module@^2.8.0: 191 | version "2.8.1" 192 | resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" 193 | integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== 194 | dependencies: 195 | has "^1.0.3" 196 | 197 | is-module@^1.0.0: 198 | version "1.0.0" 199 | resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" 200 | integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= 201 | 202 | is-reference@^1.2.1: 203 | version "1.2.1" 204 | resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" 205 | integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== 206 | dependencies: 207 | "@types/estree" "*" 208 | 209 | lit-element@^3.1.0: 210 | version "3.1.1" 211 | resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-3.1.1.tgz#562d5ccbc8ba0c01d8ba4a0ac3576263167d2ccb" 212 | integrity sha512-14ClnMAU8EXnzC+M2/KDd3SFmNUn1QUw1+GxWkEMwGV3iaH8ObunMlO5svzvaWlkSV0WlxJCi40NGnDVJ2XZKQ== 213 | dependencies: 214 | "@lit/reactive-element" "^1.1.0" 215 | lit-html "^2.1.0" 216 | 217 | lit-html@^2.1.0: 218 | version "2.1.1" 219 | resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-2.1.1.tgz#f4da485798a0d967514d31730d387350fafb79f7" 220 | integrity sha512-E4BImK6lopAYanJpvcGaAG8kQFF1ccIulPu2BRNZI7acFB6i4ujjjsnaPVFT1j/4lD9r8GKih0Y8d7/LH8SeyQ== 221 | dependencies: 222 | "@types/trusted-types" "^2.0.2" 223 | 224 | lit@^2.1.1: 225 | version "2.1.1" 226 | resolved "https://registry.yarnpkg.com/lit/-/lit-2.1.1.tgz#65f43abca945988f696391f762c645ba51966b0b" 227 | integrity sha512-yqDqf36IhXwOxIQSFqCMgpfvDCRdxLCLZl7m/+tO5C9W/OBHUj17qZpiMBT35v97QMVKcKEi1KZ3hZRyTwBNsQ== 228 | dependencies: 229 | "@lit/reactive-element" "^1.1.0" 230 | lit-element "^3.1.0" 231 | lit-html "^2.1.0" 232 | 233 | lodash@^4.17.15: 234 | version "4.17.21" 235 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" 236 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== 237 | 238 | magic-string@^0.25.7: 239 | version "0.25.7" 240 | resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" 241 | integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== 242 | dependencies: 243 | sourcemap-codec "^1.4.4" 244 | 245 | mime@>=2.4.6: 246 | version "3.0.0" 247 | resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" 248 | integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== 249 | 250 | minimatch@^3.0.4: 251 | version "3.0.4" 252 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 253 | integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== 254 | dependencies: 255 | brace-expansion "^1.1.7" 256 | 257 | once@^1.3.0: 258 | version "1.4.0" 259 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 260 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 261 | dependencies: 262 | wrappy "1" 263 | 264 | opener@1: 265 | version "1.5.2" 266 | resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" 267 | integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== 268 | 269 | path-is-absolute@^1.0.0: 270 | version "1.0.1" 271 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 272 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 273 | 274 | path-parse@^1.0.7: 275 | version "1.0.7" 276 | resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" 277 | integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== 278 | 279 | picomatch@^2.2.2: 280 | version "2.3.1" 281 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" 282 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== 283 | 284 | prettier@^2.5.1: 285 | version "2.5.1" 286 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" 287 | integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== 288 | 289 | resolve@^1.17.0, resolve@^1.19.0: 290 | version "1.21.0" 291 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" 292 | integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== 293 | dependencies: 294 | is-core-module "^2.8.0" 295 | path-parse "^1.0.7" 296 | supports-preserve-symlinks-flag "^1.0.0" 297 | 298 | rollup-plugin-serve@^1.1.0: 299 | version "1.1.0" 300 | resolved "https://registry.yarnpkg.com/rollup-plugin-serve/-/rollup-plugin-serve-1.1.0.tgz#0654a57021a21b903340c69940f7463706e8288d" 301 | integrity sha512-pYkSsuA0/psKqhhictkJw1c2klya5b+LlCvipWqI9OE1aG2M97mRumZCbBlry5CMEOzYBBgSDgd1694sNbmyIw== 302 | dependencies: 303 | mime ">=2.4.6" 304 | opener "1" 305 | 306 | rollup@^2.63.0: 307 | version "2.63.0" 308 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.63.0.tgz#fe2f7fec2133f3fab9e022b9ac245628d817c6bb" 309 | integrity sha512-nps0idjmD+NXl6OREfyYXMn/dar3WGcyKn+KBzPdaLecub3x/LrId0wUcthcr8oZUAcZAR8NKcfGGFlNgGL1kQ== 310 | optionalDependencies: 311 | fsevents "~2.3.2" 312 | 313 | sourcemap-codec@^1.4.4: 314 | version "1.4.8" 315 | resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" 316 | integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== 317 | 318 | supports-preserve-symlinks-flag@^1.0.0: 319 | version "1.0.0" 320 | resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" 321 | integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== 322 | 323 | wrappy@1: 324 | version "1.0.2" 325 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 326 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 327 | --------------------------------------------------------------------------------