Zont to HomeKit Gateway

Gateway between Microline Zont Thermostat (https://zont-online.ru) and Apple Homekit.

It allows you to:

  • translate temperature sensors readings
  • update and control you thermostat:
    • read temperature
    • set temperature
    • set mode
  • Use Siri to control your Zont

Modes mapping:

HomeKit Heating <-> ZONT Comfort

HomeKit Cooling <-> ZONT Economy

HomeKit Auto <-> ZONT Schedule

HomeKit Off <-> ZONT Anti-freezing

Requirements:

  • node-red-contrib-homekit
  • node-red-contrib-jsonpath

Brief manual:

  1. install node-red
  2. install requirements
  3. import flow
  4. configure auth settings (login & password) for "make DEVICES request" and "make UPDATE request" nodes
  5. configure your thermometers - create HomeKit Temperature Sensors and attach them to the Switch. Use the names same as in your ZONT profile.
  6. ask me if it's not working :]

TODO

  1. Fix device discovery. Now only one ZONT H-1V is supported.
  2. Boiler Alert support. Needs to support boiler error alerts.
[
    {
        "id": "7c7cc80f.76d888",
        "type": "tab",
        "label": "Zont GW"
    },
    {
        "id": "1761d462.769304",
        "type": "http request",
        "z": "7c7cc80f.76d888",
        "name": "make DEVICES request",
        "method": "POST",
        "ret": "obj",
        "url": "https://zont-online.ru/api/devices",
        "tls": "",
        "x": 181.5,
        "y": 344,
        "wires": [
            [
                "a897aa83.0bcd6",
                "fddc8c2.fa6f67",
                "7bd9fd85.63f1d4",
                "336a79a.9230786"
            ]
        ]
    },
    {
        "id": "495cf5db.61050c",
        "type": "inject",
        "z": "7c7cc80f.76d888",
        "name": "Get periodical ZONT Update",
        "topic": "",
        "payload": "{\"load_io\": true}",
        "payloadType": "json",
        "repeat": "60",
        "crontab": "",
        "once": true,
        "x": 208.5,
        "y": 169,
        "wires": [
            [
                "b5c29fb9.87983"
            ]
        ]
    },
    {
        "id": "b5c29fb9.87983",
        "type": "function",
        "z": "7c7cc80f.76d888",
        "name": "Add ZONT header",
        "func": "msg.headers = {\n    \"Content-type\" : \"application/json\",\n    \"X-ZONT-Client\" : \"[email protected]\"\n    }\n\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "x": 161.5,
        "y": 251,
        "wires": [
            [
                "1761d462.769304"
            ]
        ]
    },
    {
        "id": "4d308236.72aa84",
        "type": "homekit-service",
        "z": "7c7cc80f.76d888",
        "accessory": "d89c95f5.b65cf8",
        "name": "room",
        "serviceName": "TemperatureSensor",
        "x": 1485.5,
        "y": 173,
        "wires": [
            []
        ]
    },
    {
        "id": "a897aa83.0bcd6",
        "type": "jsonpath",
        "z": "7c7cc80f.76d888",
        "expression": "$.devices[?(@.device_type.code=='T100')].thermometers[?(@.is_assigned_to_slot==true && @.last_state=='ok')]",
        "split": true,
        "name": "Gather temperature sensors from ZONT",
        "x": 600.5,
        "y": 272,
        "wires": [
            [
                "45c44f4a.d9ac38"
            ]
        ]
    },
    {
        "id": "343744eb.740a4c",
        "type": "switch",
        "z": "7c7cc80f.76d888",
        "name": "Choose right sensor",
        "property": "payload.Name",
        "propertyType": "msg",
        "rules": [
            {
                "t": "eq",
                "v": "room",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Котел подача",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Котел обратка",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "ТП подача",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "ТП обратка",
                "vt": "str"
            },
            {
                "t": "eq",
                "v": "Улица",
                "vt": "str"
            }
        ],
        "checkall": "false",
        "outputs": 6,
        "x": 1206.5,
        "y": 276,
        "wires": [
            [
                "4d308236.72aa84"
            ],
            [
                "9f6e2b6.bb8bc58"
            ],
            [
                "2dc40a06.e50dbe"
            ],
            [
                "805024f4.4dc69"
            ],
            [
                "50763fa1.d66f28"
            ],
            [
                "5b80df79.4f77"
            ]
        ]
    },
    {
        "id": "45c44f4a.d9ac38",
        "type": "function",
        "z": "7c7cc80f.76d888",
        "name": "Create update JSON",
        "func": "msg.payload = {\n    \"Name\": msg.payload.name,\n    \"CurrentTemperature\": msg.payload.last_value,\n    \"StatusActive\": true,\n}\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "x": 924,
        "y": 274,
        "wires": [
            [
                "343744eb.740a4c"
            ]
        ]
    },
    {
        "id": "9f6e2b6.bb8bc58",
        "type": "homekit-service",
        "z": "7c7cc80f.76d888",
        "accessory": "d89c95f5.b65cf8",
        "name": "Котел подача",
        "serviceName": "TemperatureSensor",
        "x": 1518,
        "y": 225,
        "wires": [
            []
        ]
    },
    {
        "id": "2dc40a06.e50dbe",
        "type": "homekit-service",
        "z": "7c7cc80f.76d888",
        "accessory": "d89c95f5.b65cf8",
        "name": "Котел обратка",
        "serviceName": "TemperatureSensor",
        "x": 1517,
        "y": 273,
        "wires": [
            []
        ]
    },
    {
        "id": "805024f4.4dc69",
        "type": "homekit-service",
        "z": "7c7cc80f.76d888",
        "accessory": "d89c95f5.b65cf8",
        "name": "ТП подача",
        "serviceName": "TemperatureSensor",
        "x": 1509,
        "y": 326,
        "wires": [
            []
        ]
    },
    {
        "id": "50763fa1.d66f28",
        "type": "homekit-service",
        "z": "7c7cc80f.76d888",
        "accessory": "d89c95f5.b65cf8",
        "name": "ТП обратка",
        "serviceName": "TemperatureSensor",
        "x": 1511,
        "y": 385,
        "wires": [
            []
        ]
    },
    {
        "id": "5b80df79.4f77",
        "type": "homekit-service",
        "z": "7c7cc80f.76d888",
        "accessory": "d89c95f5.b65cf8",
        "name": "Улица",
        "serviceName": "TemperatureSensor",
        "x": 1500,
        "y": 440,
        "wires": [
            []
        ]
    },
    {
        "id": "fddc8c2.fa6f67",
        "type": "jsonpath",
        "z": "7c7cc80f.76d888",
        "expression": "$.devices[?(@.device_type.code=='T100')].thermostat_mode",
        "split": true,
        "name": "Gather thermostat mode",
        "x": 579,
        "y": 453,
        "wires": [
            [
                "12b76895.7f7b5f"
            ]
        ]
    },
    {
        "id": "7bd9fd85.63f1d4",
        "type": "jsonpath",
        "z": "7c7cc80f.76d888",
        "expression": "$.devices[?(@.device_type.code=='T100')].thermostat_mode_temps",
        "split": true,
        "name": "Gather thermostat temps",
        "x": 577,
        "y": 512,
        "wires": [
            [
                "8ad82210.0f4a48"
            ]
        ]
    },
    {
        "id": "12b76895.7f7b5f",
        "type": "function",
        "z": "7c7cc80f.76d888",
        "name": "Update thermostat with the current mode",
        "func": "switch(msg.payload) {\n    case \"comfort\":\n        flow.set('thermostat_mode', msg.payload)\n        msg.payload = {\n            \"CurrentHeatingCoolingState\": 1,\n            \"TargetHeatingCoolingState\": 1,\n        }\n        break;\n    case \"econom\":\n        flow.set('thermostat_mode', msg.payload)\n        msg.payload = {\n            \"CurrentHeatingCoolingState\": 2,\n            \"TargetHeatingCoolingState\": 2,\n        }\n        break;\n    case \"idle\":\n        flow.set('thermostat_mode', msg.payload)\n        msg.payload = {\n            \"CurrentHeatingCoolingState\": 0,\n            \"TargetHeatingCoolingState\": 0,\n        }\n        break;\n    case \"schedule\":\n        flow.set('thermostat_mode', msg.payload)\n        msg.payload = {\n            \"CurrentHeatingCoolingState\": 3,\n            \"TargetHeatingCoolingState\": 3,\n        }\n        break;\n}\n\nreturn msg",
        "outputs": 1,
        "noerr": 0,
        "x": 899.5554809570312,
        "y": 454,
        "wires": [
            [
                "dccf9d02.c47d08"
            ]
        ]
    },
    {
        "id": "8ad82210.0f4a48",
        "type": "function",
        "z": "7c7cc80f.76d888",
        "name": "Update thermostat teperature",
        "func": "// save thermostat temperatures\nflow.set('thermostat_temperatures', msg.payload); \n\nvar thermostat_mode = flow.get('thermostat_mode');\n\nif (typeof thermostat_mode !== 'undefined') {\n    \n    msg.payload = {\n        \"CurrentTemperature\": msg.payload[thermostat_mode],\n        \"TargetTemperature\": msg.payload[thermostat_mode],\n    };\n\n    return msg;  \n}\n",
        "outputs": 1,
        "noerr": 0,
        "x": 862,
        "y": 510.888916015625,
        "wires": [
            [
                "dccf9d02.c47d08"
            ]
        ]
    },
    {
        "id": "ddd821ad.69ea1",
        "type": "function",
        "z": "7c7cc80f.76d888",
        "name": "Prepare update",
        "func": "// check if HomeKit is initialized\nvar HomeKitInitialized = flow.get('HomeKitInitialized') || false;\nif (HomeKitInitialized === false) {\n    // catch first message from Thermostat and supress it, not updating ZONT\n    flow.set('HomeKitInitialized', true);\n    return null;\n    \n}\n\n// preserve parameters which came from Thermostat to do instantaneous updates \nmsg.data = {};\n\nreply_json = {};\n\nreply_json.device_id = flow.get('deviceid');\n\n// gather thermostat mode from flow context\nvar thermostat_mode = flow.get('thermostat_mode');\n\n// check if we have updated thermostat mode came from HomeKit\nif (typeof msg.payload.TargetHeatingCoolingState !== 'undefined') {\n\n    msg.data.CurrentHeatingCoolingState = msg.payload.TargetHeatingCoolingState; \n\n    // now we have updated mode\n    switch(msg.payload.TargetHeatingCoolingState) {\n        case 0:\n            thermostat_mode = 'idle';\n            \n            break;\n        case 1:\n            thermostat_mode = 'comfort';\n            break;\n        case 2:\n            thermostat_mode = 'econom';\n            break;\n        case 3:\n            thermostat_mode = 'schedule';\n            break;\n    }\n\n    // update thermostat mode in flow context\n    flow.set('thermostat_mode', thermostat_mode);\n\n    reply_json.thermostat_mode = thermostat_mode;\n    \n    msg.payload = reply_json;\n    return msg;\n    \n}\n\n// check if we have updated temperature\nif (typeof msg.payload.TargetTemperature !== 'undefined') {\n    \n\n    if (thermostat_mode != 'schedule') {\n        \n        // we should not update a temperature in schedule mode\n        reply_json.thermostat_mode_temps = {};\n        reply_json.thermostat_mode_temps[thermostat_mode] = msg.payload.TargetTemperature;\n        \n        msg.data.CurrentTemperature = msg.payload.TargetTemperature; \n\n    msg.payload = reply_json;\n    return msg;\n    \n    }\n}\n\nreturn null;\n",
        "outputs": "1",
        "noerr": 0,
        "x": 559,
        "y": 735.7777709960938,
        "wires": [
            [
                "ee51c829.b17808"
            ]
        ]
    },
    {
        "id": "ee51c829.b17808",
        "type": "function",
        "z": "7c7cc80f.76d888",
        "name": "Add ZONT header",
        "func": "msg.headers = {\n    \"Content-type\" : \"application/json\",\n    \"X-ZONT-Client\" : \"[email protected]\"\n    }\n\nreturn msg;\n",
        "outputs": 1,
        "noerr": 0,
        "x": 790,
        "y": 734,
        "wires": [
            [
                "d3052c22.a1491"
            ]
        ]
    },
    {
        "id": "d3052c22.a1491",
        "type": "http request",
        "z": "7c7cc80f.76d888",
        "name": "make UPDATE request",
        "method": "POST",
        "ret": "obj",
        "url": "https://zont-online.ru/api/update_device",
        "tls": "",
        "x": 1015,
        "y": 735,
        "wires": [
            [
                "a1037d30.9a7d3"
            ]
        ]
    },
    {
        "id": "336a79a.9230786",
        "type": "jsonpath",
        "z": "7c7cc80f.76d888",
        "expression": "$.devices[?(@.device_type.code=='T100')].id",
        "split": true,
        "name": "Gather device id for ZONT",
        "x": 561,
        "y": 175,
        "wires": [
            [
                "22fe7853.6d27e8"
            ]
        ]
    },
    {
        "id": "22fe7853.6d27e8",
        "type": "function",
        "z": "7c7cc80f.76d888",
        "name": "save device id into context",
        "func": "flow.set('deviceid', msg.payload);\n",
        "outputs": 1,
        "noerr": 0,
        "x": 863.5,
        "y": 164,
        "wires": [
            []
        ]
    },
    {
        "id": "dccf9d02.c47d08",
        "type": "join",
        "z": "7c7cc80f.76d888",
        "name": "",
        "mode": "custom",
        "build": "merged",
        "property": "payload",
        "propertyType": "msg",
        "key": "topic",
        "joiner": "\\n",
        "timeout": "1",
        "count": "",
        "x": 1188.5,
        "y": 512,
        "wires": [
            [
                "f2912f77.e1acc8"
            ]
        ]
    },
    {
        "id": "44e322da.9da44c",
        "type": "join",
        "z": "7c7cc80f.76d888",
        "name": "",
        "mode": "custom",
        "build": "merged",
        "property": "payload",
        "propertyType": "msg",
        "key": "topic",
        "joiner": "\\n",
        "timeout": "1",
        "count": "",
        "x": 1808.5,
        "y": 608,
        "wires": [
            [
                "ddd821ad.69ea1"
            ]
        ]
    },
    {
        "id": "acfc65cd.2d7588",
        "type": "function",
        "z": "7c7cc80f.76d888",
        "name": "check HomeKit Initialization",
        "func": "// We don't want to delay messages after HomeKit initialization,\n// thus setting up msg.complete flag\n\n// check if HomeKit is initialized\nvar HomeKitInitialized = flow.get('HomeKitInitialized') || false;\nif (HomeKitInitialized === true) {\n    msg.complete = true;\n}\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "x": 1571.5,
        "y": 611,
        "wires": [
            [
                "44e322da.9da44c"
            ]
        ]
    },
    {
        "id": "a1037d30.9a7d3",
        "type": "function",
        "z": "7c7cc80f.76d888",
        "name": "update thermostat",
        "func": "if (msg.payload[\"ok\"] === true) {\n    // updating was successful, updating thermostat\n    msg.payload = msg.data;\n    \n    // if we've updated thermostat mode we need to tune HomeKit thermostat temperature as well\n    if (typeof msg.data.CurrentHeatingCoolingState !== 'undefined') {\n        \n        switch(msg.data.CurrentHeatingCoolingState) {\n            case 0:\n                msg.payload.CurrentTemperature = flow.get('thermostat_temperatures').idle;\n                break;\n            case 1:\n                msg.payload.CurrentTemperature = flow.get('thermostat_temperatures').comfort;\n                break;\n            case 2:\n                msg.payload.CurrentTemperature = flow.get('thermostat_temperatures').econom;\n                break;\n        }\n    \n    }\n    \n    msg.payload.TargetTemperature = msg.payload.CurrentTemperature;\n    \n        return msg;\n\n}\n\nreturn null;",
        "outputs": 1,
        "noerr": 0,
        "x": 1274.5,
        "y": 736,
        "wires": [
            [
                "f2912f77.e1acc8"
            ]
        ]
    },
    {
        "id": "f2912f77.e1acc8",
        "type": "homekit-service",
        "z": "7c7cc80f.76d888",
        "accessory": "d89c95f5.b65cf8",
        "name": "ZONT",
        "serviceName": "Thermostat",
        "x": 1346.5,
        "y": 612,
        "wires": [
            [
                "acfc65cd.2d7588"
            ]
        ]
    },
    {
        "id": "bffa9d3.e530be",
        "type": "comment",
        "z": "7c7cc80f.76d888",
        "name": "TODO",
        "info": "1. Fix device discovery\nNow only one ZONT H-1V is supported.\n\n2. Boiler Alert support\nNeed to support boiler error alerts\n",
        "x": 122.5,
        "y": 66,
        "wires": []
    },
    {
        "id": "d89c95f5.b65cf8",
        "type": "homekit-accessory",
        "z": "",
        "accessoryName": "Zont",
        "pinCode": "111-11-111",
        "port": "",
        "manufacturer": "[email protected]",
        "model": "20161216",
        "serialNo": "1"
    }
]
plavrenko

Flow Info

created 9 months, 2 weeks ago

Node Types

Core
  • comment (x1)
  • function (x9)
  • http request (x2)
  • inject (x1)
  • switch (x1)
Other

Tags

  • Zont
  • Microline
  • Thermostat
  • HomeKit
  • Apple
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option