nilan-proxy


Namenilan-proxy JSON
Version 1.0.2 PyPI version JSON
download
home_pagehttps://github.com/HairingX/nilan_proxy
SummaryA library to interface with Nilan Gateway and Genvex Connect enabled ventilation units.
upload_time2024-11-01 08:24:55
maintainerNone
docs_urlNone
authorHairingX
requires_python>=3.12
licenseGPLv3
keywords nilan nilan proxy cts 602 cts 400 genvex genvex connect nabto optima 270 optima 260 optima 251 optima 250 library home automation
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Nilan Proxy
Library to locally interface with HVAC systems running Nilan or Genvex Connect gateways.
Usually, those are only cloud accessible; however, after lots of work, we finally have a local solution ready.

The library is primarily built to be used by the Home Assistant custom component [Nilan Connect](https://github.com/HairingX/nilan_connect).

### Supported Controller Models
| Controller         | Gateway Required      | Supported | Tested |
|-------------------:|:---------------------:|:---------:|:------:|
| Optima 250         | Yes, internet gateway | ✅        |        |
| Optima 251         | Yes, internet gateway | ✅        |        |
| Optima 260         | Yes, internet gateway | ✅        |        |
| Optima 270         | Built-in              | ✅        |        |
| Optima 301         | Yes, internet gateway | ✅        |        |
| Optima 312         | Yes, internet gateway | ✅        |        |
| Optima 314         | Built-in              | ✅        |        |
| Nilan CTS400       | Yes, Nilan gateway    | ✅        | ✅     |
| Nilan CTS602       | Yes, Nilan gateway    | ✅        |        |
| Nilan CTS602 Light | Yes, Nilan gateway    | ✅        |        |
| Nilan CTS602 Geo   | Yes, Nilan gateway    | ✅        |        |

For any controllers that require a gateway, it is mandatory that the device supports Modbus. Optima controllers delivered before 2014 might not have Modbus.

## Obligatory Statement
I am not personally or in any way responsible for any damages should you choose to use the library. No warranty provided.

## Special Thanks
This library owes special thanks to superrob; without him, this would not exist.
For more info see his library here: https://github.com/superrob/genvexnabto

### How Superrob Got the Base Library Developed
See more in his repository.

> Genvex Connect and Nilan gateways both use the proprietary "Micro Nabto" protocol. Any mentions of it are very scarce online, with most official documentation being wiped from the internet after the companies released "Nabto Edge," which is highly incompatible with the older "Micro Nabto."

> The usual connection flow is for the user to use the respective dedicated app. The app loads a comically large 6MB+ binary client for Nabto communication. The binary is closed source and seems obfuscated to prevent easy "decompilation." 
This is also a huge issue as binaries are only available for Win32, Linux, Mac, Android, and iOS. No generic Linux ARM binaries are available, and thus using those to implement a Home Assistant custom component would exclude the most popular hosting devices, such as the Raspberry Pi.

> The binary scans for any gateways by sending out a broadcast UDP packet. This packet either has a specific device identifier in it or a star (*) to request any gateway to respond.
Any available gateways will respond to the receiver with their unique identifier on the same UDP IP and port the broadcast was sent.

> The normal flow is then handled online, with the library using a client certificate encrypted using the password supplied when first using the device. The flow, unfortunately, is where I got stuck for a long time. 
The client requests a server peer ID from the Nabto server, which, if properly authenticated, is sent back together with a NONCE and a server certificate. The client, using the NONCE, calculates an AES encryption key, which is later used to set up a direct local connection to the device.
Next, the client sends a connection request to the Nabto server. This request is encrypted using the server certificate public key and signed using the client certificate private key. The contents of this request are currently not known, as that would require extensive reverse engineering of the Nabto client binary, and thus where I got stuck.

> The server replies back, again encrypted. I am guessing the contents to be encrypted using the public RSA key from the client certificate. I have not looked into the contents of the reply, as I currently do not know the contents of the original request.
What is clear is that these packets set up the communication on the local device where the encryption context for the connection is set up.

> The client then connects locally to the device and is expected to provide all communication forwards in CRYPT payloads, encrypted using the AES keys which the server has set for the connection during the last two phases.
Luckily, the Nabto binary has a lot of logging built in and actually displays the AES and HMAC keys during the connection request! 
Using that key, I was able to decrypt and study the payloads, which matched the "Micro Nabto" device source code I had found a while ago on their GitHub.
Their device source code contains details on the protocol used and allowed me to decode and even build my own packets.

> However, without understanding the client connection request sent to the server, this is not useful.

> That was until I tried using an extremely old version of the Nabto client binary. To my surprise, it talked with the device using cleartext CRYPT payloads! I had seen in the device source code that if an "isLocal" flag is set for the connection, that any encryption can be specified to be used. However, I couldn't figure out how to set the flag.
However, this old client binary connected to a different port on the device. To my surprise, using that UDP port instead of the one used by the newer client library, it actually set the isLocal flag and allowed for unencrypted CRYPT payloads!

> From here, I only had to implement the "Micro Nabto" protocol in my own portable library.
I have only been able to test the Optima 270 implementation as I only have a Genvex ventilation unit with the Optima 270 controller (Has the gateway built-in). 
However, I have tried implementing the older controllers which should, in theory, be compatible. I have also added the Nilan CTS400 controller as the gateway for Nilan is built by the same company and uses the same command structure.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/HairingX/nilan_proxy",
    "name": "nilan-proxy",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.12",
    "maintainer_email": null,
    "keywords": "Nilan, Nilan Proxy, CTS 602, CTS 400, Genvex, Genvex Connect, Nabto, Optima 270, Optima 260, Optima 251, Optima 250, library, Home Automation",
    "author": "HairingX",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/80/82/7b6ba98bd293cb09f1f2657190e3b886711fce39065c27d7ffc8d20a6a40/nilan_proxy-1.0.2.tar.gz",
    "platform": null,
    "description": "# Nilan Proxy\nLibrary to locally interface with HVAC systems running Nilan or Genvex Connect gateways.\nUsually, those are only cloud accessible; however, after lots of work, we finally have a local solution ready.\n\nThe library is primarily built to be used by the Home Assistant custom component [Nilan Connect](https://github.com/HairingX/nilan_connect).\n\n### Supported Controller Models\n| Controller         | Gateway Required      | Supported | Tested |\n|-------------------:|:---------------------:|:---------:|:------:|\n| Optima 250         | Yes, internet gateway | \u2705        |        |\n| Optima 251         | Yes, internet gateway | \u2705        |        |\n| Optima 260         | Yes, internet gateway | \u2705        |        |\n| Optima 270         | Built-in              | \u2705        |        |\n| Optima 301         | Yes, internet gateway | \u2705        |        |\n| Optima 312         | Yes, internet gateway | \u2705        |        |\n| Optima 314         | Built-in              | \u2705        |        |\n| Nilan CTS400       | Yes, Nilan gateway    | \u2705        | \u2705     |\n| Nilan CTS602       | Yes, Nilan gateway    | \u2705        |        |\n| Nilan CTS602 Light | Yes, Nilan gateway    | \u2705        |        |\n| Nilan CTS602 Geo   | Yes, Nilan gateway    | \u2705        |        |\n\nFor any controllers that require a gateway, it is mandatory that the device supports Modbus. Optima controllers delivered before 2014 might not have Modbus.\n\n## Obligatory Statement\nI am not personally or in any way responsible for any damages should you choose to use the library. No warranty provided.\n\n## Special Thanks\nThis library owes special thanks to superrob; without him, this would not exist.\nFor more info see his library here: https://github.com/superrob/genvexnabto\n\n### How Superrob Got the Base Library Developed\nSee more in his repository.\n\n> Genvex Connect and Nilan gateways both use the proprietary \"Micro Nabto\" protocol. Any mentions of it are very scarce online, with most official documentation being wiped from the internet after the companies released \"Nabto Edge,\" which is highly incompatible with the older \"Micro Nabto.\"\n\n> The usual connection flow is for the user to use the respective dedicated app. The app loads a comically large 6MB+ binary client for Nabto communication. The binary is closed source and seems obfuscated to prevent easy \"decompilation.\" \nThis is also a huge issue as binaries are only available for Win32, Linux, Mac, Android, and iOS. No generic Linux ARM binaries are available, and thus using those to implement a Home Assistant custom component would exclude the most popular hosting devices, such as the Raspberry Pi.\n\n> The binary scans for any gateways by sending out a broadcast UDP packet. This packet either has a specific device identifier in it or a star (*) to request any gateway to respond.\nAny available gateways will respond to the receiver with their unique identifier on the same UDP IP and port the broadcast was sent.\n\n> The normal flow is then handled online, with the library using a client certificate encrypted using the password supplied when first using the device. The flow, unfortunately, is where I got stuck for a long time. \nThe client requests a server peer ID from the Nabto server, which, if properly authenticated, is sent back together with a NONCE and a server certificate. The client, using the NONCE, calculates an AES encryption key, which is later used to set up a direct local connection to the device.\nNext, the client sends a connection request to the Nabto server. This request is encrypted using the server certificate public key and signed using the client certificate private key. The contents of this request are currently not known, as that would require extensive reverse engineering of the Nabto client binary, and thus where I got stuck.\n\n> The server replies back, again encrypted. I am guessing the contents to be encrypted using the public RSA key from the client certificate. I have not looked into the contents of the reply, as I currently do not know the contents of the original request.\nWhat is clear is that these packets set up the communication on the local device where the encryption context for the connection is set up.\n\n> The client then connects locally to the device and is expected to provide all communication forwards in CRYPT payloads, encrypted using the AES keys which the server has set for the connection during the last two phases.\nLuckily, the Nabto binary has a lot of logging built in and actually displays the AES and HMAC keys during the connection request! \nUsing that key, I was able to decrypt and study the payloads, which matched the \"Micro Nabto\" device source code I had found a while ago on their GitHub.\nTheir device source code contains details on the protocol used and allowed me to decode and even build my own packets.\n\n> However, without understanding the client connection request sent to the server, this is not useful.\n\n> That was until I tried using an extremely old version of the Nabto client binary. To my surprise, it talked with the device using cleartext CRYPT payloads! I had seen in the device source code that if an \"isLocal\" flag is set for the connection, that any encryption can be specified to be used. However, I couldn't figure out how to set the flag.\nHowever, this old client binary connected to a different port on the device. To my surprise, using that UDP port instead of the one used by the newer client library, it actually set the isLocal flag and allowed for unencrypted CRYPT payloads!\n\n> From here, I only had to implement the \"Micro Nabto\" protocol in my own portable library.\nI have only been able to test the Optima 270 implementation as I only have a Genvex ventilation unit with the Optima 270 controller (Has the gateway built-in). \nHowever, I have tried implementing the older controllers which should, in theory, be compatible. I have also added the Nilan CTS400 controller as the gateway for Nilan is built by the same company and uses the same command structure.\n",
    "bugtrack_url": null,
    "license": "GPLv3",
    "summary": "A library to interface with Nilan Gateway and Genvex Connect enabled ventilation units.",
    "version": "1.0.2",
    "project_urls": {
        "Homepage": "https://github.com/HairingX/nilan_proxy"
    },
    "split_keywords": [
        "nilan",
        " nilan proxy",
        " cts 602",
        " cts 400",
        " genvex",
        " genvex connect",
        " nabto",
        " optima 270",
        " optima 260",
        " optima 251",
        " optima 250",
        " library",
        " home automation"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "18f09b8476dc731ddd311476690df8429a81bf258a5fff1cb0f020ec17bb9988",
                "md5": "3696ac68d1774cca6ab20caf069b47da",
                "sha256": "49a0c082728b33329815c89fa989bc5f2f5af8188e52af3a704c00ca69136918"
            },
            "downloads": -1,
            "filename": "nilan_proxy-1.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "3696ac68d1774cca6ab20caf069b47da",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.12",
            "size": 42115,
            "upload_time": "2024-11-01T08:24:54",
            "upload_time_iso_8601": "2024-11-01T08:24:54.230159Z",
            "url": "https://files.pythonhosted.org/packages/18/f0/9b8476dc731ddd311476690df8429a81bf258a5fff1cb0f020ec17bb9988/nilan_proxy-1.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "80827b6ba98bd293cb09f1f2657190e3b886711fce39065c27d7ffc8d20a6a40",
                "md5": "bb612ac4a7a77aeae5a219c68d66559e",
                "sha256": "466e0d29c656c594305084d0ea30c915fb6f60246d87a8e674abfe7e22f9fd36"
            },
            "downloads": -1,
            "filename": "nilan_proxy-1.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "bb612ac4a7a77aeae5a219c68d66559e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.12",
            "size": 35208,
            "upload_time": "2024-11-01T08:24:55",
            "upload_time_iso_8601": "2024-11-01T08:24:55.968663Z",
            "url": "https://files.pythonhosted.org/packages/80/82/7b6ba98bd293cb09f1f2657190e3b886711fce39065c27d7ffc8d20a6a40/nilan_proxy-1.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-11-01 08:24:55",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "HairingX",
    "github_project": "nilan_proxy",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "nilan-proxy"
}
        
Elapsed time: 0.64401s