Skip to content

Commit

Permalink
database: added new database function "diff", "differentiate" and "du…
Browse files Browse the repository at this point in the history
…ration"
  • Loading branch information
aschwith committed Jan 4, 2024
1 parent aca74b7 commit f388a2b
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 25 deletions.
53 changes: 30 additions & 23 deletions database/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class Database(SmartPlugin):
"""

ALLOW_MULTIINSTANCE = True
PLUGIN_VERSION = '1.6.11'
PLUGIN_VERSION = '1.6.12'

# SQL queries: {item} = item table name, {log} = log table name
# time, item_id, val_str, val_num, val_bool, changed
Expand Down Expand Up @@ -1075,7 +1075,7 @@ def _series(self, func, start, end='now', count=100, ratio=1, update=False, step
:return: data structure in the form needed by the websocket plugin return it to the visu
"""
#self.logger.warning("_series: item={}, func={}, start={}, end={}, count={}".format(item, func, start, end, count))
#self.logger.debug("_series: item={}, func={}, start={}, end={}, count={}".format(item, func, start, end, count))
init = not update
if sid is None:
sid = item + '|' + func + '|' + str(start) + '|' + str(end) + '|' + str(count)
Expand All @@ -1084,7 +1084,10 @@ def _series(self, func, start, end='now', count=100, ratio=1, update=False, step
'avg': 'MIN(time), ' + self._precision_query('AVG(val_num * duration) / AVG(duration)'),
'avg.order': 'ORDER BY time ASC',
'integrate': 'MIN(time), SUM(val_num * duration)',
'differentiate': 'MIN(time), (val_num - LAG(val_num,1, -1)) / duration',
'diff': 'MIN(time), (val_num - LAG(val_num,1) OVER (ORDER BY val_num))',
'duration': 'MIN(time), duration',
# differentiate (d/dt) is scaled to match the conversion from d/dt (kWh) = kWh: time is in ms, val_num in kWh, therefore scale by 1000ms and 3600s/h to obtain the result in kW:
'differentiate': 'MIN(time), (val_num - LAG(val_num,1) OVER (ORDER BY val_num)) / ( (time - LAG(time,1) OVER (ORDER BY val_num)) / (3600 * 1000) )',
'count': 'MIN(time), SUM(CASE WHEN val_num{op}{value} THEN 1 ELSE 0 END)'.format(**expression['params']),
'countall': 'MIN(time), COUNT(*)',
'min': 'MIN(time), MIN(val_num)',
Expand All @@ -1103,22 +1106,26 @@ def _series(self, func, start, end='now', count=100, ratio=1, update=False, step
group = 'GROUP BY ROUND(time / :step)' if func + '.group' not in queries else queries[func + '.group']
logs = self._fetch_log(item, queries[func], start, end, step=step, count=count, group=group, order=order)
tuples = logs['tuples']
if tuples:
if logs['istart'] > tuples[0][0]:
tuples[0] = (logs['istart'], tuples[0][1])
if end != 'now':
tuples.append((logs['iend'], tuples[-1][1]))
else:
tuples = []
item_change = self._timestamp(logs['item'].last_change())
if item_change < logs['iend']:
value = float(logs['item']())
if item_change < logs['istart']:
tuples.append((logs['istart'], value))
elif init:
tuples.append((item_change, value))
if init:
tuples.append((logs['iend'], value))

# Append tuples by addition values (not for func differentiate)
if func != 'differentiate':

if tuples:
if logs['istart'] > tuples[0][0]:
tuples[0] = (logs['istart'], tuples[0][1])
if end != 'now':
tuples.append((logs['iend'], tuples[-1][1]))
else:
tuples = []
item_change = self._timestamp(logs['item'].last_change())
if item_change < logs['iend']:
value = float(logs['item']())
if item_change < logs['istart']:
tuples.append((logs['istart'], value))
elif init:
tuples.append((item_change, value))
if init:
tuples.append((logs['iend'], value))

if expression['finalizer']:
tuples = self._finalize(expression['finalizer'], tuples)
Expand All @@ -1129,14 +1136,14 @@ def _series(self, func, start, end='now', count=100, ratio=1, update=False, step
'step': logs['step'], 'sid': sid},
'update': self.shtime.now() + datetime.timedelta(seconds=int(logs['step'] / 1000))
}
#self.logger.warning("_series: result={}".format(result))
#self.logger.debug("_series: result={}".format(result))

return result


def _single(self, func, start, end='now', item=None):
"""
As far as it has been checked, this method is never called.
It is attached to the item object but no other plugin is known that calls this method.
This function is not used by any other plugin but can be used in logics
:param func:
:param start:
Expand All @@ -1148,11 +1155,11 @@ def _single(self, func, start, end='now', item=None):
queries = {
'avg': self._precision_query('AVG(val_num * duration) / AVG(duration)'),
'integrate': 'SUM(val_num * duration)',
'differentiate': 'val_num - LAG(val_num) / duration',
'count': 'SUM(CASE WHEN val_num{op}{value} THEN 1 ELSE 0 END)'.format(**expression['params']),
'countall': 'COUNT(*)',
'min': 'MIN(val_num)',
'max': 'MAX(val_num)',
'diff': 'MAX(val_num) - MIN(val_num)',
'on': self._precision_query('SUM(val_bool * duration) / SUM(duration)'),
'sum': 'SUM(val_num)',
'raw': 'val_num',
Expand Down
4 changes: 2 additions & 2 deletions database/plugin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ plugin:
keywords: database
support: https://knx-user-forum.de/forum/supportforen/smarthome-py/1021844-neues-database-plugin

version: 1.6.11 # Plugin version
version: 1.6.12 # Plugin version
sh_minversion: 1.9.3.2 # minimum shNG version to use this plugin
# sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest)
multi_instance: True # plugin supports multi instance
Expand Down Expand Up @@ -97,7 +97,7 @@ item_attributes:
# Definition of item attributes defined by this plugin
database:
type: str
valid_list_ci: ['', 'no', 'yes', 'init', 'init2', 'true']
valid_list_ci: ['', 'yes', 'init', 'true']
duplicate_use: True
description:
de: "Wenn auf 'yes' oder 'true' gesetzt, werden die Werte des Items in die Datenbank geschrieben. Wenn auf 'init' gesetzt, wird zusätzlich beim Start von SmartHomeNG der Wert des Items aus der Datenbank gelesen."
Expand Down

0 comments on commit f388a2b

Please sign in to comment.