Skip to content

Commit e6eb5e9

Browse files
3.1 (#45)
1 parent 55daf2f commit e6eb5e9

24 files changed

+83
-62
lines changed

.pre-commit-config.yaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.4.0
3+
rev: v4.6.0
44
hooks:
55
- id: check-ast
66
- id: check-builtin-literals
@@ -14,9 +14,16 @@ repos:
1414

1515

1616
- repo: https://github.com/astral-sh/ruff-pre-commit
17-
rev: v0.3.4
17+
rev: v0.5.5
1818
hooks:
1919
- id: ruff
20+
# I001 [*] Import block is un-sorted or un-formatted
21+
# UP035 [*] Import from {target} instead: {names}
22+
# Q000 [*] Double quote found but single quotes preferred
23+
# Q001 [*] Double quote multiline found but single quotes preferred
24+
args: [ "--select", "I001,UP035,Q000,Q001", "--fix"]
25+
26+
- id: ruff
2027

2128
- repo: https://github.com/pre-commit/pygrep-hooks
2229
rev: v1.10.0

.ruff.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ src = ["src", "test"]
77

88
[lint]
99

10-
# https://docs.astral.sh/ruff/settings/#ignore-init-module-imports
11-
ignore-init-module-imports = true
12-
1310
select = [
1411
"E", "W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
1512
"I", # https://docs.astral.sh/ruff/rules/#isort-i
@@ -56,6 +53,11 @@ ignore = [
5653
quote-style = "single"
5754

5855

56+
# https://docs.astral.sh/ruff/settings/#lintflake8-quotes
57+
[lint.flake8-quotes]
58+
inline-quotes = "single"
59+
multiline-quotes = "single"
60+
5961

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

docs/requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Packages required to build the documentation
2-
sphinx == 7.2.6
3-
sphinx-autodoc-typehints == 2.0.0
2+
sphinx == 7.4.7
3+
sphinx-autodoc-typehints == 2.2.3
44
sphinx_rtd_theme == 2.0.0
55
sphinx-exec-code == 0.12
6-
autodoc_pydantic == 2.1.0
6+
autodoc_pydantic == 2.2.0

readme.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ To read from the serial port an IR to USB reader for energy meter is required.
2121

2222
# Changelog
2323

24+
#### 3.1 (2024-08-05)
25+
- Updated dependencies
26+
- Added some small log messages
27+
2428
#### 3.0 (2024-04-24)
2529

2630
**BREAKING CHANGE**

requirements.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
-r requirements_setup.txt
22

33
# Testing
4-
pytest == 8.1.1
5-
pre-commit == 3.7.0
6-
pytest-asyncio == 0.23.6
4+
pytest == 8.3.2
5+
pre-commit == 3.8.0
6+
pytest-asyncio == 0.23.8
77
aioresponses == 0.7.6
88

99
# Linter
10-
ruff == 0.4.2
10+
ruff == 0.5.6

requirements_setup.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
aiomqtt == 2.0.1
1+
aiomqtt == 2.2.0
22
pyserial-asyncio == 0.6
33
easyconfig == 0.3.2
4-
pydantic == 2.7.0
4+
pydantic == 2.8.2
55
smllib == 1.4
6-
aiohttp == 3.9.5
6+
aiohttp == 3.10.1

setup.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# Load version number without importing HABApp
99
def load_version() -> str:
1010
version: dict[str, str] = {}
11-
with open("src/sml2mqtt/__version__.py") as fp:
11+
with open('src/sml2mqtt/__version__.py') as fp:
1212
exec(fp.read(), version)
1313
assert version['__version__'], version
1414
return version['__version__']
@@ -29,16 +29,16 @@ def load_req() -> list[str]:
2929
readme = Path(__file__).with_name('readme.md')
3030
long_description = ''
3131
if readme.is_file():
32-
with readme.open("r", encoding='utf-8') as fh:
32+
with readme.open('r', encoding='utf-8') as fh:
3333
long_description = fh.read()
3434

3535
setuptools.setup(
36-
name="sml2mqtt",
36+
name='sml2mqtt',
3737
version=__version__,
38-
author="spaceman_spiff",
38+
author='spaceman_spiff',
3939
# author_email="",
40-
description="A sml (Smart Message Language) energy meter to MQTT bridge. "
41-
"Can read from serial ports or http (e.g. Tibber Pulse).",
40+
description='A sml (Smart Message Language) energy meter to MQTT bridge. '
41+
'Can read from serial ports or http (e.g. Tibber Pulse).',
4242
keywords=[
4343
'mqtt',
4444
'sml',
@@ -47,8 +47,8 @@ def load_req() -> list[str]:
4747
'tibber'
4848
],
4949
long_description=long_description,
50-
long_description_content_type="text/markdown",
51-
url="https://github.com/spacemanspiff2007/sml2mqtt",
50+
long_description_content_type='text/markdown',
51+
url='https://github.com/spacemanspiff2007/sml2mqtt',
5252
project_urls={
5353
'GitHub': 'https://github.com/spacemanspiff2007/sml2mqtt',
5454
},
@@ -57,15 +57,15 @@ def load_req() -> list[str]:
5757
python_requires='>=3.10',
5858
install_requires=load_req(),
5959
classifiers=[
60-
"Development Status :: 4 - Beta",
61-
"Framework :: AsyncIO",
62-
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
63-
"Operating System :: OS Independent",
64-
"Programming Language :: Python :: 3.10",
65-
"Programming Language :: Python :: 3.11",
66-
"Programming Language :: Python :: 3.12",
67-
"Programming Language :: Python :: 3 :: Only",
68-
"Topic :: Home Automation"
60+
'Development Status :: 4 - Beta',
61+
'Framework :: AsyncIO',
62+
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
63+
'Operating System :: OS Independent',
64+
'Programming Language :: Python :: 3.10',
65+
'Programming Language :: Python :: 3.11',
66+
'Programming Language :: Python :: 3.12',
67+
'Programming Language :: Python :: 3 :: Only',
68+
'Topic :: Home Automation'
6969
],
7070
entry_points={
7171
'console_scripts': [

src/sml2mqtt/__log__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def setup_log():
3737
for device in sml2mqtt.CONFIG.inputs:
3838
# Name of the longest logger, should be the device status
3939
chars = max(len(get_logger(device.get_device_name()).getChild('status').name), chars)
40-
log_format = logging.Formatter("[{asctime:s}] [{name:" + str(chars) + "s}] {levelname:8s} | {message:s}", style='{')
40+
log_format = logging.Formatter('[{asctime:s}] [{name:' + str(chars) + 's}] {levelname:8s} | {message:s}', style='{')
4141

4242
# File Handler
4343
file_path = sml2mqtt.CONFIG.logging.file

src/sml2mqtt/__main__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,14 @@ async def a_main():
3636
device.frame_handler = device.analyze_frame
3737

3838
# Start all devices
39+
log.debug(f'Starting {len(ALL_DEVICES):d} device{"" if len(ALL_DEVICES) == 1 else "s":s}')
3940
await ALL_DEVICES.start()
4041

41-
except Exception:
42+
except Exception as e:
43+
log.error(f'{e.__class__.__name__} during startup: {e}')
44+
for line in traceback.format_exc().splitlines():
45+
log.error(line)
46+
4247
await do_shutdown_async()
4348

4449
# Keep tasks running
@@ -48,7 +53,7 @@ async def a_main():
4853
def main() -> int | str:
4954
# This is needed to make async-mqtt work
5055
# see https://github.com/sbtinstruments/asyncio-mqtt
51-
if sys.platform.lower() == "win32" or os.name.lower() == "nt":
56+
if sys.platform.lower() == 'win32' or os.name.lower() == 'nt':
5257
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
5358

5459
# Load config
@@ -74,7 +79,7 @@ def main() -> int | str:
7479
return 0
7580

7681

77-
if __name__ == "__main__":
82+
if __name__ == '__main__':
7883
ret = main()
7984
log.info(f'Closed with return code {ret}')
8085
sys.exit(ret)

src/sml2mqtt/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '3.0'
1+
__version__ = '3.1'

src/sml2mqtt/config/inputs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def _val_bytesize(cls, v):
7070

7171
@override
7272
def get_device_name(self) -> str:
73-
return self.url.split("/")[-1]
73+
return self.url.split('/')[-1]
7474

7575

7676
class HttpSourceSettings(SmlSourceSettingsBase):

src/sml2mqtt/config/operations.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ def generate_day_names() -> dict[str, int]:
148148
day_names.update({date(2001, 1, i).strftime('%A')[:3]: i for i in range(1, 8)})
149149

150150
# abbreviations in German and English
151-
day_names.update({"Mo": 1, "Di": 2, "Mi": 3, "Do": 4, "Fr": 5, "Sa": 6, "So": 7})
152-
day_names.update({"Mon": 1, "Tue": 2, "Wed": 3, "Thu": 4, "Fri": 5, "Sat": 6, "Sun": 7})
151+
day_names.update({'Mo': 1, 'Di': 2, 'Mi': 3, 'Do': 4, 'Fr': 5, 'Sa': 6, 'So': 7})
152+
day_names.update({'Mon': 1, 'Tue': 2, 'Wed': 3, 'Thu': 4, 'Fri': 5, 'Sat': 6, 'Sun': 7})
153153
return {k.lower(): v for k, v in day_names.items()}
154154

155155

src/sml2mqtt/const/sml_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def create(cls, timestamp: float, values: Iterable[SmlListEntry]):
5959

6060
def __init__(self, timestamp: float):
6161
self.timestamp: Final = timestamp
62-
self.values: dict[str, SmlListEntry] = {}
62+
self.values: Final[dict[str, SmlListEntry]] = {}
6363

6464
def __getattr__(self, item: str) -> SmlListEntry:
6565
return self.values[item]

src/sml2mqtt/const/task.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def __init__(self, coro: Callable[[], Awaitable], *, name: str):
5757

5858
@property
5959
def is_running(self) -> bool:
60-
if (task := self._task) is None or task.cancelled():
60+
if (task := self._task) is None or task.cancelled(): # noqa: SIM103
6161
return False
6262
return True
6363

src/sml2mqtt/sml_device/sml_devices.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,8 @@ def check_status(self):
4040

4141
return None
4242

43+
def __len__(self) -> int:
44+
return len(self._devices)
45+
4346

4447
ALL_DEVICES: Final = SmlDevices()

src/sml2mqtt/sml_value/sml_value.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from sml2mqtt.const import SmlFrameValues
66
from sml2mqtt.mqtt import MqttObj
7-
from sml2mqtt.sml_value.base import OperationContainerBase, SmlValueInfo, ValueOperationBase
7+
from sml2mqtt.sml_value.base import OperationContainerBase, SmlValueInfo
88

99

1010
class SmlValue(OperationContainerBase):

tests/sml_data.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def sml_frame_2_analyze(sml_frame_2):
8080

8181
@pytest.fixture()
8282
def sml_data_1_analyze():
83-
return """
83+
return '''
8484
Received Frame
8585
-> b'760501188e6162006200726500000101760101070000000000000b00000000000000000000010163687700760501188e626200620072650000070177010b000000000000000000000172620165002ec3f47a77078181c78203ff010101010445425a0177070100000009ff010101010b000000000000000000000177070100010800ff6401018001621e52fb690000000a7ac1bc170177070100010801ff0101621e52fb690000000a74b1ea770177070100010802ff0101621e52fb6900000000060fd1a00177070100020800ff6401018001621e52fb69000000000d19e1c00177070100100700ff0101621b52fe55000089d90177070100240700ff0101621b52fe55000020220177070100380700ff0101621b52fe5500000a9201770701004c0700ff0101621b52fe5500005f2501010163810200760501188e636200620072650000020171016325fc00'
8686
@@ -206,4 +206,4 @@ def sml_data_1_analyze():
206206
message_body <SmlCloseResponse>
207207
global_signature: None
208208
crc16 : 9724
209-
"""
209+
'''

tests/sml_device/frames/test_frame_1.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ async def test_frame_no_match_obis_id(no_mqtt, caplog, monkeypatch, sml_frame_1,
1616

1717
device.on_source_data(None)
1818

19-
msg = "\n".join(x.msg for x in caplog.records)
19+
msg = '\n'.join(x.msg for x in caplog.records)
2020

2121
assert msg.removeprefix(sml_frame_1_analyze) == '''
2222
Found none of the following obis ids in the sml frame: 0100000009ff, 01006001ffff
@@ -36,7 +36,7 @@ async def test_frame_no_config(no_mqtt, caplog, monkeypatch, sml_frame_1, arg_an
3636

3737
device.on_source_data(None)
3838

39-
msg = "\n".join(x.msg for x in caplog.records)
39+
msg = '\n'.join(x.msg for x in caplog.records)
4040

4141
assert msg.removeprefix(sml_frame_1_analyze) == '''
4242
Found obis id 0100600100ff in the sml frame
@@ -109,7 +109,7 @@ async def test_frame_with_config(no_mqtt, caplog, monkeypatch, sml_frame_1, arg_
109109

110110
device.on_source_data(None)
111111

112-
msg = "\n".join(x.msg for x in caplog.records)
112+
msg = '\n'.join(x.msg for x in caplog.records)
113113

114114
print(msg)
115115

tests/sml_device/frames/test_frame_2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async def test_frame_no_match_obis_id(no_mqtt, caplog, monkeypatch, sml_frame_2,
1515

1616
device.on_source_data(None)
1717

18-
msg = "\n".join(x.msg for x in caplog.records)
18+
msg = '\n'.join(x.msg for x in caplog.records)
1919

2020
assert msg.removeprefix(sml_frame_2_analyze) == '''
2121
get_obis failed - try parsing frame

tests/sml_device/test_device.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ async def test_device_analyze(no_mqtt, caplog, sml_data_1, arg_analyze, sml_data
1616
device.on_source_data(sml_data_1[i: i + chunk_size])
1717

1818
# This is what will be reported
19-
msg = "\n".join(x.msg for x in filter(lambda x: x.name == 'sml.mqtt.pub', caplog.records))
19+
msg = '\n'.join(x.msg for x in filter(lambda x: x.name == 'sml.mqtt.pub', caplog.records))
2020

21-
assert '\n' + msg == """
21+
assert '\n' + msg == '''
2222
00000000000000000000/0100000009ff: 00000000000000000000 (QOS: 0, retain: False)
2323
00000000000000000000/0100010800ff: 450.09189911 (QOS: 0, retain: False)
2424
00000000000000000000/0100010801ff: 449.07489911 (QOS: 0, retain: False)
@@ -29,9 +29,9 @@ async def test_device_analyze(no_mqtt, caplog, sml_data_1, arg_analyze, sml_data
2929
00000000000000000000/0100380700ff: 27.06 (QOS: 0, retain: False)
3030
00000000000000000000/01004c0700ff: 243.57 (QOS: 0, retain: False)
3131
00000000000000000000/status: OK (QOS: 0, retain: False)
32-
00000000000000000000/status: SHUTDOWN (QOS: 0, retain: False)"""
32+
00000000000000000000/status: SHUTDOWN (QOS: 0, retain: False)'''
3333

34-
msg = "\n".join(x.msg for x in filter(lambda x: x.name == 'sml.device_name', caplog.records))
34+
msg = '\n'.join(x.msg for x in filter(lambda x: x.name == 'sml.device_name', caplog.records))
3535

3636
assert msg.removeprefix(sml_data_1_analyze) == '''
3737
Found obis id 0100000009ff in the sml frame

tests/sml_device/test_setup_device.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ def test_warnings(no_mqtt, caplog, sml_frame_1_values):
2626
setup_device(device, sml_frame_1_values, device_cfg, general_cfg)
2727

2828
# This is what will be reported
29-
msg = "\n".join(
29+
msg = '\n'.join(
3030
x.msg for x in filter(lambda x: x.name == 'sml.test_device' and x.levelno == logging.WARNING, caplog.records)
3131
)
3232

33-
assert '\n' + msg + '\n' == """
33+
assert '\n' + msg + '\n' == '''
3434
Config for 0100000009ff found but 0100000009ff is also marked to be skipped
3535
Config for 0100000009ff found but 0100000009ff was not reported by the frame
36-
"""
36+
'''

tests/sml_values/test_setup_operations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def test_field_to_init(config_model: type[BaseModel], operation: callable):
5656
annotations = inspect.get_annotations(typed_dict)
5757

5858
for name, fwd_ref in annotations.items():
59-
ref_type = fwd_ref._evaluate(vars(operations_module), {}, frozenset())
59+
ref_type = fwd_ref._evaluate(vars(operations_module), {}, recursive_guard=frozenset())
6060
assert name not in config_provides, config_provides
6161
config_provides[name] = ref_type
6262
else:

tests/test_mqtt_obj.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,6 @@ def test_check_for_duplicate_messages(caplog):
6060

6161
check_for_duplicate_topics(parent)
6262

63-
msg = "\n".join(x.msg for x in caplog.records)
63+
msg = '\n'.join(x.msg for x in caplog.records)
6464

6565
assert msg == 'Topic "base/child" is already configured!'

0 commit comments

Comments
 (0)