Custom UI: Weather state card

no i use 65.5 and hassio keeps disconnecting after boot… cant upgrade through the HA interface now, and am searching for the reason it won’t load…

Hi @gerard33 would you be able to assist in getting this working with the Ecobee weather platform? I currently have it showing this much:

Here is the additional information re attributes etc. I attempted added the ecobee condition attributes to the custom html file but it wouldn’t load anything on the frontend. Just fyi I also have this working perfectly with the yweather component.

Attribute data sample:

{
“temperature”: 18,
“humidity”: 50,
“pressure”: 1023,
“wind_bearing”: 350,
“wind_speed”: 0,
“visibility”: -5002000,
“attribution”: “Ecobee weather provided by xxxx at 2018-05-08 02:34:10”,
“forecast”: [
{
“datetime”: “2018-05-08 12:26:07”,
“condition”: “Mild.”,
“temperature”: 19,
“templow”: 43.7,
“pressure”: 1023,
“windspeed”: -5002000,
“humidity”: 50
},
{
“datetime”: “2018-05-09 12:26:07”,
“condition”: “Sunny. Mild.”,
“temperature”: 19,
“templow”: 39.5,
“pressure”: 1013,
“windspeed”: 12630,
“humidity”: 40
},
{
“datetime”: “2018-05-10 12:26:07”,
“condition”: “Showers late. Afternoon clouds. Cool.”,
“temperature”: 12,
“templow”: 40.1,
“pressure”: 1007,
“windspeed”: 14730,
“humidity”: 72

There are some known issues with ecobee weather data at the moment that will hopefully be fixed soon, but that should affect this. Cheers

The new weather card (will be part of 0.69) is very similar to this one, except it does not use animated icons.

image

Here’s the PR that updated the weather card.

Is there a quick way to use animated icons with the new built-in weather card?

I really like the look of this and would like to use it in my HA instance. What code do I use there is a lot going around? Thanks.

1 Like

@proxima I would also like to use something other than Yahoo Weather since I find the current conditions are often not accurate. The issue with Ecobee is it doesn’t provide a simple weather condition. For example, on mine right now one reads “Overcast throughout the day” rather than “Overcast”. The weather icons need to be mapped to a file name. Let’s hit up the dev for the component and see if there’s another attribute that can be added.

Check out the forecast from Openweathermap. If you are satisfied with it, you can use the custom component I posted here

Hi @jer78 I have started adding in the Ecobee specific conditions into my version of the custom-weather-card.html file and the output is starting to look pretty good. Although I still have issues as there are some bugs in the Ecobee weather component where visibility, wind speed and bearing all have a value of ‘0’ of ‘-5002000’. The issue of F instead of C degrees seems fixed. There is also a newly designed weather card in .69 that is a huge improvement but still not as good as the custom card. Here is my ecobee card currently still wip.

@proxima that’s cool! Do you mind sharing your version?

@Jer78 I’ll try to finish it off and add in all of the condition values I can find and I’ll share it here when it’s done!

1 Like

Where or how to configure “weather.yweather” ?

EDIT: i got it, but cannot see animated icons and it shows celsius degree but with F° instead C°

Have you placed the icons in this folder? \\HASSIO\config\www\weather_icons\animated
Here is the state card with imperial unit system support:

custom-weather-card.html
<dom-module id='custom-weather-card'>
  <template>
    <style>
      .clear {
        clear:both;
      }
      .card {
        margin:1em auto;
        padding-left: 1em;
        padding-right:1em;
        position: relative;
      }
      .iron-icon {
        height: 18px;
        color: var(--paper-item-icon-color);
      }
      .temp {
        font-weight: 300;
        font-size: 4em;
        color: var(--primary-text-color);
        position: absolute;
        right: 1em;
      }
      .tempc {
        font-weight: 300;
        font-size: 1.5em;
        vertical-align: super;
        color: var(--primary-text-color);
        position: absolute;
        right: 1em;
        margin-top: -14px;
        margin-right: 7px;
      }
      .variations {
        font-weight:300;
        color: var(--primary-text-color);
        list-style:none;
        margin-left:-2em;
        margin-top: 3.5em;
      }
      .variations.right {
        float: right;
        margin-left: 0;
        margin-right: 1em;
      }
      .unit {
        font-size:.8em;
      }
      .forecast {
        width:100%;
        margin:0 auto;
        height:9em;
      }
      .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(2) {
        border-right:none;
        margin-right: 0;
      }
      .highTemp {
        font-weight:bold;
      }
      .lowTemp {
        color: var(--secondary-text-color);
      }
      .icon.bigger {
        width: 10em;
        height: 10em;
        margin-top: -4em;
        position: absolute;
        left: 0em;
      }
      .icon {
        width: 50px;
        height: 50px;
        display: inline-block;
        vertical-align: middle;
        background-size: contain;
        background-position: center center;
        background-repeat: no-repeat;
        text-indent: -9999px;
      }
       .weather {
        font-weight: 300;
        font-size: 1.5em;
        color: var(--primary-text-color);
        text-align:left;
        position: absolute;
        top: -0.5em;
        left: 6em;
        word-wrap: break-word;
        width: 30%;
      }

    </style>
    <div class="card">
      <span class="icon bigger" style="background: none, url(/local/weather_icons/animated/[[nowCond]].svg) no-repeat; background-size: contain;"></span>
      <span class="temp">[[roundedTemp]]</span><span class="tempc">[[getUnit('temperature')]]</span>
      <span class="weather">[[nowCondIT]]</span>
      <br>
      <span>
        <ul class="variations right">
          <template is="dom-if" if="[[weatherObj.attributes.humidity]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:water-percent"></iron-icon></span> [[weatherObj.attributes.humidity]]<span class="unit"> %</span></li>
          </template>
          <template is="dom-if" if="[[weatherObj.attributes.pressure]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:gauge"></iron-icon></span> [[weatherObj.attributes.pressure]]<span class="unit"> hPa</span></li>
          </template>
          <template is="dom-if" if="[[pressureObj.state]]"> <!-- if weather station doesn't have pressure info, get it from a separate sensor -->
            <li><span class="iron-icon"><iron-icon icon="mdi:gauge"></iron-icon></span> [[nowPressure]]<span class="unit"> hPa</span></li>
          </template>
          <template is="dom-if" if="[[precipationfcObj.state]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:weather-rainy"></iron-icon></span> [[nowPrecipationfc]]<span class="unit" > mm/u (> uur)</span></li>
          </template>

        </ul>
        <ul class="variations">
          <template is="dom-if" if="[[weatherObj.attributes.wind_speed]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:weather-windy"></iron-icon></span> [[windBearing]] [[weatherObj.attributes.wind_speed]]<span class="unit"> m/s</span></li>
          </template>
          <template is="dom-if" if="[[weatherObj.attributes.visibility]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:weather-fog"></iron-icon></span> [[weatherObj.attributes.visibility]]<span class="unit"> m</span></li>
          </template>
          <template is="dom-if" if="[[visibilityObj.state]]"> <!-- if weather station doesn't have visibility info, get it from a separate sensor -->
            <li><span class="iron-icon"><iron-icon icon="mdi:weather-fog"></iron-icon></span> [[nowVisibility]]<span class="unit" > m</span></li>
          </template>
          <template is="dom-if" if="[[precipationObj.state]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:weather-pouring"></iron-icon></span> [[nowPrecipation]]<span class="unit" > mm/u</span></li>
          </template>
        </ul>
      </span>

      <div class="forecast clear">
        <template is="dom-repeat" items="[[forecast]]">
        <div class="day"><span class="dayname">[[item.dayIT]]</span>
          <template is="dom-if" if="[[item.condIcon]]">
          <br> <i class="icon" style="background: none, url(/local/weather_icons/animated/[[item.condIcon]].svg) no-repeat; background-size: contain;"></i> 
          </template>
          <template is="dom-if" if="[[item.tempHigh]]">
          <br> <span class="highTemp">[[item.tempHigh]][[getUnit('temperature')]]</span> 
          </template>
          <template is="dom-if" if="[[item.tempLow]]">
          <br> <span class="lowTemp">[[item.tempLow]][[getUnit('temperature')]]</span>
          </template>
        </div>
      </template>
      </div>
    </div>
  </template>
</dom-module>

<script>
(function () {
  'use strict';

  var _WEATHER_TO_ICON_DAY = {
    'cloudy': 'cloudy',
    'fog': 'cloudy',
    'hail': 'rainy-7',
    'lightning': 'thunder',
    'lightning-rainy': 'thunder',
    'partlycloudy': 'cloudy-day-3',
    'pouring': 'rainy-6',
    'rainy': 'rainy-5',
    'snowy': 'snowy-6',
    'snowy-rainy': 'rainy-7',
    'sunny': 'day',
    'windy': 'cloudy',
    'windy-variant': 'cloudy-day-3',
    'exceptional': '!!',
  };
  
  var _WEATHER_TO_ICON_NIGHT = {
    'cloudy': 'cloudy',
    'fog': 'cloudy',
    'hail': 'rainy-7',
    'lightning': 'thunder',
    'lightning-rainy': 'thunder',
    'partlycloudy': 'cloudy-night-3',
    'pouring': 'rainy-6',
    'rainy': 'rainy-5',
    'snowy': 'snowy-6',
    'snowy-rainy': 'rainy-7',
    'sunny': 'night',
    'windy': 'cloudy',
    'windy-variant': 'cloudy-night-3',
    'exceptional': '!!',
  };
  
  var _WEATHER_TO_NAME = {
    cloudy: 'Cloudy',
    fog: 'Fog',
    hail: 'Hail',
    lightning: 'Lightning',
    'lightning-rainy': 'Lightning',
    partlycloudy: 'Partly cloudy',
    pouring: 'Pouring',
    rainy: 'Rainy',
    snowy: 'Snowy',
    'snowy-rainy': 'Rain and snow',
    sunny: 'Sunny',
    windy: 'Windy',
    'windy-variant': 'Windy',
    exceptional: '!',
  };


  var _DEGREE_TEXT = [
    'N', 'NNO', 'NO', 'ONO', 'O', 'OZO', 'ZO', 'ZZO',
    'Z', 'ZZW', 'ZW', 'WZW', 'W', 'WNW', 'NW', 'NNW', 'N'
  ];
  
  var _DAY_TO_DAY = {
    Mon: 'MON',
    Tue: 'TUE',
    Wed: 'WED',
    Thu: 'THU',
    Fri: 'FRI',
    Sat: 'SAT',
    Sun: 'SUN',
  };

  Polymer({
    is: 'custom-weather-card',

    properties: {
      hass: {
        type: Object,
      },
      stateObj: {
        type: Object,
      },
      weatherObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computeWeatherObj(hass, stateObj)',
      },
      sunObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computeSunObj(hass, stateObj)',
      },
      pressureObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computePressureObj(hass, stateObj)',
      },
      visibilityObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computeVisibilityObj(hass, stateObj)',
      },
      precipationObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computePrecipationObj(hass, stateObj)',
      },
      precipationfcObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computePrecipationfcObj(hass, stateObj)',
      },
    },

    computeWeatherObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.weather ? hass.states[stateObj.attributes.config.weather] : null;
    },
    computeSunObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.sun ? hass.states[stateObj.attributes.config.sun] : null;
    },
    computePressureObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.pressure ? hass.states[stateObj.attributes.config.pressure] : null;
    },
    computeVisibilityObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.visibility ? hass.states[stateObj.attributes.config.visibility] : null;
    },
    computePrecipationObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.precipation ? hass.states[stateObj.attributes.config.precipation] : null;
    },
    computePrecipationfcObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.precipationfc ? hass.states[stateObj.attributes.config.precipationfc] : null;
    },
	getUnit(unit) {
	  return this.hass.config.core.unit_system[unit] || '';
    },

    getForecastArray: function () {
      if (!this.weatherObj.attributes.forecast) {
        return [];
      }
      
      var data = this.weatherObj.attributes.forecast;
      var forecast = [];
      var prevDay = '';
      
      for (var i = 0; i < data.length; i++) {
        var day = new Date(data[i].datetime).toString().split(' ')[0];
        if (day != prevDay) {
          if (data[i].max_temp) {
            var tempHigh = Math.round(data[i].max_temp * 10) / 10;
          } else {
            var tempHigh = Math.round(data[i].temperature * 10) / 10;
          }
          if (tempHigh == 0) {
            tempHigh = '0'; // otherwise the value 0 will not be shown on the weather card
          }
          var tempLow = Math.round(data[i].templow * 10) / 10;
          if (tempLow == 0) {
            tempLow = '0'; // otherwise the value 0 will not be shown on the weather card
          }
          var condIcon = _WEATHER_TO_ICON_DAY[data[i].condition];
          var dayIT = _DAY_TO_DAY[day];
          forecast.push({dayIT:dayIT, tempHigh:tempHigh, tempLow:tempLow, condIcon:condIcon});
          prevDay = day;
        } else {
          if (data[i].max_temp) {
            var tempHigh = Math.round(data[i].max_temp * 10) / 10;
          } else {
            var tempHigh = Math.round(data[i].temperature * 10) / 10;
          }
          var tempLow = Math.round(data[i].tempLow * 10) / 10;
          if (tempLow > forecast[forecast.length-1].tempHigh) {
            forecast[forecast.length-1].tempHigh = tempLow;
          }
          if (tempHigh > forecast[forecast.length-1].tempHigh) {
            forecast[forecast.length-1].tempHigh = tempHigh;
          }
          if (!forecast[forecast.length-1].tempLow) {
            forecast[forecast.length-1].tempLow = tempHigh;
          }
          if (tempHigh < forecast[forecast.length-1].tempLow) {
            forecast[forecast.length-1].tempLow = tempHigh;
          }
          if (tempLow < forecast[forecast.length-1].tempLow) {
            forecast[forecast.length-1].tempLow = tempLow;
          }
        }
      }

      return forecast;
    },

    checkRequirements: function () {
      if (!this.weatherObj) {
        return;
      }
      if (this.nowSun == 'above_horizon') {
	    this.nowCond = _WEATHER_TO_ICON_DAY[this.weatherObj.state];
      }
      if (this.nowSun == 'below_horizon') {
	    this.nowCond = _WEATHER_TO_ICON_NIGHT[this.weatherObj.state];
      }
      this.nowCondIT = _WEATHER_TO_NAME[this.weatherObj.state];
      if (this.pressureObj != null) {
        this.nowPressure = this.pressureObj.state;
      }
      if (this.sunObj != null) {
        this.nowSun = this.sunObj.state;
      }
      if (this.visibilityObj != null) {
        this.nowVisibility = this.visibilityObj.state;
      }
      if (this.precipationObj != null) {
        this.nowPrecipation = this.precipationObj.state;
      }
      if (this.precipationfcObj != null) {
        this.nowPrecipationfc = this.precipationfcObj.state;
      }
      if (this.weatherObj.attributes.temperature) {
        this.roundedTemp = Math.round(this.weatherObj.attributes.temperature);
        if (this.roundedTemp == 0) {
          this.roundedTemp = '0'; // otherwise the value 0 will not be shown on the weather card
        }
      }
      if (this.weatherObj.attributes.wind_bearing) {
        this.windBearing = this.windBearingToText(this.weatherObj.attributes.wind_bearing);
      }
      this.forecast = this.getForecastArray().slice(0, 5);
    },

    windBearingToText: function (degree) {
      // return _DEGREE_TEXT[((parseInt(degree) + 5.63) / 11.25) | 0];
      return _DEGREE_TEXT[(parseInt((degree + 11.25) / 22.5))];
    },
  });
}());
</script>

I have the icons in the right folder.
About the degrees i was meaning that i have numbers in celsius but i have F instead of C

I updated the code in the previous message. Please test the work of the card with it

And try to refresh the page with ctrl+F5

But in the previous reply you wrote the code is for Farenheit degrees, but my problem is another.As i wrote i am using Celsius degrees and infact the degrees numbers are shown correcly in the card are Celsius, BUT they have an F instead a C after the degree numbers…

It’s not just for Fahrenheit. C or F depends on what unit system are selected in the configuration.yaml:

homeassistant:
  latitude: 32.87336
  longitude: 117.22743
  elevation: 430
  unit_system: metric #metric for Metric, imperial for Imperial
  time_zone: America/Los_Angeles
  name: Home

Please try the code from the previous post and refresh the page with ctrl+F5

I have metric in config file and all other weather platform are showing correctly Celsius. I’ll try the code…

I have the custom weather card working on my Windows 10 PC using Chrome and it looks great. It also works on an Android phone in our household. But on my iPhone and iPad, whether I use Chrome or Safari, I can only see a card with the title “Weather Forecast” - there is nothing else. When I refresh the browser page in either browser, I get a pop-up message that says

service system_log/write called

and in my log it shows

ERROR (MainThread) [frontend.js.es5.201804040] :0:0 Script error.

I’ve cleared caches and reloaded and restarted and nothing changes. The rest of my Custom UI things work - my light sliders, icons, etc. It’s just the weather card that doesn’t work properly. Can anyone point me in the right direction to solve this? Thanks!

Update to my post above, and plea for help:

I’ve spent hours and hours in the last few days and I’m making myself crazy trying to fix this. I’ve Googled and worked my way through every related post I could find. I changed my install of Custom UI from customize: to putting all the files in frontend.yaml. I’ve tried changing the Javascript version in frontend.yaml (auto, es5, or latest, no difference), I’ve tried using input_text instead of input_boolean, I’ve restarted and cleared caches in Chrome and Safari and reloaded about a million times. I’m using Hassio 0.67.1 on an Raspberry Pi 3B, Custom UI version 20180429 (haven’t gone to the latest since my understanding is that it is for Polymer 3).

I’d so appreciate knowing if others are having the same issue, or if not, how I can make the custom weather card show on my iOS devices.

This is what I have:

configuration.yaml includes

homeassistant:
  customize: !include customize.yaml

  customize_glob:
    light.*:
      custom_ui_state_card: state-card-custom-ui

frontend: !include frontend.yaml
config:
discovery:
sun:

weather:
  - platform: yweather
    woeid: XXXXXX

input_boolean:
  weather:

frontend.yaml includes:

extra_html_url:
  - /local/custom_ui/custom-weather-card.html
  - /local/custom_ui/state-card-custom-ui.html
extra_html_url_es5:
  - /local/custom_ui/state-card-custom-ui-es5.html
  - /local/custom_ui/custom-weather-card.html

customize.yaml includes:

input_boolean.weather:
  custom_ui_state_card: custom-weather-card
  config:
    weather: weather.yweather
    sun: sun.sun

groups.yaml contains:

weather:
  name: Weather Forecast
  view: no
  entities:
      - input_boolean.weather

default_view:
  view: yes
  icon: mdi:home
  entities:
    - group.lights
    - group.sensors
    - group.weather

and this my custom-weather-card.html:

<dom-module id='custom-weather-card'>
  <template>
    <style>
      .clear {
        clear:both;
      }
      .card {
        margin:1em auto;
        padding-left: 1em;
        padding-right:1em;
        position: relative;
      }
      .iron-icon {
        height: 18px;
        color: var(--paper-item-icon-color);
      }
      .temp {
        font-weight: 300;
        font-size: 4em;
        color: var(--primary-text-color);
        position: absolute;
        right: 1em;
      }
      .tempc {
        font-weight: 300;
        font-size: 1.5em;
        vertical-align: super;
        color: var(--primary-text-color);
        position: absolute;
        right: 1em;
        margin-top: -14px;
        margin-right: 7px;
      }
      .variations {
        font-weight:300;
        color: var(--primary-text-color);
        list-style:none;
        margin-left:-2em;
        margin-top: 3.5em;
      }
      .variations.right {
        float: right;
        margin-left: 0;
        margin-right: 1em;
      }
      .unit {
        font-size:.8em;
      }
      .forecast {
        width:100%;
        margin:0 auto;
        height:9em;
      }
      .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(2) {
        border-right:none;
        margin-right: 0;
      }
      .highTemp {
        font-weight:bold;
      }
      .lowTemp {
        color: var(--secondary-text-color);
      }
      .icon.bigger {
        width: 10em;
        height: 10em;
        margin-top: -4em;
        position: absolute;
        left: 0em;
      }
      .icon {
        width: 50px;
        height: 50px;
        display: inline-block;
        vertical-align: middle;
        background-size: contain;
        background-position: center center;
        background-repeat: no-repeat;
        text-indent: -9999px;
      }
       .weather {
        font-weight: 300;
        font-size: 1.5em;
        color: var(--primary-text-color);
        text-align:left;
        position: absolute;
        top: -0.5em;
        left: 6em;
        word-wrap: break-word;
        width: 30%;
      }

    </style>
    <div class="card">
      <span class="icon bigger" style="background: none, url(/local/weather_icons/animated/[[nowCond]].svg) no-repeat; background-size: contain;"></span>
      <span class="temp">[[roundedTemp]]</span><span class="tempc">&#176;F</span>
      <span class="weather">[[nowCondIT]]</span>
      <br>
      <span>
        <ul class="variations right">
          <template is="dom-if" if="[[weatherObj.attributes.humidity]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:water-percent"></iron-icon></span> [[weatherObj.attributes.humidity]]<span class="unit"> %</span></li>
          </template>
          <template is="dom-if" if="[[weatherObj.attributes.pressure]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:gauge"></iron-icon></span> [[weatherObj.attributes.pressure]]<span class="unit"> in</span></li>
          </template>
          <template is="dom-if" if="[[pressureObj.state]]"> <!-- if weather station doesn't have pressure info, get it from a separate sensor -->
            <li><span class="iron-icon"><iron-icon icon="mdi:gauge"></iron-icon></span> [[nowPressure]]<span class="unit"> hPa</span></li>
          </template>
          <template is="dom-if" if="[[precipationfcObj.state]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:weather-rainy"></iron-icon></span> [[nowPrecipationfc]]<span class="unit" > mm/u (> uur)</span></li>
          </template>

        </ul>
        <ul class="variations">
          <template is="dom-if" if="[[weatherObj.attributes.wind_speed]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:weather-windy"></iron-icon></span> [[windBearing]] [[weatherObj.attributes.wind_speed]]<span class="unit"> mi/hr</span></li>
          </template>
          <template is="dom-if" if="[[weatherObj.attributes.visibility]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:weather-fog"></iron-icon></span> [[weatherObj.attributes.visibility]]<span class="unit"> mi</span></li>
          </template>
          <template is="dom-if" if="[[visibilityObj.state]]"> <!-- if weather station doesn't have visibility info, get it from a separate sensor -->
            <li><span class="iron-icon"><iron-icon icon="mdi:weather-fog"></iron-icon></span> [[nowVisibility]]<span class="unit" > mi</span></li>
          </template>
          <template is="dom-if" if="[[precipationObj.state]]">
            <li><span class="iron-icon"><iron-icon icon="mdi:weather-pouring"></iron-icon></span> [[nowPrecipation]]<span class="unit" > mm/u</span></li>
          </template>
        </ul>
      </span>

      <div class="forecast clear">
        <template is="dom-repeat" items="[[forecast]]">
        <div class="day"><span class="dayname">[[item.dayIT]]</span>
          <template is="dom-if" if="[[item.condIcon]]">
          <br> <i class="icon" style="background: none, url(/local/weather_icons/animated/[[item.condIcon]].svg) no-repeat; background-size: contain;"></i> 
          </template>
          <template is="dom-if" if="[[item.tempHigh]]">
          <br> <span class="highTemp">[[item.tempHigh]]&#176;F</span> 
          </template>
          <template is="dom-if" if="[[item.tempLow]]">
          <br> <span class="lowTemp">[[item.tempLow]]&#176;F</span>
          </template>
        </div>
      </template>
      </div>
    </div>
  </template>
</dom-module>

<script>
(function () {
  'use strict';

  var _WEATHER_TO_ICON_DAY = {
    'cloudy': 'cloudy',
    'fog': 'foggy-1',
    'hail': 'rainy-7',
    'lightning': 'thunder',
    'lightning-rainy': 'thunder',
    'partlycloudy': 'cloudy-day-3',
    'pouring': 'rainy-6',
    'rainy': 'rainy-5',
    'snowy': 'snowy-6',
    'snowy-rainy': 'rainy-7',
    'sunny': 'day',
    'windy': 'cloudy',
    'windy-variant': 'cloudy-day-3',
    'exceptional': '!!',
  };
  
  var _WEATHER_TO_ICON_NIGHT = {
    'cloudy': 'cloudy',
    'fog': 'foggy-2',
    'hail': 'rainy-7',
    'lightning': 'thunder',
    'lightning-rainy': 'thunder',
    'partlycloudy': 'cloudy-night-3',
    'pouring': 'rainy-6',
    'rainy': 'rainy-5',
    'snowy': 'snowy-6',
    'snowy-rainy': 'rainy-7',
    'sunny': 'night',
    'windy': 'cloudy',
    'windy-variant': 'cloudy-night-3',
    'exceptional': '!!',
    31: 'night',
  };
  
  var _WEATHER_TO_NAME = {
    cloudy: 'Cloudy',
    fog: 'Fog',
    hail: 'Hail',
    lightning: 'Lightning',
    'lightning-rainy': 'Lightning',
    partlycloudy: 'Partly cloudy',
    pouring: 'Pouring',
    rainy: 'Rainy',
    snowy: 'Snowy',
    'snowy-rainy': 'Rain and snow',
    sunny: 'Clear Sky',
    windy: 'Windy',
    'windy-variant': 'Windy',
    exceptional: '!',
    31: 'Clear Sky',
  };
  
  var _DEGREE_TEXT = [
    'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE',
    'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'
  ];  
  
  var _DAY_TO_DAY = {
    Mon: 'MON',
    Tue: 'TUE',
    Wed: 'WED',
    Thu: 'THU',
    Fri: 'FRI',
    Sat: 'SAT',
    Sun: 'SUN',
  };

  Polymer({
    is: 'custom-weather-card',

    properties: {
      hass: {
        type: Object,
      },
      stateObj: {
        type: Object,
      },
      weatherObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computeWeatherObj(hass, stateObj)',
      },
      sunObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computeSunObj(hass, stateObj)',
      },
      pressureObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computePressureObj(hass, stateObj)',
      },
      visibilityObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computeVisibilityObj(hass, stateObj)',
      },
      precipationObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computePrecipationObj(hass, stateObj)',
      },
      precipationfcObj: {
        type: Object,
        observer: 'checkRequirements',
        computed: 'computePrecipationfcObj(hass, stateObj)',
      },
    },

    computeWeatherObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.weather ? hass.states[stateObj.attributes.config.weather] : null;
    },
    computeSunObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.sun ? hass.states[stateObj.attributes.config.sun] : null;
    },
    computePressureObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.pressure ? hass.states[stateObj.attributes.config.pressure] : null;
    },
    computeVisibilityObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.visibility ? hass.states[stateObj.attributes.config.visibility] : null;
    },
    computePrecipationObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.precipation ? hass.states[stateObj.attributes.config.precipation] : null;
    },
    computePrecipationfcObj: function (hass, stateObj) {
      return stateObj && stateObj.attributes && stateObj.attributes.config && stateObj.attributes.config.precipationfc ? hass.states[stateObj.attributes.config.precipationfc] : null;
    },

    getForecastArray: function () {
      if (!this.weatherObj.attributes.forecast) {
        return [];
      }
      
      var data = this.weatherObj.attributes.forecast;
      var forecast = [];
      var prevDay = '';
      
      for (var i = 0; i < data.length; i++) {
        var day = new Date(data[i].datetime).toString().split(' ')[0];
        if (day != prevDay) {
          if (data[i].max_temp) {
            var tempHigh = Math.round(data[i].max_temp * 10) / 10;
          } else {
            var tempHigh = Math.round(data[i].temperature * 10) / 10;
          }
          if (tempHigh == 0) {
            tempHigh = '0'; // otherwise the value 0 will not be shown on the weather card
          }
          var tempLow = Math.round(data[i].templow * 10) / 10;
          if (tempLow == 0) {
            tempLow = '0'; // otherwise the value 0 will not be shown on the weather card
          }
          var condIcon = _WEATHER_TO_ICON_DAY[data[i].condition];
          var dayIT = _DAY_TO_DAY[day];
          forecast.push({dayIT:dayIT, tempHigh:tempHigh, tempLow:tempLow, condIcon:condIcon});
          prevDay = day;
        } else {
          if (data[i].max_temp) {
            var tempHigh = Math.round(data[i].max_temp * 10) / 10;
          } else {
            var tempHigh = Math.round(data[i].temperature * 10) / 10;
          }
          var tempLow = Math.round(data[i].tempLow * 10) / 10;
          if (tempLow > forecast[forecast.length-1].tempHigh) {
            forecast[forecast.length-1].tempHigh = tempLow;
          }
          if (tempHigh > forecast[forecast.length-1].tempHigh) {
            forecast[forecast.length-1].tempHigh = tempHigh;
          }
          if (!forecast[forecast.length-1].tempLow) {
            forecast[forecast.length-1].tempLow = tempHigh;
          }
          if (tempHigh < forecast[forecast.length-1].tempLow) {
            forecast[forecast.length-1].tempLow = tempHigh;
          }
          if (tempLow < forecast[forecast.length-1].tempLow) {
            forecast[forecast.length-1].tempLow = tempLow;
          }
        }
      }

      return forecast;
    },

    checkRequirements: function () {
      if (!this.weatherObj) {
        return;
      }
      if (this.nowSun == 'above_horizon') {
	    this.nowCond = _WEATHER_TO_ICON_DAY[this.weatherObj.state];
      }
      if (this.nowSun == 'below_horizon') {
	    this.nowCond = _WEATHER_TO_ICON_NIGHT[this.weatherObj.state];
      }
      this.nowCondIT = _WEATHER_TO_NAME[this.weatherObj.state];
      if (this.pressureObj != null) {
        this.nowPressure = this.pressureObj.state;
      }
      if (this.sunObj != null) {
        this.nowSun = this.sunObj.state;
      }
      if (this.visibilityObj != null) {
        this.nowVisibility = this.visibilityObj.state;
      }
      if (this.precipationObj != null) {
        this.nowPrecipation = this.precipationObj.state;
      }
      if (this.precipationfcObj != null) {
        this.nowPrecipationfc = this.precipationfcObj.state;
      }
      if (this.weatherObj.attributes.temperature) {
        this.roundedTemp = Math.round(this.weatherObj.attributes.temperature);
        if (this.roundedTemp == 0) {
          this.roundedTemp = '0'; // otherwise the value 0 will not be shown on the weather card
        }
		if (this.roundedTemp > 0) {
          this.roundedTemp = ''+this.roundedTemp; // Add a + if desired
        }
      }
      if (this.weatherObj.attributes.wind_bearing) {
        this.windBearing = this.windBearingToText(this.weatherObj.attributes.wind_bearing);
      }
      this.forecast = this.getForecastArray().slice(0, 5);
    },

    windBearingToText: function (degree) {
      // return _DEGREE_TEXT[((parseInt(degree) + 5.63) / 11.25) | 0];
      return _DEGREE_TEXT[(parseInt((degree + 11.25) / 22.5))];
    },
  });
}());
</script>

Have you tried using another weather component? I do not have Apple devices, but I tested various device mode in the Windows Chrome browser (including the iPhone 5,6,7,8,X) and everything worked fine.