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

3.1 #45

Merged
merged 3 commits into from
Aug 5, 2024
Merged

3.1 #45

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
11 changes: 9 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.6.0
hooks:
- id: check-ast
- id: check-builtin-literals
Expand All @@ -14,9 +14,16 @@ repos:


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

- id: ruff

- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.10.0
Expand Down
8 changes: 5 additions & 3 deletions .ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ src = ["src", "test"]

[lint]

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

select = [
"E", "W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
"I", # https://docs.astral.sh/ruff/rules/#isort-i
Expand Down Expand Up @@ -56,6 +53,11 @@ ignore = [
quote-style = "single"


# https://docs.astral.sh/ruff/settings/#lintflake8-quotes
[lint.flake8-quotes]
inline-quotes = "single"
multiline-quotes = "single"


[lint.flake8-builtins]
builtins-ignorelist = ["id", "input"]
Expand Down
6 changes: 3 additions & 3 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Packages required to build the documentation
sphinx == 7.2.6
sphinx-autodoc-typehints == 2.0.0
sphinx == 7.4.7
sphinx-autodoc-typehints == 2.2.3
sphinx_rtd_theme == 2.0.0
sphinx-exec-code == 0.12
autodoc_pydantic == 2.1.0
autodoc_pydantic == 2.2.0
4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ To read from the serial port an IR to USB reader for energy meter is required.

# Changelog

#### 3.1 (2024-08-05)
- Updated dependencies
- Added some small log messages

#### 3.0 (2024-04-24)

**BREAKING CHANGE**
Expand Down
8 changes: 4 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
-r requirements_setup.txt

# Testing
pytest == 8.1.1
pre-commit == 3.7.0
pytest-asyncio == 0.23.6
pytest == 8.3.2
pre-commit == 3.8.0
pytest-asyncio == 0.23.8
aioresponses == 0.7.6

# Linter
ruff == 0.4.2
ruff == 0.5.6
6 changes: 3 additions & 3 deletions requirements_setup.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
aiomqtt == 2.0.1
aiomqtt == 2.2.0
pyserial-asyncio == 0.6
easyconfig == 0.3.2
pydantic == 2.7.0
pydantic == 2.8.2
smllib == 1.4
aiohttp == 3.9.5
aiohttp == 3.10.1
34 changes: 17 additions & 17 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# Load version number without importing HABApp
def load_version() -> str:
version: dict[str, str] = {}
with open("src/sml2mqtt/__version__.py") as fp:
with open('src/sml2mqtt/__version__.py') as fp:
exec(fp.read(), version)
assert version['__version__'], version
return version['__version__']
Expand All @@ -29,16 +29,16 @@ def load_req() -> list[str]:
readme = Path(__file__).with_name('readme.md')
long_description = ''
if readme.is_file():
with readme.open("r", encoding='utf-8') as fh:
with readme.open('r', encoding='utf-8') as fh:
long_description = fh.read()

setuptools.setup(
name="sml2mqtt",
name='sml2mqtt',
version=__version__,
author="spaceman_spiff",
author='spaceman_spiff',
# author_email="",
description="A sml (Smart Message Language) energy meter to MQTT bridge. "
"Can read from serial ports or http (e.g. Tibber Pulse).",
description='A sml (Smart Message Language) energy meter to MQTT bridge. '
'Can read from serial ports or http (e.g. Tibber Pulse).',
keywords=[
'mqtt',
'sml',
Expand All @@ -47,8 +47,8 @@ def load_req() -> list[str]:
'tibber'
],
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/spacemanspiff2007/sml2mqtt",
long_description_content_type='text/markdown',
url='https://github.com/spacemanspiff2007/sml2mqtt',
project_urls={
'GitHub': 'https://github.com/spacemanspiff2007/sml2mqtt',
},
Expand All @@ -57,15 +57,15 @@ def load_req() -> list[str]:
python_requires='>=3.10',
install_requires=load_req(),
classifiers=[
"Development Status :: 4 - Beta",
"Framework :: AsyncIO",
"License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Home Automation"
'Development Status :: 4 - Beta',
'Framework :: AsyncIO',
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3 :: Only',
'Topic :: Home Automation'
],
entry_points={
'console_scripts': [
Expand Down
2 changes: 1 addition & 1 deletion src/sml2mqtt/__log__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def setup_log():
for device in sml2mqtt.CONFIG.inputs:
# Name of the longest logger, should be the device status
chars = max(len(get_logger(device.get_device_name()).getChild('status').name), chars)
log_format = logging.Formatter("[{asctime:s}] [{name:" + str(chars) + "s}] {levelname:8s} | {message:s}", style='{')
log_format = logging.Formatter('[{asctime:s}] [{name:' + str(chars) + 's}] {levelname:8s} | {message:s}', style='{')

# File Handler
file_path = sml2mqtt.CONFIG.logging.file
Expand Down
11 changes: 8 additions & 3 deletions src/sml2mqtt/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,14 @@ async def a_main():
device.frame_handler = device.analyze_frame

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

except Exception:
except Exception as e:
log.error(f'{e.__class__.__name__} during startup: {e}')
for line in traceback.format_exc().splitlines():
log.error(line)

await do_shutdown_async()

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

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


if __name__ == "__main__":
if __name__ == '__main__':
ret = main()
log.info(f'Closed with return code {ret}')
sys.exit(ret)
2 changes: 1 addition & 1 deletion src/sml2mqtt/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '3.0'
__version__ = '3.1'
2 changes: 1 addition & 1 deletion src/sml2mqtt/config/inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ def _val_bytesize(cls, v):

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


class HttpSourceSettings(SmlSourceSettingsBase):
Expand Down
4 changes: 2 additions & 2 deletions src/sml2mqtt/config/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ def generate_day_names() -> dict[str, int]:
day_names.update({date(2001, 1, i).strftime('%A')[:3]: i for i in range(1, 8)})

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


Expand Down
2 changes: 1 addition & 1 deletion src/sml2mqtt/const/sml_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def create(cls, timestamp: float, values: Iterable[SmlListEntry]):

def __init__(self, timestamp: float):
self.timestamp: Final = timestamp
self.values: dict[str, SmlListEntry] = {}
self.values: Final[dict[str, SmlListEntry]] = {}

def __getattr__(self, item: str) -> SmlListEntry:
return self.values[item]
Expand Down
2 changes: 1 addition & 1 deletion src/sml2mqtt/const/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def __init__(self, coro: Callable[[], Awaitable], *, name: str):

@property
def is_running(self) -> bool:
if (task := self._task) is None or task.cancelled():
if (task := self._task) is None or task.cancelled(): # noqa: SIM103
return False
return True

Expand Down
3 changes: 3 additions & 0 deletions src/sml2mqtt/sml_device/sml_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,8 @@ def check_status(self):

return None

def __len__(self) -> int:
return len(self._devices)


ALL_DEVICES: Final = SmlDevices()
2 changes: 1 addition & 1 deletion src/sml2mqtt/sml_value/sml_value.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from sml2mqtt.const import SmlFrameValues
from sml2mqtt.mqtt import MqttObj
from sml2mqtt.sml_value.base import OperationContainerBase, SmlValueInfo, ValueOperationBase
from sml2mqtt.sml_value.base import OperationContainerBase, SmlValueInfo


class SmlValue(OperationContainerBase):
Expand Down
4 changes: 2 additions & 2 deletions tests/sml_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def sml_frame_2_analyze(sml_frame_2):

@pytest.fixture()
def sml_data_1_analyze():
return """
return '''
Received Frame
-> b'760501188e6162006200726500000101760101070000000000000b00000000000000000000010163687700760501188e626200620072650000070177010b000000000000000000000172620165002ec3f47a77078181c78203ff010101010445425a0177070100000009ff010101010b000000000000000000000177070100010800ff6401018001621e52fb690000000a7ac1bc170177070100010801ff0101621e52fb690000000a74b1ea770177070100010802ff0101621e52fb6900000000060fd1a00177070100020800ff6401018001621e52fb69000000000d19e1c00177070100100700ff0101621b52fe55000089d90177070100240700ff0101621b52fe55000020220177070100380700ff0101621b52fe5500000a9201770701004c0700ff0101621b52fe5500005f2501010163810200760501188e636200620072650000020171016325fc00'

Expand Down Expand Up @@ -206,4 +206,4 @@ def sml_data_1_analyze():
message_body <SmlCloseResponse>
global_signature: None
crc16 : 9724
"""
'''
6 changes: 3 additions & 3 deletions tests/sml_device/frames/test_frame_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ async def test_frame_no_match_obis_id(no_mqtt, caplog, monkeypatch, sml_frame_1,

device.on_source_data(None)

msg = "\n".join(x.msg for x in caplog.records)
msg = '\n'.join(x.msg for x in caplog.records)

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

device.on_source_data(None)

msg = "\n".join(x.msg for x in caplog.records)
msg = '\n'.join(x.msg for x in caplog.records)

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

device.on_source_data(None)

msg = "\n".join(x.msg for x in caplog.records)
msg = '\n'.join(x.msg for x in caplog.records)

print(msg)

Expand Down
2 changes: 1 addition & 1 deletion tests/sml_device/frames/test_frame_2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async def test_frame_no_match_obis_id(no_mqtt, caplog, monkeypatch, sml_frame_2,

device.on_source_data(None)

msg = "\n".join(x.msg for x in caplog.records)
msg = '\n'.join(x.msg for x in caplog.records)

assert msg.removeprefix(sml_frame_2_analyze) == '''
get_obis failed - try parsing frame
Expand Down
8 changes: 4 additions & 4 deletions tests/sml_device/test_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ async def test_device_analyze(no_mqtt, caplog, sml_data_1, arg_analyze, sml_data
device.on_source_data(sml_data_1[i: i + chunk_size])

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

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

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

assert msg.removeprefix(sml_data_1_analyze) == '''
Found obis id 0100000009ff in the sml frame
Expand Down
6 changes: 3 additions & 3 deletions tests/sml_device/test_setup_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ def test_warnings(no_mqtt, caplog, sml_frame_1_values):
setup_device(device, sml_frame_1_values, device_cfg, general_cfg)

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

assert '\n' + msg + '\n' == """
assert '\n' + msg + '\n' == '''
Config for 0100000009ff found but 0100000009ff is also marked to be skipped
Config for 0100000009ff found but 0100000009ff was not reported by the frame
"""
'''
2 changes: 1 addition & 1 deletion tests/sml_values/test_setup_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def test_field_to_init(config_model: type[BaseModel], operation: callable):
annotations = inspect.get_annotations(typed_dict)

for name, fwd_ref in annotations.items():
ref_type = fwd_ref._evaluate(vars(operations_module), {}, frozenset())
ref_type = fwd_ref._evaluate(vars(operations_module), {}, recursive_guard=frozenset())
assert name not in config_provides, config_provides
config_provides[name] = ref_type
else:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_mqtt_obj.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,6 @@ def test_check_for_duplicate_messages(caplog):

check_for_duplicate_topics(parent)

msg = "\n".join(x.msg for x in caplog.records)
msg = '\n'.join(x.msg for x in caplog.records)

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