Skip to content

Instantly share code, notes, and snippets.

@plavrenko
Created January 23, 2017 18:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save plavrenko/945bd7c4b6923bae70d2859c95daf545 to your computer and use it in GitHub Desktop.
Save plavrenko/945bd7c4b6923bae70d2859c95daf545 to your computer and use it in GitHub Desktop.
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\" : \"pavel@lavrenko.info\"\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\" : \"pavel@lavrenko.info\"\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": "pavel@lavrenko.info",
"model": "20161216",
"serialNo": "1"
}
]
@nils7vk
Copy link

nils7vk commented Jun 28, 2018

будут дальнейшие доработки?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment