Uponor Smatrix binding request

The reason is that when you have 11 or more rooms the room id will be higher than 500 and the id parsing breaks. I will see if I can fix it.

EDIT:
If only have one module you could try changing:

var tmp = id.intValue() % 500;

to:

var tmp = id.intValue()

It should work.

This should work:

import java.util.HashMap

val numRooms = 6
val numModules = 1

val Procedures$Procedure3<List, Integer, Integer> processRequest = [ itemQueryList, roomNumber, moduleNumber |
    val HashMap<String, String> idMap = newHashMap(
        "20" -> "module_id",
        "21" -> "cooling_available",
        "22" -> "holiday_mode",
        "23" -> "forced_eco_mode",
        "24" -> "hc_mode",
        "25" -> "hc_masterslave",
        "26" -> "ts_sv_version",
        "27" -> "holiday_setpoint",
        "28" -> "average_temp_low",
        "29" -> "low_temp_alarm_limit",
        "30" -> "low_temp_alarm_hysteresis",
        "31" -> "remote_access_alarm",
        "32" -> "device_lost_alarm",
        "33" -> "no_comm_controller1",
        "34" -> "no_comm_controller2",
        "35" -> "no_comm_controller3",
        "36" -> "no_comm_controller4",
        "37" -> "average_room_temperature",
        "38" -> "controller_presence",
        "39" -> "allow_hc_mode_change",
        "40" -> "hc_master_type",

        "60" -> "output_module",
        "61" -> "rh_deadzone",
        "62" -> "controller_sv_version",
        "63" -> "thermostat_presence",
        "64" -> "supply_high_alarm",
        "65" -> "supply_low_alarm",
        "66" -> "average_room_temperature_NO",
        "67" -> "measured_outdoor_temperature",
        "68" -> "supply_temp",
        "69" -> "dehumidifier_status",
        "70" -> "outdoor_sensor_presence",

        "80" -> "eco_profile_active_cf",
        "81" -> "dehumidifier_control_activation",
        "82" -> "rh_control_activation",
        "83" -> "eco_profile_number",
        "84" -> "setpoint_write_enable",
        "85" -> "cooling_allowed",
        "86" -> "rh_setpoint",
        "87" -> "min_setpoint",
        "88" -> "max_setpoint",
        "89" -> "min_floor_temp",
        "90" -> "max_floor_temp",
        "91" -> "room_setpoint",
        "92" -> "eco_offset",
        "93" -> "eco_profile_active",
        "94" -> "home_away_mode_status",
        "95" -> "room_in_demand",
        "96" -> "rh_limit_reached",
        "97" -> "floor_limit_status",
        "98" -> "technical_alarm",
        "99" -> "tamper_indication",
        "100" -> "rf_alarm",
        "101" -> "battery_alarm",
        "102" -> "rh_sensor",
        "103" -> "thermostat_type",
        "104" -> "regulation_mode",
        "105" -> "room_temperature",
        "106" -> "room_temperature_ext",
        "107" -> "rh_value",
        "108" -> "ch_linked_to_th",
        "109" -> "room_name",
        "110" -> "utilization_factor_24h",
        "111" -> "utilization_factor_7d",
        "112" -> "reg_mode",
        "113" -> "channel_average",
        "114" -> "radiator_heating")

    val url = "http://<ip>/api"
    val contenttype = "application/json"
    var POSTrequest = '{"jsonrpc":"2.0", "id":8, "method":"read", "params":{ "objects":[%s]}}'
    val request = String.format(POSTrequest, itemQueryList.join(','))
    val json = sendHttpPostRequest(url, contenttype, request);
    val count = Integer::parseInt(transform("JSONPATH", "$.result.objects.length()", json));
    for(var i = 0; i < count; i++) {
        if (transform("JSONPATH", "$.result.objects[" +i+ "]", json).contains("value")) {
            val Number id = Integer::parseInt(transform("JSONPATH", "$.result.objects[" +i+ "].id", json))
            val value = transform("JSONPATH", "$.result.objects[" +i+ "].properties.85.value", json)

            var Number realId = id - (moduleNumber * 500) - (roomNumber * 40)
            val printString = "%1$-35s : %2$10s : %3$2d : %4$2d"
            val name = idMap.get("" + realId)
            logWarn("Uponor", String.format(printString, name, ""+value, roomNumber.intValue(), moduleNumber.intValue()))
        }
    }
]

rule "Read Uponor values 2"
when
    Time cron "0 0/1 * * * ?"
then
    val itemQuery = '{"id":"%s","properties":{"85":{}}}'
    val itemQueryList = newArrayList()
    //Add global keys to query
    for (var i = 20; i < 41; i++) {
        itemQueryList.add(String.format(itemQuery, i))
    }
    logWarn("Uponor", "************ Global values *****************")
    processRequest.apply(itemQueryList, 0, 0)
    //Add module keys to query
    for (var j = 0; j < numModules; j++) {
        itemQueryList = newArrayList()
        for (var i = 60; i < 71; i++) {
            itemQueryList.add(String.format(itemQuery, i + j*500))
        }
        logWarn("Uponor", "************ Module " + j + " values *****************")
        processRequest.apply(itemQueryList, 0, j)
    }


    //Query room keys
    for (var j = 0; j < numModules; j++) {
        for (var k = 0; k < numRooms; k++) {
            itemQueryList = newArrayList()
            for (var i = 80; i < 115; i++) {
                itemQueryList.add(String.format(itemQuery, i + k*40 + j*500))
            }
            logWarn("Uponor", "************ Room " + k + " and Module " + j + " values *****************")
            processRequest.apply(itemQueryList, k, j)
        }
    }
end

Spot on :slight_smile:
Will do some further test, but really nice work!

Thanks.

I have made a rule for updating values. This example updates my set point values so it uses number values and works on any item in the group defined in the rule.

I added the item to the itemMap (with id 91 + (room number *40)) and created the corresponding items and group.
This rule assumes that the system is set to accept changes through the api. It is quite simple to add a switch for turning that on and off if needed.

Rule:

rule "Set Uponor target temperature"
when
    Member of tempSetting received command
then
    val url = "http://<ip>/api";
    val contenttype = "application/json";
    var POSTrequest = '{"jsonrpc":"2.0", "id":9, "method":"write", "params":{ "objects":[%s]}}'
    val itemQuery = '{"id":"%s","properties":{"85":{"value":%s:}}}'

    for(e:itemMap.entrySet) {
        if(e.value.equals(triggeringItem)) {
            POSTrequest = String.format(POSTrequest, String.format(itemQuery, e.key, triggeringItem.state));
            val json = sendHttpPostRequest(url, contenttype, POSTrequest);
        }
    }

end

Here is an example on how I modified the rule above to be able to toggle thermostat/app control of the set point for a room:

rule "Enable Uponor set temperature"
when
    Member of setWithApp received command
then
    val url = "http://<ip>/api";
    val contenttype = "application/json";
    var POSTrequest = '{"jsonrpc":"2.0", "id":9, "method":"write", "params":{ "objects":[%s]}}'
    val itemQuery = '{"id":"%s","properties":{"85":{"value":%s:}}}'

    for(e:itemMap.entrySet) {
        if(e.value.equals(triggeringItem)) {
            var state = 1;
            if(triggeringItem.state == OFF) {
                state = 0;
            }
            POSTrequest = String.format(POSTrequest, String.format(itemQuery, e.key, state));
            val json = sendHttpPostRequest(url, contenttype, POSTrequest);
        }
    }

end

The field id for that is 84 + (40*room number).

With this and the rule above I can now set the temperature from openhab and still be able to use the physical thermostat in the room if I want. I just have to remember to toggle the switch to thermostat once I have set the value with openhab.
EDIT: It turns out that the thermostat will send its value after a few minutes if it is toggled back so to control the temperature with openhab the switch have to be on all the time. That makes sense now when I think about it.

Reading out the alarms doesn’t seem to work for me. I have a couple of alarms in the system, but I don’t see anything in the log. Can it be the ID which is different?

If it is the “Read Uponor alarms” rule it only reads alarms that have triggered. I tested it by setting up the ‘low temp limit’ to 30 degrees C.

hi all,

pardon if this is a noob question, but im quite new.

I have an uponor flor heating setup at home with the same controller and i am desperate to get data out from it (beside from the smatrix app) so I was very excited when i found this page. It looks just like what i need!

I want to run the scripts that you have posted, just so i can see if i can get data from my system. (later I would like to pass it on to HASSIO whitch i use for my home automation)

The problem is that I dont know how.

I have tried saving it as .js with the right IP and then i have used node.js to run it without result.

what ‘application’ or how do you run the scripts?

sry if this a stupid question, but pls help me along.

br
edvard

This is a community for openHAB automation, and the provided code is aimed towards that platform. You might be able to extract the vital parts and use them in HASSIO, but maybe your question should be directed to that forum?

Hi Rune,

thanks for taking your time to reply.

I know this is not for hassio, i just wanted to know if there was a way to fire the code at my uponor controller outside both hass as well as openhab - before i even considered getting it into hassio.

just to find out if it is a viable path, cos that uponor/smatrix app is junk :wink:

br

Ok got it. It is possible to ‘reverse engineer’ the code to use for other home-autmations, but I do not have that ability.
Initially I found this, but it’s standalone code, does not integrate to anything.

thanks, I found that one as well.

can anybody tell me what language this code posted by johanHem is written in?

br

He writes it on the website, it’s Python.

Due to a disk error I’ve lost my OH installation (No backup of course), and I’m trying to get the Uponor rules to work again (Now on OH 2.3). Now I’m struggling with your examples, e.g. (Limited to just one room for testing):

import java.util.HashMap

//Create a map that map from uponor id to the item that should be updated :
val HashMap<String, GenericItem> itemMap  = newHashMap(
    '105' -> Room0Temp,
    '109' -> Room0Name
)

rule "Read Uponor values"
when
    Time cron "0 0/1 * * * ?"
then
	logWarn("Uponor", "Run")
    var url = "http://192.168.1.183/api";
    var contenttype = "application/json";

    var POSTrequest = '{"jsonrpc":"2.0", "id":8, "method":"read", "params":{ "objects":[%s]}}'
    val itemQuery = '{"id":"%s","properties":{"85":{}}}'
    val itemQueryList = newArrayList()
    val idSet = itemMap.keySet
    idSet.forEach[ key | itemQueryList.add(String.format(itemQuery, key)) ]
    POSTrequest = String.format(POSTrequest, itemQueryList.join(','))

    var json = sendHttpPostRequest(url, contenttype, POSTrequest);
    var count = Integer::parseInt(transform("JSONPATH", "$.result.objects.length()", json));

    for(var i = 0; i < count; i++) {
        val id = transform("JSONPATH", "$.result.objects[" +i+ "].id", json);
        val value = transform("JSONPATH", "$.result.objects[" +i+ "].properties.85.value", json);
        val item = itemMap.get(id);
        item.postUpdate(value);
		logWarn("Uponor", "id  " + id + ": value = " + value)
    }

This gives me:

2018-11-21 19:53:00.020 [WARN ] [me.internal.engine.RuleContextHelper] - Variable ‘itemMap’ on rule file ‘Uponor_room.rules’ cannot be initialized with value ‘newHashMap(,)’: The name ‘Room0Temp’ cannot be resolved to an item or type; line 5, column 14, length 9

and

2018-11-21 19:53:00.021 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule ‘Read Uponor values’: cannot invoke method public java.util.Set java.util.HashMap.keySet() on null

Any idea why? Is it perhaps an OH 2.3 thing?

Perhaps you would be so kind, so share your finished items / rules?

I missed that this thread have been updated. Sorry about that.
I’m not sure why your rules give that error. The only thing I can see wrong is that you are missing an end to the rule but I guess that is just a coopy paste error.
EDIT: Note that the right part of the HashMap values must be items. So have you created the items
Room0Temp and Room0Name?
The rules support number, switch and String items.

Here are my complete rules for uponor:

import java.util.HashMap

//Create a map that map from uponor id to the item that should be updated:
val HashMap<String, GenericItem> itemMap = newHashMap(
    '84'  -> BadrumSetWithApp,
    '91'  -> BadrumNereSetPoint,
    '105' -> BadrumNereTemp,

    '124' -> PannrumSetWithApp,
    '131' -> PannrumSetPoint,
    '145' -> PannrumTemp,

    '164' -> SminkrumSetWithApp,
    '171' -> SminkrumSetPoint,
    '185' -> SminkrumTemp,

    '204' -> TVrumSetWithApp,
    '211' -> TVrumSetPoint,
    '225' -> TVrumTemp,

    '244' -> TrapprumSetWithApp,
    '251' -> TrapprumSetPoint,
    '265' -> TrapprumTemp,

    '284' -> GarderobSetWithApp,
    '291' -> GarderobSetPoint,
    '305' -> GarderobTemp
)

val HashMap<String, GenericItem> alarmItemMap  = newHashMap(
    '28' -> TempAlarm
)

rule "Read Uponor values"
when
    Time cron "0 0/1 * * * ?"
then
    val url = "http://192.168.0.117/api";
    val contenttype = "application/json";

    var POSTrequest = '{"jsonrpc":"2.0", "id":8, "method":"read", "params":{ "objects":[%s]}}'
    val itemQuery = '{"id":"%s","properties":{"85":{}}}'
    val itemQueryList = newArrayList()
    val idSet = itemMap.keySet
    idSet.forEach[ key | itemQueryList.add(String.format(itemQuery, key)) ]
    POSTrequest = String.format(POSTrequest, itemQueryList.join(','))
    var json = null;
    var count = 0;
    try {
        json = sendHttpPostRequest(url, contenttype, POSTrequest);
        count = Integer::parseInt(transform("JSONPATH", "$.result.objects.length()", json));
    }
    catch(Throwable e) {
        logWarn("Upponor", "An error occured whuile reading the values from the Uponor gateway. " + e.getMessage())
        return;
    }

    for(var i = 0; i < count; i++) {
        //logWarn("Banan:", transform("JSONPATH", "$.result.objects[" + i + "]", json)  );

        val id = transform("JSONPATH", "$.result.objects[" +i+ "].id", json)
        val value = transform("JSONPATH", "$.result.objects[" +i+ "].properties.85.value", json)
        val item = itemMap.get(id);
        if(item instanceof Number) {
            item.postUpdate(Float::parseFloat(value))
        }
        else if(item instanceof SwitchItem) {
            if (value == '0') {item.postUpdate(OFF)}
            else {
                item.postUpdate(ON)}
        }
        else {
            item.postUpdate(value)
        }
    }
end

rule "Read Uponor alarms"
when
    Time cron "0 0/1 * * * ?"
then
    val url = "http://192.168.0.117/api";
    val contenttype = "application/json";

    var POSTrequest = '{"jsonrpc":"2.0", "id":8, "method":"read", "params":{ "objects":[%s]}}'
    val itemQuery = '{"id":"%s","properties":{"77":{}, "662":{}}}'
    val itemQueryList = newArrayList()
    val idSet = alarmItemMap.keySet
    idSet.forEach[ key | itemQueryList.add(String.format(itemQuery, key)) ]
    POSTrequest = String.format(POSTrequest, itemQueryList.join(','))

    val json = sendHttpPostRequest(url, contenttype, POSTrequest);
    val count = Integer::parseInt(transform("JSONPATH", "$.result.objects.length()", json));

    for(var i = 0; i < count; i++) {

        val id = transform("JSONPATH", "$.result.objects[" +i+ "].id", json)
        val state = transform("JSONPATH", "$.result.objects[" +i+ "].properties.662.value", json)
        val item = alarmItemMap.get(id);
        if (state.equals("0")){
            item.postUpdate('OK')
        }
        else {
            item.postUpdate('Triggered')
        }
    }
end

rule "Enable Uponor set temperature"
when
    Member of setWithApp received command
then
    val url = "http://192.168.0.117/api";
    val contenttype = "application/json";
    var POSTrequest = '{"jsonrpc":"2.0", "id":9, "method":"write", "params":{ "objects":[%s]}}'
    val itemQuery = '{"id":"%s","properties":{"85":{"value":%s:}}}'

    for(e:itemMap.entrySet) {
	if(e.value.equals(triggeringItem)) {
            var state = 1;
            if(triggeringItem.state == OFF) {
                state = 0;
            }
            POSTrequest = String.format(POSTrequest, String.format(itemQuery, e.key, state));
            val json = sendHttpPostRequest(url, contenttype, POSTrequest);
        }
    }

end

rule "Set Uponor target temperature"
when
    Member of tempSetting received command
then
    val url = "http://192.168.0.117/api";
    val contenttype = "application/json";
    var POSTrequest = '{"jsonrpc":"2.0", "id":9, "method":"write", "params":{ "objects":[%s]}}'
    val itemQuery = '{"id":"%s","properties":{"85":{"value":%s:}}}'

    for(e:itemMap.entrySet) {
	if(e.value.equals(triggeringItem)) {
            POSTrequest = String.format(POSTrequest, String.format(itemQuery, e.key, triggeringItem.state));
            val json = sendHttpPostRequest(url, contenttype, POSTrequest);
        }
    }

end

The openhab rules are written in domain specific language that is based om XBase.
If you know java you should be able to read it without much problem.

If you want to communicte with your Uponor system just to test you can use any REST tool (like postman). To build your Json you should look at the variables POSTrequest and itemQuery in my rules.