Koenkk / zigbee2mqtt

Zigbee 🐝 to MQTT bridge 🌉, get rid of your proprietary Zigbee bridges 🔨
https://www.zigbee2mqtt.io
GNU General Public License v3.0
11.77k stars 1.64k forks source link

[New device support]: Bosch Radiator Thermostat II #14926

Closed niklasarnitz closed 1 year ago

niklasarnitz commented 1 year ago

Link

https://www.bosch-smarthome.com/at/de/produkte/geraete/heizkoerper-thermostat/

Database entry

{"id":22,"type":"EndDevice","ieeeAddr":"0x18fc26000002a2d7","nwkAddr":39513,"manufId":4617,"manufName":"BOSCH","powerSource":"Battery","modelId":"RBSH-TRV0-ZB-EU","epList":[1],"endpoints":{"1":{"profId":260,"epId":1,"devId":769,"inClusterList":[0,1,3,4,32,513,516,2821],"outClusterList":[10,25],"clusters":{"genBasic":{"attributes":{"modelId":"RBSH-TRV0-ZB-EU","manufacturerName":"BOSCH","powerSource":3,"zclVersion":8,"appVersion":1,"stackVersion":5,"hwVersion":1,"dateCode":"20220627","swBuildId":"3.02.05"}},"genPollCtrl":{"attributes":{"checkinInterval":6480}},"hvacThermostat":{"attributes":{"20496":0}}},"binds":[{"cluster":32,"type":"endpoint","deviceIeeeAddress":"0x00124b0024c20f21","endpointID":1}],"configuredReportings":[],"meta":{}}},"appVersion":1,"stackVersion":5,"hwVersion":1,"dateCode":"20220627","swBuildId":"3.02.05","zclVersion":8,"interviewCompleted":true,"meta":{},"lastSeen":1668172257367,"defaultSendRequestWhen":"active"}

Comments

Here are some things I found out and got working already:

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const extend = require('zigbee-herdsman-converters/lib/extend');
const e = exposes.presets;
const ea = exposes.access;

const definition = {
    zigbeeModel: ['RBSH-TRV0-ZB-EU'],
    model: 'Radiator thermostat II',
    vendor: 'Bosch',
    description: 'Radiator thermostat',
    fromZigbee: [],
    toZigbee: [fz.thermostat, fz.hvac_user_interface],
    exposes: [
       e.child_lock(),
       exposes.climate()
        .withSetpoint('current_heating_setpoint', 5, 30, 0.5, ea.STATE_SET)
        .withLocalTemperature()
    ],
};

module.exports = definition;

Comment: The setpoint range and interval should be correct.

External converter

No response

Supported color modes

No response

Color temperature range

No response

itkama commented 1 year ago

This seems to be a duplicate of #14005 - though more people with this device should hopefully speed things up :)

danieledwardgeorgehitchcock commented 1 year ago

You might want to add a fromZigbee converter to read back the temperature from the device.

If you have the Z2M GUI enabled, you can see what clusters are supported by the endpoints, in the device clusters tab.

You can also see what the device reports back using the device dev tab and reading from the various clusters.

danieledwardgeorgehitchcock commented 1 year ago

Try adding fz.thermostat to your fromZigbee section - also remove what you have from your toZigbee section (for now)

itkama commented 1 year ago

@danieledwardgeorgehitchcock I've played around a bit with the dev tab you mentioned.

Via Endpoint 1, Cluster hvacThermostat I can read the following attributes: maxHeatSetpointLimit minHeatSetpointLimit localTemp occupiedHeatingSetpoint

I can also set the last one if I write instead of reading it. For the last one I get this answer: Read result of 'hvacThermostat': {"occupiedHeatingSetpoint":2000} It's the current heating setpoint which is set at 20°C.

How would I now add these commands to converter file @niklasarnitz started above?

danieledwardgeorgehitchcock commented 1 year ago

So, I personally would look at getting the read attributes in and working first - This is what fromZigbee converters do. I'd then work on the write attributes which are things like your occupiedHeatingSetpoint, etc which is what toZigbee converters do.

Here is something that should get you some of the way to a functional product:

const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const e = exposes.presets;
const ea = exposes.access;

const definition = {
    zigbeeModel: ['RBSH-TRV0-ZB-EU'],
    model: 'Radiator thermostat II',
    vendor: 'Bosch',
    description: 'Radiator thermostat',
    fromZigbee: [fz.thermostat, fz.hvac_user_interface],
    toZigbee: [tz.thermostat_occupied_heating_setpoint],
    exposes: [
       exposes.climate()
        .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5, ea.STATE_SET)
        .withLocalTemperature()
    ],
};

module.exports = definition;
niklasarnitz commented 1 year ago

I'll be trying this tomorrow. But the last thread entry by @danieledwardgeorgehitchcock should propably work.

itkama commented 1 year ago
const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const tz = require('zigbee-herdsman-converters/converters/toZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const e = exposes.presets;
const ea = exposes.access;

const definition = {
    zigbeeModel: ['RBSH-TRV0-ZB-EU'],
    model: 'Radiator Thermostat II',
    vendor: 'Bosch',
    description: 'Radiator Thermostat',
    fromZigbee: [fz.thermostat, fz.hvac_user_interface, fz.battery],
    toZigbee: [tz.thermostat_occupied_heating_setpoint],
    exposes: [
        e.battery(),
        exposes.climate()
        .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5, ea.STATE_SET)
        .withLocalTemperature()
    ],
    configure: async (device, coordinatorEndpoint, logger) => {
        const endpoint = device.getEndpoint(1);
        await reporting.bind(endpoint, coordinatorEndpoint, ['hvacThermostat', 'genPowerCfg']);
        await reporting.thermostat_occupied_heating_setpoint(endpoint);
        await reporting.thermostatTemperature(endpoint);
        await reporting.batteryVoltage(endpoint);
        await reporting.battery(endpoint);
    },
};

module.exports = definition;

This is my current result. Changing the setpoint works, temperature gets reported and also the battery percentage: image

What would definetly be nice to have: A current running state of the device to have it display that (in my case) in Home Assistant.

niklasarnitz commented 1 year ago

When using @itkama 's config, I get a few different errors:

When I try and refresh the temperature:

Debug 2022-11-12 03:19:28 Received MQTT message on 'zigbee2mqtt/0x18fc26000002a2d7/get' with data '{"local_temperature":""}'
Error 2022-11-12 03:19:28 No converter available for 'local_temperature' ("")

And it seems that the binding process of genPowerCfg fails:

x18fc26000005976f/1 genPowerCfg from '0x00124b0024c20f21/1' failed (AREQ - ZDO - bindRsp after 10000ms)
zigbee2mqtt_1        |     at Timeout._onTimeout (/app/node_modules/zigbee-herdsman/src/utils/waitress.ts:64:35)
zigbee2mqtt_1        |     at listOnTimeout (node:internal/timers:559:17)
zigbee2mqtt_1        |     at processTimers (node:internal/timers:502:7))
zigbee2mqtt_1        | Zigbee2MQTT:info  2022-11-12 03:12:37: MQTT publish: topic 'zigbee2mqtt/bridge/response/device/configure', payload '{"data":{"id":"0x18fc26000005976f"},"error":"Failed to configure (Bind 0x18fc26000005976f/1 genPowerCfg from '0x00124b0024c20f21/1' failed (AREQ - ZDO - bindRsp after 10000ms))","status":"error","transaction":"uxdrr-1"}'

Setting the temperature works.

Here's a screenshot of my GUI: Screenshot from 2022-11-12 03-21-33

itkama commented 1 year ago

Interestingly I get the same error when trying to refresh the temperature manually. But the thermostat seems to be doing that itself quite frequently in the background. But those messages from the thermostat only started appearing once I poked it via the dev console.

Does it work for you if you ask for it manually? image

Also added the temperature calibration:

const fz = require("zigbee-herdsman-converters/converters/fromZigbee");
const tz = require("zigbee-herdsman-converters/converters/toZigbee");
const exposes = require("zigbee-herdsman-converters/lib/exposes");
const e = exposes.presets;
const ea = exposes.access;

const definition = {
    zigbeeModel: ["RBSH-TRV0-ZB-EU"],
    model: "Radiator Thermostat II",
    vendor: "Bosch",
    description: "Radiator Thermostat",
    fromZigbee: [fz.thermostat, fz.battery],
    toZigbee: [tz.thermostat_occupied_heating_setpoint, tz.thermostat_local_temperature_calibration, tz.thermostat_local_temperature],
    exposes: [
        e.battery(),
    exposes.climate()
    .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5, ea.STATE_SET)
    .withLocalTemperature()
        .withLocalTemperatureCalibration(-30, 30, 0.1)  //Values are a guess and stolen from a eurotronic thermostat valve
    ],
    configure: async (device, coordinatorEndpoint, logger) => {
        const endpoint = device.getEndpoint(1);
        await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'hvacThermostat']);
        await reporting.thermostat_occupied_heating_setpoint(endpoint);
        await reporting.thermostatTemperature(endpoint);
        await reporting.thermostatTemperatureCalibration(endpoint);
        await reporting.battery(endpoint);
    },
};

module.exports = definition;

It seems to have the same problem that the refresh button doesn't work. Using the slider does though.

niklasarnitz commented 1 year ago

Refresh now works for me. Also the Local Temp works.

But here is another error:

zigbee2mqtt_1        | Zigbee2MQTT:error 2022-11-12 17:00:27: Failed to configure 'EG_Zimmer_Straße_Thermostat_Rechts', attempt 2 (ReferenceError: reporting is not defined
zigbee2mqtt_1        |     at Object.configure (/app/dist/util/externally-loaded.js:23:9)
zigbee2mqtt_1        |     at Configure.configure (/app/lib/extension/configure.ts:115:37)
zigbee2mqtt_1        |     at EventEmitter.<anonymous> (/app/lib/extension/configure.ts:80:62)
zigbee2mqtt_1        |     at EventEmitter.emit (node:events:525:35)
zigbee2mqtt_1        |     at EventBus.emitLastSeenChanged (/app/lib/eventBus.ts:52:22)
zigbee2mqtt_1        |     at Controller.<anonymous> (/app/lib/zigbee.ts:66:27)
zigbee2mqtt_1        |     at Controller.emit (node:events:513:28)
zigbee2mqtt_1        |     at Controller.selfAndDeviceEmit (/app/node_modules/zigbee-herdsman/src/controller/controller.ts:515:14)
zigbee2mqtt_1        |     at Controller.onZclOrRawData (/app/node_modules/zigbee-herdsman/src/controller/controller.ts:727:18)
zigbee2mqtt_1        |     at ZStackAdapter.<anonymous> (/app/node_modules/zigbee-herdsman/src/controller/controller.ts:144:70))
danieledwardgeorgehitchcock commented 1 year ago

Refresh now works for me. Also the Local Temp works.

But here is another error:

zigbee2mqtt_1        | Zigbee2MQTT:error 2022-11-12 17:00:27: Failed to configure 'EG_Zimmer_Straße_Thermostat_Rechts', attempt 2 (ReferenceError: reporting is not defined
zigbee2mqtt_1        |     at Object.configure (/app/dist/util/externally-loaded.js:23:9)
zigbee2mqtt_1        |     at Configure.configure (/app/lib/extension/configure.ts:115:37)
zigbee2mqtt_1        |     at EventEmitter.<anonymous> (/app/lib/extension/configure.ts:80:62)
zigbee2mqtt_1        |     at EventEmitter.emit (node:events:525:35)
zigbee2mqtt_1        |     at EventBus.emitLastSeenChanged (/app/lib/eventBus.ts:52:22)
zigbee2mqtt_1        |     at Controller.<anonymous> (/app/lib/zigbee.ts:66:27)
zigbee2mqtt_1        |     at Controller.emit (node:events:513:28)
zigbee2mqtt_1        |     at Controller.selfAndDeviceEmit (/app/node_modules/zigbee-herdsman/src/controller/controller.ts:515:14)
zigbee2mqtt_1        |     at Controller.onZclOrRawData (/app/node_modules/zigbee-herdsman/src/controller/controller.ts:727:18)
zigbee2mqtt_1        |     at ZStackAdapter.<anonymous> (/app/node_modules/zigbee-herdsman/src/controller/controller.ts:144:70))

Add const reporting = require('zigbee-herdsman-converters/lib/reporting'); into your declarations at the top of your file

itkama commented 1 year ago

@danieledwardgeorgehitchcock I'm getting this error now: Failed to configure 'Manuel Thermostat', attempt 3 (TypeError: reporting.thermostat_occupied_heating_setpoint is not a function at Object.configure (/app/dist/util/externally-loaded.js:25:25) at Configure.configure (/app/lib/extension/configure.ts:115:13))

danieledwardgeorgehitchcock commented 1 year ago

replace await reporting.thermostat_occupied_heating_setpoint(endpoint); with await reporting.thermostatOccupiedHeatingSetpoint(endpoint);

itkama commented 1 year ago

New error now popping up (it's still working though):

Failed to configure 'Manuel Thermostat', attempt 1 
(Error: ConfigureReporting 0x18fc26000002fc33/1 hvacThermostat([{"attribute":"localTemperatureCalibration","minimumReportInterval":0,"maximumReportInterval":3600,"reportableChange":0}], 
{"sendWhen":"active","timeout":10000,"disableResponse":false,"disableRecovery":false,"disableDefaultResponse":true,"direction":0,"srcEndpoint":null,"reservedBits":0,"manufacturerCode":null,"transactionSequenceNumber":null,"writeUndiv":false}) 
failed (Status 'UNREPORTABLE_ATTRIBUTE') 
at Endpoint.checkStatus (/app/node_modules/zigbee-herdsman/src/controller/model/endpoint.ts:317:28) 
at Endpoint.configureReporting (/app/node_modules/zigbee-herdsman/src/controller/model/endpoint.ts:694:22) 
at Object.thermostatTemperatureCalibration (/app/node_modules/zigbee-herdsman-converters/lib/reporting.js:151:9) 
at Object.configure (/app/dist/util/externally-loaded.js:27:9) 
at Configure.configure (/app/lib/extension/configure.ts:115:13))
danieledwardgeorgehitchcock commented 1 year ago

It's telling you what the issue is. localTemperatureCalibration is not reportable from the device

itkama commented 1 year ago

The question would be why it is telling me this? Via the console of this device it is possible: image

danieledwardgeorgehitchcock commented 1 year ago

You're reading an attribute there, not asking it to report it given a threshold or interval. Try doing the same thing manually in the reporting tab and you'll likely get the same error...

mmattel commented 1 year ago

Is there any progress so that the new device info can be added and published in a near future?

niklasarnitz commented 1 year ago

I have a working device config now.

I’ll create a PR shortly.

Koenkk commented 1 year ago

Added by @niklasarnitz (in https://github.com/Koenkk/zigbee-herdsman-converters/pull/4983)

Changes will be available in the dev branch in a few hours from now. (https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html)

mmattel commented 1 year ago

@Koenkk do you have an idea when a new stable release will be published?

danieledwardgeorgehitchcock commented 1 year ago

Stable releases are at the beginning of each month

BBJake commented 1 year ago

image

I added some more functions for the device, fixed some values and mapped the system_mode to the Bosch configuration_mode

BBJake commented 1 year ago
const herdsman = require('zigbee-herdsman');
const reporting = require('zigbee-herdsman-converters/lib/reporting');
const fz = require("zigbee-herdsman-converters/converters/fromZigbee");
const tz = require("zigbee-herdsman-converters/converters/toZigbee");
const exposes = require("zigbee-herdsman-converters/lib/exposes");
const utils = require("zigbee-herdsman-converters//lib/utils");
const assert = require('assert');
const e = exposes.presets;
const ea = exposes.access;

const boschManufacturer = {manufacturerCode: 0x1209};

const operatingModes = {
    'automatic': 0,
    'manual': 1,
    'pause': 5,
};

const stateOffOn = {
    'OFF': 0,
    'ON': 1,
};

const displayOrientation = {
    'normal': 0,
    'flipped': 1,
};

const tzLocal = {
    bosch_thermostat: {
        key: ['window_open','boost','system_mode'],
        convertSet: async (entity, key, value, meta) => {
            if (key === 'window_open') {
        value = value.toUpperCase();
                utils.validateValue(value, Object.keys(stateOffOn));
                const index = stateOffOn[value];
                await entity.write('hvacThermostat', {0x4042: {value: index, type: herdsman.Zcl.DataType.enum8}}, boschManufacturer);
                return {state: {window_open: value}};
            }
            if (key === 'boost') {
                value = value.toUpperCase();
                utils.validateValue(value, Object.keys(stateOffOn));
                const index = stateOffOn[value];
                await entity.write('hvacThermostat', {0x4043: {value: index, type: herdsman.Zcl.DataType.enum8}}, boschManufacturer);
                return {state: {boost: value}};
            }
            if (key === 'system_mode') {
        // Map system_mode (Off/Auto/Heat) to Boschg operating mode
                value = value.toLowerCase();

        let opMode = 1; // OperatingMode 1 = Manual (Default)
        if (value=='off') { opMode = 5 } // OperatingMode 5 = Pause 
        else if (value=='auto') { opMode = 0 } // OperatingMOde 1 = Automatic 

                await entity.write('hvacThermostat', {0x4007: {value: opMode, type: herdsman.Zcl.DataType.enum8}}, boschManufacturer);
                return {state: {system_mode: value}};
            }

        },
        convertGet: async (entity, key, meta) => {
            switch (key) {
            case 'window_open':
                await entity.read('hvacThermostat', [0x4042], boschManufacturer);
                break;
            case 'boost':
                await entity.read('hvacThermostat', [0x4043], boschManufacturer);
                break;
            case 'system_mode':
                await entity.read('hvacThermostat', [0x4007], boschManufacturer);
                break;

            default: // Unknown key
                throw new Error(`Unhandled key toZigbee.bosch_thermostat.convertGet ${key}`);
            }
        },
    },
    bosch_userInterface: {
        key: ['display_orientation','display_ontime','display_brightness','child_lock'],
        convertSet: async (entity, key, value, meta) => {
           if (key === 'display_orientation') {
                const index = displayOrientation[value];
                await entity.write('hvacUserInterfaceCfg', {0x400b: {value: index, type: herdsman.Zcl.DataType.uint8}}, boschManufacturer);
                return {state: {display_orientation: value}};
            }
           if (key === 'display_ontime') {
                await entity.write('hvacUserInterfaceCfg', {0x403a: {value: value, type: herdsman.Zcl.DataType.enum8}}, boschManufacturer);
                return {state: {display_onTime: value}};
            }
           if (key === 'display_brightness') {
                await entity.write('hvacUserInterfaceCfg', {0x403b: {value: value, type: herdsman.Zcl.DataType.enum8}}, boschManufacturer);
                return {state: {display_brightness: value}};
            }
            if (key === 'child_lock') {
                const keypadLockout = Number(value==='LOCK');
                await entity.write('hvacUserInterfaceCfg', {keypadLockout});
                return {state: {child_lock: value}};
        }
        },
        convertGet: async (entity, key, meta) => {
            switch (key) {
            case 'display_orientation':
                await entity.read('hvacUserInterfaceCfg', [0x400b], boschManufacturer);
                break;
            case 'display_ontime':
                await entity.read('hvacUserInterfaceCfg', [0x403a], boschManufacturer);
                break;
            case 'display_brightness':
                await entity.read('hvacUserInterfaceCfg', [0x403b], boschManufacturer);
                break;
            case 'child_lock':
        await entity.read('hvacUserInterfaceCfg', ['keypadLockout']);
                break;
            default: // Unknown key
                throw new Error(`Unhandled key toZigbee.bosch_userInterface.convertGet ${key}`);
            }
        },
    },
};

const fzLocal = {
    bosch_thermostat: {
        cluster: 'hvacThermostat',
        type: ['attributeReport', 'readResponse'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            const data = msg.data;
            if (data.hasOwnProperty(0x4042)) {
                result.window_open = (Object.keys(stateOffOn)[data[0x4042]]);
            }
            if (data.hasOwnProperty(0x4043)) {
                result.boost = (Object.keys(stateOffOn)[data[0x4043]]);
            }
            if (data.hasOwnProperty(0x4007)) {
        const opModes = {0: 'auto', 1: 'heat', 2: 'unknowm 2', 3: 'unknonw 3', 4: 'unknown 4', 5: 'off'};
                result.system_mode = opModes[data[0x4007]];
            }

            return result;
        },
    },
    bosch_userInterface: {
        cluster: 'hvacUserInterfaceCfg',
        type: ['attributeReport', 'readResponse'],
        convert: (model, msg, publish, options, meta) => {
            const result = {};
            const data = msg.data;
            if (data.hasOwnProperty(0x400b)) {
                result.display_orientation = (Object.keys(displayOrientation)[data[0x400b]]) ;
            }
            if (data.hasOwnProperty(0x403a)) {
                result.display_ontime = data[0x403a];
            }
            if (data.hasOwnProperty(0x403b)) {
                result.display_brightness = data[0x403b];
            }
            if (data.hasOwnProperty('keypadLockout')) {
                result.child_lock = (data['keypadLockout']==1 ? 'LOCK' : 'UNLOCK');
            }

            return result;
        },
    },

};

const definition = {
    zigbeeModel: ["RBSH-TRV0-ZB-EU"],
    model: "Radiator Thermostat II",
    vendor: "Bosch",
    description: "Radiator Thermostat",
    fromZigbee: [fz.thermostat, fz.battery, fzLocal.bosch_thermostat, fzLocal.bosch_userInterface],
    toZigbee: [tz.thermostat_occupied_heating_setpoint, tz.thermostat_local_temperature_calibration, tz.thermostat_local_temperature, tz.thermostat_keypad_lockout, tzLocal.bosch_thermostat, tzLocal.bosch_userInterface],
    exposes: [
    exposes.climate()
      .withLocalTemperature()
      .withSetpoint('occupied_heating_setpoint', 5, 30, 0.5, ea.STATE_SET)
          .withLocalTemperatureCalibration(-12, 12, 0.5)
      .withSystemMode(['off', 'heat', 'auto'], ea.STATE_SET),
        exposes.binary('boost', ea.ALL, 'ON', 'OFF')
                .withDescription('Activate Boost heating'),
        exposes.binary('window_open', ea.ALL, 'ON', 'OFF')
                .withDescription('Window open'),
    e.child_lock(),
    exposes.enum('display_orientation', ea.ALL, Object.keys(displayOrientation))
        .withDescription('Display orientation'),
    exposes.numeric('display_ontime', ea.ALL).withValueMin(5).withValueMax(30)
                .withDescription('Specifies the diplay On-time'),
    exposes.numeric('display_brightness', ea.ALL).withValueMin(0).withValueMax(10)
                .withDescription('Specifies the brightness value of the display'),
        e.battery(),
    ],
    configure: async (device, coordinatorEndpoint, logger) => {
        const endpoint = device.getEndpoint(1);
        await reporting.bind(endpoint, coordinatorEndpoint, ['genPowerCfg', 'hvacThermostat','hvacUserInterfaceCfg']);
    await reporting.thermostatOccupiedHeatingSetpoint(endpoint);
        await reporting.thermostatTemperature(endpoint);
        await reporting.batteryPercentageRemaining(endpoint);

    await endpoint.read('hvacThermostat',['localTemperatureCalibration']);
        await endpoint.read('hvacThermostat', [0x4007, 0x4042, 0x4043], boschManufacturer);

        await endpoint.read('hvacUserInterfaceCfg', ['keypadLockout']);
    await endpoint.read('hvacUserInterfaceCfg', [0x400b, 0x403a, 0x403b], boschManufacturer);
     }
};

module.exports = definition;
mmattel commented 1 year ago

@BBJake I have updated the integration according your code, but because my Conbee II currently does not support "Installation Codes", could you test the upgrade?

BBJake commented 1 year ago

Thanks for your integration of my code. I'm not sure how I can test the upgrade, because I use the addon in "Home Automation Supervisor". As this runs in a docker container, I think I cannot directly use your branch...

BBJake commented 1 year ago

Another question. What firmware version do you have on your Thermostat II? For me it shows: 3.02.05 (Date: 20220627). According to Update 2022-10-27 and Update 2022-11-14 there are some interesting updates... I do not own a Bosch Smarthome Controler. So the question is: Is there a way to get the update and use it with Z2M OTA?

itkama commented 1 year ago

Another question. What firmware version do you have on your Thermostat II? For me it shows: 3.02.05 (Date: 20220627). According to Update 2022-10-27 and Update 2022-11-14 there are some interesting updates... I do not own a Bosch Smarthome Controler. So the question is: Is there a way to get the update and use it with Z2M OTA?

Same version on my thermostat (that is working wonderfully with your code, thank you for that). Regarding getting our hands on the ota-File - I don't think Bosch is willing to provide that. I guess we would need someone with the Bosch SHC and a firewall that can log every request to get the download links for the thermostat. I did how ever added a feature request post on the bosch forums to maybe convince them to provide the files. I don't have high hopes - but can't hurt to ask.

Nezz commented 1 year ago

Would it be possible to expose how much the valve is open as a sensor? The Bosch app shows it and when connecting the thermostats to HA via the Bosch Smart Controller it's exposed too.

mmattel commented 1 year ago

Would it be possible to expose how much the valve is open as a sensor?

@BBJake any ideas?

PaulLeichtfried commented 1 year ago

I would like to take up the update topic again, because the radiator thermostats with the delivered FW version work anything but precise for many users.

Is there any indication of encryption/authentication in the official update process? Otherwise, one could try to obtain a FW using Wireshark or similar. Are there any efforts in this direction or does anyone here own the Bosch Smart Home Controller and could try their luck?

Maybe we should also create a discussion, as I think many would be happy about updates.

itkama commented 1 year ago

@PaulLeichtfried You can vote for it on the Bosch forums, see link above. I don't think Bosch will provide them. Still got a little bit of hope left though 😅. Regarding the wireshark part: As long as there is no authentification of the communication between the SHC and the Bosch servers one could get the link. Maybe a new issue to discuss things regardings Bosch firmware updates is in order, since this issue is already closed anyway.

niklasarnitz commented 1 year ago

@itkama @PaulLeichtfried I just had a look into a decompiled version of the Bosch SmartHome app. There are no signs of the firmware being downloaded in the app and then being installed via the gateway. So the gateway itself would have to download the firmware. So imo the best way to get access to the firmware images is to get a SHC and then dump it's firmware and try to have a look at the update fetching process that way.

They go for 40-50 Euros on eBay-Kleinanzeigen where I live, so if one or two more would give 10 Euros, I could buy one and extract the firmware and try to find the images. I also have the Thermostat II so I could also test it.

danieledwardgeorgehitchcock commented 1 year ago

I already looked into dumping the controller firmware or gaining root access - It's not for the faint hearted! there are 3 microcontrollers all with SRAM that could contain the needed firmware / os - there are also some unlabelled diagnostic ports but I could not work out the communication between it all.. I also decompiled the phone app too and can confirm that it uses an API to talk to the hub - the hub is where the main grunt work is done

niklasarnitz commented 1 year ago

@danieledwardgeorgehitchcock Could you shoot me an email? Maybe we could find something together. I have a fair amount of hardware hacking experience.

niklas [at] arnitz.email

PaulLeichtfried commented 1 year ago

@itkama I will upvote your contribution, I don't really think Bosch will publish anything either, but it's worth a try.

@niklasarnitz I will gladly contribute 10€ if it brings us closer to the goal.

@danieledwardgeorgehitchcock But wouldn't it be easier to first monitor the network traffic from the SHC than to somehow get the firmware from the SHC? Have you already tried something in this direction, is there anything obvious that prevents it?

danieledwardgeorgehitchcock commented 1 year ago

The traffic from the controller is HTTPS encrypted and MITM proxying is useless without being able to install a root certificate on the controller itself

niklasarnitz commented 1 year ago

@danieledwardgeorgehitchcock Do you have pictures of the PCB? I have just ordered a SHC and would like to research a bit until it arrives.

danieledwardgeorgehitchcock commented 1 year ago

I can deffo take some tomorrow

niklasarnitz commented 1 year ago

That would be awesome.

BBJake commented 1 year ago

Would it be possible to expose how much the valve is open as a sensor?

@BBJake any ideas?

I have not seen the thermostat exposing that value. But it should be possible to get it on demand. I will try something tomorrow, but I cannot verify it as don't have the SHC to see if the value is correct...

BBJake commented 1 year ago

I guess it works... but the value is not published automatically by the device... So you have to manually update:

image
danieledwardgeorgehitchcock commented 1 year ago

Here's the photos of the hub:

Bottom Top Possible UART / Debug Side Ports

niklasarnitz commented 1 year ago

After having a look at your images I came up with the following components:

I will propably just connect up my logic analyzer to the pins around the CPU and then have a look at the signals. Maybe I will just straight up find a UART. But that will have to wait until my SHC arrives.

@danieledwardgeorgehitchcock did you probe some of the pins already?

danieledwardgeorgehitchcock commented 1 year ago

I probed as many pins as I felt confident doing (mostly the ones in the 3rd picture) I have a feeling that the UART may be disabled but you may be able to do a power glitch if you're lucky? I gave up after that point as I didn't want root access that badly!

niklasarnitz commented 1 year ago

That would be frustrating if the uart was disabled.

But maybe one would be able to read the NAND flash if there were enough debug pins routed out as pads.

Let’s see when my unit arrives.

Nezz commented 1 year ago

I guess it works... but the value is not published automatically by the device...

Awesome, thanks. Is it a separate endpoint? I guess we could read that endpoint whenever we receive some data in onEvent, such as the temperature changing.

BBJake commented 1 year ago

I also managed to get the device to report changes... I just had to deal with "configureReporting" and understand the principle... the documentation isn't really helpful there.

image image

I want to do some more things, but will give a PR later...

BBJake commented 1 year ago

https://github.com/Koenkk/zigbee-herdsman-converters/pull/5150

Nezz commented 1 year ago

My thermostat seems to be stuck in an infinite loop between 18 and 24 degrees. Any idea what might cause this? image Resetting it does not help. Restarting Z2M did.