Optimization of rule

Hi,

I do not have a real problem, but I am searching for optimizations.

Especially the part between “from here” to “to here” is in my focus, I think in other languages I can do it better with arrays (of objects) with index of the adress of the onewire sensor.
In my case now, if the order of the sensors changes or one is missing then it will not work anymore.

Can someone point me to reach the goal above or can optimize my rule?

regards

rule "rule_hausautomation_windmesser_dach_get_status"
	when
		Time cron "*/30 * * * * ?"										// Sek., Min., Std., Monat, Tag
		or
		Item Dach_Windmesser_TX20_Action received update
	then
		logInfo("RuleExecutionLog", "rule_hausautomation_windmesser_dach_get_status")

		if (var_RuleStartupFired.state != ON)
		{
			logWarn("RuleExecutionLog", "rule_hausautomation_windmesser_dach_get_status :: beendet :: " + var_RuleStartupFired.state.toString + " :: Startup-Rule nicht durchgeführt")
			return
		}

		val aktTime = new DateTimeType()

		if (Windmesser_TX20_Dach_Device_IP.state.toString == "")
		{
			logError("RuleExecutionLog", "rule_hausautomation_windmesser_dach_get_status * Windmesser Dach Device: keine IP !!")
			return
		}

		strLocalWeatherTempDach_28FF8B89831704DC_Adr.postUpdate("Requesting")
		strLocalWeatherTempDach_28FFF0888317044E_Adr.postUpdate("Requesting")
		strLocalWeatherTempDach_28FFF3B39017052C_Adr.postUpdate("Requesting")
		strLocalWeatherTempDach_28FF0E8B831704C2_Adr.postUpdate("Requesting")
		
		var String res = "" // "'{"variables": {"sa": 4, "sb": 1, "sc": 25, "sd": 10, "se": 3, "sf": 25, "vd": false, "vcc": 3.03, "1WireDevCnt": 4, "1WireValues": "adr_1:40.255.240.136.131.23.04.78.;temp_1:16.88;adr_2:40.255.014.139.131.23.04.194.;temp_2:24.00;adr_3:40.255.243.179.144.23.05.44.;temp_3:18.69;adr_4:40.255.139.137.131.23.04.220.;temp_4:25.25;"}, "id": "Wind01", "name": "Windmesser Dach", "hardware": "esp8266", "connected": true}'

		try 
		{
			res = sendHttpGetRequest("http://" + Windmesser_TX20_Dach_Device_IP.state.toString + "/status")
			logWarn("RES", "RES: -> " + res)
		}
		catch(Throwable t) 
		{
			res = ""
			strLocalWeatherTempDach_28FF8B89831704DC_Adr.postUpdate("Fehler bei Abfrage")
			strLocalWeatherTempDach_28FFF0888317044E_Adr.postUpdate("Fehler bei Abfrage")
			strLocalWeatherTempDach_28FFF3B39017052C_Adr.postUpdate("Fehler bei Abfrage")	
			strLocalWeatherTempDach_28FF0E8B831704C2_Adr.postUpdate("Fehler bei Abfrage")
			return
		}
		finally 
		{
		}

		/*
			{"variables":	{
								"sa": 4, 
								"sb": 15, 
								"sc": 37, 
								"sd": 4, 
								"se": 15, 
								"sf": 37, 
								"vd": false, 
								"vcc": 3.03, 
								"1WireDevCnt": 3, 
								"1WireValues": "adr_1:40.255.240.136.131.23.04.78.;temp_1:21.25;
												adr_2:40.255.243.179.144.23.05.44.;temp_2:26.38;
												adr_3:40.255.139.137.131.23.04.220.;temp_3:23.44;"
							}, 
							"id": "Wind01", 
							"name": "Windmesser Dach", 
							"hardware": "esp8266", 
							"connected": true}
		*/



		// Temperaturdaten verarbeiten

		val json_status = transform("JSONPATH", "$.variables.1WireValues", res)

		if (json_status == "")
		{
			logError("RuleExecutionLog", "rule_hausautomation_windmesser_dach_get_status * Windmesser Dach Status setzen * Fehlerhafter Rückgabewert * " + json_status +  " * Länge: " + json_status.length())
			return
		}

		Variable_Dach_Windmesser_TX20_Action_GetStatus.postUpdate(json_status)

		// from here
		
		var String temp_adr_01 = "0.0.0.0.0.0.0.0"
		var String temp_adr_02 = "0.0.0.0.0.0.0.0"
		var String temp_adr_03 = "0.0.0.0.0.0.0.0"
		var String temp_adr_04 = "0.0.0.0.0.0.0.0"

		var temp_val_01 = -999.00
		var temp_val_02 = -999.00
		var temp_val_03 = -999.00
		var temp_val_04 = -999.00
		
		val StringBuilder adr_hex_01 = new StringBuilder
		val StringBuilder adr_hex_02 = new StringBuilder
		val StringBuilder adr_hex_03 = new StringBuilder
		val StringBuilder adr_hex_04 = new StringBuilder


		try 
		{
			temp_adr_01 = json_status.split("\\;").get(0).split("\\:").get(1)
			temp_val_01 = Float::parseFloat( json_status.split("\\;").get(1).split("\\:").get(1) )
		}
		catch(Throwable t) 
		{
			temp_adr_01 = "Fehler 01"
			temp_val_01 = -888.00
		}
		finally 
		{
		}
		
		try 
		{
			temp_adr_02 = json_status.split("\\;").get(2).split("\\:").get(1)
			temp_val_02 = Float::parseFloat( json_status.split("\\;").get(3).split("\\:").get(1) )
		}
		catch(Throwable t) 
		{
			temp_adr_02 = "Fehler 02"
			temp_val_02 = -888.00
		}
		finally 
		{
		}
		
		try 
		{
			temp_adr_03 = json_status.split("\\;").get(4).split("\\:").get(1)
			temp_val_03 = Float::parseFloat( json_status.split("\\;").get(5).split("\\:").get(1) )
		}
		catch(Throwable t) 
		{
			temp_adr_03 = "Fehler 03"
			temp_val_03 = -888.00
		}
		finally 
		{
		}
		
		try 
		{
			temp_adr_04 = json_status.split("\\;").get(6).split("\\:").get(1)
			temp_val_04 = Float::parseFloat( json_status.split("\\;").get(7).split("\\:").get(1) )
		}
		catch(Throwable t) 
		{
			temp_adr_04 = "Fehler 04"
			temp_val_04 = -888.00
		}
		finally 
		{
		}



		if (temp_adr_01 != "")
		{
			temp_adr_01.split("\\.").forEach
			(b |
				{ 
					var val_int = Integer::parseInt(b)
					if (val_int < 16)
					{
						adr_hex_01.append("0")
					}
					adr_hex_01.append(Integer::toHexString(val_int).toUpperCase)
				}
			)

			strLocalWeatherTempDach_28FFF0888317044E_Adr.postUpdate(adr_hex_01.toString)
			numLocalWeatherTempDach_28FFF0888317044E_Val.postUpdate(temp_val_01)
		}



		if (temp_adr_02 != "")
		{
			temp_adr_02.split("\\.").forEach
			(b |
				{ 
					var val_int = Integer::parseInt(b)
					if (val_int < 16)
					{
						adr_hex_02.append("0")
					}
					adr_hex_02.append(Integer::toHexString(val_int).toUpperCase)
				}
			)

			strLocalWeatherTempDach_28FF0E8B831704C2_Adr.postUpdate(adr_hex_02.toString)
			numLocalWeatherTempDach_28FF0E8B831704C2_Val.postUpdate(temp_val_02)
		}



		if (temp_adr_03 != "")
		{
			temp_adr_03.split("\\.").forEach
			(b |
				{ 
					var val_int = Integer::parseInt(b)
					if (val_int < 16)
					{
						adr_hex_03.append("0")
					}
					adr_hex_03.append(Integer::toHexString(val_int).toUpperCase)
				}
			)

			strLocalWeatherTempDach_28FFF3B39017052C_Adr.postUpdate(adr_hex_03.toString)
			numLocalWeatherTempDach_28FFF3B39017052C_Val.postUpdate(temp_val_03)
		}



		if (temp_adr_04 != "")
		{
			temp_adr_04.split("\\.").forEach
			(b |
				{ 
					var val_int = Integer::parseInt(b)
					if (val_int < 16)
					{
						adr_hex_04.append("0")
					}
					adr_hex_04.append(Integer::toHexString(val_int).toUpperCase)
				}
			)

			strLocalWeatherTempDach_28FF8B89831704DC_Adr.postUpdate(adr_hex_04.toString)
			numLocalWeatherTempDach_28FF8B89831704DC_Val.postUpdate(temp_val_04)
		}

		// to here

		// Winddaten verarbeiten	

		/*
			"sa": 4, "sb": 0, "sc": 14, "sd": 14, "se": 1, "sf": 14, "vd": false, "vcc": 3.03
		*/

		numLocalWeatherWindDach_sa.postUpdate(transform("JSONPATH", "$.variables.sa", res))	
		numLocalWeatherWindDach_sb.postUpdate(transform("JSONPATH", "$.variables.sb", res))	
		numLocalWeatherWindDach_sc.postUpdate(transform("JSONPATH", "$.variables.sc", res))	
		numLocalWeatherWindDach_sd.postUpdate(transform("JSONPATH", "$.variables.sd", res))	
		numLocalWeatherWindDach_se.postUpdate(transform("JSONPATH", "$.variables.se", res))	
		numLocalWeatherWindDach_sf.postUpdate(transform("JSONPATH", "$.variables.sf", res))	

		var wind_dir = transform("JSONPATH", "$.variables.se", res)
		var wind_spd = transform("JSONPATH", "$.variables.sf", res)

		var wind_dir_grad = Float::parseFloat(wind_dir) * 22.5
		var wind_spd_msec = Float::parseFloat(wind_spd) / 10
		var wind_spd_kmh = (Float::parseFloat(wind_spd) / 10) * 3.6

		if (wind_dir_grad == NULL)
		{
			wind_dir_grad = 0
		}

		strLocalWeatherWindDach_dir_name.postUpdate(wind_dir)
		strLocalWeatherWindDach_dir_grad.postUpdate( Math::round(wind_dir_grad) )
		strLocalWeatherWindDach_speed_msec.postUpdate(wind_spd_msec)
		strLocalWeatherWindDach_speed_kmh.postUpdate(wind_spd_kmh)
		
		var wind_avg_diff_10min = now.minusMinutes(10)
		var wind_avg_dir_10min = strLocalWeatherWindDach_dir_grad.averageSince( wind_avg_diff_10min )
		var wind_avg_speed_10min_msec = strLocalWeatherWindDach_speed_msec.averageSince( wind_avg_diff_10min )
		var wind_avg_speed_10min_kmh = strLocalWeatherWindDach_speed_kmh.averageSince( wind_avg_diff_10min )

		try
		{ 
			numLocalWeatherWindDach_AVG10min_dir_grad.postUpdate( wind_avg_dir_10min )	
		}
		catch(Throwable t) {} finally {}

		try
		{ 
			numLocalWeatherWindDach_AVG10min_speed_msec.postUpdate( wind_avg_speed_10min_msec )
		}
		catch(Throwable t) {} finally {}

		try
		{ 
			numLocalWeatherWindDach_AVG10min_speed_kmh.postUpdate( wind_avg_speed_10min_kmh )	
		}
		catch(Throwable t) {} finally {}


		// VCC und ValidData
		
		numLocalWeatherDeviceVCC.postUpdate(transform("JSONPATH", "$.variables.vcc", res))	
		
		if (transform("JSONPATH", "$.variables.vd", res) == "false")
		{
			contactLocalWeatherValiddata.postUpdate(CLOSED)	
		}
		else
		{
			contactLocalWeatherValiddata.postUpdate(OPEN)		
		}

		numLocalWeatherTempDach_1WireCount.postUpdate(transform("JSONPATH", "$.variables.1WireDevCnt", res))	

		datLocalWeatherWindDach_LastUpdate.postUpdate(aktTime)	
end

Some initial thoughts.

  • This would probably be easier for you to implement using Jython

  • The finally doesn’t need to be there if it’s just empty

  • Consider initializing your variables to the “failed” values and then you only need one try catch and the catch doesn’t really need to do anything.

  • You could use a List and a for loop for the temp_adr and temp_val variables, but you don’t even need that. Just do it all in the one for loop.


    for (i : 0 ..< 4) {
        try {
            adr = json_status.split("\\;").get(i*2).split("\\:").get(1)
            value = Float::parseFloat( json_status.split("\\;").get(i*2+1).split("\\:").get(1)

            adr_hex = new StringBuilder
            if(adr != "") {
                adr.split("\\.").forEach[ b |
                    val num = Integer::parseInt(b)
                     adr_hex.append(if(num < 16) "0" else Integer::toHexString(val_int).toUpperCase)
                ]
                strLocalWeatherTempDach_28FFF0888317044E_Adr.postUpdate(adr_hex.toString)
                numLocalWeatherTempDach_28FFF0888317044E_Val.postUpdate(value)
            }
        }
        catch(Exception e){
            // log an error message
        }
    }

Ok, than I have 4 times splitted my Values in the for loop, but I did not have the ability to use the right item or the actual value?

The failed value is there to know which error appeared … thats not the real fact …

I got the HEX-Adress o the sensor and I will assign them to the associated item … independant the order of the sensors.

Jython … two worlds I have no knowledge in …

Right, the Item names are so long and complicated I didn’t notice they are different. Item names are the model of your home automation. They shouldn’t contain technology specific stuff in the name. The whole point is you don’t have to care what technology the Items are linked to.

So just put the Item names into a List or a Map and grab the Item name out of it.

Though an even better approach would be to name them so you can build the Item name from the index.

    val List<String> itms = newArrayList("28FFF0888317044E",
                                         "28FF0E8B831704C2",
                                         "28FFF3B39017052C",
                                         "28FF8B89831704DC")
                               
    for (i : 0 ..< 4) {
        try {
            adr = json_status.split("\\;").get(i*2).split("\\:").get(1)
            value = Float::parseFloat( json_status.split("\\;").get(i*2+1).split("\\:").get(1)

            adr_hex = new StringBuilder
            if(adr != "") {
                adr.split("\\.").forEach[ b |
                    val num = Integer::parseInt(b)
                    adr_hex.append(if(num < 16) "0" else Integer::toHexString(val_int).toUpperCase)
                    postUpdate("strLocalWeatherTempDach_"+itms.get(i)+"_ADR", adr_hex.toString)
                    postUpdate("strLocalWeatherTempDach_"+itms.get(i)+"_Val", value.toString)
                ]
            }
        }
        catch(Exception e){
            // log an error message
        }
    }

Or with naming similar to Design Pattern: Associated Items

    for (i : 0 ..< 4) {
        try {
            adr = json_status.split("\\;").get(i*2).split("\\:").get(1)
            value = Float::parseFloat( json_status.split("\\;").get(i*2+1).split("\\:").get(1)

            adr_hex = new StringBuilder
            if(adr != "") {
                adr.split("\\.").forEach[ b |
                    val num = Integer::parseInt(b)
                    adr_hex.append(if(num < 16) "0" else Integer::toHexString(val_int).toUpperCase)
                    postUpdate("strLocalWeatherTempDach_"+i+"_ADR", adr_hex.toString)
                    postUpdate("strLocalWeatherTempDach_"+i+"_Val", value.toString)
                ]
            }
        }
        catch(Exception e){
            // log an error message
        }
    }

If you put your Items into a Group (e.g. Group:String gStrLocalWeather) you can replace

		strLocalWeatherTempDach_28FF8B89831704DC_Adr.postUpdate("Requesting")
		strLocalWeatherTempDach_28FFF0888317044E_Adr.postUpdate("Requesting")
		strLocalWeatherTempDach_28FFF3B39017052C_Adr.postUpdate("Requesting")
		strLocalWeatherTempDach_28FF0E8B831704C2_Adr.postUpdate("Requesting")

with

                gStrLocalWeather.postUpdate("Requesting")

You can eliminate the sendHttpGetRequest by using the caching config with the HTTP binding. That would eliminate the need for the whole updating the Items to “Requesting” anyway.

I’m pretty sure sendHttpGetRequest does not throw any exceptions so the whole try catch on that part of the rule is redundant code that can never be executed.

I assume you are not just using the JSONPATH transfromation because you need to do further transformation of the text that came from the JSONPATH. If that’s the case, you could use HTTP binding - openHAB 2 version which is going to replace the existing HTTP 1.x binding anyway. Then you can chain transformations together to, for example, extract the JSON element you need and then use the REGEX transform and JavaScript transform to further extract from that.

If you did that, 90% of this rule, if not the entire rule would simply go away.