Skip to content

Commit bf4a9c8

Browse files
authored
Merge pull request #1023 from onkelandy/jsonread
2 parents 114ab7e + def1578 commit bf4a9c8

File tree

15 files changed

+1343
-124
lines changed

15 files changed

+1343
-124
lines changed

jsonread/__init__.py

Lines changed: 306 additions & 65 deletions
Large diffs are not rendered by default.

jsonread/_pv_1_0_4/__init__.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#!/usr/bin/env python3
2+
# vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
3+
#########################################################################
4+
# Copyright 2019 Torsten Dreyer torsten (at) t3r (dot) de
5+
# Copyright 2021 Bernd Meiners Bernd.Meiners@mail.de
6+
#########################################################################
7+
# This file is part of SmartHomeNG.
8+
# https://www.smarthomeNG.de
9+
# https://knx-user-forum.de/forum/supportforen/smarthome-py
10+
#
11+
# SmartHomeNG is free software: you can redistribute it and/or modify
12+
# it under the terms of the GNU General Public License as published by
13+
# the Free Software Foundation, either version 3 of the License, or
14+
# (at your option) any later version.
15+
#
16+
# SmartHomeNG is distributed in the hope that it will be useful,
17+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
# GNU General Public License for more details.
20+
#
21+
# You should have received a copy of the GNU General Public License
22+
# along with SmartHomeNG. If not, see <http://www.gnu.org/licenses/>.
23+
#
24+
#########################################################################
25+
26+
import logging
27+
import json
28+
import requests
29+
from requests_file import FileAdapter
30+
import pyjq
31+
from lib.model.smartplugin import SmartPlugin
32+
from lib.item import Items
33+
from .webif import WebInterface
34+
35+
36+
class JSONREAD(SmartPlugin):
37+
PLUGIN_VERSION = "1.0.4"
38+
39+
def __init__(self, sh):
40+
"""
41+
Initializes the plugin
42+
@param url: URL of the json data to fetch
43+
@param cycle: the polling interval in seconds
44+
"""
45+
# Call init code of parent class (SmartPlugin)
46+
super().__init__()
47+
48+
from bin.smarthome import VERSION
49+
if '.'.join(VERSION.split('.', 2)[:2]) <= '1.5':
50+
self.logger = logging.getLogger(__name__)
51+
52+
self._url = self.get_parameter_value('url')
53+
self._cycle = self.get_parameter_value('cycle')
54+
self._session = requests.Session()
55+
self._session.mount('file://', FileAdapter())
56+
self._items = {}
57+
self._lastresult = {}
58+
self._lastresultstr = ""
59+
self._lastresultjq = ""
60+
61+
# if plugin should start even without web interface
62+
self.init_webinterface(WebInterface)
63+
64+
65+
def run(self):
66+
"""
67+
Run method for the plugin
68+
"""
69+
self.logger.debug("Run method called")
70+
self.alive = True
71+
self.scheduler_add(self.get_fullname(), self.poll_device, cycle=self._cycle)
72+
73+
def stop(self):
74+
self.logger.debug("Stop method called")
75+
self.scheduler_remove(self.get_fullname() )
76+
self.alive = False
77+
78+
def parse_item(self, item):
79+
if self.has_iattr(item.conf, 'jsonread_filter'):
80+
self._items[item] = self.get_iattr_value(item.conf, 'jsonread_filter')
81+
82+
def poll_device(self):
83+
try:
84+
response = self._session.get(self._url)
85+
86+
except Exception as ex:
87+
self.logger.error("Exception when sending GET request for {}: {}".format(self._url,str(ex)))
88+
return
89+
90+
if response.status_code != 200:
91+
self.logger.error("Bad response code from GET '{}': {}".format(self._url, response.status_code))
92+
return
93+
94+
try:
95+
json_obj = response.json()
96+
except Exception as ex:
97+
self.logger.error("Response from '{}' doesn't look like json '{}'".format(self._url, str(response.content)[:30]))
98+
return
99+
100+
try:
101+
self._lastresult = json_obj
102+
self._lastresultstr = json.dumps(self._lastresult, indent=4, sort_keys=True)
103+
self._lastresultjq = '\n'.join(str(x) for x in pathes(self._lastresult))
104+
except Exception as ex:
105+
self.logger.error("Could not change '{}' into pretty json string'{}'".format(self._lastresult,self._lastresultstr))
106+
self._lastresultstr = "<empty due to failure>"
107+
108+
for k in self._items.keys():
109+
try:
110+
jqres = pyjq.first(self._items[k], json_obj)
111+
112+
except Exception as ex:
113+
self.logger.error("jq filter failed: {}'".format(str(ex)))
114+
continue
115+
116+
k(jqres)
117+
118+
# just a helper function
119+
120+
def pathes( d, stem=""):
121+
#print("Stem:",stem)
122+
if isinstance(d, dict):
123+
for key, value in d.items():
124+
if isinstance(value, dict):
125+
for d in pathes(value, "{}.{}".format(stem,key)):
126+
yield d
127+
elif isinstance(value, list) or isinstance(value, tuple):
128+
for v in value:
129+
for d in pathes(v, "{}.{}".format(stem,key)):
130+
yield d
131+
else:
132+
yield "{}.{} => {}".format(stem,key,value)
133+
elif isinstance(d, list) or isinstance(d, tuple):
134+
for value in d:
135+
if isinstance(value, dict):
136+
for d in pathes(value, "{}.{}".format(stem,key)):
137+
yield d
138+
elif isinstance(value, list) or isinstance(value, tuple):
139+
for v in value:
140+
for d in pathes(v, "{}.{}".format(stem,key)):
141+
yield d
142+
else:
143+
yield "{}.{} => {}".format(stem,key,value)
144+
else:
145+
yield "{}.{}".format(stem,d)
358 KB
Loading

jsonread/_pv_1_0_4/demo.json

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"Body": {
3+
"Data": {
4+
"DeviceStatus": {
5+
"InverterState": "Sleeping"
6+
},
7+
"IAC": {
8+
"Unit": "A",
9+
"Value": 0.0
10+
},
11+
"IAC_L1": {
12+
"Unit": "A",
13+
"Value": 0.0
14+
},
15+
"IAC_L2": {
16+
"Unit": "A",
17+
"Value": 0.0
18+
},
19+
"IAC_L3": {
20+
"Unit": "A",
21+
"Value": 0.0
22+
},
23+
"IDC": {
24+
"Unit": "A",
25+
"Value": 0.0
26+
},
27+
"IDC_2": {
28+
"Unit": "A",
29+
"Value": 0.0
30+
},
31+
"PAC": {
32+
"Unit": "W",
33+
"Value": 0.0
34+
},
35+
"SAC": {
36+
"Unit": "VA",
37+
"Value": 0.0
38+
},
39+
"UAC": {
40+
"Unit": "V",
41+
"Value": 0.0
42+
},
43+
"UDC": {
44+
"Unit": "V",
45+
"Value": 75.19073486328
46+
},
47+
"UDC_2": {
48+
"Unit": "V",
49+
"Value": 75.34899902344
50+
}
51+
}
52+
},
53+
"Head": {
54+
"RequestArguments": {
55+
"DataCollection": "CommonInverterData",
56+
"DeviceClass": "Inverter",
57+
"Scope": "Device"
58+
},
59+
"Status": {
60+
"Code": 0,
61+
"Reason": "",
62+
"UserMessage": ""
63+
},
64+
"Timestamp": "2020-01-01T12:00:30+00:00"
65+
}
66+
}

jsonread/_pv_1_0_4/locale.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# translations for the web interface
2+
plugin_translations:
3+
'Item': {'de': '=', 'en': '='}
4+
'jsonread_filter': {'de': '=', 'en': '='}
5+
'Wert': {'de': '=', 'en': 'Value'}
6+
'Letzte Auslesung': {'de': '=', 'en': 'Last Reading'}
7+
'Items definiert': {'de': '=', 'en': 'Items defined'}
8+
'Letzte Auslesung einfache jq Abfragesyntax': {'de': '=', 'en': 'Last Reading simple jq query syntax'}

jsonread/_pv_1_0_4/plugin.yaml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Metadata for the JSONread-Plugin
2+
plugin:
3+
# Global plugin attributes
4+
type: web # plugin type (gateway, interface, protocol, system, web)
5+
description:
6+
de: 'json Parser Plugin basierend auf jq'
7+
en: 'json parser plugin based on jq'
8+
maintainer: onkelandy
9+
tester: bmxp
10+
state: ready
11+
keywords: json jq
12+
documentation: http://smarthomeng.de/user/plugins_doc/config/jsonread.html
13+
support: https://knx-user-forum.de/forum/supportforen/smarthome-py/not-yet
14+
15+
version: 1.0.4 # Plugin version
16+
sh_minversion: '1.4' # minimum shNG version to use this plugin
17+
#sh_maxversion: # maximum shNG version to use this plugin (leave empty if latest)
18+
#py_minversion: 3.6 # minimum Python version to use for this plugin
19+
py_maxversion: '3.11' # maximum Python version to use for this plugin (leave empty if latest)
20+
py_versioncomment: "Die aktuellste Version (2.6.0, released August 2022) des benötigten Packages 'pyjq' ist nicht kompatibel mit Python 3.12"
21+
restartable: True
22+
multi_instance: True # plugin supports multi instance
23+
classname: JSONREAD # class containing the plugin
24+
25+
parameters:
26+
# Definition of parameters to be configured in etc/plugin.yaml
27+
url:
28+
type: str
29+
mandatory: True
30+
description:
31+
de: >
32+
URL der JSON Datenquelle. Aktuell unterstützt werden Webprotokolle https:// und http:// sowie ein Dateiadapter file:/// (benötigt wirklich 3 Schrägstriche)
33+
en: >
34+
URL of the JSON source. Currently supported are web protocols https:// and http:// as well as file adapter file:/// (which needs indeed 3 slashes)
35+
cycle:
36+
type: int
37+
default: 30
38+
valid_min: 0
39+
description:
40+
de: 'Das Abfrage-Intervall für die gegebene Datenquelle in Sekunden'
41+
en: 'The polling interval for the given data source in seconds'
42+
43+
44+
item_attributes:
45+
# Definition of item attributes defined by this plugin
46+
jsonread_filter:
47+
type: str
48+
description:
49+
de: 'JQ Pfad um innerhalb eines JSON Datensatzes einen Wert auszuwählen. Dieser Wert wird dem Item dann zugewiesen'
50+
en: 'JQ path to select a value from JSON dataset. The Item will then receive this value'
51+
52+
item_structs: NONE
53+
# Definition of item-structure templates for this plugin (enter 'item_structs: NONE', if section should be empty)
54+
55+
#item_attribute_prefixes:
56+
# Definition of item attributes that only have a common prefix (enter 'item_attribute_prefixes: NONE' or ommit this section, if section should be empty)
57+
# NOTE: This section should only be used, if really nessesary (e.g. for the stateengine plugin)
58+
59+
logic_parameters: NONE
60+
# Definition of logic parameters defined by this plugin (enter 'logic_parameters: NONE', if section should be empty)
61+
62+
plugin_functions: NONE
63+
# Definition of function interface of the plugin
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#requests requirement moved to core
2+
requests-file
3+
# pyjq:
4+
# The most recent version (2.6 released August 2022) is compatible with Python 3.10
5+
pyjq
6+
# The version (2.5.2 released May 2021) is not compatible with Python 3.10
7+
# The project's README states, that the project is deprecated in favor Slixmpp (a fork of sleekxmpp).
8+
#pyjq;python_version<'3.10'

0 commit comments

Comments
 (0)