Translate

Archives

GNOME Shell Weather Extension

The current version of the GNOME shell does not come with a built-in weather application. As a result I, and at least two other developers that I am aware of, have written GNOME Shell extensions to provide this functionality.

In this post, I show you how to write a simple weather extension for the GNOME Shell using the free weather data from World Weather Online (WWO). What is different about this particular weather extension is that it displays 5 days’ worth of forecast data and uses the weather data suppliers’ images instead of GNOME icons to display a pictorial representation of the weather.

The WWO weather data feed can provide the requested weather data in either XML or JSON format. This extension asks WWO to supply the data in JSON format because JavaScript natively supports JSON. In a future post I plan to discuss how to use ECMAScript for XML (E4X) to parse XML data.

Here is an example of the JSON-formatted weather data returned by WWO for the 32940 zipcode (Melbourne, Florida) in JSON format:

{
   "data": {
         "current_condition": [ {
                       "cloudcover": "0",
                       "humidity": "62",
                       "observation_time": "07:49 PM",
                       "precipMM": "0.1",
                       "pressure": "1018",
                       "temp_C": "28",
                       "temp_F": "83",
                       "visibility": "16",
                       "weatherCode": "113",
                       "weatherDesc": [ {"value": "Sunny" } ],
                       "weatherIconUrl": [ {"value": "http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0001_sunny.png" } ],
                       "winddir16Point": "NNE",
                       "winddirDegree": "20",
                       "windspeedKmph": "9",
                       "windspeedMiles": "6"
                     } ],
          "request": [ {
                        "query": "32940",
                        "type": "Zipcode"
                     } ],
          "weather": [ {
                        "date": "2011-06-01",
                        "precipMM": "0.5",
                        "tempMaxC": "27",
                        "tempMaxF": "81",
                        "tempMinC": "24",
                        "tempMinF": "75",
                        "weatherCode": "116",
                        "weatherDesc": [ {"value": "Partly Cloudy" } ],
                        "weatherIconUrl": [ {"value": "http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0002_sunny_intervals.png" } ],
                        "winddir16Point": "ENE",
                        "winddirDegree": "66",
                        "winddirection": "ENE",
                        "windspeedKmph": "20",
                        "windspeedMiles": "13"
                       },
                       {
                        "date": "2011-06-02",
                        "precipMM": "0.0",
                        "tempMaxC": "27",
                        "tempMaxF": "81",
                        "tempMinC": "24",
                        "tempMinF": "75",
                        "weatherCode": "113",
                        "weatherDesc": [ {"value": "Sunny" } ],
                        "weatherIconUrl": [ {"value": "http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0001_sunny.png" } ],
                        "winddir16Point": "ENE",
                        "winddirDegree": "58",
                        "winddirection": "ENE",
                        "windspeedKmph": "28",
                        "windspeedMiles": "18"
                       },
                       {
                        "date": "2011-06-03",
                        "precipMM": "0.0",
                        "tempMaxC": "28",
                        "tempMaxF": "82",
                        "tempMinC": "24",
                        "tempMinF": "76",
                        "weatherCode": "113",
                        "weatherDesc": [ {"value": "Sunny" } ],
                        "weatherIconUrl": [ {"value": "http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0001_sunny.png" } ],
                        "winddir16Point": "ENE",
                        "winddirDegree": "68",
                        "winddirection": "ENE",
                        "windspeedKmph": "28",
                        "windspeedMiles": "18"
                       },
                       {
                        "date": "2011-06-04",
                        "precipMM": "0.0",
                        "tempMaxC": "28",
                        "tempMaxF": "82",
                        "tempMinC": "24",
                        "tempMinF": "75",
                        "weatherCode": "113",
                        "weatherDesc": [ {"value": "Sunny" } ],
                        "weatherIconUrl": [ {"value": "http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0001_sunny.png" } ],
                        "winddir16Point": "ENE",
                        "winddirDegree": "76",
                        "winddirection": "ENE",
                        "windspeedKmph": "24",
                        "windspeedMiles": "15"
                       },                       {
                        "date": "2011-06-05",
                        "precipMM": "0.0",
                        "tempMaxC": "29",
                        "tempMaxF": "84",
                        "tempMinC": "25",
                        "tempMinF": "76",
                        "weatherCode": "113",
                        "weatherDesc": [ {"value": "Sunny" } ],
                        "weatherIconUrl": [ {"value": "http:\/\/www.worldweatheronline.com\/images\/wsymbols01_png_64\/wsymbol_0001_sunny.png" } ],
                        "winddir16Point": "E",
                        "winddirDegree": "82",
                        "winddirection": "E",
                        "windspeedKmph": "15",
                        "windspeedMiles": "10"
                      } ]
    }
}


I pretty printed it to make it easier to read. As you can see, the data consists of three main sections – current_condition which contains information about the current weather conditions as reported at observation_time, request which contains information about the request type, i.e. in this case a zipcode, and weather which contains an array of between 2 and 5 days’ worth of forecast data. How many days of forecast data is returned is specified by you in the request URI. For some unknown reason, some data such as visibility and precipitation is only available in metric units so the weather extension had to convert this data into imperial units if imperial units are selected for the displayed data. By the way, this is the data which was in use when the screenshots shown below were taken.

Before you can obtain weather data from WWO, you have to sign up to obtain an API key. There is a free weather API and a paid premium weather API which provides much more weather data. For the purposes of this weather extension, the free weather API is adequate. Some people have complained to me that they cannot find where on the WWO website to sign up for the free API.

Here is a hint on where to go to sign up for the API key:

GNOME3 Shell screenshot

One person even claimed that the API key sign up was too complicated! All I can say is that I found the sign up page within a few seconds of looking for it and the actual sign up was fast and hassle free. Your mileage may vary but please do not bother asking me to help you sign up as some have done. I will not deign to answer such requests.

The weather extension button is located to the right of the date/time button on the top bar. That causes the date button to be offset to the left from the screen center. A couple of people complained to me about this but this is not visually upsetting to me. My response – feel free to add your own code to the extension to correct this offset if it bothers you or even move the weather button elsewhere on the top bar if that is what you want. The first case is somewhat difficult to code correctly, the second is trivial.

GNOME3 Shell screenshot

When the weather extension is first installed or after you log back into the GNOME Shell, the weather information may not be updated for a short while. In the first case, the delay is due to an asynchronous SOUP call (see the _loadJSON method below) is made to WWO to request and receive the weather data. In the second case, the extension is designed to only periodically update its weather data and the update may not be due to occur until some minutes after you have logged in. This period is user configurable (see WEATHERDATA_UPDATE_INTERVAL below.)

Here is the source code for the extension:

//
//  Copyright 2011 (c) Finnbarr P. Murphy.  All rights reserved.
//

const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const St = imports.gi.St;
const Soup = imports.gi.Soup;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;

const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;

const KPH2MPH = 0.62137;
const MM2INCH = 0.03937;

// configuration 
const MUNITS = 1;              //  0 - Imperial units, >0 - SI (Metric) units
const ZIPCODE = '32940';          //  Enclose in single quotes
const WEATHERDATA_KEY = '8067XXXX84214XXXX32805';    //  Enclose in single quotes
const WEATHERDATA_URL = 'http://free.worldweatheronline.com/feed/weather.ashx?q='
                        + ZIPCODE + '&format=json&num_of_days=5&key=' + WEATHERDATA_KEY;
const WEATHERDATA_UPDATE_INTERVAL = 600;


function WeatherButton() {
    this._init.apply(this, arguments);
}

WeatherButton.prototype = {
    _weatherInfo: null,
    _currentWeather: null,
    _futureWeather: null,
    _metadata: null,

    __proto__: PanelMenu.Button.prototype,

    _init: function(metadata) {

        PanelMenu.Button.prototype._init.call(this, 0.5);

        this._weatherButton = new St.BoxLayout({ style_class: 'weather-status'});
        this._weatherIconBox = new St.BoxLayout({ style_class: 'weather-status-icon'});

        let weatherIcon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
                                        icon_size: Main.panel.button.get_child().height - 2,
                                        icon_name: 'view-refresh-symbolic',
                                        style_class: 'weather-status-icon' });

        this._weatherIconBox.add_actor(weatherIcon);
        this._weatherButton.add_actor(this._weatherIconBox);
        this.actor.set_child(this._weatherButton);

        Main.panel._centerBox.add(this.actor, { y_fill: true });
        Main.panel._menus.addMenu(this.menu);

        this._metadata = metadata;
        this._getWeatherInfo;

        here = this;
        Mainloop.timeout_add(2000, function() {
            here._getWeatherInfo();
        });
    },

    // retrieve the weather data using SOAP
    _loadJSON: function(url, callback) {
        here = this;
        let session = new Soup.SessionAsync();
        let message = Soup.Message.new('GET', url);
        session.queue_message(message, function(session, message) {
            jObj = JSON.parse(message.response_body.data);
            callback.call(here, jObj['data']);
        });
    },


    // retrieve a weather icon image
    _getIconImage: function(iconpath) {
         let icon_file = this._metadata.path + "/icons/" +
                         iconpath[0].value.match(/\/wsymbols01_png_64\/(.*)/)[1];
         let file = Gio.file_new_for_path(icon_file);
         let icon_uri = file.get_uri();

         return St.TextureCache.get_default().load_uri_sync(1, icon_uri, 64, 64);
    },


    // get the weather information every WEATHERDATA_UPDATE_INTERVAL 
    // and update weather status on Panel
    _getWeatherInfo: function() {
        this._loadJSON(WEATHERDATA_URL, function(weatherinfo) {
            this._weatherInfo = weatherinfo;

            let curr = weatherinfo.current_condition;
            let desc = curr[0].weatherDesc;
            let comment = desc[0].value;
            let weatherIcon = this._getIconImage(curr[0].weatherIconUrl);
            let weatherInfo = new St.Label({text: (MUNITS > 0 ? curr[0].temp_C + 'C' : curr[0].temp_F + 'F'),
                                            style_class: 'weather-status-text' });

            this._weatherButton.get_children().forEach(function (actor) { actor.destroy(); });
            this._weatherIconBox.add_actor(weatherIcon);
            this._weatherButton.add_actor(this._weatherIconBox);
            this._weatherButton.add_actor(weatherInfo);
            this.actor.set_child(this._weatherButton);
            this.actor.connect('button-press-event', Lang.bind(this, this._displayUI));
        });

        Mainloop.timeout_add_seconds(WEATHERDATA_UPDATE_INTERVAL,
                                     Lang.bind(this, this. _getWeatherInfo));
    },

    _displayUI: function() {
        if (this._weatherInfo == null) return;

        let weather = this._weatherInfo;
        let request = weather.request;
        let curr = weather.current_condition;
        let weathers = weather.weather;
        let desc = curr[0].weatherDesc;
        let currLocation = request[0].query;
        let comment = desc[0].value;
        let observeTime = this._adjustTime(weathers[0].date, curr[0].observation_time);

        // current data
        let currTemperature = new St.Label({ text: (MUNITS > 0 ? curr[0].temp_F + 'F' : curr[0].temp_C + 'C') });
        let currHumidity = new St.Label({ text: (curr[0].humidity + '%') });
        let currPressure = new St.Label({ text: (MUNITS > 0 ? curr[0].pressure + ' mm' :  (curr[0].pressure * MM2INCH).toFixed(2) + '"') });
        let currWind = new St.Label({ text: (curr[0].winddir16Point + ' ' +
                                     (MUNITS > 0 ? curr[0].windspeedKmph + ' kph' : curr[0].windspeedMiles + ' mph')) });

        let currVisibility = new St.Label({ text: (MUNITS > 0 ? curr[0].visibility + ' km' : parseInt(curr[0].visibility * KPH2MPH) + ' miles') });
        let currObserveTime = new St.Label({ text: observeTime });
        let currCloudCover = new St.Label({ text: (curr[0].cloudcover +'%') });
        let currPercipitation = new St.Label({ text: (MUNITS > 0 ? curr[0].precipMM + ' mm' :  (curr[0].precipMM * MM2INCH).toFixed(2) + '"') });

        // destroy any previous components 
        if (this._currentWeather != null) {
            this._currentWeather.get_children().forEach(function (actor) { actor.destroy(); });
        }
        if (this._futureWeather != null) {
            this._futureWeather.get_children().forEach(function (actor) { actor.destroy(); });
        }

        let mainBox = new St.BoxLayout({ vertical: true,
                                         style_class: 'weather-box' });
        this._currentWeather = new St.BoxLayout({ style_class: 'weather-current-box'});
        this._futureWeather =  new St.BoxLayout({ style_class: 'weather-forecast-box'});
        mainBox.add_actor(this._currentWeather, { expand: false, x_fill: false });
        mainBox.add_actor(this._futureWeather, { expand: false, x_fill: false });
        this.menu.addActor(mainBox);

        let boxIcon = new St.BoxLayout({ style_class: 'weather-current-icon'});
        boxIcon.add_actor(this._getIconImage(curr[0].weatherIconUrl), { expand: false, x_fill: false, y_fill: false } );

        // set up left box for current conditions
        let boxLeft = new St.BoxLayout({ vertical: true,
                                         style_class: 'weather-current-summarybox'});

        let summary = (MUNITS > 0 ? curr[0].temp_C + 'C' : curr[0].temp_F + 'F');
        let currSummary = new St.Label({ text: summary,
                                         style_class: 'weather-current-summary'});
        let currLocation = new St.Label({ text: currLocation,
                                          style_class: 'weather-location' });
        boxLeft.add_actor(currLocation);
        boxLeft.add_actor(currSummary);

        // set up middle box for current conditions
        let boxMiddle = new St.BoxLayout({ style_class: 'weather-current-databox-left'});
        let mbCaptions = new St.BoxLayout({ vertical: true,
                                            style_class: 'weather-current-databox-captions'});
        let mbValues = new St.BoxLayout({ vertical: true,
                                          style_class: 'weather-current-databox-values'});
        boxMiddle.add_actor(mbCaptions);
        boxMiddle.add_actor(mbValues);

        mbCaptions.add_actor(new St.Label({ text: _('Visibility:')}));
        mbValues.add_actor(currVisibility);
        mbCaptions.add_actor(new St.Label({ text: _('Humidity:')}));
        mbValues.add_actor(currHumidity);
        mbCaptions.add_actor(new St.Label({ text: _('Pressure:')}));
        mbValues.add_actor(currPressure);
        mbCaptions.add_actor(new St.Label({ text: _('Wind:')}));
        mbValues.add_actor(currWind);

        // set up right box for current conditions
        let boxRight = new St.BoxLayout({ style_class: 'weather-current-databox-right'});
        rbCaptions = new St.BoxLayout({ vertical: true,
                                        style_class: 'weather-current-databox-captions'});
        rbValues = new St.BoxLayout({ vertical: true,
                                      style_class: 'weather-current-databox-values'});
        boxRight.add_actor(rbCaptions);
        boxRight.add_actor(rbValues);

        rbCaptions.add_actor(new St.Label({ text: _('Temperature:')}));
        rbValues.add_actor(currTemperature);
        rbCaptions.add_actor(new St.Label({ text: _('Observe Time:')}));
        rbValues.add_actor(currObserveTime);
        rbCaptions.add_actor(new St.Label({ text: _('Cloud Cover:')}));
        rbValues.add_actor(currCloudCover);
        rbCaptions.add_actor(new St.Label({ text: _('Precipitation:')}));
        rbValues.add_actor(currPercipitation);

        this._currentWeather.add_actor(boxIcon);
        this._currentWeather.add_actor(boxLeft);
        this._currentWeather.add_actor(boxMiddle);
        this._currentWeather.add_actor(boxRight);

       // now set up the 5 day forecast area
        for (let i = 0; i < weathers.length; i++) {
            let weather = weathers[i];
            let foreWeather = {};

            // forecast data
            let desc = weather.weatherDesc;
            let t_low = (MUNITS > 0 ?  weather.tempMinC : weather.tempMinF);
            let t_high = (MUNITS > 0 ?  weather.tempMaxC : weather.tempMaxF);
            let foreDate = this._getDate(i, weather.date);

            foreWeather.Icon = this._getIconImage(weather.weatherIconUrl);
            foreWeather.Day = new St.Label({ style_class: 'weather-forecast-day',
                                             text: foreDate });
            foreWeather.Temperature = new St.Label({ style_class: 'weather-forecast-temperature',
                                                     text: (t_low + ' - ' + t_high + (MUNITS > 0 ? 'C' : 'F')) });

            let dataBox = new St.BoxLayout({vertical: true, style_class: 'weather-forecast-databox'});
            dataBox.add_actor(foreWeather.Day, { x_align: St.Align.START, expand: false, x_fill: false });
            dataBox.add_actor(foreWeather.Temperature, { x_align: St.Align.START, expand: false, x_fill: false });
            let iconBox = new St.BoxLayout({style_class: 'weather-forecast-icon'});
            iconBox.add_actor(foreWeather.Icon);

            this._futureWeather.add_actor(iconBox);
            this._futureWeather.add_actor(dataBox);
        }
    },

   // UTC time provided by weather data source. Convert to local time.
    _adjustTime: function(dateStr, timeStr) {

        let pattern = /(\d{4})-(\d{2})-(\d{2})/;
        let newDateStr = dateStr.replace(pattern, "$2/$3/$1") + ' ' + timeStr;

        let curDate = new Date();
        let tzOffset = curDate.getTimezoneOffset();

        let newMS = Date.parse(newDateStr) - ( tzOffset * 60000 );
        let newDate = new Date(newMS);

        return newDate.toTimeString().substring(0,5);
    },


     // get day-of-week for a date
    _getDate: function(index, dateStr) {
         switch (index) {
             case 0:
                 return _('Today');
             case 1:
                 return _('Tomorrow');
         }

         let dowString = [ _('Monday'),  _('Tuesday'),  _('Wednesday'),  _('Thursday'),
                           _('Friday'),  _('Saturday'),  _('Sunday') ];

         let tmpDate = new Date(dateStr);
         let tmpDOW  = tmpDate.getDay();

         return dowString[tmpDOW];
    }
};

function main(metadata) {

    if (WEATHERDATA_KEY && ZIPCODE) {
        new WeatherButton(metadata);
    } else {
       global.log("ERROR: Weather extension. Missing WEATHERDATA_KEY or ZIPCODE.");
    }

}


You can choose to display the weather data in either metric or imperial units of measurements by changing the value of the MUNITS constant. The extension is set up to display weather information for US ZIPCODES. However it is trivial to modify the extension source code to display information by UK or Canadian postal code or by city name or airport code. Please do not ask me to do that for you – I am leaving it as an exercise for you to make the required changes. Note that the query URL asks for 5 days of forecast data. This is the maximum number of days that the free data feed from WWO will supply forecast data for. You could also modify the extension to ask for fewer days than this but this is a more complex task as the layout of the panel would have to be modified to take account of the reduced information being displayed.

To improve performance, WWO icon images are stored locally in an extension subdirectory rather than being retrieved as required from WWO. The _getIconImage method is passed a full pathname to the appropriate image and uses the load_uri_sync method to load the icon image into the texture cache. This could be made into an asynchronous call but I choose not to do so. If you wish to use the default GNOME weather icons instead of the WWO icons, you could modify the above code to use a simple lookup table to map each weatherCode value (see the example JSON-formatted weather datafile above) to the appropriate GNOME weather icon.

Here is a closeup of the weather panel:

GNOME3 Shell screenshot

To achieve this layout, the weather extension makes extensive use of CSS. Here is the stylesheet.css for this extension.

weather-icon {
    padding-right: 5px;
}

.weather-status-icon {
    height: 20px;
    width: 20px;
    padding: 0px;
    border: 1px solid rgba(128, 128, 128, 0.4);
    border-radius: 2px;
}

.weather-status-text {
    padding: 1px 0 0 5px;

}

.weather-current-summarybox {
    padding: 0px;
}

.weather-location {
    padding-left: 5px;
    font-size: 2em;
    font-weight: bold;
}

.weather-current-summary {
    padding-left: 10px;
    font-size: 2em;
}

.weather-current-databox-left {
    padding: 5px 5px 5px 10px;
}

.weather-current-databox-right {
    padding: 5px;
}

.weather-current-icon {
    height: 64px;
    width: 64px;
    padding: 10px 0 10px 0;
}

.weather-current-box {
    padding: 0 0 0 10px;
}

.weather-current-databox-captions {
    text-align: right;
    padding-right: 5px;
    font-size: 14px;
    color: #999999;
}

.weather-forecast-box {
    padding: 0 10px;
}

.weather-forecast-icon {
    padding: 0px;
    width: 32px;
    height: 32px;
    border: 1px solid rgba(128, 128, 128, 0.5);
    border-radius: 2px;
}

.weather-forecast-databox {
    padding: 0 10px 0 5px;
}

.weather-forecast-day {
    color: #999999;
    font-size: 70%;
    font-weight: bold;
}
.weather-forecast-temperature {
    font-size: 80%;
}

.weather-box {
    margin: 0px;
}

.weather-forecast {
    margin: 0px;
    padding: 0px;
}


As you can see the stylesheet allows for extensive styling of the weather extension to suit your own particular tastes. I am going to assume that you understand CSS so no explanation of the contents of the above stylesheet is provided. However I will point out that you need to ensure that your stylesheet selector names are unique to avoid overriding other extension selector names or theme selector names.

The need for a weather extension such as this will diminish within a period of 12 months or so as there are plans in place to include weather observation and forecast functionality in the next major release of the GNOME shell.

Well, that is all there is to this very simple weather extension. Feel free to modify it to come up with your own version. Please just ensure that you include my copyright notice as well as your own. By the way, this extension is not completely bug free. I am aware that sometimes when the GNOME Shell is restarted an empty ghost-like menu is displayed. When I get time I will sort this out and update the extension.

Enjoy and keep experimenting with the GNOME Shell!

P.S.  You can download a tarball of this extension in the GNOME Shell Extensions download area of my website. Please read the README file before installing it and follow the instructions therein.

9 comments to GNOME Shell Weather Extension

  • steven0lisa

    Wow, great job!

    Mind you telling me where to find the api doc of gnome-shell script?
    I googled it, but still couldn’t find it.
    Thank you!

  • William

    Hi,

    Thank’s for your extension….but, there is two little bugs.

    First one in the temperature unit at line 131

    // current data
    let currTemperature = new St.Label({ text: (MUNITS > 0 ? curr[0].temp_F + ‘F’ : curr[0].temp_C + ‘C’) });

    should be

    // current data
    let currTemperature = new St.Label({ text: (MUNITS > 0 ? curr[0].temp_C + ‘C’ : curr[0].temp_F + ‘F’) });

    Second one is with 5 days display:

    I have
    – today (Friday for the example) (aujourd’hui in French)
    – tomorow (should be Saturday in the example) (Demain in French)
    – Here we should have Sunday but I have Monday !!!
    – Here we should have Monday but I have Tuesday !!!
    – Here we should have Tuesday but I have Wednesday !!!

    There is no Sunday in this week :-)

    Bye

    William

  • Christoph

    Hi William,
    I was looking for the same issues. Thanks for your suggestion.

    My suggestion to solve the second issue you mentioned is to change:
    // get day-of-week for a date
    _getDate: function(index, dateStr) {
    switch (index) {
    case 0:
    return _(‘Today’);
    case 1:
    return _(‘Tomorrow’);
    }
    into
    // get day-of-week for a date
    _getDate: function(index, dateStr) {
    switch (index) {
    case 0:
    return _(‘Tomorrow’);
    }

    By the way the part in the README “A future version will probably support city/country as well as
    zipcodes” is not entirely true, as for now you can already use citys, Airport Codes and even geolocations. In the extension.js you might want to try:

    const ZIPCODE = ‘Munich’;
    const ZIPCODE = ‘48.14,11.58’;
    const ZIPCODE = ‘MUC’;

    Many thanks to Finnbarr, tho code is relly very clean and it’s yust fun to read it.

    Bye,
    Christoph

  • Christoph

    Hi,
    sorry, there I did not look into the json file properly. The correct suggestion would be:
    // get day-of-week for a date
    _getDate: function(index, dateStr) {
    switch (index) {
    case 0:
    return _(‘Today’);
    case 1:
    return _(‘Tomorrow’);
    }

    let dowString = [ _(‘Sunday’), _(‘Monday’), _(‘Tuesday’), _(‘Wednesday’),
    _(‘Thursday’), _(‘Friday’), _(‘Saturday’) ];

    Since the day starts with sunday.
    Cheers,
    Christoph

  • Hugo

    Sorry for posting this here but I couldn’t find a post about the Autohide top bar extension for Gnome Shell.

    Regarding said extension, it would be nice if the top bar could behave like the bottom panel meaning when unhidden an option to hover on top of open windows instead of displacing them downwards when one wants to see it.

    Cheers

  • Simon

    Hi,
    I experienced an issue when I installed your extension — it doesn’t work for me.
    http://img208.imageshack.us/img208/4051/bug2h.jpg
    I put a screen shot. There is a problem connected to creating a new file
    Gio.make_directory_with_parents cannot create a directory — file exists.
    And you can see that this extension doesn’t seem to work, although the statement says it has been loaded (empty bubble).
    How can I resolve that issue? I am operating on FC15, gnome3 which have been installed recently.

    All best,
    Simon

    • The GIO make directory error occurs on F15 GNOME 3.0 with or without the Weather extension. Remove all your extensions and you will still see that particular error. It is a minor code design flaw in that particular version of the GNOME Shell.

      Unless you configure the Weather extension per provided instructions, it is not going to work.

  • I like the extension! However it only works under shell 3.0. Would you pleased to migrate it to shell 3.2? Thank you!