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"}]

Flow Info

Created 7 years, 10 months ago
Updated 6 years, 3 months ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • comment (x1)
  • function (x9)
  • http request (x2)
  • inject (x1)
  • join (x2)
  • 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