Australian Bureau of Meteorology integration with OpenHAB

Just need to make sure the mapping in ng-init at top of template shows what you want it to show. The first phase is the BOM precis and then after the colon the second is what you want it to match to for image.

ie.
‘whatever BOM phrase’:‘partly cloudy’

Thanks Mark that sorted it out!

Now to determine what my matrixtheme background color is to update the example.css color so it matches :wink:

Hi Mark

Any thoughts as to why its Sunday yet it shows Saturtday still?

Are you still using BOM with this?

Hi andrew yes i am

Here is what I see today, i’d imagine that Saturday/Sunday should disappear :slight_smile:

Hi All

Ive still not been able to fix this. Today is Thursday, so why is the current condition showing something entirely different? The code doesnt make sense to me

My current widget:


<div class="section">
<div class="controls">
	<div class="title">Dashboards</div>
    
    
    <div class="widget.wide">
       <div class="controlGroup">
        <div class="control" ng-class="{true: 'on'}[itemValue('MENU_page')=='System']" ng-click="sendCmd('MENU_page', 'System')"><svg viewBox="0 0 80 80"><use xlink:href="/static/matrix-theme/squidink.svg#controal-4"></use></svg></div>
				<div class="control" ng-class="{true: 'on'}[itemValue('MENU_page')=='Lighting']" ng-click="sendCmd('MENU_page', 'Lighting')"><svg viewBox="0 0 80 80"><use xlink:href="/static/matrix-theme/matrixicons.svg#light_bulb"></use></svg></div>
				<div class="control" ng-class="{true: 'on'}[itemValue('MENU_page')=='Entertainment']" ng-click="sendCmd('MENU_page', 'Entertainment')"><svg viewBox="0 0 80 80"><use xlink:href="/static/matrix-theme/squidink.svg#box"></use></svg></div>
        <div class="control" ng-class="{true: 'on'}[itemValue('MENU_page')=='Comfort']" ng-click="sendCmd('MENU_page', 'Comfort')"><svg viewBox="0 0 80 80"><use xlink:href="/static/matrix-theme/squidink.svg#thermometer-3"></use></svg></div>
        <div class="control" ng-class="{true: 'on'}[itemValue('MENU_page')=='Gate & Garage']" ng-click="sendCmd('MENU_page', 'Gate & Garage')"><svg viewBox="0 0 80 80"><use xlink:href="/static/matrix-theme/squidink.svg#garage"></use></svg></div>
			  <div class="control" ng-class="{true: 'on'}[itemValue('MENU_page')=='Temp & Power']" ng-click="sendCmd('MENU_page', 'Temp')"><svg viewBox="0 0 80 80"><use xlink:href="/static/matrix-theme/squidink.svg#thunder-1"></use></svg></div>
        <div class="control" ng-class="{true: 'on'}[itemValue('MENU_page')=='Cameras']" ng-click="sendCmd('MENU_page', 'Cameras')"><svg viewBox="0 0 80 80"><use xlink:href="/static/matrix-theme/squidink.svg#cam-4"></use></svg></div>
        <div class="control" ng-class="{true: 'on'}[itemValue('MENU_page')=='Irrigation']" ng-click="sendCmd('MENU_page', 'Irrigation')"><svg viewBox="0 0 80 80"><use xlink:href="/static/matrix-theme/squidink.svg#drops"></use></svg></div>
         
        </div>		
				</div>
   	</div>
    </div>

<div class="section">
<div ng-init="ServerPath='//oh2.ddns.net/weather/'; IconSet='colorful'; weathermap={'Shower or two':'few showers','Shower or two clearing':'few showers','Possible shower':'scattered showers','Rain at times':'rain',
'Rain developing':'rain','Clear':'clear night','Showers. Windy':'showers','Possible showers':'showers','Possible late shower':'showers','Showers easing':'showers','Early shower or two':'showers','Showers easing.
 Wind easing':'showers','Rain easing':'showers','Showers. Possible storm':'storm','Rain periods. Possible storm':'storm','Possible morning storm. Showers':'storm','Rain developing':'rain','Becoming windy. Sunny':'clear',
'Showers, heavy at times':'rain','Becoming cloudy':'cloudy','Mostly sunny':'partly cloudy','Shower or two. Windy':'showers','Showers increasing. Windy':'showers','Mostly sunny day. Late rain':'showers','Showers. Very windy':'showers',
'Partly cloudy. Possible shower':'chance-of-rain','Cloud clearing. Possible shower':'mostly sunny','Mostly sunny. Afternoon cool change':'mostly sunny','Possible morning storm':'chance storms','Shower or two developing':'showers',
'Showers developing':'showers','Showers increasing':'showers','Possible shower later':'showers','Possible morning shower':'showers','Late showers':'showers','Morning shower or two':'showers','Late shower or two':'showers',
'Partly cloudy':'partly cloudy','Cloud increasing':'cloudy','NULL':'unknown','N/A':'unknown'};">
<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</td>
		<td rowspan=11 width=3%>&nbsp; </td>
		<td>&nbsp; </td>
		<td style="font-weight:bold;">Today</td>
		<td style="font-weight:bold;">{{itemValue('BOM_Date_0') | date:'EEEE'}}</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>
	</tr>
  <tr><td>&nbsp; </td></tr>
	<tr>
    <td align="center" colspan=2><img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathermap[itemValue('BOM_Precis_0')].replace(' ','-') | lowercase }}.png"/>
			<p style="font-weight:bold;">{{itemValue('BOM_Precis_0')}}</p></td>
		<td>&nbsp; </td>
    <td>
      <img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathermap[itemValue('BOM_Precis_1')].replace(' ','-') | lowercase }}.png"/>
			<p style="font-weight:bold;">{{itemValue('BOM_Precis_1')}}</p>
		</td>
		<td>
			<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathermap[itemValue('BOM_Precis_2')].replace(' ','-') | lowercase }}.png"/>
			<p style="font-weight:bold;">{{itemValue('BOM_Precis_2')}}</p>
		</td>
		<td>
			<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathermap[itemValue('BOM_Precis_3')].replace(' ','-') | lowercase }}.png"/>
			<p style="font-weight:bold;">{{itemValue('BOM_Precis_3')}}</p>
		</td>
    <td>
			<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathermap[itemValue('BOM_Precis_4')].replace(' ','-') | lowercase }}.png"/>
			<p style="font-weight:bold;">{{itemValue('BOM_Precis_4')}}</p>
		</td>
    <td>
			<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathermap[itemValue('BOM_Precis_5')].replace(' ','-') | lowercase }}.png"/>
			<p style="font-weight:bold;">{{itemValue('BOM_Precis_5')}}</p>
		</td>
       <td>
				<img width="100" src="{{ServerPath}}/{{IconSet}}/{{weathermap[itemValue('BOM_Precis_6')].replace(' ','-') | lowercase }}.png"/>
			<p style="font-weight:bold;">{{itemValue('BOM_Precis_6')}}</p>
		</td>
  </tr>
  
	<tr>
    <td align=right width=7%>Outside Temp:&nbsp; </td><td align=left>&nbsp; {{'%.1f' | sprintf:itemValue('gOutsideTemp')}} °C</td>
		<td style="text-align:right;color:#fff">Maximum&nbsp; </td>
		<td class="col-xs-4" style="color:#fff">{{'%.0f' | sprintf:itemValue('BOM_Temp_Max_0')}} °C</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>
	</tr>
	<tr>
		<td align=right>Attic Humidity:&nbsp; </td><td align=left>&nbsp; {{'%.0f' | sprintf:itemValue('AtticHumidity')}} %</td>
		<td style="text-align:right;color:#fff">Minimum&nbsp; </td>
		<td class="col-xs-4" style="color:#fff"></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>
	</tr>
  	<tr>
		<td align=right>Attic Temp:&nbsp; </td><td align=left>&nbsp; {{'%.0f' | sprintf:itemValue('AtticTemp')}} °C</td>
		<td style="text-align:right;color:#fff">Minimum&nbsp; </td>
		<td class="col-xs-4" style="color:#fff"></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>
	</tr>

	<tr>
		<td align=right>UV Index:&nbsp; </td><td align=left>&nbsp; {{'%.2f' | sprintf:itemValue('openuv_uvreport_local_UVIndex')}} ({{itemValue('UV_Range')}})</td>
		<td style="text-align:right">Sunrise&nbsp; </td>
		<td class="col-xs-4" style="color:#fff">{{itemValue('vSunrise_Time') | date:'HH:mm'}}</td>
		<td class="col-xs-4" style="color:#fff"></td>
		<td class="col-xs-4" style="color:#fff"></td>
    <td class="col-xs-4" style="color:#fff"></td>
	</tr>
	
	<tr>
		<td align=right></td><td align=left></td>
		<td style="text-align:right">Sunset&nbsp; </td>
		<td class="col-xs-4" style="color:#fff">{{itemValue('vSunset_Time') | date:'HH:mm'}}</td>
		<td class="col-xs-4" style="color:#fff"></td>
		<td class="col-xs-4" style="color:#fff"></td>
    <td class="col-xs-4" style="color:#fff"></td>
	</tr>
</table>
  </div>

Try restarting Hab i found it doesnt like it too much on an ipad when you are playing with items etc
Also noticed you are doing ng-init translations - ive moved on to using the icon type to do the image translation - works much more reliably and dont need to keep adding as BOM adds creative names

Hi Andrew

Restart doesnt make a difference.

Icon type? Can you show me what you mean, or was that present in your widget code anyway?

Cheers

Hi @Andrew_Pawelski

Would you mind sharing you transform for the icon codes? I can’t seem to find a listing anywhere to explain them, and am also getting tired of updating my panel every day with whatever new condition description the BOM throws at me.

Cheer!

Sure thing:

<div ng-init="weathericonmap={
              '1' : 'sunny',
              '2' : 'clear',
              '3' : 'mostlysunny',
              '4' : 'cloudy',
              '6' : 'hazy',
              '8' : 'chancerain',
              '9' : 'wind',
              '10' : 'fog',
              '11' : 'chancerain',
              '12' : 'rain',
              '13' : 'wind',
              '14' : 'snow',
              '15' : 'snow',
              '16' : 'tstorms',
              '17' : 'chancerain',
              '18' : 'rain',
              '19' : 'sunny'
              };
              ">
	
</div>

and an example use supporting astro night event for different icons day or nught

<div>
         <img ng-if="itemValue('Sunset_Event')=='OFF'"  class="weather-icon-row pulse" src="/static/images/{{config.IconSet}}/{{weathericonmap[itemValue('BOM_Forecast_Icon_Code_0')]}}.png"/> 
         <img ng-if="itemValue('Sunset_Event')=='ON'" class="weather-icon-row pulse" src="/static/images/{{config.IconSet}}/nt_{{weathericonmap[itemValue('BOM_Forecast_Icon_Code_0')]}}.png"/>
</div>
1 Like

Great work to everyone on this. Given I’m running docker and the the python command line was problematic :frowning:, 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%">&nbsp; </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>&nbsp; </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>&nbsp; </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:&nbsp; </td><td align="left">&nbsp; {{'%s' | sprintf:itemValue('BOM_AirTemp')}} °C</td>
		<td style="text-align:right;color:#fff">Max&nbsp; </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:&nbsp; </td><td align="left">&nbsp; {{'%s' | sprintf:itemValue('BOM_ApparentTemp')}} °C</td>
		<td style="text-align:right;color:#fff">Min&nbsp; </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:&nbsp; </td><td align="left">&nbsp; {{'%s' | sprintf:itemValue('Lounge_Temperature')}}</td>
		<td style="text-align:right;color:#fff">Rain&nbsp; </td>
    <td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_1')}}&nbsp;&nbsp;</td>
    <td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_2')}}&nbsp;&nbsp;</td>
    <td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_3')}}&nbsp;&nbsp;</td>
    <td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_4')}}&nbsp;&nbsp;</td>
    <td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_5')}}&nbsp;&nbsp;</td>
    <td class="col-xs-4" style="color:#fff"><img width="20" src="{{ServerPath}}/raindrop-icon.png"/>{{'%s' | sprintf:itemValue('BOM_Precipitation_6')}}&nbsp;&nbsp;</td>
	</tr>
	<tr>
		<td style="text-align:right">Rainfall:&nbsp; </td><td align="left">&nbsp; {{'%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:&nbsp; </td><td align="left">&nbsp; {{'%s' | sprintf:itemValue('BOM_WindSpeed')}} km/h {{'%s' | sprintf:itemValue('BOM_WindDir')}}</td>
		<td style="text-align:right;color:#fff">Wind&nbsp; </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>

3 Likes

Hi James, looks nice.

Are you able to share your images as I can see you have a few extra nice ones! I like the little rain drop, I found the image but did you have to resize it? if so, what resolution?

Hi Kris,

The raindrop image is raindrop-icon.png.
The other images are the ones referred to by @marklavercombe in post #56

Thanks James, working. The Wind, does that only work on certain times? All other observations are fine, but the wind wont display. Shows NULL. I looked at the observation json file and the name as per your rule and it has the right value

al wind_spd = transform("JSONPATH", "$.observations.data[0].wind_spd_kmh", bom_data)
        logInfo("BOM data","wind_spd="+wind_spd)

But I get NULL values. I also note the rule doesnt appear to log when it runs, maybe thats why its not reading the value as the second rule isn’t running.


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/IDN60901/IDN60901.94766.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();

        val 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

I also see in the console, is this a var /val thing?


20:25:54.990 [INFO ] [del.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'weather.rules', using it anyway:
Assignment to final variable

Hi Kris,

Change the definition of time for val to var, “var String time = …”, I edited my original post to reflect this.

I noticed some observation data does not appear at certain times. I guess if there is no wind, BOM don’t publish a wind speed. You could check the raw json data from BOM in your browser.

Thanks, that fixed that.

The wind speed is present, as seen here:

http://www.bom.gov.au/fwo/IDN60901/IDN60901.94766.json

I do not see in the logs the rule running every 5 minutes, nothing is logged at all.

But it shows up NULL in your rules. If i update them manually, they display fine in the widget.


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/IDN60901/IDN60901.94766.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

Edit : FIXED

I rebooted openHab and it started to work! Very odd. Thanks James!

Hi All

I have successfully implemented the above OpenHAB rule but have noticed the entire contents of the xml file end up in the OpenHAB log file. Is there anyway to stop this from happening?

Hi Adam

You can remove the logInfos from the rule

Regards