{"id":49984201,"url":"https://github.com/dataheld/knxray","last_synced_at":"2026-05-18T19:48:11.679Z","repository":{"id":357337067,"uuid":"1235698015","full_name":"dataheld/knxray","owner":"dataheld","description":"Creates diffable, plain-text JSON from *parts* of your `*.knxproj` using the xknxproject parser","archived":false,"fork":false,"pushed_at":"2026-05-12T14:23:36.000Z","size":199,"stargazers_count":0,"open_issues_count":14,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-12T14:29:58.833Z","etag":null,"topics":["building-automation","diff","ets","git","home-automation","knx","scm"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dataheld.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-11T15:10:56.000Z","updated_at":"2026-05-12T13:52:28.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dataheld/knxray","commit_stats":null,"previous_names":["dataheld/knxplain","dataheld/knxray"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/dataheld/knxray","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dataheld%2Fknxray","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dataheld%2Fknxray/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dataheld%2Fknxray/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dataheld%2Fknxray/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dataheld","download_url":"https://codeload.github.com/dataheld/knxray/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dataheld%2Fknxray/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33189278,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-18T09:27:30.708Z","status":"ssl_error","status_checked_at":"2026-05-18T09:27:28.300Z","response_time":71,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["building-automation","diff","ets","git","home-automation","knx","scm"],"created_at":"2026-05-18T19:48:10.766Z","updated_at":"2026-05-18T19:48:11.669Z","avatar_url":"https://github.com/dataheld.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# knxray \u003cimg src=\"logo.svg\" align=\"right\" height=\"140\" alt=\"knxray logo\"/\u003e\n\n[KNX](https://www.knx.org) is a building-automation standard used to wire up lighting, heating, blinds, and similar systems.\nInstallations are configured in [ETS](https://www.knx.org/knx-en/for-professionals/software/ets-6/), a proprietary Windows application that stores its project state in `*.knxproj` files — zipped XML archives.\n\nWithout knxray, committing a `*.knxproj` to git means `git diff` tells you nothing:\n\n```console\n$ git diff HEAD -- my-installation.knxproj\ndiff --git a/my-installation.knxproj b/my-installation.knxproj\nBinary files a/my-installation.knxproj and b/my-installation.knxproj differ\n```\n\nWith knxray installed and configured, the same command shows:\n\n\u003c!-- BEGIN git-diff-example --\u003e\n```diff\ndiff --git a/my-installation.knxproj b/my-installation.knxproj\nindex 255ab14..c04674e 100644\n--- a/my-installation.knxproj\n+++ b/my-installation.knxproj\n@@ -153,36 +153,6 @@\n       \"number\": 1,\n       \"object_size\": \"1 Bit\",\n       \"text\": \"S1.1: Schalten (steigende Flanke)\"\n-    },\n-    \"1.1.3/O-3_R-36\": {\n-      \"channel\": \"CH-2\",\n-      \"description\": \"\",\n-      \"device_address\": \"1.1.3\",\n-      \"device_application\": \"M-0007_A-6125-96-F910\",\n-      \"dpas\": null,\n-      \"dpts\": [\n-        {\n-          \"main\": 1,\n-          \"sub\": 1\n-        }\n-      ],\n-      \"flags\": {\n-        \"communication\": true,\n-        \"read\": false,\n-        \"read_on_init\": false,\n-        \"transmit\": true,\n-        \"update\": true,\n-        \"write\": true\n-      },\n-      \"function_text\": \"Input/Output\",\n-      \"group_address_links\": [\n-        \"0/0/2\"\n-      ],\n-      \"module_def\": null,\n-      \"name\": \"Obj_Wert_steigendeFlanke\",\n-      \"number\": 3,\n-      \"object_size\": \"1 Bit\",\n-      \"text\": \"S1.2: Schalten (steigende Flanke)\"\n     }\n   },\n   \"devices\": {\n@@ -311,8 +281,7 @@\n         }\n       },\n       \"communication_object_ids\": [\n-        \"1.1.3/O-1_R-17\",\n-        \"1.1.3/O-3_R-36\"\n+        \"1.1.3/O-1_R-17\"\n       ],\n       \"description\": \"Countertop\",\n       \"hardware_name\": \"6125/01 ctrl. el., solo\\u00ae stand., 1gang, fl. mtd.\",\n@@ -349,8 +318,7 @@\n       \"comment\": \"\",\n       \"communication_object_ids\": [\n         \"1.1.1/O-0_R-16\",\n-        \"1.1.2/O-3_R-36\",\n-        \"1.1.3/O-3_R-36\"\n+        \"1.1.2/O-3_R-36\"\n       ],\n       \"data_secure\": false,\n       \"description\": \"\",\n```\n\u003c!-- END git-diff-example --\u003e\n\nknxray makes those files *somewhat* more transparent:\nit uses [xknxproject](https://github.com/XKNX/xknxproject) to convert the parts of a `*.knxproj` that can be extracted (group addresses, devices, communication objects) into sorted, stable JSON, which git can then diff normally.\n\n\u003e [!IMPORTANT]\n\u003e `xknxproject` only parses a *subset* of your `*.knxproj`, including group addresses, devices, and communication objects.\n\u003e Device parameters (for example, a dim curve) live in opaque per-device XML and are **not** shown.\n\u003e A clean diff here does **not** mean the `.knxproj` files are identical.\n\u003e To highlight these \"false negatives\", `knxray diff` emits a warning when the `*.knxproj` files differ but the parsed JSON does not.\n\n## How diffing works\n\nA `.knxproj` file is a zip archive containing XML.\nComparing two of them is not as simple as a byte-for-byte check — ETS rewrites internal metadata files (`.validation`, `.certificate`) on every save, even when the project hasn't changed.\nknxray therefore applies a cascade of checks, stopping as soon as the two files are considered equivalent at that level:\n\n```mermaid\nflowchart TD\n    A([\"knxray diff A B\"]) --\u003e byte\n\n    byte{\"1 · byte-identical?\"}\n    byte --\u003e|yes| done_byte([\"no output\"])\n    byte --\u003e|no| xml\n\n    xml{\"2 · XML-identical?\\nexcl. non-deterministic\\nETS metadata\"}\n    xml --\u003e|yes| done_xml([\"no output\"])\n    xml --\u003e|no| xmlsem\n\n    xmlsem{\"3 · XML semantically\\nidentical?\\n— planned —\"}\n    xmlsem --\u003e|yes| done_xmlsem([\"no output\"])\n    xmlsem --\u003e|no| json\n\n    json{\"4 · parsed JSON\\nidentical?\\nxknxproject\"}\n    json --\u003e|yes| warn([\"⚠ warn to stderr:\\nXML differs but\\nparser is blind to it\"])\n    json --\u003e|no| out([\"print JSON diff\\nto stdout\"])\n```\n\n| Level | What it catches | What it misses |\n| --- | --- | --- |\n| 1 · byte | anything | — |\n| 2 · XML-byte | real project changes | XML whitespace / attribute-order noise |\n| 3 · XML semantic *(planned)* | all XML-level changes | — |\n| 4 · JSON (xknxproject) | group addresses, devices, comm. objects | device parameters, some ETS settings |\n\n## Commands\n\n### `show`\n\n```sh\nknxray show \u003cfile.knxproj\u003e\n```\n\nParse `\u003cfile.knxproj\u003e` and emit sorted, stable JSON to stdout.\nPrimary git `textconv` driver — wire it up once with `knxray setup`.\n\n### `diff`\n\n```sh\nknxray diff \u003cfile1.knxproj\u003e \u003cfile2.knxproj\u003e\n```\n\nRun the diff cascade on two `.knxproj` files.\nWrites a unified JSON diff to stdout; writes a warning to stderr when the files differ in ways the JSON parser cannot see.\n\n### `setup`\n\n```sh\nknxray setup [--global]\n```\n\nConfigure the `textconv` driver for `*.knxproj` files.\nWithout `--global`, writes to `.git/config` and appends `*.knxproj diff=knxray` to `.gitattributes` in the current repo.\nWith `--global`, writes to `~/.gitconfig` only.\n\n## Quick start\n\n**One-off inspection** (no install needed):\n\n```bash\nnix run github:dataheld/knxray -- show my-installation.knxproj\n```\n\n**Permanent git integration:**\n\n```bash\n# 1. Install\nnix profile install github:dataheld/knxray\n\n# 2. Configure git (once per repo, or --global for all repos)\nknxray setup\n```\n\nAfter `knxray setup`, `git diff`, `git show`, and `git log -p` show human-readable JSON diffs on committed `*.knxproj` files automatically.\nUnder the hood this uses git's [`textconv` driver](https://git-scm.com/docs/gitattributes#_performing_text_diffs_of_binary_files).\n\n\u003e [!NOTE]\n\u003e GitHub and GitLab web UIs do not use `textconv`, so `git diff` integration only works locally.\n\n## Related work\n\n- **[AutoBackup](https://it-gmbh.de/en/knx/ets-apps/autobackup/) by IT GmbH** (paid, ETS5/6) — automatically exports a `.knxproj` file to a configurable folder whenever you close a project.\n  Pairs naturally with knxray: AutoBackup handles the export step, knxray handles the diff.\n  ETS 6.4+ also ships a built-in [Auto Backup to Archive](https://support.knx.org/hc/en-us/articles/31061986226450-ETS-v6-4-0) feature at no extra cost, though it stores versions inside ETS's own archive rather than writing a standalone file to a configurable path.\n- **[Project Tracing](https://my.knx.org/shop/product?product_type_category=etsapps\u0026product_type=project-tracing)** by KNX Association (paid, ETS5/6) — logs every user action inside a project (downloads, parameter changes) with timestamps and user identity.\n  Think audit trail, not diff view.\n- **[Project Comparison](https://it-gmbh.de/en/knx/ets-apps/project-comparison/) by IT GmbH** (paid, ETS5 only) — compares two project snapshots and lists all differences, with direct navigation to changed elements.\n  The closest ETS-native equivalent to knxray's diff output, but not available for ETS6.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdataheld%2Fknxray","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdataheld%2Fknxray","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdataheld%2Fknxray/lists"}