Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

23.11.0 #427

Merged
merged 11 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/run-tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on: [push, pull_request]

jobs:
pre-commit:
name:
name: pre-commit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down
7 changes: 0 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@ repos:
# - pep8-naming==0.13.3


- repo: https://github.com/asottile/pyupgrade
rev: v3.10.1
hooks:
- id: pyupgrade
args: ["--py38-plus"]


- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.10.0
hooks:
Expand Down
69 changes: 69 additions & 0 deletions .ruff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

line-length = 120
indent-width = 4

target-version = "py38"
src = ["src", "test"]

# https://docs.astral.sh/ruff/settings/#ignore-init-module-imports
ignore-init-module-imports = true

extend-exclude = ["__init__.py"]

select = [
"E", "W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
"I", # https://docs.astral.sh/ruff/rules/#isort-i
"UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up

"A", # https://docs.astral.sh/ruff/rules/#flake8-builtins-a
"ASYNC", # https://docs.astral.sh/ruff/rules/#flake8-async-async
"C4", # https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4
"EM", # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em
"FIX", # https://docs.astral.sh/ruff/rules/#flake8-fixme-fix
"INP", # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp
"ISC", # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc
"PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie
"PT", # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt
"PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth
"RET", # https://docs.astral.sh/ruff/rules/#flake8-return-ret
"SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim
"SLOT", # https://docs.astral.sh/ruff/rules/#flake8-slots-slot
"T10", # https://docs.astral.sh/ruff/rules/#flake8-debugger-t10
"TCH", # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tch
"TD", # https://docs.astral.sh/ruff/rules/#flake8-todos-td

"TRY", # https://docs.astral.sh/ruff/rules/#tryceratops-try
"FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly
"PERF", # https://docs.astral.sh/ruff/rules/#perflint-perf
"RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf

# "PL", # https://docs.astral.sh/ruff/rules/#pylint-pl
# "FURB", # https://docs.astral.sh/ruff/rules/#refurb-furb
]

ignore = [
"RET501", # https://docs.astral.sh/ruff/rules/unnecessary-return-none/#unnecessary-return-none-ret501
"TRY400", # https://docs.astral.sh/ruff/rules/error-instead-of-exception/

"A003", # https://docs.astral.sh/ruff/rules/builtin-attribute-shadowing/
]


[format]
# Use single quotes for non-triple-quoted strings.
quote-style = "single"



[lint.flake8-builtins]
builtins-ignorelist = ["id", "input"]


[lint.per-file-ignores]
"docs/conf.py" = ["INP001", "A001"]
"setup.py" = ["PTH123"]


[lint.isort]
# https://docs.astral.sh/ruff/settings/#isort-lines-after-imports
lines-after-imports = 2
17 changes: 8 additions & 9 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
import os
import re
import sys
from pathlib import Path

import sphinx
from docutils.nodes import Text, Node
from docutils.nodes import Node, Text
from sphinx.addnodes import desc_signature

IS_RTD_BUILD = os.environ.get('READTHEDOCS', '-').lower() == 'true'
IS_CI = os.environ.get('CI', '-') == 'true'


# https://www.sphinx-doc.org/en/master/extdev/logging.html
sphinx_logger = sphinx.util.logging.getLogger('post')
logger_lvl = logging.DEBUG if IS_RTD_BUILD or IS_CI else logging.INFO # set level to DEBUG for CI
Expand All @@ -35,7 +35,11 @@ def log(msg: str):
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
sys.path.insert(0, os.path.join(os.path.abspath('..'), 'src'))
src_folder = Path(__file__).parent.with_name('src')
assert src_folder.is_dir()

# required for autodoc
sys.path.insert(0, str(src_folder))

# -- Project information -----------------------------------------------------

Expand All @@ -49,6 +53,7 @@ def log(msg: str):
release = 'beta'
try:
from HABApp import __version__

version = __version__
print(f'Building docs for {version}')
except Exception as e:
Expand Down Expand Up @@ -229,15 +234,13 @@ def log(msg: str):
(re.compile(r'py:class'), re.compile(r'(?:datetime|pendulum|aiohttp|pathlib)\..+'))
]


# -- Extension configuration -------------------------------------------------
exec_code_working_dir = '../src'
exec_code_source_folders = ['../src', '../tests']

autodoc_member_order = 'bysource'
autoclass_content = 'class'


# No config on member
autodoc_pydantic_model_show_config_member = False
autodoc_pydantic_model_show_config_summary = False
Expand All @@ -255,9 +258,6 @@ def log(msg: str):
autodoc_pydantic_field_list_validators = False
autodoc_pydantic_field_swap_name_and_alias = True




# ----------------------------------------------------------------------------------------------------------------------
# Post processing of default value

Expand Down Expand Up @@ -339,7 +339,6 @@ def setup(app):
'python': ('https://docs.python.org/3', None)
}


# Don't show warnings for missing python references since these are created via intersphinx during the RTD build
if not IS_RTD_BUILD:
nitpick_ignore_regex.append(
Expand Down
2 changes: 1 addition & 1 deletion docs/interface_habapp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ And since it is just like a normal item triggering on changes etc. is possible,
my_agg.aggregation_period(2 * 3600)

# Use max as an aggregation function
my_agg.aggregation_func = max
my_agg.aggregation_func(max)


The value of ``my_agg`` in the example will now always be the maximum of ``MyInputItem`` in the last two hours.
Expand Down
5 changes: 5 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ MyOpenhabRule()
```

# Changelog
#### 23.11.1 (2023-11-23)
- Fix for very small float values (#425)
- Fix for writing to persistence (#424)
- Updated dependencies

#### 23.09.2 (2023-09-24)
- Made channel type on a ``Thing`` optional (#416)
- Fixed an issue with mqtt publish and reconnect
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
# -----------------------------------------------------------------------------
# Packages for source formatting
# -----------------------------------------------------------------------------
pre-commit >= 3.3, < 3.4
pre-commit >= 3.5, < 3.6
ruff >= 0.1.6, < 0.2

# -----------------------------------------------------------------------------
# Packages for other developement tasks
Expand Down
16 changes: 8 additions & 8 deletions requirements_setup.txt
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
aiohttp == 3.8.5
pydantic == 2.3.0
msgspec == 0.18.2
aiohttp == 3.9.0
pydantic == 2.5.2
msgspec == 0.18.4
pendulum == 2.1.2
bidict == 0.22.1
watchdog == 3.0.0
ujson == 5.8.0
aiomqtt == 1.2.0
aiomqtt == 1.2.1

immutables == 0.20
eascheduler == 0.1.11
easyconfig == 0.3.0
stack_data == 0.6.2
easyconfig == 0.3.1
stack_data == 0.6.3
colorama == 0.4.6

voluptuous == 0.13.1
voluptuous == 0.14.1

typing-extensions == 4.7.1
typing-extensions == 4.8.0

aiohttp-sse-client == 0.2.1

Expand Down
4 changes: 2 additions & 2 deletions requirements_tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
# -----------------------------------------------------------------------------
# Packages to run source tests
# -----------------------------------------------------------------------------
packaging == 23.1
pytest == 7.4.2
packaging == 23.2
pytest == 7.4.3
pytest-asyncio == 0.21.1
15 changes: 14 additions & 1 deletion run/conf_testing/rules/openhab/test_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from HABApp.core.events import ValueUpdateEventFilter
from HABApp.core.types import HSB, RGB
from HABApp.openhab.interface_async import async_get_items
from HABApp.openhab.items import GroupItem, StringItem, ColorItem
from HABApp.openhab.items import GroupItem, StringItem, ColorItem, NumberItem
from HABAppTests import OpenhabTmpItem, TestBaseRule, ItemWaiter, EventWaiter


Expand All @@ -21,6 +21,7 @@ def __init__(self):
self.add_test('TestExisting', self.test_existing)
self.add_test('TestColor', self.test_color)
self.add_test('TestGroupFunction', self.test_group_func)
self.add_test('TestSmallValues', self.test_small_float_values)

self.item_number = OpenhabTmpItem('Number')
self.item_switch = OpenhabTmpItem('Switch')
Expand Down Expand Up @@ -66,6 +67,18 @@ def test_api(self):
self.openhab.get_item(self.item_group.name)
asyncio.run_coroutine_threadsafe(async_get_items(), loop).result()

@OpenhabTmpItem.create('Number', arg_name='tmp_item')
def test_small_float_values(self, tmp_item: OpenhabTmpItem):
# https://github.com/spacemanspiff2007/HABApp/issues/425
item = NumberItem.get_item(tmp_item.name)
assert item.value is None

for i in range(3, 16, 3):
with ItemWaiter(item) as waiter:
value = 1 / 10 ** i
item.oh_post_update(value)
waiter.wait_for_state(value)

@OpenhabTmpItem.use('String', arg_name='oh_item')
def test_tags(self, oh_item: OpenhabTmpItem):
oh_item.create_item(tags=['tag1', 'tag2'])
Expand Down
75 changes: 47 additions & 28 deletions run/conf_testing/rules/openhab/test_persistence.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,66 @@
from __future__ import annotations

from datetime import datetime, timedelta
from typing import Final, Any

from HABApp.openhab.definitions.helpers import OpenhabPersistenceData
from HABApp.openhab.items import NumberItem
from HABAppTests import TestBaseRule


class TestPersistence(TestBaseRule):

def __init__(self):
class TestPersistenceBase(TestBaseRule):
def __init__(self, service_name: str, item_name: str):
super().__init__()
self.item = 'RRD4J_Item'

self.add_test('RRD4J configured', self.test_configured)
self.add_test('RRD4J get', self.test_get)
self.config.skip_on_failure = True
self.item_name: Final = item_name
self.service_name: Final = service_name

self.add_test(f'Persistence {service_name} available', self.test_service_available)

def set_up(self):
i = NumberItem.get_item(self.item)
i.oh_post_update(i.value + 1 if i.value < 10 else 0)
i = NumberItem.get_item(self.item_name)
i.oh_post_update(int(i.value) + 1 if i.value < 10 else 0)

def test_configured(self):
def test_service_available(self):
for cfg in self.oh.get_persistence_services():
if cfg.id == 'rrd4j':
if cfg.id == self.service_name:
break
else:
raise ValueError('rrd4j not found!')
raise ValueError(f'Persistence service "{self.service_name}" not found!')

def set_persistence_data(self, time: datetime, state: Any):
return self.openhab.set_persistence_data(self.item_name, self.service_name, time, state)

def get_persistence_data(self, start_time: datetime | None, end_time: datetime | None) -> OpenhabPersistenceData:
return self.openhab.get_persistence_data(self.item_name, self.service_name, start_time, end_time)


class TestRRD4j(TestPersistenceBase):

def __init__(self):
super().__init__('rrd4j', 'RRD4J_Item')
self.add_test('RRD4J get', self.test_get)

def test_get(self):
now = datetime.now()
d = self.openhab.get_persistence_data(self.item, 'rrd4j', now - timedelta(seconds=60), now)
d = self.get_persistence_data(now - timedelta(seconds=60), now)
assert d.get_data()

# def test_set(self):
# now = datetime.now()
# d = self.openhab.get_persistence_data(self.item, 'mapdb', now - timedelta(seconds=5), now)
# was = d.get_data()
#
# assert list(was.values()) == [1]
#
# self.openhab.set_persistence_data(self.item, 'mapdb', now, 2)
#
# d = self.openhab.get_persistence_data(self.item, 'mapdb', now - timedelta(seconds=5),
# now + timedelta(seconds=5))
# ist = d.get_data()
# assert list(ist.values()) == [2], ist


TestPersistence()

TestRRD4j()


class TestMapDB(TestPersistenceBase):

def __init__(self):
super().__init__('mapdb', 'RRD4J_Item')
self.add_test('MapDB get', self.test_get)

def test_get(self):
now = datetime.now()
d = self.get_persistence_data(now - timedelta(seconds=60), now)
assert d.get_data()


TestMapDB()
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def load_req() -> typing.List[str]:
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Home Automation"
],
Expand Down
Loading