Great work to everyone on this. Given I’m running docker and the the python command line was problematic , I converted the python to a standard OH rule and made a few tweaks.
Here’s my OH rule:
import java.io.InputStreamReader;
import java.nio.CharBuffer;
import java.net.URL;
import java.lang.StringBuilder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
rule "Get weather forecast data from BOM"
when
Time cron "0 */5 * ? * *" // update every 5 minutes
then
logInfo("BOM data","reading forecast data")
val URL url = new URL("ftp://ftp.bom.gov.au/anon/gen/fwo/IDN10035.xml");
val InputStreamReader instream = new InputStreamReader(url.openStream())
var StringBuilder sb = new StringBuilder();
var CharBuffer cb = CharBuffer.allocate(1024);
var int read = instream.read ( cb );
while(read != -1) {
sb.append(cb.array(), 0, read )
cb.clear
read = instream.read( cb )
}
instream.close();
val String bom_data = sb.toString();
for(var int i=0; i<7; i++)
{
logInfo("BOM data","day index="+i)
val String start_time = transform ("XPATH", "//forecast/area[@aac='NSW_PT027']/forecast-period[@index='"+i+"']/@start-time-local", bom_data)
logInfo("BOM data","start_time="+start_time)
postUpdate("BOM_Date_"+i, start_time)
val String air_temperature_maximum = transform ("XPATH", "//forecast/area[@aac='NSW_PT027']/forecast-period[@index='"+i+"']/element[@type='air_temperature_maximum']/text()", bom_data)
logInfo("BOM data","air_temperature_maximum="+air_temperature_maximum)
postUpdate("BOM_Temp_Max_"+i, air_temperature_maximum)
val String air_temperature_minimum = transform ("XPATH", "//forecast/area[@aac='NSW_PT027']/forecast-period[@index='"+i+"']/element[@type='air_temperature_minimum']/text()", bom_data)
logInfo("BOM data","air_temperature_minimum="+air_temperature_minimum)
postUpdate("BOM_Temp_Min_"+i, air_temperature_minimum)
val String precipitation_range = transform ("XPATH", "//forecast/area[@aac='NSW_PT027']/forecast-period[@index='"+i+"']/element[@type='precipitation_range']/text()", bom_data)
logInfo("BOM data","precipitation_range="+precipitation_range)
postUpdate("BOM_Precipitation_Range_"+i, precipitation_range)
val String forecast_icon_code = transform ("XPATH", "//forecast/area[@aac='NSW_PT027']/forecast-period[@index='"+i+"']/element[@type='forecast_icon_code']/text()", bom_data)
logInfo("BOM data","forecast_icon_code="+forecast_icon_code)
postUpdate("BOM_Forecast_Icon_Code_"+i, forecast_icon_code)
var String precis = transform ("XPATH", "//forecast/area[@aac='NSW_PT027']/forecast-period[@index='"+i+"']/text[@type='precis']/text()", bom_data)
logInfo("BOM data","precis="+precis)
postUpdate("BOM_Precis_"+i, precis)
val String probability_of_precipitation = transform ("XPATH", "//forecast/area[@aac='NSW_PT027']/forecast-period[@index='"+i+"']/text[@type='probability_of_precipitation']/text()", bom_data)
logInfo("BOM data","probability_of_precipitation="+probability_of_precipitation)
postUpdate("BOM_Precipitation_"+i, probability_of_precipitation)
val String forecast = transform ("XPATH", "//forecast/area[@aac='NSW_ME006']/forecast-period[@index='"+i+"']/text[@type='forecast']/text()", bom_data)
logInfo("BOM data","forecast="+forecast)
postUpdate("BOM_Forecast_"+i, forecast)
var String wind_range = "";
var Pattern pattern = Pattern.compile("([0-9]+ to [0-9]+ km/h)");
var Matcher matcher = pattern.matcher(forecast);
if (matcher.find())
{
wind_range = matcher.group(1);
}
else if (forecast.indexOf("Light winds")>=0)
{
wind_range = "Light winds"
}
logInfo("BOM data","wind_range_"+i+"="+wind_range)
postUpdate("BOM_Wind_Range_"+i, wind_range)
}
end
rule "Get weather observation data from BOM"
when
Time cron "0 */5 * ? * *" // update every 5 minutes
then
logInfo("BOM data","reading observation data")
val URL url = new URL("http://www.bom.gov.au/fwo/IDN60903/IDN60903.94926.json");
val InputStreamReader instream = new InputStreamReader(url.openStream())
var StringBuilder sb = new StringBuilder();
var CharBuffer cb = CharBuffer.allocate(1024);
var int read = instream.read ( cb );
while(read != -1) {
sb.append(cb.array(), 0, read )
cb.clear
read = instream.read( cb )
}
instream.close();
val String bom_data = sb.toString();
var String time = transform("JSONPATH", "$.observations.data[0].local_date_time", bom_data)
time = time.substring(time.indexOf('/')+1,time.length())
logInfo("BOM data","observation_time="+time)
postUpdate("BOM_Observation_Time", time)
val air_temp = transform("JSONPATH", "$.observations.data[0].air_temp", bom_data)
logInfo("BOM data","air_temp="+air_temp)
postUpdate("BOM_AirTemp", air_temp)
val apparent_t = transform("JSONPATH", "$.observations.data[0].apparent_t", bom_data)
logInfo("BOM data","apparent_t="+apparent_t)
postUpdate("BOM_ApparentTemp", apparent_t)
val wind_dir = transform("JSONPATH", "$.observations.data[0].wind_dir", bom_data)
logInfo("BOM data","wind_dir="+wind_dir)
postUpdate("BOM_WindDir", wind_dir)
val wind_spd = transform("JSONPATH", "$.observations.data[0].wind_spd_kmh", bom_data)
logInfo("BOM data","wind_spd="+wind_spd)
postUpdate("BOM_WindSpeed", wind_spd)
val rain = transform("JSONPATH", "$.observations.data[0].rain_trace", bom_data)
logInfo("BOM data","rain="+rain)
postUpdate("BOM_Rain_Amount", rain)
end
items:
/*BOM weather observation data*/
String BOM_Observation_Time "Observation time [%s]"
Number BOM_AirTemp "Current Temp [%.1f °C]" <temperature>
Number BOM_ApparentTemp "Apparent Temp [%.1f °C]" <temperature>
String BOM_WindDir "Wind Direction [%s]"
Number BOM_WindSpeed "Wind Speed [%.0f km/h]"
Number BOM_Rain_Amount "Rainfall [%.1f mm]"
/*BOM Weather forecast data*/
DateTime BOM_Date_0 "Forecast time [%1$td.%1$tm.%1$tY %1$tH:%1$tM]"
DateTime BOM_Date_1 "Forecast time [%1$td.%1$tm.%1$tY %1$tH:%1$tM]"
DateTime BOM_Date_2 "Forecast time [%1$td.%1$tm.%1$tY %1$tH:%1$tM]"
DateTime BOM_Date_3 "Forecast time [%1$td.%1$tm.%1$tY %1$tH:%1$tM]"
DateTime BOM_Date_4 "Forecast time [%1$td.%1$tm.%1$tY %1$tH:%1$tM]"
DateTime BOM_Date_5 "Forecast time [%1$td.%1$tm.%1$tY %1$tH:%1$tM]"
DateTime BOM_Date_6 "Forecast time [%1$td.%1$tm.%1$tY %1$tH:%1$tM]"
Number BOM_Temp_Max_0 " max Today [%.2f °C]" <temperature> (Weather)
Number BOM_Temp_Min_0 " min Today [%.2f °C]" <temperature> (Weather)
String BOM_Precipitation_Range_0 " Rain today [%s]" (Weather)
String BOM_Precipitation_0 "Precip probability today [%s]" (Weather)
Number BOM_Forecast_Icon_Code_0 " Icon Code Today [%d]" (Weather)
String BOM_Precis_0 " Condition Today [%s]" (Weather)
String BOM_Forecast_0 " Forecast: [%s]" (Weather)
String BOM_Wind_Range_0 " Forecast: [%s]" (Weather)
Number BOM_Temp_Max_1 "Forecast max tomorrow [%.2f °C]" <temperature> (Weather)
Number BOM_Temp_Min_1 "Forecast min tomorrow [%.2f °C]" <temperature> (Weather)
String BOM_Precipitation_Range_1 "Forecast Rain tomorrow [%s]" (Weather)
String BOM_Precipitation_1 "Precip probability tomorrow [%s]" (Weather)
Number BOM_Forecast_Icon_Code_1 "Forecast Icon Code tomorrow [%d]" (Weather)
String BOM_Precis_1 "Forecast Condition tomorrow [%s]" (Weather)
String BOM_Forecast_1 " Forecast: [%s]" (Weather)
String BOM_Wind_Range_1 " Forecast: [%s]" (Weather)
Number BOM_Temp_Max_2 "Forecast max tomorrow [%.2f °C]" <temperature> (Weather)
Number BOM_Temp_Min_2 "Forecast min tomorrow [%.2f °C]" <temperature> (Weather)
String BOM_Precipitation_Range_2 "Forecast Rain tomorrow [%s]" (Weather)
String BOM_Precipitation_2 "Precip probability tomorrow [%s]" (Weather)
Number BOM_Forecast_Icon_Code_2 "Forecast Icon Code tomorrow [%d]" (Weather)
String BOM_Precis_2 "Forecast Condition tomorrow [%s]" (Weather)
String BOM_Forecast_2 " Forecast: [%s]" (Weather)
String BOM_Wind_Range_2 " Forecast: [%s]" (Weather)
Number BOM_Temp_Max_3 "Forecast max tomorrow [%.2f °C]" <temperature> (Weather)
Number BOM_Temp_Min_3 "Forecast min tomorrow [%.2f °C]" <temperature> (Weather)
String BOM_Precipitation_Range_3 "Forecast Rain tomorrow [%s]" (Weather)
String BOM_Precipitation_3 "Precip probability tomorrow [%s]" (Weather)
Number BOM_Forecast_Icon_Code_3 "Forecast Icon Code tomorrow [%d]" (Weather)
String BOM_Precis_3 "Forecast Condition tomorrow [%s]" (Weather)
String BOM_Forecast_3 " Forecast: [%s]" (Weather)
String BOM_Wind_Range_3 " Forecast: [%s]" (Weather)
Number BOM_Temp_Max_4 "Forecast max tomorrow [%.2f °C]" <temperature> (Weather)
Number BOM_Temp_Min_4 "Forecast min tomorrow [%.2f °C]" <temperature> (Weather)
String BOM_Precipitation_Range_4 "Forecast Rain tomorrow [%s]" (Weather)
String BOM_Precipitation_4 "Precip probability tomorrow [%s]" (Weather)
Number BOM_Forecast_Icon_Code_4 "Forecast Icon Code tomorrow [%d]" (Weather)
String BOM_Precis_4 "Forecast Condition tomorrow [%s]" (Weather)
String BOM_Forecast_4 " Forecast: [%s]" (Weather)
String BOM_Wind_Range_4 " Forecast: [%s]" (Weather)
Number BOM_Temp_Max_5 "Forecast max tomorrow [%.2f °C]" <temperature> (Weather)
Number BOM_Temp_Min_5 "Forecast min tomorrow [%.2f °C]" <temperature> (Weather)
String BOM_Precipitation_Range_5 "Forecast Rain tomorrow [%s]" (Weather)
String BOM_Precipitation_5 "Precip probability tomorrow [%s]" (Weather)
Number BOM_Forecast_Icon_Code_5 "Forecast Icon Code tomorrow [%d]" (Weather)
String BOM_Precis_5 "Forecast Condition tomorrow [%s]" (Weather)
String BOM_Forecast_5 " Forecast: [%s]" (Weather)
String BOM_Wind_Range_5 " Forecast: [%s]" (Weather)
Number BOM_Temp_Max_6 "Forecast max tomorrow [%.2f °C]" <temperature> (Weather)
Number BOM_Temp_Min_6 "Forecast min tomorrow [%.2f °C]" <temperature> (Weather)
String BOM_Precipitation_Range_6 "Forecast Rain tomorrow [%s]" (Weather)
String BOM_Precipitation_6 "Precip probability tomorrow [%s]" (Weather)
Number BOM_Forecast_Icon_Code_6 "Forecast Icon Code tomorrow [%d]" (Weather)
String BOM_Precis_6 "Forecast Condition tomorrow [%s]" (Weather)
String BOM_Forecast_6 " Forecast: [%s]" (Weather)
String BOM_Wind_Range_6 " Forecast: [%s]" (Weather)
Widget:
<div class="section">
<div class="section">
<div ng-init="ServerPath='//192.168.1.60:8080/static/images'; IconSet='colorful'; weathericonmap={
'1' : 'sunny',
'2' : 'clear',
'3' : 'partly-cloudy-day',
'4' : 'cloudy',
'6' : 'hazy',
'8' : 'chancerain',
'9' : 'wind',
'10' : 'fog',
'11' : 'scattered-showers',
'12' : 'rain',
'13' : 'wind',
'14' : 'snow',
'15' : 'snow',
'16' : 'scattered-thundershowers',
'17' : 'scattered-showers',
'18' : 'rain',
'19' : 'sunny'
};
">
<link rel="stylesheet" type="text/css" href="{{ServerPath}}/layouts/example.css" />
</div>
<table id="weather-forecast-table" border=0 width="100%" style="table-layout: fixed">
<tr>
<td width=25% colspan=2 style="font-weight:bold;">Current Conditions {{itemValue('BOM_Observation_Time')}}</td>
<td width="3%"> </td>
<td style="font-weight:bold;">{{itemValue('BOM_Date_1') | date:'EEEE'}}</td>
<td style="font-weight:bold;">{{itemValue('BOM_Date_2') | date:'EEEE'}}</td>
<td style="font-weight:bold;">{{itemValue('BOM_Date_3') | date:'EEEE'}}</td>
<td style="font-weight:bold;">{{itemValue('BOM_Date_4') | date:'EEEE'}}</td>
<td style="font-weight:bold;">{{itemValue('BOM_Date_5') | date:'EEEE'}}</td>
<td style="font-weight:bold;">{{itemValue('BOM_Date_6') | date:'EEEE'}}</td>
</tr>
<tr><td> </td></tr>
<tr>
<td align="center" colspan=2><img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathericonmap[itemValue('BOM_Forecast_Icon_Code_0')]}}.png"/>
<p style="font-weight:bold;">{{itemValue('BOM_Precis_0')}}</p>
</td>
<td> </td>
<td>
<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathericonmap[itemValue('BOM_Forecast_Icon_Code_1')]}}.png"/>
<p style="font-weight:bold;">{{itemValue('BOM_Precis_1')}}</p>
</td>
<td>
<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathericonmap[itemValue('BOM_Forecast_Icon_Code_2')]}}.png"/>
<p style="font-weight:bold;">{{itemValue('BOM_Precis_2')}}</p>
</td>
<td>
<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathericonmap[itemValue('BOM_Forecast_Icon_Code_3')]}}.png"/>
<p style="font-weight:bold;">{{itemValue('BOM_Precis_3')}}</p>
</td>
<td>
<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathericonmap[itemValue('BOM_Forecast_Icon_Code_4')]}}.png"/>
<p style="font-weight:bold;">{{itemValue('BOM_Precis_4')}}</p>
</td>
<td>
<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathericonmap[itemValue('BOM_Forecast_Icon_Code_5')]}}.png"/>
<p style="font-weight:bold;">{{itemValue('BOM_Precis_5')}}</p>
</td>
<td>
<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathericonmap[itemValue('BOM_Forecast_Icon_Code_6')]}}.png"/>
<p style="font-weight:bold;">{{itemValue('BOM_Precis_6')}}</p>
</td>
</tr>
<tr>
<td style="text-align:right" width="7%">Outside Temp: </td><td align="left"> {{'%s' | sprintf:itemValue('BOM_AirTemp')}} °C</td>
<td style="text-align:right;color:#fff">Max </td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Max_1')}} °C</td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Max_2')}} °C</td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Max_3')}} °C</td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Max_4')}} °C</td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Max_5')}} °C</td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Max_6')}} °C</td>
</tr>
<tr>
<td style="text-align:right">Feels Like: </td><td align="left"> {{'%s' | sprintf:itemValue('BOM_ApparentTemp')}} °C</td>
<td style="text-align:right;color:#fff">Min </td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Min_1')}} °C</td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Min_2')}} °C</td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Min_3')}} °C</td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Min_4')}} °C</td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Min_5')}} °C</td>
<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Min_6')}} °C</td>
</tr>
<tr>
<td style="text-align:right">Inside Temp: </td><td align="left"> {{'%s' | sprintf:itemValue('Lounge_Temperature')}}</td>
<td style="text-align:right;color:#fff">Rain </td>
<td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_1')}} </td>
<td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_2')}} </td>
<td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_3')}} </td>
<td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_4')}} </td>
<td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_5')}} </td>
<td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_6')}} </td>
</tr>
<tr>
<td style="text-align:right">Rainfall: </td><td align="left"> {{'%s' | sprintf:itemValue('BOM_Rain_Amount')}} mm</td>
<td></td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Precipitation_Range_1')}}</td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Precipitation_Range_2')}}</td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Precipitation_Range_3')}}</td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Precipitation_Range_4')}}</td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Precipitation_Range_5')}}</td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Precipitation_Range_6')}}</td>
</tr>
<tr>
<td style="text-align:right">Wind: </td><td align="left"> {{'%s' | sprintf:itemValue('BOM_WindSpeed')}} km/h {{'%s' | sprintf:itemValue('BOM_WindDir')}}</td>
<td style="text-align:right;color:#fff">Wind </td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Wind_Range_1')}}</td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Wind_Range_2')}}</td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Wind_Range_3')}}</td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Wind_Range_4')}}</td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Wind_Range_5')}}</td>
<td class="col-xs-4" style="color:#fff">{{'%s' | sprintf:itemValue('BOM_Wind_Range_6')}}</td>
</tr>
</table>
</div>