Lovelace: Weather card with chart

Thank you for a nice card! I wonder how and where I can specify how many days of forecast I want to specify (I would like less than 9 days)

I see a few posts with pictures showing a forecasts with less than 9 days, how did you do that? @Mariusthvdb

it: {

      'tempHi': 'Temperatura',

      'tempLo': 'Temperatura Notturna',

      'precip': 'Precipitazioni',

      'units': {

        'km/h': 'km/h',

        'm/s': 'm/s',

        'hPa': 'hPa',

        'mmHg': 'mm Hg',

        'mm': 'mm',

        'in': 'in'

      },

      'cardinalDirections': [

        'N', 'N-NE', 'NE', 'E-NE', 'E', 'E-SE', 'SE', 'S-SE',

        'S', 'S-SO', 'SO', 'O-SO', 'O', 'O-NO', 'NO', 'N-NO', 'N'

      ],

      'clear-night': 'Notte Chiara',

      'cloudy': 'Nuvoloso',

      'fog': 'Nebbia',

      'hail': 'Grandine',

      'lightning': 'Temporali',

      'lightning-rainy': 'Temporali e Pioggia',

      'partlycloudy': 'Parzialmente Nuvoloso',

      'pouring': 'Torrenti',

      'rainy': 'Pioggia',

      'snowy': 'Neve',

      'snowy-rainy': 'Neve e Pioggia',

      'sunny': 'Sereno',

      'windy': 'Vento',

      'windy-variant': 'Vento Variabile'

    },

This is the code for italian language.Preformatted text

This card no longer works with Firefox.

1 Like

Same here, it works with Microsoft Edge but not with the latest Firefox (105.0.2) :cry:

I’ve reported an issue but the developer is not very active at the moment.

Last update to the card was 6 months ago and last activity in the forum was in April.

Are you able to provide details on how you got it to use animated icons? Thanks.

Your card is great. Are you able to share the YAML code and any setup required? Thanks.

well, I decided to keep them separate, so the graph bit is just that, the old card with all the rest deleted :wink:
and yet we can cutout even more, since not used. see all localizations eg.
All depends a bit on what you want to display or not.

  - type: custom:weather-chart
    weather: weather.buienradar

showing as

ready to be used as you like, standalone or concatenated. maybe even embedded on a button.

console.info(
  `%c  WEATHER-CHART  \n%c  Version 0.2`,
  'color: orange; font-weight: bold; background: black',
  'color: white; font-weight: bold; background: dimgray',
);

const locale = {
  da: {
    tempHi: "Temperatur",
    tempLo: "Temperatur nat",
    precip: "Nedbør",
    uPress: "hPa",
    uSpeed: "m/s",
    uPrecip: "mm",
    uVisib: "km",
    cardinalDirections: [
      'N', 'N-NØ', 'NØ', 'Ø-NØ', 'Ø', 'Ø-SØ', 'SØ', 'S-SØ',
      'S', 'S-SV', 'SV', 'V-SV', 'V', 'V-NV', 'NV', 'N-NV', 'N'
    ]
  },
  en: {
    tempHi: "Temperature",
    tempLo: "Temperature night",
    precip: "Precipitations",
    uPress: "hPa",
    uSpeed: "km/h",
    uPrecip: "mm",
    uVisib: "mi",
    cardinalDirections: [
      'N', 'N-NE', 'NE', 'E-NE', 'E', 'E-SE', 'SE', 'S-SE',
      'S', 'S-SW', 'SW', 'W-SW', 'W', 'W-NW', 'NW', 'N-NW', 'N'
    ]
  },
  fr: {
    tempHi: "Température",
    tempLo: "Température nuit",
    precip: "Précipitations",
    uPress: "hPa",
    uSpeed: "m/s",
    uPrecip: "mm",
    uVisib: "km",
    cardinalDirections: [
      'N', 'N-NE', 'NE', 'E-NE', 'E', 'E-SE', 'SE', 'S-SE',
      'S', 'S-SO', 'SO', 'O-SO', 'O', 'O-NO', 'NO', 'N-NO', 'N'
    ]
  },
  nl: {
    tempHi: "Max temperatuur",
    tempLo: "Min temperatuur",
    precip: "Neerslag",
    uPress: "hPa",
    uSpeed: "km/u",
    uPrecip: "mm",
    uVisib: "km",
    cardinalDirections: [
      'N', 'N-NO', 'NO', 'O-NO', 'O', 'O-ZO', 'ZO', 'Z-ZO',
      'Z', 'Z-ZW', 'ZW', 'W-ZW', 'W', 'W-NW', 'NW', 'N-NW', 'N'
    ]
  },
  ru: {
    tempHi: "Температура",
    tempLo: "Температура ночью",
    precip: "Осадки",
    uPress: "гПа",
    uSpeed: "м/с",
    uPrecip: "мм",
    uVisib: "км",
    cardinalDirections: [
      'С', 'С-СВ', 'СВ', 'В-СВ', 'В', 'В-ЮВ', 'ЮВ', 'Ю-ЮВ',
      'Ю', 'Ю-ЮЗ', 'ЮЗ', 'З-ЮЗ', 'З', 'З-СЗ', 'СЗ', 'С-СЗ', 'С'
    ]
  }
};

class WeatherChart extends Polymer.Element {

  static get template() {
    return Polymer.html`
      <style>
        ha-icon {
          color: var(--paper-item-icon-color);
        }
        .card {
          padding: 16px;
        }
      </style>
      <ha-card header="[[title]]">
        <div class="card">
          <ha-chart-base data="[[ChartData]]" options="[[ChartOptions]]" ></ha-chart-base>
        </div>
      </ha-card>
    `;
  }

  static get properties() {
    return {
      config: Object,
      mode: String,
      weatherObj: {
        type: Object,
        observer: 'dataChanged',
      },
    };
  }


  setConfig(config) {
    this.config = config;
    this.title = config.title;
    this.weatherObj = config.weather;
    this.mode = config.mode;
    if (!config.weather) {
      throw new Error('Please define "weather" entity in the card config');
    }
  }

  set hass(hass) {
    this._hass = hass;
    this.lang = this.config.locale in hass.states ? hass.states[this.config.locale] : this._hass.language;
    this.weatherObj = this.config.weather in hass.states ? hass.states[this.config.weather] : null;
    this.forecast = this.weatherObj.attributes.forecast.slice(0,9);
  }

  dataChanged() {
    this.drawChart();
  }

  ll(str) {
    if (locale[this.lang] === undefined)
      return locale.en[str];
    return locale[this.lang][str];
  }

  drawChart() {
    var data = this.weatherObj.attributes.forecast.slice(0,9);
    var tempUnit = this._hass.config.unit_system.temperature;
    var lengthUnit = this._hass.config.unit_system.length;
    var precipUnit = lengthUnit === 'km' ? this.ll('uPrecip') : 'in';
    var mode = this.mode;
    var i;
    if (!this.weatherObj.attributes.forecast) {
      return [];
    }
    var dateTime = [];
    var tempHigh = [];
    var tempLow = [];
    var precip = [];
    for (i = 0; i < data.length; i++) {
      var d = data[i];
      dateTime.push(new Date(d.datetime));
      tempHigh.push(d.temperature);
      tempLow.push(d.templow);
      precip.push(d.precipitation);
    }
    var style = getComputedStyle(document.body);
    var textColor = style.getPropertyValue('--primary-text-color');
    var dividerColor = style.getPropertyValue('--divider-color');
    const chartData = {
        labels: dateTime,
        datasets: [
          {
            label: this.ll('tempHi'),
            type: 'line',
            data: tempHigh,
            yAxisID: 'yTempAxis',
            borderColor: 'red',
            backgroundColor: 'orange',
            borderWidth: 2.0,
            lineTension: 0.4,
            pointRadius: 1.0,
            pointHitRadius: 5.0,
            fill: false,

            tooltip: {
             callbacks: {
               label: function(context) {
                 var label = context.dataset.label || '';
                 return label += ': ' + context.parsed.y + tempUnit;
               }
             }
           }
           },

          {
            label: this.ll('tempLo'),
            type: 'line',
            data: tempLow,
            yAxisID: 'yTempAxis',
            borderColor: 'green',
            backgroundColor: 'purple',
            borderWidth: 2.0,
            lineTension: 0.4,
            pointRadius: 0.0,
            pointHitRadius: 5.0,
            fill: false,

            tooltip: {
             callbacks: {
               label: function(context) {
                 var label = context.dataset.label || '';
                 return label += ': ' + context.parsed.y + tempUnit;
               }
             }
           }
          },
          {
            label: this.ll('precip'),
            type: 'bar',
            data: precip,
            yAxisID: 'yPrecipAxis',
            borderColor: 'skyblue',
            backgroundColor: 'steelblue',
            maxBarThickness: 15,
            barThickness: 8,
            tooltip: {
             callbacks: {
               label: function(context) {
                 var label = context.dataset.label || '';
                 return label += ': ' + context.parsed.y + precipUnit;
               }
             }
           }
          },
        ]
      }

    const chartOptions = {
        animation: {
          duration: 300,
          easing: 'linear',
          onComplete: function (animation) {
            var chartInstance = animation.chart,
              ctx = chartInstance.ctx;
            ctx.fillStyle = textColor;
            var fontSize = 10;
            var fontStyle = 'normal';
            var fontFamily = 'Roboto';
            ctx.font = fontStyle + ' ' + fontSize + 'px ' + fontFamily;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'bottom';
            var meta = chartInstance.getDatasetMeta(2);
            meta.data.forEach(function (bar, index) {
              var data = (Math.round((chartInstance.data.datasets[2].data[index]) * 10) / 10).toFixed(1);
              if (data > 0)
                 ctx.fillText(data, bar.x, bar.y - 5);
            });
          },
        },
        legend: {
          display: false,
        },
        scales: {
          xAxes: {
            type: 'time',
            adapters: {
              date: {
                locale: this._hass.locale,
              },
            },
//             display: true,
            ticks: {
              display: false,
            },
            grid: {
              display: false,
            },
          },
          xDateAxis: {
            type: 'time',
            position: 'top',
            adapters: {
              date: {
                locale: this._hass.locale,
              },
            },
            grid: {
              display: true,
              drawBorder: false,
              color: dividerColor,
            },
            ticks: {
              display: true,
              source: 'labels',
              autoSkip: true,
              color: textColor,
              maxRotation: 0,
              callback: function(value, index, values) {
                var date = new Date(0);
                date.setUTCMilliseconds(values[index].value);
                if (mode == 'hourly') {
                  return date.toLocaleTimeString(locale, { hour: 'numeric' });
                }
                return date.toLocaleDateString(locale, { weekday: 'short' });;
              },
            },
          },
          yTempAxis: {
            position: 'left',
            adapters: {
              date: {
                locale: this._hass.locale,
              },
            },
            grid: {
              display: true,
              drawBorder: false,
              color: dividerColor,
              borderDash: [1,3],
            },
            ticks: {
              display: true,
              color: textColor,
              callback: function(value, index, values) {
                 return value + 'º';
               },
            },
            afterFit: function(scaleInstance) {
              scaleInstance.width = 37;
            },
          },
          yPrecipAxis: {
            display: false,
            position: 'right',
            suggestedMax: 20,
            adapters: {
              date: {
                locale: this._hass.locale,
              },
            },
            grid: {
              display: false,
              drawBorder: false,
              color: dividerColor,
            },
            ticks: {
              display: false,
              min: 0,
              color: textColor,
            },
            afterFit: function(scaleInstance) {
              scaleInstance.width = 15;
            },
          },
        },
        plugins: {
           tooltip: {
             callbacks: {
               title: function(context) {
                 return new Date(context[0].label).toLocaleDateString(locale, {
                   month: 'long',
                   day: 'numeric',
                   weekday: 'long',
//                    hour: 'numeric',
//                    minute: 'numeric',
                 });
              }
            }
           }
        },
      }
    this.ChartData = chartData;
    this.ChartOptions = chartOptions;
/*    this.ChartType = chartType;*/
  }

  _fire(type, detail, options) {
    const node = this.shadowRoot;
    options = options || {};
    detail = (detail === null || detail === undefined) ? {} : detail;
    const e = new Event(type, {
      bubbles: options.bubbles === undefined ? true : options.bubbles,
      cancelable: Boolean(options.cancelable),
      composed: options.composed === undefined ? true : options.composed
    });
    e.detail = detail;
    node.dispatchEvent(e);
    return e;
  }
}

customElements.define('weather-chart', WeatherChart);

Thanks! I will check it out.

Hi,
Added:

Example Lovelace UI config entry

resources:

  • type: module
    url: /local/weather-chart-card.js

to my configuration.yaml file.

Got:

Integration error: resources - Integration ‘resources’ not found.

when checking the configuration file.
Please advise.
thanks

It is better if you add this to the Custom Repository. You then add the card via HACS. Doing this aviods the need to edit the configuation file.

Brilliant. Works great for weekly forecast. I tried it for hourly and it did not work very well. Still happy with the weekly. Thanks.

I can see that the card has different icons for the same reported weather condition to differentiate night and day. eg the condition ‘Sunny’ is mapped to ‘clear-day’ and ‘clear-night’. I assume there is logic that determines the time of day and therefore which icon the conditopon of ‘Sunny’ should use. This is not working for me. Is anyone else having this problem?

image

Hi,
I’m a noob.
I don’t understand how to install this card.
Please help.

Have you installed HACS? I assume you have. If not, search how to do this. Once HACS is installed…

  1. Click HACS from side menu
  2. Click Frontend
  3. Top right corner, click on the 3 dots and click Custom Respositories
  4. Type in ‘GitHub - Yevgenium/weather-chart-card: Custom weather card with charts’ in the Repository field
  5. Select Lovelace as the Catagory.
  6. Click Add
  7. Once added, you click on Explore & Download Repositories in the bottom right corner
  8. Seach for Weather card with chart. Install.
1 Like

Worked. Thanks!

I am using this card for a long time (thanks!), but have a display issue for some time now (not sure if it started with a certain HA update, as I did not directly notice due to mainly using HA on my mobile phone).
While the chart still displays properly on the iphone, on my tablet and different computers in different browsers, I just see:
grafik

This is how it looks when displayed completely:

As different devices and browsers are affected, it does not seem to be a problem of local caching.
Any idea what the problem and solution could be?

This is my config of the card:

type: custom:weather-chart-card
entity: weather.openweathermap
show_main: false
show_attributes: false
icons: /local/img/weather_animated/
icons_size: 50

Icons are the only thing not directy coming from this project, but are animated ones that I put in, so this is probably the reason why this is the only element that is still being displayed?!

Got the same problem as flobidan, but only in FireFox the other browsers do show the graph!