JsonMod - Lokale Datei auslesen

Begonnen von Stargazer, 05 Januar 2023, 21:30:49

Vorheriges Thema - Nächstes Thema

Stargazer

Hallo zusammen,

ich wollte gerne eine json - Datei auslesen, die direkt auf dem System liegt, wo auch FHEM drauf läuft.

Hier mal ein List des Moduls:

Internals:
   API_LAST_MSG 200
   API_LAST_RES 1672949813.85706
   CFGFN     
   DEF        file://home/pi/BTCInfo.json
   FUUID      63b5f00e-f33f-de75-143a-a00245a638994b8e
   NAME       BTCInfoTest
   NEXT       2023-01-05 22:00:00
   NR         513
   SOURCE     /home/pi/BTCInfo.json (339)
   STATE      ???
   SVN        24783 2021-07-21 22:37:12 UTC
   TYPE       JsonMod
   eventCount 109
   CONFIG:
     IN_REQUEST 0
     SOURCE     file://home/pi/BTCInfo.json
     SECRET:
   OLDREADINGS:
   READINGS:
    Attributes:
   readingList complete();
   room       BETA


Die JSON - Datei hat folgenden Inhalt:

getPositions: b'{"code":0,"msg":"","ttl":1,"data":{"positions":[{"positionId":"16106910628XXXXXXXXX","symbol":"BTC-USDT","currency":"","volume":0.000X,"availableVolume":0.000X,"positionSide":"Long","marginMode":"Isolated","avgPrice":16906,"liquidatedPrice":XX.X,"margin":X.4454,"leverage":1,"pnlRate":-0.08,"unrealisedPNL":-0.0075,"realisedPNL":-0.0034}]}}'


Nur irgendwie will es noch nicht so ganz. Eine andere Datei als JSON läuft ohne Probleme und ich erhalte Readings.
Bei der Datei, die ich zum testen habe, erscheint am Ende der Zeile SOURCE in Klammern eine 78. Hier ist es eine 339 zu sehen.
Ist das die Anzahl der Zeichen oder ist das noch ein interner Code ?

Oder kann es an der Kodierung der JSON ansich liegen ? Da habe ich allerdings keinen Einfluss drauf  :o.

Viele Grüße und Besten Dank

MadMax-FHEM

Warum in Anfängerfragen und nicht im passenden Unterforum:

Zitat von: help jsonmod
Module: 98_JsonMod.pm Maintainer: herrmannj Forum: Automatisierung

Du kannst auch verschieben...
...dann bekommen es auch Leute mit, die das Modul genau(er) kennen.

Wenn es mit einer Datei geht und mit einer anderen nicht:

Wo ist die Datei/Inhalt, die geht?

Liegen sie an unterschiedlichen Orten?

Weil die, die nicht geht liegt ja in /home/pi/ da dürfen andere User u.U. nicht viel/alles, weil "gehört" ja dem User pi und fhem läuft (norm.) unter dem User fhem...

Ansonsten: steht etwas im fhem Log? verbose höher setzen und noch mal schauen...

Gruß, Joachim
FHEM PI3B+ Bullseye: HM-CFG-USB, 40x HM, ZWave-USB, 13x ZWave, EnOcean-PI, 15x EnOcean, HUE/deCONZ, CO2, ESP-Multisensor, Shelly, alexa-fhem, ...
FHEM PI2 Buster: HM-CFG-USB, 25x HM, ZWave-USB, 4x ZWave, EnOcean-PI, 3x EnOcean, Shelly, ha-bridge, ...
FHEM PI3 Buster (Test)

betateilchen

Deine Datei enthält schlicht keinen gültigen JSON Input.

Das kannst Du mit jedem beliebigen JSON pretty printer, den Du online findest, nachprüfen.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

betateilchen

Hast Du eigentlich die ganzen "X" und das "b'" in den Beispielcode eingebaut? Problem dabei ist, dass rein numerische Werte nicht in "" stehen, aber wenn Du da ein X reinschreibst, dann ist das halt kein numerischer Wert mehr, sondern ein alphanumerischer. Und der muss dann doch wieder in "" eingefasst werden.
Was das "b'" soll, erschließt sich mir allerdings nicht.
Und um den gesamten Inhalt gehört nochmal eine geschweifte Klammer.

Dann käme am Ende sowas raus:


{
  "getPositions": {
    "code": 0,
    "msg": "",
    "ttl": 1,
    "data": {
      "positions": [
        {
          "positionId": "16106910628XXXXXXXXX",
          "symbol": "BTC-USDT",
          "currency": "",
          "volume": 0.0,
          "availableVolume": 0.0,
          "positionSide": "Long",
          "marginMode": "Isolated",
          "avgPrice": 16906,
          "liquidatedPrice": "XX.X",
          "margin": "X.4454",
          "leverage": 1,
          "pnlRate": -0.08,
          "unrealisedPNL": -0.0075,
          "realisedPNL": -0.0034
        }
      ]
    }
  }
}
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

Stargazer

Hallo zusammen,

Danke für eure Antworten. Ich hatte das mit dem Dateiinhalt schon so grob in Verdacht. Bin aber bei der Auswertung von JSON - Inhalten noch blutiger Anfänger.
Es gibt hier im Forum das Beispiel mit einer Zisterne. Da hatte ich folgendes als "Testobjekt" als JSON Datei hinterlegt:

{"Sensor":"Zisterne","IP":"192.168.18.90","Fuellstand":"156","Abstand":"0"}

Nachzulesen hier: https://forum.fhem.de/index.php?topic=109412.0

Da liefert das Modul dann natürlich 4 ordentliche Readings.

Die Datei, wie sie mir per Python-Skript geschrieben wird, kann ich nicht ändern. Sprich, es in einen konformen JSON - Inhalt zu bringen.
Die "XXXX"se war ich. Da stehen normalerweise Zahlen.

Ich hatte ebenfalls kurz einmal das FileRead angefangen zu nutzen. Bis ich über JsonMod gestolpert bin.

Wie kann ich denn wohl aus dem "nicht konformen Zeichensalat" meine Readings generieren ?

Habt ihr noch einen Tipp zum Anstoß ?

Vielen Dank an euch für eure Hilfe !

betateilchen

Warum kannst Du das python Skript nicht ändern?

Woher stammt das skript und was tut das eigentlich?
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

Stargazer

Guten Morgen,

da hole ich kurz einmal etwas weiter aus. Die Funktionsweise sieht so aus, dass ich eine Anfrage per Python–Skript an einen Server sende. Dieser soll mir dann ein Ergebnis einiger Bitcoin–Kursdaten zurück geben. Wenn ich das Abfrageskript starte, ,,baut" er die Anfrage mit verschieden Variablen in dem Pythonskript zusammen und sendet diese zu dem besagten Server. Und dieser sendet dann die obige Rückantwort. Diese wird dann 1:1 in eine Datei gespeichert, die FHEM als Datengrundlage nutzen soll. Die Serverantwort kann ich dann halt nur manuell ändern. Aber das würde für mich dann nur wenig Sinn machen. Viel einfacher wäre es natürlich, wenn ich eine direkte Abfrage per HTTPMOD oder JsonMod per Webadresse starten könnte.

Viele Grüße

André

CoolTux

Zeig doch mal das Skript wenn es möglich ist. Das geht sicherlich auch mit HTTPMOD oder man erweitert das Skript so das es gleich an FHEM sendet und nicht das FHEM da "abholen" muss.
Du musst nicht wissen wie es geht! Du musst nur wissen wo es steht, wie es geht.
Support me to buy new test hardware for development: https://www.paypal.com/paypalme/MOldenburg
My FHEM Git: https://git.cooltux.net/FHEM/
Das TuxNet Wiki:
https://www.cooltux.net

Stargazer

#8
Hi CoolTux,

Hier mal das Skript was eigentlich drei Funktionen kann. Ich nutze aber nur die 'getPositions'.

#coding: utf-8

import urllib.request
import json
import base64
import hmac
import time

APIURL = "https://api-swap-rest.bingbon.pro"
APIKEY = "Set your api key here !!"
SECRETKEY = "Set your secret key here!!"

def genSignature(path, method, paramsMap):
    sortedKeys = sorted(paramsMap)
    paramsStr = "&".join(["%s=%s" % (x, paramsMap[x]) for x in sortedKeys])
    paramsStr = method + path + paramsStr
    return hmac.new(SECRETKEY.encode("utf-8"), paramsStr.encode("utf-8"), digestmod="sha256").digest()

def post(url, body):
    req = urllib.request.Request(url, data=body.encode("utf-8"), headers={'User-Agent': 'Mozilla/5.0'})
    return urllib.request.urlopen(req).read()

def getBalance():
    paramsMap = {
        "apiKey": APIKEY,
        "timestamp": int(time.time()*1000),
        "currency": "USDT",
    }
    sortedKeys = sorted(paramsMap)
    paramsStr = "&".join(["%s=%s" % (x, paramsMap[x]) for x in sortedKeys])
    paramsStr += "&sign=" + urllib.parse.quote(base64.b64encode(genSignature("/api/v1/user/getBalance", "POST", paramsMap)))
    url = "%s/api/v1/user/getBalance" % APIURL
    return post(url, paramsStr)

def getPositions(symbol):
    paramsMap = {
        "symbol": symbol,
        "apiKey": APIKEY,
        "timestamp": int(time.time()*1000),
    }
    sortedKeys = sorted(paramsMap)
    paramsStr = "&".join(["%s=%s" % (x, paramsMap[x]) for x in sortedKeys])
    paramsStr += "&sign=" + urllib.parse.quote(base64.b64encode(genSignature("/api/v1/user/getPositions", "POST", paramsMap)))
    url = "%s/api/v1/user/getPositions" % APIURL
    return post(url, paramsStr)

def placeOrder(symbol, side, price, volume, tradeType, action):
    paramsMap = {
        "symbol": symbol,
        "apiKey": APIKEY,
        "side": side,
        "entrustPrice": price,
        "entrustVolume": volume,
        "tradeType": tradeType,
        "action": action,
        "timestamp": int(time.time()*1000),
    }
    sortedKeys = sorted(paramsMap)
    paramsStr = "&".join(["%s=%s" % (x, paramsMap[x]) for x in sortedKeys])
    paramsStr += "&sign=" + urllib.parse.quote(base64.b64encode(genSignature("/api/v1/user/trade", "POST", paramsMap)))
    url = "%s/api/v1/user/trade" % APIURL
    return post(url, paramsStr)

def main():
    print("getBalance:", getBalance())

    print("placeOpenOrder:", placeOrder("BTC-USDT", "Bid", 0, 0.0004, "Market", "Open"))

    print("getPositions:", getPositions("BTC-USDT"))

    print("placeCloseOrder:", placeOrder("BTC-USDT", "Ask", 0, 0.0004, "Market", "Close"))

if __name__ == "__main__":
    main()


Weiß nicht, ob man da was umbauen kann ?



VG

betateilchen

Da ist nix geheimnisvolles dabei, und das ist so simpel, dass man nichtmal HTTPMOD oder JsonMod dazu braucht.
Das geht problemlos als Funktion in der 99_myUtils.pm.

Die URL zusammenbasteln und mit FHEM Bordmitteln aufrufen,
das Ergebnis mit json2nameValue() oder json2reading() verarbeiten, dann kommt sowas raus:


$VAR1 = {
          'getPositions_data_positions_1_leverage' => '1',
          'getPositions_data_positions_1_symbol' => 'BTC-USDT',
          'getPositions_data_positions_1_volume' => '0.0',
          'getPositions_data_positions_1_marginMode' => 'Isolated',
          'getPositions_data_positions_1_margin' => 'X.4454',
          'getPositions_data_positions_1_pnlRate' => '-0.08',
          'getPositions_data_positions_1_positionId' => '16106910628XXXXXXXXX',
          'getPositions_ttl' => '1',
          'getPositions_code' => '0',
          'getPositions_data_positions_1_positionSide' => 'Long',
          'getPositions_data_positions_1_liquidatedPrice' => 'XX.X',
          'getPositions_data_positions_1_unrealisedPNL' => '-0.0075',
          'getPositions_msg' => '',
          'getPositions_data_positions_1_realisedPNL' => '-0.0034',
          'getPositions_data_positions_1_avgPrice' => '16906',
          'getPositions_data_positions_1_currency' => '',
          'getPositions_data_positions_1_availableVolume' => '0.0'
        };


Was ich immer noch nicht weiß: Was kommt als Ergebnis wirklich aus dem Skript?
Der json-input im ersten Beitrag wurde derartig verhunzt, dass ich einfach nicht glaube, dass sowas aus einem API-Abruf als Ergebnis kommt.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

Stargazer

Hallo zusammen,

ZitatDer json-input im ersten Beitrag wurde derartig verhunzt, dass ich einfach nicht glaube, dass sowas aus einem API-Abruf als Ergebnis kommt.

Doch. Ist leider so. Sonst hätte ich ihn auch leichter dekodiert bekommen. Habe es nochmal gegen gecheckt, indem ich den Aufruf nochmals von Hand in die Konsole getippert habe und die Antwort, die ich dann vom Server bekomme ist absolut identisch mit der, die ich oben gepostet habe. Nur habe ich die private Keys "verstümmelt". Mehr aber nicht.

Mit deinem Ergebnis ist ja schon genial. Da muss ich mich dann wohl noch weiter einarbeiten...

Bis hierhin schonmal ein herzliches Dankeschön für die Hilfestellung !


betateilchen

Ok, nehmen wir mal an, der Teil am Anfang "getPositions: b'" gehört gar nicht zum JSON selbst, sondern ist einfach für die Identifikation des Datensatzes notwendig. Dann müssen wir das inklusive des einfachen Anführungszeichen wegregexen.

$json =~ s/^getPositions.*{/{/;

Dann vernichten wir noch das einfache Anführungszeichen ganz am Ende, damit passen dann auch die geschweiften Klammern.

$json =~ s/'$//;

Und schon bleibt ein korrekter JSON Inhalt übrig.

Probier mal folgendes in der 99_myUtils.pm:


use Data::Dumper;
sub test {
  my ($err,@input) = FileRead({FileName=>'/tmp/test.input', ForceType=>'file'});
  return $err if $err;
  my $json = join ("",@input);
  $json =~ s/^getPositions.*{/{/;
  $json =~ s/'$//;
  return Dumper json2nameValue($json);
}


Den FileName musst Du natürlich auf Deinen Dateinamen ändern.

Bei mir kommt dann das als Ergebnis:


$VAR1 = {
          'currency' => '',
          'unrealisedPNL' => '-0.0075',
          'positionId' => '16106910628XXXXXXXXX',
          'symbol' => 'BTC-USDT',
          'leverage' => '1',
          'avgPrice' => '16906',
          'pnlRate' => '-0.08',
          'marginMode' => 'Isolated',
          'availableVolume' => '0.0003',
          'volume' => '0.0002',
          'realisedPNL' => '-0.0034',
          'liquidatedPrice' => '23.4',
          'positionSide' => 'Long',
          'margin' => '0.4454'
        };
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

Stargazer

Hallo betateilchen,

jau ! Das geht schon genau so, wie du geschrieben hast. Habe das gleiche Ergebnis !  :D.

Jetzt bleibt halt nur die Frage, wie ich die Readings in einen dummy oder so bekomme ?

Ich habe vor Ur-Zeiten mal einmal ein paar Antworten für einen Telegram-Bot in der 99_myUtils abgelegt. Mehr habe ich damit (leider)
noch nicht machen müssen. Aber ich glaube, ich habe es mir dadurch in der Vergangenheit schwerer gemacht, als ich hätte müssen... ???

VG

betateilchen

Zitat von: Stargazer am 06 Januar 2023, 19:24:22
Jetzt bleibt halt nur die Frage, wie ich die Readings in einen dummy oder so bekomme ?

Dazu musst Du nur die Zeile mit dem return austauschen:

  return json2reading('testDummy',$json);

'testDummy' ist der Name eines device, in dem die readings gesetzt werden sollen.
Das kann natürlich auch eine Variable sein, in der ein Name steckt.
-----------------------
Formuliere die Aufgabe möglichst einfach und
setze die Lösung richtig um - dann wird es auch funktionieren.
-----------------------
Lesen gefährdet die Unwissenheit!

Stargazer

Hallo betateilchen,

vielen Dank an dich. Es läuft absolut perfekt.

da werde ich mich wohl noch des öfteren mit der 99_myUtils beschäftigen müssen  ;) :D.

Viele Grüße und einen schönen Abend...