Is there a Simpler way to split a string

Another option is to use the new rule engine, scripted automation, and the helper libraries, which has a json module built in. Let me know if you need help or more information.

That’s a language I know absolutely nothing about, but I’ll give it some thought :slight_smile:

1 Like

Well, you didn’t know anything about the rules DSL when you started with OH either :stuck_out_tongue_winking_eye:! Python is very easy to pickup and there is a lot of information available on the Internet, books, courses, etc. The helper libraries are set up to make it easier to use than the rules DSL. It’s also going to be mainstream for OH 3.0, so you can wait for it or get started now!

1 Like

The trouble is I have a very complicated and extensive system developed since OH 1.6, complete with 350K of rules spread over 23 files written in the current rules DSL. That’ll take considerable work and time to translate into a language I know nothing about. My system runs everything from waking us up through to controlling the temperature, failure would be very inconvenient, and I outgrew a Pi2 and Pi3 combination months ago. That together with the inevitable things that don’t work as expected when they are rewritten is daunting on a stable system. Whilst I have a long standing background in IT OpenHAB is the first thing I’ve programmed.
Is this a mature language or still evolving? I’m keen to avoid the “I’ve updated it does anything work as expected” scenario
Is it pure Python and can it run Python scripts inside OH rather than from a command prompt? Perhaps as a first thing I could experiment with the simple Python Script I hacked together to read a couple of sensors.

Do you mean 350K LOC?! If so, that has got to be a record! I went from 6000+ LOC in the rules DSL to <2000 LOC using Jython and the helper libraries.

Yes, Jython is mature and very stable. I recommend using 2.7.0, but 2.7.2 is about to be released and 2.7.2b3 has been working very well. There has been work on a v3 and discussion of continuing that effort after the release of 2.7.2.

No.

Yes, this is the only option.

If you post a DSL rule or your script, I will happily migrate it for you to get things started!

That 350K is the uncompressed file size.

I’m busy for the rest of the day, but here’s a simple rule to start with that’s running right now. Let me know if you’d prefer it in txt format as the formatting seems to be messed up. Thanks for taking a look, may as well see what’s in store!

rule "Office Heatpump Control loop"
when Item OfficeHeatpump changed or Item OfficeTemperature changed or Item OfficeCoolingTemperature changed or Item OfficeHeatingTemperature changed or Item OfficeCoolingMode changed then {
KOT = OfficeTemperature.state as Number
var HSP = OfficeHeatingTemperature.state as Number
var CSP = OfficeCoolingTemperature.state as Number
if (OfficeHeatpump.state == ON)	{	
	if (OfficeCoolingMode.state == ON)	{
		if (OfficeHeatpumpMode.state == "cool"){} else {OfficeHeatpumpMode.sendCommand('cool')}
		//cooling code
		if (OfficeHPRequestedTemp === NULL) {OfficeHPRequestedTemp.sendCommand(18.0)}
		switch(KOT)	{
			case KOT > CSP + 0.7	:    {	if (OfficeHPFanSpeed.state == "auto") {} else{
																	OfficeHPFanSpeed.sendCommand("auto")
																}
																if (OfficeHPPower.state == ON) {} else {
																	OfficeHPPower.sendCommand(ON) 
																}
																if (OfficeHPRequestedTemp.state == 18.0){} else {
													//				logInfo("KOR", "need 18.0 OfficeHPRequestedTemp is "+OfficeHPRequestedTemp.state)
																	OfficeHPRequestedTemp.sendCommand(18.0)
																}
															}
			case KOT > CSP + 0.3 && KOT <= CSP + 0.6	:	{	if (OfficeHPFanSpeed.state == "auto") {} else{
																	OfficeHPFanSpeed.sendCommand("auto")
																}
																if (OfficeHPPower.state == ON) {} else {
																	OfficeHPPower.sendCommand(ON)
																}
																if (OfficeHPRequestedTemp.state == CoolIRTemp){} else {
												//					logInfo("KOR", "need CoolIRTemp OfficeHPRequestedTemp is "+OfficeHPRequestedTemp.state)
																	OfficeHPRequestedTemp.sendCommand(CoolIRTemp)
																}
															}	
			case KOT > CSP && KOT <=CSP + 0.2			: 	{	if (OfficeHPFanSpeed.state == "min") {} else{
																	OfficeHPFanSpeed.sendCommand("min")
																}
																if (OfficeHPPower.state == ON) {} else {
																	OfficeHPPower.sendCommand(ON)
																}
																if (OfficeHPRequestedTemp.state == CoolIRTemp){} else {
												///					logInfo("KOR", "need CoolIRTemp OfficeHPRequestedTemp is "+OfficeHPRequestedTemp.state)
																	OfficeHPRequestedTemp.sendCommand(CoolIRTemp)
																}
															}	
			case KOT < CSP - 0.1						:	if (OfficeHPPower.state == OFF) {} else {
																OfficeHPPower.sendCommand(OFF)
																}
		}
	}
	if (OfficeCoolingMode.state == OFF)	{	//heating code
		if (OfficeHeatpumpMode.state == "heat") {} else {OfficeHeatpumpMode.sendCommand("heat")}
		if (OfficeHPRequestedTemp === NULL) {OfficeHPRequestedTemp.sendCommand(28.0)}
		switch(KOT)	{
			case KOT < HSP - 1.1						:	{	if (OfficeHPFanSpeed.state == "auto") {} else{
																OfficeHPFanSpeed.sendCommand("auto")
													//			logInfo("KOR", "need auto OfficeHPFanSpeed is "+OfficeHPFanSpeed.state)
																}
																if (OfficeHPPower.state == ON) {} else { OfficeHPPower.sendCommand(ON)
													//				logInfo("KOR", "need on top OfficeHPPower is "+OfficeHPPower.state)
																	}
																if (OfficeHPRequestedTemp.state == 28.0) {} else {
													//				logInfo("KOR", "need 28 OfficeHPRequestedTemp is "+OfficeHPRequestedTemp.state)
																	OfficeHPRequestedTemp.sendCommand(28)
																	}
															}	
			case KOT < HSP - 0.2 && KOT > HSP - 0.9	:	{	if (OfficeHPFanSpeed.state == "min") {} else {
																	OfficeHPFanSpeed.sendCommand("min")
																	//logInfo("KOR", "need min OfficeHPFanSpeed is "+OfficeHPFanSpeed.state)
																	}
																if (OfficeHPRequestedTemp.state == CoolIRTemp){} else {
													//				logInfo("KOR", "need CoolIRTemp OfficeHPRequestedTemp is "+OfficeHPRequestedTemp.state)
																	OfficeHPRequestedTemp.sendCommand(CoolIRTemp)
																	}
																if (OfficeHPPower.state == ON) {} else {
																	OfficeHPPower.sendCommand(ON)}
																	}
			case KOT > HSP								:		if (OfficeHPPower.state == OFF){} else { OfficeHPPower.sendCommand(OFF) }
			}
		}
	} else if (OfficeHPPower.state == OFF) {} else {OfficeHPPower.sendCommand(OFF)}
}
end

This rule could be cleaned up, but I’ve tried to show a side-by-side comparison with what you had. I’ve tried to keep it a pretty straight conversion, but I corrected a couple issues with your rule. You did not include a definition for CoolIRTemp, which I assume is a global variable in the DSL rule. For this Jython version, I made it a DecimalType.

from core.rules import rule
from core.triggers import when

COOL_IR_TEMP = DecimalType(55)

#rule "Office Heatpump Control loop"
@rule("Office Heatpump Control loop")
#when Item OfficeHeatpump changed or Item OfficeTemperature changed or Item OfficeCoolingTemperature changed or Item OfficeHeatingTemperature changed or Item OfficeCoolingMode changed then {
@when("Item OfficeHeatpump changed")
@when("Item OfficeTemperature changed")
@when("Item OfficeCoolingTemperature changed")
@when("Item OfficeHeatingTemperature changed")
@when("Item OfficeCoolingMode changed")
def office_heatpump_control(event):
    #KOT = OfficeTemperature.state as Number
    KOT = items["OfficeTemperature"].doubleValue()
    #var HSP = OfficeHeatingTemperature.state as Number
    HSP = items["OfficeHeatingTemperature"].doubleValue()
    #var CSP = OfficeCoolingTemperature.state as Number
    CSP = items["OfficeCoolingTemperature"].doubleValue()
    #if (OfficeHeatpump.state == ON)	{	
    if items["OfficeHeatpump"] == ON:
        #if (OfficeCoolingMode.state == ON)	{
        if items["OfficeCoolingMode"] == ON:
            #if (OfficeHeatpumpMode.state == "cool"){} else {OfficeHeatpumpMode.sendCommand('cool')}
            if items["OfficeHeatpumpMode"] != StringType("cool"):
                events.sendCommand("OfficeHeatpumpMode", "cool")
            #//cooling code
            #if (OfficeHPRequestedTemp === NULL) {OfficeHPRequestedTemp.sendCommand(18.0)}
            # as written, this is never possible, so you probably meant to do this
            if isinstance(items["OfficeHPRequestedTemp"], UnDefType):
                events.sendCommand("OfficeHPRequestedTemp", "18.0")
            #switch(KOT)	{
            # switch/case does not exist in Python
            #    case KOT > CSP + 0.7	:    {	if (OfficeHPFanSpeed.state == "auto") {} else{
            #                                                            OfficeHPFanSpeed.sendCommand("auto")
            #                                                        }
            #                                                        if (OfficeHPPower.state == ON) {} else {
            #                                                            OfficeHPPower.sendCommand(ON) 
            #                                                        }
            #                                                        if (OfficeHPRequestedTemp.state == 18.0){} else {
            #                                            //				logInfo("KOR", "need 18.0 OfficeHPRequestedTemp is "+OfficeHPRequestedTemp.state)
            #                                                            OfficeHPRequestedTemp.sendCommand(18.0)
            #                                                        }
            #                                                    }
            if KOT > CSP + 0.7:
                if items["OfficeHPFanSpeed"] != StringType("auto"):
                    events.sendCommand("OfficeHPFanSpeed", "auto")
                if items["OfficeHPPower"] != ON:
                    events.sendCommand("OfficeHPPower", "ON")
                if items["OfficeHPRequestedTemp"] != DecimalType(18.0):
                    #office_heatpump_control.log.info("need 18.0 OfficeHPRequestedTemp is {}".format(items["OfficeHPRequestedTemp"]))
                    events.sendCommand("OfficeHPRequestedTemp", "18.0")
            #    case KOT > CSP + 0.3 && KOT <= CSP + 0.6	:	{	if (OfficeHPFanSpeed.state == "auto") {} else{
            #                                                            OfficeHPFanSpeed.sendCommand("auto")
            #                                                        }
            #                                                        if (OfficeHPPower.state == ON) {} else {
            #                                                            OfficeHPPower.sendCommand(ON)
            #                                                        }
            #                                                        if (OfficeHPRequestedTemp.state == CoolIRTemp){} else {
            #                                        //					logInfo("KOR", "need CoolIRTemp OfficeHPRequestedTemp is "+OfficeHPRequestedTemp.state)
            #                                                            OfficeHPRequestedTemp.sendCommand(CoolIRTemp)
            #                                                        }
            #                                                    }
            elif KOT > CSP + 0.3 and KOT <= CSP + 0.6:
                if items["OfficeHPFanSpeed"] != "auto":
                    events.sendCommand("OfficeHPFanSpeed", "auto")
                if items["OfficeHPPower"] != ON:
                    events.sendCommand("OfficeHPPower", "ON")
                if items["OfficeHPRequestedTemp"] != COOL_IR_TEMP:
                    #office_heatpump_control.log.info("need COOL_IR_TEMP OfficeHPRequestedTemp is {}".format(items"[OfficeHPRequestedTemp"]))
                    events.sendCommand("OfficeHPRequestedTemp", COOL_IR_TEMP.toString())
            #    case KOT > CSP && KOT <=CSP + 0.2			: 	{	if (OfficeHPFanSpeed.state == "min") {} else{
            #                                                            OfficeHPFanSpeed.sendCommand("min")
            #                                                        }
            #                                                        if (OfficeHPPower.state == ON) {} else {
            #                                                            OfficeHPPower.sendCommand(ON)
            #                                                        }
            #                                                        if (OfficeHPRequestedTemp.state == CoolIRTemp){} else {
            #                                        ///					logInfo("KOR", "need CoolIRTemp OfficeHPRequestedTemp is "+OfficeHPRequestedTemp.state)
            #                                                            OfficeHPRequestedTemp.sendCommand(CoolIRTemp)
            #                                                        }
            #                                                    }
            elif KOT > CSP and KOT <= CSP + 0.2:
                if items["OfficeHPFanSpeed"] != "min":
                    events.sendCommand("OfficeHPFanSpeed", "min")
                if items["OfficeHPPower"] != ON:
                    events.sendCommand("OfficeHPPower", "ON")
                if items["OfficeHPRequestedTemp"] != COOL_IR_TEMP:
                    #office_heatpump_control.log.info("need COOL_IR_TEMP OfficeHPRequestedTemp is {}".format(items"[OfficeHPRequestedTemp"]))
                    events.sendCommand("OfficeHPRequestedTemp", COOL_IR_TEMP.toString())
            #    case KOT < CSP - 0.1						:	if (OfficeHPPower.state == OFF) {} else {
            #                                                        OfficeHPPower.sendCommand(OFF)
            #                                                        }
            elif KOT < CSP - 0.1:
                if items["OfficeHPPower"] != OFF:
                    events.sendCommand("OfficeHPPower", "OFF")
            #}
        #}
        #if (OfficeCoolingMode.state == OFF)	{	//heating code
        elif items["OfficeCoolingMode"] == OFF:# heating code
            #if (OfficeHeatpumpMode.state == "heat") {} else {OfficeHeatpumpMode.sendCommand("heat")}
            if items["OfficeHeatpumpMode"] != StringType("heat"):
                events.sendCommand("OfficeHeatpumpMode", "heat")
            #if (OfficeHPRequestedTemp === NULL) {OfficeHPRequestedTemp.sendCommand(28.0)}
            elif isinstance(items["OfficeHPRequestedTemp"], UnDefType):
                events.sendCommand("OfficeHPRequestedTemp", "28.0")
            #switch(KOT)	{
            #    case KOT < HSP - 1.1						:	{	if (OfficeHPFanSpeed.state == "auto") {} else{
            #                                                        OfficeHPFanSpeed.sendCommand("auto")
            #                                            //			logInfo("KOR", "need auto OfficeHPFanSpeed is "+OfficeHPFanSpeed.state)
            #                                                        }
            #                                                        if (OfficeHPPower.state == ON) {} else { OfficeHPPower.sendCommand(ON)
            #                                            //				logInfo("KOR", "need on top OfficeHPPower is "+OfficeHPPower.state)
            #                                                            }
            #                                                        if (OfficeHPRequestedTemp.state == 28.0) {} else {
            #                                            //				logInfo("KOR", "need 28 OfficeHPRequestedTemp is "+OfficeHPRequestedTemp.state)
            #                                                            OfficeHPRequestedTemp.sendCommand(28)
            #                                                            }
            #                                                    }	
            elif KOT < HSP - 1.1:
                if items["OfficeHPFanSpeed"] != "auto":
                    events.sendCommand("OfficeHPFanSpeed", "auto")
                    #office_heatpump_control.log.info("need auto OfficeHPFanSpeed is {}".format(items["OfficeHPFanSpeed"]))
                if items["OfficeHPPower"] != ON:
                    events.sendCommand("OfficeHPPower", "ON")
                    #office_heatpump_control.log.info("need on top OfficeHPPower is {}".format(items["OfficeHPPower"]))
                if items["OfficeHPRequestedTemp"] != DecimalType(28.0):
                    #office_heatpump_control.log.info("need 28 OfficeHPRequestedTemp is {}".format(items["OfficeHPRequestedTemp"]))
                    events.sendCommand("OfficeHPRequestedTemp", "28.0")
            #    case KOT < HSP - 0.2 && KOT > HSP - 0.9	:	{	if (OfficeHPFanSpeed.state == "min") {} else {
            #                                                            OfficeHPFanSpeed.sendCommand("min")
            #                                                            //logInfo("KOR", "need min OfficeHPFanSpeed is "+OfficeHPFanSpeed.state)
            #                                                            }
            #                                                        if (OfficeHPRequestedTemp.state == CoolIRTemp){} else {
            #                                            //				logInfo("KOR", "need CoolIRTemp OfficeHPRequestedTemp is "+OfficeHPRequestedTemp.state)
            #                                                            OfficeHPRequestedTemp.sendCommand(CoolIRTemp)
            #                                                            }
            #                                                        if (OfficeHPPower.state == ON) {} else {
            #                                                            OfficeHPPower.sendCommand(ON)}
            #                                                            }
            elif KOT < HSP - 0.2 and KOT > HSP - 0.9:
                if items["OfficeHPFanSpeed"] != "min":
                    events.sendCommand("OfficeHPFanSpeed", "min")
                    #office_heatpump_control.log.info("need min OfficeHPFanSpeed is {}".format(items["OfficeHPFanSpeed"]))
                if items["OfficeHPPower"] != ON:
                    events.sendCommand("OfficeHPPower", "ON")
                if items["OfficeHPRequestedTemp"] != COOL_IR_TEMP:
                    #office_heatpump_control.log.info("need COOL_IR_TEMP OfficeHPRequestedTemp is {}".format(items"[OfficeHPRequestedTemp"]))
                    events.sendCommand("OfficeHPRequestedTemp", COOL_IR_TEMP.toString())
            #    case KOT > HSP								:		if (OfficeHPPower.state == OFF){} else { OfficeHPPower.sendCommand(OFF) }
            #    }
            #}
            elif KOT > HSP:
                if items["OfficeHPPower"] != OFF:
                    events.sendCommand("OfficeHPPower", "OFF")
        #} else if (OfficeHPPower.state == OFF) {} else {OfficeHPPower.sendCommand(OFF)}
        elif items["OfficeHPPower"] != OFF:
            events.sendCommand("OfficeHPPower", "OFF")
#   }
#end

Thanks @5iver that surprisingly makes sense. Does it have full vscode error checking and if it does is it the same when OH is running on the same Windows machine as vscode or does it need a seperate Linux box?
Can you recommend any “start from the beginning” documentation to read?
My plans for next week have changed and I could have a few hours spare to learn and experiment.

Scripted automation does not yet have support in the OH VSC extension, but it will get there. However, you do have pylint and a lot of other Python tools available in VSC that you do not have for the rules DSL.

Read through everything here, including the links in the References section…

https://openhab-scripters.github.io/openhab-helper-libraries/index.html

For installation, go to the beta Jython bundle topic. Installation will soon be through the UI. I plan to post a large update this weekend too. Whenever anyone asks a question of mentions something is missing, I update the docs. After pushing updates to the helper libraries, I will update the beta Jython bundle too. If you have any questions at all, just ask!

Thanks @5iver is there a dedicated new rules thread I can move this discussion to?
On a different note do have a create a jar from an pull request on github setup working? I’ve discovered the binding which was the original reason for this post has a pull request that could allow my long convoluted rule to be replaced by a later uncompiled version of the experimental Enphase Binding.

Sorry to get your topic off-track! There are several, but this is a good one…

Yes, I have an IDE setup and can build jars. There’s really not much to it, but you can do builds from the command line too.

Blockquote
Yes, I have an IDE setup and can build jars. There’s really not much to it, but you can do builds from the command line too.

Any chance you could build this one?

The other envoy request doesn’t apply to my setup

I’m sorry, but I really don’t want to be in that business. I have plenty of other things to do! Have you asked the developer for a build?

The original developer seems to have lost interest as that Pull has been sat around for over a year.
No matter, I’ll see if I can get the IDE to load without too many errors, last time I used it was a couple of years back so hopefully it will be easier!
Thanks for your help, I’ll ask any questions on a relevant thread is I experiment with JSR223-Jython

I took a peak and in the time this PR has sat around, the build system has been replaced with BND., so it would not be a quick and easy task to get this built.

Thanks, I’ll abandon that one, I got as far as do I have an Oracle account / send me a password reset in the instructions. As I haven’t received a password reset I likely don’t…
That’s the thing I struggle with, as I’m no longer full time in the IT industry I have no idea what “BND” is let alone what implications moving to it has so you’ve just saved me a lot of time and frustration.
My convoluted rule works so I’ll see if I can slim it down a bit :grinning:

1 Like

Note that the Envoy binding isn’t doing all that much. A simple curl command gets the data for you (replace the 6 digits with the last part of your serial number - see this). You can then use the rest API through another curl call to pump the data to an Item. And a script can modify the data or parse it as needed. Probably significantly less time to do this than to try to rebuild the binding.

curl -s --anyauth --user envoy:083581 http://169.254.120.1/api/v1/production/inverters

That’s exactly what I’m currently doing albeit with a 152 line rule. I have to use the installer user on mine as the envoy user doesn’t work on my ancient Envoy. That’s an interesting link.

You might find it easier to code the parsing in python or another language, then POST to Items instead of trying to write the rules in DSL. May or may not be easier that way.

For a sample, you can see this post. It’s for a propane tank, but it’s the same idea. Get the data and parse it a bit in python, then POST it to openHAB.

1 Like