Parse Json Array

Ayant avancé sur l’intégration de mes petits senseurs CO2 dans Jeedom, je bute sur l’extraction des infos avec le plugin JQMTT. J’arrive à souscrir au Broker du constucteur et rapatrier les données qui se présentent sous un objet JSON de ce type:

{"type":"V","content":"[{\"content\":\"0\",\"dptId\":1,\"seq\":1,\"deviceToken\":\"xxxxxxxxxxxxxxxxxxxx"},{\"content\":null,\"dptId\":1,\"seq\":2,\"deviceToken\":\"\"},{\"content\":null,\"dptId\":1,\"seq\":3,\"deviceToken\":\"\"},{\"content\":\"13\",\"dptId\":1,\"seq\":4,\"deviceToken\":\"\"},{\"content\":\"49\",\"dptId\":1,\"seq\":5,\"deviceToken\":\"\"},{\"content\":\"0\",\"dptId\":1,\"seq\":6,\"deviceToken\":\"\"},{\"content\":\"0.068\",\"dptId\":1,\"seq\":7,\"deviceToken\":\"\"},{\"content\":\"0.500\",\"dptId\":1,\"seq\":8,\"deviceToken\":\"\"},{\"content\":\"1005\",\"dptId\":1,\"seq\":9,\"deviceToken\":\"\"},{\"content\":null,\"dptId\":1,\"seq\":10,\"deviceToken\":\"\"}]","deviceToken":"xxxxxxxxxxxxxxxxxxxx"}

Dans le Broker JMQTT, le parsing JSON ne va pas plus loin que le premier objet « Content » et éclate les données comme suit:
{type}
{deviceToken}
{content}

L’objet {content} est un array qui les valeurs que je voudrai extraire et se présente comme suit:

[{\"content\":\"0\",\"dptId\":1,\"seq\":1,\"deviceToken\":\"XXXXXXXXXXXXXXXXXX\"},{\"content\":null,\"dptId\":1,\"seq\":2,\"deviceToken\":\"\"},{\"content\":null,\"dptId\":1,\"seq\":3,\"deviceToken\":\"\"},{\"content\":\"13\",\"dptId\":1,\"seq\":4,\"deviceToken\":\"\"},{\"content\":\"49\",\"dptId\":1,\"seq\":5,\"deviceToken\":\"\"},{\"content\":\"0\",\"dptId\":1,\"seq\":6,\"deviceToken\":\"\"},{\"content\":\"0.082\",\"dptId\":1,\"seq\":7,\"deviceToken\":\"\"},{\"content\":\"0.517\",\"dptId\":1,\"seq\":8,\"deviceToken\":\"\"},{\"content\":\"1039\",\"dptId\":1,\"seq\":9,\"deviceToken\":\"\"},{\"content\":null,\"dptId\":1,\"seq\":10,\"deviceToken\":\"\"}]

Je voudrai extraire les valeurs des objets string « content » imbriqués dans l’array:

{« content »:« 13 »,« dptId »:1,« seq »:4,« deviceToken »:""},{« content »:« 49 »,« dptId »:1,« seq »:5,« deviceToken »:""},{« content »:« 0 »,« dptId »:1,« seq »:6,« deviceToken »:""},{« content »:« 0.082 »,« dptId »:1,« seq »:7,« deviceToken »:""},{« content »:« 0.517 »,« dptId »:1,« seq »:8,« deviceToken »:""},{« content »:« 1039 »,« dptId »:1,« seq »:9,« deviceToken »:""},

Est-ce possible directement dans la commande info ou est-ce trop demander à ce plugin très bien fait soit dit en passant.
Merci

Bonjour @nordix,
Le problème est que ta donnée ne respecte pas le standard JSON.
Tu peux le vérifier sur ce site par exemple : https://jsonformatter.org/.
Dans le détail, il y a 2 problèmes:

  • Les " ne devraient pas être précédées d’un \
  • Idem pour [ et ]

J’ai testé, en corrigeant ces problèmes, le plugin est capable d’interpréter les données jusqu’au niveau le plus bas.

Cordialement

Bonjour @domotruc, merci pour ta réponse rapide.
En fait les caracatères " et [ sont escapés selon le format UTF-8.
N’importe quel décodeur UTF-8 vers ASCII par exemple enlève tous les
https://mothereff.in/utf-8
Pas moyen de décoder ce payload avant de le traiter?
Merci encore

1 « J'aime »

Re-bonjour,
Je ne crois pas être d’accord avec toi. D’où tiens tu que l’encodage UTF-8 nécessite « d’escaper » ces caractères, il n’y a pas de raisons.
D’où proviennent tes senseurs de CO2?
Cordialement

En complément à ma réponse précédente, le plugin utilise la fonction PHP:json_decode pour décoder les payloads JSON, fonction qui suppose que la payload est encodée en UTF-8.

Je ne suis pas très chaud pour modifier le décodage des payloads à cause des risques d’effets de bord, d’autant que tu es le seul, à ma connaissance, à avoir des payloads JSON encodées de cette manière.

Comment as-tu obtenu la payload que tu as mises dans ton premier message?
Pour être sûr, pourrais-tu me passer le log du plugin en mode debug?

Bonsoir @nordix,
Tes capteurs publient directement sur le brocker ?
Est ce des capteurs DIY ??

Mes capteurs sont décrits dans ce topic pour lesquels je pensais avoir besoin d’un nouveau développement spécifique:

Ces capteurs publient les données sur le Broker du constructeur, une compagnie chinoise nommée Youpinyuntai.
Je me connecte donc à ce Broker comme le fait l’application Android ou IOS pour récupérer les données avec le plugin JMQTT.
Maintenant je comprends que la fonction PHP:json_decode décode l’UTF-8 à la volée mais je pense qu’il le fait que sur l’objet primaire.
On dirait dans ce cas que l’objet « Content » est le seul qui est encodé, pas les autres! Bizzare je l’accorde comme façon de faire.
En tous les cas @domotruc , si tu regardes ce que fait ce dévellopeur qui a construit un petit code pour récupérer les données, il décode de l’UTF-8 le message avant de Parser le JSON:
reads and parses sensor data of JQ-200 / JQ-300 from ypyt cloud for given device token · GitHub
C’est pour cela je pensais que le décodage ne se faisait pas par le plugin.
Sinon pour ajouter de l’info sur le petit problème que je rencontre, voici un log en mode débug:

[2019-12-29 22:21:47][DEBUG] : broker msg: Client XXXXXXXXXXXXXXXXXXX received PUBLISH (d0, q0, r0, m0, 'XXXXXXXXXXXXXXXXXXX', ... (723 bytes))
[2019-12-29 22:21:47][DEBUG] : Payload {"type":"V","content":"[{\"content\":\"0\",\"dptId\":1,\"seq\":1,\"deviceToken\":\"XXXXXXXXXXXXXXXXXXX\"},{\"content\":null,\"dptId\":1,\"seq\":2,\"deviceToken\":\"\"},{\"content\":null,\"dptId\":1,\"seq\":3,\"deviceToken\":\"\"},{\"content\":\"19\",\"dptId\":1,\"seq\":4,\"deviceToken\":\"\"},{\"content\":\"52\",\"dptId\":1,\"seq\":5,\"deviceToken\":\"\"},{\"content\":\"0\",\"dptId\":1,\"seq\":6,\"deviceToken\":\"\"},{\"content\":\"0.028\",\"dptId\":1,\"seq\":7,\"deviceToken\":\"\"},{\"content\":\"0.387\",\"dptId\":1,\"seq\":8,\"deviceToken\":\"\"},{\"content\":\"774\",\"dptId\":1,\"seq\":9,\"deviceToken\":\"\"},{\"content\":null,\"dptId\":1,\"seq\":10,\"deviceToken\":\"\"}]","deviceToken":"XXXXXXXXXXXXXXXXXXX"} for topic XXXXXXXXXXXXXXXXXXX
[2019-12-29 22:21:47][INFO] : -> mq.youpinyuntai.com|XXXXXXXXXXXXXXXXXXX {"type":"V","content":"[{\"content\":\"0\",\"dptId\":1,\"seq\":1,\"deviceToken\":\"XXXXXXXXXXXXXXXXXXX\"},{\"content\":null,\"dptId\":1,\"seq\":2,\"deviceToken\":\"\"},{\"content\":null,\"dptId\":1,\"seq\":3,\"deviceToken\":\"\"},{\"content\":\"19\",\"dptId\":1,\"seq\":4,\"deviceToken\":\"\"},{\"content\":\"52\",\"dptId\":1,\"seq\":5,\"deviceToken\":\"\"},{\"content\":\"0\",\"dptId\":1,\"seq\":6,\"deviceToken\":\"\"},{\"content\":\"0.028\",\"dptId\":1,\"seq\":7,\"deviceToken\":\"\"},{\"content\":\"0.387\",\"dptId\":1,\"seq\":8,\"deviceToken\":\"\"},{\"content\":\"774\",\"dptId\":1,\"seq\":9,\"deviceToken\":\"\"},{\"content\":null,\"dptId\":1,\"seq\":10,\"deviceToken\":\"\"}]","deviceToken":"XXXXXXXXXXXXXXXXXXX"}
[2019-12-29 22:21:47][INFO] : -> mq.youpinyuntai.com|Content "[{\"content\":\"0\",\"dptId\":1,\"seq\":1,\"deviceToken\":\"XXXXXXXXXXXXXXXXXXX\"},{\"content\":null,\"dptId\":1,\"seq\":2,\"deviceToken\":\"\"},{\"content\":null,\"dptId\":1,\"seq\":3,\"deviceToken\":\"\"},{\"content\":\"19\",\"dptId\":1,\"seq\":4,\"deviceToken\":\"\"},{\"content\":\"52\",\"dptId\":1,\"seq\":5,\"deviceToken\":\"\"},{\"content\":\"0\",\"dptId\":1,\"seq\":6,\"deviceToken\":\"\"},{\"content\":\"0.028\",\"dptId\":1,\"seq\":7,\"deviceToken\":\"\"},{\"content\":\"0.387\",\"dptId\":1,\"seq\":8,\"deviceToken\":\"\"},{\"content\":\"774\",\"dptId\":1,\"seq\":9,\"deviceToken\":\"\"},{\"content\":null,\"dptId\":1,\"seq\":10,\"deviceToken\":\"\"}]"
[2019-12-29 22:21:47][INFO] : -> mq.youpinyuntai.com|Token "XXXXXXXXXXXXXXXXXXX"
[2019-12-29 22:21:47][INFO] : -> mq.youpinyuntai.com|Type "V"

Merci

Bonjour nordix,

Je te confirme que json_decode décode bien jusqu’au niveau le plus bas, j’ai plein d’autres exemples de payloads JSON imbriquées qui le montre.

Dans ton cas, le champ content est une chaîne de caractère au sens JSON (car encapsulée par des "), elle ne sera jamais interprétée par un décodeur JSON.

J’ai analysé et testé le script python:

  1. Si tu enlèves l’appel à la fonction decode (i.e. fais directement jsondata = json.loads(payload)), le script marche également, ce qui confirme que le problème n’a rien à voir avec l’encodage UTF-8;
  2. Le décodage du champ content n’est pas fait via le décodeur JSON. Dans la fonction handle_message_values, la chaîne content est décodée via la fonctionnalité dictionnaire du langage python.

Donc, malheureusement, le plugin jMQTT ne pourra pas décoder directement le champ content. Tu pourrais (non exhaustif):

  • Peut-être passer par le plugin virtuel qui réinterpréteraient la commande info content récupérée par jMQTT; ou
  • Avoir un script similaire au script python qui republierait les infos en JSON jusqu’au niveau le plus bas sur un broker local et ensuite utiliser jMQTT en le connectant à ce broker local; ou
  • Demander au fournisseur de ton capteur d’encoder en JSON jusqu’au niveau le plus bas…

cordialement

Bonjour @domotruc
Merci pour cette réponse fournie et détaillée. J’en étais arrivé aux mêmes conclusions concenrnant le format du string Content, pas du tout un objet JSON effectivement.
Je vais tester tes recommandations à commencer par un virtuel et un Regex probablement pour extraire ces valeurs. Ça va pomper des ressources probalement car la publication se fait aux 3 secondes. Si c’est le cas, un script séparé qui publie dans un Broker local sera plus efficace.

Au plaisir et merci encore pour tes efforts.

Finalement l’intégration avec un virtuel et un scénario programmé donne une bonne intégration de ces senseurs.
Un simple scénario code PHP comme suit fait l’affaire:

// grab value from jmqtt Broker
$cmd = cmd::byString("#[Bureau][mq.youpinyuntai.com][Value]#");
$value = $cmd->execCmd();
//strip slashes and back slashes
$stripped = stripslashes($value);
//$scenario->setLog($stripped);
$jsonarray = json_decode($stripped, true);
if(is_array($jsonarray)){
	//récupération des données
	$temp=$jsonarray[3]['content'];
	$humidity=$jsonarray[4]['content'];
	$PM25=$jsonarray[5]['content'];
	$HCHO=$jsonarray[6]['content'];
	$TVOC=$jsonarray[7]['content'];
	$eCO2=$jsonarray[8]['content'];
	// Mise à jour des commandes du virtuel
	cmd::byString("#[CONTROLE][JQ300-Virtuel][Temperature]#")->event($temp);
	cmd::byString("#[CONTROLE][JQ300-Virtuel][Humidity]#")->event(($humidity));
	cmd::byString("#[CONTROLE][JQ300-Virtuel][PM2.5 ]#")->event(($PM25));
	cmd::byString("#[CONTROLE][JQ300-Virtuel][HCHO]#")->event(($HCHO));
	cmd::byString("#[CONTROLE][JQ300-Virtuel][TVOC]#")->event(($TVOC));
	cmd::byString("#[CONTROLE][JQ300-Virtuel][eCO2]#")->event(($eCO2));
}

En développant ce code je me suis aperçu d’ailleurs que l’objet imbriqué « content », si on lui applique une simple fonction stripslashes() est reconnu comme un Json Array comme il se doit. C’est juste l’objet racine qui n’est pas reconnu comme un objet Json si on applique la même fonction.
Je ne sais pas si par exemple dans le plugin, lorsqu’on rencontre des objets imbriqués contenant des caractères Escape, on ne pourrait pas leur appliquer la même recette avec la fonction stripslashes().
Juste une idée :wink:

De manière plus pratique, ces senseurs valent le coup à un prix dérisoire et donnent des infos très précises sur la qualité de l’air et la présence de gaz indésirables.

2 « J'aime »

Bonsoir,

Je suis très interessé par ce type de capteur ! Comment as-tu validé la précision des informations remontées par ce capteur ?

En fait mes tests ont été effectués de manière empirique pour les senseurs de qualité de l’air. Outre le thermomètre et l’hygromètre avec lesquels j’ai fait des comparatifs avec des sondes que j’avais, variation de 1 degrés max pour l a température et 10% pour l’hygro, le reste est basé sur ces standards en milieu sain:

On y compare différents senseurs et les données de celui-ci chez moi, sont en plein dans les valeurs des autres appareils.
J’ai placé le senseur dans le garage où les niveaux de eCO2 sont elevés (fumée de cigarette) et les valeurs explosent instantanément pour le TVOC et même le HCHO.
Bref pour moins de 20 euros je crois qu’il n’y a pas à se poser de questions, je vais d’ailleurs en racheter 2 autres qui pourraient servir de detecteurs de fumée et incendie dans les coins stratégiques dans la maison (Cuisine, salle à fournaise).
Voici d’ailleurs les données de mon senseur dans un milieu fermé en général, le bureau:
03

Bonjour nordix,
Jolie intégration ! :wink:
Pas si simple l’intégration dans le plugin car l’ajout de commande découlant de payload JSON est géré par le navigateur (la vue JSON dans l’interface). Il faudrait à la fois du code javascript et du code php.
Et je ne suis pas favorable à particulariser le plugin au fonctionnement spécifique d’équipements.
Cordialement

Ce sujet a été automatiquement fermé après 24 heures suivant le dernier commentaire. Aucune réponse n’est permise dorénavant.