# 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%%]" (gshutters) ["OpenLevel", "Blinds"] {alexa="PercentageController.percentage" [category="SWITCH"]} // Office Group:Rollershutter:AVG gShuttersOffice "Rollläden Büro [%d%%]" (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 ```