Another day, another weather service down. Here are the steps to fix broken weather skins and migrate to different weather service APIs.
Note: This is not a full tutorial on how to fix weather skins, but rather to give you an idea on what needs to be done. I'll only be showing basic regex techniques to substitute the new data.
TL;DR: It's tedious to fix a weather skin unless it's a really simple one.
Update 5/6/2020: Before getting technical below, you might be interested in some of the threads from the Rainmeter forum.
- Weather Skins: List of currently functioning weather skins.
- Weather.com - Parsing the JSON: Use data from weather.com. No sign up required.
- Weather.com JSON not loading correctly: In case this happens.
Before we get started, here's what you need to know about what we'll be doing in the next few minutes.
We will start by looking for a new weather API. It could be any weather API as long as it provides the information you need and of course, a reasonable request rate.
Next, we will change to the skin's API endpoint and apply a new regex to it. (Don't worry, I'll guide you through it)
And finally, I'll list what else that needs to be done because different skins have different ways of handling and displaying weather.
Update 12/6/2020: Dark Sky have been acquired by Apple and is shuting down its services by 1 July 2020 :(
Most weather API services require sign ups to acquire an API key, which is totally fair to prevent DDOS-ing on their servers.
Once you acquired your API key, hop on to the next step!
Note: DO NOT share your API key. Someone who has your API key may randomly use it and easily hit the API request limit.
The instructions below will be skin agnostic, but since different weather skins may need different data, you may need to do some extra tweaks.
For the Mond weather skin, in
Weather.ini, here's the issue (Most other weather skins has this too):
[MeasureCurrent] Measure=Plugin Plugin=WebParser.dll UpdateRate=900 Url=http://wxdata.weather.com/wxdata/weather/local/#Location#?cc=*&unit=#Unit# RegExp="(?siU)<head>.*<ut>(.*)</ut>.*<dnam>(.*),.*</dnam>.*<tmp>(.*)</tmp>.*<t>(.*)</t>.*<icon>(.*)</icon>"
Every weather skin will almost always pull data through the WebParser measure. The
Url key is our API endpoint,
RegExp is our regex to be match on our response data, which may be in the form of JSON or XML (Depends on what the service provides). Learn more about WebParser in the official tutorial.
Looking at the code, the url
wxdata.weather.com is deprecated, so we need to change that. Let's change it to OpenWeatherMap's API. Here's what it should look like:
[MeasureCurrent] Measure=Plugin Plugin=WebParser.dll UpdateRate=900 Url=https://api.openweathermap.org/data/2.5/weather?appid=<your-api-key>&id=#Location#&units=#Unit#&mode=xmlRegExp="(?siU)<head>.*<ut>(.*)</ut>.*<dnam>(.*),.*</dnam>.*<tmp>(.*)</tmp>.*<t>(.*)</t>.*<icon>(.*)</icon>"
<your-api-key> with your API key (Make sure to remove it before sharing your code to others). As you can see, I've reused the variables for the location id and temperature unit as the API parameters. Learn more about Rainmeter's variables here and OpenWeatherMap's API here.
Also note I'm using XML format as denoted at
mode=xml in the
Now we have reached the real deal, the almighty Regex battlefield. Take a deep breath and let's dive in.
Before we start, let's take a look at what data Mond needs. Below are the measure Mond has that retrieves data from the WebParser's regex:
[MeasureTempUnit] Measure=Plugin Plugin=WebParser.dll Url=[MeasureCurrent] StringIndex=1 [MeasureLocation] Measure=Plugin Plugin=WebParser.dll Url=[MeasureCurrent] StringIndex=2 [MeasureWeatherTemp] Measure=Plugin Plugin=WebParser.dll Url=[MeasureCurrent] StringIndex=3 [MeasureWeatherCond] Measure=Plugin Plugin=WebParser.dll Url=[MeasureCurrent] StringIndex=4 Substitute=#Conditions# [MeasureWeatherIcons] Measure=Plugin Plugin=WebParser.dll Url=[MeasureCurrent] StringIndex=5
Mond has 5 data: temperature unit, location, temperature, weather condition and icons.
Now let's inspect our XML data and see how we can capture them. Below is an example response:
<current> <city id="2643743" name="London"> <coord lon="-0.13" lat="51.51"/> <country>GB</country> <sun rise="2017-01-30T07:40:36" set="2017-01-30T16:47:56"/> </city> <temperature value="7" min="5" max="8" unit="metric"/> <humidity value="81" unit="%"/> <pressure value="1012" unit="hPa"/> <wind> <speed value="4.6" name="Gentle Breeze"/> <gusts/> <direction value="90" code="E" name="East"/> </wind> <clouds value="90" name="overcast clouds"/> <visibility value="10000"/> <precipitation mode="no"/> <weather number="701" value="mist" icon="50d"/> <lastupdate value="2017-01-30T15:50:00"/> </current>
From the XML, here are the values we need to get:
- Temperature unit:
- Weather condition:
So our regex will be like this:
Good lord what is this!
First, we declare
(?siU), which is the option modifier for the regex (More info here).
Next, take a look at
<city.*name="(.*)". It kinda looks like the
<city> tag in the original XML, but what's
.*? It consists of two characters:
. means "any character",
* means "of any length", so combined it means "match any character of any length".
We also have the parentheses
(.*), and the parentheses is called a capture group that will be captured and return so we can use the data.
So what this whole thing means is that we match from
<city, and then
.* for any character of any length, until
name=", and then match
(.*) for any character of any length again but this time capturing it too, and finally it'll will match until it reaches
The rest are also of the same form as
And lastly, remember the 5 measures that Mond uses to get the regex's data, we have to tweak a bit since the capturing order changed.
StringIndex is the parentheses number in the
RegExp. Notice we have 5
(.*), those are the capturing groups that the 5 measures will have to index, with the first
StringIndex=1 and so on.
Finally the code should be like this:
[MeasureCurrent] Measure=Plugin Plugin=WebParser.dll UpdateRate=900 Url=https://api.openweathermap.org/data/2.5/weather?appid=<your-api-key>&id=#Location#&units=#Unit#&mode=xml RegExp=(?siU)<city.*name="(.*)".*<temperature.*value="(.*)".*unit="(.*)".*<weather .*value="(.*)".*icon="(.*)" [MeasureTempUnit] Measure=Plugin Plugin=WebParser.dll Url=[MeasureCurrent] StringIndex=3 [MeasureLocation] Measure=Plugin Plugin=WebParser.dll Url=[MeasureCurrent] StringIndex=1 [MeasureWeatherTemp] Measure=Plugin Plugin=WebParser.dll Url=[MeasureCurrent] StringIndex=2 [MeasureWeatherCond] Measure=Plugin Plugin=WebParser.dll Url=[MeasureCurrent] StringIndex=4Substitute=#Conditions# [MeasureWeatherIcons] Measure=Plugin Plugin=WebParser.dll Url=[MeasureCurrent] StringIndex=5
After all the hard work, are we done? Yes, and no. We fixed the WebParser, provided correct data, but it's not a perfect skin yet.
We still have these TODOs:
- Change variable
#Unit#settings to accept
imperialso it fits the APIs format.
- Change variable
#Location#to accept OpenWeatherMap's id format.
- OpenWeatherMap's icon value is different from
wxdata's, so since the Mond weather icons are named, 1.png..., we have to rename to OpenWeatherMap's format.
Note: I did not actually fix the Mond weather skin. It's still broken at its current state. RIP DeviantArt comments.
And there's that. A lot of work has to be done depending on how complex the skin is. But this should give you an idea on what needs to be done to migrate a skin to a new API.