@wim19 @Denethor Here is what I currently have.
In the openhab.cfg file I have the following for the TCP/UDP binding.
################################# TCP - UDP Binding ###################################
# tcp:port=25001
# tcp:reconnectcron=0 0 0 * * ?
# tcp:retryinterval=5
# tcp:queue=true
tcp:buffersize=1024
tcp:itemsharedconnections=true
tcp:bindingsharedconnections=true
tcp:directionssharedconnections=true
tcp:addressmask=false
# tcp:preamble=
# tcp:postamble=\r\n
tcp:blocking=true
# tcp:timeout=3000
tcp:updatewithresponse=true
tcp:refreshinterval=250
# tcp:selecttimeout=1000/* My NHC startup item */
# tcp:charset=ASCII
In the items file I have a generic NHC start item and one item per switch/dimmer:
/* NHC Start */
String NHC_Start {tcp=">[192.168.0.121:8000:'REGEX((.*))']"}
/* Lights NHC */
Switch Licht_Garage "Garage" (gBeneden_Garage, gLichten) {tcp=">[192.168.0.121:8000:'JS(4.js)']"}
Dimmer Licht_Eetkamer "Eetkamer [%d %%]" <slider> (gBeneden_Eetkamer, gLichten) {tcp=">[192.168.0.121:8000:'JS(29.js)']"}
NHC_Start is triggered with one simple rule:
import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
/**
* The following rules help initializing the items with some helpful states.
*/
rule "Initialize NHC"
when
System started
then
sendCommand(NHC_Start, "{\"cmd\":\"startevents\"}")
sendCommand(NHC_Start, "{\"cmd\":\"listactions\"}")
end
As all items are on the same TCP connection, they should each interpret the JSON feedback string and set the initial state. For this to work, I created a JavaScript transform:
(function(input_value) {
// This function maps all OpenHab switches and dimmers to Niko Home Control switches or dimmers
// for use with the TCP/UDP binding.
// This mapping file has to exist for each switch or dimmer. The constants at the top of this file
// need to be adjusted accordingly.
// constant to indentify Niko Home Control switch or dimmer
var item_id = x;
// constant to identify if this is a switch or dimmer, valid values are "switch" or "dimmer"
var item_type = x;
var result = "unknown";
var json_arr, i, response;
if (input_value == "ON") {
// This is a switch to be turned on
result = "{\"cmd\":\"executeactions\",\"id\":" + item_id + ",\"value1\":100}";
} else if (input_value == "OFF") {
// This is a switch to be turned off
result = "{\"cmd\":\"executeactions\",\"id\":" + item_id + ",\"value1\":0}";
} else if (/^[0-9]*$/.test(input_value)) {
// This is a dimmer to be set to a value between 0 and 100
result = "{\"cmd\":\"executeactions\",\"id\":" + item_id + ",\"value1\":" + input_value + ",\"value2\":0}";
} else {
// Here is the feedback from Niko Home Control
// There can be multiple feedback, therefore split in multiple JSON strings
// and only interpret the one matching this item.
// If this is a switch, it should be ON or OFF.
// For a dimmer this should give the value between 0 and 100.
json_arr = input_value.split("\n");
json_arr_length = json_arr.length;
if (json_arr[json_arr_length - 1] == "") { json_arr_length--; };
i = 0;
do {
print("JSON " + i + ": " + json_arr[i]);
response = JSON.parse(json_arr[i]);
try {
if (response.event == "listactions") {
if (response.data[0].id == item_id){
result = response.data[0].value1;
};
if (item_type == "switch") {
if (result == 0) {
result = "OFF";
} else {
result = "ON";
}
};
print("State update: " + item_type + " " + item_id + " result " + result);
} else if (response.cmd == "executeactions") {
if (response.data.error == 0) {
// command got executed well
print("Success: " + item_type + " " + item_id);
} else {
// error executing command
print("Error " + response.data.error + ": " + item_type + " " + item_id);
};
} else if (response.cmd == "listactions") {
// this gets parsed at initialization in response the the cmd:listactions request
for (var key in response.data) {
if (response.data[key].id == item_id) {
result = response.data[key].value1;
if (item_type == "switch") {
if (result == 0) {
result = "OFF";
} else {
result = "ON";
}
};
print("State initialized: " + item_type + " " + item_id + " " + result);
};
};
};
} catch (e) {
// no match for JSON
print("JSON not interpreted");
};
i++;
} while ((result == "unknown") && (i < json_arr_length));
};
return result;
})(input)
There are 2 parameters in the script above (item_id and item_type). One copy of this script should exist in the transform folder for each switch or dimmer as I don’t know a way to pass parameters from the items file to a transform script. The name for the script is the number of switch or dimmer (=item_id) and is what you see in the call to the script in the item file. Replace the x for the item_id and item_type with the appropriate values (number and switch/dimmer).
I have automated the process of creating the multiple transform script copies using a simple awk script and batch file. That allows me to keep one master copy (the version above) and auto-generate all when I do updates. Here is the awk script and a sample batch file for a switch and a dimmer. The batch file then contains a line for each switch/dimmer.
Awk script (create_NHC_js.awk):
# replace constants with their value
/item_id = x/ {printf "\tvar item_id = %i;\n", item_id}
/item_type = x/ {printf "\tvar item_type = \"%s\";\n", item_type}
# default
! /item_.* = x/ {print $0}
Windows batch file:
gawk -v item_id=4 -v item_type="switch" -f create_NHC_js.awk NHC.js > 4.js
gawk -v item_id=29 -v item_type="dimmer" -f create_NHC_js.awk NHC.js > 29.js
This is all I have at the moment.
I believe this can be improved a lot. First, without creating a binding yet, it would probably be better to only have one item directly linked to the NHC gateway and have the switches and dimmers as virtual (string) items. When these change, a rule should than update the one linked item and the other way around. That would avoid all these items trying to interpret the same JSON and filter out the relevant parts for the item. I think my approach will lead to problems when there are a larger number of switches and dimmers. Especially dimmers will cause problems as these generate multiple updates.
Secondly, the IP address is fixed in the items file. I noticed in my install this IP address may change. I don’t have the possibility through my router to fix the IP address for the NHC gateway. Using wireshark, I noticed there is some kind of discovery for the IP going on at start (using the Android app on an Android emulator on Windows), using UDP packets. Getting this is probably something for which a separate binding would be better suited. As the feedback from NHC describes the full install, it would actually allow discovery in openHAB 2. Someone with more Java skills than I have would need to tackle that though.
A 3rd future (I know I am dreaming now) would be to not only use switches and dimmers (and all other elements addressed by the NHC app), but also the elements touched by the NHC configuration app. E.g. I defined a presence simulation in NHC. But as I don’t have a twilight switch in my NHC install, this presence simulation is set up with fixed times of the day. I would like to be able to overwrite them from NHC with values from the astro binding. I prefer this above defining the presence simulation in rules in openHAB as I see this as more robust.
That’s the story. All input welcome. I don’t have much time to spend on this though. Good luck with your configs.