Skip to content

Commit 782cf30

Browse files
committed
Added weather nodes
1 parent 16a52f4 commit 782cf30

File tree

3 files changed

+389
-0
lines changed

3 files changed

+389
-0
lines changed

weather/icons/weather.png

491 Bytes
Loading

weather/weather.html

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
<script type="text/x-red" data-template-name="openweathermap">
2+
<div class="form-row">
3+
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
4+
<input type="text" id="node-input-name" placeholder="Name">
5+
</div>
6+
<div class="form-row">
7+
<label for="weather-location-type-select"><i class="fa fa-globe"></i> Location</label>
8+
<select id="weather-location-type-select" style="width: 288px"><option value="city">City</option><option value="coordinates">Coordinates</option></select>
9+
</div>
10+
<div class="form-row weather-location-type" id="weather-location-type-city">
11+
<label for=""><i class="weather-location-type"></i> City</label>
12+
<input type="text" class="weather-location-input" style="width: 180px; margin-bottom:7px" id="node-input-city" placeholder="City"></input>
13+
</br>
14+
<label for=""><i class="weather-location-type"></i> Country</label>
15+
<input type="text" class="weather-location-input" style="width: 180px; margin-bottom:7px" id="node-input-country" placeholder="Country"></input>
16+
</div>
17+
<div class="form-row weather-location-type hidden" id="weather-location-type-coordinates">
18+
<label for=""><i class="weather-location-type"></i> Latitude</label><input type="text" class="weather-location-input" id="node-input-lat" style="width: 180px; margin-bottom:7px" placeholder="Latitude">
19+
</br>
20+
<label for=""><i class="weather-location-type"></i> Longitude</label><input type="text" class="weather-location-input" id="node-input-lon" style="width: 180px; margin-bottom:7px" placeholder="Longitude">
21+
</div>
22+
</script>
23+
24+
<script type="text/javascript">
25+
RED.nodes.registerType('openweathermap',{
26+
category: 'weather',
27+
color: '#FFCC66',
28+
defaults: {
29+
name: {value:""},
30+
lon: {value:"", validate:function(v) {return ((v>=-180) && (v<=180));} },
31+
lat: {value:"", validate:function(v) {return ((v>=-90) && (v<=90));} },
32+
city: {value:""},
33+
country: {value:""},
34+
},
35+
inputs:1,
36+
outputs:1,
37+
icon: "weather.png",
38+
label: function() {
39+
return this.name||"openweathermap";
40+
},
41+
oneditprepare: function() {
42+
$("#weather-location-type-select").change(function() {
43+
var id = $("#weather-location-type-select option:selected").val();
44+
$(".weather-location-type").hide();
45+
$("#weather-location-type-"+id).show();
46+
});
47+
if(this.lon !== ""||this.lat !== ""){
48+
$("#weather-location-type-select").val("coordinates");
49+
} else {
50+
$("#weather-location-type-select").val("city");
51+
}
52+
var id = $("#weather-location-type-select option:selected").val();
53+
$(".weather-location-type").hide();
54+
$("#weather-location-type-"+id).show();
55+
},
56+
oneditsave: function() {
57+
var type = $("#weather-location-type-select option:selected").val();
58+
if(type == "coordinates"){
59+
$("#node-input-city").val("");
60+
$("#node-input-country").val("");
61+
} else if (type == "city") {
62+
$("#node-input-lon").val("");
63+
$("#node-input-lat").val("");
64+
}
65+
},
66+
labelStyle: function() {
67+
return this.name?"node_label_italic":"";
68+
}
69+
});
70+
</script>
71+
72+
<script type="text/x-red" data-help-name="openweathermap">
73+
<p>A node which polls a server for current weather data when an input is received.</p>
74+
75+
<p> This is done using either:</p>
76+
<ul>
77+
<li> a city and the country in which that city lies</li>
78+
<li> a latitude and longitude set of coordinates </li>
79+
</ul>
80+
<p>These can be passed in as settings on the node, or as the:</p>
81+
<ul>
82+
<li> <b>msg.payload.lat</b> and <b>msg.payload.lon</b>, or </li>
83+
<li> <b>msg.payload.city</b> and <b>msg.payload.country</b> </li>
84+
</ul>
85+
<p>of the message input. </p>
86+
<p>The node will always prioritise the parameters passed in when both of <b>lat/lon</b> or <b>country/city</b> are present.</p>
87+
88+
<p>The node sets the following properties of <b>msg.payload</b>:</p>
89+
<ul>
90+
<li><b>description</b> - a brief verbal description of the current weather for human reading.</li><br>
91+
<li><b>weather</b> - a short description of the current weather.</li><br>
92+
<li><b>detail</b> - a more detailed expansion on <b>weather</b>.</li><br>
93+
<li><b>tempk</b> - the current gorund temperature at that location in Kelvin.</li><br>
94+
<li><b>humidity</b> - the current humidity at the location in percent.</li><br>
95+
<li><b>maxtemp</b> - the current maximum temperature at the location in Kelvin.</li><br>
96+
<li><b>mintemp</b> - the current minimum temperature at the location in Kelvin.</li><br>
97+
<li><b>windspeed</b> - the current wind speed at the location in Metres per second.</li><br>
98+
<li><b>winddirection</b> - the current wind direction at the location in meteorological degrees.</li><br>
99+
<li><b>town</b> - the name of the location from which the data was sourced.</li><br>
100+
<li><b>lon</b> - the longitude of the location from which the data was sourced.</li><br>
101+
<li><b>lat</b> - to the latitude of the location from which the data was sourced.</li><br>
102+
<li><b>sunrise</b> - the time at which the sun rose in Unix UTC format.</li><br>
103+
<li><b>sunset</b> - the time at which the sun will set in Unix UTC format.</li><br>
104+
<li><b>clouds</b> - the current cloud coverage of the location in percent.</li><br>
105+
</ul>
106+
<p>Weather data provided by <a href="http://openweathermap.org/" target="_blank">openweathermap.org/</a></p>
107+
</script>
108+
109+
110+
111+
<script type="text/x-red" data-template-name="openweathermap in">
112+
<div class="form-row">
113+
<label for="node-input-name"><i class="icon-tag"></i> Name</label>
114+
<input type="text" id="node-input-name" placeholder="Name">
115+
</div>
116+
<div class="form-row">
117+
<label for="weather-location-type-select"><i class="fa fa-globe"></i> Location</label>
118+
<select id="weather-location-type-select" style="width: 288px"><option value="city">City</option><option value="coordinates">Coordinates</option></select>
119+
</div>
120+
<div class="form-row weather-location-type" id="weather-location-type-city">
121+
<label for=""><i class="weather-location-type"></i> City</label>
122+
<input type="text" class="weather-location-input" style="width: 180px; margin-bottom:7px" id="node-input-city" placeholder="City"></input>
123+
</br>
124+
<label for=""><i class="weather-location-type"></i> Country</label>
125+
<input type="text" class="weather-location-input" style="width: 180px; margin-bottom:7px" id="node-input-country" placeholder="Country"></input>
126+
</div>
127+
<div class="form-row weather-location-type hidden" id="weather-location-type-coordinates">
128+
<label for=""><i class="weather-location-type"></i> Latitude</label><input type="text" class="weather-location-input" id="node-input-lat" style="width: 180px; margin-bottom:7px" placeholder="Latitude">
129+
</br>
130+
<label for=""><i class="weather-location-type"></i> Longitude</label><input type="text" class="weather-location-input" id="node-input-lon" style="width: 180px; margin-bottom:7px" placeholder="Longitude">
131+
</div>
132+
</script>
133+
134+
<script type="text/javascript">
135+
RED.nodes.registerType('openweathermap in',{
136+
category: 'weather',
137+
color: '#FFCC66',
138+
defaults: {
139+
name: {value:""},
140+
lon: {value:"", validate:function(v) {return ((v>=-180) && (v<=180));} },
141+
lat: {value:"", validate:function(v) {return ((v>=-90) && (v<=90));} },
142+
city: {value:""},
143+
country: {value:""},
144+
},
145+
inputs:0,
146+
outputs:1,
147+
icon: "weather.png",
148+
label: function() {
149+
return this.name||"openweathermap";
150+
},
151+
oneditprepare: function() {
152+
$("#weather-location-type-select").change(function() {
153+
var id = $("#weather-location-type-select option:selected").val();
154+
$(".weather-location-type").hide();
155+
$("#weather-location-type-"+id).show();
156+
});
157+
if(this.lon !== ""||this.lat !== ""){
158+
$("#weather-location-type-select").val("coordinates");
159+
} else {
160+
$("#weather-location-type-select").val("city");
161+
}
162+
var id = $("#weather-location-type-select option:selected").val();
163+
$(".weather-location-type").hide();
164+
$("#weather-location-type-"+id).show();
165+
166+
167+
},
168+
oneditsave: function() {
169+
var type = $("#weather-location-type-select option:selected").val();
170+
if(type == "coordinates"){
171+
$("#node-input-city").val("");
172+
$("#node-input-country").val("");
173+
} else if (type == "city") {
174+
$("#node-input-lon").val("");
175+
$("#node-input-lat").val("");
176+
}
177+
},
178+
labelStyle: function() {
179+
return this.name?"node_label_italic":"";
180+
}
181+
});
182+
</script>
183+
184+
<script type="text/x-red" data-help-name="openweathermap in">
185+
<p>A node which polls a server for current weather data periodically and returns when a change is detected.</p>
186+
187+
<p> This is done using either:</p>
188+
<ul>
189+
<li> a city and the country in which that city lies</li>
190+
<li> a latitude and longitude set of coordinates </li>
191+
</ul>
192+
193+
<p>The node sets the following properties of <b>msg.payload</b>:</p>
194+
<ul>
195+
<li><b>description</b> - a brief verbal description of the current weather for human reading.</li><br>
196+
<li><b>weather</b> - a short description of the current weather.</li><br>
197+
<li><b>detail</b> - a more detailed expansion on <b>weather</b>.</li><br>
198+
<li><b>tempk</b> - the current gorund temperature at that location in Kelvin.</li><br>
199+
<li><b>humidity</b> - the current humidity at the location in percent.</li><br>
200+
<li><b>maxtemp</b> - the current maximum temperature at the location in Kelvin.</li><br>
201+
<li><b>mintemp</b> - the current minimum temperature at the location in Kelvin.</li><br>
202+
<li><b>windspeed</b> - the current wind speed at the location in Metres per second.</li><br>
203+
<li><b>winddirection</b> - the current wind direction at the location in meteorological degrees.</li><br>
204+
<li><b>town</b> - the name of the location from which the data was sourced.</li><br>
205+
<li><b>lon</b> - the longitude of the location from which the data was sourced.</li><br>
206+
<li><b>lat</b> - to the latitude of the location from which the data was sourced.</li><br>
207+
<li><b>sunrise</b> - the time at which the sun rose in Unix UTC format.</li><br>
208+
<li><b>sunset</b> - the time at which the sun will set in Unix UTC format.</li><br>
209+
<li><b>clouds</b> - the current cloud coverage of the location in percent.</li><br>
210+
</ul>
211+
<p>Weather data provided by <a href="http://openweathermap.org/" target="_blank">openweathermap.org/</a></p>
212+
</script>
213+
214+
<style>
215+
.weather-location-type {
216+
padding-left: 110px;
217+
}
218+
219+
.weather-data-credit {
220+
font-size: 70%;
221+
font-color: gray;
222+
text-align: right;
223+
}
224+
225+
.weather-location-input{
226+
margin-bottom: 10px;
227+
}
228+
</style>

weather/weather.js

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
module.exports = function(RED) {
2+
"use strict";
3+
var http = require("http");
4+
5+
function weatherPoll(node, n, msg, callback) {
6+
var url;
7+
var lat;
8+
var lon;
9+
var city;
10+
var country;
11+
12+
if (n.city !== "" || n.country !== "") {
13+
city = n.city;
14+
country = n.country;
15+
} else if (n.lat !== "" || n.lon !== "") {
16+
lat = n.lat;
17+
lon = n.lon;
18+
}
19+
20+
//if there is data in the message input, it overwrites the node setting values.
21+
//If the data is erroneous or not there, the values remain the node settings.
22+
if(msg.payload){
23+
//query node code to check the input for information.
24+
if (msg.payload.city && msg.payload.country) {
25+
city = msg.payload.city;
26+
country = msg.payload.country;
27+
lat = "";
28+
lon = "";
29+
}
30+
if (msg.payload.lat && msg.payload.lon){
31+
if(90 >= msg.payload.lat && 180 >= msg.payload.lon && msg.payload.lat >= -90 && msg.payload.lon >= -180){
32+
lat = msg.payload.lat;
33+
lon = msg.payload.lon;
34+
city = "";
35+
country = "";
36+
} else {
37+
node.warn("Invalid lat/lon in input payload");
38+
}
39+
}
40+
}
41+
//wipe clear the payload if it exists, or create it if it doesn't
42+
msg.payload = {};
43+
44+
//If there is a value missing, the URL is not initialised.
45+
if (lat && lon){
46+
url = "http://api.openweathermap.org/data/2.5/weather?lat=" + lat + "&lon=" + lon;
47+
} else if (city && country) {
48+
url = "http://api.openweathermap.org/data/2.5/weather?q=" + city + "," + country;
49+
}
50+
51+
//If the URL is not initialised, there has been an error with the input data,
52+
//and a node.error is reported.
53+
if(url){
54+
http.get(url, function(res) {
55+
var weather = "";
56+
57+
res.on('data', function(d) {
58+
weather += d;
59+
});
60+
61+
res.on('end', function() {
62+
var jsun = JSON.parse(weather);
63+
if(jsun.weather){
64+
msg.payload.weather = jsun.weather[0].main;
65+
msg.payload.detail = jsun.weather[0].description;
66+
msg.payload.tempk = jsun.main.temp;
67+
msg.payload.humidity = jsun.main.humidity;
68+
msg.payload.maxtemp = jsun.main.temp_max;
69+
msg.payload.mintemp = jsun.main.temp_min;
70+
msg.payload.windspeed = jsun.wind.speed;
71+
msg.payload.winddirection = jsun.wind.deg;
72+
msg.payload.location = jsun.name;
73+
msg.payload.lon = jsun.coord.lon;
74+
msg.payload.lat = jsun.coord.lat;
75+
msg.payload.sunrise = jsun.sys.sunrise;
76+
msg.payload.sunset = jsun.sys.sunset;
77+
msg.payload.clouds = jsun.clouds.all;
78+
msg.payload.description = ("The weather in " + jsun.name + " at coordinates: " + jsun.coord.lat + ", " + jsun.coord.lon + " is " + jsun.weather[0].main + " (" + jsun.weather[0].description + ")." );
79+
callback();
80+
} else {
81+
if (jsun.message === "Not found city"){
82+
if (n.city && n.country && country != n.country && city != n.city){
83+
node.warn("Invalid city/country in input payload, trying node city/country");
84+
msg.payload.country = n.country;
85+
msg.payload.city = n.city;
86+
weatherPoll(node, n, msg, function(){
87+
node.send(msg);
88+
});
89+
} else if (n.lat && n.lon) {
90+
node.warn("Invalid city/country in input payload, trying node lat/lon");
91+
msg.payload.lat = n.lat;
92+
msg.payload.lon = n.lon;
93+
weatherPoll(node, n, msg, function(){
94+
node.send(msg);
95+
});
96+
} else {
97+
if(!n.city && !n.country){
98+
node.error("Invalid city/country in input payload");
99+
} else {
100+
node.error("Invalid city/country in node settings");
101+
}
102+
}
103+
} else {
104+
node.error(jsun.cod + " " + jsun.message);
105+
}
106+
}
107+
});
108+
}).on('error', function(e) {
109+
node.error(e);
110+
});
111+
} else {
112+
node.error("Invalid location information provided");
113+
}
114+
}
115+
116+
function OpenWeatherMapInputNode(n) {
117+
RED.nodes.createNode(this, n);
118+
var node = this;
119+
this.repeat = 300000;
120+
this.interval_id = null;
121+
var previousdata = null;
122+
123+
this.interval_id = setInterval( function() {
124+
node.emit("input",{});
125+
}, this.repeat );
126+
127+
this.on('input', function(msg) {
128+
weatherPoll(node, n, msg, function(){
129+
var msgString = JSON.stringify(msg);
130+
if(msgString !== previousdata){
131+
previousdata = msgString;
132+
node.send(msg);
133+
}
134+
135+
});
136+
});
137+
138+
this.on("close", function() {
139+
if (this.interval_id !== null) {
140+
clearInterval(this.interval_id);
141+
}
142+
});
143+
144+
node.emit("input",{});
145+
}
146+
147+
function OpenWeatherMapQueryNode(n) {
148+
RED.nodes.createNode(this,n);
149+
var node = this;
150+
151+
this.on ('input', function(msg) {
152+
weatherPoll(node, n, msg, function(){
153+
node.send(msg);
154+
});
155+
});
156+
}
157+
158+
RED.nodes.registerType("openweathermap",OpenWeatherMapQueryNode);
159+
RED.nodes.registerType("openweathermap in",OpenWeatherMapInputNode);
160+
161+
};

0 commit comments

Comments
 (0)