Hey Oliver,
sorry for the wait. I had a lot to do… But, as promised, here my configuration including the connection to Amazon Alexa.
I mentioned my hardware (nanoCUL-Stick and openhabian 3 on a Rasperry Pi 4) earlier in this thread.
Let’s begin:
under /etc/openhab/rules the rule for the shutters:
import org.openhab.core.model.script.ScriptServiceUtil
/*
=======================================================================================================================
DESCRIPTION:
Change the following data in this script: Group name, scriptName
All devices have to be a member of the group.
Syntax of item names:
<itemName>_control (Type: STRING; valid commands: 0 or UP, STOP or MY, 100 or DOWN, PROG, NUMBER:dimensionless [1..99])
<itemName>_rollingCode (Type: STRING; persisted item, which stores the last rollingCode)
<itemName>_calibration (Type: NUMBER; persisted item, which stores the duration of a half cycle from 0% to 100% in seconds. Required to control the motor with a command like 60[%])
<itemName>_address (Type: STRING; persisted item, which stores address of device)
Make sure that Items for rollingcode, Calibration and Address are not NULL
If there are problems, set debug variable in this script to true
still to be done:
more error checking, esp. if item is null
check return string of python script
or use serial binding
=======================================================================================================================
=======================================================================================================================
CHANGE VALUES ACCORDINGLY: */
var Boolean debug = true
val String fileName = "Shutter_BueroFlo.rules"
val String scriptCommand = "python3"
val String scriptName = "/etc/openhab/scripts/RTS.py"
var Timer tScript = null
rule "RTS BueroFlo"
//======================================================================================================================
when
Member of gShutter_BueroFlo received command
then
if (debug) logInfo(fileName,"Rule started")
tScript?.cancel
var String strThing = triggeringItem.name.toString().split("_").get(0)
var String strItem = triggeringItem.name.toString()
var String strCommand = receivedCommand.toString()
if (debug) logInfo(fileName, "Thing: '{}', Item: '{}', Command: '{}'",strThing,strItem,strCommand)
var String strRTSCode = ""
var Integer intCommand = 0
var Float fSeconds = 0.0
//find regular commands
switch strCommand.toUpperCase {
case "" : {
logInfo(fileName,"Item: '{}' received empty command",strItem)
return
}
case "MY", case "STOP" : strRTSCode = "1"
case "UP" : strRTSCode = "2"
case "DOWN" : strRTSCode = "4"
case "PROG" : strRTSCode = "8"
//find number values in command
default: {
if (debug) logInfo(fileName,"No standard commands submitted")
var strCommand2 = transform("REGEX", "\\D*(\\d+)\\D*", strCommand)
if (debug) logInfo(fileName,"strCommand2: '{}'",strCommand2)
if (strCommand2 !== null) {
intCommand = Integer.parseInt(strCommand2)
if (debug) logInfo(fileName,"intCommand: '{}'",intCommand.toString())
switch intCommand {
case 0 : strRTSCode = "2"
case 100 : strRTSCode = "4"
case intCommand > 100 : logInfo(fileName,"Item: '{}' received command '{}': not in range [0..100]",strItem,intCommand.toString())
//calculate time when motor has to stop
default: {
strRTSCode = "4"
var Float fCalibration = (ScriptServiceUtil.getItemRegistry.getItem(strThing+"_calibration").state as DecimalType).floatValue
if (debug) logInfo(fileName, "fCalibration: '{}'",fCalibration.toString())
fSeconds = intCommand.floatValue * fCalibration / 100
if (debug) logInfo(fileName, "fSeconds: '{}'",fSeconds.toString())
}
}
} else {
logInfo(fileName,"Item: '{}' received unknown command '{}'",strItem,strCommand)
return
}
}
}
//prepare serial data payload for python script
//Syntax: Ys + EncryptionKey=A1 + Command + 0 + RollingCode + Address
var Integer intRollingCode = (ScriptServiceUtil.getItemRegistry.getItem(strThing + "_rollingCode").state as DecimalType).intValue
if (debug) logInfo(fileName, "intRollingCode: '{}'",intRollingCode.toString())
var String strRollingCodeHex = String::format("%04X", intRollingCode)
var String strAddress = ScriptServiceUtil.getItemRegistry.getItem(strThing + "_address").state.toString()
if (debug) logInfo(fileName, "PAYLOAD: strRTSCode: '{}', strRollingCodeHex: '{}', strAddress: '{}'",strRTSCode,strRollingCodeHex,strAddress)
var String strPayLoad = "YsA1" + strRTSCode + "0" + strRollingCodeHex + strAddress + "\n"
if (debug) logInfo(fileName, "PAYLOAD STRING: '{}'",strPayLoad)
var result = executeCommandLine(Duration.ofSeconds(2),scriptCommand, scriptName, strPayLoad)
logInfo(fileName, "Python script result: {}",result)
ScriptServiceUtil.getItemRegistry.getItem(strThing + "_rollingCode").postUpdate(intRollingCode + 1)
//send STOP after fSeconds if command is number from 1 to 99
if (fSeconds > 0.0) {
strRTSCode = "1"
strRollingCodeHex = String::format("%04X", intRollingCode +1)
strPayLoad = "YsA1" + strRTSCode + "0" + strRollingCodeHex + strAddress + "\n"
if (debug) logInfo(fileName, "PAYLOAD STRING timer triggered: '{}'",strPayLoad)
tScript = createTimer(now.plusSeconds(fSeconds.longValue), [ |
var result = executeCommandLine(Duration.ofSeconds(2),scriptCommand, scriptName, strPayLoad)
logInfo(fileName, "Python script result timer triggered: {}",result)
ScriptServiceUtil.getItemRegistry.getItem(strThing + "_rollingCode").postUpdate(intRollingCode + 2)
])
}
end
under /etc/openhab/scripts the RTS-Python script, where I needed to change the coding of the Payload (notice the line with ser.write):
#!/usr/bin/python3
# cudos go to chacha
# Install pyserial from https://pythonhosted.org/pyserial/pyserial.html#installation
# add 'stty -F /dev/ttyUSB0 -hupcl' to file /etc/rc.local
import serial
import sys
payLoad = sys.argv[1]
print("Python script received payload " + payLoad)
ser = serial.Serial('/dev/ttyUSB0', 38400, timeout=.1)
#or: ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)
ser.write(payLoad.encode())
ser.close()
under /etc/openhab/items the next following items:
the _address.items-Item:
String ShutterBueroFlo_address "Address: [%s]"
the _calibration.items-Item:
Number ShutterBueroFlo_calibration "Calibration: [%d]%%"
the _control.items-Item:
Number:Dimensionless ShutterBueroFlo_control "Command [%d]"
the _rollingCode.items-Item:
Number ShutterBueroFlo_rollingCode "RollingCode [%s]"
Additionally I added a Rollershutter item, which is in the group of gShutter_BueroFlo. This triggeres the rule, mentioned above. More importantly I use this item for the connection to Alexa, but I will come to that later in the post. So here the Rollershutter item:
Rollershutter ShutterBueroFlo "Rolladen Flo Office" <rollershutter> (gShutter_BueroFlo)
As Oliver explained, we need a persistence for the rollingCode-item, so we need to change/update/create the file /etc/openhab/persistence/rrd4j.persist and add this line/lines:
Items {
gShutter_BueroFlo* : strategy = restoreOnStartup
}
To be able to control the shutters, I need to put them in my openhab-sitemap unter /etc/openhab/sitemaps:
Group item=BueroFlo label="Büro Flo" icon=office
{
Switch item=ShutterBueroFlo icon=rollershutter label="Rolladen Büro Flo"
Slider item=ShutterBueroFlo_calibration icon=rollershutter label="Stand Rolladen Büro Flo"
Group item=BueroFlo_rolladeninfo label="Rolladeninfo"
{
Text item=ShutterBueroFlo_control icon=network label="Command Code [%s]"
Text item=ShutterBueroFlo_address icon=rollershutter label="Adresse [%s]"
Text item=ShutterBueroFlo_calibration icon=rollershutter label="Stand [%d]%%"
Text item=ShutterBueroFlo_rollingCode icon=rollershutter label="RollingCode [%s]"
}
}
For troubleshooting reasons I also wanted to see the control-, address-, calibration- and rollingCode-items. So you can ignore the part after “Rolladeninfo”. The main items here are the switch and slider item for the control of the rollershutters.
Now, to implement Alexa in this whole setup (thanks again to all of you):
The first requirement to use Alexa is the openHAB Cloud Connector, see here.
The second requirement is the integration from openHAB Cloud to your Amazon Account. I found a very good step-by-step-guide here.
For Alexa to work, we need to make a change in the rollershutter item and add a few things:
Rollershutter ShutterBueroFlo "Rolladen Flo Office" <rollershutter> (gShutter_BueroFlo) {alexa="RangeController.rangeValue" [category="EXTERIOR_BLIND", friendlyNames="@Setting.Opening", supportedRange="0:100:10", unitOfMeasure="Percent", actionMappings="Close=0,Open=100,Lower=(-10),Raise=(+10)", stateMappings="Closed=0,Open=1:100"]}
After that it should pop up as a device in your Alexa App, when you scan for new devices in your Alexa App. Mappings and assignments in the Alexa App are up to you.
After that you can control the Rollershutters via your voice. So far - depending on the language - these commands work after saying “Alexa %Name of Rollershutter device%” in my configuration “Alexa Rolladen Büro Flo”:
"UP", "100", "OPEN", "AUF" -> for opening the shutter
"CLOSE", "0", "SHUT", "DOWN", "RUNTER" -> for closing the shutter
Of course you can configure your “own” commands by adding a scene(?) for Alexa, where you put in the actual command and map it to a saying like “Good night” (for closing the rollershutters). But I did not implement it until now. AND this is another topic and has nothing to do with this thread.
Oliver, sorry for the long post, you only wanted to know about the “.encode”-command in the python script, but I thought, that maybe someome could benefit from this post.
best regards to all and thank you again for this amazing script!
Flo