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

Smartmeter: neues Plugin für SML- und DLMS-Smartmeter #982

Merged
merged 34 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ab0ae98
smartmeter [wip]: initial commit,
Morg42 Dec 1, 2024
0bc68d7
smartmeter: first iteration of DLMS part finished, no webif or SML yet
Morg42 Dec 2, 2024
c42bc35
smartmeter: disable DLMS testing mode
Morg42 Dec 2, 2024
a32f51c
smartmeter: fix type hinting for Python 3.9, minor fixes, add require…
Morg42 Dec 2, 2024
99a8600
smartmeter: add DLMS autodiscovery (untested)
Morg42 Dec 2, 2024
20b4f1b
smartmeter: implement sml protocol, fix testing for both protocols
Morg42 Dec 3, 2024
d5d85e2
smartmeter: fix requirements
Morg42 Dec 3, 2024
e095c3c
smartmeter: fix typing for Python 3.9
Morg42 Dec 4, 2024
efb036c
smartmeter: fix and refactor code, improve error handling
Morg42 Dec 5, 2024
92d1358
smartmeter: further aligned dlms and sml value/property return
Morg42 Dec 6, 2024
873c609
smartmeter: refactor code for asyncio implementation
Morg42 Dec 7, 2024
70b67b9
smartmeter: streamline SML/DLMS returned data, remove old code, cleanup
Morg42 Dec 7, 2024
f98132f
smartmeter: remove special handling no longer necessary
Morg42 Dec 8, 2024
a4d18b5
smartmeter: cleanup item handling
Morg42 Dec 9, 2024
6c581a2
smartmeter: refactor for webif api access
Morg42 Dec 9, 2024
11a39c0
smartmeter: initial web interface
Morg42 Dec 12, 2024
355303f
smartmeter: add asyncio feature for SML
Morg42 Dec 13, 2024
9789b19
smartmeter: update parameters, item updates only every x seconds
Morg42 Dec 13, 2024
918cd93
smartmeter: fix discovery, improve locking
Morg42 Dec 14, 2024
9b90c04
smartmeter: update webif
Morg42 Dec 15, 2024
b900aac
smartmeter: add DLMS asyncio, fix webif (note: DLMS asyncio needs tes…
Morg42 Dec 16, 2024
5d18ad0
smartmeter: create items file
Morg42 Dec 16, 2024
a619790
smartmeter: fix standalone operations
Morg42 Dec 16, 2024
1bb135c
smartmeter: fix standalone...again
Morg42 Dec 16, 2024
f6905c1
smartmeter: error handling for item creation
Morg42 Dec 16, 2024
40517bd
smartmeter: don't create empty items file
Morg42 Dec 17, 2024
ad2d812
smartmeter: improve item generation
Morg42 Dec 17, 2024
4b114ee
smartmeter: update webif
Morg42 Dec 22, 2024
5fcca3a
smartmeter: adjust metadata
Morg42 Dec 22, 2024
9470e97
smartmeter: updated webif
Morg42 Dec 23, 2024
de05c22
smartmeter: fix hex string decoding
Morg42 Dec 23, 2024
1a51ab7
smartmeter: fix hex string conversion again
Morg42 Dec 23, 2024
36a7637
smartmeter: disable scaler calculations
Morg42 Dec 26, 2024
2f4bafd
smartmeter: disabled DLMS asyncio
Morg42 Jan 4, 2025
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
539 changes: 539 additions & 0 deletions smartmeter/__init__.py

Large diffs are not rendered by default.

164 changes: 164 additions & 0 deletions smartmeter/conversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#!/usr/bin/env python3
# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
#########################################################################
# Copyright 2016 - 2018 Bernd Meiners [email protected]
#########################################################################
#
# This file is part of SmartHomeNG.py.
# Visit: https://github.com/smarthomeNG/
# https://knx-user-forum.de/forum/supportforen/smarthome-py
#
# SmartHomeNG.py is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SmartHomeNG.py is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SmartHomeNG.py. If not, see <http://www.gnu.org/licenses/>.
#########################################################################


__license__ = "GPL"
__version__ = "2.0"
__revision__ = "0.1"
__docformat__ = 'reStructuredText'

import datetime

CONVERTERS = {
'int': 'int',
'float': 'float',
'ZST10': 'datetime',
'ZST12': 'datetime',
'D6': 'date',
'Z6': 'time',
'Z4': 'time',
'num': 'num'
}


class Conversion:
def _from_ZST10(self, text: str) -> datetime.datetime:
"""
this function converts a string of form "YYMMDDhhmm" into a datetime object
:param text: string to convert
:return: a datetime object upon success or None if error found by malformed string
"""
if len(text) != 10:
raise ValueError("too few characters for date/time code from OBIS")

year = int(text[0:2]) + 2000
month = int(text[2:4])
day = int(text[4:6])
hour = int(text[6:8])
minute = int(text[8:10])
return datetime.datetime(year, month, day, hour, minute, 0)

def _from_ZST12(self, text: str) -> datetime.datetime:
"""
this function converts a string of form "YYMMDDhhmmss" into a datetime object
:param text: string to convert
:return: a datetime object upon success or None if error found by malformed string
"""
if len(text) != 12:
raise ValueError("too few characters for date/time code from OBIS")

year = int(text[0:2]) + 2000
month = int(text[2:4])
day = int(text[4:6])
hour = int(text[6:8])
minute = int(text[8:10])
second = int(text[10:12])
return datetime.datetime(year, month, day, hour, minute, second)

def _from_D6(self, text: str) -> datetime.date:
"""
this function converts a string of form "YYMMDD" into a datetime.date object
:param text: string to convert
:return: a datetime.date object upon success or None if error found by malformed string
"""
if len(text) != 6:
raise ValueError("too few characters for date code from OBIS")

year = int(text[0:2]) + 2000
month = int(text[2:4])
day = int(text[4:6])
return datetime.date(year, month, day)

def _from_Z4(self, text: str) -> datetime.time:
"""
this function converts a string of form "hhmm" into a datetime.time object
:param text: string to convert
:return: a datetime.time object upon success or None if error found by malformed string
"""
if len(text) != 4:
raise ValueError("too few characters for time code from OBIS")

hour = int(text[0:2])
minute = int(text[2:4])
return datetime.time(hour, minute)

def _from_Z6(self, text: str) -> datetime.time:
"""
this function converts a string of form "hhmmss" into a datetime.time object
:param text: string to convert
:return: a datetime.time object upon success or None if error found by malformed string
"""
if len(text) != 6:
raise ValueError("too few characters for time code from OBIS")

hour = int(text[0:2])
minute = int(text[2:4])
second = int(text[4:6])
return datetime.time(hour, minute, second)

def _convert_value(self, val, converter: str = ''):
"""
This function converts the OBIS value to a user chosen valalue
:param val: the value to convert given as string
:param converter: type of value, should contain one of CONVERTERS
:return: after successful conversion the value in converted form
"""
if converter not in CONVERTERS:
return val

try:
if converter in ('num', 'float', 'int'):

if converter in ('num', 'int'):
try:
return int(val)
except (ValueError, AttributeError):
if converter == 'int':
raise ValueError

# try/except to catch floats like '1.0' and '1,0'
try:
return float(val)
except ValueError:
if ',' in val:
val = val.replace(',', '.')
return float(val)
else:
raise ValueError

if not val.isdigit():
raise ValueError("only digits allowed for date/time code from OBIS")

if converter == 'int':
return int(val)

# try to find self._from_<converter> -> run it and return result
if hasattr(self, f'_from_{converter}'):
return getattr(self, f'_from_{converter}')(val)

# no suitable converter found
raise ValueError

except ValueError as e:
raise ValueError(f'could not convert from "{val}" to a {CONVERTERS[converter]} ({e})')
Loading