Skip to content

Commit 2f4bafd

Browse files
committed
smartmeter: disabled DLMS asyncio
1 parent 36a7637 commit 2f4bafd

File tree

3 files changed

+131
-141
lines changed

3 files changed

+131
-141
lines changed

smartmeter/__init__.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@
2323
# along with SmartHomeNG. If not, see <http://www.gnu.org/licenses/>.
2424
#########################################################################
2525

26-
__license__ = 'GPL'
27-
__version__ = '2.0'
28-
__revision__ = '0.1'
29-
__docformat__ = 'reStructuredText'
30-
3126
import asyncio
3227
import os
3328
import threading
@@ -83,6 +78,7 @@ class Smartmeter(SmartPlugin, Conversion):
8378
the update functions for the items
8479
"""
8580

81+
# move to 1.0.0 as soon as DLMS asyncio is tested
8682
PLUGIN_VERSION = '0.9.0'
8783

8884
def __init__(self, sh):
@@ -368,8 +364,12 @@ def _load_parameters(self):
368364
self._config['poll'] = True
369365
poll = self.get_parameter_value('poll')
370366
if not poll:
371-
self.use_asyncio = True
372-
self._config['poll'] = False
367+
# TODO: remove if/else as soon as async DLMS is tested with live device
368+
if self.protocol == 'SML':
369+
self.use_asyncio = True
370+
self._config['poll'] = False
371+
else:
372+
self.logger.warning('async mode requested, but not yet available for DLMS. Plugin will be polling regularly...')
373373

374374
if self.use_asyncio:
375375
self.timefilter = self.get_parameter_value('time_filter')

smartmeter/dlms.py

Lines changed: 124 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@
2727
# along with SmartHomeNG.py. If not, see <http://www.gnu.org/licenses/>.
2828
#########################################################################
2929

30-
31-
__license__ = "GPL"
32-
__version__ = "2.0"
33-
__revision__ = "0.1"
34-
__docformat__ = 'reStructuredText'
35-
3630
import asyncio
3731
import logging
3832
import time
@@ -246,129 +240,131 @@ def format_time(timedelta: float) -> str:
246240
return f"{timedelta * 10 ** 9:.2f} ns"
247241

248242

243+
# TODO: asyncio for DLMS disabled until real testing has succeeded
244+
# #
245+
# # asyncio reader
246+
# #
247+
#
248+
#
249+
# class AsyncReader():
250+
#
251+
# def __init__(self, logger, plugin: SmartPlugin, config: dict):
252+
# self.buf = bytes()
253+
# self.logger = logger
254+
# self.lock = config['lock']
255+
#
256+
# if not ASYNC_IMPORTED:
257+
# raise ImportError('pyserial_asyncio not installed, running asyncio not possible.')
258+
#
259+
# if 'serial_port' not in config:
260+
# raise ValueError(f'configuration {config} is missing serial port config')
261+
#
262+
# self.serial_port = config.get('serial_port')
263+
# self.timeout = config.get('timeout', 2)
264+
# self.baudrate = config.get('baudrate', 300)
265+
# if not config['dlms'].get('only_listen', False):
266+
# self.logger.warning('asyncio operation can only listen, smartmeter will not be triggered!')
267+
#
268+
# self.target = '(not set)'
269+
# self.listening = False
270+
# self.reader = None
271+
#
272+
# self.config = config
273+
# self.transport = None
274+
# self.protocol = DlmsProtocol(logger, config)
275+
#
276+
# # set from plugin
277+
# self.plugin = plugin
278+
# self.data_callback = plugin._update_values
279+
#
280+
# async def listen(self):
281+
# result = self.lock.acquire(blocking=False)
282+
# if not result:
283+
# self.logger.error('couldn\'t acquire lock, polling/manual access active?')
284+
# return
285+
#
286+
# self.logger.debug('acquired lock')
287+
# try: # LOCK
288+
# self.reader, _ = await serial_asyncio.open_serial_connection(
289+
# url=self.serial_port,
290+
# baudrate=self.baudrate,
291+
# bytesize=S_BITS,
292+
# parity=S_PARITY,
293+
# stopbits=S_STOP,
294+
# )
295+
# self.target = f'async_serial://{self.serial_port}'
296+
# self.logger.debug(f'target is {self.target}')
297+
#
298+
# if self.reader is None and not TESTING:
299+
# self.logger.error('error on setting up async listener, reader is None')
300+
# return
301+
#
302+
# self.plugin.connected = True
303+
# self.listening = True
304+
# self.logger.debug('starting to listen')
305+
#
306+
# buf = bytes()
307+
#
308+
# while self.listening and self.plugin.alive:
309+
#
310+
# if TESTING:
311+
# # make this bytes...
312+
# data = RESULT.encode()
313+
# else:
314+
# data = await self.reader.readuntil(b'!')
315+
#
316+
# # check we got a start byte if buf is empty
317+
# if len(buf) == 0:
318+
# if b'/' not in data:
319+
# self.logger.warning('incomplete data received, no start byte, discarding')
320+
# continue
321+
# else:
322+
# # trim data to start byte
323+
# data = data[data.find(b'/'):]
324+
#
325+
# # add data to buffer
326+
# buf += data
327+
#
328+
# # check if we have an end byte
329+
# if b'!' not in buf:
330+
# if len(buf) > 100000:
331+
# self.logger.warning(f'got {len(buf)} characters without end byte, discarding data')
332+
# buf = bytes()
333+
# continue
334+
#
335+
# # get data from start (b'/') to end (b'!') into data
336+
# # leave the remainder in buf
337+
# data, _, buf = buf.partition(b'!')
338+
#
339+
# # we should have data beginning with b'/' and ending with b'!'
340+
# identification_message = str(data, 'utf-8').splitlines()[0]
341+
# manid = identification_message[1:4]
342+
# manname = manufacturer_ids.get(manid, 'unknown')
343+
# self.logger.debug(f"manufacturer for {manid} is {manname} (out of {len(manufacturer_ids)} given manufacturers)")
344+
#
345+
# response = self.protocol(data.decode())
346+
#
347+
# # get data from frameparser and call plugin
348+
# if response and self.data_callback:
349+
# self.data_callback(response)
350+
#
351+
# finally:
352+
# # cleanup
353+
# try:
354+
# self.reader.feed_eof()
355+
# except Exception:
356+
# pass
357+
# self.plugin.connected = False
358+
# self.lock.release()
359+
#
360+
# async def stop_on_queue(self):
361+
# """ wait for STOP in queue and signal reader to terminate """
362+
# self.logger.debug('task waiting for STOP from queue...')
363+
# await self.plugin. wait_for_asyncio_termination()
364+
# self.logger.debug('task received STOP, halting listener')
365+
# self.listening = False
249366
#
250-
# asyncio reader
251-
#
252-
253-
254-
class AsyncReader():
255-
256-
def __init__(self, logger, plugin: SmartPlugin, config: dict):
257-
self.buf = bytes()
258-
self.logger = logger
259-
self.lock = config['lock']
260-
261-
if not ASYNC_IMPORTED:
262-
raise ImportError('pyserial_asyncio not installed, running asyncio not possible.')
263-
264-
if 'serial_port' not in config:
265-
raise ValueError(f'configuration {config} is missing serial port config')
266-
267-
self.serial_port = config.get('serial_port')
268-
self.timeout = config.get('timeout', 2)
269-
self.baudrate = config.get('baudrate', 300)
270-
if not config['dlms'].get('only_listen', False):
271-
self.logger.warning('asyncio operation can only listen, smartmeter will not be triggered!')
272-
273-
self.target = '(not set)'
274-
self.listening = False
275-
self.reader = None
276-
277-
self.config = config
278-
self.transport = None
279-
self.protocol = DlmsProtocol(logger, config)
280-
281-
# set from plugin
282-
self.plugin = plugin
283-
self.data_callback = plugin._update_values
284-
285-
async def listen(self):
286-
result = self.lock.acquire(blocking=False)
287-
if not result:
288-
self.logger.error('couldn\'t acquire lock, polling/manual access active?')
289-
return
290-
291-
self.logger.debug('acquired lock')
292-
try: # LOCK
293-
self.reader, _ = await serial_asyncio.open_serial_connection(
294-
url=self.serial_port,
295-
baudrate=self.baudrate,
296-
bytesize=S_BITS,
297-
parity=S_PARITY,
298-
stopbits=S_STOP,
299-
)
300-
self.target = f'async_serial://{self.serial_port}'
301-
self.logger.debug(f'target is {self.target}')
302-
303-
if self.reader is None and not TESTING:
304-
self.logger.error('error on setting up async listener, reader is None')
305-
return
306-
307-
self.plugin.connected = True
308-
self.listening = True
309-
self.logger.debug('starting to listen')
310-
311-
buf = bytes()
312-
313-
while self.listening and self.plugin.alive:
314-
315-
if TESTING:
316-
# make this bytes...
317-
data = RESULT.encode()
318-
else:
319-
data = await self.reader.readuntil(b'!')
320-
321-
# check we got a start byte if buf is empty
322-
if len(buf) == 0:
323-
if b'/' not in data:
324-
self.logger.warning('incomplete data received, no start byte, discarding')
325-
continue
326-
else:
327-
# trim data to start byte
328-
data = data[data.find(b'/'):]
329-
330-
# add data to buffer
331-
buf += data
332-
333-
# check if we have an end byte
334-
if b'!' not in buf:
335-
if len(buf) > 100000:
336-
self.logger.warning(f'got {len(buf)} characters without end byte, discarding data')
337-
buf = bytes()
338-
continue
339-
340-
# get data from start (b'/') to end (b'!') into data
341-
# leave the remainder in buf
342-
data, _, buf = buf.partition(b'!')
343-
344-
# we should have data beginning with b'/' and ending with b'!'
345-
identification_message = str(data, 'utf-8').splitlines()[0]
346-
manid = identification_message[1:4]
347-
manname = manufacturer_ids.get(manid, 'unknown')
348-
self.logger.debug(f"manufacturer for {manid} is {manname} (out of {len(manufacturer_ids)} given manufacturers)")
349-
350-
response = self.protocol(data.decode())
351-
352-
# get data from frameparser and call plugin
353-
if response and self.data_callback:
354-
self.data_callback(response)
355-
356-
finally:
357-
# cleanup
358-
try:
359-
self.reader.feed_eof()
360-
except Exception:
361-
pass
362-
self.plugin.connected = False
363-
self.lock.release()
364-
365-
async def stop_on_queue(self):
366-
""" wait for STOP in queue and signal reader to terminate """
367-
self.logger.debug('task waiting for STOP from queue...')
368-
await self.plugin. wait_for_asyncio_termination()
369-
self.logger.debug('task received STOP, halting listener')
370-
self.listening = False
371-
367+
# TODO end
372368

373369
#
374370
# single-shot reader

smartmeter/sml.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@
2727
# along with SmartHomeNG.py. If not, see <http://www.gnu.org/licenses/>.
2828
#########################################################################
2929

30-
31-
__license__ = "GPL"
32-
__version__ = "2.0"
33-
__revision__ = "0.1"
34-
__docformat__ = 'reStructuredText'
35-
3630
import asyncio
3731
import errno
3832
import logging

0 commit comments

Comments
 (0)