velux-cli/openhab.md

212 lines
6.2 KiB
Markdown

# OpenHab integration using `exec` binding
## generate `token.json`
Login to Velux using username and password
```
CLIENT_ID=".."
CLIENT_SECRET=".."
USERNAME="email"
PASSWORD=""
curl -v -d "grant_type=password&client_id=${CLIENT_ID}&client_secret=${CLIENT_SECRET}&username=${USERNAME}&password=${PASSWORD}&user_prefix=velux" https://app.velux-active.com/oauth2/token
```
Then create a file named `token.json`
```
{
"token": {
"access_token": "5...d|5...0",
"refresh_token": "5...d|c...4",
"scope": [
"all_scopes"
],
"expires_in": 10800
},
"refreshed": "2019-08-11T21:42:51.597296912+02:00"
}
```
## Define Shutters including Alexa integration
```
// South
Group:Rollershutter:AVG gShuttersSouth
"Rollläden Süd [%d%%]"
<rollershutter>
(gshutters)
["OpenLevel", "Blinds"]
{alexa="PercentageController.percentage" [category="SWITCH"]}
// Office
Group:Rollershutter:AVG gShuttersOffice
"Rollläden Büro [%d%%]"
<rollershutter>
(Office, gshutters)
["OpenLevel", "Blinds"]
{alexa="PercentageController.percentage" [category="SWITCH"]}
Rollershutter OfficeLeft_Rollershutter
"Rollladen Büro Links [%d%%]"
(Office, gshutters, gShuttersOffice, gShuttersSouth)
["OpenLevel", "Blinds"]
{alexa="PercentageController.percentage" [category="SWITCH"]}
Rollershutter OfficeRight_Rollershutter
"Rollladen Büro Rechts [%d%%]"
(Office, gshutters, gShuttersOffice, gShuttersSouth)
["OpenLevel", "Blinds"]
{alexa="PercentageController.percentage" [category="SWITCH"]}
```
## Update item state
### file `things/velux.things`
```
Thing exec:command:velux [command="/openhab/conf/velux-cli dump -outfile - -tokenfile /openhab/conf/token.json", interval=30, timeout=20]
```
### file `items/trigger.items`
```
String VeluxExec "velux status update" {channel="exec:command:velux:output"}
```
### file `rules/velux.rules`
```
val mapRollerShutterState = [ String veluxName, String state |
100-Integer::parseInt(transform("JSONPATH", "$.ShutterStatus['" + veluxName + "']", state))
]
rule "velux item update"
when
Item VeluxExec changed
then
val jsonstate = triggeringItem.state.toString
logInfo("velux item update", jsonstate)
val temp = transform("JSONPATH", "$.Temperature.Bedroom", jsonstate)
VeluxBedroomTemperature.postUpdate(Integer::parseInt(temp)/10.0)
val co2 = transform("JSONPATH", "$.Co2.Bedroom", jsonstate)
VeluxBedroomCo2.postUpdate(Integer::parseInt(co2))
val humidity = transform("JSONPATH", "$.Humidity.Bedroom", jsonstate)
VeluxBedroomHumidity.postUpdate(Integer::parseInt(humidity))
val airQuality = transform("JSONPATH", "$.AirQuality.Bedroom", jsonstate)
VeluxBedroomAirQuality.postUpdate(Integer::parseInt(airQuality))
val lux = transform("JSONPATH", "$.Lux.Bedroom", jsonstate)
VeluxBedroomLux.postUpdate(Integer::parseInt(lux))
OfficeLeft_Rollershutter.postUpdate(mapRollerShutterState.apply("Büro links", jsonstate))
OfficeRight_Rollershutter.postUpdate(mapRollerShutterState.apply("Büro rechts", jsonstate))
# ...
LivingroomLeft_Rollershutter.postUpdate(mapRollerShutterState.apply("Wohnzimmer links", jsonstate))
LivingroomRight_Rollershutter.postUpdate(mapRollerShutterState.apply("Wohnzimmer rechts", jsonstate))
val batterySensor = transform("JSONPATH", "$.BatteryPercent['Sensor switch 1']", jsonstate)
VeluxSensor_BatteryLevel.postUpdate(Integer::parseInt(batterySensor))
if (Integer::parseInt(batterySensor) < 10) {
VeluxSensor_BatteryLow.postUpdate(ON)
} else {
VeluxSensor_BatteryLow.postUpdate(OFF)
}
val batteryDeparture = transform("JSONPATH", "$.BatteryPercent['Departure switch 1']", jsonstate)
VeluxDeparture_BatteryLevel.postUpdate(Integer::parseInt(batteryDeparture))
if (Integer::parseInt(batteryDeparture) < 10) {
VeluxDeparture_BatteryLow.postUpdate(ON)
} else {
VeluxDeparture_BatteryLow.postUpdate(OFF)
}
end
```
## Control Shutters
### file `rules/velux.rules`
```
val mapRollerShutterState = [ String veluxName, String state |
100-Integer::parseInt(transform("JSONPATH", "$.ShutterStatus['" + veluxName + "']", state))
]
rule "shutters command"
when
Member of gshutters received command
then
logInfo("shutters", triggeringItem.name + ": " + triggeringItem.state.toString + " - " + receivedCommand)
if (triggeringItem instanceof GroupItem) {
return
} else {
var int position
if (receivedCommand == UP) {
position = 100
} else if (receivedCommand == DOWN) {
position = 0
} else if (receivedCommand == STOP) {
position = 100
} else if (receivedCommand instanceof Number) {
position = 100 - receivedCommand.intValue
}
var String veluxShutter
switch (triggeringItem) {
case OfficeLeft_Rollershutter: veluxShutter = "Büro links"
case OfficeRight_Rollershutter: veluxShutter = "Büro rechts"
# ...
case LivingroomRight_Rollershutter: veluxShutter = "Wohnzimmer rechts"
case LivingroomLeft_Rollershutter: veluxShutter = "Wohnzimmer links"
}
logInfo("shutters", "moving " + veluxShutter + " to " + position)
logInfo("shutters", "/openhab/conf/velux-cli@@moveShutters@@-tokenfile@@/openhab/conf/token.json@@-shutters@@" + veluxShutter + "@@-pos@@" + position, 1000)
val String Answer = executeCommandLine("/openhab/conf/velux-cli@@moveShutters@@-tokenfile@@/openhab/conf/token.json@@-shutters@@" + veluxShutter + "@@-pos@@" + position, 1000)
logInfo("shutters", "Result:" + Answer)
}
end
```
## Usage in rules
```
var tempThreshold = 28|"℃"
rule "Close south shutters to 50% at 8:30 when too warm"
when
Time cron "0 30 8 ? * * *"
then
if (gWeatherForecastTodayMaxTemperature.state >= tempThreshold) {
if (gShuttersSouth.state == 0) {
gShuttersSouth.sendCommand(50)
} else {
logInfo("shutters", "some shutters already down: " + gShuttersSouth.state)
}
} else {
logInfo("shutters", "keeping shutters open temperture below " + tempThreshold + ": " + gWeatherForecastTodayMaxTemperature.state)
}
end
rule "Open south shutters at 18:00"
when
Time cron "0 0 18 ? * * *"
then
if (gShuttersSouth.state == 50 || gShuttersSouth.state == 70) {
gShuttersSouth.sendCommand(0)
} else {
logInfo("shutters", "some shutters already down up: " + gShuttersSouth.state)
}
end
```