Skip to content

Commit ab90063

Browse files
docs(weather): update provider API docs (#381)
Weather providers now run server-side in v2.35.0, so the old WeatherProvider.register() docs no longer apply. Add the new provider class shape, lifecycle callbacks, HTTPFetcher usage, migration notes for old providers, and updated WeatherObject fields. Refs #380
1 parent 22234aa commit ab90063

2 files changed

Lines changed: 171 additions & 97 deletions

File tree

cspell.config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"updatenotification",
7777
"verjaardag",
7878
"weatherapi",
79+
"weatherprovider",
7980
"Weatherbit",
8081
"Weatherflow",
8182
"weathergov",

module-development/weather-provider.md

Lines changed: 170 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,92 @@
33
This document describes the way to develop your own MagicMirror² weather
44
provider for the weather module.
55

6-
## The weather provider file: yourprovider.js
6+
::: warning BREAKING CHANGE in v2.35.0
77

8-
This is the script in which the weather provider will be defined. In its most
9-
simple form, the weather provider must implement the following:
8+
Weather providers now run **server-side** as Node.js classes. The old
9+
client-side `WeatherProvider.register("name", { ... })` API was removed. Custom
10+
providers from earlier versions must be updated for the new structure.
11+
12+
:::
13+
14+
## The weather provider file: `yourprovider.js`
15+
16+
This is the script in which the weather provider will be defined. The file must
17+
be placed in MagicMirror's built-in weather provider directory:
18+
19+
```bash
20+
~/MagicMirror/defaultmodules/weather/providers/yourprovider.js
21+
```
22+
23+
The filename (lowercased) must match the `weatherProvider` value in `config.js`.
1024

1125
```js
12-
WeatherProvider.register("yourprovider", {
13-
providerName: "YourProvider",
26+
{
27+
module: "weather",
28+
config: {
29+
weatherProvider: "yourprovider",
30+
type: "current" // or "forecast" / "hourly"
31+
}
32+
}
33+
```
1434

15-
fetchCurrentWeather() {},
35+
This directory is part of the MagicMirror git repository. A custom file placed
36+
here may conflict with future updates, so keep a copy outside the repository
37+
before running `git pull`. You can also tell git not to track local changes to
38+
that file:
1639

17-
fetchWeatherForecast() {},
18-
});
40+
```bash
41+
git -C ~/MagicMirror update-index --assume-unchanged \
42+
defaultmodules/weather/providers/yourprovider.js
43+
```
44+
45+
In its most simple form, the weather provider must implement the following:
46+
47+
```js
48+
const HTTPFetcher = require("#http_fetcher");
49+
50+
class YourProvider {
51+
constructor(config) {
52+
this.config = config;
53+
this.locationName = null;
54+
this.fetcher = null;
55+
this.onDataCallback = null;
56+
this.onErrorCallback = null;
57+
}
58+
59+
setCallbacks(onData, onError) {
60+
this.onDataCallback = onData;
61+
this.onErrorCallback = onError;
62+
}
63+
64+
initialize() {
65+
this.fetcher = new HTTPFetcher("https://your.api/endpoint", {
66+
reloadInterval: this.config.updateInterval,
67+
logContext: "weatherprovider.yourprovider",
68+
});
69+
70+
this.fetcher.on("response", async (response) => {
71+
const data = await response.json();
72+
this.onDataCallback(this.parseWeather(data));
73+
});
74+
75+
this.fetcher.on("error", (errorInfo) => this.onErrorCallback(errorInfo));
76+
}
77+
78+
start() {
79+
this.fetcher?.startPeriodicFetch();
80+
}
81+
82+
stop() {
83+
this.fetcher?.clearTimer();
84+
}
85+
86+
parseWeather(data) {
87+
return { temperature: data.temp };
88+
}
89+
}
90+
91+
module.exports = YourProvider;
1992
```
2093
2194
## Weather provider methods to implement
@@ -29,121 +102,114 @@ The weather module expects the weather data to be in metric units:
29102
30103
Some weather APIs already deliver their data in those units.
31104
32-
If that is not the case you can use helper methods from the `weatherutils.js`
33-
class to convert the data.
105+
If that is not the case, convert the values before sending them to the weather
106+
module.
34107
35108
:::
36109
37-
### `fetchCurrentWeather()`
38-
39-
This method is called when the weather module tries to fetch the current weather
40-
of your provider. The implementation of this method is required for current
41-
weather support. The implementation can make use of the already implemented
42-
function `this.fetchData(url, method, data);`, which is returning a promise.
43-
After the response is processed, the current weather information (as a
44-
[WeatherObject](#weatherobject)) needs to be set with
45-
`this.setCurrentWeather(currentWeather);`. It will then automatically refresh
46-
the module DOM with the new data.
47-
48-
### `fetchWeatherForecast()`
49-
50-
This method is called when the weather module tries to fetch the weather of your
51-
provider. The implementation of this method is required for forecast support.
52-
The implementation can make use of the already implemented function
53-
`this.fetchData(url, method, data);`, which is returning a promise. After the
54-
response is processed, the weather forecast information (as an array of
55-
[WeatherObject](#weatherobject)s) needs to be set with
56-
`this.setWeatherForecast(forecast);`. It will then automatically refresh the
57-
module DOM with the new data.
58-
59-
### `fetchWeatherHourly()`
60-
61-
This method is called when the weather module tries to fetch the weather of your
62-
provider. The implementation of this method is required for hourly support. The
63-
implementation can make use of the already implemented function
64-
`this.fetchData(url, method, data);`, which is returning a promise. After the
65-
response is processed, the hourly weather forecast information (as an array of
66-
[WeatherObject](#weatherobject)s) needs to be set with
67-
`this.setWeatherHourly(forecast);`. It will then automatically refresh the
68-
module DOM with the new data.
110+
### `constructor(config)`
69111

70-
## Weather Provider instance methods
112+
This method receives the full weather module config. Store it as `this.config`
113+
and initialize the provider state here. The optional `locationName` property is
114+
shown in the module header.
71115

72-
### `init()`
116+
### `setCallbacks(onData, onError)`
73117

74-
Called when a weather provider is initialized.
118+
This method is called by the `node_helper` before `initialize()`. Store both
119+
callbacks and call `onData(weatherData)` whenever new weather data is available.
120+
Call `onError({ message, translationKey })` when something goes wrong.
75121
76-
### `setConfig(config)`
122+
### `initialize()`
77123
78-
Called to set the config, this config is the same as the weather module's
79-
config.
124+
This method is called once when the provider is loaded. It may be `async`.
80125
81-
### `start()`
126+
Validate required options such as API keys, coordinates, or provider-specific
127+
location IDs here. If required configuration is missing, call `onErrorCallback`
128+
with a useful message and return early — without creating the fetcher.
82129
83-
Called when the weather provider is about to start.
130+
If validation passes, delegate the fetcher setup to a private helper (e.g.
131+
`#initializeFetcher()`). Keeping these two concerns separate makes the code
132+
easier to follow. You can also resolve `this.locationName` here if your provider
133+
needs an extra lookup before starting to fetch.
84134
85-
#### `currentWeather()`
135+
If `initialize()` throws, provider startup fails and the module reports an
136+
error. For API errors, call `onErrorCallback` with a useful message instead of
137+
throwing from setup code.
86138
87-
This returns a WeatherDay object for the current weather.
88-
89-
### `weatherForecast()`
90-
91-
This returns an array of WeatherDay objects for the weather forecast.
92-
93-
### `weatherHourly()`
94-
95-
This returns an array of WeatherDay objects for the hourly weather forecast.
96-
97-
### `fetchedLocation()`
139+
### `start()`
98140
99-
This returns the name of the fetched location or an empty string.
141+
This method is called when the weather provider is about to start. Usually this
142+
starts periodic fetching:
100143
101-
### `setCurrentWeather(currentWeatherObject)`
144+
```js
145+
start() {
146+
this.fetcher?.startPeriodicFetch();
147+
}
148+
```
102149
103-
Set the currentWeather and notify the delegate that new information is
104-
available.
150+
### `stop()`
105151
106-
### `setWeatherForecast(weatherForecastArray)`
152+
This method is called when the weather provider is stopped. Cancel timers or
153+
other open resources here:
107154
108-
Set the weatherForecastArray and notify the delegate that new information is
109-
available.
155+
```js
156+
stop() {
157+
this.fetcher?.clearTimer();
158+
}
159+
```
110160
111-
### `setWeatherHourly(weatherHourlyArray)`
161+
### Parsing weather data
112162
113-
Set the weatherHourlyArray and notify the delegate that new information is
114-
available.
163+
Process the API response in your `HTTPFetcher` response handler and pass the
164+
result to `onDataCallback`. Return a plain JavaScript object for
165+
`type: "current"`, or an array of objects for `type: "forecast"` and
166+
`type: "hourly"`.
115167
116-
### `setFetchedLocation(name)`
168+
Wrap `response.json()` and your parsing logic in `try`/`catch` so invalid JSON
169+
or unexpected API response shapes can be reported through `onErrorCallback`
170+
instead of failing silently.
117171
118-
Set the fetched location name.
172+
`HTTPFetcher` (`require("#http_fetcher")`) handles periodic fetching with retry
173+
and backoff. See
174+
[`js/http_fetcher.js`](https://github.com/MagicMirrorOrg/MagicMirror/blob/master/js/http_fetcher.js)
175+
for all options. Common `translationKey` values: `MODULE_ERROR_UNAUTHORIZED`,
176+
`MODULE_ERROR_RATE_LIMITED`, `MODULE_ERROR_SERVER_ERROR`,
177+
`MODULE_ERROR_NO_CONNECTION`, `MODULE_ERROR_UNSPECIFIED`.
119178
120-
### `updateAvailable()`
179+
## Weather Provider instance methods
121180
122-
Notify the delegate that new weather is available.
181+
The `node_helper` controls the provider lifecycle in this order:
123182
124-
### `fetchData(url, method, data)`
183+
1. `setCallbacks(onData, onError)`
184+
2. `initialize()`
185+
3. `start()`
186+
4. `stop()` when the module is stopped
125187
126-
A convenience function to make requests. It returns a promise.
188+
These replace the old client-side provider methods such as
189+
`fetchCurrentWeather()`, `fetchWeatherForecast()`, `setCurrentWeather()`, and
190+
`updateAvailable()`.
127191
128192
## WeatherObject
129193
130-
This object holds all data from your provider for usage in the template.
131-
132-
| Property | Type | Value/Unit |
133-
| -------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
134-
| date | `object` | [Moment.js](https://momentjs.com/) object of the time/date. |
135-
| windSpeed | `number` | Speed of the wind in metric: `meter/second` |
136-
| windDirection | `number` | Direction of the wind in degrees. |
137-
| sunrise | `object` | [Moment.js](https://momentjs.com/) object of sunrise. |
138-
| sunset | `object` | [Moment.js](https://momentjs.com/) object of sunset. |
139-
| temperature | `number` | Current temperature in metric `celsius degree`. |
140-
| minTemperature | `number` | Lowest temperature of the day in metric `celsius degree`. |
141-
| maxTemperature | `number` | Highest temperature of the day in metric `celsius degree`. |
142-
| weatherType | `string` | Icon name of the weather type. <br> Possible values: [WeatherIcons](https://www.npmjs.com/package/weathericons) |
143-
| humidity | `number` | Percentage of humidity |
144-
| rain | `number` | Metric: `millimeters` <br> Imperial: `inches` |
145-
| snow | `number` | Metric: `millimeters` <br> Imperial: `inches` |
146-
| precipitation | `number` | Metric: `millimeters` <br> Imperial: `inches` <br> UK Met Office provider: `percent` |
194+
This object holds all data from your provider for usage in the template. A
195+
server-side provider returns plain objects; the weather module converts them to
196+
`WeatherObject` instances on the client.
197+
198+
| Property | Type | Value/Unit |
199+
| ------------------------ | -------- | --------------------------------------------------------------------------------------------------------------- |
200+
| date | `Date` | JavaScript `Date` object of the time/date. |
201+
| windSpeed | `number` | Speed of the wind in metric: `meter/second` |
202+
| windFromDirection | `number` | Direction of the wind in degrees. |
203+
| sunrise | `Date` | JavaScript `Date` object of sunrise. |
204+
| sunset | `Date` | JavaScript `Date` object of sunset. |
205+
| temperature | `number` | Current temperature in metric `celsius degree`. |
206+
| minTemperature | `number` | Lowest temperature of the day in metric `celsius degree`. |
207+
| maxTemperature | `number` | Highest temperature of the day in metric `celsius degree`. |
208+
| weatherType | `string` | Icon name of the weather type. <br> Possible values: [WeatherIcons](https://www.npmjs.com/package/weathericons) |
209+
| humidity | `number` | Percentage of humidity |
210+
| precipitationAmount | `number` | Metric: `millimeters` |
211+
| precipitationUnits | `string` | Optional precipitation unit override |
212+
| precipitationProbability | `number` | Precipitation probability as percentage |
147213
148214
### Current weather
149215
@@ -154,7 +220,7 @@ For the current weather object the following properties are required:
154220
- sunset
155221
- temperature
156222
- weatherType
157-
- windDirection
223+
- windFromDirection
158224
- windSpeed
159225
160226
### Weather forecast
@@ -164,5 +230,12 @@ For the forecast weather object the following properties are required:
164230
- date
165231
- maxTemperature
166232
- minTemperature
167-
- rain
233+
- precipitationAmount
168234
- weatherType
235+
236+
If your API does not provide one of these values, use a sensible fallback where
237+
possible. The important part is that the weather module receives the fields it
238+
expects for the selected `type`.
239+
240+
For real examples, look at the built-in providers in
241+
[`defaultmodules/weather/providers/`](https://github.com/MagicMirrorOrg/MagicMirror/tree/master/defaultmodules/weather/providers).

0 commit comments

Comments
 (0)