15 Commits
Author SHA1 Message Date
Nikolay MiroshnichenkoandClaude Opus 5 481bab0e0c analyze: subdpt_suspect stops flagging flags, timestamps and legitimate DPTs of the same quantity
Found on a real 1312-GA house, confirmed across all seven corpus projects.

- A quantity word in the name of a 1-bit, date/time or text GA names what the flag,
  threshold trigger or timestamp is about ("on by motion detector by illuminance",
  "CO2 threshold 1", "meter value recorded, date"), not the value. 12 of 12 hits on
  1-bit GAs in the corpus were false positives; mains 1, 10, 11, 16, 19 are skipped.
- Legitimate alternatives are accepted, checked against xknx 3.20: power 9.024 kW and
  14.080 VA; energy 13.010 to 13.016 and 14.031 J, plus power DPTs under Russian
  "электроэнергия" names that are routinely used for power readings.
- Power factor gets its own rule (14.057) ahead of power, so "Фактор мощности" is no
  longer expected to be 14.056.

Real findings still fire: a bare DPT 9 named "lux threshold", a 5.x named "temperature
control", a 5.001 named "power", 9.002 named "temperature". New test file
test_real_house_fixes.py, wired into CI.

Corpus: subdpt_suspect 17 -> 2 on the house, 7 -> 2 and 1 -> 0 on two others; no other
metric moved. Baseline re-recorded in this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 18:30:13 +02:00
Nikolay MiroshnichenkoandClaude Opus 5 63551f40cf ci: ignore mcp >=2 by version range; the semver-major rule did not hold
Dependabot opened the same mcp <3 widening a third time (PR #17) with
update-types: semver-major in place. The docs do not say whether update-types
covers requirement widening; a versions range is the documented, direct form.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 09:05:52 +02:00
Nikolay MiroshnichenkoandClaude Opus 5 ec63bcf106 load_ga_export, entity naming, climate setpoint shift; fix suggest_repairs crash; CI runs every test
Three things found by auditing a TapPlan export, plus one regression found on the way.

load_ga_export(path): new tool. Reads an ETS ga-export/01 XML (ETS "Export Group
Addresses", or the import file a planning tool writes) into a project without
devices, through safe_fromstring, capped at 50 MB and 8 levels of range nesting.
Invalid or duplicate addresses and unknown DPT tokens go to import_warnings, never
silently. Round trip with our own generate_ets_xml is covered by a test.

Entity naming: lights, covers and climates are named after the common word prefix
of their member names, cutting only function words. The first version of the rule
turned "01. <room> - All Blinds - Move" into "01" on a real project, so anything not
in the function vocabulary now stays. On six real projects 214 of 1189 entities got
a shorter name, each rename reviewed, no address mapping changed. Regenerated
packages show different names; noted in the changelog.

Setpoint shift: 9.002 / 6.010 with a shift word maps to setpoint_shift_address,
setpoint_shift_state_address and setpoint_shift_mode (keys checked against the HA
KNX climate docs). It used to become a plain sensor.

Fixed: suggest_repairs raised UnboundLocalError on any project with a missing status
GA. My local rename in the 12.09 typing cleanup left two references to the old name.
test_council_fixes covers it but was not in CI, so it shipped. CI now runs all 21
test files instead of a hand-picked 10; the 11 added ones pass from a clean clone.

Verified: all 21 tests, ruff, mypy with the package installed, corpus guard no drift.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 00:11:45 +02:00
Nikolay Miroshnichenko e4f0f00c45 ci: restrict GITHUB_TOKEN to contents: read
First CodeQL run flagged the workflow for running on the default token
permissions. It only checks out and runs tests, so read is all it needs.
2026-09-12 21:42:03 +02:00
dependabot[bot]andNikolay 44a2296d3a ci: bump the actions group with 2 updates (#14)
Bumps the actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [actions/setup-python](https://github.com/actions/setup-python).


Updates `actions/checkout` from 4 to 7
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v7)

Updates `actions/setup-python` from 5 to 7
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
- dependency-name: actions/setup-python
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nikolay <nickol@me.com>
2026-09-12 21:41:44 +02:00
Nikolay Miroshnichenko 39be07ebd6 ci: keep Dependabot off mcp major bumps until the v2 port
mcp 2.x drops mcp.server.fastmcp; the import fails outright on 2.2.0, which is
what Dependabot's first PR would have allowed. Minor and patch updates still flow.
2026-09-12 21:40:53 +02:00
Nikolay Miroshnichenko 264767f91f ci: ruff + mypy gates, Python 3.13/3.14 in the matrix, Dependabot
The CI ran tests only. Added a lint job with two narrow gates:

ruff with select = E9,F — syntax errors and pyflakes (undefined names, unused
imports, broken f-strings). Style rules are deliberately off: the full default
set flagged 202 things, almost all cosmetic, and a gate nobody reads is not a
gate. Fixed the 9 findings it did have (8 unused imports, 1 empty f-string).

mypy, clean at 13 errors fixed. None of the 13 was a live bug, so every fix is
an annotation or a local rename: the two accumulators in advanced, the block map
in appprog_parser, a reused loop name in project and in repair, a TypedDict lost
through dict(), a mixed-type summary dict in handover. Two are worth naming.
_CH_TOKEN_RE now captures the letter prefix directly instead of re-matching its
own capture, which removes an unguarded .group() on a possibly-None match; the
regex accepts exactly the same strings. repair's status-DPT lookup now spells out
the None case, which is what dict.get already did since None is not a key there.

Verified the fixes moved nothing: the full suite passes, and the corpus guard
reports no drift across all six real projects, the 3646-GA one included.

Matrix extended to 3.13 and 3.14; both were smoke-tested locally first.
Dependabot config for pip and github-actions, weekly and grouped so a quiet week
is one PR. Dependabot security updates and CodeQL default setup enabled in the
repository settings.
2026-09-12 21:36:23 +02:00
Nikolay Miroshnichenko 4010743eb5 tools: stable ordering + cursor paging for list_group_addresses and get_devices
- sort by the address itself (GA main/middle/sub numerically, device area/line/device), so the
  result no longer depends on parser dict order and a retry returns the same page
- return {items, total_matched, returned, next_cursor} instead of a bare list truncated at limit:
  truncation was silent and a caller could not distinguish a full answer from a cut one
- tests/test_paging.py (in CI): numeric vs lexical order, full walk at page sizes 1..100 covers every
  row exactly once, same page after a reshuffled re-parse, filters reflected in total_matched
- verified on a real 3646-GA project; README EN/RU + CHANGELOG updated
- prompted by feedback on the r/mcp post (2026-09-12)
2026-09-12 21:15:55 +02:00
Nikolay Miroshnichenko aea59066ba suggest.py: structure-first entity suggestions prototype (HA SuggestionProvider #2 contract)
- channel -> object flags (write=sink, transmit=source, dual ignored) -> DPT pattern
  (cover, climate, light/switch, sensors) -> vendor object texts -> names as tie-break
- FB-covered channels skipped (also in fallback); shared status GAs excluded;
  duplicate writers deduped; pseudo-channels for channel-less devices; unwired flagged
- tests/test_suggest.py: FB-provider nameless fixture reproduced from structure (CI)
- tools/eval_suggest.py: agreement vs name/Function engine on real projects
- CHANGELOG (Unreleased/Added, experimental)
2026-09-10 12:18:49 +02:00
Nikolay Miroshnichenko a32b1970f3 check_policy: example profile seeded from the loaded project's own main groups (issue #13)
- example_policy_yaml(project): mains written with inferred domain, range name and
  category mix; no-majority mains commented-out with their mix; defaults only as a
  labelled comment; reserve.expect_range follows the project; empty/2-level -> {}.
- taxonomy_seed() is the single source for _infer_taxonomy and the example.
- server.check_policy(write_example_to) passes the loaded project, reports seeded_from.
- tests/test_policy.py: cases 4-6 (no leaked mains, round-trip, mixed main, quotes, no project); added to CI.
- README/README.ru/CHANGELOG.
2026-09-09 20:03:32 +02:00
Nikolay MiroshnichenkoandClaude Opus 4.8 f20b2e4126 fix(#12): recognize a '… RM' (Rückmeldung) shutter feedback as the cover's position status, not a light
Field data by Kris1166 (issue #12). A DPT-5.001 shutter position feedback named only '… RM'
was classified lighting/light and emitted as a bogus HA light entity. Two layers:
- classification (project.py): an ETS Function carrying a shutter role promotes a member to
  shutter — but only on POSITIVE evidence (member is a step/stop 1.007/1.010 OR a 5.001 position
  STATUS, the Function has an up/down move sibling, and the name has no explicit non-shutter
  domain), so a scene-recall/dimmer-feedback/lock sharing a mixed Function is left alone
  (LLM-council review; a Function is a free grouping, not proof of domain).
- status recognition (project.py name_is_status): a standalone 'RM'/'FB' token is now a status —
  the 'rm '/'rm_' substrings and the stopword tokenizer both missed a name ending in bare 'RM'.
- analyze._is_status_ga delegates to name_is_status; generate_ha then wires it as the cover's
  position_state_address.
Passed both gates (self-audit + LLM-council). tests/test_rm_status.py pins it (red/green + negatives:
a light's RM stays light, a stray light / a scene-recall in a shutter Function are not promoted).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-02 10:02:06 +02:00
Nikolay MiroshnichenkoandClaude Opus 4.8 dd8ecac090 fix(ha): pair a cover's step/stop within its ETS Function, never across it (issue #11)
generate_ha_package cross-wired move_short_address across unrelated ETS
Functions sharing a zone token (a window and an awning both 'Room A'), and
could leave a cover with no step/stop. Pair siblings by ETS Function
membership (authoritative); a sibling owned by a different function is never
borrowed. Falls back to the existing name-token logic only when the move GA
has no function, so projects without ETS Functions are unchanged.

Adds tests/test_cover_pairing.py (reproduces the field case from #11, fails
without the fix) and wires it into CI.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-27 11:01:54 +02:00
Nikolay Miroshnichenko ff93d44166 feat: positional status pairing + self-reporting commands, app-program parser v2 (ComObjectRef merge, version pick), authoritative main names, grader range-name evidence (v0.8.0)
Closes #3, closes #4, closes #5, closes #6
2026-07-07 12:24:24 +02:00
Nikolay Miroshnichenko e39c4e682a ci: run all three test suites (pipeline + device catalog + app-program parser) 2026-07-02 10:48:47 +02:00
Nikolay MiroshnichenkoandClaude Opus 4.8 bba8befcde nickol-knx-mcp v0.1.0 — design-time KNX/ETS6 MCP server (public beta)
Design-time MCP server that reads .knxproj (read-only), validates naming/DPT/status,
and generates Home Assistant KNX YAML + ETS-importable group addresses (XML/CSV).
No live bus access — confined-workspace writes only.

Includes: 12 MCP tools, end-to-end smoke test, MIT license, English-first README
(+ Russian), CONTRIBUTING with a real-project test call, SECURITY policy, CHANGELOG,
GitHub Actions CI (Python 3.10–3.12), and issue/PR templates.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 09:25:57 +02:00