New Shelly TRV BLU integration

I think MQTT could be a solution for you.

Hello,
On my home assistant I get an error: Incorrect type. Expected “object”.
What I am doing wrong? It should be the correct spaces.
grafik

Show me the config validation result.

How can I integrate the Shelly BLU TRV gen3 via ZHA?
So that I can use it as a climate device?

Great integration. I have setup 2x TRV via a single gateway, using the python scripts and automation.

The only question I have, is the “call for heat” as in, a trigger for the boiler to run. When the TRV opens, the boiler should switch on and start the pump. This was possible with the older TRV as the value position was available.

Any ideas?

Thanks.

The device must use firmware 1.1.0 or newer and when you push the button (behind the battery cover) 3 times the pairing process will start.

1 Like

Solved. I checked the MQTT payload and couldn’t find anything relating to the valve position.

I ended up creating a rest command in node red which queries the API for each sensor every 5 minutes. If the valve is more than 20% open, the boiler starts. If it’s less than 20%, it stops.

Are there any plans to (or is it even possible) include the valve position in the MQTT payload?

Thanks.

@Bieniu for reference.

You can use BluTrv.GetRemoteStatus method to get the valve position. There is a device script in this thread that allows getting the remote status by MQTT without polling the device.

You should ask Shelly support about this. I don’t think anyone here knows Shelly plans.

Thanks, script update runs every 30 seconds via MQTT. A better solution than the manual query.

I meant this script, it sends data only when there is an actual change New Shelly TRV BLU integration - #88 by vlad.barjovanu

Yes, the story continues. After I found out that zigbee is also supported from firmware 1.1.x, I learnt a TRV in HA by pressing the button three times (on the battery compartment - ZbE display).
The problem with the second TRV is that it keeps finding the same TRV even though I am using a different, second TRV. So I have not yet managed to learn two TRVs. The display sometimes shows “ZbE” + “Zb0” + “ZbI” or “no”. After several attempts, the first TRV is then displayed again as being programmed in HomeAssistant ZHA.
Unfortunately, the MQTT variant only works well to a limited extent.

Ask Shelly support about this. I informed them about this problem, I thought it was resolved with firmware 1.1.0.

What do you mean?

@Bieniu
Only thats all right. Is the bluetooth just unesseary, because its connected throw the gateway?
grafik

@tomg1970
Thats it. With zigbee it works out of the box.

Ok I have just opened a ticket with Shelly

Hello, I would like to integrate the Shelly BLU TRV gen3 into Home Assistant via zigbee ZHA. After I have registered the first TRV, which unfortunately also works very “slowly” and sporadically, a problem occurs with the second one. Zigbee Mesh in the house is very well available, as I am a technician.

  1. The problem with the second TRV is that it keeps finding the same TRV even though I am using a different, second TRV. So I have not yet managed to learn two TRVs. The display sometimes shows “ZbE” + “Zb0” + “ZbI” or “no”. After several attempts, the first TRV is then displayed again as programmed in HomeAssistant ZHA.This means that I cannot currently integrate a second TRV into HomeAssistant.
  2. How exactly can I connect a TRV BLU to HomeAssistant via MQTT?
  3. How do I deal with the time profile when I am on holiday - can I create different profiles for one room, or how is this intended? The weekly schedule is also not sufficient for shift workers.
  4. A user-friendly note: The weekly schedule creation is anything but practical and innovative. The creation is cumbersome, confusing and old-fashioned. It is not even possible to sort the individual time periods. Unfortunately, Shelly has implemented this with very little attention to detail.
  5. Unfortunately, there is still very little input available for the ZHA integration in HomeAssistant. Many important sensors and data are missing. E.g. Valve State
  6. How can I temporarily deactivate a created time profile, e.g. for one day?
    Unfortunately, new problems arise every day for the use of the new BLU TRV gen3, so I can’t yet convert my house to the TRV I bought. Even if I want to use the TRV independently in the Shelly app, the implementation is not thought through to the end and is sometimes inferior to very cheap thermostats. The control with the DW window sensors is also counterproductive. When a window is opened, the window immediately lowers the TRV. What about a patio door, for example, where I quickly go in and out? The TRV moves back and forth every time. A variable time offset is needed here.
    Window open - wait variable time - TRV temp down.
    Window closed - wait variable time - set TRV temp to original value se
    What about absence control. Why can’t I somehow build this into the Shelly app so that the thermostats don’t go into ECO mode, for example, when I’m away?
    Unfortunately, as a professional, I can still think of a lot of things in relation to Shelly’s heating control. Whether in the Shelly app or the flexibility in the connection to HomeAssistant.
    I hope that there is still a lot to come and ask for feedback on further plans. Otherwise I will have to request the return of the TRV BLU because they are not suitable for everyday use.
    THANK YOU BEST REGARDS

Was meinst Du damit genau?
What do you mean by that?

BLU TRV is using Bluetooth to communicate with BLU Gateway.

This is an English forum… please translate.

Hi,
this is the latest version of the script. It uses a custom method to throttle calls to Shelly.call. I adapted the functions from Dekats Toolbox.

let isDebugLog = false;
/**
 * KVS key name for configuration of the mqtt topic where to publish the remote status
 * @type {string}
 */
let publishRemoteStatusTopicKey = "publish_remotestatus_topic";
/**
 * KVS key default value of the mqtt topic where to publish the remote status<br>
 * The key value should have a placeholder {component}
 * @type {string}
 */
let publishRemoteStatusTopicDefaultValue = "/remotestatus/{component}/trv";
/**
 * mqtt topic prefix for the Shelly BLU GW<br>
 * It is used when pushing data on mqtt topics
 * @type {string}
 */
let topicPrefix = null;

/**
 * A map, where key value settings (KVS) for the Shelly BLU GW device are persisted in memory
 * @type {{key:string, value: string, etag: string}}
 */
var keyValueSettings = {};


/*
Section-begin: Dekats Toolbox
    Description: a universal Toolbox for Shelly scripts
                 adapted from original script
 */

var tH7 = 0, callLimit = 5, cacheLimit = 40, cSp = 0.2; //Toolbox global variable
/**
 * actual calls count
 *
 * @type {number}
 */
var actualCallsCount = 0;
/**
 * array to cache calls that can't be made immediately, because the limit is reached
 *
 * @type {{method: string, param: Object, callBackFn: function, userData: Object, debug: boolean}[]}
 */
var callCache = [];
/**
 * call entry for current call that is trying to be executed
 *
 * @type {method: string, param: Object, callBackFn: function, userData: Object, debug: boolean}
 */
var nCall = null;

/**
 * Upgrade JSON.stringify
 * @param {*} d
 * @returns {string|null}
 * @constructor
 */
function Str(d) {
    try {
        if (d === null || d === undefined) return null;
        if (typeof d === 'string') return d;
        return JSON.stringify(d);
    } catch (e) {
        ErrorMsg(e, 'Str()');
    }
}

/**
 *
 * @param {*} result
 * @param {number} errorCode
 * @param {string} errorMessage
 * @param {callBackFn: function, userData: Object} userDataWrapper
 */
function customCallbackHandler(result, errorCode, errorMessage, userDataWrapper) { //Shelly.customCall error check
    try {
        actualCallsCount--;
        if (actualCallsCount < 0) actualCallsCount = 0;
        if (userDataWrapper.callBackFn) {
            if (userDataWrapper.userData)
                userDataWrapper.callBackFn(result, errorCode, errorMessage, userDataWrapper.userData);
            else
                userDataWrapper.callBackFn(result, errorCode, errorMessage);
        } else {
            if (userDataWrapper.userData) print('Debug: ', userDataWrapper.userData);
        }
    } catch (e) {
        console.log("customCallbackHandler has failed. Error: ", e);
    }
}

function callQueue() { //Shelly.customCall queue
    try {
        if (!callCache[0] && nCall === null) return;
        if (nCall === null) {
            nCall = callCache[0];
            callCache.splice(0, 1);
        }
        if (nCall !== null && actualCallsCount < callLimit) {
            customCall(nCall.method, nCall.param, nCall.callBackFn, nCall.userData, nCall.debug);
            nCall = null;
        }
        if ((nCall !== null || callCache[0]) && !tH7) tH7 = Timer.set(1000 * cSp, 0, function () {
            tH7 = 0;
            callQueue();
        });
    } catch (e) {
        console.log("callQueue has failed. Error: ", e);
    }
}

function customCall(method, param, callBackFn, userData, debug) { //Upgrade Shelly.customCall
    try {
        let userDataWrapper = {callBackFn: callBackFn, userData: userData};
        if (debug) print('Debug: calling:', method, param);
        if (!method && callBackFn) {
            callBackFn(userData);
            return;
        }
        if (actualCallsCount < callLimit) {
            actualCallsCount++;
            Shelly.call(method, param, customCallbackHandler, userDataWrapper);
        } else if (callCache.length < cacheLimit) {
            // callCache.push([method, param, callBackFn, userData, debug]);
            let callEntry = {method: method, param: param, callBackFn: callBackFn, userData: userData, debug: debug};
            callCache.push(callEntry);
            if (debug) print('Debug: save customCall:', method, param, ', customCall queue now:', callCache.length);
            callQueue();
        } else {
            console.log("customCall() to many Calls in use, droping customCall: ", Str(method), ", ", Str(param));
        }
    } catch (e) {
        console.log("customCall() has failed. Error: ", e);
    }
}

/*
Section-end: Dekats Toolbox
 */


/**
 * Gets the value of a key from the Shelly's KVS
 * @param {*} key the key for which the value will be retrieved
 * @param {*} defaultValue the default value for the key, in case no entry is found in KVS
 */
function kvsGet(key, defaultValue) {
    try {
        var userDataParam = {"key": key, "defaultValue": defaultValue};
        customCall(
            "KVS.get",
            {"key": key},
            function (result, error_code, error_message, userData) {
                var keyValueSettingObj;
                if (error_code === 0) {
                    keyValueSettingObj = {"key": userData.key, "value": result.value, "etag": result.etag};
                } else {
                    keyValueSettingObj = {"key": userData.key, "value": userData.defaultValue, "etag": null};
                }
                keyValueSettings[key] = keyValueSettingObj;
            },
            userDataParam
        );
    } catch (e1) {
        console.log("kvsGet has failed for key '", key, "'. Error: ", e1);
    }
}

/**
 * Handler function that is called when MQTT configuration is retrieved from Shelly BLU GW<br>
 * It initializes the topicPrefix variable
 * @param config
 */
function getMqttConfigHandler(config) {
    try {
        if (config) {
            if (isDebugLog)
                console.log("set topicPrefix=", config.topic_prefix);
            topicPrefix = config.topic_prefix;
        }
    } catch (e) {
        console.log("getMqttConfigHandler '", Str(config), "' has failed. Error: ", e);
    }
}

/**
 * Function that retrieves the MQTT configuration of the Shelly BLU GW
 */
function getMqttConfig() {
    try {
        customCall("MQTT.GetConfig", {}, getMqttConfigHandler);
    } catch (e) {
        console.log("getMqttConfig has failed. Error: ", e1);
    }
}

/**
 * Handler function that is called when Shelly BLU GW is providing the response for the BluTrv.GetRemoteStatus customCall<br>
 * It obtains the value of the status."trv:0" and it pushes this value to the configured MQTT topic
 *
 * @param status BluTrv.GetRemoteStatus response
 * @param errNo error number; 0 if no error
 * @param errMsg error message
 * @param {component: string, id: int} userData predefined user data, containing the component and the id of the device, for which the status was retrieved
 */
function bluTrvGetRemoteStatusHandler(status, errNo, errMsg, userData) {
    try {
        if (isDebugLog)
            console.log("bluTrvGetRemoteStatusHandler status: ", Str(status), ", errNo: ", Str(errNo), ", errMsg: ", Str(errMsg), ", userData: ", Str(userData))
        if (errNo !== 0) {
            console.log("bluTrvGetRemoteStatusHandler error no: ", errNo, ", errMsg: '", errMsg, "', userData: ", Str(userData));
            return;
        }

        if (keyValueSettings[publishRemoteStatusTopicKey] && keyValueSettings[publishRemoteStatusTopicKey].value) {
            var topic = topicPrefix + keyValueSettings[publishRemoteStatusTopicKey].value.replace("{component}", userData.component);
            MQTT.publish(topic, Str(status.status["trv:0"]));
        } else
            console.log("KVS missing and no default value for key: ", publishRemoteStatusTopicKey);
    } catch (e) {
        console.log("bluTrvGetRemoteStatusHandler status: '", Str(status), "', errNo: ", errNo, ", errMsg: '", Str(errMsg), "', userData: ", Str(userData), " has failed. Error: ", e);
    }
}

function sendTrvStatus(component, id) {
    try {
        if (topicPrefix !== null) {
            customCall("BluTrv.GetRemoteStatus", {"id": id}, bluTrvGetRemoteStatusHandler, {
                component: component,
                id: id
            });
        } else
            console.log("sendTrvStatus topicPrefix is null");
    } catch (e) {
        console.log("sendTrvStatus component: '", component, "', id: ", id, " has failed. Error: ", e);
    }
}

function eventHandler(event) {
    try {
        if (isDebugLog)
            console.log("eventHandler(event=", Str(event), ")");
        if (event && event.info) {
            if (event.info.component && event.info.component.indexOf("blutrv:") === 0 && event.info.event === "config_changed") {
                sendTrvStatus(event.info.component, event.info.id)
            }
        } else
            console.log("eventHandler (event && event.info) is false");
    } catch (e1) {
        console.log("Shelly handle event '", event, "' has failed. Error: ", e1);
    }
}

function addEventHandler() {
    try {
        if (isDebugLog) {
            console.log("addEventHandler...");
        }
        Shelly.addEventHandler(eventHandler);
        if (isDebugLog) {
            console.log("addEventHandler done.");
        }
    } catch (e1) {
        console.log("addEventHandler has failed. Error: ", e1);
    }
}

function initScript() {
    try {
        kvsGet(publishRemoteStatusTopicKey, publishRemoteStatusTopicDefaultValue);
        getMqttConfig();
        addEventHandler();
        console.log("initScript was completed...");
    } catch (e) {
        console.log("initScript has failed. Error: ", e1);
    }
}

initScript();

My configuration for one TRV looks like this:

- climate:
name: "ShellyTRV.office"
unique_id: "1234567890ab"
current_temperature_topic: "shellyblugwg3-e1b234567ac8/status/bthomesensor:203"
current_temperature_template: "{{ value_json.value }}"
max_temp: 30
min_temp: 4
temp_step: 0.1
temperature_state_topic: "shellyblugwg3-e1b234567ac8/status/bthomesensor:202"
temperature_state_template: "{{ value_json.value }}"
temperature_command_template: "{{ {'id': 0, 'src': 'homeassistant', 'method': 'BluTRV.Call', 'params': {'id': 200, 'method': 'TRV.SetTarget', 'params': {'id': 0, 'target_C': value | round(1)}}} | to_json }}"
temperature_command_topic: "shellyblugwg3-e1b234567ac8/rpc"
action_topic: "shellyblugwg3-e1b234567ac8/remotestatus/blutrv:200/trv"
action_template: "{% if value_json.pos > 0 %}heating{% else %}idle{% endif %}"
mode_state_topic: "shellyblugwg3-e1b234567ac8/status/bthomesensor:202"
mode_state_template: "{{ 'off' if value_json.value == 4 else 'heat' }}"
modes: ["heat", "off"]
mode_command_topic: "shellyblugwg3-e1b234567ac8/rpc"
mode_command_template: "{% set target = 4 if value == 'off' else 21 %}{{ {'id': 0, 'src': 'homeassistant', 'method': 'BluTRV.Call', 'params': {'id': 200, 'method': 'TRV.SetTarget', 'params': {'id': 0, 'target_C': target}}} | to_json }}"
availability:
  - topic: "shellyblugwg3-e1b234567ac8/status/blutrv:200"
    value_template: "{%if value_json.rpc%}online{%else%}offline{%endif%}"
device:
  connections:
    - - bluetooth
      - 12:34:56:78:90:ab
  name: Shelly BLU TRV
  model: Shelly BLU TRV
  model_id: SBTR-EU90AB
  manufacturer: Allterco Robotics