Weather card - struggling with methods

I am trying to implement the Weather Card example in the Vuetify v-card example to Dashoard 2.0. And I want this to work with a OWM OneCall response, like this:

{"lat":47.4979,"lon":19.0402,"timezone":"Europe/Budapest","timezone_offset":7200,"current":{"dt":1696411074,"sunrise":1696394764,"sunset":1696436348,"temp":18.69,"feels_like":18.08,"pressure":1025,"humidity":56,"dew_point":9.75,"uvi":1.95,"clouds":0,"visibility":10000,"wind_speed":8.75,"wind_deg":330,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}]},"daily":[{"dt":1696413600,"sunrise":1696394764,"sunset":1696436348,"moonrise":1696445520,"moonset":1696417500,"moon_phase":0.68,"summary":"Expect a day of partly cloudy with clear spells","temp":{"day":19.16,"min":16.63,"max":21.42,"night":16.63,"eve":19.91,"morn":18.03},"feels_like":{"day":18.52,"night":15.45,"eve":18.9,"morn":17.64},"pressure":1025,"humidity":53,"dew_point":9.36,"wind_speed":6.07,"wind_deg":328,"wind_gust":13.53,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"clouds":19,"pop":0.16,"uvi":2.74},{"dt":1696500000,"sunrise":1696481247,"sunset":1696522627,"moonrise":1696534860,"moonset":1696507740,"moon_phase":0.71,"summary":"Expect a day of partly cloudy with clear spells","temp":{"day":20.46,"min":14.49,"max":22.27,"night":17.88,"eve":20.66,"morn":14.86},"feels_like":{"day":19.45,"night":16.82,"eve":19.85,"morn":13.71},"pressure":1025,"humidity":34,"dew_point":3.88,"wind_speed":3.86,"wind_deg":300,"wind_gust":6.34,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"clouds":45,"pop":0,"uvi":2.9},{"dt":1696586400,"sunrise":1696567730,"sunset":1696608907,"moonrise":1696624740,"moonset":1696597320,"moon_phase":0.75,"summary":"Expect a day of partly cloudy with clear spells","temp":{"day":19.48,"min":15.35,"max":21.58,"night":17.26,"eve":20.3,"morn":15.8},"feels_like":{"day":18.5,"night":16.29,"eve":19.35,"morn":14.66},"pressure":1026,"humidity":39,"dew_point":4.89,"wind_speed":2.88,"wind_deg":306,"wind_gust":6.97,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"clouds":100,"pop":0,"uvi":3.02},{"dt":1696672800,"sunrise":1696654213,"sunset":1696695188,"moonrise":1696715100,"moonset":1696686120,"moon_phase":0.78,"summary":"There will be clear sky until morning, then partly cloudy","temp":{"day":20.09,"min":14.68,"max":22.74,"night":20.55,"eve":22.74,"morn":15.03},"feels_like":{"day":19.25,"night":19.91,"eve":22.04,"morn":13.97},"pressure":1023,"humidity":42,"dew_point":6.58,"wind_speed":6.13,"wind_deg":277,"wind_gust":11.95,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"clouds":88,"pop":0,"uvi":3.4},{"dt":1696759200,"sunrise":1696740696,"sunset":1696781469,"moonrise":0,"moonset":1696774320,"moon_phase":0.81,"summary":"There will be partly cloudy today","temp":{"day":23.71,"min":12.25,"max":25.48,"night":12.25,"eve":16.9,"morn":19.53},"feels_like":{"day":23,"night":10.68,"eve":15.98,"morn":18.79},"pressure":1016,"humidity":33,"dew_point":6.5,"wind_speed":8.1,"wind_deg":304,"wind_gust":18.46,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"clouds":44,"pop":0,"uvi":3.12},{"dt":1696845600,"sunrise":1696827180,"sunset":1696867750,"moonrise":1696805520,"moonset":1696862160,"moon_phase":0.84,"summary":"There will be partly cloudy today","temp":{"day":11.6,"min":9.31,"max":13.25,"night":11.6,"eve":13.13,"morn":9.31},"feels_like":{"day":9.73,"night":9.44,"eve":11.1,"morn":8.75},"pressure":1031,"humidity":35,"dew_point":-3.58,"wind_speed":3.96,"wind_deg":330,"wind_gust":7.14,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"clouds":84,"pop":0,"uvi":4},{"dt":1696932000,"sunrise":1696913665,"sunset":1696954033,"moonrise":1696896060,"moonset":1696949700,"moon_phase":0.87,"summary":"Expect a day of partly cloudy with clear spells","temp":{"day":11.89,"min":9.03,"max":16.9,"night":14.61,"eve":16.9,"morn":9.56},"feels_like":{"day":9.89,"night":13.09,"eve":15.38,"morn":9.56},"pressure":1028,"humidity":29,"dew_point":-5.68,"wind_speed":3.08,"wind_deg":204,"wind_gust":5.82,"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"clouds":5,"pop":0,"uvi":4},{"dt":1697018400,"sunrise":1697000149,"sunset":1697040316,"moonrise":1696986540,"moonset":1697037060,"moon_phase":0.9,"summary":"You can expect partly cloudy in the morning, with clearing in the afternoon","temp":{"day":17.75,"min":13.37,"max":23.03,"night":17.42,"eve":23.03,"morn":13.37},"feels_like":{"day":16.65,"night":16.52,"eve":22.38,"morn":11.91},"pressure":1023,"humidity":41,"dew_point":4.23,"wind_speed":1.82,"wind_deg":247,"wind_gust":4.16,"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"clouds":100,"pop":0,"uvi":4}]}

And the template looks like this:

<template>
  <v-card 
    class="mx-auto"
    max-width="368"
  >
    <v-card-item title="Budapest">
      <template v-slot:subtitle>
        {{ description }}
      </template>
    </v-card-item>

    <v-card-text class="py-0">
      <v-row align="center" no-gutters>
        <v-col
          class="text-h2"
          cols="6"
        >
          {{ temp }}&deg;C
        </v-col>

        <v-col cols="6" class="text-right">
          <v-icon :icon="mdiicon" size="88"></v-icon>
        </v-col>
      </v-row>
    </v-card-text>

    <div class="d-flex py-3 justify-space-between">
      <v-list-item
        density="compact"
        prepend-icon="mdi-weather-windy"
      >
        <v-list-item-subtitle>{{ wind_speed }} km/h</v-list-item-subtitle>
      </v-list-item>

      <v-list-item
        density="compact"
        prepend-icon="mdi-weather-pouring"
      >
        <v-list-item-subtitle>{{ pop }}%</v-list-item-subtitle>
      </v-list-item>
    </div>

    <v-expand-transition>
      <div v-if="expand">
        <v-list class="bg-transparent">
          <v-list-item
            v-for="item in daily"
            :key="item.dt"
            :title="item.daytext"
            :append-icon="item.icon"
            :subtitle="item.temp.subtitle"
          >
          </v-list-item>
        </v-list>
      </div>
    </v-expand-transition>

    <v-divider></v-divider>

    <v-card-actions>
      <v-btn @click="expand = !expand">
        {{ !expand ? 'Full Report' : 'Hide Report' }}
      </v-btn>
    </v-card-actions>
  </v-card>
</template>

<script>
    export default {
        data() {
            return {      
                expand: false,         
                temp: 0,
                wind_speed: 0,
                description: "",
                icon: "00d",
                mdiicon: "mdi-weather-cloudy-clock",
                pop: 0,
                daily: []
            }
        },
        methods: {
          getDayText: function(daynum) {
            let thisday = new Date(daynum);
            const dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
            return dayNames[daynum.getDay()];
          },
          getMDIicon: function(iconname) {
            switch(iconname) {
              case "01d.png":
                return "mdi-white-balance-sunny";
                break;
              case "01n.png":
                return "mdi-moon-full";
                break;
              case "02d.png":
                return "mdi-weather-partly-cloudy";
                break;
              case "02n.png":
                return "mdi-weather-night-partly-cloudy";
                break;
              case "03d.png":
                return "mdi-weather-cloudy";
                break;
              case "03n.png":
                return "mdi-weather-cloudy";
                break;
              case "04d.png":
                return "mdi-clouds";
                break;
              case "04n.png":
                return "mdi-clouds";
                break;
              case "09d.png":
                return "mdi-weather-rainy";
                break;
              case "09n.png":
                return "mdi-weather-rainy";
                break;
              case "10d.png":
                return "mdi-weather-partly-rainy";
                break;
              case "10n.png":
                return "mdi-weather-partly-rainy";
                break;
              case "11d.png":
                return "mdi-weather-lightning-rainy";
                break;
              case "11n.png":
                return "mdi-weather-lightning-rainy";
                break;
              case "13d.png":
                return "mdi-snowflake";
                break;
              case "13n.png":
                return "mdi-snowflake";
                break;
              case "50d.png":
                return "mdi-weather-fog";
                break;
              case "50n.png":
                return "mdi-weather-fog";
                break;
              default;
                return "mdi-weather-cloudy-clock";
                break;
            }
          }
        },
        watch: {
            msg: function(){            
                if(this.msg.payload != undefined){                    
                    this.temp = this.msg.payload.current.temp.toFixed(0);
                    this.wind_speed = this.msg.payload.current.wind_speed.toFixed(1);
                    this.description = this.msg.payload.daily[0].summary;
                    this.icon = "http://openweathermap.org/img/w/" + this.msg.payload.current.weather[0].icon+ ".png";
                    this.mdiicon = getMDIicon(this.msg.payload.current.weather[0].icon);
                    this.pop = this.msg.payload.daily[0].pop*100;
                    this.daily = this.msg.payload.daily;
                    this.daily.forEach((element) => element.temp.subtitle = element.temp.min.toFixed(0) + "\xB0 / " + element.temp.max.toFixed(0) + "\xB0");
                    this.daily.forEach((element) => element.daytext = getDayText(element.dt) );
                }
            }
        }
    }
</script>

The template part looks OK so far. But I added the getDayText method to return the name of the date from the .daily[x].dt field. And I got error that getDayText method does not exist.
Next, I also added the getMDIicon function to convert the OWM icon names to MDI icon names. And after I did that the entire template does not render and I am seeing an Unexpected token (77:21) error message. I find this console absolutely useless to tell me what the issue is. I am sure it is a simple type, but I just can't find it.
Can you please help?

Methods belong to this

this.mdiicon = this.getMDIicon(this.msg.payload.current.weather[0].icon)

1 Like

Damn, I found the problem. I types default; instead of default: in the switch block. I think I will stop writing methods in template, and rather move all the logic into function node and pass all data prepared if necessary.

Unfortunately, that does not seem to be possible yet.

The problem is that if you write code in function node you'll get help from the editor so you don't fall into that "hard to debug cryptic console errors mess" :

But for ui_template node no such help from editor:

I don't know if there is something what can be improved but yeah, it is hard understand such errors specially with the vue now...

1 Like

Unfortunately, this is a feature Monaco editor does not support. unless Microsoft add code lens/Syntax checking etc to embedded code blocks or Vue add a language parser, this isn't gonna happen any time soon.

You can always fire up vscode with the volar plugin or write your templates in the Vuetify playground


Having just wrote the above, I realise the playground is web based and it supports embedded scripts parsing. I wonder if vuetify have written a language parser that's open to the public? :thinking:

UPDATE (and note to self) - there is a volar vue web worker here: GitHub - Kingwl/monaco-volar

1 Like

@Steve-Mcl can you please open up the update about the monaco volar. Is it possible way out of the current trouble?

1 Like