Custom Dark Sky Animated Weather Card

Now now, it’s doing it’s job only refreshing when the server tells it to :wink:

I also added the “Feels like” apparent temperature to this card. But that snowballed into learning some JS and CSS, and a lot of practice editing the card and moving stuff around. This resource was really helpful: https://www.w3schools.com/default.asp

Ended up with this as the result.

screenshot

  • moved icon to right, and temperature to left (alignments were less affected by different sized columns and viewing devices)
  • added apparent “Feels like” temperature
  • moved summary up from the bottom, splitting it into a today summary and the forecasted summary
  • changed the visibility icon to an eye from whatever non-descriptive icon it was before
  • added sunrise and sunset which move positions based on what is next (when after sunrise it’ll display sunset on the left side, and switch after sunset)
  • UPDATE 4.2: with sunrise/sunset. Now they also only display day name when it’s not occurring today.
  • UPDATE 4.2: and they don’t require a template sensor for rise/set, uses the sun.sun component that is already passed to the script
  • added precipitation probability to the day list
  • added a 3-day summary to the bottom of the card
  • added a max-width attribute to the card so that it didn’t get all stretched and weird when it’s the only card in a view:panel
  • and the card is updated as per the thread to not break for HA 0.83
  • UPDATE 4.3: swapped out a bunch of the existing day name display stuff to make it easier to change the names for different languages, and I think it’ll be more efficient.
  • UPDATE 4.4. Removed the hardcoded language stuff like the day names, feels like, today and forecast. Now can be set in the ui-lovelace.yaml instead of editing the .js file. Defaults to English.
  • UPDATE 4.4: Added bold emphasis to the Today and Forecast in upper summary, and to the day names in the lower 3-day summary.
  • UPDATE 4.4: Modified some stuff in the .css file so that the dynamic “Feels like” naming didn’t break the formatting.
  • UPDATED the ui-lovelace.yaml version numbering of the card to 4.4

Here’s the code:

sensors.yaml
Or wherever you keep your sensors.
Added comment for language option in ver 4.3, set your language if not English and Dark Sky returns everything in that language.

#https://www.home-assistant.io/components/sensor.darksky/
  - platform: darksky
    api_key: !secret darksky_apikey
    #language: en   #set your language see the sensor page for list
    forecast:
      - 1
      - 2
      - 3
      - 4
      - 5
    monitored_conditions:
      - summary
      - icon
      - precip_probability
      - temperature
      - apparent_temperature
      - wind_speed
      - wind_bearing
      - humidity
      - pressure
      - visibility
      - hourly_summary
      - daily_summary
      - temperature_high
      - temperature_low
      - uv_index  # not currently being used by Lovelace weather card
# other Dark Sky conditions, none of which are being used by Lovelace weather card
      #- precip_type
      #- precip_intensity
      #- precip_accumulation
      #- precip_intensity_max
      #- dew_point
      #- minutely_summary
      #- apparent_temperature_high
      #- apparent_temperature_low
      #- moon_phase
      #- nearest_storm_distance
      #- nearest_storm_bearing
      #- wind_gust
      #- cloud_cover
      #- ozone
    update_interval:
      minutes: 5

# removed the unnecessary template sensors in ver 4.2

www\custom_ui\dark-sky-weather-card.css
Updated this in ver 4.4. Create or replace the existing file.

.clear {
    clear: both;
  }

  .card {
    margin: auto;
    padding-top: 2em;
    padding-bottom: 1em;
    padding-left: 1em;
    padding-right:1em;
    position: relative;
    max-width: 600px;
    /* min-width: 200px; */
  }

  .ha-icon {
    height: 18px;
    margin-right: 5px;
    color: var(--paper-item-icon-color);
  }

  .divtemp {
    position: absolute;
  }
  
  .temp {
    font-weight: 300;
    font-size: 4em;
    color: var(--primary-text-color);
  }    

  .tempc {
    font-weight: 300;
    font-size: .5em;
    vertical-align: super;
    color: var(--primary-text-color);
    position: relative;
    top: -1px;
    left: -6px;
  }
  
  .feels {
    position: relative;
    top: -32px;
    left: 4px;   
  }
  
  .feelslike {
    font-weight: 300;
    font-size: 1em;
  }
  
  .feelstemp {
    font-size: 2em;
    font-weight: 300;
    position: absolute;
    left: 0;
    top: 18px;
  }
  
  .feelstempc {
    font-size: .6em;
    font-weight: 300;
    vertical-align: super;
  }

  .tdaysum {
    display: block;
    font-size: .8em;
    position: relative;
    top: 55px;
  }

  .threedaysum {
    display: block;
    font-size: .8em;
    position: relative;
  }
  
  .variations {
    display: inline-block;
    font-weight: 300;
    color: var(--primary-text-color);
    list-style: none;
    position: relative;
    margin-left: -2em;
    margin-top: 4em;
  }

  .variations.right {
    position: absolute;
    right: 1em;
    margin-left: 0;
    margin-right: 1em;
  }

  .unit {
    font-size: .8em;
  }

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

  .day {
    display: block;
    width: 20%;
    float: left;
    text-align: center;
    color: var(--primary-text-color);
    border-right: .1em solid #d9d9d9;
    line-height: 2;
    box-sizing: border-box;
  }

  .dayname {
    text-transform: uppercase;
  }

  .forecast .day:first-child {
    margin-left: 0;
  }

  .forecast .day:nth-last-child(1) {
    border-right: none;
    margin-right: 0;
  }

  .highTemp {
    font-weight: bold;
  }

  .lowTemp {
    color: var(--secondary-text-color);
  }

  .icon.bigger {
    width: 10em;
    height: 10em;
    margin-top: -3em;
    position: absolute;
    right: -20px;
  }

  .icon {
    width: 50px;
    height: 50px;
    margin-right: 5px;
    display: inline-block;
    vertical-align: middle;
    background-size: contain;
    background-position: center center;
    background-repeat: no-repeat;
    text-indent: -9999px;
  }

www\custom_ui\dark-sky-weather-card.js
Updated for ver 4.4. Create or replace the existing file.

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.unit_system.length;
      switch (measure) {
        case 'air_pressure':
          return lengthUnit === 'km' ? 'hPa' : 'mbar';
        case 'length':
          return lengthUnit;
        case 'precipitation':
          return lengthUnit === 'km' ? 'mm' : 'in';
        default:
          return hass.config.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 other_words = [ "Feels like", "Today", "Forecast" ];
    if (this.config.other_words) {
      other_words = this.config.other_words;
    }
    
    var day_names = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ];
    if (this.config.day_names) {
      day_names = this.config.day_names;
    }   
    function dayName(dayNum) {
        if ( dayNum > 6 ) {
            dayNum -= 7;
        }
        return day_names[dayNum];
    }
 
    var d = new Date();
    const forecast1 = { date: dayName(d.getDay()+1),
    				   condition: this.config.entity_forecast_icon_1,
    				   temphigh: this.config.entity_forecast_high_temp_1,
    				   templow:  this.config.entity_forecast_low_temp_1, 
               pop: this.config.entity_forecast_pop_1, };
    const forecast2 = { date: dayName(d.getDay()+2),
    				   condition: this.config.entity_forecast_icon_2,
    				   temphigh: this.config.entity_forecast_high_temp_2,
    				   templow:  this.config.entity_forecast_low_temp_2,
               pop: this.config.entity_forecast_pop_2, };
    const forecast3 = { date: dayName(d.getDay()+3),
    				   condition: this.config.entity_forecast_icon_3,
    				   temphigh: this.config.entity_forecast_high_temp_3,
    				   templow:  this.config.entity_forecast_low_temp_3,
               pop: this.config.entity_forecast_pop_3, };
    const forecast4 = { date: dayName(d.getDay()+4),
    				   condition: this.config.entity_forecast_icon_4,
    				   temphigh: this.config.entity_forecast_high_temp_4,
    				   templow:  this.config.entity_forecast_low_temp_4,
               pop: this.config.entity_forecast_pop_4, };
    const forecast5 = { date: dayName(d.getDay()+5),
    				   condition: this.config.entity_forecast_icon_5,
    				   temphigh: this.config.entity_forecast_high_temp_5,
    				   templow:  this.config.entity_forecast_low_temp_5,
               pop: this.config.entity_forecast_pop_5, };

    const forecast = [forecast1,forecast2,forecast3,forecast4,forecast5];
    
    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 feelstemperature = Math.round(hass.states[this.config.entity_feelstemp].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);

    var sunSetOrRiseA = new Date(hass.states[this.config.entity_sun].attributes.next_setting);
    var sunSetOrRiseIconA = "mdi:weather-sunset-down";
    var sunSetOrRiseB = new Date(hass.states[this.config.entity_sun].attributes.next_rising);
    var sunSetOrRiseIconB = "mdi:weather-sunset-up";
    // A == sunset   B == sunrise
    if ( hass.states[this.config.entity_sun].state == "above_horizon" ) {
        // sun has risen, but hasn't set
        // sunset is today (no date displayed). sunrise is tomorrow (display date)
        // next is sunset == A
        sunSetOrRiseA = sunSetOrRiseA.toLocaleTimeString();
        var ssrI = sunSetOrRiseA.lastIndexOf(":");
        sunSetOrRiseA = sunSetOrRiseA.substr(0,ssrI) + sunSetOrRiseA.substr(ssrI+4);
        sunSetOrRiseB = dayName(sunSetOrRiseB.getDay()) + " " + sunSetOrRiseB.toLocaleTimeString();
        ssrI = sunSetOrRiseB.lastIndexOf(":");
        sunSetOrRiseB = sunSetOrRiseB.substr(0,ssrI) + sunSetOrRiseB.substr(ssrI+4);
    } else {
        // next is sunrise == B
        var ss = new Date(sunSetOrRiseA);
        if ( new Date().getDate() != sunSetOrRiseB.getDate() ) {
            // sun hasn't risen, and it's not same day
            // so display dates for both
            sunSetOrRiseA = dayName(sunSetOrRiseB.getDay()) + " " + sunSetOrRiseB.toLocaleTimeString();
            sunSetOrRiseB = dayName(ss.getDay()) + " " + ss.toLocaleTimeString();
        } else {
            // sun hasn't risen, but it's the same day
            // since rise and set are today, no dates displayed
            sunSetOrRiseA = sunSetOrRiseB.toLocaleTimeString();
            sunSetOrRiseB = ss.toLocaleTimeString();
        }
        var ssrI = sunSetOrRiseA.lastIndexOf(":");
        sunSetOrRiseA = sunSetOrRiseA.substr(0,ssrI) + sunSetOrRiseA.substr(ssrI+4);
        ssrI = sunSetOrRiseB.lastIndexOf(":");
        sunSetOrRiseB = sunSetOrRiseB.substr(0,ssrI) + sunSetOrRiseB.substr(ssrI+4);
        sunSetOrRiseIconA = "mdi:weather-sunset-up";
        sunSetOrRiseIconB = "mdi:weather-sunset-down";     
    }

    this.content.innerHTML = `
      <div>
      <span class="icon bigger" style="background: none, url(/local/icons/weather_icons/animated/${weatherIcons[currentConditions]}.svg) no-repeat; background-size: contain;"></span>
        <div class="divtemp">
          <span class="temp">${temperature}<span class="tempc"> ${getUnit('temperature')}</span></span>
          <span class="feels"><span class="feelslike">${other_words[0]}<span class="feelstemp">${feelstemperature}<span class="feelstempc">${getUnit('temperature')}</span></span></span></span>
        </div>
      </div>
      <span class="tdaysum"><span style="font-weight:bold">${other_words[1]}:</span> ${hass.states[this.config.entity_today_summary].state} <br><span style="font-weight:bold">${other_words[2]}:</span> ${hass.states[this.config.entity_daily_summary].state}</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>
            <li><span class="ha-icon"><ha-icon icon=${sunSetOrRiseIconB}></ha-icon></span>${sunSetOrRiseB}</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:eye"></ha-icon></span>${visibility}<span class="unit"> ${getUnit('length')}</span></li>
            <li><span class="ha-icon"><ha-icon icon=${sunSetOrRiseIconA}></ha-icon></span>${sunSetOrRiseA}</li>
        </ul>
      </span>
      <div class="forecast clear">
          ${forecast.map(daily => `
              <div class="day">
                  <span class="dayname">${daily.date}</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>
                  <br><span class="highTemp">${Math.round(hass.states[daily.pop].state)}%</span>
              </div>`).join('')}
      </div>
      <span class="threedaysum">
          <span style="font-weight:bold">${forecast[0].date}:</span> ${hass.states[this.config.entity_forecast_sum_1].state}<br>
          <span style="font-weight:bold">${forecast[1].date}:</span> ${hass.states[this.config.entity_forecast_sum_2].state}<br>
          <span style="font-weight:bold">${forecast[2].date}:</span> ${hass.states[this.config.entity_forecast_sum_3].state}<br>
      </span>      
      `;
  }
 
  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);

and finally my ui-lovelace.yaml
updated version numbering, and added comments about changing language.

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

views:
  - title: Weather
    cards:
      - type: custom:dark-sky-weather-card
        entity_sun: sun.sun
        entity_daily_summary: sensor.dark_sky_daily_summary
        entity_today_summary: sensor.dark_sky_hourly_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_feelstemp: sensor.dark_sky_apparent_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_1
        entity_forecast_low_temp_2: sensor.dark_sky_overnight_low_temperature_2
        entity_forecast_low_temp_3: sensor.dark_sky_overnight_low_temperature_3
        entity_forecast_low_temp_4: sensor.dark_sky_overnight_low_temperature_4
        entity_forecast_low_temp_5: sensor.dark_sky_overnight_low_temperature_5
        entity_forecast_pop_1: sensor.dark_sky_precip_probability_1
        entity_forecast_pop_2: sensor.dark_sky_precip_probability_2
        entity_forecast_pop_3: sensor.dark_sky_precip_probability_3
        entity_forecast_pop_4: sensor.dark_sky_precip_probability_4
        entity_forecast_pop_5: sensor.dark_sky_precip_probability_5
        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
        entity_forecast_sum_1: sensor.dark_sky_summary_1
        entity_forecast_sum_2: sensor.dark_sky_summary_2
        entity_forecast_sum_3: sensor.dark_sky_summary_3
# Uncomment and replace with the day names and words you want in their place. FOLLOW THE SAME ORDER!
# By not including day_names or other_words you will get the English defaults shown below.        
        #day_names:
          #- Sun
          #- Mon
          #- Tue
          #- Wed
          #- Thu
          #- Fri
          #- Sat
        #other_words:
          #- Feels like
          #- Today
          #- Forecast

I’ll keep maintaining this for my own use based on how the main card gets updated. Suppose I’m going to have to get a GitHub account or something (sheesh! something else to learn!) so anyone who’s using my version can also stay up to date. But as it is, folks should be able to just use the above, the only thing you need is the animated icons https://www.amcharts.com/free-animated-svg-weather-icons/

9 Likes

Hi @jusdwy and thanks for sharing.

Very well done card!!!

I’m trying to translate it in Italian and the last two objects remaining are sunrise and sunset time.

  - platform: template
    sensors:
      nextsunrise:
        friendly_name: 'Next Sunrise'
        value_template: '{{ as_timestamp(states.sun.sun.attributes.next_rising) | timestamp_custom("%a %I:%M%p") }}'

      nextsunset:
        friendly_name: 'Next Sunset'
        value_template: '{{ as_timestamp(states.sun.sun.attributes.next_setting) | timestamp_custom("%a %I:%M%p") }}'

Is there a way to translate it?

Thanks to other in the forum I was able to translate the main days like this (the dayname row)

  <div class="forecast clear">
      ${forecast.map(daily => `
          <div class="day">
              <span class="dayname">${(daily.date).toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' })}</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>
              <br><span class="highTemp">${Math.round(hass.states[daily.pop].state)}%</span>
          </div>`).join('')}
  </div>

And also the forecast text like this

  <span class="threedaysum">
      ${(forecast[0].date).toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' })}: ${hass.states[this.config.entity_forecast_sum_1].state}<br>
      ${(forecast[1].date).toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' })}: ${hass.states[this.config.entity_forecast_sum_2].state}<br>
      ${(forecast[2].date).toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' })}: ${hass.states[this.config.entity_forecast_sum_3].state}<br>
  </span>

These changes reflect the language: xx option in the darksky sensor definition, in my case ‘it’

Is there a way to translate the sunrise and sunset day as well as the 24hrs instead of 12hrs?

Thanks

hi,

trying to adapt my weather-card for Buienradar, I run into these 2 probably very simple issues:

04

1 - the unit of the visibility is incorrect and should be meters. Ive tried to change it in the .js file:

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

but no matter what I use in this line, km is displayed.

How can I change that please?
the line where this is called is:

<li><span class="ha-icon"><ha-icon icon="mdi:weather-fog"></ha-icon></span>${visibility}<span class="unit"> ${getUnit('length')}</span></li>

2 - I need to change the sun set and rise to show time only (if on this day) and time and short date (if tomorrow)

where do I change that, and how?
preferably not using yet another sensor, but maybe in the stylesheet? Please let me now how I can achieve that, thanks!

Hey woody4165,
I haven’t done any work with languages other than English, so I don’t know how they change internally, or how to force them to change. I’m learning some stuff from your code actually. But that’s all happening on the JS side, and the sensors are happening on the HA side which I would have thought would be doing the language change automatically.

Anyways, to set the 24-hour clock use %H instead of %I in the template.

  - platform: template
    sensors:
      nextsunrise:
        friendly_name: 'Next Sunrise'
        value_template: '{{ as_timestamp(states.sun.sun.attributes.next_rising) | timestamp_custom("%a %H:%M") }}'

      nextsunset:
        friendly_name: 'Next Sunset'
        value_template: '{{ as_timestamp(states.sun.sun.attributes.next_setting) | timestamp_custom("%a %H:%M") }}'

but I do think that we could remove the two template sensors (or just pass them as timestamp values), and do all the converting with JS. I’ll have a look at that.

1 Like

Thanks @jusdwy

It’s not my code!!! :blush:
I’ve grabbed somewhere in this community and just adapted!

Regarding this line, how can I make the first letter of the day translated in capital.

      ${(forecast[0].date).toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' })}: ${hass.states[this.config.entity_forecast_sum_1].state}<br>

please yes, see 1 post above :wink: Much needed. btw I use the @pnbrucknerCC for sun.sun so that’s why I have

const sunrise = hass.states[this.config.entity_sun].attributes.next_rising;
const sunset = hass.states[this.config.entity_sun].attributes.next_setting;

This one I can handle!

  1. For visibility, if it’s currently being reported in m just convert it to km instead of trying to convert the unit.

Find this line in dark-sky-weather-card.js

     const visibility = hass.states[this.config.entity_visibility].state;

// And change it to
    const visibility = hass.states[this.config.entity_visibility].state / 1000;

If you want to keep m and change the unit, I think you’re going to have to add a few extra bits. Maybe…

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

// and find this line lower down and change it to below
            <li><span class="ha-icon"><ha-icon icon="mdi:weather-fog"></ha-icon></span>${visibility}<span class="unit"> ${getUnit('vis')}</span></li>

The second option with changing the unit is untested.

  1. The sunset / sunrise is pretty easy if you’re already using my code by just adding one line below the if statement. If you’re not already using my code then add that block to the dark-sky-weather-card.js file where everything is being declared (have a look at my code a couple posts above if you’re still not sure)
    var sunSetOrRiseA = hass.states[this.config.entity_sunup].state;
    var sunSetOrRiseIconA = "mdi:weather-sunset-up";
    var sunSetOrRiseB = hass.states[this.config.entity_sundown].state;
    var sunSetOrRiseIconB = "mdi:weather-sunset-down";
    if ( hass.states[this.config.entity_sun].state == "above_horizon" ) {
        sunSetOrRiseA = hass.states[this.config.entity_sundown].state;
        sunSetOrRiseB = hass.states[this.config.entity_sunup].state;
        sunSetOrRiseIconA = "mdi:weather-sunset-down";
        sunSetOrRiseIconB = "mdi:weather-sunset-up";
    }
    sunSetOrRiseA = sunSetOrRiseA.slice(4);

thanks! I now see what my efforts to change didn’t work. I tried to change the wrong entities unit… duh.

added:

    case 'visibility':
      return lengthUnit === 'km' ? 'm' : 'in';

and all is well! (btw /1000 works also of course, will decide later on which I prefer, but for now want to keep the units as close as possible to the real sensors value, and not hardcode any math if I can prevent it)

09

now to the next challenge.
btw2 no Im not using the dark sky card, but the animated weather card for Lovelace https://github.com/arsaboo/homeassistant-config/blob/master/www/custom_ui/weather-card.js

much the same of course, still enough differences to not always fit the weather component.
Example: I havent been able to create the summary in forecast yet with platform Buienradar, or Open weather map.

Anyways, thanks!

Ya I just saw that you are using a different method to display the sunrise / sunset (and even a custom sun component).

untested because i’m not running that custom component, but I think this will get you close…

    var sunrise = hass.states[this.config.entity_sun].attributes.next_rising;
    var sunset = hass.states[this.config.entity_sun].attributes.next_setting;
    if ( hass.states[this.config.entity_sun].state == "above_horizon" ) {
      sunset = sunset.toLocaleTimeString();
      sunrise = sunrise.toDateString().slice(0,3) + " " + sunrise.toLocaleTimeString();
    } else {
      sunset = sunset.toDateString().slice(0,3) + " " + sunset.toLocaleTimeString();
      sunrise = sunrise.toLocaleTimeString();
    }

thanks, but:

90:41 TypeError: sunset.toLocaleTimeString is not a function. (In 'sunset.toLocaleTimeString()', 'sunset.toLocaleTimeString' is undefined)

oops, noticed lots of errors there.
try my new edit above

sorry, but still:

lovelace/weather-card.js:90:41 TypeError: sunset.toLocaleTimeString is not a function. (In 'sunset.toLocaleTimeString()', 'sunset.toLocaleTimeString' is undefined)

fyi:

must be because the object coming from that custom component isn’t a time object in JS.

Since I am new to JS in this matter, let’s ask @pnbruckner about this, he’s the expert since he wrote the CC. Phil, please have a look?

Okay, I figured it out. The problem was that I wasn’t creating a JS Date object, I was just assuming that Hass was passing a valid JS Date object.

There’s a lot going on here, but mostly for how I wanted the date/time to be displayed. It does satisfy your @Mariusthvdb request to only display the time for the thing that is happening next, and the day name plus time for the next next. Since I’m using the JS Date object function, toLocaleTimeString() and toDateString() I worry that it might not display exactly the same for everyone.

this code switches the position of the sunrise/sunset based on what’s happening next as well. so the next thing will always be in the left column. The code could be simplified a bit if this isn’t wanted.

Replace all the sunset/sunrise variable stuff with this. It doesn’t require the custom sun component, as I don’t have it, and it works just fine on mine. I think it’ll still work with that custom component, but it’s untested.

    var sunSetOrRiseA = hass.states[this.config.entity_sun].attributes.next_setting;
    sunSetOrRiseA = new Date(sunSetOrRiseA);
    var sunSetOrRiseIconA = "mdi:weather-sunset-down";
    var sunSetOrRiseB = hass.states[this.config.entity_sun].attributes.next_rising;
    sunSetOrRiseB = new Date(sunSetOrRiseB);
    var sunSetOrRiseIconB = "mdi:weather-sunset-up";
    // A == sunset   B == sunrise
    
    if ( hass.states[this.config.entity_sun].state == "above_horizon" ) {
        // next is sunset == A
        sunSetOrRiseA = sunSetOrRiseA.toLocaleTimeString();
        var ssrI = sunSetOrRiseA.lastIndexOf(":");
        sunSetOrRiseA = sunSetOrRiseA.substr(0,ssrI) + sunSetOrRiseA.substr(ssrI+4);
        sunSetOrRiseB = sunSetOrRiseB.toDateString().slice(0,3) + " " + sunSetOrRiseB.toLocaleTimeString();
        ssrI = sunSetOrRiseB.lastIndexOf(":");
        sunSetOrRiseB = sunSetOrRiseB.substr(0,ssrI) + sunSetOrRiseB.substr(ssrI+4);
    } else {
        // next is sunrise == B
        var ss = sunSetOrRiseA;
        sunSetOrRiseA = sunSetOrRiseB.toLocaleTimeString();
        var ssrI = sunSetOrRiseA.lastIndexOf(":");
        sunSetOrRiseA = sunSetOrRiseA.substr(0,ssrI) + sunSetOrRiseA.substr(ssrI+4);
        sunSetOrRiseB = ss.toDateString().slice(0,3) + " " + ss.toLocaleTimeString();
        ssrI = sunSetOrRiseB.lastIndexOf(":");
        sunSetOrRiseB = sunSetOrRiseB.substr(0,ssrI) + sunSetOrRiseB.substr(ssrI+4);
        sunSetOrRiseIconA = "mdi:weather-sunset-up";
        sunSetOrRiseIconB = "mdi:weather-sunset-down";     
    }

// and then lower down replace these lines
// this is the first one, under ul class="variations right"
            <li><span class="ha-icon"><ha-icon icon=${sunSetOrRiseIconB}></ha-icon></span>${sunSetOrRiseB}</li>

// this is the second one, under ul class="variations"
            <li><span class="ha-icon"><ha-icon icon=${sunSetOrRiseIconA}></ha-icon></span>${sunSetOrRiseA}</li>

This also eliminates the need for the two template sensors, and therefore the need to pass them within ui-lovelace.yaml.

I’ll go ahead and update all my code in my original post above to reflect these changes.

now @woody4165
I think we can adapt the above code for language based on the code you showed me. But this is untested.

    var sunSetOrRiseA = hass.states[this.config.entity_sun].attributes.next_setting;
    sunSetOrRiseA = new Date(sunSetOrRiseA);
    var sunSetOrRiseIconA = "mdi:weather-sunset-down";
    var sunSetOrRiseB = hass.states[this.config.entity_sun].attributes.next_rising;
    sunSetOrRiseB = new Date(sunSetOrRiseB);
    var sunSetOrRiseIconB = "mdi:weather-sunset-up";
    // A == sunset   B == sunrise
    
    if ( hass.states[this.config.entity_sun].state == "above_horizon" ) {
        // next is sunset == A
        sunSetOrRiseA = sunSetOrRiseA.toLocaleTimeString();
        var ssrI = sunSetOrRiseA.lastIndexOf(":");
        sunSetOrRiseA = sunSetOrRiseA.substr(0,ssrI) + sunSetOrRiseA.substr(ssrI+4);
        sunSetOrRiseB = sunSetOrRiseB.toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' }).slice(0,3) + " " + sunSetOrRiseB.toLocaleTimeString();
        ssrI = sunSetOrRiseB.lastIndexOf(":");
        sunSetOrRiseB = sunSetOrRiseB.substr(0,ssrI) + sunSetOrRiseB.substr(ssrI+4);
    } else {
        // next is sunrise == B
        var ss = sunSetOrRiseA;
        sunSetOrRiseA = sunSetOrRiseB.toLocaleTimeString();
        var ssrI = sunSetOrRiseA.lastIndexOf(":");
        sunSetOrRiseA = sunSetOrRiseA.substr(0,ssrI) + sunSetOrRiseA.substr(ssrI+4);
        sunSetOrRiseB = ss.toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' }).slice(0,3) + " " + ss.toLocaleTimeString();
        ssrI = sunSetOrRiseB.lastIndexOf(":");
        sunSetOrRiseB = sunSetOrRiseB.substr(0,ssrI) + sunSetOrRiseB.substr(ssrI+4);
        sunSetOrRiseIconA = "mdi:weather-sunset-up";
        sunSetOrRiseIconB = "mdi:weather-sunset-down";     
    }

Thanks @jusdwy

So I have to add this code to the js file, right?
Do I need to remove some other code?

I will try and let you know.

Thanks!!

wow, much better:

23

just notice the lack of Wed on the left hand side, where the next sunrise will be…

magic you did!

I have just one more challenge;-)

as you can see ij the regular card:

21 there’s the condition and name of the station in the upper left corner.

I think there’s just about enough room in the custom_card for the name left top, then Animated Icon and condition below that.

How would I have to go and try that in the JS file? this new is the regular HA animated card, with a title.

26
Instead of the title we could have the name of the station (which is the name of the component, in this case weather.woensdrecht.) as per configuration:

  - type: 'custom:weather-card'
    entity_weather: weather.woensdrecht
    entity_sun: sun.sun
    entity_apparent_temperature: sensor.jagti_windchill

strangely enough, there is this first line for the card:

  <span class="icon bigger" style="background: none, url(/local/weather/animated/${weatherIcons[currentCondition]}.svg) no-repeat; background-size: contain;">${currentCondition}</span>

which should take care of that…it doesn’t show however.

@jusdwy

Works perfectly!!!
Thanks

21

Now I have to understand how to correct the last three rows in the picture where the day name have to be with the first letter capitalized.

This is one of the three rows

      ${(forecast[0].date).toLocaleDateString(hass.selectedLanguage || hass.language, { weekday: 'short' })}: ${hass.states[this.config.entity_forecast_sum_1].state}<br>

EDIT:
I see that correctly in the sunset it shows tomorrow, mer, but in the sunrise, that also occurs tomorrow, it doesn’t show anything.

Is it an issue?

try this. replace the starting block of code in the .js file. Note the new card.header line.
this will replace it with whatever you put as entity_weather: in your ui-lovelace.yaml

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

or you could simply add some text there instead of the state of the entity.

      card.header = this.config.entity_weather; 

although that might screw up the spacing of the other stuff, so you might have to play with the values in the .css file to position stuff to your liking.

1 Like