As experiment with LoraWAN a device type LSN50v2D20 is feeding data to TTN's network & servers:
- data from 1 analogue input through ADC
- data from 3 thermosensors DS18B20 (of which 1 actively connected)
2 Outputs from TTN:
- Webhook towards Datacake.be periodically producing a csv-file
- Webhook from TTN to my own webserver.
The latter output is resulting from the PHP-script from
https://github.com/descartes/TheThingsS ... ok.Tab.php running on my webserver.
Slightly expanded by one function exporting a json-file by means of this inserted piece of script
Code: Select all
// Line xxx = Save a second copy of the raw file, with fixed filename, as json-type and in other folder!
// Because of fixed filename will be overwritten by latest version and a single source of data for subsequent extraction of data.
// Preferred that application_id is copied from the raw file, but not yet found appropriate formulation, and therefore plain, fixed text as ident
$path1File = "data/".LSN50v2D20."-ttn0raw.json";
file_put_contents($path1File, $data);
This piece of PHP-script periodically produces a json-file, dropped at my webserver.
The next stage is a Python-script running on a Raspberry with Raspian_Bullseye & Domoticz.
Test-objective for that Python-script is reading the json-file, data-extraction and driving of 4 widgets for the 1*ADC-signal and the 3*Thermosensor.
The Python-script below is rather self-explaining, has abundant non-essential auxiliary functions for date&time included and also ample printing-lines for debug-checking.
- Spoiler: show
- #!/usr/bin/env python3
# -*- coding = utf-8 to enable reading by simple editors -*-
# (c)2025 script compiled by Toulon7559 from various material from forums and from comparable own scripts.
# Version 00_250107, reading Info-table from json-file produced for LSN50v2D20 by TTN-Webhook-output.
#
#
#
# ------------------------------------------------------------
# Line 009 Description of setup
# ------------------------------------------------------------
# File-extraction is related to specific contents as produced by a Dragino LSN50v2-D20.
# Setup for data-extraction, compilation & upload:
# 0. Optionally install module pyEphem to get accurate info on sunrise and sunset, module requests for http-handling, and module dicttoxml for json/xml-generation
# 1. Read the json-file from the folder at the webserver
# 2. Extract the various desired fields from the json-file
# 3. Use extracted data for setting devices in Domoticz
# 3. Optional compilation & generation & upload of derived files.
# Overall script running interval to be realised by a cronjob: set running of that cronjob at the interval selected below or shorter.
# Don't set the general script interval shorter than 20 minutes, because the json-file is coming from TTN with that interval.
# Setting of interval&delay included in this script as lower level option for such purpose.
#
# ------------------------------------------------------------
# Line 023 = PREPARATION & SETTING
# ------------------------------------------------------------
# Imports for script-operation
# Just in case Application environment could change, consider below selections:
# PYTHON3 = (version_info.major > 2)
# Choice of some HTTP libraries depends upon application of Python 2.x or Python3.x
# Select the compatible urlopen-function
try:
# For Python 3.0 and later
import urllib.request
from urllib.request import urlopen
except ImportError:
# Fall back to Python 2's urllib2
import urllib2
from urllib2 import urlopen
# In both cases:
import json # used for JSON-handling
import requests # used for (optional) handling of http-requests
import dicttoxml # used for (optional) handling of dictionaries and xml-files
# Line 044 = Settings
print ('Start of script LSN50v2D20PayloadDecoder_00')
DEBUG = 1 # DEBUG=0 is 'operationally running without print'
# the "print"-lines only added for local read-out e.g. during script_tuning
# ------------------------------------------------------------
# Line 050 = PREPARATION FOR HANDLING DATE&TIME
# ------------------------------------------------------------
import calendar
import datetime # used for timestamps & timecheck
import time # used for timestamps & delays
from datetime import datetime, timedelta
now = datetime.now()
now_utc = datetime.utcnow()
dattim1 = datetime.now().strftime('%Y%m%d %H:%M')
dattim2 = datetime.now().strftime('%Y%m%d%H%M%S')
dattim3 = datetime.utcnow().strftime('%Y%m%d%H%M%S')
dattim4 = datetime.utcnow().strftime('%d-%m-%Y')
dattim4a = datetime.utcnow().strftime('%Y-%m-%d')
dattim5 = datetime.utcnow().strftime('%H:%M')
dattim6 = datetime.now().strftime('%d-%m-%Y')
dattim7 = datetime.now().strftime('%H:%M')
dattim8 = datetime.now().strftime('%d')
DD_CET = int(dattim8)
dattim8a = datetime.utcnow().strftime('%d')
DD_UTC = int(dattim8a)
dattim9 = datetime.now().strftime('%H%M')
HHMM_CET = int(dattim9)
dattim9a = datetime.utcnow().strftime('%H%M')
HHMM_UTC = int(dattim9a)
dattim10 = datetime.now().strftime('%M')
MM_CET = int(dattim10)
dattim11 = datetime.utcnow().strftime('%H')
HH_UTC = int(dattim11)
dattim12 = datetime.now().strftime('%H')
HH_CET = int(dattim12)
dattim13 = int(int(dattim12) - int(dattim11))
dattim14 = datetime.utcnow().strftime('%m')
Mnd_UTC = int(dattim14)
Year_UTC = int(datetime.utcnow().strftime('%Y'))
Year_CET = int(datetime.now().strftime('%Y'))
DST = time.localtime().tm_isdst # localtime() returns a tuple.
# Value is 0 when DST is not in effect. Value is 1 when DST is in effect. Value is -1 when DST is unknown.
# Extract Date&Time for yesterday
yesterday = datetime.utcnow() - timedelta(1)
type(yesterday)
dattim4b = datetime.strftime(yesterday, '%Y-%m-%d')
# Line 092 = Check by print
if DEBUG == 1:
print ('Date&Time, Auxiliary values')
print ('===========================')
print ('Present Year (UTC) = ', Year_UTC)
print ('Present Year (CET) = ', Year_CET)
print ('Present Date&Time (UTC) = ', now_utc)
print ('Present Date&Time (CET) = ', now)
print ('Present DatTim1 (ZTxt_CET) = ', dattim1)
print ('Present DatTim2 (Strg_CET) = ', dattim2)
print ('Present DatTim3 (Strg_UTC) = ', dattim3)
print ('Present DatTim4 (DMY_UTC) = ', dattim4)
print ('Present DatTim4 (YMD_UTC) = ', dattim4a)
print ('Present DatTim5 (Time_UTC) = ', dattim5)
print ('Present DatTim6 (DMY_CET) = ', dattim6)
print ('Present DatTim7 (Time_CET) = ', dattim7)
print ('Present DatTim8 (DDay_CET) = ', dattim8)
print ('Present DatTim8 (DDay_UTC) = ', dattim8a)
print ('Present DatTim9 (Tval_CET) = ', dattim9)
print ('Present DatTim10 (Min_CET) = ', dattim10)
print ('Present DatTim11 (Hr_UTC) = ', dattim11)
print ('Present DatTim12 (Hr_CET) = ', dattim12)
print ('Delta-time (Hr_UTC_CET) = ', dattim13)
print ('Present DatTim14 (Mnd_UTC) = ', dattim14)
print ('DST-status (1/0/-1) = ', DST)
print ('Yesterday (YMD_UTC) = ', dattim4b)
# Line 119 = Definition of local functions
# Line 121 = Translation of UTC or Epoch to LocalTime
def utc_to_local(utc_dt):
# get integer timestamp to avoid precision lost
timestamp = calendar.timegm(utc_dt.timetuple())
local_dt = datetime.fromtimestamp(timestamp)
assert utc_dt.resolution >= timedelta(microseconds=1)
return local_dt.replace(microsecond=utc_dt.microsecond)
def epoch_to_local1(utc_epoch):
local_datetime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(utc_epoch))
return local_datetime
def epoch_to_local2(utc_epoch):
local_filedate = time.strftime('%d-%m-%Y', time.localtime(utc_epoch))
return local_filedate
def epoch_to_local3(utc_epoch):
local_filetime = time.strftime('%H:%M', time.localtime(utc_epoch))
return local_filetime
# ------------------------------------------------------------
# Line 143 = READ Information BY url-call
# ------------------------------------------------------------
# Call-string for data as indicated in the header of this script
JSONFile = "https://www.vannwnhzn.nl/TTN/data/LSN50 ... n0raw.json"
# ------------------------------------------------------------
# Line 149 = Read URL for data within timeslot
# ------------------------------------------------------------
import sys # For check that time is 0 ~ +4 from n*10 minutes
Delay = 5 # Delay (seconds) before execution of subsequent scripted actions [also for spreading url-calls]
if (MM_CET)%10 <= 4:
print('Inside window = programmed delay of ',Delay,' seconds')
time.sleep(Delay) # delay till execution of next scriptline
else:
print('Outside window = stop the script')
sys.exit()
# Line 160 = Extract the data-file and print the data as reference for debug
page = urlopen(JSONFile)
content_test = page.read()
data = json.loads(content_test)
print('Basic data-file =')
print(data)
print
print ('End of reading datafile')
# ------------------------------------------------------------
# Line 169 = Extraction & Print of components from the json-file
# ------------------------------------------------------------
# Following & using the structure of the json-file
# Line 172 = Device-Info
deviceid = data['end_device_ids']['device_id']
applicid = data['end_device_ids']['application_ids']['application_id']
time_received = data['received_at']
time_epoch = data['uplink_message']['rx_metadata'][0]['timestamp']
stationlat = data['uplink_message']['rx_metadata'][0]['location']['latitude']
stationlong = data['uplink_message']['rx_metadata'][0]['location']['longitude']
stationalt = data['uplink_message']['rx_metadata'][0]['location']['altitude']
# Line 180 = Values
# For some parameters the extraction marked # does not yield values, but error reports.
# As temporary solution defaultvalues have been inserted.
ADC_CH0 = data['uplink_message']['decoded_payload']['ADC_CH0V']; print('ADC_CH0 = ',ADC_CH0,' Units')
BatVolt = 3.55 # data['uplink_message']['decoded_payload']['Batv']; print('Battery = ',BatVolt,' V')
Dig_Status = "L" # data['uplink_message']['decoded_payload']['Digital_IStatus']
Door_Status = "OPEN" # data['uplink_message']['decoded_payload']['Door_status']
Ext1_Status = "FALSE" # data['uplink_message']['decoded_payload']['EXT1_Trigger']
Temp_CH1 = float(data['uplink_message']['decoded_payload']['TempC1']); print('Temp_CH1 = ',Temp_CH1,' C')
Temp_CH2 = float(data['uplink_message']['decoded_payload']['TempC2']); print('Temp_CH2 = ',Temp_CH2,' C')
Temp_CH3 = float(data['uplink_message']['decoded_payload']['TempC3']); print('Temp_CH3 = ',Temp_CH3,' C')
WorkMode = data['uplink_message']['decoded_payload']['Work_mode']; print('Workmode = ',WorkMode)
# Line 193 = print main data
if DEBUG == 1:
print
print('Device data in file')
print(deviceid, time_received, time_epoch, stationlat, stationlong, stationalt)
print
print('Sensor data in file')
print(ADC_CH0, BatVolt, Dig_Status, Door_Status, Ext1_Status, Temp_CH1, Temp_CH2, Temp_CH3, WorkMode)
print ('End of reading datafile')
# -------------------------------------------------
# Line 205 = Upload to Virtual Devices at Domoticz3
# -------------------------------------------------
# For instructions and layouts for JSON-call to set devices, see Domoticz' wiki
# For this application
# JSON-call for update Custom-device: /json.htm?type=command¶m=udevice&idx=IDX&nvalue=0&svalue=VALUE
# JSON-call for update Temp-device: /json.htm?type=command¶m=udevice&idx=IDX&nvalue=0&svalue=TEMP
# JSON-call for update Voltage-device: /json.htm?type=command¶m=udevice&idx=IDX&nvalue=0&svalue=VOLTAGE
# Line 213 = Settings for upload in domoticz3 to local devices
domoticz3_host = '127.0.0.1' #ip adress of the domoticz host
domoticz3_port = '8080'
domoticz3_url = 'json.htm'
# Line 218 = Definition of IDXes for local devices
domoticz3_idx30 = '10' #idx of new Custom device ADC_CH0
domoticz3_idx31 = '11' #idx of new Temp device Temp_CH1
domoticz3_idx32 = '12' #idx of new Temp device Temp_CH2
domoticz3_idx33 = '13' #idx of new Temp device Temp_CH3
domoticz3_idx34 = '14' #idx of new Voltage device BatVolt
# Line 225 = uploading values to domoticz3
url = ("http://" + domoticz3_host + ":" + domoticz3_port + "/" + domoticz3_url+ "?type=command¶m=udevice&idx=" + domoticz3_idx30 + "&nvalue=0&svalue=" + str(ADC_CH0))
urllib.request.urlopen(url)
url = ("http://" + domoticz3_host + ":" + domoticz3_port + "/" + domoticz3_url+ "?type=command¶m=udevice&idx=" + domoticz3_idx31 + "&nvalue=0&svalue=" + str(Temp_CH1))
urllib.request.urlopen(url)
url = ("http://" + domoticz3_host + ":" + domoticz3_port + "/" + domoticz3_url+ "?type=command¶m=udevice&idx=" + domoticz3_idx32 + "&nvalue=0&svalue=" + str(Temp_CH2))
urllib.request.urlopen(url)
url = ("http://" + domoticz3_host + ":" + domoticz3_port + "/" + domoticz3_url+ "?type=command¶m=udevice&idx=" + domoticz3_idx33 + "&nvalue=0&svalue=" + str(Temp_CH3))
urllib.request.urlopen(url)
url = ("http://" + domoticz3_host + ":" + domoticz3_port + "/" + domoticz3_url+ "?type=command¶m=udevice&idx=" + domoticz3_idx34 + "&nvalue=0&svalue=" + str(BatVolt))
urllib.request.urlopen(url)
# ==================================================
# Line 242 = End of setup of devices
# ==================================================
print ('End of setting devices')
# Line 246 = End of script
# -------------------------------------------------
print ('End of script LSN50v2D20PayloadDecoder_00')
However not all functions of this Python-script yet running, and therefore hints requested enabling to move forward:
- for unknown reasons the extraction at lines 184~187 throws errors, not recognizing Batv and the Status-signals.
- although not exotic, the instruction
at line 227 (and comparable) does not have the effect that the widgets are updated.
Please note that the indents present in the original Python-script for if-statements etc. are not correctly shown in the code-insertion in this message.