externer Zugriff Javascript

Hallo zusammen,

komme aktuell nicht weiter und brauche bitte mal einen Anschub. Ich möchte aus einer externen Anwendung per Javascript Daten abfragen . Das Ganze läuft im internen Netz ohne Passwort für API.
Die ID der Variablen ist nur ein Beispiel zum testen.


  <script type="text/javascript">
 getData: function() {

  		var urlApi = "https://xxx.xxx.xxx.xxx:3777/api/";
		
	    var HTTP = new XMLHttpRequest();
	    HTTP.open("GET",urlApi,true);
	    var rpc = JSON.stringify({'"jsonrpc":"2.0", "method":"GetValue", "params":"18697", "id":0});
   		HTTP.setRequestHeader("Content-type", "application/json");
   		HTTP.send(rpc);
  }
  </script>

Bin ich grundsätzlich auf dem richtigen Weg oder völlig falsch ? In der Testumgebung bekomme ich kein
Ergebnis angezeigt und kann das nicht richtig debuggen.

Wäre super wenn mir hier jemand helfen könnte.

Danke Thomas

Hallo,
nach intensiver Suche in diversen Foren und vor allem hier, habe ich folgende Lösung im Einsatz.
Der Rahmen ist aus diesem Forum, die Sensoren habe ich eingefügt.
Matthias


#!/usr/bin/python
# -*- coding: latin-1 -*-

from MCP3008 import MCP3008
import RPi.GPIO as GPIO  
import requests
import json
from requests.auth import HTTPBasicAuth
import time
import Adafruit_DHT

#####################################################################
#
# I2C vorbereiten
# wenn keine I2C-Sensoren im Einsatz, dann kommentieren
#
#####################################################################

# I2C-Daten
import Adafruit_GPIO.I2C as I2C
from time import sleep, strftime
from datetime import datetime
deviceAddr = 0x20 # fuer Chirp-Bodenfeuchtesensor

i2c = I2C.Device( deviceAddr, -1, False )
i2c = I2C.Device(deviceAddr,1)

#####################################################################
#
# JSON-RPC zu IPS definieren
#
#####################################################################

def IPSrpc(methodIps, paramIps):
    url = "http://192.168.xxx.yyy:3777/api/" # IP-Adresse des Symcon-Servers und Port
    auth=HTTPBasicAuth('emailaddresse', 'symconpassword') # Zugangsdaten
    headers = {'content-type': 'application/json'}

    payload = {
        "method": methodIps,
        "params": paramIps,
        "jsonrpc": "2.0",
        "id": "0",
    }
    response = requests.post(url, auth=auth, data=json.dumps(payload), headers=headers).json


try:
    while True:
	start_time = time.time()

        #############################################################
        #
        # AM2302 auslesen
        # GPIO17
        #
        #############################################################

        sensor = Adafruit_DHT.AM2302
        humidity, temperature = Adafruit_DHT.read_retry(sensor, 17)
        print("Lufttemperatur   : %.2f" % temperature)
        print("Luftfeuchtigkeit : %.2f" % humidity)

       # Variable z.B. 29863 in Symcon anlegen und hier eintragen

        IPSrpc("SetValue", [29863, temperature])
        IPSrpc("SetValue", [15622, humidity])

        #############################################################
        #
        # VH400 auslesen
        # GPIO08.09.10.11
        #
        #############################################################
        
        adc = MCP3008()
        value = adc.read( channel = 0 ) # auszulesender Channel
        v400u = ( value / 1023.0 * 3.3 )
        print("Anliegende Spannung  : %.2f" % v400u)
        v400f = ( value / 2.77 / 1023.0 * 3.3 * 100 )
        print("Substratfeuchtigkeit : %.2f" % v400f)
        vwc=(11.6552* v400u **4+7.10835* v400u **2-0.569557)/( v400u **2 + 1)
        print("volumetrische Bodenfeuchte : %.2f" % vwc)

        IPSrpc("SetValue", [46226, v400u])
        IPSrpc("SetValue", [38013, v400f])
        IPSrpc("SetValue", [54482, vwc])

        #############################################################
        #
        # DS18B20 auslesen
        # GPIO04
        #
        #############################################################

    		# 1-wire Slave Datei lesen Sensor_28-031655b44fff
    	file = open('/sys/bus/w1/devices/28-031655b44fff/w1_slave')
    	filecontent = file.read()
    	file.close()
	    	# Temperaturwerte auslesen und konvertieren
    	stringvalue = filecontent.split("
")[1].split(" ")[9]
    	tempds = float(stringvalue[2:]) / 1000

    		# Temperatur ausgeben
      	print("Bodentemperatur : %.2f" % tempds)

        IPSrpc("SetValue", [30772, tempds])

        #############################################################
        #
        # I2C Chirp auslesen
		# GPIO02.03
        #
        #############################################################

        	# reset sensor
        i2c.write8( deviceAddr, 0x06 )
        sleep(5)

			# Helligkeit lesen
		i2c.write8(deviceAddr, 3)
		sleep(3)
		light = i2c.readU16(4, False)

			# Temperatur lesen
		temp = i2c.readS16(5, False)/float(10)

			# Bodenfeuchte lesen
		moisture = i2c.readU16(0, False)

			# Berechnungen
		print "Chirp Temperature	Moisture	Brightness"
		print str(temp) + "	" + str(moisture) + "	" + str(light)

		IPSrpc("SetValue", [45574, temp])
		IPSrpc("SetValue", [44157, moisture])
		IPSrpc("SetValue", [19004, light])

        #############################################################
 
	end_time = time.time()
#        delay = float(end_time - start_time)
#        time.sleep(10 - delay)
	time.sleep(10)

except KeyboardInterrupt:
    GPIO.cleanup()

GPIO.cleanup()           # clean up GPIO on normal exit


Besten Dank. Ich muss mich in das Thema erst einmal einarbeiten und suche noch eine vernünftige Testumgebung für Javascript. Mit CURL kann ich von dem entfernten Rechner die Daten auch ohne User / PW abgreifen, aber aus Java heraus bekomm ich es irgendwie nicht hin. Naja zurück an die Schulbank und lernen. Wahrscheinlich ist die Syntax noch irgendwie fehlerhaft .

Thomas

bin etwas weiter , aber noch eine Frage ggf. an Symcon :

das ist der Ausschnitt Javascript . Die Antwortbehandlung hab ich mal rausgenommen.
Ich bekomme aber trotzdem keine Antwort von IPS , ob mit oder ohne Passwort ( getestet an
2 IPS Instanzen ) . Die Curl Abfrage auf der Console des Rechners, auf welchem Java läuft funktioniert,
aber im Java bekomm ich keine Antwort . Muss ich den XMLHttpRequest synchron oder asynchron ausführen ?



 getData = function() {

          var urlApi = "https://xxx.xxx.xxx.xxx:3777/api/";
        
        var HTTP = new XMLHttpRequest();
        HTTP.open("POST",urlApi,true);
        var rpc = JSON.stringify({"jsonrpc":"2.0", "method":"GetValue", "params":[18697], "id":0});
           HTTP.setRequestHeader("Content-type", "application/json");
           HTTP.send(rpc);
  }
  </script>  

Thomas

Im JSON.stringify steckt vor dem „jsonrpc“-String noch ein einzelnes '. Dadurch wird der gesamte Rest der Datei als String gewertet. Ich weiß jetzt nicht genau wie tolerant Javascript damit umgeht, aber das solltest du wohl rausnehmen, da sonst die folgenden Funktionsaufrufe wohl nicht ausgeführt werden.

Wäre zu schön gewesen, wenn es das Problem gelöst hatte, das zusätzliche Zeichen hatte sich nur hier im Forum
eingeschlichen, im org. Script ist das nicht drin.

Naja ich werde die Response nochmals auswerten und schauen was passiert . Eine JSON Abfrage einer externen Webseite, die allerdings klassisch XML liefert funktioniert , nur hier bekomm ich irgendwie keine Antwort

Ich such mal weiter .

Ich arbeite auch gerade an Javascript in Verbindung mit der JSON-RPC Schnittstelle. Habe mich bereits eingelesen und so ziemlich jeden Thread überflogen, der irgendwie mit der JSON-RPC Schnitstelle zu tun hat. Im Internet-Explorer läuft es super (Licht wird geschaltet!), Chrome und Firefox werfen Fehler aus. Der nachfolgende Code und die Webseite befinden sich dabei nicht auf dem IP-Symcon Server und werden auch nicht über eine HTMLBox gestartet. Ziel ist es ja, das ganze von extern zu steuern.

Folgender Code:


function switchLight(value) {
                                var url = "http://192.168.2.12:3777/api/";
				var username = "email@provider.com";
				var password = "password";
				
				var HTTP = new XMLHttpRequest();
				HTTP.open("POST", url, true);
				HTTP.onreadystatechange = function () {
				HTTP.setRequestHeader('Content-type', 'text/plain; charset=utf-8');
				HTTP.setRequestHeader('Authorization', 'Basic ' + btoa(username+':'+password));
				
				data = {
					'jsonrpc' : '2.0',
					'id' : 0,
					'method' : 'HM_WriteValueBoolean',
					'params' : [42301, 'STATE', value],
				}
				var rpc = JSON.stringify(data);
				HTTP.send(rpc);
}

Funktioniert in Internet-Explorer (getested auf 2 Laptops), aber NICHT in Chrome und Firefox.
Chrome wirft folgenden Fehler:

OPTIONS http://192.168.2.12:3777/api/ net::ERR_EMPTY_RESPONSE

Durch Nachforschungen habe ich festgestellt, dass es sich bei „OPTIONS“ um eine Preflight Anfrage an den Symcon Server handelt, welche einige Browser vor der eigentlichen Payload abschicken um zu prüfen, ob der Server überhaupt Daten vom Client empfangen möchte. Diesen Preflight-Check kann man umgehen, indem man eine „einfache“ HTTP Anfrage an den Server schickt, also ohne „Authorization“ Header. Zudem muss der content-type auf „text/plain“ gestellt werden. Lasse ich also den Header mit Username und Password weg, so bekomme ich von Chrome zumindest den korrekten Fehlercode 401. Diese zeigt mir die fehlende Autorisierung an. Schicke ich den Header mit, kommt vom Symcon-Server keine Antwort (auf den Preflight-Check) und Chrome meldet mir einfach nur, dass der Request failed.

Hat jemand eine Idee, wie man das lösen kann und auf allen [gängigen] Browsern zum laufen bringt?

Grüße,
Andy

Ich würde dir empfehlen einfach einen Webhook zu implementieren, das macht es viel einfacher. Javascript und JSON ist keine bequeme Kombination.

//EDIT : Und für die weiter oben stehenden Beiträge Java<>Javascript …

Es scheint ja weniger das JSON zu sein, als vielmehr die Tatsache dass die Symcon Schnittstelle keine Antwort auf den Preflight sendet… was zumindest bei Chrome wohl abgefragt und erwartet wird. Aus Internet-Explorer heraus geht es soweit problemlos, viel komplexer soll das Script auch eigentlich nicht werden für meine Zwecke.

Die Frage wäre jetzt, ob der Fehler bei Chrome bekannt und reproduzierbar ist (also eventuell Problem bei der Symcon Schnittstelle), oder ob ich etwas vergessen habe. Hat denn jemand eine Javascrip Implementierung hier im Forum die ohne zusätzliche Libraries auch in Chrome funktioniert? Ich kann mir nur schwer vorstellen dass das sonst keiner in Javascript am laufen kann.

Ich habe Mr. Google kurz angeworfen, habe folgende gefunden, was anscheinend deinem Fehler entspricht:
Cross-Origin Resource Sharing – Wikipedia
javascript - Receiving net::ERR_EMPTY_RESPONSE with Nodejs Post Request - Stack Overflow

Im 2. Beitrag ist ein korrigierter Header den du übernehmen solltest, dann müsste es wohl gehen. Im Wiki-Eintrag steht warum es zum Fehler kommt.

So wie ich das sehe beziehen sich diese Header auf die Antwort welche vom Server gesendet werden, nicht auf den Client.

Die Preflight-Abfrage wird ja komplett vom Browser übernommen. Erhält dieser vom Server (Symcon) keine Antwort, so leitet er die eigentliche Anfrage aus Javascript garnicht erst an den Server.

Die CORS-Gesichte muss wohl auch Server-seitig implementiert sein. Für den Client ändert sich bei einer CORS Kommunikation eigentlich nichts, da auch hier der Browser die Header einfügt und auswertet.

Hat denn niemand ein funktionierendes Javascript, mit dem ich die JSON-RPC Schnittstelle nutzen kann, ohne dass es auf dem Symcon-Server läuft?

Hast Du es mal mit jquery probiert vielleicht macht diese Kombination ja keine Probleme mit Firefox.

JQuery werde ich jetzt bei Gelegenheit mal probieren.

Interessant ist aber folgendes: Wenn ich mein Script 1:1 in eine HTML-Box in IP-Symcon packe, dann kann ich auch in Chrome ohne Probleme schalten; identischer Code, gleiche Ziel-IP für die JSON Schnittstelle (nicht localhost). Also scheint es wirklich ein Problem mit der Authorization/CORS zu sein. Wenn ich den Code in der HTMLBox laufen lasse, ist es ja kein CORS, da ich ja vom gleichen Gerät aus die Schnittstelle anspreche.

Aber die JSON-Schnitstelle ist doch dafür gedacht, dass ich von extern (also nicht aus IP-Symcon heraus) auf das System zugreifen kann. Beispielsweise für eine eigene Visualisierung oder um Temperatur-Daten etc auszulesen. Oder versuche ich hier die Schnittstelle falsch einzusetzen?

Lies dir das mal durch: Same-Origin-Policy – Wikipedia

dann verstehtst du vielleicht was der eigentliche Fehler ist und wie CORS das „Problem“ beheben kann, dazu muss aber der Header richtig sein.

Ich denke das mit den SOP und CORS habe ich soweit verstanden. Aber muss denn nicht der Server (also Symcon) eine entsprechende Antwort mit Header liefern, die meinem Client ‚erlaubt‘ von einer anderen Origin (also nicht Symcon-Server) den Befehl auszuführen? Wie kann ich das mit einem Header, den der Client liefert, ändern?

Hat vielleicht jemand mal einen Beispielcode, der unter Chrome funktioniert? :stuck_out_tongue: