IP-Symcon - JSON RPC Schnittstelle

Ab IP-Symcon Version 3.0 gibt es eine native JSON-RPC Schnittstelle!

#################################################################

In diesem Thread veröffentliche ich immer den aktuellsten Fortschritt der IP-Symcon JSON Schnittstelle.

Pfad zur Datei: Die PHP-Datei sollte sich im root WebFront-Verzeichnis von IP-Symcon befinden. (Beispiel: I:\IP-Symcon\webfront).

Name der Datei: jsonrpc.php

Versionen:

[ul]
[li]1.0 - Erste Beta der Schnittstelle[/li][li]1.1[/li][LIST]
[li]Einige Bugfixes, korrektes plain-Value und array handling[/li][li]Sicherheitsmechanismen eingebaut, um ausschließlich IP-Symcon Funktionen aufzurufen.[/li][/ul]

[li]1.2[/li][ul]
[li]Funktion hinzugefügt, die „Objekte“ nach einer angegebenen parentID zurückgibt.[/li][li]UTF8-Handling eingebaut. json_encode erlaubt nur UTF8 formatierte Strings.[/li][/ul]

[li]1.3 - Methoden hinzufügt, die alle OBJEKTE und nicht nur die IDs zurückgeben. Besonders hilfreich, um einen DatenCache [/li]anzulegen.
[li]1.4 - Verändert und die Anforderungen der „JSON-RPC Working Group <json-rpc(at)googlegroups.com>“ zu erfüllen.[/li][li]1.5 - Funktion „isAPIReady“ hinzugefügt. Überprüft ob die JSON-RPC Schnittstelle „bereit“ ist, RPC Anfragen zu beantworten.[/li][li]1.6 - Es wird nun eine Überprüfung durchgeführt, ob benötigte JSON Funktionen von PHP bereitgestellt werden. Sollte das nicht der Fall sein, wird ein ERROR-Objekte ausgegeben.[/li][li]1.7 - Danke an pelota - Defects in createErrorObj und der postdata-Variable wurden durch ihn erkannt und im Source gefixt.[/li][/LIST]

Nutzung:
REQUEST Anforderungen und REPLY Beschreibungen: JSON-RPC 2.0 - JSON-RPC | Google Groups

Feedback ist Erwünscht, um die Schnittstelle möglichst nach allen Anforderungen auszurichten!

Aktueller code:


<?php
/*
 * IP-Symcon JSON RPC 1.7
 *
 * JSON-RPC 2.0 Specification
 * http://groups.google.com/group/json-rpc/web/json-rpc-2-0
 */

ini_set('display_errors', 1);
ini_set('error_reporting', E_ERROR);

// Check for existing json_encode/json_decode functions.
// are available.
if ( !function_exists('json_encode') || !function_exists('json_decode') )
{
   echo "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":\"-32603\",\"message\":\"JSON RPC not available\",\"data\":\"SJON PHP functions are not available.\"},\"id\":\"Null\"}";
   exit;
}

$postdata = $HTTP_RAW_POST_DATA;
if(empty($postdata)) {
    $postdata = $_SERVER['QUERY_STRING'];
}
$request = json_decode($postdata);

// Check if we got a bad request.
if ( $request == null )
{
   echo createErrorObj("Null", "-32700", "Invalid request", "Invalid header or postdata.");
   exit;
}

// Check if we got an invalid request object.
if (! isset($request->{'jsonrpc'}) ||
    ! isset($request->{'method'}) ||
    ! isset($request->{'params'}) ||
    ! isset($request->{'id'}) )
{
   echo createErrorObj("Null", "-32700", "Invalid request", "Invalid request object.");
   exit;
}

// Make a security check. Only IPS functions are allowed.
if (! preg_match("/^IPS_/", $request->{'method'}) )
{
   if ( $request->{'method'} != "isAPIReady" &&
        $request->{'method'} != "GetValue" &&
        $request->{'method'} != "GetValueFormatted" &&
        $request->{'method'} != "GetValueBoolean" &&
        $request->{'method'} != "GetValueInteger" &&
        $request->{'method'} != "GetValueFloat" &&
        $request->{'method'} != "GetValueString" &&
        $request->{'method'} != "SetValue" &&
        $request->{'method'} != "SetValueBoolean" &&
        $request->{'method'} != "SetValueInteger" &&
        $request->{'method'} != "SetValueFloat" &&
        $request->{'method'} != "SetValueString" )
   {
      echo createErrorObj($request->{'id'}, "-32601", "Method not found", "Only IP-Symcon methods are allowed for JSON-RPC.");
      exit;
   }
}

// Check if the given method is callable.
if ( is_callable($request->{'method'}, false, $functionname) )
{
   $result = call_user_func_array($functionname, $request->{'params'});
   echo createReturnObj("0", $request->{'id'}, $result);
} else
{
      echo createErrorObj($request->{'id'}, "-32601", "Method not found", "The method does not exist / is not available.");
      exit;
}

/*
 * Method to check if the API and/or IP-Symcon server is ready to
 * serve requests.
 */
function isAPIReady()
{
   return true;
}

/*
 * IPS function to return OBJECTS by a given parentID as json-objects.
 */
function IPS_GetObjectChilds($id)
{
   $idArr = IPS_GetChildrenIDs($id);
   $returnArr = array();

   foreach ( $idArr as $tmpid )
   {
      $obj = IPS_GetObject($tmpid);
      array_push($returnArr, $obj);
   }
   return $returnArr;
}

/*
 * IPS function to return -ALL- OBJECTS as json-objects.
 */
function IPS_GetAllObjects()
{
   $idArr = IPS_GetObjectList();
   $returnArr = array();

   foreach ( $idArr as $tmpid )
   {
      $obj = IPS_GetObject($tmpid);
      array_push($returnArr, $obj);
   }
   return $returnArr;
}

/*
 * IPS function to return -ALL- INSTANCES as json-objects.
 */
function IPS_GetAllInstances()
{
   $idArr = IPS_GetInstanceList();
   $returnArr = array();

   foreach ( $idArr as $tmpid )
   {
      $obj = IPS_GetInstance($tmpid);
      array_push($returnArr, $obj);
   }
   return $returnArr;
}

/*
 * IPS function to return -ALL- VARIABLES as json-objects.
 */
function IPS_GetAllVariables()
{
   $idArr = IPS_GetVariableList();
   $returnArr = array();

   foreach ( $idArr as $tmpid )
   {
      $obj = IPS_GetVariable($tmpid);
      array_push($returnArr, $obj);
   }
   return $returnArr;
}

/*
 * IPS function to return -ALL- VARIABLE PROFILES as json-objects.
 */
function IPS_GetAllVariableProfiles()
{
   $idArr = IPS_GetVariableProfileList();
   $returnArr = array();

   foreach ( $idArr as $tmpid )
   {
      $obj = IPS_GetVariableProfile($tmpid);
      array_push($returnArr, $obj);
   }
   return $returnArr;
}

/*
 * IPS function to return -ALL- SCRIPTS as json-objects.
 */
function IPS_GetAllScripts()
{
   $idArr = IPS_GetScriptList();
   $returnArr = array();

   foreach ( $idArr as $tmpid )
   {
      $obj = IPS_GetScript($tmpid);
      array_push($returnArr, $obj);
   }
   return $returnArr;
}

/*
 * IPS function to return -ALL- MEDIA as json-objects.
 */
function IPS_GetAllMedia()
{
   $idArr = IPS_GetMediaList();
   $returnArr = array();

   foreach ( $idArr as $tmpid )
   {
      $obj = IPS_GetMedia($tmpid);
      array_push($returnArr, $obj);
   }
   return $returnArr;
}

/*
 * IPS function to return -ALL- LINKS as json-objects.
 */
function IPS_GetAllLinks()
{
   $idArr = IPS_GetLinkList();
   $returnArr = array();

   foreach ( $idArr as $tmpid )
   {
      $obj = IPS_GetLink($tmpid);
      array_push($returnArr, $obj);
   }
   return $returnArr;
}

/*
 * IPS function to return -ALL- EVENTS as json-objects.
 */
function IPS_GetAllEvents()
{
   $idArr = IPS_GetEventList();
   $returnArr = array();

   foreach ( $idArr as $tmpid )
   {
      $obj = IPS_GetEvent($tmpid);
      array_push($returnArr, $obj);
   }
   return $returnArr;
}

/****************************************************************************
 * INTERNAL FUNCTIONS WHICH ARE NEEDED FOR JSON-RPC OPERATIONS
 ****************************************************************************/

/*
 * json_encode() can only handle utf8 strings. So we have to convert
 * existing strings.
 *
 * @param &item REFERENCE to the item.
 * @param key   The KEY of the item.
 */
function utf8_conv(&$item, $key)
{
   if ( is_string($item) )
      $item = utf8_encode($item);
}

/*
 * Creates a JSON error object, which can be used within the return object.
 *
 * @param id      The id of the request object.
 * @param code    A Number that indicates the actual error that occurred.
 *                This MUST be an integer.
 * @param message A String providing a short description of the error.
 *                The message SHOULD be limited to a concise single sentence.
 * @param data    Additional information, may be omitted. Its contents is entirely
 *                defined by the application (e.g. detailed error
 *                information, nested errors etc.).
 */
function createErrorObj($id, $code, $message, $data)
{
   $arr = array(
      "code"    => $code,
      "message" => $message,
      "data"    => $data
   );

   // If we got a parse error, the id has to be "Null" (JSON-RPC RFC).
   if ( $code == "-32700" )
      $id = "Null";

   return createReturnObj("1", $id, $arr);
}

/*
 * Creates a JSON return object, which will be returned to the sender.
 *
 * @param mode 0 = successfull return, 1 error return.
 * @param id   ID of the request object.
 * @param obj  JSON object which will be returned.
 */
function createReturnObj($mode, $id, $obj)
{
   // Version of this JSON-RPC object.
   $jsonrpc = "2.0";

   // First we have to check, if it's a error/standard object.
   if ( $mode == "0" )
   {
      $arr = array(
         "jsonrpc" => $jsonrpc,
         "result"  => $obj,
         "id"      => $id
      );
   } else
   {
      $arr = array(
         "jsonrpc" => $jsonrpc,
         "error"   => $obj,
         "id"      => $id
      );
   }

   array_walk_recursive($arr, 'utf8_conv');
   return json_encode($arr);
}
?>

WICHTIG!

Letztes Update auf Version 1.4 beachten…

Um 100% JSON-RPC konform zu sein, mußten einige Felder umbenannt und das ERROR Objekt verändert werden.

JSON-RPC 2.0 Spezifikationen: http://groups.google.com/group/json-rpc/web/json-rpc-2-0

Viele Grüße
Sascha

So habs nun alles hinbekommen, einzig frage ich mich wenn ich in IP Symcon Ordner habe wo ich meine Werte aufgelistet habe warum ich hier wieder pro Variable einen Ordner brauche, da funktioniert die retromaske einfacher

lg

Bim

Kann man über die setValueFloat-Methode einen Wert in IPS setzen?

Probiere ich folgendes:

curl --data-binary „{„jsonrpc“: „2.0“, „id“:„1“, „method“: „SetValueFloat“, „params“: [12831 , 0.3] }“ http://192.168.178.13/jsonrpc.php

Der Parameter Wert 12831 ist die Variable LEVEL meines Homematic-Dimmers, 0.3 der Dimm-Wert von 30%.

Erhalte ich:
2011-11-02 20:27:27.909 Warning: Variable wurde als „Nur-Lesen“ markiert und kann nicht ver###ert werden

Hab ich einen Denkfehler, oder was müsste ich tun, um von aussen per Json meinen Dimmer zu steuern?

BG Johannes

Du musst das Skript um eine Funktion erweitern, die die HM_* Befehle aufruft. Eine StatusVariable zu verändern war noch nie möglich :slight_smile:

paresy

Für alle zur Info:

Ich arbeite gerade die json_rpc Schnittstelle auf, so dass zukünftig auch JSONP-Requests möglich sind. Das ist wichtig um sog. CrossDomain-Requests durchführen zu können.

Über „normal“ JSON werden diese über Domains hinweg vom Browser abgelehnt.

PS: Eventuell sollte man über ein Merge mit ios.php nachdenken und die Schnittstelle „allgemeiner“ bezeichnen :wink:

Hallo,

nach der Umstellung auf 2.5 funktioniert bei mir leider der Zugriff über JSON nicht mehr.

Bereits herausfinden konnte ich, dass nach der Zuweisung

$postdata = $GLOBALS['QUERY_STRING'];

die Variable $postdata leer ist.

Somit liefert der Aufruf


curl --data-binary "{\"jsonrpc\": \"2.0\", \"id\":\"1\", \"method\": \"isAPIReady\", \"params\": [] }" http://shuttle:82/jsonrpc.php

immer

„{„jsonrpc“:„2.0“,„error“:{„code“:“-32700",„message“:„Invalid request“,„data“:„Invalid header or postdata.“},„id“:„Null“}"

Was kann die Ursache sein?

Viele Grüße

bumaas


Nachtrag:

Nach der Umstellung auf

$postdata = $_SERVER['QUERY_STRING'];

funktioniert es wieder.:slight_smile:

Hallo zusammen

Die Lösung von bumaas scheint für curl zu funktionieren, aber nicht für standard HTTP POST Requests.

curl scheint die HTTP POST Daten in der URL zu übertragen, so wie ein HTTP GET, anstatt im Body des Requests, daher wird von PHP die $_SERVER[‚QUERY_STRING‘] populiert.

Nutzt man hingegen die Daten im Body, so wie ich aus Java heraus, so ist logischerweise der Quert String leer und somit auch die obige Variable.

Die Lösung is demnach, beides zu überprüfen:


...
$postdata = $HTTP_RAW_POST_DATA;
if(empty($postdata)) {
	$postdata = $_SERVER['QUERY_STRING'];
}
$request = json_decode($postdata);
...

Des weiteren scheint sich ein Fehler in die Methode createErrorObj eingeschlichen zu haben, die Funktion json_encode wird 2x aufgerufen, einmal im Aufruf der createReurnObj-Methode und einmal im return. Das führt zu ungültigem JSON.

Hier die gefixte Methode:

function createErrorObj($id, $code, $message, $data)
{
   $arr = array(
      "code"    => $code,
      "message" => $message,
      "data"    => $data
   ); 

   // If we got a parse error, the id has to be "Null" (JSON-RPC RFC).
   if ( $code == "-32700" )
      $id = "Null";

   return createReturnObj("1", $id, $arr);
}

Grüsse
pelota

Source habe ich eben gefixt. Sorry, hat etwas länger als gewöhnlich gedauert :slight_smile:

Ich frage mich, ob es nicht möglich ist das json_rpc-Modul generell in die IPS-Distri aufzunehmen?

Ist es zutreffend, dass IPS_GetVariableIDByName von der JSON RPC Schnittstelle nicht unterstützt wird?

Es sollte ohne Probleme unterstützt werden. Wie sieht denn dein Aufruf aus?

paresy

Stimmt, es funktioniert z.B. so:


function getId()
	{
	ips.IPS_GetObjectIDByName("DALI",0).then
		(
		function (data)
			{
			var DALIId = data;
			console.log("DALI id is "+DALIId);
			ips.IPS_GetVariableIDByName("DaliSetByEnocean",DALIId).then
				(
				function (data)
					{
					var DaliSetByEnoceanId = data;
					console.log("DaliSetByEnocean id is "+DaliSetByEnoceanId);
					}
				)
			}
		)
	}


Zuerst wird die DALI Module Id abgefragt und dananch die Variable Id von DaliSetByEnocean.

hmmm…schon beim includieren der jsonrpc.php bekomme ich folgenden Fehler

{"jsonrpc":"2.0","error":{"code":"-32700","message":"Invalid request","data":"Invalid header or postdata."},"id":"Null"}

Die Lösung von pelota habe ich schon drin, ändert aber nichts. Ansonsten: IPS 2.6 #2497.

Ich benutze das Skript von der ersten Seite.

Kann einer helfen ?

Gruß
Martin

Warum würdest du es inkludieren wollen? Du darfst es nur über den Web Server aufrufen.

paresy

offensichtlich ein cognitives Problem: Ich nutze im Script json_decode - wenn ich es nicht includiere, findet er keinen wrapper.

Warning:  file_get_contents(): Unable to find the wrapper "https" - did you forget to enable it when you configured PHP? in C:\IP-Symcon\scripts\41190.ips.php on line 5

Da dachte ich mir: json includieren !

Offensichtlich falsch…wenn ich mir die Fehlermeldung genau anschaue, geht es wohl auch eher um https… :smiley:

cognitives,

gibst da ach wat deutsches für?

Denken (im umfassenden Sinn), Verständnis

http://www.gutefrage.net/frage/kann-mir-jemand-erklaeren-was-kognitive-defizite-bedeutet

Wann wird es denn möglich sein, dass IP-Symcon Variablenaktualisierungen automatisch an die JSON RPC Schnittstelle weitergibt? Dies wäre natürlich eine viel elegantere Lösung, als andauernd die Werte aktualisieren zu lassen.

Beste Grüße

Mal so allgemein gefragt, was bringt die JSON Schnittstelle???
Ich aktualisiere auf meiner eigenen Seite per BodyOnload und XMLHttpRequest();…
Wenn ich aber mehrere Request habe werden meine Seiten extrem langsam. Wird es durch das JSON besser?

Gruß
Martin