#
### project ##########################################################################
# last checked/updated: 2026-01-11
#

[project]
  name = "ramses_rf"
  dynamic = ["version"]
  description = "A stateful RAMSES-II protocol decoder & analyser."
  readme = "README.md"
  authors = [
    {name = "David Bonnes", email = "zxdavb@bonnes.me"},
    {name = "Egbert Broerse", email = "dcc2@ebroerse.nl"}
  ]
  maintainers = [
    {name = "Egbert Broerse", email = "dcc2@ebroerse.nl"}
  ]
  license = "MIT"
  license-files = ["LICENSE*"]

  keywords = [
    "evohome",
    "honeywell",
    "resideo",
    "round thermostat",
    "ramses",
    "airios",
    "chronotherm",
    "climarad",
    "hometronics",
    "itho",
    "nuaire",
    "orcon",
    "sundial",
    "vasco",
  ]
  classifiers = [
    "Topic :: Home Automation",
    "Programming Language :: Python :: 3.13",
  ]
  requires-python = ">=3.12"        # HA core >=3.13.2
  dependencies = [
    "aiofiles>=23.2.1",             # latest
    "colorlog>=6.9.0",              # latest
    "paho-mqtt>=2.1.0",             # latest
    "pyserial-asyncio-fast>=0.16",  # latest
    "voluptuous>=0.15.2",           # latest
  ]

#
[project.urls]
  "Homepage" = "https://github.com/ramses-rf/ramses_rf"
  "Bug Tracker" = "https://github.com/ramses-rf/ramses_rf/issues"
  "Wiki" = "https://github.com/ramses-rf/ramses_rf/wiki"

#
[project.scripts]
  client = "ramses_cli.client:main"

#
### build-system #####################################################################

[build-system]
  requires = ["hatchling"]
  build-backend = "hatchling.build"

[tool.hatch.build]
  sources = ["src"]
  artifacts = ["misc/ser2net.yaml",]

[tool.hatch.build.targets.wheel]
  packages = ["src/ramses_rf", "src/ramses_tx", "src/ramses_cli"]

[tool.hatch.version]
  path = "src/ramses_rf/version.py"


#
### pytest ###########################################################################

[tool.pytest.ini_options]
  asyncio_default_fixture_loop_scope = "function"
  asyncio_mode = "auto"

  testpaths = [
    "tests/"
  ]
  norecursedirs = "deprecated/*"

  # pytest-cov slows down our tests to 10 mins, so leave off.
  # We run coverage in a separate workflow on python 3.13 only (fastest)
  # pytest-cov coverage reporting. open htmlcov/index.html in a browser for details
  # addopts = ["--cov=src", "--cov-fail-under=50", "--cov-report=xml", "--cov-report=term"]
  # to create an interactive local HTML report, add: --cov-report=html
  # to report missed lines during test writing, add: --cov-report=term-missing
  # PyCharm might not hit a breakpoint. For debugging, disable -cov by adding:
  # –no-cov"
  # cov_report_term = "term-missing:skip-covered"

#
### coverage #########################################################################

[tool.coverage.run]
  core = "sysmon"  # essential setting for speed on GitHub < 11 secs instead of >12 mins
  branch = false  # true takes a lot longer and can't be combined with sysmon
#  patch = ["subprocess"]
  source = [
    "src",
    "tests"
  ]

[tool.coverage.report]
  # Regexes for lines to exclude from consideration
  exclude_also = [
    # Don't complain if tests don't hit defensive assertion code:
    "raise AssertionError",
    "raise NotImplementedError",
  ]
  format = "text"
  fail_under = 80  # total cov now 83%
  ignore_errors = true

[tool.coverage.html]
  directory = "coverage_html"

#
### mypy #############################################################################

[tool.mypy]
  files = [
    "client.py",
    "src",
    "tests"
  ]
  exclude = [
    "src/ramses_cli/utils",
    "tests/deprecated",
    "tests/wip",
  ]

  # ### Import discovery...
  # - https://mypy.readthedocs.io/en/stable/command_line.html#import-discovery

  # ignore_missing_imports = true  # only used per-module
  # follow_imports = normal  # avoid skip/silent, prefer normal (default) over error

  # ### Strict typing (--strict) if all following are set to true (excl. ramses-rf)
  #  - https://mypy.readthedocs.io/en/stable/existing_code.html#introduce-stricter-options

  # # Config file & Configuring warning
  #  - https://mypy.readthedocs.io/en/stable/command_line.html#config-file

  # Start off with these
  warn_unused_configs = true
  warn_redundant_casts = true  # from: Configuring warning
  warn_unused_ignores = true  # from: Configuring warning

  # # Miscellaneous strictness flags
  #  - https://mypy.readthedocs.io/en/stable/command_line.html#miscellaneous-strictness-flags

  # Getting these passing should be easy
  extra_checks = true  # now incl.: strict_concatenate = true
  strict_equality = true

  # Strongly recommend enabling this one as soon as you can
  check_untyped_defs = true

  # allow_redefinition = false  # default is false
  no_implicit_reexport = true  # added by zxdavb

  # # Disallow dynamic typing...
  #  - https://mypy.readthedocs.io/en/stable/command_line.html#disallow-dynamic-typing

  # These shouldn't be too much additional work, but may be tricky to
  # get passing if you use a lot of untyped libraries
  disallow_subclassing_any = true
  disallow_untyped_decorators = true  # from: Untyped definitions and calls
  disallow_any_generics = true

  # disallow_any_explicit = true  # TODO: added by zxdavb

  # # Untyped definitions and calls
  #  - https://mypy.readthedocs.io/en/stable/command_line.html#untyped-definitions-and-calls

  # These next few are various gradations of forcing use of type annotations
  disallow_untyped_calls = true
  disallow_incomplete_defs = true
  disallow_untyped_defs = true

  # disallow-untyped-decorators = true  # added by zxdavb

  # This one isn't too hard to get passing, but return on investment is lower
  # no_implicit_reexport = true  # 242  # from: Miscellaneous strictness flags

  # # Configuring warnings
  #  - https://mypy.readthedocs.io/en/stable/command_line.html#configuring-warnings

  # This one can be tricky to get passing if you use a lot of untyped libraries
  warn_return_any = true

  disallow_any_unimported = true  # from: Disallow dynamic typing
  warn_no_return = true
  warn_unreachable = true

  # # None and Optional handling
  #  - https://mypy.readthedocs.io/en/stable/command_line.html#none-and-optional-handling

  no_implicit_optional = true


#

[[tool.mypy.overrides]]
  module = "debugpy.*"
  ignore_missing_imports = true

[[tool.mypy.overrides]]
  module = "colorama.*"
  ignore_missing_imports = true

[[tool.mypy.overrides]]
  module = "ramses_tx.parsers"

  disallow_any_generics = false  # 89 - will be lots of work

[[tool.mypy.overrides]]
  module = "ramses_rf.entity_base.*"

  disallow_any_generics = false     # 14
  no_implicit_optional = false      # 12

  disable_error_code = [
    "arg-type",  #     13 (but a can of worms)
    "unreachable",  #  13 (but a can of worms)
  ]

[[tool.mypy.overrides]]
  module = "ramses_rf.device.*"

  disable_error_code = [
    "assignment",  #   18 - doable
    "return-value",  # 64
    "type-arg",  #     10 - doable
    "unreachable",  #  14
  ]

[[tool.mypy.overrides]]
  module = "ramses_rf.system.zones"

  disable_error_code = [
    "assignment",  #    6
    "unreachable",  #  10
  ]

[[tool.mypy.overrides]]
  module = "tests.*"

  disable_error_code = [
    "arg-type",  #     30
    "type-arg",  #     22
  ]

[[tool.mypy.overrides]]
  module = "tests_rf.*"

  disable_error_code = [
    "arg-type",  #     54
    "type-arg",  #     11
  ]

[[tool.mypy.overrides]]
  module = [
    "ramses_cli.*",
    "ramses_rf.*",
    "ramses_tx.parsers",
    "tests",
  ]

  disallow_any_explicit = false


#
## ruff ##############################################################################

[tool.ruff]
  exclude = ["tests/deprecated/*.py"]
  src = ["src", "tests"]
  target-version = "py312"


[tool.ruff.lint]
  select = [
    "ASYNC",  # flake8-async
    "B",  #     flake8-bugbear
    "E",  #     pycodestyle
    "F",  #     Pyflakes
    "I",  #     isort
    "SIM",  #   flake8-simplify
    "UP",  #    pyupgrade
  ]
  ignore = ["ASYNC109", "ASYNC110", "B011", "E501", "SIM102", "SIM114", "UP040"]

  # B011   - Do not call assert False since python -O removes these calls
  # E501   - Line too long
  # SIM102 - Use a single `if` statement instead of nested `if` statements
  # SIM114 - Combine `if` branches using logical `or` operator
  # UP040  - Type alias uses `TypeAlias` annotation instead of the `type` keyword


[tool.ruff.lint.flake8-import-conventions.extend-aliases]
  voluptuous = "vol"


[tool.ruff.lint.isort]
  combine-as-imports = true
  force-sort-within-sections = false
  split-on-trailing-comma = false


[tool.ruff.lint.per-file-ignores]
  "tests/*" = ["ASYNC"]
