eQ-3 MAX! integration

Hello all,
not sure if this is the correct place to post this, since this is not a “fully custom integration”, this is just a fix of bugs in the official integration + the python class used, with some new features.
MAX! system is quite old and starting from July all the cloud part was dismissed, but it still works and with a persistent connection to the cube it’s also stable, so i can’t see a reason atm to dismiss it.
The class was bugged and has no maintainer left, so to fix it i took the occasion also to add some features i found useful.

Btw, i just posted the code on git to share if someone is still using this system

This is based on code from:

Includes:
Integration:

  • added a binary sensor for link quality of devices (useful to monitor devices)
  • added a fake HVAC for the cube itself (useful to set config of all rooms in one place. The feedback is obviously simulated at cube level and take some seconds for all the rest)
  • fixed the use of presets (away is useless, but windows open is not and was missing)
  • extended windows open value also to wall thermostat
  • widely extended devices attributes. Taken valve position also on wall thermostat
  • new sensor for valve opening value (why not??)

Class:

  • included management of more devices’ data (it was sooooooo limited)
  • extended “get_programmed_temp_at” also to wall thermostat
  • fixed command transmission to manage cube-level commands (this was the bug all started from)

For the use, just put the full directory in your config/custom_components dir.
The use is the very same of the original integration.

3 Likes

This is much appreciated, will install this weekend.

Would it not be a better option if you’d make a PR to fix the original integration?
I’d love config flow (I program everything in YAML but like the device setup to be clearly visible on the integration page).

Thanks for your efforts!

i’ve done this through this nodered flow as the integration wasn’t stable enough during my tests. i also remembers that some sensors wasn’t implemented. it publish status and battery through mqtt integration and even receive temperature changes. it even send ON to the boiler if needed.

[
  {
    "id": "103b1fd4c70bd5ad",
    "type": "tab",
    "label": "Chaudière MQTT",
    "disabled": false,
    "info": "",
    "env": []
  },
  {
    "id": "fd3ce805ed926c80",
    "type": "inject",
    "z": "103b1fd4c70bd5ad",
    "name": "timer",
    "props": [],
    "repeat": "15",
    "crontab": "",
    "once": true,
    "onceDelay": "1",
    "topic": "",
    "x": 90,
    "y": 420,
    "wires": [
      [
        "248a31587c7960fe"
      ]
    ]
  },
  {
    "id": "7b8330524ea2f4c4",
    "type": "mqtt out",
    "z": "103b1fd4c70bd5ad",
    "name": "mqtt chaudiere",
    "topic": "tasmota/chaudiere/cmnd/POWER",
    "qos": "0",
    "retain": "false",
    "respTopic": "",
    "contentType": "",
    "userProps": "",
    "correl": "",
    "expiry": "",
    "broker": "d7efe4ef4af81eb9",
    "x": 620,
    "y": 100,
    "wires": []
  },
  {
    "id": "9fabcb15eb75a432",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "compute action",
    "func": "var res = {};\nvar miss = 0;\nfor (const [key, value] of Object.entries(msg.payload)) {\n    if (value.device_type == 3) {\n        name = value.room_name.replace(\" \", \"_\");// + \"_\" + value.device_name.replace(\" \", \"_\");\n        if (value.temp < value.setpoint) {\n            if (value.room_name != 'WC')\n                miss += value.setpoint - value.temp;\n        }\n        res[name] = { \"status\": \"off\" };\n    }\n}\n\nvar action = null;\nif (miss <= 0.5) {\n    action = 'OFF';\n}\nif (miss >= 1.2) {\n    action = 'ON';\n    for (const [key, value] of Object.entries(msg.payload)) {\n        if (value.device_type == 3) {\n            name = value.room_name.replace(\" \", \"_\");// + \"_\" + value.device_name.replace(\" \", \"_\")\n            if (value.temp < value.setpoint && value.room_name != 'WC') {\n                status = 'heat';\n            } else {\n                status = 'off';\n            }\n            res[name] = { \"status\": status };\n        }\n    }\n}\nreturn [ { 'payload': action, 'heat': miss }, { \"payload\": res } ];",
    "outputs": 2,
    "timeout": "",
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 400,
    "y": 120,
    "wires": [
      [
        "7b8330524ea2f4c4",
        "87831a7d8384ba61"
      ],
      [
        "f1d30ee021d42cf6"
      ]
    ]
  },
  {
    "id": "3355f9d8134bb8d5",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "",
    "x": 590,
    "y": 440,
    "wires": [
      [
        "abcd09898c38c8b2",
        "92e0fc2670adcad8",
        "150d48cddfcad4b4"
      ]
    ]
  },
  {
    "id": "bd0c8714d97255f4",
    "type": "mqtt out",
    "z": "103b1fd4c70bd5ad",
    "name": "mqtt thermostat",
    "topic": "",
    "qos": "",
    "retain": "",
    "respTopic": "",
    "contentType": "",
    "userProps": "",
    "correl": "",
    "expiry": "",
    "broker": "d7efe4ef4af81eb9",
    "x": 1100,
    "y": 440,
    "wires": []
  },
  {
    "id": "abcd09898c38c8b2",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "config",
    "func": "msg.topic = 'homeassistant/climate/' + msg.payload.room_name.replace(\" \", \"_\");\nmsg.payload.config = {}\nmsg.payload.config[\"~\"] = msg.topic;\nmsg.payload.config[\"uniq_id\"] = \"maxcube_\" + msg.payload.room_name.replace(\" \", \"_\");\nmsg.payload.config[\"name\"] = \"Thermostat \" + msg.payload.room_name;\nmsg.payload.config[\"modes\"] = [ \"heat\", \"off\" ]\nmsg.payload.config[\"mode_state_topic\"] = \"~/status\";\nmsg.payload.config[\"temp_cmd_t\"] = \"~/setpoint/set\";\nmsg.payload.config[\"temp_stat_t\"] = \"~/setpoint\";\nmsg.payload.config[\"curr_temp_t\"] = \"~/temp\";\nmsg.payload.config[\"min_temp\"] = \"12\";\nmsg.payload.config[\"max_temp\"] = \"25\";\nmsg.payload.config[\"temp_step\"] = \"0.5\";\nmsg.payload.config[\"preset_modes\"] = [ \"AUTO\", \"MANUAL\", \"BOOST\" ];\nmsg.payload.config[\"preset_mode_command_topic\"] = \"~/mode/set\";\nmsg.payload.config[\"preset_mode_state_topic\"] = \"~/mode\";\ndelete msg.parts;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 710,
    "y": 440,
    "wires": [
      [
        "6e516fa2a5793285"
      ]
    ]
  },
  {
    "id": "6e516fa2a5793285",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "key",
    "x": 830,
    "y": 440,
    "wires": [
      [
        "7ffcfab4ee726aaf"
      ]
    ]
  },
  {
    "id": "7ffcfab4ee726aaf",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "suffix",
    "func": "msg.topic = msg.topic + '/' + msg.key;\ndelete msg.key;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 950,
    "y": 440,
    "wires": [
      [
        "bd0c8714d97255f4"
      ]
    ]
  },
  {
    "id": "a9b073610ed0da42",
    "type": "mqtt in",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "topic": "",
    "qos": "2",
    "datatype": "auto",
    "broker": "d7efe4ef4af81eb9",
    "nl": false,
    "rap": true,
    "rh": 0,
    "inputs": 1,
    "x": 850,
    "y": 380,
    "wires": [
      [
        "e9411a4bc76c03a2"
      ]
    ]
  },
  {
    "id": "92e0fc2670adcad8",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "subscribe",
    "func": "msgo = {}\nmsgo.action = 'subscribe'\nmsgo.topic = 'homeassistant/climate/' + msg.payload.room_name.replace(\" \", \"_\") + '/setpoint/set';\nvar persist = flow.get('persist', 'file');\nif (typeof persist == 'undefined') {\n    persist = {};\n}\npersist[msgo.topic] = msg.payload.rf_address;\nflow.set('persist', persist, 'file');\nreturn msgo;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 720,
    "y": 380,
    "wires": [
      [
        "a9b073610ed0da42"
      ]
    ]
  },
  {
    "id": "2ebfae9701dd6b68",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "decode",
    "func": "var res = {};\nfor (const [key, value] of Object.entries(msg.payload)) {\n    if (value.device_type) {\n        name = value.room_name.replace(\" \", \"_\") + \"_\" + value.device_name.replace(\" \", \"_\");\n        if (!(value.device_type in res))\n            res[value.device_type] = {};\n        res[value.device_type][name] = value;\n    }\n}\nreturn [ { \"payload\": res[1] }, { \"payload\": res[3] }, { \"payload\": res[4] }, res ];",
    "outputs": 4,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 380,
    "y": 420,
    "wires": [
      [
        "f3a618cb9b217476"
      ],
      [
        "3355f9d8134bb8d5"
      ],
      [
        "da37c30786c72470"
      ],
      []
    ]
  },
  {
    "id": "f1d30ee021d42cf6",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "",
    "x": 590,
    "y": 160,
    "wires": [
      [
        "835b0a70c0092e13"
      ]
    ]
  },
  {
    "id": "87ccec39faacfbcc",
    "type": "mqtt out",
    "z": "103b1fd4c70bd5ad",
    "name": "mqtt thermostat etat",
    "topic": "",
    "qos": "",
    "retain": "",
    "respTopic": "",
    "contentType": "",
    "userProps": "",
    "correl": "",
    "expiry": "",
    "broker": "d7efe4ef4af81eb9",
    "x": 1120,
    "y": 160,
    "wires": []
  },
  {
    "id": "835b0a70c0092e13",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "config",
    "func": "msg.topic = 'homeassistant/climate/' + msg.parts.key.replace(\" \", \"_\");\ndelete msg.parts;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 710,
    "y": 160,
    "wires": [
      [
        "f18cca60a898e6f9"
      ]
    ]
  },
  {
    "id": "f18cca60a898e6f9",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "key",
    "x": 830,
    "y": 160,
    "wires": [
      [
        "6396fa8e0d2f7d94"
      ]
    ]
  },
  {
    "id": "6396fa8e0d2f7d94",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "suffix",
    "func": "msg.topic = msg.topic + '/' + msg.key;\ndelete msg.key;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 950,
    "y": 160,
    "wires": [
      [
        "87ccec39faacfbcc"
      ]
    ]
  },
  {
    "id": "f3a618cb9b217476",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "",
    "x": 590,
    "y": 280,
    "wires": [
      [
        "8513a6b21cadce2a",
        "74a4afe44b816105"
      ]
    ]
  },
  {
    "id": "62f3a5b66c9cb055",
    "type": "mqtt out",
    "z": "103b1fd4c70bd5ad",
    "name": "mqtt valve",
    "topic": "",
    "qos": "",
    "retain": "",
    "respTopic": "",
    "contentType": "",
    "userProps": "",
    "correl": "",
    "expiry": "",
    "broker": "d7efe4ef4af81eb9",
    "x": 1090,
    "y": 280,
    "wires": []
  },
  {
    "id": "8513a6b21cadce2a",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "config",
    "func": "msg.topic = 'homeassistant/climate/' + msg.parts.key.replace(\" \", \"_\");\nmsg.payload.config = {}\nmsg.payload.config[\"~\"] = msg.topic;\nmsg.payload.config[\"uniq_id\"] = \"maxcube_valve_\" + msg.parts.key.replace(\" \", \"_\");\nmsg.payload.config[\"name\"] = \"Valve \" + msg.payload.room_name + \" / \" + msg.payload.device_name;\nmsg.payload.config[\"temp_cmd_t\"] = \"~/setpoint/set\";\nmsg.payload.config[\"temp_stat_t\"] = \"~/setpoint\";\nmsg.payload.config[\"curr_temp_t\"] = \"~/temp\";\nmsg.payload.config[\"min_temp\"] = \"12\";\nmsg.payload.config[\"max_temp\"] = \"25\";\nmsg.payload.config[\"temp_step\"] = \"0.5\";\ndelete msg.parts;\nreturn msg;",
    "outputs": 1,
    "timeout": "",
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 710,
    "y": 280,
    "wires": [
      [
        "aa8232289a396eff"
      ]
    ]
  },
  {
    "id": "aa8232289a396eff",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "key",
    "x": 830,
    "y": 280,
    "wires": [
      [
        "27fc31acdbfffe24"
      ]
    ]
  },
  {
    "id": "27fc31acdbfffe24",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "suffix",
    "func": "msg.topic = msg.topic + '/' + msg.key;\ndelete msg.key;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 950,
    "y": 280,
    "wires": [
      [
        "62f3a5b66c9cb055"
      ]
    ]
  },
  {
    "id": "da37c30786c72470",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "",
    "x": 590,
    "y": 540,
    "wires": [
      [
        "6c5e65de567ed8c2",
        "ab2eac1169d5bf93"
      ]
    ]
  },
  {
    "id": "110869ca1733d7fc",
    "type": "mqtt out",
    "z": "103b1fd4c70bd5ad",
    "name": "mqtt window",
    "topic": "",
    "qos": "",
    "retain": "",
    "respTopic": "",
    "contentType": "",
    "userProps": "",
    "correl": "",
    "expiry": "",
    "broker": "d7efe4ef4af81eb9",
    "x": 1090,
    "y": 540,
    "wires": []
  },
  {
    "id": "6c5e65de567ed8c2",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "config",
    "func": "msg.topic = 'homeassistant/binary_sensor/' + msg.parts.key.replace(\" \", \"_\");\nmsg.payload.config = {}\nmsg.payload.config[\"~\"] = msg.topic;\nmsg.payload.config[\"uniq_id\"] = \"maxcube_\" + msg.parts.key.replace(\" \", \"_\");\nmsg.payload.config[\"name\"] = \"Contact \" + msg.payload.room_name + \" / \" + msg.payload.device_name;\nmsg.payload.config[\"state_topic\"] = \"~/open\";\nmsg.payload.config[\"device_class\"] = \"window\";\nmsg.payload.config[\"payload_off\"] = \"false\";\nmsg.payload.config[\"payload_on\"] = \"true\";\ndelete msg.parts;\nreturn msg;",
    "outputs": 1,
    "timeout": "",
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 710,
    "y": 540,
    "wires": [
      [
        "ed64e89cb77da718"
      ]
    ]
  },
  {
    "id": "ed64e89cb77da718",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "key",
    "x": 830,
    "y": 540,
    "wires": [
      [
        "034bc8bc91f49b79"
      ]
    ]
  },
  {
    "id": "034bc8bc91f49b79",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "suffix",
    "func": "msg.topic = msg.topic + '/' + msg.key;\ndelete msg.key;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 950,
    "y": 540,
    "wires": [
      [
        "110869ca1733d7fc"
      ]
    ]
  },
  {
    "id": "e9411a4bc76c03a2",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "get_rfaddress",
    "func": "var persist = flow.get('persist', 'file');\nreturn { \"payload\": { \"rf_address\": persist[msg.topic], \"degrees\": parseFloat(msg.payload), \"mode\": \"MANUAL\" } };",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 1000,
    "y": 380,
    "wires": [
      [
        "5991875a317f02fa"
      ]
    ]
  },
  {
    "id": "5d446138a103fe2c",
    "type": "delay",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "pauseType": "delay",
    "timeout": "1",
    "timeoutUnits": "seconds",
    "rate": "1",
    "nbRateUnits": "1",
    "rateUnits": "second",
    "randomFirst": "1",
    "randomLast": "5",
    "randomUnits": "seconds",
    "drop": false,
    "allowrate": false,
    "outputs": 1,
    "x": 1300,
    "y": 380,
    "wires": [
      [
        "248a31587c7960fe"
      ]
    ]
  },
  {
    "id": "87831a7d8384ba61",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "config",
    "func": "msgo = {};\nmsgo.payload = msg;\nmsgo.payload.heat = msgo.payload.heat.toFixed(1)\nmsgo.topic = 'homeassistant/sensor/maxcube_heat';\nmsgo.payload.config = {}\nmsgo.payload.config[\"~\"] = msgo.topic;\nmsgo.payload.config[\"uniq_id\"] = \"maxcube_heat\";\nmsgo.payload.config[\"name\"] = \"Chaudière Total\";\nmsgo.payload.config[\"state_topic\"] = \"~/heat\";\nmsgo.payload.config[\"unit_of_measurement\"] = \"°\";\nreturn msgo;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 590,
    "y": 60,
    "wires": [
      [
        "f855b4166b9fe922"
      ]
    ]
  },
  {
    "id": "9288a4f985382537",
    "type": "mqtt out",
    "z": "103b1fd4c70bd5ad",
    "name": "mqtt chaudiere total",
    "topic": "",
    "qos": "",
    "retain": "",
    "respTopic": "",
    "contentType": "",
    "userProps": "",
    "correl": "",
    "expiry": "",
    "broker": "d7efe4ef4af81eb9",
    "x": 1020,
    "y": 60,
    "wires": []
  },
  {
    "id": "f855b4166b9fe922",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "key",
    "x": 730,
    "y": 60,
    "wires": [
      [
        "9f45cc50a1240208"
      ]
    ]
  },
  {
    "id": "9f45cc50a1240208",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "suffix",
    "func": "msg.topic = msg.topic + '/' + msg.key;\ndelete msg.key;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 850,
    "y": 60,
    "wires": [
      [
        "9288a4f985382537"
      ]
    ]
  },
  {
    "id": "29949c9d2834f922",
    "type": "comment",
    "z": "103b1fd4c70bd5ad",
    "name": "boiler",
    "info": "boiler management.\nCompute total of missing degrees in each room and turn on the boiler if needed.",
    "x": 370,
    "y": 80,
    "wires": []
  },
  {
    "id": "af681e641dcfc3b7",
    "type": "comment",
    "z": "103b1fd4c70bd5ad",
    "name": "homeassistant",
    "info": "send/receive to homeassistant",
    "x": 400,
    "y": 360,
    "wires": []
  },
  {
    "id": "f3a417bab7c65a6f",
    "type": "http in",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "url": "/cube",
    "method": "post",
    "upload": false,
    "swaggerDoc": "",
    "x": 420,
    "y": 700,
    "wires": [
      [
        "808c2d31a411d3ae",
        "248a31587c7960fe"
      ]
    ]
  },
  {
    "id": "808c2d31a411d3ae",
    "type": "http response",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "statusCode": "200",
    "headers": {},
    "x": 610,
    "y": 740,
    "wires": []
  },
  {
    "id": "26ec92d3c27156f2",
    "type": "comment",
    "z": "103b1fd4c70bd5ad",
    "name": "cube notification",
    "info": "receive cube notification",
    "x": 410,
    "y": 660,
    "wires": []
  },
  {
    "id": "8d145d485d89195c",
    "type": "http in",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "url": "/cube",
    "method": "get",
    "upload": false,
    "swaggerDoc": "",
    "x": 410,
    "y": 740,
    "wires": [
      [
        "808c2d31a411d3ae"
      ]
    ]
  },
  {
    "id": "5991875a317f02fa",
    "type": "maxcube in",
    "z": "103b1fd4c70bd5ad",
    "server": "9a07e5adcaeb56f7",
    "x": 1160,
    "y": 380,
    "wires": [
      [
        "5d446138a103fe2c"
      ]
    ]
  },
  {
    "id": "248a31587c7960fe",
    "type": "maxcube out",
    "z": "103b1fd4c70bd5ad",
    "server": "9a07e5adcaeb56f7",
    "singleMessage": true,
    "x": 220,
    "y": 420,
    "wires": [
      [
        "9fabcb15eb75a432",
        "2ebfae9701dd6b68"
      ]
    ]
  },
  {
    "id": "f7cb83ab364fcd59",
    "type": "mqtt out",
    "z": "103b1fd4c70bd5ad",
    "name": "mqtt thermostat bat",
    "topic": "",
    "qos": "",
    "retain": "",
    "respTopic": "",
    "contentType": "",
    "userProps": "",
    "correl": "",
    "expiry": "",
    "broker": "d7efe4ef4af81eb9",
    "x": 1110,
    "y": 480,
    "wires": []
  },
  {
    "id": "150d48cddfcad4b4",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "config",
    "func": "msg.topic = 'homeassistant/sensor/' + msg.parts.key.replace(\" \", \"_\") + \"_battery\";\nmsg.payload.config = {}\nmsg.payload.config[\"~\"] = msg.topic;\nmsg.payload.config[\"uniq_id\"] = \"maxcube_\" + msg.parts.key.replace(\" \", \"_\") + \"_battery\";\nmsg.payload.config[\"name\"] = \"Thermostat \" + msg.payload.room_name + \" / \" + msg.payload.device_name + \" Battery\";\nmsg.payload.config[\"device_class\"] = \"battery\";\nmsg.payload.config[\"entity_category\"] = \"diagnostic\";\nmsg.payload.config[\"state_class\"] = \"measurement\";\nmsg.payload.config[\"state_topic\"] = \"~/battery_low\";\nmsg.payload.config[\"unit_of_measurement\"] = \"%\";\nmsg.payload.config[\"value_template\"] = '{{ 0 if value == \"true\" else 100 }}';\ndelete msg.parts;\nreturn msg;",
    "outputs": 1,
    "timeout": "",
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 710,
    "y": 480,
    "wires": [
      [
        "a51f87f02c4caa9c"
      ]
    ]
  },
  {
    "id": "a51f87f02c4caa9c",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "key",
    "x": 830,
    "y": 480,
    "wires": [
      [
        "e4afa539314f57e0"
      ]
    ]
  },
  {
    "id": "e4afa539314f57e0",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "suffix",
    "func": "msg.topic = msg.topic + '/' + msg.key;\ndelete msg.key;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 950,
    "y": 480,
    "wires": [
      [
        "f7cb83ab364fcd59"
      ]
    ]
  },
  {
    "id": "81bd3c2349b324fd",
    "type": "mqtt out",
    "z": "103b1fd4c70bd5ad",
    "name": "mqtt valve bat",
    "topic": "",
    "qos": "",
    "retain": "",
    "respTopic": "",
    "contentType": "",
    "userProps": "",
    "correl": "",
    "expiry": "",
    "broker": "d7efe4ef4af81eb9",
    "x": 1100,
    "y": 320,
    "wires": []
  },
  {
    "id": "74a4afe44b816105",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "config",
    "func": "msg.topic = 'homeassistant/sensor/' + msg.parts.key.replace(\" \", \"_\") + \"_battery\";\nmsg.payload.config = {}\nmsg.payload.config[\"~\"] = msg.topic;\nmsg.payload.config[\"uniq_id\"] = \"maxcube_\" + msg.parts.key.replace(\" \", \"_\") + \"_battery\";\nmsg.payload.config[\"name\"] = \"Valve \" + msg.payload.room_name + \" / \" + msg.payload.device_name + \" Battery\";\nmsg.payload.config[\"device_class\"] = \"battery\";\nmsg.payload.config[\"entity_category\"] = \"diagnostic\";\nmsg.payload.config[\"state_class\"] = \"measurement\";\nmsg.payload.config[\"state_topic\"] = \"~/battery_low\";\nmsg.payload.config[\"unit_of_measurement\"] = \"%\";\nmsg.payload.config[\"value_template\"] = '{{ 0 if value == \"true\" else 100 }}';\ndelete msg.parts;\nreturn msg;",
    "outputs": 1,
    "timeout": "",
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 710,
    "y": 320,
    "wires": [
      [
        "cfbbccaac48a692f"
      ]
    ]
  },
  {
    "id": "cfbbccaac48a692f",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "key",
    "x": 830,
    "y": 320,
    "wires": [
      [
        "66cde02a0101b7b3"
      ]
    ]
  },
  {
    "id": "66cde02a0101b7b3",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "suffix",
    "func": "msg.topic = msg.topic + '/' + msg.key;\ndelete msg.key;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 950,
    "y": 320,
    "wires": [
      [
        "81bd3c2349b324fd"
      ]
    ]
  },
  {
    "id": "5dcb6933d4102886",
    "type": "mqtt out",
    "z": "103b1fd4c70bd5ad",
    "name": "mqtt window bat",
    "topic": "",
    "qos": "",
    "retain": "",
    "respTopic": "",
    "contentType": "",
    "userProps": "",
    "correl": "",
    "expiry": "",
    "broker": "d7efe4ef4af81eb9",
    "x": 1100,
    "y": 580,
    "wires": []
  },
  {
    "id": "ab2eac1169d5bf93",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "config",
    "func": "msg.topic = 'homeassistant/sensor/' + msg.parts.key.replace(\" \", \"_\") + \"_battery\";\nmsg.payload.config = {}\nmsg.payload.config[\"~\"] = msg.topic;\nmsg.payload.config[\"uniq_id\"] = \"maxcube_\" + msg.parts.key.replace(\" \", \"_\") + \"_battery\";\nmsg.payload.config[\"name\"] = \"Contact \" + msg.payload.room_name + \" / \" + msg.payload.device_name + \" Battery\";\nmsg.payload.config[\"device_class\"] = \"battery\";\nmsg.payload.config[\"entity_category\"] = \"diagnostic\";\nmsg.payload.config[\"state_class\"] = \"measurement\";\nmsg.payload.config[\"state_topic\"] = \"~/battery_low\";\nmsg.payload.config[\"unit_of_measurement\"] = \"%\";\nmsg.payload.config[\"value_template\"] = '{{ 0 if value == \"true\" else 100 }}';\ndelete msg.parts;\nreturn msg;",
    "outputs": 1,
    "timeout": "",
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 710,
    "y": 580,
    "wires": [
      [
        "e8287865bfb90923"
      ]
    ]
  },
  {
    "id": "e8287865bfb90923",
    "type": "split",
    "z": "103b1fd4c70bd5ad",
    "name": "",
    "splt": "\\n",
    "spltType": "str",
    "arraySplt": 1,
    "arraySpltType": "len",
    "stream": false,
    "addname": "key",
    "x": 830,
    "y": 580,
    "wires": [
      [
        "fbb85cba9aa4609c"
      ]
    ]
  },
  {
    "id": "fbb85cba9aa4609c",
    "type": "function",
    "z": "103b1fd4c70bd5ad",
    "name": "suffix",
    "func": "msg.topic = msg.topic + '/' + msg.key;\ndelete msg.key;\nreturn msg;",
    "outputs": 1,
    "noerr": 0,
    "initialize": "",
    "finalize": "",
    "libs": [],
    "x": 950,
    "y": 580,
    "wires": [
      [
        "5dcb6933d4102886"
      ]
    ]
  },
  {
    "id": "d7efe4ef4af81eb9",
    "type": "mqtt-broker",
    "name": "MQTT",
    "broker": "10.68.69.5",
    "port": "1883",
    "clientid": "nodered",
    "autoConnect": true,
    "usetls": false,
    "protocolVersion": "4",
    "keepalive": "60",
    "cleansession": true,
    "autoUnsubscribe": true,
    "birthTopic": "nodered/status",
    "birthQos": "0",
    "birthRetain": "true",
    "birthPayload": "online",
    "birthMsg": {},
    "closeTopic": "nodered/status",
    "closeQos": "0",
    "closeRetain": "true",
    "closePayload": "offline",
    "closeMsg": {},
    "willTopic": "nodered/status",
    "willQos": "0",
    "willRetain": "true",
    "willPayload": "timeout",
    "willMsg": {},
    "userProps": "",
    "sessionExpiry": ""
  },
  {
    "id": "9a07e5adcaeb56f7",
    "type": "maxcube-server",
    "host": "10.68.69.120",
    "port": "62910",
    "disabled": false
  }
]

With the official integration I have problems controling my 3 heater thermostats in the living room. In the original EQ3 Max Software i configured them to be together in one room. All “single” thermostats in the other rooms are working pretty good.
Does anybody know this problem?

with no wall thermostats? afaik if you set up devices in the same room, the commands to a single device are taken to the whole room, but i have wall thermostats… you could try to get the command to be sent to the room id in place of device rf id

Thanks for your very fast reply. In fact i bougt all this stuff used only one year ago and unfortunately there was no room thermostat included. But so far it worked. When i changed the temperature on on thermostat all the others did the same. But know with the HA integration it seems this is not working when i chnage dem temperature on one of the thermostas via HA.
But if you say/think it will work with a room thermostat i will try to find one used i can buy.
By the way: Thanks for your work on this integration! Its very nice to be able to have a stable integration now.

what is the system doing when you change the temperature on one? it changes only that one and not the others?
sounds strange.
yes with wall thermostat it works (it’s my setup) but honestly it should work also in your case.
if you want to have same fun, you can try changing the code to send the command to the room id instead of the device id, in this way the command should be sent to all the devices in the room.

No in fact nothing changes if i chnage dem temperature of only one thermostat via HA. After some Minutes i can see the old value in HA.
I now found a used wall thermostat i ordered and will try this.
But I can also try to change some code if you can give me some hints were I can find the place I have to change it.
Thanks!

no errors in logs?
very strange :slight_smile:

Yes there are error Messages. But in my opinion they are not correct, because i only have one Cube where all thermostats are connected to and changing the others is working. So connection between Cube and HA seem to be ok. Also I get INformation about the Status the “faulty” thermostats have if i change e.g. the temperature directly on the thermostat.

This is from the logs:

Dieser Fehler wurde von einer benutzerdefinierten Integration verursacht

Logger: custom_components.maxcube.maxcube.commander
Source: custom_components/maxcube/maxcube/commander.py:85
Integration: eQ-3 MAX!
First occurred: 14. November 2023 um 18:48:42 (46 occurrences)
Last logged: 17:36:32

Error sending radio message to Max! Cube: Deadline send-radio-msg[15/30]:cmd-reply[0/2]
Error sending radio message to Max! Cube: Deadline send-radio-msg[9.98/30]:cmd-reply[0/2]
Error sending radio message to Max! Cube: Deadline send-radio-msg[4.97/30]:cmd-reply[0/2]
Error sending radio message to Max! Cube: timed out
Error sending radio message to Max! Cube: Deadline send-radio-msg[0/30]:cmd-reply[0/2]

your log is about command not being sent to the cube itself, so not linked with your structure. it’s all about timeout, quite strange if in the meantime commands to other devices are working
and you also have A LOT of errors, i’d suggest to check connection with the cube before…

Sorry but not sure how I should check this. As written connection is absolut OK and is working for all other thermostats without any delay.
Also as written a change on the thermostat itself is directly shown in HA.
So I can not understand where I could have a connection issue between Cube and HA.
BTW: Both (HA and Cube) are connected to the same “flat” LAN without any special things (Firewalls etc) in between.
Also of course I already tried several restarts of Cube as well as HA but always with the same result. The “solo” thermostats are working without any problems but the three linked to the same room have the problem.

I think i will wait until the room thermostat arrives and test with that one.

EDIT:
I just had the idea to seperate one of the “problem thermostats” from the others. So i disconnected the cube from HA, opened the old MAX Software and put one of these thermostats into a seperate room. Than disconnected the MAX Software and connect again via HA.
Result:
The one thermostat now also works but the other two still bundled to one room still are not working.

Just to make the picture complete i added a second thermostat to the new room and from than on the error in HA was there again. Shifting back one of the thermostats and leaving only one in the room and it worked again.

So for me its relativly sure now that the problem is to have more than one thermostat in one room.

Interesting. I have rooms with more than One radiator thermostats, and It work, bit i also have Wall thermostat and here Is where i send commands :slight_smile:
Ill try this evening to send a command tò One of the radiator, since the errore you reported Is specifically on the connection so its really strange…
Otherwise well try tò debug the command itself :slight_smile:
(Sorry for typos coming from my italian auto correction keyboard lol)

Just tried remotely. The command was successful and It changed the temperature to all the radiator thermostats and also on the Wall thermostat in the room.
Ill try this evening to publish update scripts (i had tò fix another couple of bugs in the official integration) and suggest a modification tò use room id, lets see

Hello,
just posted some fixes to the code

those are needed since the official integration (!!!) is not working with service climate.set_temperature

Now, for your problem, i’d try to check what the set_temperature_mode is transmitting when you try to change the value.

Please try enabling debugging or, cleares, just turn the level of logger to “error” in:
maxcube/cube.py line 331

Thanks for all your help!
I copied the new files and changed the logger level.
Unfortunatley now it does also not work on the thermostates it worked before…

Here are the logs:

Logger: custom_components.maxcube.maxcube.cube
Source: custom_components/maxcube/maxcube/cube.py:331
Integration: eQ-3 MAX!
First occurred: 20:35:24 (4 occurrences)
Last logged: 20:35:55

Setting temperature 17.0 and mode 0 on device 0AF1A5! Room 01 - starting device mode 0 (command: 0004400000000AF1A50122)
Setting temperature 20.0 and mode 0 on device 0AF5FA! Room 05 - starting device mode 0 (command: 0004400000000AF5FA0528)
Setting temperature 17.5 and mode 0 on device 0AF7E8! Room 03 - starting device mode 0 (command: 0004400000000AF7E80323)

Logger: custom_components.maxcube.maxcube.commander
Source: custom_components/maxcube/maxcube/commander.py:85
Integration: eQ-3 MAX!
First occurred: 20:35:27 (4 occurrences)
Last logged: 20:35:57

* Error sending radio message to Max! Cube: Deadline send-radio-msg[0/2]:cmd-reply[0/2]
* Error sending radio message to Max! Cube: timed out

in the maxcube/commander.py i shorted the timeouts because for me they were far too long.
You still get connection error an timeouts, i’d suggest changing them back to the old value (lines 16-20) but still, those are cube connections errors!

if one of rooms 1, 3 or 5 is the room where you have more than one radiator thermostat, i can’t see anything wrong, but in case you can try sengin only the room ID (e.g. 01 for the first line) and six zeros in place of the rf id (so “000000” in place of “0AF1A5”)

Seems i was little to fast after reboot. Now it works more or less. Interestingly now if i change te temperature of one of the working thermostats it changes shortly in HA and directly on ther thermostat, but after 1 second it jumps back to the old value in HA while on the thermostat itself it is still the new set value.
Here are some new logs.
Room 5 is working, Room 4 is the one that is not working:

Setting temperature 17.0 and mode 0 on device 0AF5FA! Room 05 - starting device mode 0 (command: 0004400000000AF5FA0522)
Setting temperature 21.5 and mode 0 on device 0AF544! Room 04 - starting device mode 0 (command: 0004400000000AF544042B)
Setting temperature 21.0 and mode 0 on device 0AF544! Room 04 - starting device mode 0 (command: 0004400000000AF544042A)
Setting temperature 23.0 and mode 0 on device 0AF0D6! Room 04 - starting device mode 0 (command: 0004400000000AF0D6042E)
Setting temperature 24.0 and mode 0 on device 0AF5FA! Room 05 - starting device mode 0 (command: 0004400000000AF5FA0530)

yes because the integration updates values very badly, but this is just delayed, if you wait 30 seconds it goes to the right value.
what about your multi-device room? is it working? i see nothing wrong in the log, the command is properly formatted

No. Seems like Room 4 is the one with multiple thermostats and that one is still not working. And if i assign another thermostat to room 5 it is also not working. So (in my case) it is not depending on the room itself but on the fact that it has more than one thermostat.
Maybe on the weekend i will try to make a complete reset on the cube, but not sure if i can find time to do that and reconnect and reconfigure all the thermostats…