Custom Dark Sky Animated Weather Card

EDIT: Mar 03, 2019

Dark Sky Weather Card

image

The Dark Sky Weather Card provides current and forecasted weather conditions using the Dark Sky platform. You configure the card by passing in sensor entities from the Dark Sky Sensor platform.

The card is very customizable. You can configure many aspects of it’s look and feel as well as which specific content to show by passing in customization flags and defining optional sensors. Content can also be rearranged if desired.

Hovering over a forecast day will display the daily weather summary in a tooltip popup if that option has been enabled.

The complete readme file can be found here. It contains the latest instructions on how to setup the card as well as how to use the various features.

The latest version of the card can be found here.

The GitHub site is here. Please use GitHub issues to request new features as it makes them easier to track.


Original Post:
For reference only.
See above for links to current card and instructions

A couple of folks have asked about using the very nice Custom Animated Weather Card by @arsaboo with Dark Sky. I modified the card based on my needs. - All credit goes to the folks who created the original and those that modified it to get it to where I could hack together something that works for me.

My modified version uses the Dark Sky Sensor Platform as shown below:

sensors.yaml code:

  - platform: darksky
    api_key: <REDACTED>
    forecast:
      - 1
      - 2
      - 3
      - 4
      - 5
    monitored_conditions:
      - icon
      - summary
      - nearest_storm_distance
      - nearest_storm_bearing
      - humidity
      - temperature
      - temperature_high
      - temperature_low
      - apparent_temperature
      - apparent_temperature_high
      - apparent_temperature_low
      - wind_speed
      - wind_bearing
      - precip_type
      - precip_probability
      - precip_accumulation
      - precip_intensity
      - precip_intensity_max
      - uv_index
      - daily_summary
      - pressure
      - visibility
    update_interval:
      minutes: 5

I pass in the sensor entities the card needs. Note the forecast_low_temp entities are staggered back 1. This will use the low temp for the morning of the specific day which matches the way the Dark Sky app shows it.

Note: All the entities listed below are required.

ui-lovelace.yaml code:

      - type: custom:dark-sky-weather-card
        entity_sun: sun.sun
        entity_daily_summary: sensor.dark_sky_daily_summary
        entity_current_conditions: sensor.dark_sky_icon  
        entity_humidity: sensor.dark_sky_humidity
        entity_pressure: sensor.dark_sky_pressure
        entity_temperature: sensor.dark_sky_temperature
        entity_visibility: sensor.dark_sky_visibility
        entity_wind_bearing: sensor.dark_sky_wind_bearing
        entity_wind_speed: sensor.dark_sky_wind_speed
        entity_forecast_high_temp_1: sensor.dark_sky_daytime_high_temperature_1
        entity_forecast_high_temp_2: sensor.dark_sky_daytime_high_temperature_2
        entity_forecast_high_temp_3: sensor.dark_sky_daytime_high_temperature_3
        entity_forecast_high_temp_4: sensor.dark_sky_daytime_high_temperature_4
        entity_forecast_high_temp_5: sensor.dark_sky_daytime_high_temperature_5
        entity_forecast_low_temp_1: sensor.dark_sky_overnight_low_temperature
        entity_forecast_low_temp_2: sensor.dark_sky_overnight_low_temperature_1
        entity_forecast_low_temp_3: sensor.dark_sky_overnight_low_temperature_2
        entity_forecast_low_temp_4: sensor.dark_sky_overnight_low_temperature_3
        entity_forecast_low_temp_5: sensor.dark_sky_overnight_low_temperature_4
        entity_forecast_icon_1: sensor.dark_sky_icon_1
        entity_forecast_icon_2: sensor.dark_sky_icon_2
        entity_forecast_icon_3: sensor.dark_sky_icon_3
        entity_forecast_icon_4: sensor.dark_sky_icon_4
        entity_forecast_icon_5: sensor.dark_sky_icon_5

I am by no means a JavaScript developer . I make no claim that the way I approached this is the best or even a good way. It works for me and that’s what I set out to do

dark-sky-weather-card.js:

class DarkSkyWeatherCard extends HTMLElement {
  set hass(hass) {
    if (!this.content) {
      const card = document.createElement('ha-card');
      const link = document.createElement('link');
      link.type = 'text/css';
      link.rel = 'stylesheet';
      link.href = '/local/custom_ui/dark-sky-weather-card.css';
      card.appendChild(link);
      this.content = document.createElement('div');
      this.content.className = 'card';
      card.appendChild(this.content);
      this.appendChild(card);
    }

    const getUnit = function (measure) {
      const lengthUnit = hass.config.core.unit_system.length;
      switch (measure) {
        case 'air_pressure':
          return lengthUnit === 'km' ? 'hPa' : 'inHg';
        case 'length':
          return lengthUnit;
        case 'precipitation':
          return lengthUnit === 'km' ? 'mm' : 'in';
        default:
          return hass.config.core.unit_system[measure] || '';
      }
    };

    const transformDayNight = {
      "below_horizon": "night",
      "above_horizon": "day",
    }
    const sunLocation = transformDayNight[hass.states[this.config.entity_sun].state];
    const weatherIcons = {
      'clear-day': 'day',
      'clear-night': 'night',
      'rain': 'rainy-5',
      'snow': 'snowy-6',
      'sleet': 'rainy-6',
      'wind': 'cloudy',
      'fog': 'cloudy',
      'cloudy': 'cloudy',
      'partly-cloudy-day': 'cloudy-day-3',
      'partly-cloudy-night': 'cloudy-night-3',
      'hail': 'rainy-7',
      'lightning': 'thunder',
      'thunderstorm': 'thunder',
      'windy-variant': `cloudy-${sunLocation}-3`,
      'exceptional': '!!',
    }

    const windDirections = [
      'N',
      'NNE',
      'NE',
      'ENE',
      'E',
      'ESE',
      'SE',
      'SSE',
      'S',
      'SSW',
      'SW',
      'WSW',
      'W',
      'WNW',
      'NW',
      'NNW',
      'N'
    ];
    
    var forecastDate1 = new Date();
    forecastDate1.setDate(forecastDate1.getDate()+1);
    var forecastDate2 = new Date();
    forecastDate2.setDate(forecastDate2.getDate()+2);
    var forecastDate3 = new Date();
    forecastDate3.setDate(forecastDate3.getDate()+3);
    var forecastDate4 = new Date();
    forecastDate4.setDate(forecastDate4.getDate()+4);
    var forecastDate5 = new Date();
    forecastDate5.setDate(forecastDate5.getDate()+5);
    
    
    const currentConditions = hass.states[this.config.entity_current_conditions].state;
    const humidity = hass.states[this.config.entity_humidity].state;
    const pressure = Math.round(hass.states[this.config.entity_pressure].state);
    const temperature = Math.round(hass.states[this.config.entity_temperature].state);
    const visibility = hass.states[this.config.entity_visibility].state;
    const windBearing = windDirections[(Math.round((hass.states[this.config.entity_wind_bearing].state / 360) * 16))];
    const windSpeed = Math.round(hass.states[this.config.entity_wind_speed].state);
    const forecast1 = { date: forecastDate1,
    				   condition: this.config.entity_forecast_icon_1,
    				   temphigh: this.config.entity_forecast_high_temp_1,
    				   templow:  this.config.entity_forecast_low_temp_1, };
    const forecast2 = { date: forecastDate2,
    				   condition: this.config.entity_forecast_icon_2,
    				   temphigh: this.config.entity_forecast_high_temp_2,
    				   templow:  this.config.entity_forecast_low_temp_2, };
    const forecast3 = { date: forecastDate3,
    				   condition: this.config.entity_forecast_icon_3,
    				   temphigh: this.config.entity_forecast_high_temp_3,
    				   templow:  this.config.entity_forecast_low_temp_3, };
    const forecast4 = { date: forecastDate4,
    				   condition: this.config.entity_forecast_icon_4,
    				   temphigh: this.config.entity_forecast_high_temp_4,
    				   templow:  this.config.entity_forecast_low_temp_4, };
    const forecast5 = { date: forecastDate5,
    				   condition: this.config.entity_forecast_icon_5,
    				   temphigh: this.config.entity_forecast_high_temp_5,
    				   templow:  this.config.entity_forecast_low_temp_5, };

    const forecast = [forecast1,forecast2,forecast3,forecast4,forecast5];


    this.content.innerHTML = `
      <span class="icon bigger" style="background: none, url(/local/icons/weather_icons/animated/${weatherIcons[currentConditions]}.svg) no-repeat; background-size: contain;">${currentConditions}</span>
      <span class="temp">${temperature}</span><span class="tempc"> ${getUnit('temperature')}</span>
      <span>
        <ul class="variations right">
            <li><span class="ha-icon"><ha-icon icon="mdi:water-percent"></ha-icon></span>${humidity}<span class="unit"> %</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:gauge"></ha-icon></span>${pressure}<span class="unit"> ${getUnit('air_pressure')}</span></li>
        </ul>
        <ul class="variations">
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-windy"></ha-icon></span>${windBearing} ${windSpeed}<span class="unit"> ${getUnit('length')}/h</span></li>
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-fog"></ha-icon></span>${visibility}<span class="unit"> ${getUnit('length')}</span></li>
        </ul>
      </span>
      <div class="forecast clear">
          ${forecast.map(daily => `
              <div class="day">
                  <span class="dayname">${(daily.date).toString().split(' ')[0]}</span>
                  <br><i class="icon" style="background: none, url(/local/icons/weather_icons/animated/${weatherIcons[hass.states[daily.condition].state]}.svg) no-repeat; background-size: contain;"></i>
                  <br><span class="highTemp">${Math.round(hass.states[daily.temphigh].state)}${getUnit('temperature')}</span>
                  <br><span class="lowTemp">${Math.round(hass.states[daily.templow].state)}${getUnit('temperature')}</span>
              </div>`).join('')}
      </div>
      <br><span class="unit">${hass.states[this.config.entity_daily_summary].state}</span></br>`;
  }

  setConfig(config) {
    if (!config.entity_current_conditions || 
    		!config.entity_humidity ||
    		!config.entity_pressure ||
     		!config.entity_temperature ||
    		!config.entity_visibility ||
    		!config.entity_wind_bearing ||
    		!config.entity_wind_speed) {
      throw new Error('Please define entities');
    }
    this.config = config;
  }

  // @TODO: This requires more intelligent logic
  getCardSize() {
    return 3;
  }
}

customElements.define('dark-sky-weather-card', DarkSkyWeatherCard);

dark-sky-weather-card.css

Only made a small change to the css file so only showing the change. changed it’s name to match the card and avoid conflicts with the original as well.


  .forecast {
    width: 100%;
    margin: 0 auto;
    height: 10em;
  }
38 Likes

Awesome! Thanks for sharing! That helps me understand a bit more how to make custom UI, plus dark sky is more accurate here.

Yes, this looks really nice but I’m just starting to experiment with Lovelace. Can you tell me where to put the .js and the .css files? I’m running hassio.

Also a link to @arsaboo’s original would be good to help with the learning.
Thanks.

1 Like

@klogg, here is a link to arsaboo’s card: Custom animated weather card for Lovelace. The instructions there have links to his original files as well as the animated icons that you will need. I would recommend getting that to work first and then if you are still interested in using Dark Sky as the weather source, modify it using the code in my post as an example.

To answer your other question, the .js and .css files go in the /config/www/custom_ui directory. If this is the first custom card you are using you may need to create the www and custom_ui directories.

Note that the css snippet in my original post only shows a small piece of that file (just the change I made). The change is not really necessary so using the original css file works just fine. I renamed it in my configuration. You can see the reference to it in the dark-sky-weather-card.js code (including its directory).

At the top of my ui-lovelace file I have the following:

resources:
  - url: /local/custom_ui/dark-sky-weather-card.js?v=4
    type: js

This tells the UI where to find the custom card. Note that the url is listed as /local/custom_ui/dark-sky-weather-card.js. The /local directory refers to the config/www directory.

2 Likes

@m.p.frankland
Fantastic reply.
Thank you!

EDIT: and I have it all working. Thanks again. (And it is a good learning resource).
My only complaint is that until I have fully moved to using Lovelace I now have about 100 extra sensor badges on my original ui home screen for all the forecast weather conditions!!
So I think I won’t be using it for a while. :frowning_face:

@klogg,

Glad to hear you got it working.

You’re correct, the Dark Sky sensor platform does generate a lot of entities for the forecasts. It was never really an issue for me as I was using a default view for the overview which didn’t show the badges. I suppose you could go into Customization and set the hidden attribute for each one individually. That would keep them from flooding your main view.

1 Like

I’ve got the .js and .css in www/custom_ui and the icons are in www/icons/weather_icons/animated, the sensors are loaded as well, so all should be fine, but this is what I get after adding it to my lovelace config:

It looks to me as the css is not loaded for some reason. What did overlook? This is in 0.74.0b2.

I have the exact same problem when using Firefox. Chrome does work as expected though.

I haven’t updated to any of the HA beta images so can’t say if that is part of your issue or not. It does look like the .css is not being applied…

Things to check:

  • Using the original .css file from arsaboo. (the snippet in my original post is just a small piece of that file)
  • Renamed the .css file to dark-sky-weather-card.css (or whatever you have used in this line in the .js file)
link.href = '/local/custom_ui/dark-sky-weather-card.css';
  • Using Chrome. I have not tested in other browsers but as @docbobo mentions, there are known issues with Lovelace custom cards in Firefox and other browsers. I believe there are solutions posted for Firefox in other threads.

Actually I am using arsaboo’s css file but it doesn’t make a difference. I’ll go and check got possible workarounds for Firefox. Thanks for the hint.

This is how it looks for me:

I’ve enabled custom elements and shadow DOM as suggested here, which fixes some other UI issues with Firefox. Disabling those two things again fixed the weather card, but then other things don’t work properly.

Thanks for checking. I’ve enabled those as well. I’ll check if I need it.

Okay, it seems as if Firefox is ignoring the linked CSS when it occurs in the Shadow DOM. Directly embedding the style definitions into the the JS fixes the issue.

1 Like

Setting javascript_version: latest and possibly embedding the css right into the JS fixed it at least in Safari on the mac. Firefox still isn’t working for me, and I don’t understand why.

At least on Safari it is looking nice.
36

Firefox is definitely working here. How did you embed the CSS?

class DarkSkyWeatherCard extends HTMLElement {
  set hass(hass) {
    if (!this.content) {
      const card = document.createElement('ha-card');
      // card.appendChild(link);
      this.content = document.createElement('div');
      this.content.className = 'card';
      const style = document.createElement('style');
      style.textContent = `
      .clear {
        clear: both;
      }
      [SNIP]
      `;
      card.appendChild(this.content);
      this.appendChild(card);
    }
1 Like

I just added mine to the innerHTML assignment towards the end of the file…

1 Like

Great, that did the trick for Firefox. Now I’m curious if it still works on Safari, but I have to wait 'till I get home later.

Unfortunately the whole custom card thing doesn’t work on iOS due to es5. :frowning:

That should totally work on iOS with javascript_version: latest…

1 Like

Can someone see my problem?

image

The files:

Thanks for your help