diff --git a/The Dark Sky Forecast API Docs.html b/The Dark Sky Forecast API Docs.html new file mode 100644 index 0000000..09a4220 --- /dev/null +++ b/The Dark Sky Forecast API Docs.html @@ -0,0 +1,666 @@ +The Dark Sky Forecast API + + + v2 Forecast API + +The Forecast API lets you query for most locations on the globe, and +returns: + + * Current conditions + * Minute-by-minute forecasts out to 1 hour (where available) + * Hour-by-hour forecasts out to 48 hours + * Day-by-day forecasts out to 7 days + +There are two main API calls. The first <#forecast_call> returns the +current forecast (for the next week): + +|https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE| + +The second <#time_call> lets one query for a specific time, past or +future (for many places, 60 years in the past to 10 years in the future): + +|https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE,TIME| + +*Note to Dark Sky API users*: The old docs can be found here . +Details on migrating to the new API can be found at the bottom of the +page <#migration>. + + + Changelog + + * *3 Dec 2014:* Added Russian to the list of supported languages. + * *23 Sep 2014:* Added Portuguese and Bosnian to the list of supported + languages. + * *14 Aug 2014:* Added Polish and Pig Latin (!?) to the list of + supported languages. + * *29 Jul 2014:* Added Italian to the list of supported languages. + * *11 Jun 2014:* Added Spanish to the list of supported languages. + * *29 May 2014:* Added the |lang| option <#options>. + * *7 Jan 2014:* Added the |moonPhase| property to data points. + * *10 Dec 2013:* Added the MADIS <#data-sources> data source. + * *5 Dec 2013:* Added the |nearestStormDistance| and + |nearestStormBearing| properties to data points. + * *15 Aug 2013:* Added the |apparentTemperature| properties to data + points. + * *17 Jul 2013:* Added the |description| property to severe weather + alerts <#alerts>. + * *15 Jul 2013:* Added the RTMA and SREF weather models + <#data-sources>, and the |X-Forecast-API-Calls| response header + <#response-headers>. + * *22 May 2013:* Added the |extend| option. + * *6 May 2013:* Added string date format to time machine requests. + * *17 Apr 2013:* Added |dewPoint| and |ozone| properties to data points. + * *9 Apr 2013:* Added the |ca| and |auto| unit options, and added + |precipIntensityMax| and |precipIntensityMaxTime| properties on + daily data points. + * *1 Apr 2013:* Added the |exclude| option. + * *29 Mar 2013:* Deprecated the |si| option, replacing it with the + |units| option. When upgrading, be advised that |windSpeed| is now + in meters per second, rather than in kilometers per hour. (*Edit 9 + Apr 2013:* one can now use |?units=ca| to precisely emulate the + functionality of |?si|. Please update any code using the deprecated + functionality at your earliest convenience.) + * *26 Mar 2013:* Initial version. + + + API Libraries + +Looking to include Forecast in your app? These wrapper libraries should +make it quick and easy to do so: + + * Official Ruby Wrapper Library + by David Czarnecki + + * Android Client Wrapper Library + by Christopher Brown + + * C++ Wrapper Library + by Todd Shore + + * C# Wrapper Library by + Charl Cilliers + * Portable C# Wrapper Library + by Jerome Cheng + + * CFML Wrapper Library by + Danny Cork + * Clojure Wrapper Library + by J. D. Hollis + + * Erlang Wrapper Library by + Derek Brown + * Go Wrapper Library by + Martin-Louis Bright + * Java Wrapper Library + by David Ervideira + * Java Wrapper Library + by Kushan Jayatilleke + * JavaScript Wrapper Library + (with a + PHP-based proxy) by Ian Tearle + * Node.JS Wrapper Library + by Matt Walters + + * Node.JS Wrapper Library by Chris + Jones + * Node.JS Wrapper Library + by Serhiy + Oplakanets + * Objective-C Wrapper Library + by Rob Phillips + + * Objective-C Wrapper Library by + Carl Jahn + * iOS XCode Project Template + by Brandon Emrich + + * Perl Wrapper Library + + by Martin-Louis Bright + * PHP Wrapper Library + by Tobias + Redmann + * PHP Wrapper Library by + Guilherm Uhelski + * PHP Wrapper Library + (supporting multiple, simultaneous calls) by Charlie Gorichanaz + + * Python Wrapper Library + by Ze'ev Gilovitz + * R Wrapper Library by Bob + Rudis + * Unofficial Ruby Wrapper Library + by Vicent Gozalbes + + * Scala Wrapper Library + by Garrett Thornburg + +Additionally, Gerard Davison has +provided a JSON Schema of +the API, and Alex Handy has provided a CodeEnvy IDE + for a Java API wrapper +of our API. + +Finally, please note that all of the above API wrappers (except for the +official Ruby wrapper) are unofficial: we cannot provide support for +them! If you have any questions about them, please contact the library's +author. + + + The Forecast Call + +|https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE| + +|APIKEY| should be your Dark Sky API key. |LATITUDE| and |LONGITUDE| +should be the geographic coordinates of a location in decimal degrees. +/(Please note that the base domain for this API request is +|api.forecast.io|, not |api.darkskyapp.com|.)/ + +The response will be a JSON-formatted object with the following +properties defined: + + * |latitude|: The requested latitude. + * |longitude|: The requested longitude. + * |timezone|: The IANA timezone name for the requested location (e.g. + |America/New_York|). This is the timezone used for text forecast + summaries and for determining the exact start time of daily data + points. /(Developers are advised to rely on local system settings + rather than this value if at all possible: users may deliberately + set an unusual timezone, and furthermore are likely to know what + they actually want better than our timezone database does.)/ + * |offset|: The current timezone offset in hours from GMT. + * |currently|: A /data point/ (see below) containing the current + weather conditions at the requested location. + * |minutely|: A /data block/ (see below) containing the weather + conditions minute-by-minute for the next hour. /(This property’s + name should be read as an adjective—analogously to “hourly” or + “daily” and meaning “reckoned by the minute”—rather than as an + adverb meaning “meticulously.” Yes, we know that this is not proper + English. No, we will not change it. Complaints to this effect will + be deleted with utmost prejudice.)/ + * |hourly|: A /data block/ (see below) containing the weather + conditions hour-by-hour for the next two days. + * |daily|: A /data block/ (see below) containing the weather + conditions day-by-day for the next week. + * |alerts|: An array of /alert objects/ (see below), which, if + present, contains any severe weather alerts, issued by a + governmental weather authority, pertinent to the requested location. + * |flags|: An object containing miscellaneous metadata concerning this + request. (See /flags object/, below.) + +In general, to determine the weather at a given point in time, one +should examine the highest-precision data block defined (|minutely|, +|hourly|, and |daily| respectively), taking any data available from from +it and falling back to the next-highest precision data block for any +properties that are missing for the point in time desired. + + + Data Points + +A /data point/ object represents the various weather phenomena occurring +at a specific instant of time, and has many varied properties. All of +these properties (except |time|) are /optional/, and will only be set if +we have that type of information for that location and time. Please note +that |minutely| data points are always aligned to the nearest minute +boundary, |hourly| points to the top of the hour, and |daily| points to +midnight of that day. + +Data points in the |daily| /data block/ (see below) are special: instead +of representing the weather phenomena at a given instant of time, they +are an aggregate point representing the weather phenomena that will +occur over the entire day. For precipitation fields, this aggregate is a +maximum; for other fields, it is an average. + +Data point objects may contain the following properties: + + * |time|: The UNIX time (that is, seconds since midnight GMT on 1 Jan + 1970) at which this data point occurs. + * |summary|: A human-readable text summary of this data point. + * |icon|: A machine-readable text summary of this data point, suitable + for selecting an icon for display. If defined, this property will + have one of the following values: |clear-day|, |clear-night|, + |rain|, |snow|, |sleet|, |wind|, |fog|, |cloudy|, + |partly-cloudy-day|, or |partly-cloudy-night|. (Developers should + ensure that a sensible default is defined, as additional values, + such as |hail|, |thunderstorm|, or |tornado|, may be defined in the + future.) + * |sunriseTime| and |sunsetTime| (only defined on |daily| data + points): The UNIX time (that is, seconds since midnight GMT on 1 Jan + 1970) of the last sunrise before and first sunset after the solar + noon closest to local noon on the given day. (Note: near the poles, + these may occur on a different day entirely!) + * |moonPhase| (only defined on |daily| data points): A number + representing the fractional part of the lunation number + of the given day. + This can be thought of as the “percentage complete” of the current + lunar month: a value of |0| represents a new moon, a value of |0.25| + represents a first quarter moon, a value of |0.5| represents a full + moon, and a value of |0.75| represents a last quarter moon. (The + ranges in between these represent waxing crescent, waxing gibbous, + waning gibbous, and waning crescent moons, respectively.) + * |nearestStormDistance| (only defined on |currently| data points): A + numerical value representing the distance to the nearest storm in + miles. (This value is /very approximate/ and should not be used in + scenarios requiring accurate results. In particular, a storm + distance of zero doesn’t necessarily refer to a storm at the + requested location, but rather a storm in the vicinity of that + location.) + * |nearestStormBearing| (only defined on |currently| data points): A + numerical value representing the direction of the nearest storm in + degrees, with true north at 0° and progressing clockwise. (If + |nearestStormDistance| is zero, then this value will not be defined. + The caveats that apply to |nearestStormDistance| also apply to this + value.) + * |precipIntensity|: A numerical value representing the average + expected intensity (in inches of liquid water per hour) of + precipitation occurring at the given time /conditional on + probability/ (that is, assuming any precipitation occurs at all). A + /very/ rough guide is that a value of |0| in./hr. corresponds to no + precipitation, |0.002| in./hr. corresponds to very light + precipitation, |0.017| in./hr. corresponds to light precipitation, + |0.1| in./hr. corresponds to moderate precipitation, and |0.4| + in./hr. corresponds to heavy precipitation. + * |precipIntensityMax|, and |precipIntensityMaxTime| (only defined on + |daily| data points): numerical values representing the maximumum + expected intensity of precipitation (and the UNIX time at which it + occurs) on the given day in inches of liquid water per hour. + * |precipProbability|: A numerical value between 0 and 1 (inclusive) + representing the probability of precipitation occuring at the given + time. + * |precipType|: A string representing the type of precipitation + occurring at the given time. If defined, this property will have one + of the following values: |rain|, |snow|, |sleet| (which applies to + each of freezing rain, ice pellets, and “wintery mix”), or |hail|. + (If |precipIntensity| is zero, then this property will not be defined.) + * |precipAccumulation| (only defined on |hourly| and |daily| data + points): the amount of snowfall accumulation expected to occur on + the given day. (If no accumulation is expected, this property will + not be defined.) + * |temperature| (not defined on |daily| data points): A numerical + value representing the temperature at the given time in degrees + Fahrenheit. + * |temperatureMin|, |temperatureMinTime|, |temperatureMax|, and + |temperatureMaxTime| (only defined on |daily| data points): + numerical values representing the minimum and maximumum temperatures + (and the UNIX times at which they occur) on the given day in degrees + Fahrenheit. + * |apparentTemperature| (not defined on |daily| data points): A + numerical value representing the apparent (or “feels like”) + temperature at the given time in degrees Fahrenheit. + * |apparentTemperatureMin|, |apparentTemperatureMinTime|, + |apparentTemperatureMax|, and |apparentTemperatureMaxTime| (only + defined on |daily| data points): numerical values representing the + minimum and maximumum apparent temperatures (and the UNIX times at + which they occur) on the given day in degrees Fahrenheit. + * |dewPoint|: A numerical value representing the dew point at the + given time in degrees Fahrenheit. + * |windSpeed|: A numerical value representing the wind speed in miles + per hour. + * |windBearing|: A numerical value representing the direction that the + wind is coming /from/ in degrees, with true north at 0° and + progressing clockwise. (If |windSpeed| is zero, then this value will + not be defined.) + * |cloudCover|: A numerical value between 0 and 1 (inclusive) + representing the percentage of sky occluded by clouds. A value of + |0| corresponds to clear sky, |0.4| to scattered clouds, |0.75| to + broken cloud cover, and |1| to completely overcast skies. + * |humidity|: A numerical value between 0 and 1 (inclusive) + representing the relative humidity. + * |pressure|: A numerical value representing the sea-level air + pressure in millibars. + * |visibility|: A numerical value representing the average visibility + in miles, capped at 10 miles. + * |ozone|: A numerical value representing the columnar density of + total atmospheric ozone at the given time in Dobson units. + +All of the above numeric, non-time fields may, optionally, have an +associated |Error| value defined (with the property +|precipIntensityError|, |windSpeedError|, |pressureError|, etc.), +representing our system’s confidence in its prediction. Such properties +represent standard deviations of the value of their associated property; +small error values therefore represent a strong confidence, while large +error values represent a weak confidence. These properties are omitted +where the confidence is not precisely known (though generally considered +to be adequate). + + + Data Blocks + +A /data block/ object represents the various weather phenomena occurring +over a period of time. Such objects contain the following properties: + + * |summary|: A human-readable text summary of this data block. + * |icon|: A machine-readable text summary of this data block (see + /data point/, above, for an enumeration of possible values that this + property may take on). + * |data|: An array of /data point/ objects (see above), ordered by + time, which together describe the weather conditions at the + requested location over time. + +Ideally, the |minutely| data block will contain data for the next hour, +the |hourly| data block for the next two days, and the |daily| data +block for the next week; however, if we are lacking data for a given +time period, the data point sequence may contain gaps or terminate +early. Furthermore, if no data points for a time period are known, then +the data block will be omitted from the response in its entirety. +Developers are strongly encouraged, therefore, to check for the presence +of data before attempting to read it. + + + Alert Objects + +An /alert object/ represents a severe weather warning issued for the +requested location by a governmental authority (for a list of which +authorities we currently support, please see /data sources/, below). +Such objects contain the following properties: + + * |title|: A short text summary of the alert. + * |expires|: The UNIX time (that is, seconds since midnight GMT on 1 + Jan 1970) at which the alert will cease to be valid. + * |description|: A detailed text description of the alert from the + appropriate weather service. + * |uri|: An HTTP(S) URI that contains detailed information about the + alert. + + + Flags Objects + +The /flags object/ contains various metadata information related to the +request. Such objects may optionally contain any of the following +properties: + + * |darksky-unavailable|: The presence of this property indicates that + the Dark Sky data source supports the given location, but a + temporary error (such as a radar station being down for maintenace) + has made the data unavailable. + * |darksky-stations|: This property contains an array of IDs for each + radar station utilized in servicing this request. + * |datapoint-stations|: This property contains an array of IDs for + each DataPoint station utilized in servicing this request. + * |isd-stations|: This property contains an array of IDs for each ISD + station utilized in servicing this request. + * |lamp-stations|: This property contains an array of IDs for each + LAMP station utilized in servicing this request. + * |metar-stations|: This property contains an array of IDs for each + METAR station utilized in servicing this request. + * |metno-license|: The presence of this property indicates that data + from api.met.no was utilized in order to + facilitate this request (as per their license agreement). + * |sources|: This property contains an array of IDs for each data + source utilized in servicing this request. (For more information, + see /data sources/, below.) + * |units|: The presence of this property indicates which units were + used for the data in this request. (For more information, see + /options/, below.) + + + Forecast at a Given Time + +It is also possible to request a forecast for an arbitrary point in time: + +|https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE,TIME| + +|TIME| should either be a UNIX time (that is, seconds since midnight GMT +on 1 Jan 1970) or a string formatted as follows: +|[YYYY]-[MM]-[DD]T[HH]:[MM]:[SS]| (with an optional time zone formatted +as |Z| for GMT time or |{+,-}[HH][MM]| for an offset in hours or +minutes). For the latter format, if no timezone is present, local time +(at the provided latitude and longitude) is assumed. (This string format +is a subset of ISO 8601 + +time. An as example, |2013-05-06T12:00:00-0400|.) + +If a time is provided in this way, only the conditions for the day on +which that time occurred (or will occur), midnight-to-midnight, are +provided; in all other ways, making such a request is equivalent to +getting in a time machine, going back or forward in time to the given +moment, and making a normal forecast request. (In fact, this is how it +is implemented behind-the-scenes.) Generally speaking, forecasted data +is more accurate the nearer you query to the present moment. (That is, +the present moment if you don’t have a time machine.) + + + Options + +The API request may optionally be modified through the use of query +parameters. It will respond to the following: + + * |callback=[callback]|: Return the API response as JSONP + . /Please use caution when + using this,/ since exposing your API key to the public is a security + hazard and, if abused, will result in the revokation of your API + key. However, if developing a personal- or internal-use app, this is + a convenient method of doing so. + * |units=[setting]|: Return the API response in units other than the + default Imperial units. In particular, the following settings are + possible: + o |us|: The default, as outlined above. + o |si|: Returns results in SI units. In particular, properties now + have the following units: + + |summary|: Any summaries containing temperature or snow + accumulation units will have their values in degrees Celsius + or in centimeters (respectively). + + |nearestStormDistance|: Kilometers. + + |precipIntensity|: Millimeters per hour. + + |precipIntensityMax|: Millimeters per hour. + + |precipAccumulation|: Centimeters. + + |temperature|: Degrees Celsius. + + |temperatureMin|: Degrees Celsius. + + |temperatureMax|: Degrees Celsius. + + |apparentTemperature|: Degrees Celsius. + + |dewPoint|: Degrees Celsius. + + |windSpeed|: Meters per second. + + |pressure|: Hectopascals (which are, conveniently, + equivalent to the default millibars). + + |visibility|: Kilometers. + o |ca|: Identical to |si|, except that |windSpeed| is in + kilometers per hour. + o |uk|: Identical to |si|, except that |windSpeed| is in miles per + hour, as in the US. (This option is provided because adoption of + SI in the UK has been inconsistent.) + o |auto|: Selects the relevant units automatically, based on + geographic location. + * |exclude=[blocks]|: Exclude some number of data blocks from the API + response. This is useful for reducing latency and saving cache + space. |[blocks]| should be a comma-delimeted list (without spaces) + of any of the following: |currently|, |minutely|, |hourly|, |daily|, + |alerts|, |flags|. (Crafting a request with /all/ of the above + blocks excluded is exceedingly silly and not recommended.) + * |extend=hourly|: When present on a forecast request, return hourly + data for the next seven days, rather than the next two. (This option + is ignored on time machine requests. When using this option, we + strongly recommend ensuring that your HTTP client supports compression.) + * |lang=[language]|: Return text summaries in the desired language. + (Please be advised that units in the summary will be set according + to the |units| option, above, so be sure to set both options as + needed.) |[language]| may be |bs| (Bosnian), |de| (German), |en| + (English, which is the default), |es| (Spanish), |fr| (French), |it| + (Italian), |nl| (Dutch), |pl| (Polish), |pt| (Portuguese), |ru| + (Russian), |tet| (Tetum), or |x-pig-latin| (Igpay Atinlay). (If a + different language is desired, please consider contributing to our + API translation module + on Github.) + + + Response Headers + +The API will set the following HTTP response headers to values useful to +developers: + + * |Cache-Control| and |Expires|: These headers are set to conservative + values for data caching purposes based on the data present in the + response body. + * |X-Forecast-API-Calls|: The number of API calls made by the given + API key for today. + * |X-Response-Time|: The server-side response time of the request. + + + Notes + + * Just about every object property in the response is optional. In + fact, a request with no data to return will return a nearly empty + object, rather than a failure. Robust code will check for the + presence of required parameters before using them, and will fail + gracefully if they are not present (or return an error if a minimum + functional set of data is not available). + * All numeric properties are real numbers, except for UNIX timestamps, + which are (signed) integers. + * Summaries on the |hourly| data block actually only cover up to a + maximum of 24 hours, rather than the full time period in the data + block. We found that covering the full 48 hours could be, in a + number of circumstances, far too wordy to be practical. + * Summaries and icons on |daily| data points actually cover the period + from 4AM to 4AM, rather than the stated time period of midnight to + midnight. We found that the summaries so generated were less awkward. + * The Forecast Data API supports HTTP compression. We heartily + recommend using it, as it will make responses much smaller over the + wire. To enable it, simply add an |Accept-Encoding: gzip| header to + your request. (Most HTTP client libraries wrap this functionality + for you, please consult your library’s documentation for details. Be + advised that we do not support such compression over HTTP/1.0 + connections.) + * JSON responses are in UTF-8 format (due to text summaries, which can + contain Unicode characters). Please ensure that your JSON parser + acts accordingly. + + + Data Sources + +This API is backed by a wide range of data sources, which are aggregated +together statistically to provide the most accurate forecast possible +for a given location. Any data sources used to service a given request +will be noted in the |flags| section of a forecast response. These +sources include: + + 1. Dark Sky’s own hyperlocal precipitation forecasting system + (id + |darksky|), backed by radar data from the following systems: + * The USA NOAA’s NEXRAD system (USA). + * The UK Met Office’s NIMROD system (UK, Ireland). + * (More coming soon.) + 2. The USA NOAA’s LAMP system + (USA, id |lamp|). + 3. The UK Met Office’s Datapoint API + (UK, id |datapoint|). + 4. The Norwegian Meteorological Institute’s meteorological forecast API + (global, id |metno|). + 5. The USA NOAA’s Global Forecast System + (global, id + |gfs|). + 6. The USA NOAA’s Integrated Surface Database + (global, id |isd|). + 7. The USA NOAA’s Public Alert system + (USA, id |nwspa|). + 8. The UK Met Office’s Severe Weather Warning system + (UK, id + |metwarn|). + 9. Environment Canada’s Canadian Meteorological Center ensemble model + + (global, id |cmc|). +10. The US Navy’s Fleet Numerical Meteorology and Oceanography Ensemble + Forecast System + + (global, id |fnmoc|). +11. The USA NOAA and Environment Canada’s North American Ensemble + Forecast System + + (global, id |naefs|). +12. The USA NOAA’s North American Mesoscale Model + (North + America, id |nam|). +13. The USA NOAA’s Rapid Refresh Model + (North America, id |rap|). +14. The Norwegian Meteorological Institute’s GRIB file forecast for + Central Europe + (Europe, + id |metno_ce|). +15. The Norwegian Meteorological Institute’s GRIB file forecast for + Northern Europe + (Europe, + id |metno_ne|). +16. Worldwide METAR weather reports + (global, id |metar|). +17. The USA NOAA/NCEP’s Short-Range Ensemble Forecast + (North America, id + |sref|). +18. The USA NOAA/NCEP’s Real-Time Mesoscale Analysis + model (North America, id |rtma|). +19. The USA NOAA/ESRL’s Meteorological Assimilation Data Ingest System + (global, id |madis|). + + + v1-to-v2 Migration Guide + +The v2 Forecast API is designed to be as close to a pure superset of the +v1 Forecast API as +possible, allowing for a straightforward upgrade path if you’re +currently using the v1 API and wish to upgrade. Below is a mapping of +all v1 response properties and how they map to v2 properties. + +v1 Property v2 Property Notes +|timezone| |timezone| and |offset| In v1, |timezone| was a string +containing the IANA timezone name, the timezone abbrevation, and the +timezone offset in hours. In v2, |timezone| contains the IANA timezone +name, while |offset| contains the timezone offset in hours. The timezone +abbreviation has no analogue in v2. +|currentSummary| |currently.summary| +|currentIntensity| |currently.precipIntensity| In v1, +|currentIntensity| was in dBZ. In v2, |precipIntensity| is in inches of +liquid water per hour. An equation for converting between the two is +available from the Wikipedia page for dBZ +. +|currentTemp| |currently.temperature| +|hourSummary| |minutely.summary| +|hourPrecipitation| |minutely.data| In v2, |minutely| may be omitted +entirely given a lack of data. +|hourPrecipitation[x].time| |minutely.data[x].time| +|hourPrecipitation[x].probability| |minutely.data[x].precipProbability| +|hourPrecipitation[x].intensity| |minutely.data[x].precipIntensity| In +v1, |intensity| was in dBZ. In v2, |precipIntensity| is in inches of +liquid water per hour. An equation for converting between the two is +available from the Wikipedia page for dBZ +. +|hourPrecipitation[x].error| |minutely.data[x].precipIntensityError| +Note the change in |precipIntensity| units outlined above. Additionally, +in v2, this value is a one-sigma value rather than a three-sigma value, +and if |precipIntensity| is zero, then this property will be omitted +entirely. +|hourPrecipitation[x].type| |minutely.data[x].precipType| In v2, there +is an additional type of precipitation: |hail|. Additionally, in v2, if +|precipIntensity| is zero, then this property will be omitted. +|daySummary| |hourly.summary| +|dayPrecipitation| |hourly.data| In v2, |hourly| may be omitted +entirely given a lack of data. +|dayPrecipitation[x].time| |hourly.data[x].time| +|dayPrecipitation[x].probability| |hourly.data[x].precipProbability| +In v2, |precipProbability| will be omitted if |precipIntensity| is zero. +It is also very uncommonly provided by our data sources; developers are +encouraged to use rely on |precipIntensity| whenever possible. +|dayPrecipitation[x].type| |hourly.data[x].precipType| In v2, there is +an additional type of precipitation: |hail|. Additionally, in v2, if +|precipIntensity| is zero, then this property will be omitted. +|dayPrecipitation[x].temp| |hourly.data[x].temperature| +|dayPrecipitation[x].cloudCover| |hourly.data[x].cloudCover| +|dayPrecipitation[x].relHumidity| |hourly.data[x].humidity| +|radarStation| |flags["darksky-stations"]| In v2, this property will +be omitted if we do not have radar coverage of the given location. +Additionally, v2 makes use of multiple radar sources, so this property +contains an array of station IDs, rather than a single ID. Finally, in +v1, radar stations were lower case and would have their first letter +omitted if they began with a "K". In v2, radar station IDs are always +four letters long and upper case. +|isPrecipitating| /not present/ In v2, simply check if +|currently.precipIntensity| is nonzero. +|minutesUntilChange| /not present/ In v2, iterate over |minutely.data| +and |hourly.data| in order, finding the first data point for which +|currently.precipIntensity !== 0| does not equal +|datapoint.precipIntensity !== 0|. Once that data point is found, +|minutesUntilChange| may be calculated as follows: |(datapoint.time - +currently.time) / 60|. +|checkTimeout| /not present/ Use either the |Cache-Control| or +|Expires| HTTP response headers. + +© 2013 The Dark Sky Company, LLC +Email Us Terms of Use +Privacy Policy + diff --git a/config/compile b/config/compile new file mode 100755 index 0000000..531136b --- /dev/null +++ b/config/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/data/#weather-settings_2.ui# b/data/#weather-settings_2.ui# new file mode 100644 index 0000000..0c6f164 --- /dev/null +++ b/data/#weather-settings_2.ui# @@ -0,0 +1,1185 @@ + + + + + + + + + + + + + + + + + False + 14 + + + True + False + True + + + True + True + center + True + True + in + 200 + + + True + True + liststore + False + 0 + False + 12 + + + + + + + + 0 + 0 + 1 + 1 + + + + + True + False + + + False + True + False + list-add-symbolic + + + False + True + + + + + False + True + False + list-remove-symbolic + + + False + True + + + + + + 0 + 1 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + + + True + False + 36 + 14 + 2 + 36 + 12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + True + 0 + + + + + False + True + 1 + + + + diff --git a/data/metadata.json.in b/data/metadata.json.in index b038896..4159225 100644 --- a/data/metadata.json.in +++ b/data/metadata.json.in @@ -2,7 +2,7 @@ "uuid": "@uuid@", "name": "OpenWeather", "description": "Weather extension to display weather information from https://openweathermap.org/ or https://forecast.io for almost all locations in the world.\nTo use the weather-data of forecast.io, you have to register on their site (https://developer.forecast.io/register) and get a personal API-key.\n\nSince version 29 this extensions uses coordinates to store the locations and makes the names editable to support multiple weather-providers!\nIf you update from versions prior to 29 to 29 or greater (with forecast.io - support) you have to recreate your locations.", -"shell-version": [ "3.12", "3.14", "3.16", "3.17.4", "3.17.90", "3.17.91" ], +"shell-version": [ "3.12", "3.14", "3.16", "3.17.4", "3.17.90", "3.17.91", "3.17.92" ], "localedir": "@LOCALEDIR@", "url": "@url@", "version": "@version@" diff --git a/data/weather-settings_2.ui b/data/weather-settings_2.ui new file mode 100644 index 0000000..7d157c8 --- /dev/null +++ b/data/weather-settings_2.ui @@ -0,0 +1,1100 @@ + + + + + + + + + + + + + + + + + False + 14 + + + True + False + True + + + True + True + center + True + True + in + + + True + True + liststore + False + 0 + False + 12 + + + + + + + + 0 + 0 + 1 + 1 + + + + + True + False + + + False + True + False + list-add-symbolic + + + False + True + + + + + False + True + False + list-remove-symbolic + + + False + True + + + + + + 0 + 1 + 1 + 1 + + + + + False + True + 0 + + + + + True + False + + + True + False + 36 + 14 + 2 + 36 + 12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + True + 0 + + + + + False + True + 1 + + + + diff --git a/gnome-shell-extension-openweather.bmarks b/gnome-shell-extension-openweather.bmarks new file mode 100644 index 0000000..2608b85 --- /dev/null +++ b/gnome-shell-extension-openweather.bmarks @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gnome-shell-extension-openweather.cscope_file_list b/gnome-shell-extension-openweather.cscope_file_list new file mode 100644 index 0000000..c90f732 --- /dev/null +++ b/gnome-shell-extension-openweather.cscope_file_list @@ -0,0 +1,53 @@ +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/cs.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/zh_CN.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/ca.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/src/prefs.js" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/ChangeLog" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/sk.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/el.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/zh_TW.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/vi.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/aclocal.m4" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/COPYING" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/uk.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/data/Screenshot.jpg" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/configure.ac" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/AUTHORS" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/lt.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/it.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/src/convenience.js" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/data/openweather-search.ui" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/nl.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/data/org.gnome.shell.extensions.openweather.gschema.xml.in" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/fr.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/de.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/pl.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/NEWS" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/data/Makefile.am" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/Makefile.am" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/update.js" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/src/Makefile.am" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/da.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/pt.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/POTFILES.in" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/gnome-shell-extension-openweather.spec" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/sv.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/data/weather-settings.gif" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/LINGUAS" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/nb.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/gnome-shell-extension-weather.pot" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/bg.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/ru.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/README.md" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/ja.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/autogen.sh" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/fi.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/pt_BR.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/data/weather-settings.ui" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/data/metadata.json.in" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/he.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/data/stylesheet.css" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/hu.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/es.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/po/ro.po" +"/home/jens/sources/w/weather/gnome-shell-extension-openweather/src/extension.js" diff --git a/gnome-shell-extension-openweather.layout b/gnome-shell-extension-openweather.layout new file mode 100644 index 0000000..45c39dc --- /dev/null +++ b/gnome-shell-extension-openweather.layout @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/search.txt b/search.txt new file mode 100644 index 0000000..560014c --- /dev/null +++ b/search.txt @@ -0,0 +1,104 @@ +https://open.mapquestapi.com/nominatim/v1/search.php?format=json&q=Sommerweg+2,+Neu-Eichenberg&addressdetails=1 + +[ + { + "place_id":"57218283", + "licence":"Data \u00a9 OpenStreetMap contributors, ODbL 1.0. http:\/\/www.openstreetmap.org\/copyright", + "osm_type":"way", + "osm_id":"26124984", + "boundingbox": + [ + "51.3747137", + "51.3755891", + "9.9164565", + "9.9192863" + ], + "lat":"51.3750768", + "lon":"9.9175961", + "display_name":"Sommerweg, Neu-Eichenberg, Werra-Mei\u00dfner-Kreis, Regierungsbezirk Kassel, Hessen, 37249, Deutschland, Europa", + "class":"highway", + "type":"residential", + "importance":0.51, + "address": + { + "road":"Sommerweg", + "village":"Neu-Eichenberg", + "county":"Werra-Mei\u00dfner-Kreis", + "state_district":"Regierungsbezirk Kassel", + "state":"Hessen", + "postcode":"37249", + "country":"Deutschland", + "country_code":"de", + "continent":"Europa" + } + } +] + + +[ + { + "place_id":"151244439", + "licence":"Data \u00a9 OpenStreetMap contributors, ODbL 1.0. http:\/\/www.openstreetmap.org\/copyright", + "osm_type":"relation", + "osm_id":"549411", + "boundingbox": + [ + "51.3462395", + "51.4210337", + "9.8500839", + "9.9327238" + ], + "lat":"51.3835891", + "lon":"9.8833684", + "display_name":"Neu-Eichenberg, Werra-Mei\u00dfner-Kreis, Regierungsbezirk Kassel, Hessen, Deutschland, Europa", + "class":"boundary", + "type":"administrative", + "importance":0.57579531509901, + "icon":"http:\/\/open.mapquestapi.com\/nominatim\/v1\/images\/mapicons\/poi_boundary_administrative.p.20.png", + "address": + { + "city":"Neu-Eichenberg", + "county":"Werra-Mei\u00dfner-Kreis", + "state_district":"Regierungsbezirk Kassel", + "state":"Hessen", + "country":"Deutschland", + "country_code":"de", + "continent":"Europa" + } + } +] + +http://open.mapquestapi.com/nominatim/v1/reverse.php?format=json&lat=51.3750768&lon=+9.9175961 + +[ + { + "place_id":"151244439", + "licence":"Data \u00a9 OpenStreetMap contributors, ODbL 1.0. http:\/\/www.openstreetmap.org\/copyright", + "osm_type":"relation", + "osm_id":"549411", + "boundingbox": + [ + "51.3462395", + "51.4210337", + "9.8500839", + "9.9327238" + ], + "lat":"51.3835891", + "lon":"9.8833684", + "display_name":"Neu-Eichenberg, Werra-Mei\u00dfner-Kreis, Regierungsbezirk Kassel, Hessen, Deutschland, Europa", + "class":"boundary", + "type":"administrative", + "importance":0.57579531509901, + "icon":"http:\/\/open.mapquestapi.com\/nominatim\/v1\/images\/mapicons\/poi_boundary_administrative.p.20.png", + "address": + { + "city":"Neu-Eichenberg", + "county":"Werra-Mei\u00dfner-Kreis", + "state_district":"Regierungsbezirk Kassel", + "state":"Hessen", + "country":"Deutschland", + "country_code":"de", + "continent":"Europa" + } + } +] diff --git a/src/extension_test.js b/src/extension_test.js new file mode 100644 index 0000000..f822f00 --- /dev/null +++ b/src/extension_test.js @@ -0,0 +1,1426 @@ +/* jshint esnext:true */ +/* + * + * Weather extension for GNOME Shell + * - Displays a small weather information on the top panel. + * - On click, gives a popup with details about the weather. + * + * Copyright (C) 2011 - 2015 + * ecyrbe , + * Timur Kristof , + * Elad Alfassa , + * Simon Legner , + * Christian METZLER , + * Mark Benjamin weather.gnome.Markie1@dfgh.net, + * Mattia Meneguzzo odysseus@fedoraproject.org, + * Meng Zhuo , + * Jens Lody + * + * + * This file is part of gnome-shell-extension-openweather. + * + * gnome-shell-extension-openweather is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * gnome-shell-extension-openweather is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gnome-shell-extension-openweather. If not, see . + * + */ + +const ExtensionUtils = imports.misc.extensionUtils; +const Me = ExtensionUtils.getCurrentExtension(); +const Config = imports.misc.config; +const Convenience = Me.imports.convenience; +const ForecastIo = Me.imports.forecast_io; +const OpenweathermapOrg = Me.imports.openweathermap_org; +const Clutter = imports.gi.Clutter; +const Gettext = imports.gettext.domain('gnome-shell-extension-openweather'); +const Gio = imports.gi.Gio; +const Gtk = imports.gi.Gtk; +const GLib = imports.gi.GLib; +const Lang = imports.lang; +const Mainloop = imports.mainloop; +const Soup = imports.gi.Soup; +const St = imports.gi.St; +const Util = imports.misc.util; +const _ = Gettext.gettext; + +const Main = imports.ui.main; +const PanelMenu = imports.ui.panelMenu; +const PopupMenu = imports.ui.popupMenu; + +// Settings +const OPENWEATHER_SETTINGS_SCHEMA = 'org.gnome.shell.extensions.openweather'; +const OPENWEATHER_DESKTOP_INTERFACE = 'org.gnome.desktop.interface'; +const OPENWEATHER_PROVIDER_KEY = 'weather-provider'; +const OPENWEATHER_UNIT_KEY = 'unit'; +const OPENWEATHER_WIND_SPEED_UNIT_KEY = 'wind-speed-unit'; +const OPENWEATHER_WIND_DIRECTION_KEY = 'wind-direction'; +const OPENWEATHER_PRESSURE_UNIT_KEY = 'pressure-unit'; +const OPENWEATHER_CITY_KEY = 'city'; +const OPENWEATHER_ACTUAL_CITY_KEY = 'actual-city'; +const OPENWEATHER_TRANSLATE_CONDITION_KEY = 'translate-condition'; +const OPENWEATHER_USE_SYMBOLIC_ICONS_KEY = 'use-symbolic-icons'; +const OPENWEATHER_USE_TEXT_ON_BUTTONS_KEY = 'use-text-on-buttons'; +const OPENWEATHER_SHOW_TEXT_IN_PANEL_KEY = 'show-text-in-panel'; +const OPENWEATHER_POSITION_IN_PANEL_KEY = 'position-in-panel'; +const OPENWEATHER_SHOW_COMMENT_IN_PANEL_KEY = 'show-comment-in-panel'; +const OPENWEATHER_SHOW_COMMENT_IN_FORECAST_KEY = 'show-comment-in-forecast'; +const OPENWEATHER_REFRESH_INTERVAL_CURRENT = 'refresh-interval-current'; +const OPENWEATHER_REFRESH_INTERVAL_FORECAST = 'refresh-interval-forecast'; +const OPENWEATHER_CENTER_FORECAST_KEY = 'center-forecast'; +const OPENWEATHER_DAYS_FORECAST = 'days-forecast'; +const OPENWEATHER_DECIMAL_PLACES = 'decimal-places'; +const OPENWEATHER_OWM_API_KEY = 'appid'; +const OPENWEATHER_FC_API_KEY = 'appid-fc'; + +// Keep enums in sync with GSettings schemas +const WeatherProvider = { + DEFAULT: -1, + OPENWEATHERMAP: 0, + FORECAST_IO: 1 +}; + +const WeatherUnits = { + CELSIUS: 0, + FAHRENHEIT: 1, + KELVIN: 2, + RANKINE: 3, + REAUMUR: 4, + ROEMER: 5, + DELISLE: 6, + NEWTON: 7 +}; + +const WeatherWindSpeedUnits = { + KPH: 0, + MPH: 1, + MPS: 2, + KNOTS: 3, + FPS: 4, + BEAUFORT: 5 +}; + +const WeatherPressureUnits = { + HPA: 0, + INHG: 1, + BAR: 2, + PA: 3, + KPA: 4, + ATM: 5, + AT: 6, + TORR: 7, + PSI: 8, + MMHG: 9 +}; + +const WeatherPosition = { + CENTER: 0, + RIGHT: 1, + LEFT: 2 +}; + +const OPENWEATHER_CONV_MPS_IN_MPH = 2.23693629; +const OPENWEATHER_CONV_MPS_IN_KPH = 3.6; +const OPENWEATHER_CONV_MPS_IN_KNOTS = 1.94384449; +const OPENWEATHER_CONV_MPS_IN_FPS = 3.2808399; + +let _httpSession; + +const OpenweatherMenuButton = new Lang.Class({ + Name: 'OpenweatherMenuButton', + + Extends: PanelMenu.Button, + + _init: function() { + log(new Error().fileName+':'+new Error().lineNumber+'11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'); + log(new Error().fileName+':'+new Error().lineNumber+' => Gio.NetworkConnectivity = '+Gio.NetworkConnectivity.toString()); + log(new Error().fileName+':'+new Error().lineNumber+' => Gio.NetworkConnectivity.full = '+Gio.NetworkConnectivity.full); + for (let i = 0; i < Gio.NetworkConnectivity.length; ++i) + { + log(new Error().fileName+':'+new Error().lineNumber+' => Gio.network_monitor_get_default()['+i+'] = '+Gio.network_monitor_get_default()[i]); + } +// log(new Error().fileName+':'+new Error().lineNumber+' => Gio.network_monitor_get_default().NETWORK_CONNECTIVITY_FULL = '+Gio.network_monitor_get_default().NetworkConnectivity.G_NETWORK_CONNECTIVITY_FULL); + log(new Error().fileName+':'+new Error().lineNumber+'22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222'); + this.owmCityId = 0; + + this.oldProvider = this._weather_provider; + this.oldTranslateCondition = this._translate_condition; + this.switchProvider(); + + this.currentWeatherCache = undefined; + this.forecastWeatherCache = undefined; + // Load settings + this.loadConfig(); + + // Label + this._weatherInfo = new St.Label({ + y_align: Clutter.ActorAlign.CENTER, + text: _('...') + }); + + this._weatherIcon = new St.Icon({ + icon_name: 'view-refresh' + this.getIconType(), + style_class: 'system-status-icon openweather-icon' + }); + + // Panel menu item - the current class + let menuAlignment = 0.25; + if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) + menuAlignment = 1.0 - menuAlignment; + this.parent(menuAlignment); + + // Putting the panel item together + let topBox = new St.BoxLayout(); + topBox.add_actor(this._weatherIcon); + topBox.add_actor(this._weatherInfo); + this.actor.add_actor(topBox); + + let dummyBox = new St.BoxLayout(); + this.actor.reparent(dummyBox); + dummyBox.remove_actor(this.actor); + dummyBox.destroy(); + + let children = null; + switch (this._position_in_panel) { + case WeatherPosition.LEFT: + children = Main.panel._leftBox.get_children(); + Main.panel._leftBox.insert_child_at_index(this.actor, children.length); + break; + case WeatherPosition.CENTER: + children = Main.panel._centerBox.get_children(); + Main.panel._centerBox.insert_child_at_index(this.actor, children.length); + break; + case WeatherPosition.RIGHT: + children = Main.panel._rightBox.get_children(); + Main.panel._rightBox.insert_child_at_index(this.actor, 0); + break; + } + if (Main.panel._menus === undefined) + Main.panel.menuManager.addMenu(this.menu); + else + Main.panel._menus.addMenu(this.menu); + + this._old_position_in_panel = this._position_in_panel; + + // Current weather + this._currentWeather = new St.Bin(); + // Future weather + this._futureWeather = new St.Bin(); + + // Putting the popup item together + let _itemCurrent = new PopupMenu.PopupBaseMenuItem({ + reactive: false + }); + let _itemFuture = new PopupMenu.PopupBaseMenuItem({ + reactive: false + }); + + _itemCurrent.actor.add_actor(this._currentWeather); + _itemFuture.actor.add_actor(this._futureWeather); + + this.menu.addMenuItem(_itemCurrent); + + this._separatorItem = new PopupMenu.PopupSeparatorMenuItem(); + this.menu.addMenuItem(this._separatorItem); + + this.menu.addMenuItem(_itemFuture); + + let item = new PopupMenu.PopupSeparatorMenuItem(); + this.menu.addMenuItem(item); + + this._selectCity = new PopupMenu.PopupSubMenuMenuItem(""); + this._selectCity.actor.set_height(0); + this._selectCity._triangle.set_height(0); + + this._buttonMenu = new PopupMenu.PopupBaseMenuItem({ + reactive: false, + style_class: 'openweather-menu-button-container' + }); + + this.rebuildButtonMenu(); + + this.menu.addMenuItem(this._buttonMenu); + this.menu.addMenuItem(this._selectCity); + this.rebuildSelectCityItem(); + this._selectCity.menu.connect('open-state-changed', Lang.bind(this, function() { + this._selectCity.actor.remove_style_pseudo_class('open'); + })); + + this.rebuildCurrentWeatherUi(); + this.rebuildFutureWeatherUi(); + + this._network_monitor = Gio.network_monitor_get_default(); + + this._connected = false; + this._network_monitor_connection = this._network_monitor.connect('network-changed', Lang.bind(this, this._onNetworkStateChanged)); + this._checkConnectionState(); + + this.menu.connect('open-state-changed', Lang.bind(this, this.recalcLayout)); + }, + + stop: function() { + if (_httpSession !== undefined) + _httpSession.abort(); + + _httpSession = undefined; + + if (this._timeoutCurrent) + Mainloop.source_remove(this._timeoutCurrent); + + this._timeoutCurrent = undefined; + + if (this._timeoutForecast) + Mainloop.source_remove(this._timeoutForecast); + + this._timeoutForecast = undefined; + + if (this._network_monitor_connection) { + this._network_monitor.disconnect(this._network_monitor_connection); + this._network_monitor_connection = undefined; + } + + if (this._settingsC) { + this._settings.disconnect(this._settingsC); + this._settingsC = undefined; + } + + if (this._settingsInterfaceC) { + this._settingsInterface.disconnect(this._settingsInterfaceC); + this._settingsInterfaceC = undefined; + } + + if (this._globalThemeChangedId) { + let context = St.ThemeContext.get_for_stage(global.stage); + context.disconnect(this._globalThemeChangedId); + this._globalThemeChangedId = undefined; + } + }, + + switchProvider: function() { + if (this._weather_provider == WeatherProvider.FORECAST_IO) + this.useForecastIo(); + else + this.useOpenweathermapOrg(); + }, + + useOpenweathermapOrg: function() { + this.parseWeatherForecast = OpenweathermapOrg.parseWeatherForecast; + this.parseWeatherCurrent = OpenweathermapOrg.parseWeatherCurrent; + this.getWeatherIcon = OpenweathermapOrg.getWeatherIcon; + this.refreshWeatherCurrent = OpenweathermapOrg.refreshWeatherCurrent; + this.refreshWeatherForecast = OpenweathermapOrg.refreshWeatherForecast; + + this.weatherProvider = "https://openweathermap.org/"; + }, + + useForecastIo: function() { + this.parseWeatherCurrent = ForecastIo.parseWeatherCurrent; + this.parseWeatherForecast = ForecastIo.parseWeatherForecast; + this.getWeatherIcon = ForecastIo.getWeatherIcon; + this.refreshWeatherCurrent = ForecastIo.refreshWeatherCurrent; + this.refreshWeatherForecast = function() {}; + + this.weatherProvider = "https://forecast.io/"; + + this.fc_locale = 'en'; + + if (this._translate_condition) { + let fc_locales = ['bs', 'de', 'en', 'es', 'fr', 'it', 'nl', 'pl', 'pt', 'ru', 'tet', 'x-pig-latin']; + let locale = GLib.get_language_names()[0]; + + if (locale.indexOf('_') != -1) + locale = locale.split("_")[0]; + + if (fc_locales.indexOf(locale) != -1) + this.fc_locale = locale; + } + }, + + getWeatherProviderURL: function() { + let url = ""; + if (this._weather_provider == WeatherProvider.FORECAST_IO) { + url = "https://forecast.io/#/f/"; + url += this.extractCoord(this._city); + } else { + url = "https://openweathermap.org"; + url += "/city/" + this.owmCityId; + if (this._appid) + url += "?APPID=" + this._appid; + } + return url; + }, + + loadConfig: function() { + this._settings = Convenience.getSettings(OPENWEATHER_SETTINGS_SCHEMA); + + if (this._cities.length === 0) + this._cities = "-8.5211767,179.1976747>Vaiaku, Tuvalu>-1"; + + this._settingsC = this._settings.connect("changed", Lang.bind(this, function() { + if (this._cities.length === 0) + this._cities = "-8.5211767,179.1976747>Vaiaku, Tuvalu>-1"; + this.rebuildCurrentWeatherUi(); + this.rebuildFutureWeatherUi(); + if (this.providerChanged()) { + this.switchProvider(); + this.currentWeatherCache = undefined; + this.forecastWeatherCache = undefined; + } + if (this.locationChanged()) { + this.currentWeatherCache = undefined; + this.forecastWeatherCache = undefined; + } + this.rebuildButtonMenu(); + this.parseWeatherCurrent(); + })); + }, + + loadConfigInterface: function() { + this._settingsInterface = Convenience.getSettings(OPENWEATHER_DESKTOP_INTERFACE); + this._settingsInterfaceC = this._settingsInterface.connect("changed", Lang.bind(this, function() { + this.rebuildCurrentWeatherUi(); + this.rebuildFutureWeatherUi(); + if (this.providerChanged()) { + this.switchProvider(); + this.currentWeatherCache = undefined; + this.forecastWeatherCache = undefined; + } + if (this.locationChanged()) { + this.currentWeatherCache = undefined; + this.forecastWeatherCache = undefined; + } + this.parseWeatherCurrent(); + })); + }, + + _onNetworkStateChanged: function() { + this._checkConnectionState(); + }, + + _checkConnectionState: function() { + log(new Error().fileName+':'+new Error().lineNumber+' => this._network_monitor.connectivity = '+this._network_monitor.connectivity); + this._connected = this._network_monitor.network_available; + + log(new Error().fileName+':'+new Error().lineNumber+' => this._connected = '+this._connected); + + if (this._connected) { + if (!this._timeoutForecast) + this.forecastWeatherCache = undefined; + if (!this._timeoutCurrent) + this.currentWeatherCache = undefined; + this.parseWeatherCurrent(); + } + }, + + locationChanged: function() { + let location = this.extractCoord(this._city); + if (this.oldLocation != location) { + return true; + } + return false; + }, + + providerChanged: function() { + let provider = this._weather_provider; + if (this.oldProvider != provider) { + this.oldProvider = provider; + return true; + } + if (provider == WeatherProvider.FORECAST_IO) { + let translateCondition = this._translate_condition; + if (this.oldTranslateCondition != translateCondition) { + this.oldTranslateCondition = translateCondition; + return true; + } + } + return false; + }, + + get _clockFormat() { + if (!this._settingsInterface) + this.loadConfigInterface(); + return this._settingsInterface.get_string("clock-format"); + }, + + get _weather_provider() { + if (!this._settings) + this.loadConfig(); + + let provider = this.extractProvider(this._city); + + if (provider == WeatherProvider.DEFAULT) + provider = this._settings.get_enum(OPENWEATHER_PROVIDER_KEY); + + return provider; + }, + + get _units() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_enum(OPENWEATHER_UNIT_KEY); + }, + + get _wind_speed_units() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_enum(OPENWEATHER_WIND_SPEED_UNIT_KEY); + }, + + get _wind_direction() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(OPENWEATHER_WIND_DIRECTION_KEY); + }, + + get _pressure_units() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_enum(OPENWEATHER_PRESSURE_UNIT_KEY); + }, + + get _cities() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_string(OPENWEATHER_CITY_KEY); + }, + + get _actual_city() { + if (!this._settings) + this.loadConfig(); + var a = this._settings.get_int(OPENWEATHER_ACTUAL_CITY_KEY); + var b = a; + var cities = this._cities.split(" && "); + + if (typeof cities != "object") + cities = [cities]; + + var l = cities.length - 1; + + if (a < 0) + a = 0; + + if (l < 0) + l = 0; + + if (a > l) + a = l; + + return a; + }, + + set _actual_city(a) { + if (!this._settings) + this.loadConfig(); + var cities = this._cities.split(" && "); + + if (typeof cities != "object") + cities = [cities]; + + var l = cities.length - 1; + + if (a < 0) + a = 0; + + if (l < 0) + l = 0; + + if (a > l) + a = l; + + this._settings.set_int(OPENWEATHER_ACTUAL_CITY_KEY, a); + }, + + get _city() { + let cities = this._cities; + let cities = cities.split(" && "); + if (cities && typeof cities == "string") + cities = [cities]; + if (!cities[0]) + return ""; + cities = cities[this._actual_city]; + return cities; + }, + + get _translate_condition() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(OPENWEATHER_TRANSLATE_CONDITION_KEY); + }, + + get _getIconType() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(OPENWEATHER_USE_SYMBOLIC_ICONS_KEY) ? 1 : 0; + }, + + get _use_text_on_buttons() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(OPENWEATHER_USE_TEXT_ON_BUTTONS_KEY) ? 1 : 0; + }, + + get _text_in_panel() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(OPENWEATHER_SHOW_TEXT_IN_PANEL_KEY); + }, + + get _position_in_panel() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_enum(OPENWEATHER_POSITION_IN_PANEL_KEY); + }, + + get _comment_in_panel() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(OPENWEATHER_SHOW_COMMENT_IN_PANEL_KEY); + }, + + get _comment_in_forecast() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(OPENWEATHER_SHOW_COMMENT_IN_FORECAST_KEY); + }, + + get _refresh_interval_current() { + if (!this._settings) + this.loadConfig(); + let v = this._settings.get_int(OPENWEATHER_REFRESH_INTERVAL_CURRENT); +// return ((v >= 600) ? v : 600); + return 30; + }, + + get _refresh_interval_forecast() { + if (!this._settings) + this.loadConfig(); + let v = this._settings.get_int(OPENWEATHER_REFRESH_INTERVAL_FORECAST); +// return ((v >= 600) ? v : 600); + return 30; + }, + + get _center_forecast() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(OPENWEATHER_CENTER_FORECAST_KEY); + }, + + get _days_forecast() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_int(OPENWEATHER_DAYS_FORECAST); + }, + + get _decimal_places() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_int(OPENWEATHER_DECIMAL_PLACES); + }, + + get _appid() { + if (!this._settings) + this.loadConfig(); + let key = this._settings.get_string(OPENWEATHER_OWM_API_KEY); + return (key.length == 32) ? key : ''; + }, + + get _appid_fc() { + if (!this._settings) + this.loadConfig(); + let key = this._settings.get_string(OPENWEATHER_FC_API_KEY); + return (key.length == 32) ? key : ''; + }, + + rebuildButtonMenu: function() { + if (this._buttonBox) { + if (this._buttonBox1) { + this._buttonBox1.destroy(); + this._buttonBox1 = undefined; + + } + if (this._buttonBox2) { + this._buttonBox2.destroy(); + this._buttonBox2 = undefined; + } + this._buttonMenu.removeActor(this._buttonBox); + this._buttonBox.destroy(); + this._buttonBox = undefined; + } + + if (this._buttonBox1) { + this._buttonBox1.destroy(); + this._buttonBox1 = undefined; + } + if (this._buttonBox2) { + this._buttonBox2.destroy(); + this._buttonBox2 = undefined; + } + + this._locationButton = Main.panel.statusArea.aggregateMenu._system._createActionButton('find-location-symbolic', _("Locations")); + if (this._use_text_on_buttons) + this._locationButton.set_label(this._locationButton.get_accessible_name()); + + this._locationButton.connect('clicked', Lang.bind(this, function() { + this._selectCity._setOpenState(!this._selectCity._getOpenState()); + })); + this._buttonBox1 = new St.BoxLayout({ + style_class: 'openweather-button-box' + }); + this._buttonBox1.add_actor(this._locationButton); + + this._reloadButton = Main.panel.statusArea.aggregateMenu._system._createActionButton('view-refresh-symbolic', _("Reload Weather Information")); + if (this._use_text_on_buttons) + this._reloadButton.set_label(this._reloadButton.get_accessible_name()); + this._reloadButton.connect('clicked', Lang.bind(this, function() { + this.currentWeatherCache = undefined; + this.forecastWeatherCache = undefined; + this.parseWeatherCurrent(); + this.recalcLayout(); + })); + this._buttonBox1.add_actor(this._reloadButton); + + this._buttonBox2 = new St.BoxLayout({ + style_class: 'openweather-button-box' + }); + + this._urlButton = Main.panel.statusArea.aggregateMenu._system._createActionButton('', _("Weather data provided by:") + (this._use_text_on_buttons ? "\n" : " ") + this.weatherProvider); + this._urlButton.set_label(this._urlButton.get_accessible_name()); + this._urlButton.style_class += ' openweather-provider'; + + this._urlButton.connect('clicked', Lang.bind(this, function() { + this.menu.actor.hide(); + let url = this.getWeatherProviderURL(); + + try { + Gtk.show_uri(null, url, global.get_current_time()); + } catch (err) { + let title = _("Can not open %s").format(url); + Main.notifyError(title, err.message); + } + })); + + this._buttonBox2.add_actor(this._urlButton); + + this._prefsButton = Main.panel.statusArea.aggregateMenu._system._createActionButton('preferences-system-symbolic', _("Weather Settings")); + if (this._use_text_on_buttons) + this._prefsButton.set_label(this._prefsButton.get_accessible_name()); + this._prefsButton.connect('clicked', Lang.bind(this, this._onPreferencesActivate)); + this._buttonBox2.add_actor(this._prefsButton); + + this._buttonMenu.actor.add_actor(this._buttonBox1); + this._buttonMenu.actor.add_actor(this._buttonBox2); + + this._buttonBox1MinWidth = undefined; + }, + + rebuildSelectCityItem: function() { + this._selectCity.menu.removeAll(); + let item = null; + + let cities = this._cities; + cities = cities.split(" && "); + if (cities && typeof cities == "string") + cities = [cities]; + if (!cities[0]) + return; + + for (let i = 0; cities.length > i; i++) { + item = new PopupMenu.PopupMenuItem(this.extractLocation(cities[i])); + item.location = i; + if (i == this._actual_city) { + item.setOrnament(PopupMenu.Ornament.DOT); + } + + this._selectCity.menu.addMenuItem(item); + // override the items default onActivate-handler, to keep the ui open while chosing the location + item.activate = this._onActivate; + } + + if (cities.length == 1) + this._selectCity.actor.hide(); + else + this._selectCity.actor.show(); + + }, + + _onActivate: function() { + openweatherMenu._actual_city = this.location; + }, + + extractLocation: function() { + if (!arguments[0]) + return ""; + + if (arguments[0].search(">") == -1) + return _("Invalid city"); + return arguments[0].split(">")[1]; + }, + + extractCoord: function() { + let coords = 0; + + if (arguments[0] && (arguments[0].search(">") != -1)) + coords = arguments[0].split(">")[0]; + + if ((coords.search(",") == -1) || isNaN(coords.split(",")[0]) || isNaN(coords.split(",")[1])) { + Main.notify("Openweathermap", _("Invalid location! Please try to recreate it.")); + return 0; + } + + return coords; + }, + + extractProvider: function() { + if (!arguments[0]) + return -1; + if (arguments[0].split(">")[2] === undefined) + return -1; + if (isNaN(parseInt(arguments[0].split(">")[2]))) + return -1; + return parseInt(arguments[0].split(">")[2]); + }, + + _onPreferencesActivate: function() { + this.menu.actor.hide(); + Util.spawn(["gnome-shell-extension-prefs", "openweather-extension@jenslody.de"]); + return 0; + }, + + recalcLayout: function() { + if (!this.menu.isOpen) + return; + if (this._buttonBox1MinWidth === undefined) + this._buttonBox1MinWidth = this._buttonBox1.get_width(); + this._buttonBox1.set_width(Math.max(this._buttonBox1MinWidth, this._currentWeather.get_width() - this._buttonBox2.get_width())); + if (this._forecastScrollBox !== undefined && this._forecastBox !== undefined && this._currentWeather !== undefined) { + this._forecastScrollBox.set_width(Math.max(this._currentWeather.get_width(), (this._buttonBox1.get_width() + this._buttonBox2.get_width()))); + this._forecastScrollBox.show(); + if (this._forecastBox.get_preferred_width(this._forecastBox.get_height())[0] > this._currentWeather.get_width()) { + this._forecastScrollBox.hscroll.margin_top = 10; + this._forecastScrollBox.hscroll.show(); + } else { + this._forecastScrollBox.hscroll.margin_top = 0; + this._forecastScrollBox.hscroll.hide(); + } + } + }, + + unit_to_unicode: function() { + if (this._units == WeatherUnits.FAHRENHEIT) + return '\u00B0F'; + else if (this._units == WeatherUnits.KELVIN) + return 'K'; + else if (this._units == WeatherUnits.RANKINE) + return '\u00B0Ra'; + else if (this._units == WeatherUnits.REAUMUR) + return '\u00B0R\u00E9'; + else if (this._units == WeatherUnits.ROEMER) + return '\u00B0R\u00F8'; + else if (this._units == WeatherUnits.DELISLE) + return '\u00B0De'; + else if (this._units == WeatherUnits.NEWTON) + return '\u00B0N'; + else + return '\u00B0C'; + }, + + hasIcon: function(icon) { + return Gtk.IconTheme.get_default().has_icon(icon + this.getIconType()); + }, + + toFahrenheit: function(t) { + return ((Number(t) * 1.8) + 32).toFixed(this._decimal_places); + }, + + toKelvin: function(t) { + return (Number(t) + 273.15).toFixed(this._decimal_places); + }, + + toRankine: function(t) { + return ((Number(t) * 1.8) + 491.67).toFixed(this._decimal_places); + }, + + toReaumur: function(t) { + return (Number(t) * 0.8).toFixed(this._decimal_places); + }, + + toRoemer: function(t) { + return ((Number(t) * 21 / 40) + 7.5).toFixed(this._decimal_places); + }, + + toDelisle: function(t) { + return ((100 - Number(t)) * 1.5).toFixed(this._decimal_places); + }, + + toNewton: function(t) { + return (Number(t) - 0.33).toFixed(this._decimal_places); + }, + + toInHg: function(p /*, t*/ ) { + return (p / 33.86530749).toFixed(this._decimal_places); + }, + + toBeaufort: function(w, t) { + if (w < 0.3) + return (!t) ? "0" : "(" + _("Calm") + ")"; + + else if (w >= 0.3 && w <= 1.5) + return (!t) ? "1" : "(" + _("Light air") + ")"; + + else if (w > 1.5 && w <= 3.4) + return (!t) ? "2" : "(" + _("Light breeze") + ")"; + + else if (w > 3.4 && w <= 5.4) + return (!t) ? "3" : "(" + _("Gentle breeze") + ")"; + + else if (w > 5, 4 && w <= 7.9) + return (!t) ? "4" : "(" + _("Moderate breeze") + ")"; + + else if (w > 7.9 && w <= 10.7) + return (!t) ? "5" : "(" + _("Fresh breeze") + ")"; + + else if (w > 10.7 && w <= 13.8) + return (!t) ? "6" : "(" + _("Strong breeze") + ")"; + + else if (w > 13.8 && w <= 17.1) + return (!t) ? "7" : "(" + _("Moderate gale") + ")"; + + else if (w > 17.1 && w <= 20.7) + return (!t) ? "8" : "(" + _("Fresh gale") + ")"; + + else if (w > 20.7 && w <= 24.4) + return (!t) ? "9" : "(" + _("Strong gale") + ")"; + + else if (w > 24.4 && w <= 28.4) + return (!t) ? "10" : "(" + _("Storm") + ")"; + + else if (w > 28.4 && w <= 32.6) + return (!t) ? "11" : "(" + _("Violent storm") + ")"; + + else + return (!t) ? "12" : "(" + _("Hurricane") + ")"; + }, + + getLocaleDay: function(abr) { + let days = [_('Sunday'), _('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'), _('Friday'), _('Saturday')]; + return days[abr]; + }, + + getWindDirection: function(deg) { + let arrows = ["\u2193", "\u2199", "\u2190", "\u2196", "\u2191", "\u2197", "\u2192", "\u2198"]; + let letters = [_('N'), _('NE'), _('E'), _('SE'), _('S'), _('SW'), _('W'), _('NW')]; + let idx = Math.round(deg / 45) % arrows.length; + return (this._wind_direction) ? arrows[idx] : letters[idx]; + }, + + getIconType: function(icon_name) { + if (!icon_name) + if (this._getIconType) + return "-symbolic"; + else + return ""; + + if (this._getIconType) + if (String(icon_name).search("-symbolic") != -1) + return icon_name; + else + return icon_name + "-symbolic"; + else + if (String(icon_name).search("-symbolic") != -1) + return String(icon_name).replace("-symbolic", ""); + else + return icon_name; + }, + + load_json_async: function(url, params, fun) { + if (_httpSession === undefined) { + _httpSession = new Soup.Session(); + } + + let message = Soup.form_request_new_from_hash('GET', url, params); + + _httpSession.queue_message(message, Lang.bind(this, function(_httpSession, message) { + + try { + if (!message.response_body.data) { + fun.call(this, 0); + return; + } + let jp = JSON.parse(message.response_body.data); + fun.call(this, jp); + } catch (e) { + fun.call(this, 0); + return; + } + })); + return; + }, + + checkPositionInPanel: function() { + if (this._old_position_in_panel != this._position_in_panel) { + switch (this._old_position_in_panel) { + case WeatherPosition.LEFT: + Main.panel._leftBox.remove_actor(this.actor); + break; + case WeatherPosition.CENTER: + Main.panel._centerBox.remove_actor(this.actor); + break; + case WeatherPosition.RIGHT: + Main.panel._rightBox.remove_actor(this.actor); + break; + } + + let children = null; + switch (this._position_in_panel) { + case WeatherPosition.LEFT: + children = Main.panel._leftBox.get_children(); + Main.panel._leftBox.insert_child_at_index(this.actor, children.length); + break; + case WeatherPosition.CENTER: + children = Main.panel._centerBox.get_children(); + Main.panel._centerBox.insert_child_at_index(this.actor, children.length); + break; + case WeatherPosition.RIGHT: + children = Main.panel._rightBox.get_children(); + Main.panel._rightBox.insert_child_at_index(this.actor, 0); + break; + } + this._old_position_in_panel = this._position_in_panel; + } + + }, + + formatPressure: function(pressure) { + let pressure_unit = 'hPa'; + switch (this._pressure_units) { + case WeatherPressureUnits.INHG: + pressure = this.toInHg(pressure); + pressure_unit = "inHg"; + break; + + case WeatherPressureUnits.HPA: + pressure = pressure.toFixed(this._decimal_places); + pressure_unit = "hPa"; + break; + + case WeatherPressureUnits.BAR: + pressure = (pressure / 1000).toFixed(this._decimal_places); + pressure_unit = "bar"; + break; + + case WeatherPressureUnits.PA: + pressure = (pressure * 100).toFixed(this._decimal_places); + pressure_unit = "Pa"; + break; + + case WeatherPressureUnits.KPA: + pressure = (pressure / 10).toFixed(this._decimal_places); + pressure_unit = "kPa"; + break; + + case WeatherPressureUnits.ATM: + pressure = (pressure * 0.000986923267).toFixed(this._decimal_places); + pressure_unit = "atm"; + break; + + case WeatherPressureUnits.AT: + pressure = (pressure * 0.00101971621298).toFixed(this._decimal_places); + pressure_unit = "at"; + break; + + case WeatherPressureUnits.TORR: + pressure = (pressure * 0.750061683).toFixed(this._decimal_places); + pressure_unit = "Torr"; + break; + + case WeatherPressureUnits.PSI: + pressure = (pressure * 0.0145037738).toFixed(this._decimal_places); + pressure_unit = "psi"; + break; + + case WeatherPressureUnits.MMHG: + pressure = (pressure * 0.750061683).toFixed(this._decimal_places); + pressure_unit = "mmHg"; + break; + } + return parseFloat(pressure).toLocaleString() + ' ' + pressure_unit; + }, + + formatTemperature: function(temperature) { + switch (this._units) { + case WeatherUnits.FAHRENHEIT: + temperature = this.toFahrenheit(temperature); + break; + + case WeatherUnits.CELSIUS: + temperature = temperature.toFixed(this._decimal_places); + break; + + case WeatherUnits.KELVIN: + temperature = this.toKelvin(temperature); + break; + + case WeatherUnits.RANKINE: + temperature = this.toRankine(temperature); + break; + + case WeatherUnits.REAUMUR: + temperature = this.toReaumur(temperature); + break; + + case WeatherUnits.ROEMER: + temperature = this.toRoemer(temperature); + break; + + case WeatherUnits.DELISLE: + temperature = this.toDelisle(temperature); + break; + + case WeatherUnits.NEWTON: + temperature = this.toNewton(temperature); + break; + } + return parseFloat(temperature).toLocaleString() + ' ' + this.unit_to_unicode(); + }, + + formatWind: function(speed, direction) { + let unit = 'm/s'; + switch (this._wind_speed_units) { + case WeatherWindSpeedUnits.MPH: + speed = (speed * OPENWEATHER_CONV_MPS_IN_MPH).toFixed(this._decimal_places); + unit = 'mph'; + break; + + case WeatherWindSpeedUnits.KPH: + speed = (speed * OPENWEATHER_CONV_MPS_IN_KPH).toFixed(this._decimal_places); + unit = 'km/h'; + break; + + case WeatherWindSpeedUnits.MPS: + speed = speed.toFixed(this._decimal_places); + break; + + case WeatherWindSpeedUnits.KNOTS: + speed = (speed * OPENWEATHER_CONV_MPS_IN_KNOTS).toFixed(this._decimal_places); + unit = 'kn'; + break; + + case WeatherWindSpeedUnits.FPS: + speed = (speed * OPENWEATHER_CONV_MPS_IN_FPS).toFixed(this._decimal_places); + unit = 'ft/s'; + break; + + case WeatherWindSpeedUnits.BEAUFORT: + speed = this.toBeaufort(speed); + unit = this.toBeaufort(speed, true); + break; + + } + + if (!speed) + return '\u2013'; + else if (speed === 0 || !direction) + return parseFloat(speed).toLocaleString() + ' ' + unit; + else // i.e. speed > 0 && direction + return direction + ' ' + parseFloat(speed).toLocaleString() + ' ' + unit; + }, + + reloadWeatherCurrent: function(interval) { + log(new Error().fileName+':'+new Error().lineNumber+' => this._timeoutCurrent = '+this._timeoutCurrent); + if (this._timeoutCurrent) { + Mainloop.source_remove(this._timeoutCurrent); + this._timeoutCurrent = undefined; + log(new Error().fileName+':'+new Error().lineNumber+' => this._timeoutCurrent = '+this._timeoutCurrent); + } + log(new Error().fileName+':'+new Error().lineNumber+' => this._timeoutCurrent = '+this._timeoutCurrent); + if (this._connected) { + this._timeoutCurrent = Mainloop.timeout_add_seconds(interval, Lang.bind(this, function() { + this.currentWeatherCache = undefined; + this.parseWeatherCurrent(); + return true; + })); + } + }, + + reloadWeatherForecast: function(interval) { + log(new Error().fileName+':'+new Error().lineNumber+' => this._timeoutForecast = '+this._timeoutForecast); + if (this._timeoutForecast) { + Mainloop.source_remove(this._timeoutForecast); + this._timeoutForecast = undefined; + log(new Error().fileName+':'+new Error().lineNumber+' => this._timeoutForecast = '+this._timeoutForecast); + } + log(new Error().fileName+':'+new Error().lineNumber+' => this._timeoutForecast = '+this._timeoutForecast); + if (this._connected) { + this._timeoutForecast = Mainloop.timeout_add_seconds(interval, Lang.bind(this, function() { + this.forecastWeatherCache = undefined; + this.parseWeatherForecast(); + return true; + })); + } + }, + + destroyCurrentWeather: function() { + if (this._currentWeather.get_child() !== null) + this._currentWeather.get_child().destroy(); + }, + + destroyFutureWeather: function() { + if (this._futureWeather.get_child() !== null) + this._futureWeather.get_child().destroy(); + }, + + rebuildCurrentWeatherUi: function() { + this._weatherInfo.text = _(' '); + this._weatherIcon.icon_name = 'view-refresh' + this.getIconType(); + + this.destroyCurrentWeather(); + + // This will hold the icon for the current weather + this._currentWeatherIcon = new St.Icon({ + icon_size: 72, + icon_name: 'view-refresh' + this.getIconType(), + style_class: 'system-menu-action openweather-current-icon' + }); + + this._sunriseIcon = new St.Icon({ + icon_size: 15, + icon_name: 'weather-clear' + this.getIconType(), + style_class: 'openweather-sunrise-icon' + }); + + this._sunsetIcon = new St.Icon({ + icon_size: 15, + icon_name: 'weather-clear-night' + this.getIconType(), + style_class: 'openweather-sunset-icon' + }); + + this._buildIcon = new St.Icon({ + icon_size: 15, + icon_name: 'view-refresh' + this.getIconType(), + style_class: 'openweather-build-icon' + }); + + // The summary of the current weather + this._currentWeatherSummary = new St.Label({ + text: _('Loading ...'), + style_class: 'openweather-current-summary' + }); + this._currentWeatherLocation = new St.Label({ + text: _('Please wait') + }); + + let bb = new St.BoxLayout({ + vertical: true, + style_class: 'system-menu-action openweather-current-summarybox' + }); + bb.add_actor(this._currentWeatherLocation); + bb.add_actor(this._currentWeatherSummary); + + this._currentWeatherSunrise = new St.Label({ + text: '-' + }); + this._currentWeatherSunset = new St.Label({ + text: '-' + }); + this._currentWeatherBuild = new St.Label({ + text: '-' + }); + + let ab = new St.BoxLayout({ + style_class: 'openweather-current-infobox' + }); + + ab.add_actor(this._sunriseIcon); + ab.add_actor(this._currentWeatherSunrise); + ab.add_actor(this._sunsetIcon); + ab.add_actor(this._currentWeatherSunset); + ab.add_actor(this._buildIcon); + ab.add_actor(this._currentWeatherBuild); + bb.add_actor(ab); + + // Other labels + this._currentWeatherCloudiness = new St.Label({ + text: '...' + }); + this._currentWeatherHumidity = new St.Label({ + text: '...' + }); + this._currentWeatherPressure = new St.Label({ + text: '...' + }); + this._currentWeatherWind = new St.Label({ + text: '...' + }); + + let rb = new St.BoxLayout({ + style_class: 'openweather-current-databox' + }); + let rb_captions = new St.BoxLayout({ + vertical: true, + style_class: 'popup-status-menu-item openweather-current-databox-captions' + }); + let rb_values = new St.BoxLayout({ + vertical: true, + style_class: 'system-menu-action openweather-current-databox-values' + }); + rb.add_actor(rb_captions); + rb.add_actor(rb_values); + + rb_captions.add_actor(new St.Label({ + text: _('Cloudiness:') + })); + rb_values.add_actor(this._currentWeatherCloudiness); + rb_captions.add_actor(new St.Label({ + text: _('Humidity:') + })); + rb_values.add_actor(this._currentWeatherHumidity); + rb_captions.add_actor(new St.Label({ + text: _('Pressure:') + })); + rb_values.add_actor(this._currentWeatherPressure); + rb_captions.add_actor(new St.Label({ + text: _('Wind:') + })); + rb_values.add_actor(this._currentWeatherWind); + + let xb = new St.BoxLayout(); + xb.add_actor(bb); + xb.add_actor(rb); + + let box = new St.BoxLayout({ + style_class: 'openweather-current-iconbox' + }); + box.add_actor(this._currentWeatherIcon); + box.add_actor(xb); + this._currentWeather.set_child(box); + }, + + scrollForecastBy: function(delta) { + if (this._forecastScrollBox === undefined) + return; + this._forecastScrollBox.hscroll.adjustment.value += delta; + }, + + rebuildFutureWeatherUi: function(cnt) { + this.destroyFutureWeather(); + + this._forecast = []; + this._forecastBox = new St.BoxLayout({ + x_align: this._center_forecast ? St.Align.END : St.Align.START, + style_class: 'openweather-forecast-box' + }); + + this._forecastScrollBox = new St.ScrollView({ + style_class: 'openweather-forecasts' + }); + + let pan = new Clutter.PanAction({ + interpolate: true + }); + pan.connect('pan', Lang.bind(this, function(action) { + + let[dist, dx, dy] = action.get_motion_delta(0); + + this.scrollForecastBy(-1 * (dx / this._forecastScrollBox.width) * this._forecastScrollBox.hscroll.adjustment.page_size); + return false; + })); + this._forecastScrollBox.add_action(pan); + + this._forecastScrollBox.connect('scroll-event', Lang.bind(this, this._onScroll)); + this._forecastScrollBox.hscroll.connect('scroll-event', Lang.bind(this, this._onScroll)); + + this._forecastScrollBox.hscroll.margin_right = 25; + this._forecastScrollBox.hscroll.margin_left = 25; + this._forecastScrollBox.hscroll.hide(); + this._forecastScrollBox.vscrollbar_policy = Gtk.PolicyType.NEVER; + this._forecastScrollBox.hscrollbar_policy = Gtk.PolicyType.AUTOMATIC; + this._forecastScrollBox.enable_mouse_scrolling = true; + this._forecastScrollBox.hide(); + + this._futureWeather.set_child(this._forecastScrollBox); + + if (cnt === undefined) + cnt = this._days_forecast; + for (let i = 0; i < cnt; i++) { + let forecastWeather = {}; + + forecastWeather.Icon = new St.Icon({ + icon_size: 48, + icon_name: 'view-refresh' + this.getIconType(), + style_class: 'system-menu-action openweather-forecast-icon' + }); + forecastWeather.Day = new St.Label({ + style_class: 'popup-status-menu-item openweather-forecast-day' + }); + forecastWeather.Summary = new St.Label({ + style_class: 'system-menu-action openweather-forecast-summary' + }); + forecastWeather.Summary.clutter_text.line_wrap = true; + forecastWeather.Temperature = new St.Label({ + style_class: 'system-menu-action openweather-forecast-temperature' + }); + + let by = new St.BoxLayout({ + vertical: true, + style_class: 'openweather-forecast-databox' + }); + by.add_actor(forecastWeather.Day); + if (this._comment_in_forecast) + by.add_actor(forecastWeather.Summary); + by.add_actor(forecastWeather.Temperature); + + let bb = new St.BoxLayout({ + style_class: 'openweather-forecast-iconbox' + }); + bb.add_actor(forecastWeather.Icon); + bb.add_actor(by); + + this._forecast[i] = forecastWeather; + this._forecastBox.add_actor(bb); + } + this._forecastScrollBox.add_actor(this._forecastBox); + }, + + _onScroll: function(actor, event) { + let dx = 0; + let dy = 0; + switch (event.get_scroll_direction()) { + case Clutter.ScrollDirection.UP: + dy = -1; + break; + case Clutter.ScrollDirection.DOWN: + dy = 1; + break; + // horizontal scrolling will be handled by the control itself + default: + return true; + } + + this.scrollForecastBy(dy * this._forecastScrollBox.hscroll.adjustment.stepIncrement); + return false; + } +}); + +let openweatherMenu; + +function init() { + Convenience.initTranslations('gnome-shell-extension-openweather'); +} + +function enable() { + openweatherMenu = new OpenweatherMenuButton(); + Main.panel.addToStatusArea('openweatherMenu', openweatherMenu); +} + +function disable() { + openweatherMenu.stop(); + openweatherMenu.destroy(); +} diff --git a/src/fetch_data_base.js b/src/fetch_data_base.js new file mode 100644 index 0000000..b8eeedd --- /dev/null +++ b/src/fetch_data_base.js @@ -0,0 +1,1979 @@ +/* jshint esnext:true */ +/* + * + * Weather extension for GNOME Shell + * - Displays a small weather information on the top panel. + * - On click, gives a popup with details about the weather. + * + * Copyright (C) 2011 - 2015 + * ecyrbe , + * Timur Kristof , + * Elad Alfassa , + * Simon Legner , + * Christian METZLER , + * Mark Benjamin weather.gnome.Markie1@dfgh.net, + * Mattia Meneguzzo odysseus@fedoraproject.org, + * Meng Zhuo , + * Jens Lody + * + * + * This file is part of gnome-shell-extension-openweather. + * + * gnome-shell-extension-openweather is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * gnome-shell-extension-openweather is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with gnome-shell-extension-openweather. If not, see . + * + */ + +const ExtensionUtils = imports.misc.extensionUtils; +const Me = ExtensionUtils.getCurrentExtension(); +const Config = imports.misc.config; +const Convenience = Me.imports.convenience; +const Clutter = imports.gi.Clutter; +const Gettext = imports.gettext.domain('gnome-shell-extension-openweather'); +const Gio = imports.gi.Gio; +const Gtk = imports.gi.Gtk; +const Lang = imports.lang; +const Mainloop = imports.mainloop; +const Soup = imports.gi.Soup; +const St = imports.gi.St; +const Util = imports.misc.util; +const _ = Gettext.gettext; + +const Main = imports.ui.main; +const PanelMenu = imports.ui.panelMenu; +const PopupMenu = imports.ui.popupMenu; + +// Settings +const WEATHER_SETTINGS_SCHEMA = 'org.gnome.shell.extensions.openweather'; +const WEATHER_UNIT_KEY = 'unit'; +const WEATHER_WIND_SPEED_UNIT_KEY = 'wind-speed-unit'; +const WEATHER_WIND_DIRECTION_KEY = 'wind-direction'; +const WEATHER_PRESSURE_UNIT_KEY = 'pressure-unit'; +const WEATHER_CITY_KEY = 'city'; +const WEATHER_ACTUAL_CITY_KEY = 'actual-city'; +const WEATHER_TRANSLATE_CONDITION_KEY = 'translate-condition'; +const WEATHER_USE_SYMBOLIC_ICONS_KEY = 'use-symbolic-icons'; +const WEATHER_USE_TEXT_ON_BUTTONS_KEY = 'use-text-on-buttons'; +const WEATHER_SHOW_TEXT_IN_PANEL_KEY = 'show-text-in-panel'; +const WEATHER_POSITION_IN_PANEL_KEY = 'position-in-panel'; +const WEATHER_SHOW_COMMENT_IN_PANEL_KEY = 'show-comment-in-panel'; +const WEATHER_REFRESH_INTERVAL_CURRENT = 'refresh-interval-current'; +const WEATHER_REFRESH_INTERVAL_FORECAST = 'refresh-interval-forecast'; +const WEATHER_CENTER_FORECAST_KEY = 'center-forecast'; +const WEATHER_DAYS_FORECAST = 'days-forecast'; +const WEATHER_DECIMAL_PLACES = 'decimal-places'; +const WEATHER_OWM_API_KEY = 'appid'; + +//URL +const WEATHER_URL_HOST = ''; +const WEATHER_URL_PORT = 80; +const WEATHER_URL_BASE = 'http://' + WEATHER_URL_HOST; +const WEATHER_URL_CURRENT = WEATHER_URL_BASE + ''; +const WEATHER_URL_FORECAST = WEATHER_URL_BASE + ''; + +// Keep enums in sync with GSettings schemas +const WeatherUnits = { + CELSIUS: 0, + FAHRENHEIT: 1, + KELVIN: 2, + RANKINE: 3, + REAUMUR: 4, + ROEMER: 5, + DELISLE: 6, + NEWTON: 7 +}; + +const WeatherWindSpeedUnits = { + KPH: 0, + MPH: 1, + MPS: 2, + KNOTS: 3, + FPS: 4, + BEAUFORT: 5 +}; + +const WeatherPressureUnits = { + hPa: 0, + inHg: 1, + bar: 2, + Pa: 3, + kPa: 4, + atm: 5, + at: 6, + Torr: 7, + psi: 8, + mmHg: 9 +}; + +const WeatherPosition = { + CENTER: 0, + RIGHT: 1, + LEFT: 2 +}; + +const WEATHER_CONV_MPS_IN_MPH = 2.23693629; +const WEATHER_CONV_MPS_IN_KPH = 3.6; +const WEATHER_CONV_MPS_IN_KNOTS = 1.94384449; +const WEATHER_CONV_MPS_IN_FPS = 3.2808399; + +let _httpSession; + +const OpenweatherProviderBase = new Lang.Class({ + Name: 'OpenweatherProviderBase', + + _init: function() { + this.currentWeatherCache = undefined; + this.forecastWeatherCache = undefined; + // Load settings + this.loadConfig(); + + // Label + this._weatherInfo = new St.Label({ + y_align: Clutter.ActorAlign.CENTER, + text: _('...') + }); + + this._weatherIcon = new St.Icon({ + icon_name: 'view-refresh' + this.icon_type(), + style_class: 'system-status-icon openweather-icon' + }); + + // Panel menu item - the current class + let menuAlignment = 0.25; + if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) + menuAlignment = 1.0 - menuAlignment; + this.parent(menuAlignment); + + // Putting the panel item together + let topBox = new St.BoxLayout(); + topBox.add_actor(this._weatherIcon); + topBox.add_actor(this._weatherInfo); + this.actor.add_actor(topBox); + + let dummyBox = new St.BoxLayout(); + this.actor.reparent(dummyBox); + dummyBox.remove_actor(this.actor); + dummyBox.destroy(); + + let children = null; + switch (this._position_in_panel) { + case WeatherPosition.LEFT: + children = Main.panel._leftBox.get_children(); + Main.panel._leftBox.insert_child_at_index(this.actor, children.length); + break; + case WeatherPosition.CENTER: + children = Main.panel._centerBox.get_children(); + Main.panel._centerBox.insert_child_at_index(this.actor, children.length); + break; + case WeatherPosition.RIGHT: + children = Main.panel._rightBox.get_children(); + Main.panel._rightBox.insert_child_at_index(this.actor, 0); + break; + } + if (Main.panel._menus === undefined) + Main.panel.menuManager.addMenu(this.menu); + else + Main.panel._menus.addMenu(this.menu); + + this._old_position_in_panel = this._position_in_panel; + + // Current weather + this._currentWeather = new St.Bin(); + // Future weather + this._futureWeather = new St.Bin(); + + // Putting the popup item together + let _itemCurrent = new PopupMenu.PopupBaseMenuItem({ + reactive: false + }); + let _itemFuture = new PopupMenu.PopupBaseMenuItem({ + reactive: false + }); + + if (ExtensionUtils.versionCheck(['3.6', '3.8'], Config.PACKAGE_VERSION)) { + _itemCurrent.addActor(this._currentWeather); + _itemFuture.addActor(this._futureWeather); + } else { + _itemCurrent.actor.add_actor(this._currentWeather); + _itemFuture.actor.add_actor(this._futureWeather); + } + + this.menu.addMenuItem(_itemCurrent); + + this._separatorItem = new PopupMenu.PopupSeparatorMenuItem(); + this.menu.addMenuItem(this._separatorItem); + + this.menu.addMenuItem(_itemFuture); + + let item = new PopupMenu.PopupSeparatorMenuItem(); + this.menu.addMenuItem(item); + + this._selectCity = new PopupMenu.PopupSubMenuMenuItem(""); + this._selectCity.actor.set_height(0); + this._selectCity._triangle.set_height(0); + + this._buttonMenu = new PopupMenu.PopupBaseMenuItem({ + reactive: false, + style_class: 'openweather-menu-button-container' + }); + + this.rebuildButtonMenu(); + + this.menu.addMenuItem(this._buttonMenu); + this.menu.addMenuItem(this._selectCity); + this.rebuildSelectCityItem(); + this._selectCity.menu.connect('open-state-changed', Lang.bind(this, function() { + this._selectCity.actor.remove_style_pseudo_class('open'); + })); + + this.rebuildCurrentWeatherUi(); + this.rebuildFutureWeatherUi(); + + this._network_monitor = Gio.network_monitor_get_default(); + + this._connected = false; + this._network_monitor_connection = this._network_monitor.connect('network-changed', Lang.bind(this, this._onNetworkStateChanged)); + this._checkConnectionState(); + + this.menu.connect('open-state-changed', Lang.bind(this, this.recalcLayout)); + if (ExtensionUtils.versionCheck(['3.6', '3.8'], Config.PACKAGE_VERSION)) { + this._needsColorUpdate = true; + let context = St.ThemeContext.get_for_stage(global.stage); + this._globalThemeChangedId = context.connect('changed', Lang.bind(this, function(){this._needsColorUpdate = true;})); + } + }, + + stop: function() { + if (_httpSession !== undefined) + _httpSession.abort(); + + _httpSession = undefined; + + if (this._timeoutCurrent) + Mainloop.source_remove(this._timeoutCurrent); + + this._timeoutCurrent = undefined; + + if (this._timeoutForecast) + Mainloop.source_remove(this._timeoutForecast); + + this._timeoutForecast = undefined; + + if (this._network_monitor_connection) { + this._network_monitor.disconnect(this._network_monitor_connection); + this._network_monitor_connection = undefined; + } + + if (this._settingsC) { + this._settings.disconnect(this._settingsC); + this._settingsC = undefined; + } + + if (this._settingsInterfaceC) { + this._settingsInterface.disconnect(this._settingsInterfaceC); + this._settingsInterfaceC = undefined; + } + + if (this._globalThemeChangedId) { + let context = St.ThemeContext.get_for_stage(global.stage); + context.disconnect(this._globalThemeChangedId); + this._globalThemeChangedId = undefined; + } + }, + + loadConfig: function() { + this._settings = Convenience.getSettings(WEATHER_SETTINGS_SCHEMA); + this._settingsC = this._settings.connect("changed", Lang.bind(this, function() { + this.rebuildCurrentWeatherUi(); + this.rebuildFutureWeatherUi(); + this.rebuildButtonMenu(); + if (this.locationChanged()) { + this.currentWeatherCache = undefined; + this.forecastWeatherCache = undefined; + } + this.parseWeatherCurrent(); + })); + }, + + loadConfigInterface: function() { + let schemaInterface = "org.gnome.desktop.interface"; + if (Gio.Settings.list_schemas().indexOf(schemaInterface) == -1) + throw _("Schema \"%s\" not found.").replace("%s", schemaInterface); + this._settingsInterface = new Gio.Settings({ + schema: schemaInterface + }); + this._settingsInterfaceC = this._settingsInterface.connect("changed", Lang.bind(this, function() { + this.rebuildCurrentWeatherUi(); + this.rebuildFutureWeatherUi(); + if (this.locationChanged()) { + this.currentWeatherCache = undefined; + this.forecastWeatherCache = undefined; + } + this.parseWeatherCurrent(); + })); + }, + + _onNetworkStateChanged: function() { + this._checkConnectionState(); + }, + + _checkConnectionState: function() { + this._connected = this._network_monitor.network_available; + if (this._connected) + this.parseWeatherCurrent(); + }, + + locationChanged: function() { + let location = this.extractId(this._city); + if (this.oldLocation != location) { + return true; + } + return false; + }, + + get _clockFormat() { + if (!this._settingsInterface) + this.loadConfigInterface(); + return this._settingsInterface.get_string("clock-format"); + }, + + get _units() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_enum(WEATHER_UNIT_KEY); + }, + + set _units(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_enum(WEATHER_UNIT_KEY, v); + }, + + get _wind_speed_units() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_enum(WEATHER_WIND_SPEED_UNIT_KEY); + }, + + set _wind_speed_units(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_enum(WEATHER_WIND_SPEED_UNIT_KEY, v); + }, + + get _wind_direction() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(WEATHER_WIND_DIRECTION_KEY); + }, + + set _wind_direction(v) { + if (!this._settings) + this.loadConfig(); + return this._settings.set_boolean(WEATHER_WIND_DIRECTION_KEY, v); + }, + + get _pressure_units() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_enum(WEATHER_PRESSURE_UNIT_KEY); + }, + + set _pressure_units(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_enum(WEATHER_PRESSURE_UNIT_KEY, v); + }, + + get _cities() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_string(WEATHER_CITY_KEY); + }, + + set _cities(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_string(WEATHER_CITY_KEY, v); + }, + + get _actual_city() { + if (!this._settings) + this.loadConfig(); + var a = this._settings.get_int(WEATHER_ACTUAL_CITY_KEY); + var b = a; + var cities = this._cities.split(" && "); + + if (typeof cities != "object") + cities = [cities]; + + var l = cities.length - 1; + + if (a < 0) + a = 0; + + if (l < 0) + l = 0; + + if (a > l) + a = l; + + return a; + }, + + set _actual_city(a) { + if (!this._settings) + this.loadConfig(); + var cities = this._cities.split(" && "); + + if (typeof cities != "object") + cities = [cities]; + + var l = cities.length - 1; + + if (a < 0) + a = 0; + + if (l < 0) + l = 0; + + if (a > l) + a = l; + + this._settings.set_int(WEATHER_ACTUAL_CITY_KEY, a); + }, + + get _city() { + let cities = this._cities; + let cities = cities.split(" && "); + if (cities && typeof cities == "string") + cities = [cities]; + if (!cities[0]) + return ""; + cities = cities[this._actual_city]; + return cities; + }, + + set _city(v) { + let cities = this._cities; + cities = cities.split(" && "); + if (cities && typeof cities == "string") + cities = [cities]; + if (!cities[0]) + cities = []; + cities.splice(this.actual_city, 1, v); + cities = cities.join(" && "); + if (typeof cities != "string") + cities = cities[0]; + this._cities = cities; + }, + + get _translate_condition() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(WEATHER_TRANSLATE_CONDITION_KEY); + }, + + set _translate_condition(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_boolean(WEATHER_TRANSLATE_CONDITION_KEY, v); + }, + + get _icon_type() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(WEATHER_USE_SYMBOLIC_ICONS_KEY) ? 1 : 0; + }, + + set _icon_type(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_boolean(WEATHER_USE_SYMBOLIC_ICONS_KEY, v); + }, + + get _use_text_on_buttons() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(WEATHER_USE_TEXT_ON_BUTTONS_KEY) ? 1 : 0; + }, + + set _use_text_on_buttons(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_boolean(WEATHER_USE_TEXT_ON_BUTTONS_KEY, v); + }, + + get _text_in_panel() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(WEATHER_SHOW_TEXT_IN_PANEL_KEY); + }, + + set _text_in_panel(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_boolean(WEATHER_SHOW_TEXT_IN_PANEL_KEY, v); + }, + + get _position_in_panel() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_enum(WEATHER_POSITION_IN_PANEL_KEY); + }, + + set _position_in_panel(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_enum(WEATHER_POSITION_IN_PANEL_KEY, v); + }, + + get _comment_in_panel() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(WEATHER_SHOW_COMMENT_IN_PANEL_KEY); + }, + + set _comment_in_panel(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_boolean(WEATHER_SHOW_COMMENT_IN_PANEL_KEY, v); + }, + + get _refresh_interval_current() { + if (!this._settings) + this.loadConfig(); + let v = this._settings.get_int(WEATHER_REFRESH_INTERVAL_CURRENT); + return ((v >= 600) ? v : 600); + }, + + set _refresh_interval_current(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_int(WEATHER_REFRESH_INTERVAL_CURRENT, ((v >= 600) ? v : 600)); + }, + + get _refresh_interval_forecast() { + if (!this._settings) + this.loadConfig(); + let v = this._settings.get_int(WEATHER_REFRESH_INTERVAL_FORECAST); + return ((v >= 600) ? v : 600); + }, + + set _refresh_interval_forecast(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_int(WEATHER_REFRESH_INTERVAL_FORECAST, ((v >= 600) ? v : 600)); + }, + + get _center_forecast() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_boolean(WEATHER_CENTER_FORECAST_KEY); + }, + + set _center_forecast(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_boolean(WEATHER_CENTER_FORECAST_KEY, v); + }, + + get _days_forecast() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_int(WEATHER_DAYS_FORECAST); + }, + + set _days_forecast(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_int(WEATHER_DAYS_FORECAST, v); + }, + + get _decimal_places() { + if (!this._settings) + this.loadConfig(); + return this._settings.get_int(WEATHER_DECIMAL_PLACES); + }, + + set _decimal_places(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_int(WEATHER_DECIMAL_PLACES, v); + }, + + get _appid() { + if (!this._settings) + this.loadConfig(); + let key = this._settings.get_string(WEATHER_OWM_API_KEY); + return (key.length == 32) ? key : ''; + }, + + set _appid(v) { + if (!this._settings) + this.loadConfig(); + this._settings.set_string(WEATHER_OWM_API_KEY, v); + }, + + createButton: function(iconName, accessibleName) { + let button; + + if (ExtensionUtils.versionCheck(['3.6', '3.8'], Config.PACKAGE_VERSION)) { + button = new St.Button({ + reactive: true, + can_focus: true, + track_hover: true, + accessible_name: accessibleName, + style_class: 'popup-menu-item openweather-button' + }); + button.child = new St.Icon({ + icon_name: iconName + }); + button.connect('notify::hover', Lang.bind(this, this._onButtonHoverChanged)); + } else + button = Main.panel.statusArea.aggregateMenu._system._createActionButton(iconName, accessibleName); + + return button; + }, + + _onButtonHoverChanged: function(actor, event) { + if (actor.hover) { + actor.add_style_pseudo_class('hover'); + actor.set_style(this._button_background_style); + } else { + actor.remove_style_pseudo_class('hover'); + actor.set_style('background-color:;'); + if (actor != this._urlButton) + actor.set_style(this._button_border_style); + } + }, + + _updateButtonColors: function() { + if (!this._needsColorUpdate) + return; + this._needsColorUpdate = false; + let color; + if (ExtensionUtils.versionCheck(['3.6'], Config.PACKAGE_VERSION)) + color = this._separatorItem._drawingArea.get_theme_node().get_color('-gradient-end'); + else + color = this._separatorItem._separator.actor.get_theme_node().get_color('-gradient-end'); + + let alpha = (Math.round(color.alpha / 2.55) / 100); + + if (color.red > 0 && color.green > 0 && color.blue > 0) + this._button_border_style = 'border:1px solid rgb(' + Math.round(alpha * color.red) + ',' + Math.round(alpha * color.green) + ',' + Math.round(alpha * color.blue) + ');'; + else + this._button_border_style = 'border:1px solid rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + alpha + ');'; + + this._locationButton.set_style(this._button_border_style); + this._reloadButton.set_style(this._button_border_style); + this._prefsButton.set_style(this._button_border_style); + + this._buttonMenu.actor.add_style_pseudo_class('active'); + color = this._buttonMenu.actor.get_theme_node().get_background_color(); + this._button_background_style = 'background-color:rgba(' + color.red + ',' + color.green + ',' + color.blue + ',' + (Math.round(color.alpha / 2.55) / 100) + ');'; + this._buttonMenu.actor.remove_style_pseudo_class('active'); + }, + + rebuildButtonMenu: function() { + if (this._buttonBox) { + if (this._buttonBox1) { + this._buttonBox1.destroy(); + this._buttonBox1 = undefined; + + } + if (this._buttonBox2) { + this._buttonBox2.destroy(); + this._buttonBox2 = undefined; + } + this._buttonMenu.removeActor(this._buttonBox); + this._buttonBox.destroy(); + this._buttonBox = undefined; + } + + if (this._buttonBox1) { + this._buttonBox1.destroy(); + this._buttonBox1 = undefined; + } + if (this._buttonBox2) { + this._buttonBox2.destroy(); + this._buttonBox2 = undefined; + } + + this._locationButton = this.createButton('find-location-symbolic', _("Locations")); + if (this._use_text_on_buttons) + this._locationButton.set_label(this._locationButton.get_accessible_name()); + + this._locationButton.connect('clicked', Lang.bind(this, function() { + if (ExtensionUtils.versionCheck(['3.6', '3.8'], Config.PACKAGE_VERSION)) + this._selectCity.menu.toggle(); + else + this._selectCity._setOpenState(!this._selectCity._getOpenState()); + })); + this._buttonBox1 = new St.BoxLayout({ + style_class: 'openweather-button-box' + }); + this._buttonBox1.add_actor(this._locationButton); + + this._reloadButton = this.createButton('view-refresh-symbolic', _("Reload Weather Information")); + if (this._use_text_on_buttons) + this._reloadButton.set_label(this._reloadButton.get_accessible_name()); + this._reloadButton.connect('clicked', Lang.bind(this, function() { + this.currentWeatherCache = undefined; + this.forecastWeatherCache = undefined; + this.parseWeatherCurrent(); + this.recalcLayout(); + })); + this._buttonBox1.add_actor(this._reloadButton); + + + this._buttonBox2 = new St.BoxLayout({ + style_class: 'openweather-button-box' + }); + + this._urlButton = new St.Button({ + label: _("Weather data provided by:") + (this._use_text_on_buttons ? "\n" : " ") + WEATHER_URL_BASE, + style_class: 'system-menu-action openweather-provider' + }); + if (ExtensionUtils.versionCheck(['3.6', '3.8'], Config.PACKAGE_VERSION)) { + this._urlButton.connect('notify::hover', Lang.bind(this, this._onButtonHoverChanged)); + } + this._urlButton.connect('clicked', Lang.bind(this, function() { + this.menu.actor.hide(); + let title = ; + Main.notifyError(_("Not implemented"), + _("Opening the website of the weatherprovider is not (yet) implemeted !")); + })); + + this._buttonBox2.add_actor(this._urlButton); + + + this._prefsButton = this.createButton('preferences-system-symbolic', _("Weather Settings")); + if (this._use_text_on_buttons) + this._prefsButton.set_label(this._prefsButton.get_accessible_name()); + this._prefsButton.connect('clicked', Lang.bind(this, this._onPreferencesActivate)); + this._buttonBox2.add_actor(this._prefsButton); + + if (ExtensionUtils.versionCheck(['3.6', '3.8'], Config.PACKAGE_VERSION)) { + this._buttonBox = new St.BoxLayout(); + this._buttonBox1.add_style_class_name('openweather-button-box-38'); + this._buttonBox2.add_style_class_name('openweather-button-box-38'); + this._buttonBox.add_actor(this._buttonBox1); + this._buttonBox.add_actor(this._buttonBox2); + + this._buttonMenu.addActor(this._buttonBox); + this._needsColorUpdate = true; + } else { + this._buttonMenu.actor.add_actor(this._buttonBox1); + this._buttonMenu.actor.add_actor(this._buttonBox2); + } + this._buttonBox1MinWidth = undefined; + }, + rebuildSelectCityItem: function() { + this._selectCity.menu.removeAll(); + let item = null; + + let cities = this._cities; + cities = cities.split(" && "); + if (cities && typeof cities == "string") + cities = [cities]; + if (!cities[0]) + return; + + for (let i = 0; cities.length > i; i++) { + item = new PopupMenu.PopupMenuItem(this.extractLocation(cities[i])); + item.location = i; + if (i == this._actual_city) { + if (ExtensionUtils.versionCheck(['3.6', '3.8'], Config.PACKAGE_VERSION)) + item.setShowDot(true); + else + item.setOrnament(PopupMenu.Ornament.DOT); + } + + this._selectCity.menu.addMenuItem(item); + // override the items default onActivate-handler, to keep the ui open while chosing the location + item.activate = this._onActivate; + } + + if (cities.length == 1) + this._selectCity.actor.hide(); + else + this._selectCity.actor.show(); + + }, + + _onActivate: function() { + openweatherMenu._actual_city = this.location; + }, + + extractLocation: function() { + if (!arguments[0]) + return ""; + + if (arguments[0].search(">") == -1) + return _("Invalid city"); + return arguments[0].split(">")[1]; + }, + + extractCity: function() { + if (!arguments[0]) + return ""; + let city = this.extractLocation(arguments[0]); + if (city.indexOf("(") == -1) + return _("Invalid city"); + return city.split("(")[0].trim(); + }, + + extractId: function() { + if (!arguments[0]) + return 0; + + if (arguments[0].search(">") == -1) + return 0; + return arguments[0].split(">")[0]; + }, + + updateCities: function() { + let cities = this._cities; + + cities = cities.split(" && "); + if (cities && typeof cities == "string") + cities = [cities]; + if (!cities[0]) + cities = []; + + if (cities.length === 0) { + this._cities = "2516479>Eivissa (CA)"; + this.updateCities(); + return; + } + + for (let a in cities) { + if (!this.extractCity(cities[a])) { + let params = { + q: cities[a], + type: 'like' + }; + if (this._appid) + params.APPID = this._appid; + + this.load_json_async(WEATHER_URL_CURRENT, params, Lang.bind(this, this._updateCitiesCallback)); + return; + } else + continue; + } + }, + + _updateCitiesCallback: function() { + let city = arguments[0]; + + if (Number(city.cod) != 200) + return; + + let cityText = city.coord + ">" + city.name; + + if (city.sys) + cityText += " (" + city.sys.country + ")"; + + cities.splice(a, 1, cityText); + + cities = cities.join(" && "); + if (typeof cities != "string") + cities = cities[0]; + this._cities = cities; +// this.updateCities(); + }, + + _onPreferencesActivate: function() { + this.menu.actor.hide(); + Util.spawn(["gnome-shell-extension-prefs", "openweather-extension@jenslody.de"]); + return 0; + }, + + recalcLayout: function() { + if (!this.menu.isOpen) + return; + this._updateButtonColors(); + if(this._buttonBox1MinWidth === undefined) + this._buttonBox1MinWidth = this._buttonBox1.get_width(); + this._buttonBox1.set_width(Math.max(this._buttonBox1MinWidth, this._currentWeather.get_width() - this._buttonBox2.get_width())); + if (this._forecastScrollBox !== undefined && this._forecastBox !== undefined && this._currentWeather !== undefined) { + this._forecastScrollBox.set_width(Math.max(this._currentWeather.get_width(), (this._buttonBox1.get_width() + this._buttonBox2.get_width()))); + this._forecastScrollBox.show(); + if (this._forecastBox.get_preferred_width(this._forecastBox.get_height())[0] > this._currentWeather.get_width()) { + this._forecastScrollBox.hscroll.margin_top = 10; + this._forecastScrollBox.hscroll.show(); + } else { + this._forecastScrollBox.hscroll.margin_top = 0; + this._forecastScrollBox.hscroll.hide(); + } + } + }, + + unit_to_unicode: function() { + if (this._units == WeatherUnits.FAHRENHEIT) + return '\u00B0F'; + else if (this._units == WeatherUnits.KELVIN) + return 'K'; + else if (this._units == WeatherUnits.RANKINE) + return '\u00B0Ra'; + else if (this._units == WeatherUnits.REAUMUR) + return '\u00B0R\u00E9'; + else if (this._units == WeatherUnits.ROEMER) + return '\u00B0R\u00F8'; + else if (this._units == WeatherUnits.DELISLE) + return '\u00B0De'; + else if (this._units == WeatherUnits.NEWTON) + return '\u00B0N'; + else + return '\u00B0C'; + }, + + get_weather_icon: function(code) { + // see http://bugs.openweathermap.org/projects/api/wiki/Weather_Condition_Codes + // fallback icons are: weather-clear-night weather-clear weather-few-clouds-night weather-few-clouds weather-fog weather-overcast weather-severe-alert weather-showers weather-showers-scattered weather-snow weather-storm + /* +weather-clouds-night.png +weather-freezing-rain.png +weather-hail.png +weather-many-clouds.png +weather-showers-day.png +weather-showers-night.png +weather-showers-scattered-day.png +weather-showers-scattered-night.png +weather-snow-rain.png +weather-snow-scattered-day.png +weather-snow-scattered-night.png +weather-snow-scattered.png +weather-storm-day.png +weather-storm-night.png + +weather-severe-alert-symbolic.svg + + +weather-clear-night.png = weather-clear-night-symbolic.svg +weather-clear.png = weather-clear-symbolic.svg +weather-clouds.png = weather-overcast-symbolic.svg +weather-few-clouds-night.png = weather-few-clouds-night-symbolic.svg +weather-few-clouds.png = weather-few-clouds-symbolic.svg +weather-mist.png = weather-fog-symbolic.svg +weather-showers-scattered.png = weather-showers-scattered-symbolic.svg +weather-showers.png = weather-showers-symbolic.svg +weather-snow.png = weather-snow-symbolic.svg +weather-storm.png = weather-storm-symbolic.svg + +*/ + switch (parseInt(code, 10)) { + case 200: //thunderstorm with light rain + case 201: //thunderstorm with rain + case 202: //thunderstorm with heavy rain + case 210: //light thunderstorm + case 211: //thunderstorm + case 212: //heavy thunderstorm + case 221: //ragged thunderstorm + case 230: //thunderstorm with light drizzle + case 231: //thunderstorm with drizzle + case 232: //thunderstorm with heavy drizzle + return ['weather-storm']; + case 300: //light intensity drizzle + case 301: //drizzle + case 302: //heavy intensity drizzle + case 310: //light intensity drizzle rain + case 311: //drizzle rain + case 312: //heavy intensity drizzle rain + case 313: //shower rain and drizzle + case 314: //heavy shower rain and drizzle + case 321: //shower drizzle + return ['weather-showers']; + case 500: //light rain + case 501: //moderate rain + case 502: //heavy intensity rain + case 503: //very heavy rain + case 504: //extreme rain + return ['weather-showers-scattered', 'weather-showers']; + case 511: //freezing rain + return ['weather-freezing-rain', 'weather-showers']; + case 520: //light intensity shower rain + case 521: //shower rain + case 522: //heavy intensity shower rain + case 531: //ragged shower rain + return ['weather-showers']; + case 600: //light snow + case 601: //snow + case 602: //heavy snow + case 611: //sleet + case 612: //shower sleet + case 615: //light rain and snow + case 616: //rain and snow + case 620: //light shower snow + case 621: //shower snow + case 622: //heavy shower snow + return ['weather-snow']; + case 701: //mist + case 711: //smoke + case 721: //haze + case 741: //Fog + return ['weather-fog']; + case 731: //Sand/Dust Whirls + case 751: //sand + case 761: //dust + case 762: //VOLCANIC ASH + case 771: //SQUALLS + case 781: //TORNADO + return ['weather-severe-alert']; + case 800: //sky is clear + return ['weather-clear']; + case 801: //few clouds + case 802: //scattered clouds + return ['weather-few-clouds']; + case 803: //broken clouds + return ['weather-many-clouds', 'weather-overcast']; + case 804: //overcast clouds + return ['weather-overcast']; + default: + return ['weather-severe-alert']; + } + }, + + get_weather_icon_safely: function(code, night) { + let iconname = this.get_weather_icon(code); + for (let i = 0; i < iconname.length; i++) { + if (night && this.has_icon(iconname[i] + '-night')) + return iconname[i] + '-night' + this.icon_type(); + if (this.has_icon(iconname[i])) + return iconname[i] + this.icon_type(); + } + return 'weather-severe-alert' + this.icon_type(); + }, + + has_icon: function(icon) { + return Gtk.IconTheme.get_default().has_icon(icon + this.icon_type()); + }, + + get_weather_condition: function(code) { + switch (parseInt(code, 10)) { + case 200: //thunderstorm with light rain + return _('thunderstorm with light rain'); + case 201: //thunderstorm with rain + return _('thunderstorm with rain'); + case 202: //thunderstorm with heavy rain + return _('thunderstorm with heavy rain'); + case 210: //light thunderstorm + return _('light thunderstorm'); + case 211: //thunderstorm + return _('thunderstorm'); + case 212: //heavy thunderstorm + return _('heavy thunderstorm'); + case 221: //ragged thunderstorm + return _('ragged thunderstorm'); + case 230: //thunderstorm with light drizzle + return _('thunderstorm with light drizzle'); + case 231: //thunderstorm with drizzle + return _('thunderstorm with drizzle'); + case 232: //thunderstorm with heavy drizzle + return _('thunderstorm with heavy drizzle'); + case 300: //light intensity drizzle + return _('light intensity drizzle'); + case 301: //drizzle + return _('drizzle'); + case 302: //heavy intensity drizzle + return _('heavy intensity drizzle'); + case 310: //light intensity drizzle rain + return _('light intensity drizzle rain'); + case 311: //drizzle rain + return _('drizzle rain'); + case 312: //heavy intensity drizzle rain + return _('heavy intensity drizzle rain'); + case 313: //shower rain and drizzle + return _('shower rain and drizzle'); + case 314: //heavy shower rain and drizzle + return _('heavy shower rain and drizzle'); + case 321: //shower drizzle + return _('shower drizzle'); + case 500: //light rain + return _('light rain'); + case 501: //moderate rain + return _('moderate rain'); + case 502: //heavy intensity rain + return _('heavy intensity rain'); + case 503: //very heavy rain + return _('very heavy rain'); + case 504: //extreme rain + return _('extreme rain'); + case 511: //freezing rain + return _('freezing rain'); + case 520: //light intensity shower rain + return _('light intensity shower rain'); + case 521: //shower rain + return _('shower rain'); + case 522: //heavy intensity shower rain + return _('heavy intensity shower rain'); + case 531: //ragged shower rain + return _('ragged shower rain'); + case 600: //light snow + return _('light snow'); + case 601: //snow + return _('snow'); + case 602: //heavy snow + return _('heavy snow'); + case 611: //sleet + return _('sleet'); + case 612: //shower sleet + return _('shower sleet'); + case 615: //light rain and snow + return _('light rain and snow'); + case 616: //rain and snow + return _('rain and snow'); + case 620: //light shower snow + return _('light shower snow'); + case 621: //shower snow + return _('shower snow'); + case 622: //heavy shower snow + return _('heavy shower snow'); + case 701: //mist + return _('mist'); + case 711: //smoke + return _('smoke'); + case 721: //haze + return _('haze'); + case 731: //Sand/Dust Whirls + return _('Sand/Dust Whirls'); + case 741: //Fog + return _('Fog'); + case 751: //sand + return _('sand'); + case 761: //dust + return _('dust'); + case 762: //VOLCANIC ASH + return _('VOLCANIC ASH'); + case 771: //SQUALLS + return _('SQUALLS'); + case 781: //TORNADO + return _('TORNADO'); + case 800: //sky is clear + return _('sky is clear'); + case 801: //few clouds + return _('few clouds'); + case 802: //scattered clouds + return _('scattered clouds'); + case 803: //broken clouds + return _('broken clouds'); + case 804: //overcast clouds + return _('overcast clouds'); + default: + return _('Not available'); + } + }, + + toFahrenheit: function(t) { + return ((Number(t) * 1.8) + 32).toFixed(this._decimal_places); + }, + + toKelvin: function(t) { + return (Number(t) + 273.15).toFixed(this._decimal_places); + }, + + toRankine: function(t) { + return ((Number(t) * 1.8) + 491.67).toFixed(this._decimal_places); + }, + + toReaumur: function(t) { + return (Number(t) * 0.8).toFixed(this._decimal_places); + }, + + toRoemer: function(t) { + return ((Number(t) * 21 / 40) + 7.5).toFixed(this._decimal_places); + }, + + toDelisle: function(t) { + return ((100 - Number(t)) * 1.5).toFixed(this._decimal_places); + }, + + toNewton: function(t) { + return (Number(t) - 0.33).toFixed(this._decimal_places); + }, + + toInHg: function(p /*, t*/ ) { + return (p / 33.86530749).toFixed(this._decimal_places); + }, + + toBeaufort: function(w, t) { + if (w < 0.3) + return (!t) ? "0" : "(" + _("Calm") + ")"; + + else if (w >= 0.3 && w <= 1.5) + return (!t) ? "1" : "(" + _("Light air") + ")"; + + else if (w > 1.5 && w <= 3.4) + return (!t) ? "2" : "(" + _("Light breeze") + ")"; + + else if (w > 3.4 && w <= 5.4) + return (!t) ? "3" : "(" + _("Gentle breeze") + ")"; + + else if (w > 5, 4 && w <= 7.9) + return (!t) ? "4" : "(" + _("Moderate breeze") + ")"; + + else if (w > 7.9 && w <= 10.7) + return (!t) ? "5" : "(" + _("Fresh breeze") + ")"; + + else if (w > 10.7 && w <= 13.8) + return (!t) ? "6" : "(" + _("Strong breeze") + ")"; + + else if (w > 13.8 && w <= 17.1) + return (!t) ? "7" : "(" + _("Moderate gale") + ")"; + + else if (w > 17.1 && w <= 20.7) + return (!t) ? "8" : "(" + _("Fresh gale") + ")"; + + else if (w > 20.7 && w <= 24.4) + return (!t) ? "9" : "(" + _("Strong gale") + ")"; + + else if (w > 24.4 && w <= 28.4) + return (!t) ? "10" : "(" + _("Storm") + ")"; + + else if (w > 28.4 && w <= 32.6) + return (!t) ? "11" : "(" + _("Violent storm") + ")"; + + else + return (!t) ? "12" : "(" + _("Hurricane") + ")"; + }, + + get_locale_day: function(abr) { + let days = [_('Sunday'), _('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'), _('Friday'), _('Saturday')]; + return days[abr]; + }, + + get_wind_direction: function(deg) { + let arrows = ["\u2193", "\u2199", "\u2190", "\u2196", "\u2191", "\u2197", "\u2192", "\u2198"]; + let letters = [_('N'), _('NE'), _('E'), _('SE'), _('S'), _('SW'), _('W'), _('NW')]; + let idx = Math.round(deg / 45) % arrows.length; + return (this._wind_direction) ? arrows[idx] : letters[idx]; + }, + + icon_type: function(icon_name) { + if (!icon_name) + if (this._icon_type) + return "-symbolic"; + else + return ""; + + if (this._icon_type) + if (String(icon_name).search("-symbolic") != -1) + return icon_name; + else + return icon_name + "-symbolic"; + else + if (String(icon_name).search("-symbolic") != -1) + return String(icon_name).replace("-symbolic", ""); + else + return icon_name; + }, + + load_json_async: function(url, params, fun) { + if (_httpSession === undefined) { + if (ExtensionUtils.versionCheck(['3.6'], Config.PACKAGE_VERSION)) { + // Soup session (see https://bugzilla.gnome.org/show_bug.cgi?id=661323#c64) (Simon Legner) + _httpSession = new Soup.SessionAsync(); + Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault()); + } else + _httpSession = new Soup.Session(); + } + + let message = Soup.form_request_new_from_hash('GET', url, params); + + _httpSession.queue_message(message, Lang.bind(this, function(_httpSession, message) { + + try { + if (!message.response_body.data) { + fun.call(this, 0); + return; + } + let jp = JSON.parse(message.response_body.data); + fun.call(this, jp); + } catch (e) { + fun.call(this, 0); + return; + } + })); + return; + }, + + parseWeatherCurrent: function() { + if (this.currentWeatherCache === undefined) { + this.refreshWeatherCurrent(); + return; + } + + if (this._old_position_in_panel != this._position_in_panel) { + switch (this._old_position_in_panel) { + case WeatherPosition.LEFT: + Main.panel._leftBox.remove_actor(this.actor); + break; + case WeatherPosition.CENTER: + Main.panel._centerBox.remove_actor(this.actor); + break; + case WeatherPosition.RIGHT: + Main.panel._rightBox.remove_actor(this.actor); + break; + } + + let children = null; + switch (this._position_in_panel) { + case WeatherPosition.LEFT: + children = Main.panel._leftBox.get_children(); + Main.panel._leftBox.insert_child_at_index(this.actor, children.length); + break; + case WeatherPosition.CENTER: + children = Main.panel._centerBox.get_children(); + Main.panel._centerBox.insert_child_at_index(this.actor, children.length); + break; + case WeatherPosition.RIGHT: + children = Main.panel._rightBox.get_children(); + Main.panel._rightBox.insert_child_at_index(this.actor, 0); + break; + } + this._old_position_in_panel = this._position_in_panel; + } + + let json = this.currentWeatherCache; + // Refresh current weather + let location = this.extractLocation(this._city); + + let comment = json.weather[0].description; + if (this._translate_condition) + comment = this.get_weather_condition(json.weather[0].id); + + let temperature = json.main.temp; + let cloudiness = json.clouds.all; + let humidity = json.main.humidity + ' %'; + let pressure = json.main.pressure; + let pressure_unit = 'hPa'; + + let wind_direction = this.get_wind_direction(json.wind.deg); + let wind = json.wind.speed; + let wind_unit = 'm/s'; + + let sunrise = new Date(json.sys.sunrise * 1000); + let sunset = new Date(json.sys.sunset * 1000); + let now = new Date(); + + let iconname = this.get_weather_icon_safely(json.weather[0].id, now < sunrise || now > sunset); + + if (this.lastBuildId === undefined) + this.lastBuildId = 0; + + if (this.lastBuildDate === undefined) + this.lastBuildDate = 0; + + if (this.lastBuildId != json.dt || !this.lastBuildDate) { + this.lastBuildId = json.dt; + this.lastBuildDate = new Date(this.lastBuildId * 1000); + } + + switch (this._pressure_units) { + case WeatherPressureUnits.inHg: + pressure = this.toInHg(pressure); + pressure_unit = "inHg"; + break; + + case WeatherPressureUnits.hPa: + pressure = pressure.toFixed(this._decimal_places); + pressure_unit = "hPa"; + break; + + case WeatherPressureUnits.bar: + pressure = (pressure / 1000).toFixed(this._decimal_places); + pressure_unit = "bar"; + break; + + case WeatherPressureUnits.Pa: + pressure = (pressure * 100).toFixed(this._decimal_places); + pressure_unit = "Pa"; + break; + + case WeatherPressureUnits.kPa: + pressure = (pressure / 10).toFixed(this._decimal_places); + pressure_unit = "kPa"; + break; + + case WeatherPressureUnits.atm: + pressure = (pressure * 0.000986923267).toFixed(this._decimal_places); + pressure_unit = "atm"; + break; + + case WeatherPressureUnits.at: + pressure = (pressure * 0.00101971621298).toFixed(this._decimal_places); + pressure_unit = "at"; + break; + + case WeatherPressureUnits.Torr: + pressure = (pressure * 0.750061683).toFixed(this._decimal_places); + pressure_unit = "Torr"; + break; + + case WeatherPressureUnits.psi: + pressure = (pressure * 0.0145037738).toFixed(this._decimal_places); + pressure_unit = "psi"; + break; + } + + switch (this._units) { + case WeatherUnits.FAHRENHEIT: + temperature = this.toFahrenheit(temperature); + break; + + case WeatherUnits.CELSIUS: + temperature = temperature.toFixed(this._decimal_places); + break; + + case WeatherUnits.KELVIN: + temperature = this.toKelvin(temperature); + break; + + case WeatherUnits.RANKINE: + temperature = this.toRankine(temperature); + break; + + case WeatherUnits.REAUMUR: + temperature = this.toReaumur(temperature); + break; + + case WeatherUnits.ROEMER: + temperature = this.toRoemer(temperature); + break; + + case WeatherUnits.DELISLE: + temperature = this.toDelisle(temperature); + break; + + case WeatherUnits.NEWTON: + temperature = this.toNewton(temperature); + break; + } + + let lastBuild = '-'; + + if (this._clockFormat == "24h") { + sunrise = sunrise.toLocaleFormat("%R"); + sunset = sunset.toLocaleFormat("%R"); + lastBuild = this.lastBuildDate.toLocaleFormat("%R"); + } else { + sunrise = sunrise.toLocaleFormat("%I:%M %p"); + sunset = sunset.toLocaleFormat("%I:%M %p"); + lastBuild = this.lastBuildDate.toLocaleFormat("%I:%M %p"); + } + + let beginOfDay = new Date(new Date().setHours(0, 0, 0, 0)); + let d = Math.floor((this.lastBuildDate.getTime() - beginOfDay.getTime()) / 86400000); + if (d < 0) { + lastBuild = _("Yesterday"); + if (d < -1) + lastBuild = _("%s days ago").replace("%s", -1 * d); + } + + this._currentWeatherIcon.icon_name = this._weatherIcon.icon_name = iconname; + + let weatherInfoC = ""; + let weatherInfoT = ""; + + if (this._comment_in_panel) + weatherInfoC = comment; + + if (this._text_in_panel) + weatherInfoT = parseFloat(temperature).toLocaleString() + ' ' + this.unit_to_unicode(); + + this._weatherInfo.text = weatherInfoC + ((weatherInfoC && weatherInfoT) ? ", " : "") + weatherInfoT; + + this._currentWeatherSummary.text = comment + ", " + parseFloat(temperature).toLocaleString() + ' ' + this.unit_to_unicode(); + this._currentWeatherLocation.text = location; + this._currentWeatherTemperature.text = cloudiness + ' %'; + this._currentWeatherHumidity.text = parseFloat(humidity).toLocaleString() + ' %'; + this._currentWeatherPressure.text = parseFloat(pressure).toLocaleString() + ' ' + pressure_unit; + this._currentWeatherSunrise.text = sunrise; + this._currentWeatherSunset.text = sunset; + this._currentWeatherBuild.text = lastBuild; + + // Override wind units with our preference + switch (this._wind_speed_units) { + case WeatherWindSpeedUnits.MPH: + wind = (wind * WEATHER_CONV_MPS_IN_MPH).toFixed(this._decimal_places); + wind_unit = 'mph'; + break; + + case WeatherWindSpeedUnits.KPH: + wind = (wind * WEATHER_CONV_MPS_IN_KPH).toFixed(this._decimal_places); + wind_unit = 'km/h'; + break; + + case WeatherWindSpeedUnits.MPS: + wind = wind.toFixed(this._decimal_places); + break; + + case WeatherWindSpeedUnits.KNOTS: + wind = (wind * WEATHER_CONV_MPS_IN_KNOTS).toFixed(this._decimal_places); + wind_unit = 'kn'; + break; + + case WeatherWindSpeedUnits.FPS: + wind = (wind * WEATHER_CONV_MPS_IN_FPS).toFixed(this._decimal_places); + wind_unit = 'ft/s'; + break; + + case WeatherWindSpeedUnits.BEAUFORT: + wind_unit = this.toBeaufort(wind, true); + wind = this.toBeaufort(wind); + } + + if (!wind) + this._currentWeatherWind.text = '\u2013'; + else if (wind === 0 || !wind_direction) + this._currentWeatherWind.text = parseFloat(wind).toLocaleString() + ' ' + wind_unit; + else // i.e. wind > 0 && wind_direction + this._currentWeatherWind.text = wind_direction + ' ' + parseFloat(wind).toLocaleString() + ' ' + wind_unit; + + this.parseWeatherForecast(); + this.recalcLayout(); + }, + + refreshWeatherCurrent: function() { + if (!this.extractId(this._city)) { + this.updateCities(); + return; + } + this.oldLocation = this.extractId(this._city); + + let params = { + id: this.oldLocation, + units: 'metric' + }; + if (this._appid) + params.APPID = this._appid; + + this.load_json_async(WEATHER_URL_CURRENT, params, function(json) { + if (json && (Number(json.cod) == 200)) { + + if (this.currentWeatherCache != json) + this.currentWeatherCache = json; + + this.rebuildSelectCityItem(); + + this.parseWeatherCurrent(); + } else { + // we are connected, but get no (or no correct) data, so invalidate + // the shown data and reload after 10 minutes (recommendded by openweathermap.org) + this.rebuildCurrentWeatherUi(); + this.reloadWeatherCurrent(600); + } + }); + this.reloadWeatherCurrent(this._refresh_interval_current); + }, + + reloadWeatherCurrent: function(interval) { + if (this._timeoutCurrent) { + Mainloop.source_remove(this._timeoutCurrent); + } + this._timeoutCurrent = Mainloop.timeout_add_seconds(interval, Lang.bind(this, function() { + this.currentWeatherCache = undefined; + if (this._connected) + this.parseWeatherCurrent(); + else + this.rebuildCurrentWeatherUi(); + return true; + })); + }, + + parseWeatherForecast: function() { + if (this.forecastWeatherCache === undefined) { + this.refreshWeatherForecast(); + return; + } + + let forecast = this.forecastWeatherCache; + let beginOfDay = new Date(new Date().setHours(0, 0, 0, 0)); + + // Refresh forecast + for (let i = 0; i < this._days_forecast; i++) { + let forecastUi = this._forecast[i]; + let forecastData = forecast[i]; + if (forecastData === undefined) + continue; + + let t_low = forecastData.temp.min; + let t_high = forecastData.temp.max; + + switch (this._units) { + case WeatherUnits.FAHRENHEIT: + t_low = this.toFahrenheit(t_low); + t_high = this.toFahrenheit(t_high); + break; + + case WeatherUnits.CELSIUS: + t_low = t_low.toFixed(this._decimal_places); + t_high = t_high.toFixed(this._decimal_places); + break; + + case WeatherUnits.KELVIN: + t_low = this.toKelvin(t_low); + t_high = this.toKelvin(t_high); + break; + + case WeatherUnits.RANKINE: + t_low = this.toRankine(t_low); + t_high = this.toRankine(t_high); + break; + + case WeatherUnits.REAUMUR: + t_low = this.toReaumur(t_low); + t_high = this.toReaumur(t_high); + break; + + case WeatherUnits.ROEMER: + t_low = this.toRoemer(t_low); + t_high = this.toRoemer(t_high); + break; + + case WeatherUnits.DELISLE: + t_low = this.toDelisle(t_low); + t_high = this.toDelisle(t_high); + break; + + case WeatherUnits.NEWTON: + t_low = this.toNewton(t_low); + t_high = this.toNewton(t_high); + break; + } + + let comment = forecastData.weather[0].description; + if (this._translate_condition) + comment = this.get_weather_condition(forecastData.weather[0].id); + + let forecastDate = new Date(forecastData.dt * 1000); + let dayLeft = Math.floor((forecastDate.getTime() - beginOfDay.getTime()) / 86400000); + + let date_string = _("Today"); + if (dayLeft == 1) + date_string = _("Tomorrow"); + else if (dayLeft > 1) + date_string = _("In %s days").replace("%s", dayLeft); + else if (dayLeft == -1) + date_string = _("Yesterday"); + else if (dayLeft < -1) + date_string = _("%s days ago").replace("%s", -1 * dayLeft); + + forecastUi.Day.text = date_string + ' (' + this.get_locale_day(forecastDate.getDay()) + ')\n' + forecastDate.toLocaleDateString(); + forecastUi.Temperature.text = '\u2193 ' + parseFloat(t_low).toLocaleString() + ' ' + this.unit_to_unicode() + ' \u2191 ' + parseFloat(t_high).toLocaleString() + ' ' + this.unit_to_unicode(); + forecastUi.Summary.text = comment; + forecastUi.Icon.icon_name = this.get_weather_icon_safely(forecastData.weather[0].id); + } + }, + + refreshWeatherForecast: function() { + + if (!this.extractId(this._city)) { + this.updateCities(); + return; + } + + this.oldLocation = this.extractId(this._city); + + let params = { + id: this.oldLocation, + units: 'metric', + cnt: '13' + }; + if (this._appid) + params.APPID = this._appid; + + this.load_json_async(WEATHER_URL_FORECAST, params, function(json) { + if (json && (Number(json.cod) == 200)) { + if (this.forecastWeatherCache != json.list) + this.forecastWeatherCache = json.list; + + this.parseWeatherForecast(); + } else { + // we are connected, but get no (or no correct) data, so invalidate + // the shown data and reload after 10 minutes (recommendded by openweathermap.org) + this.rebuildFutureWeatherUi(); + this.reloadWeatherForecast(600); + } + }); + this.reloadWeatherForecast(this._refresh_interval_forecast); + }, + + reloadWeatherForecast: function(interval) { + if (this._timeoutForecast) { + Mainloop.source_remove(this._timeoutForecast); + } + this._timeoutForecast = Mainloop.timeout_add_seconds(interval, Lang.bind(this, function() { + this.forecastWeatherCache = undefined; + if (this._connected) + this.parseWeatherForecast(); + else + this.rebuildFutureWeatherUi(); + return true; + })); + }, + + destroyCurrentWeather: function() { + if (this._currentWeather.get_child() !== null) + this._currentWeather.get_child().destroy(); + }, + + destroyFutureWeather: function() { + if (this._futureWeather.get_child() !== null) + this._futureWeather.get_child().destroy(); + }, + + rebuildCurrentWeatherUi: function() { + this._weatherInfo.text = _('Loading current weather ...'); + this._weatherIcon.icon_name = 'view-refresh' + this.icon_type(); + + this.destroyCurrentWeather(); + + // This will hold the icon for the current weather + this._currentWeatherIcon = new St.Icon({ + icon_size: 72, + icon_name: 'view-refresh' + this.icon_type(), + style_class: 'openweather-current-icon' + }); + + this._sunriseIcon = new St.Icon({ + icon_size: 15, + icon_name: 'weather-clear' + this.icon_type(), + style_class: 'openweather-sunrise-icon' + }); + + this._sunsetIcon = new St.Icon({ + icon_size: 15, + icon_name: 'weather-clear-night' + this.icon_type(), + style_class: 'openweather-sunset-icon' + }); + + this._buildIcon = new St.Icon({ + icon_size: 15, + icon_name: 'view-refresh' + this.icon_type(), + style_class: 'openweather-build-icon' + }); + + // The summary of the current weather + this._currentWeatherSummary = new St.Label({ + text: _('Loading ...'), + style_class: 'openweather-current-summary' + }); + this._currentWeatherLocation = new St.Label({ + text: _('Please wait') + }); + + let bb = new St.BoxLayout({ + vertical: true, + style_class: 'openweather-current-summarybox' + }); + bb.add_actor(this._currentWeatherLocation); + bb.add_actor(this._currentWeatherSummary); + + this._currentWeatherSunrise = new St.Label({ + text: '-' + }); + this._currentWeatherSunset = new St.Label({ + text: '-' + }); + this._currentWeatherBuild = new St.Label({ + text: '-' + }); + + let ab = new St.BoxLayout({ + style_class: 'openweather-current-infobox' + }); + + ab.add_actor(this._sunriseIcon); + ab.add_actor(this._currentWeatherSunrise); + ab.add_actor(this._sunsetIcon); + ab.add_actor(this._currentWeatherSunset); + ab.add_actor(this._buildIcon); + ab.add_actor(this._currentWeatherBuild); + bb.add_actor(ab); + + // Other labels + this._currentWeatherTemperature = new St.Label({ + text: '...' + }); + this._currentWeatherHumidity = new St.Label({ + text: '...' + }); + this._currentWeatherPressure = new St.Label({ + text: '...' + }); + this._currentWeatherWind = new St.Label({ + text: '...' + }); + + let rb = new St.BoxLayout({ + style_class: 'openweather-current-databox' + }); + let rb_captions = new St.BoxLayout({ + vertical: true, + style_class: 'openweather-current-databox-captions' + }); + let rb_values = new St.BoxLayout({ + vertical: true, + style_class: 'openweather-current-databox-values' + }); + rb.add_actor(rb_captions); + rb.add_actor(rb_values); + + rb_captions.add_actor(new St.Label({ + text: _('Cloudiness:') + })); + rb_values.add_actor(this._currentWeatherTemperature); + rb_captions.add_actor(new St.Label({ + text: _('Humidity:') + })); + rb_values.add_actor(this._currentWeatherHumidity); + rb_captions.add_actor(new St.Label({ + text: _('Pressure:') + })); + rb_values.add_actor(this._currentWeatherPressure); + rb_captions.add_actor(new St.Label({ + text: _('Wind:') + })); + rb_values.add_actor(this._currentWeatherWind); + + let xb = new St.BoxLayout(); + xb.add_actor(bb); + xb.add_actor(rb); + + let box = new St.BoxLayout({ + style_class: 'openweather-current-iconbox' + }); + box.add_actor(this._currentWeatherIcon); + box.add_actor(xb); + this._currentWeather.set_child(box); + }, + + scrollForecastBy: function(delta) { + if (this._forecastScrollBox === undefined) + return; + this._forecastScrollBox.hscroll.adjustment.value += delta; + }, + + rebuildFutureWeatherUi: function() { + this.destroyFutureWeather(); + + this._forecast = []; + this._forecastBox = new St.BoxLayout({ + x_align: this._center_forecast ? St.Align.END : St.Align.START, + style_class: 'openweather-forecast-box' + }); + + this._forecastScrollBox = new St.ScrollView({ + style_class: 'openweather-forecasts' + }); + + let pan = new Clutter.PanAction({ + interpolate: true + }); + pan.connect('pan', Lang.bind(this, function(action) { + + let[dist, dx, dy] = action.get_motion_delta(0); + + this.scrollForecastBy(-1 * (dx / this._forecastScrollBox.width) * this._forecastScrollBox.hscroll.adjustment.page_size); + return false; + })); + this._forecastScrollBox.add_action(pan); + + this._forecastScrollBox.connect('scroll-event', Lang.bind(this, this._onScroll)); + this._forecastScrollBox.hscroll.connect('scroll-event', Lang.bind(this, this._onScroll)); + + this._forecastScrollBox.hscroll.margin_right = 25; + this._forecastScrollBox.hscroll.margin_left = 25; + this._forecastScrollBox.hscroll.hide(); + this._forecastScrollBox.vscrollbar_policy = Gtk.PolicyType.NEVER; + this._forecastScrollBox.hscrollbar_policy = Gtk.PolicyType.AUTOMATIC; + this._forecastScrollBox.enable_mouse_scrolling = true; + this._forecastScrollBox.hide(); + + this._futureWeather.set_child(this._forecastScrollBox); + + for (let i = 0; i < this._days_forecast; i++) { + let forecastWeather = {}; + + forecastWeather.Icon = new St.Icon({ + icon_size: 48, + icon_name: 'view-refresh' + this.icon_type(), + style_class: 'openweather-forecast-icon' + }); + forecastWeather.Day = new St.Label({ + style_class: 'openweather-forecast-day' + }); + forecastWeather.Summary = new St.Label({ + style_class: 'openweather-forecast-summary' + }); + forecastWeather.Temperature = new St.Label({ + style_class: 'openweather-forecast-temperature' + }); + + let by = new St.BoxLayout({ + vertical: true, + style_class: 'openweather-forecast-databox' + }); + by.add_actor(forecastWeather.Day); + by.add_actor(forecastWeather.Summary); + by.add_actor(forecastWeather.Temperature); + + let bb = new St.BoxLayout({ + style_class: 'openweather-forecast-iconbox' + }); + bb.add_actor(forecastWeather.Icon); + bb.add_actor(by); + + this._forecast[i] = forecastWeather; + this._forecastBox.add_actor(bb); + } + this._forecastScrollBox.add_actor(this._forecastBox); + }, + + _onScroll: function(actor, event) { + let dx = 0; + let dy = 0; + switch (event.get_scroll_direction()) { + case Clutter.ScrollDirection.UP: + dy = -1; + break; + case Clutter.ScrollDirection.DOWN: + dy = 1; + break; + // horizontal scrolling will be handled by the control itself + default: + return true; + } + + this.scrollForecastBy(dy * this._forecastScrollBox.hscroll.adjustment.stepIncrement); + return false; + } +}); + +let openweatherMenu; + +function init() { + Convenience.initTranslations('gnome-shell-extension-openweather'); +} + +function enable() { + openweatherMenu = new OpenweatherMenuButton(); + Main.panel.addToStatusArea('openweatherMenu', openweatherMenu); +} + +function disable() { + openweatherMenu.stop(); + openweatherMenu.destroy(); +} diff --git a/temp.txt b/temp.txt new file mode 100644 index 0000000..9bc8967 --- /dev/null +++ b/temp.txt @@ -0,0 +1,937 @@ +{ + "cod":"200", + "message":0.0227, + "city": + { + "id":2807184, + "name":"Witzenhausen", + "coord": + { + "lon":9.86667, + "lat":51.333328 + }, + "country":"DE", + "population":16055 + }, + "cnt":10, + "list": + [ + { + "dt":1382526000, + "temp": + { + "day":18.21, + "min":15.06, + "max":18.39, + "night":15.06, + "eve":17.24, + "morn":17.89 + }, + "pressure":990.22, + "humidity":75, + "weather": + [ + { + "id":804, + "main":"Clouds", + "description":"overcast clouds", + "icon":"04d" + } + ], + "speed":7.59, + "deg":216, + "clouds":88 + }, + { + "dt":1382612400, + "temp": + { + "day":12.78, + "min":10.18, + "max":13.3, + "night":10.18, + "eve":10.8, + "morn":12.04 + }, + "pressure":999.55, + "humidity":100, + "weather": + [ + { + "id":501, + "main":"Rain", + "description":"moderate rain", + "icon":"10d" + } + ], + "speed":2.28, + "deg":328, + "clouds":88, + "rain":6 + }, + { + "dt":1382698800, + "temp": + { + "day":14.51, + "min":11.29, + "max":14.95, + "night":14.95, + "eve":14.45, + "morn":11.29 + }, + "pressure":997.12, + "humidity":99, + "weather": + [ + { + "id":500, + "main":"Rain", + "description":"light rain", + "icon":"10d" + } + ], + "speed":5.02, + "deg":166, + "clouds":92, + "rain":1 + }, + { + "dt":1382785200, + "temp": + { + "day":18.32, + "min":15.21, + "max":18.32, + "night":15.81, + "eve":17.21, + "morn":15.21 + }, + "pressure":991.83, + "humidity":89, + "weather": + [ + { + "id":500, + "main":"Rain", + "description":"light rain", + "icon":"10d" + } + ], + "speed":6.92, + "deg":211, + "clouds":20, + "rain":1 + }, + { + "dt":1382871600, + "temp": + { + "day":11.91, + "min":10.7, + "max":13.76, + "night":10.7, + "eve":11.4, + "morn":13.28 + }, + "pressure":991.86, + "humidity":90, + "weather": + [ + { + "id":501, + "main":"Rain", + "description":"moderate rain", + "icon":"10d" + } + ], + "speed":9.27, + "deg":256, + "clouds":24, + "rain":7 + }, + { + "dt":1382958000, + "temp": + { + "day":12.72, + "min":9.59, + "max":13.11, + "night":13.11, + "eve":12.94, + "morn":9.59 + }, + "pressure":983.93, + "humidity":0, + "weather": + [ + { + "id":501, + "main":"Rain", + "description":"moderate rain", + "icon":"10d" + } + ], + "speed":7.39, + "deg":205, + "clouds":85, + "rain":9.48 + }, + { + "dt":1383044400, + "temp": + { + "day":12.55, + "min":10.99, + "max":12.55, + "night":10.99, + "eve":11.32, + "morn":11.08 + }, + "pressure":974.35, + "humidity":0, + "weather": + [ + { + "id":501, + "main":"Rain", + "description":"moderate rain", + "icon":"10d" + } + ], + "speed":11.04, + "deg":222, + "clouds":78, + "rain":4.04 + }, + { + "dt":1383130800, + "temp": + { + "day":13.45, + "min":11.12, + "max":13.45, + "night":11.12, + "eve":12.01, + "morn":11.28 + }, + "pressure":974.2, + "humidity":0, + "weather": + [ + { + "id":501, + "main":"Rain", + "description":"moderate rain", + "icon":"10d" + } + ], + "speed":7.86, + "deg":214, + "clouds":96, + "rain":3.98 + }, + { + "dt":1383217200, + "temp": + { + "day":12.61, + "min":10.44, + "max":12.61, + "night":10.44, + "eve":11.22, + "morn":10.67 + }, + "pressure":983.29, + "humidity":0, + "weather": + [ + { + "id":501, + "main":"Rain", + "description":"moderate rain", + "icon":"10d" + } + ], + "speed":7.34, + "deg":228, + "clouds":4, + "rain":5.07 + }, + { + "dt":1383303600, + "temp": + { + "day":10.96, + "min":8.19, + "max":10.96, + "night":8.19, + "eve":9.54, + "morn":9.71 + }, + "pressure":996.06, + "humidity":0, + "weather": + [ + { + "id":501, + "main":"Rain", + "description":"moderate rain", + "icon":"10d" + } + ], + "speed":3.4, + "deg":268, + "clouds":98, + "rain":6.79 + } + ] +} + + + +{ + "coord": + { + "lon":9.86667, + "lat":51.333328 + }, + "sys": + { + "country":"DE", + "sunrise":1382507949, + "sunset":1382544624 + }, + "weather": + [ + { + "id":804, + "main":"Clouds", + "description":"overcast clouds", + "icon":"04d" + } + ], + "base":"global stations", + "main": + { + "temp":17.885, + "temp_min":17.885, + "temp_max":17.885, + "pressure":990.17, + "sea_level":1019.55, + "grnd_level":990.17, + "humidity":77 + }, + "wind": + { + "speed":7.17, + "deg":205.505 + }, + "clouds": + { + "all":92 + }, + "dt":1382514022, + "id":2807184, + "name":"Witzenhausen", + "cod":200 +} + +{ + "coord": + { + "lon":1.43296, + "lat":38.908829 + }, + "sys": + { + "country":"ES", + "sunrise":1382508754, + "sunset":1382547866 + }, + "weather": + [ + { + "id":800, + "main":"Clear", + "description":"sky is clear", + "icon":"02d" + } + ], + "base":"global stations", + "main": + { + "temp":23.453, + "temp_min":23.453, + "temp_max":23.453, + "pressure":1028.28, + "sea_level":1028.37, + "grnd_level":1028.28, + "humidity":100 + }, + "wind": + { + "speed":7.96, + "deg":225.5 + }, + "clouds": + { + "all":8 + }, + "dt":1382545727, + "id":2516479, + "name":"Ibiza", + "cod":200 +} + + English - en, Russian - ru, Italian - it, Spanish - sp, Ukrainian - ua, German - de, Portuguese - pt, Romanian - ro, Polish - pl, Finnish - fi, Dutch - nl, French - fr, Bulgarian - bg, Swedish - se, Chinese Traditional - zh_tw, Chinese Simplified - zh_cn, Turkish - tr , Czech - cz + + +https://api.forecast.io/forecast/b251335276b56396cc98b7c79b282971/51.3750768,9.9179173?exclude=minutely,hourly&units=si + +{ + "latitude":51.3750768, + "longitude":9.9179173, + "timezone":"Europe/Berlin", + "offset":1, + "currently": + { + "time":1426481725, + "summary":"Clear", + "icon":"clear-night", + "precipIntensity":0, + "precipProbability":0, + "temperature":1.07, + "apparentTemperature":-3.68, + "dewPoint":-0.08, + "humidity":0.92, + "windSpeed":5.19, + "windBearing":98, + "cloudCover":0.01, + "pressure":1026.57, + "ozone":424.04 + }, + "daily": + { + "summary":"Drizzle on Friday and Saturday, with temperatures peaking at 16C on Wednesday.", + "icon":"rain", + "data": + [ + { + "time":1426460400, + "summary":"Mostly cloudy starting in the afternoon, continuing until evening.", + "icon":"partly-cloudy-day", + "sunriseTime":1426484069, + "sunsetTime":1426526784, + "moonPhase":0.85, + "precipIntensity":0, + "precipIntensityMax":0, + "precipProbability":0, + "temperatureMin":0.94, + "temperatureMinTime":1426478400, + "temperatureMax":11.72, + "temperatureMaxTime":1426518000, + "apparentTemperatureMin":-3.93, + "apparentTemperatureMinTime":1426478400, + "apparentTemperatureMax":11.72, + "apparentTemperatureMaxTime":1426518000, + "dewPoint":4.12, + "humidity":0.85, + "windSpeed":5.33, + "windBearing":104, + "cloudCover":0.17, + "pressure":1026.07, + "ozone":420.3 + }, + { + "time":1426546800, + "summary":"Partly cloudy starting in the afternoon.", + "icon":"partly-cloudy-night", + "sunriseTime":1426570332, + "sunsetTime":1426613285, + "moonPhase":0.89, + "precipIntensity":0, + "precipIntensityMax":0, + "precipProbability":0, + "temperatureMin":1.35, + "temperatureMinTime":1426568400, + "temperatureMax":13.86, + "temperatureMaxTime":1426604400, + "apparentTemperatureMin":-1.95, + "apparentTemperatureMinTime":1426568400, + "apparentTemperatureMax":13.86, + "apparentTemperatureMaxTime":1426604400, + "dewPoint":5.29, + "humidity":0.86, + "windSpeed":2.7, + "windBearing":105, + "cloudCover":0.24, + "pressure":1025.65, + "ozone":391.18 + }, + { + "time":1426633200, + "summary":"Partly cloudy in the morning.", + "icon":"partly-cloudy-night", + "sunriseTime":1426656595, + "sunsetTime":1426699785, + "moonPhase":0.93, + "precipIntensity":0, + "precipIntensityMax":0, + "precipProbability":0, + "temperatureMin":2.74, + "temperatureMinTime":1426654800, + "temperatureMax":15.66, + "temperatureMaxTime":1426687200, + "apparentTemperatureMin":2.74, + "apparentTemperatureMinTime":1426654800, + "apparentTemperatureMax":15.66, + "apparentTemperatureMaxTime":1426687200, + "dewPoint":5.94, + "humidity":0.83, + "windSpeed":0.49, + "windBearing":49, + "cloudCover":0.21, + "pressure":1025.06, + "ozone":387.11 + }, + { + "time":1426719600, + "summary":"Mostly cloudy throughout the day.", + "icon":"partly-cloudy-day", + "sunriseTime":1426742858, + "sunsetTime":1426786286, + "moonPhase":0.97, + "precipIntensity":0, + "precipIntensityMax":0, + "precipProbability":0, + "temperatureMin":3.92, + "temperatureMinTime":1426744800, + "temperatureMax":9.96, + "temperatureMaxTime":1426770000, + "apparentTemperatureMin":2.91, + "apparentTemperatureMinTime":1426744800, + "apparentTemperatureMax":8.87, + "apparentTemperatureMaxTime":1426770000, + "dewPoint":5.89, + "humidity":0.91, + "windSpeed":1.7, + "windBearing":308, + "cloudCover":0.6, + "pressure":1024.53, + "ozone":401.06 + }, + { + "time":1426806000, + "summary":"Drizzle until evening, starting again overnight.", + "icon":"rain", + "sunriseTime":1426829121, + "sunsetTime":1426872786, + "moonPhase":0.01, + "precipIntensity":0.1041, + "precipIntensityMax":0.2184, + "precipIntensityMaxTime":1426863600, + "precipProbability":0.68, + "precipType":"rain", + "temperatureMin":3.6, + "temperatureMinTime":1426831200, + "temperatureMax":6.87, + "temperatureMaxTime":1426863600, + "apparentTemperatureMin":1.98, + "apparentTemperatureMinTime":1426831200, + "apparentTemperatureMax":4.71, + "apparentTemperatureMaxTime":1426863600, + "dewPoint":4.37, + "humidity":0.91, + "windSpeed":2.48, + "windBearing":290, + "cloudCover":0.89, + "pressure":1020.38, + "ozone":387.87 + }, + { + "time":1426892400, + "summary":"Light rain until afternoon.", + "icon":"rain", + "sunriseTime":1426915383, + "sunsetTime":1426959286, + "moonPhase":0.04, + "precipIntensity":0.1321, + "precipIntensityMax":0.2438, + "precipIntensityMaxTime":1426906800, + "precipProbability":0.8, + "precipType":"rain", + "temperatureMin":0.57, + "temperatureMinTime":1426975200, + "temperatureMax":5.71, + "temperatureMaxTime":1426892400, + "apparentTemperatureMin":-2.42, + "apparentTemperatureMinTime":1426975200, + "apparentTemperatureMax":3.27, + "apparentTemperatureMaxTime":1426892400, + "dewPoint":2.03, + "humidity":0.91, + "windSpeed":3.17, + "windBearing":357, + "cloudCover":0.86, + "pressure":1015.43, + "ozone":410.38 + }, + { + "time":1426978800, + "summary":"Partly cloudy overnight.", + "icon":"partly-cloudy-night", + "sunriseTime":1427001646, + "sunsetTime":1427045786, + "moonPhase":0.08, + "precipIntensity":0.0305, + "precipIntensityMax":0.0356, + "precipIntensityMaxTime":1427025600, + "precipProbability":0.01, + "precipType":"snow", + "precipAccumulation":0.602, + "temperatureMin":-2.46, + "temperatureMinTime":1427000400, + "temperatureMax":4.5, + "temperatureMaxTime":1427036400, + "apparentTemperatureMin":-5.86, + "apparentTemperatureMinTime":1427000400, + "apparentTemperatureMax":1.8, + "apparentTemperatureMaxTime":1427036400, + "dewPoint":-3.39, + "humidity":0.75, + "windSpeed":2.65, + "windBearing":59, + "cloudCover":0.05, + "pressure":1020.27, + "ozone":356.07 + }, + { + "time":1427065200, + "summary":"Mostly cloudy throughout the day.", + "icon":"partly-cloudy-day", + "sunriseTime":1427087908, + "sunsetTime":1427132286, + "moonPhase":0.12, + "precipIntensity":0.0356, + "precipIntensityMax":0.0914, + "precipIntensityMaxTime":1427133600, + "precipProbability":0.1, + "precipType":"rain", + "temperatureMin":-2.33, + "temperatureMinTime":1427086800, + "temperatureMax":6.96, + "temperatureMaxTime":1427119200, + "apparentTemperatureMin":-4.58, + "apparentTemperatureMinTime":1427090400, + "apparentTemperatureMax":4.99, + "apparentTemperatureMaxTime":1427119200, + "dewPoint":-3.86, + "humidity":0.65, + "windSpeed":1.13, + "windBearing":216, + "cloudCover":0.73, + "pressure":1019.58, + "ozone":334.74 + } + ] + }, + "flags": + { + "sources": + [ + "isd", + "madis", + "metno_ce", + "metno_ne", + "fnmoc", + "cmc", + "gfs" + ], + "isd-stations": + [ + "094490-99999", + "104380-99999", + "104440-99999", + "104480-99999", + "104490-99999" + ], + "madis-stations": + [ + "D8965", + "E0831", + "E0954", + "E4790", + "EDVK", + "ETHF" + ], + "metno-license":"Based on data from the Norwegian Meteorological Institute. (http://api.met.no/)", + "units":"si" + } +} + +https://api.forecast.io/forecast/b251335276b56396cc98b7c79b282971/51.3750768,9.9175961?units=si&exclude=currently,minutely,hourly,alerts,flags + +{ + "latitude":51.3750768, + "longitude":9.9175961, + "timezone":"Europe/Berlin", + "offset":1, + "daily": + { + "summary":"Light rain today through Friday, with temperatures rising to 13C on Monday.", + "icon":"rain", + "data": + [ + { + "time":1426892400, + "summary":"Light rain until afternoon, starting again in the evening.", + "icon":"rain", + "sunriseTime":1426915383, + "sunsetTime":1426959286, + "moonPhase":0.04, + "precipIntensity":0.0914, + "precipIntensityMax":0.2388, + "precipIntensityMaxTime":1426960800, + "precipProbability":0.88, + "precipType":"rain", + "temperatureMin":-0.01, + "temperatureMinTime":1426903200, + "temperatureMax":7.6, + "temperatureMaxTime":1426950000, + "apparentTemperatureMin":-2.87, + "apparentTemperatureMinTime":1426910400, + "apparentTemperatureMax":6.66, + "apparentTemperatureMaxTime":1426950000, + "dewPoint":2.58, + "humidity":0.92, + "windSpeed":1.95, + "windBearing":287, + "cloudCover":0.79, + "pressure":1016.81, + "ozone":418.83 + }, + { + "time":1426978800, + "summary":"Mostly cloudy until afternoon.", + "icon":"partly-cloudy-day", + "sunriseTime":1427001646, + "sunsetTime":1427045786, + "moonPhase":0.08, + "precipIntensity":0.0203, + "precipIntensityMax":0.0864, + "precipIntensityMaxTime":1426982400, + "precipProbability":0.03, + "precipType":"rain", + "temperatureMin":-0.59, + "temperatureMinTime":1427004000, + "temperatureMax":6.78, + "temperatureMaxTime":1427036400, + "apparentTemperatureMin":-5.62, + "apparentTemperatureMinTime":1427004000, + "apparentTemperatureMax":3.92, + "apparentTemperatureMaxTime":1427036400, + "dewPoint":-0.18, + "humidity":0.82, + "windSpeed":3.97, + "windBearing":77, + "cloudCover":0.5, + "pressure":1025.36, + "ozone":364.7 + }, + { + "time":1427065200, + "summary":"Partly cloudy until afternoon.", + "icon":"partly-cloudy-day", + "sunriseTime":1427087908, + "sunsetTime":1427132286, + "moonPhase":0.12, + "precipIntensity":0, + "precipIntensityMax":0, + "precipProbability":0, + "temperatureMin":-3.18, + "temperatureMinTime":1427086800, + "temperatureMax":13.21, + "temperatureMaxTime":1427119200, + "apparentTemperatureMin":-5.2, + "apparentTemperatureMinTime":1427079600, + "apparentTemperatureMax":13.21, + "apparentTemperatureMaxTime":1427119200, + "dewPoint":-1.16, + "humidity":0.71, + "windSpeed":1.21, + "windBearing":169, + "cloudCover":0.33, + "pressure":1018.13, + "ozone":319.54 + }, + { + "time":1427151600, + "summary":"Mostly cloudy starting in the afternoon.", + "icon":"partly-cloudy-night", + "sunriseTime":1427174171, + "sunsetTime":1427218786, + "moonPhase":0.15, + "precipIntensity":0.0203, + "precipIntensityMax":0.0635, + "precipIntensityMaxTime":1427212800, + "precipProbability":0.02, + "precipType":"rain", + "temperatureMin":-1.47, + "temperatureMinTime":1427169600, + "temperatureMax":12.66, + "temperatureMaxTime":1427205600, + "apparentTemperatureMin":-1.47, + "apparentTemperatureMinTime":1427169600, + "apparentTemperatureMax":12.66, + "apparentTemperatureMaxTime":1427205600, + "dewPoint":2.52, + "humidity":0.83, + "windSpeed":1.1, + "windBearing":230, + "cloudCover":0.3, + "pressure":1007.38, + "ozone":368.29 + }, + { + "time":1427238000, + "summary":"Light rain overnight.", + "icon":"rain", + "sunriseTime":1427260433, + "sunsetTime":1427305285, + "moonPhase":0.19, + "precipIntensity":0.0483, + "precipIntensityMax":0.0864, + "precipIntensityMaxTime":1427274000, + "precipProbability":0.05, + "precipType":"rain", + "temperatureMin":0.86, + "temperatureMinTime":1427252400, + "temperatureMax":8.93, + "temperatureMaxTime":1427295600, + "apparentTemperatureMin":0.86, + "apparentTemperatureMinTime":1427252400, + "apparentTemperatureMax":7.97, + "apparentTemperatureMaxTime":1427295600, + "dewPoint":2.88, + "humidity":0.89, + "windSpeed":0.95, + "windBearing":335, + "cloudCover":0.93, + "pressure":1005.42, + "ozone":384.05 + }, + { + "time":1427324400, + "summary":"Light rain throughout the day.", + "icon":"rain", + "sunriseTime":1427346696, + "sunsetTime":1427391785, + "moonPhase":0.22, + "precipIntensity":0.3023, + "precipIntensityMax":0.5588, + "precipIntensityMaxTime":1427360400, + "precipProbability":0.8, + "precipType":"rain", + "temperatureMin":1.57, + "temperatureMinTime":1427349600, + "temperatureMax":7.74, + "temperatureMaxTime":1427385600, + "apparentTemperatureMin":-2.31, + "apparentTemperatureMinTime":1427349600, + "apparentTemperatureMax":5.8, + "apparentTemperatureMaxTime":1427385600, + "dewPoint":4.02, + "humidity":0.93, + "windSpeed":3.08, + "windBearing":1, + "cloudCover":0.99, + "pressure":1004.17, + "ozone":383.65 + }, + { + "time":1427410800, + "summary":"Overcast throughout the day.", + "icon":"cloudy", + "sunriseTime":1427432959, + "sunsetTime":1427478284, + "moonPhase":0.25, + "precipIntensity":0.0686, + "precipIntensityMax":0.1829, + "precipIntensityMaxTime":1427410800, + "precipProbability":0.42, + "precipType":"rain", + "temperatureMin":1.35, + "temperatureMinTime":1427436000, + "temperatureMax":7.37, + "temperatureMaxTime":1427472000, + "apparentTemperatureMin":-1.55, + "apparentTemperatureMinTime":1427436000, + "apparentTemperatureMax":5.62, + "apparentTemperatureMaxTime":1427472000, + "dewPoint":3.51, + "humidity":0.93, + "windSpeed":2.32, + "windBearing":331, + "cloudCover":1, + "pressure":1009.45, + "ozone":351.61 + }, + { + "time":1427497200, + "summary":"Overcast throughout the day.", + "icon":"cloudy", + "sunriseTime":1427519221, + "sunsetTime":1427564784, + "moonPhase":0.29, + "precipIntensity":0.0432, + "precipIntensityMax":0.0813, + "precipIntensityMaxTime":1427554800, + "precipProbability":0.08, + "precipType":"rain", + "temperatureMin":0.93, + "temperatureMinTime":1427518800, + "temperatureMax":8.17, + "temperatureMaxTime":1427554800, + "apparentTemperatureMin":0.93, + "apparentTemperatureMinTime":1427518800, + "apparentTemperatureMax":6.77, + "apparentTemperatureMaxTime":1427554800, + "dewPoint":3.03, + "humidity":0.9, + "windSpeed":1.51, + "windBearing":311, + "cloudCover":0.96, + "pressure":1011.46, + "ozone":349.29 + } + ] + } +} + +https://api.forecast.io/forecast/b251335276b56396cc98b7c79b282971/51.3750768,9.9175961?units=si&exclude=daily,minutely,hourly,alerts,flags + +{ + "latitude":51.3750768, + "longitude":9.9175961, + "timezone":"Europe/Berlin", + "offset":1, + "currently": + { + "time":1426944648, + "summary":"Mostly Cloudy", + "icon":"partly-cloudy-day", + "precipIntensity":0.1245, + "precipProbability":0.1, + "precipType":"rain", + "temperature":6.17, + "apparentTemperature":4.28, + "dewPoint":4.68, + "humidity":0.9, + "windSpeed":2.51, + "windBearing":295, + "cloudCover":0.71, + "pressure":1015.34, + "ozone":448.84 + } +} diff --git a/updateChangeLog.sh b/updateChangeLog.sh new file mode 100755 index 0000000..de3266b --- /dev/null +++ b/updateChangeLog.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +COM_LAST=`head -1 ChangeLog | cut -d " " -f 2` + +echo "Downloading git log from commit $COM_LAST (excluded) to HEAD (included)" + +git log --decorate=no --date=rfc $COM_LAST...HEAD > ChangeLog.new + +cat "ChangeLog" >> "ChangeLog.new" + +mv "ChangeLog.new" "ChangeLog" diff --git a/zip-file/openweather.shell-extension.zip b/zip-file/openweather.shell-extension.zip new file mode 100644 index 0000000..8134683 Binary files /dev/null and b/zip-file/openweather.shell-extension.zip differ