Developper un service pour les compteurs Linky

Bonjour à tous :slight_smile:

J’aurais aimé développer mon premier service, comme dit dans le titre, celui-ci serait pour afficher ma conso d’elec en “quasi” temps réel.

Bon déjà, il faut dire que j’ai vraiment galéré à me mettre un environnement de dev, j’ai tenté avec un raspberry et j’ai obtenu un souci vraiment bizarre … Aucune page d’installation/configuration de Gladys au premier redémarrage, j’étais directement redirigé sur la page dashboard alors qu’il n’y avait aucun utilisateur créé (j’ai vérifié le fichier .db avec VS Code, merci à @AlexTrovato d’ailleurs qui a essayé de m’aider et m’a aiguillé du coup sur Windows avec WSL).

Du coup là je suis opérationnel sur WSL avec mon environnement de dev, Gladys est bien installée et configurée.
Je fouine donc un peu sur google pour mon projet, je trouve ce module nodejs

Je l’installe, je créer un fichier avec le code qu’il propose et bien sur je modifies les informations de connexion de la ligne linky.login

Je lance le fichier node monfichier.js
et là je me retrouve avec un beau 302 et un pavé d’erreurS :dizzy_face: :face_with_raised_eyebrow:

    (node:9357) UnhandledPromiseRejectionWarning: Error: Unexpected export response (2): Request failed with status code 302 - Check the Enedis website if the error persists
    at LinkySession.getData (/home/guims/node_modules/@bokub/linky/index.js:112:11)
    at process._tickCallback (internal/process/next_tick.js:68:7)
(node:9357) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 4)
(node:9357) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:9357) UnhandledPromiseRejectionWarning: Error: Unexpected data: {"etat":{"valeur":"erreur"}} - Check the Enedis website if the error persists
    at Function.parseData (/home/guims/node_modules/@bokub/linky/index.js:163:10)
    at LinkySession.getDailyData (/home/guims/node_modules/@bokub/linky/index.js:66:23)
    at process._tickCallback (internal/process/next_tick.js:68:7)
(node:9357) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 5)

En pensant que mes identifiants sont incorrectes, je vais sur le site d’Enedis pour me connecter dessus et là je vois que les url’s de connexion ont toutes changées.
N’ayant aucunes connaissances “dev”, je vais dans le fichier index.js du module linky et par déduction, je change les url’s avec les nouvelles adresses de connexion, et je relance le fichier avec node:

    { status: 302,
  statusText: 'Found',
  headers:
   { date: 'Wed, 08 Apr 2020 10:00:49 GMT',
     location: '/messages/inexistant.html',
     'content-length': '209',
     connection: 'close',
     'content-type': 'text/html; charset=iso-8859-1' },
  config:
   { url:
      'https://mon-compte.enedis.fr/auth/XUI/#login/&realm=/enedis&forward=true&spEntityID=SP-ODW-PROD&goto=/SSOPOST/metaAlias/enedis/providerIDP?ReqID%3Da24218306ai78bfh4302h4h5c70ab57%26index%3Dnull%26acsURL%3Dhttps://apps.lincs.enedis.fr/saml/SSO%26spEntityID%3DSP-ODW-PROD%26binding%3Durn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST&AMAuthCookie=',
     method: 'post',
     data:
      'IDToken1=mon_email_de_connexion&IDToken2=mon_mdp_de_connexion&SunQueryParamsString=cmVhbG09cGFydGljdWxpZXJz&encoded=true&gx_charset=UTF-8',
     headers:
      { Accept: 'application/json, text/plain, */*',
        'Content-Type': 'application/x-www-form-urlencoded',
        'User-Agent': 'axios/0.19.2',
        'Content-Length': 128 },
     transformRequest: [ [Function: transformRequest] ],
     transformResponse: [ [Function: transformResponse] ],
     timeout: 0,
     adapter: [Function: httpAdapter],
     xsrfCookieName: 'XSRF-TOKEN',
     xsrfHeaderName: 'X-XSRF-TOKEN',
     maxContentLength: -1,
     validateStatus: [Function: validateStatus],
     maxRedirects: 0 },
  request:
   ClientRequest {
     _events:
      [Object: null prototype] {
        error: [Function: handleRequestError],
        prefinish: [Function: requestOnPrefinish] },
     _eventsCount: 2,
     _maxListeners: undefined,
     output: [],
     outputEncodings: [],
     outputCallbacks: [],
     outputSize: 0,
     writable: true,
     _last: true,
     chunkedEncoding: false,
     shouldKeepAlive: false,
     useChunkedEncodingByDefault: true,
     sendDate: false,
     _removedConnection: false,
     _removedContLen: false,
     _removedTE: false,
     _contentLength: 128,
     _hasBody: true,
     _trailer: '',
     finished: true,
     _headerSent: true,
     socket:
      TLSSocket {
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        servername: 'mon-compte.enedis.fr',
        alpnProtocol: false,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        _events: [Object],
        _eventsCount: 8,
        connecting: false,
        _hadError: false,
        _handle: [TLSWrap],
        _parent: null,
        _host: 'mon-compte.enedis.fr',
        _readableState: [ReadableState],
        readable: true,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: false,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: undefined,
        _server: null,
        ssl: [TLSWrap],
        _requestCert: true,
        _rejectUnauthorized: true,
        parser: null,
        _httpMessage: [Circular],
        [Symbol(res)]: [TLSWrap],
        [Symbol(asyncId)]: 6,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(timeout)]: null,
        [Symbol(kBytesRead)]: 0,
        [Symbol(kBytesWritten)]: 0,
        [Symbol(connect-options)]: [Object] },
     connection:
      TLSSocket {
        _tlsOptions: [Object],
        _secureEstablished: true,
        _securePending: false,
        _newSessionPending: false,
        _controlReleased: true,
        _SNICallback: null,
        servername: 'mon-compte.enedis.fr',
        alpnProtocol: false,
        authorized: true,
        authorizationError: null,
        encrypted: true,
        _events: [Object],
        _eventsCount: 8,
        connecting: false,
        _hadError: false,
        _handle: [TLSWrap],
        _parent: null,
        _host: 'mon-compte.enedis.fr',
        _readableState: [ReadableState],
        readable: true,
        _maxListeners: undefined,
        _writableState: [WritableState],
        writable: false,
        allowHalfOpen: false,
        _sockname: null,
        _pendingData: null,
        _pendingEncoding: '',
        server: undefined,
        _server: null,
        ssl: [TLSWrap],
        _requestCert: true,
        _rejectUnauthorized: true,
        parser: null,
        _httpMessage: [Circular],
        [Symbol(res)]: [TLSWrap],
        [Symbol(asyncId)]: 6,
        [Symbol(lastWriteQueueSize)]: 0,
        [Symbol(timeout)]: null,
        [Symbol(kBytesRead)]: 0,
        [Symbol(kBytesWritten)]: 0,
        [Symbol(connect-options)]: [Object] },
     _header:
      'POST /auth/XUI/ HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nContent-Type: application/x-www-form-urlencoded\r\nUser-Agent: axios/0.19.2\r\nContent-Length: 128\r\nHost: mon-compte.enedis.fr\r\nConnection: close\r\n\r\n',
     _onPendingData: [Function: noopPendingOutput],
     agent:
      Agent {
        _events: [Object],
        _eventsCount: 1,
        _maxListeners: undefined,
        defaultPort: 443,
        protocol: 'https:',
        options: [Object],
        requests: {},
        sockets: [Object],
        freeSockets: {},
        keepAliveMsecs: 1000,
        keepAlive: false,
        maxSockets: Infinity,
        maxFreeSockets: 256,
        maxCachedSessions: 100,
        _sessionCache: [Object] },
     socketPath: undefined,
     timeout: undefined,
     method: 'POST',
     path: '/auth/XUI/',
     _ended: true,
     res:
      IncomingMessage {
        _readableState: [ReadableState],
        readable: false,
        _events: [Object],
        _eventsCount: 3,
        _maxListeners: undefined,
        socket: [TLSSocket],
        connection: [TLSSocket],
        httpVersionMajor: 1,
        httpVersionMinor: 1,
        httpVersion: '1.1',
        complete: true,
        headers: [Object],
        rawHeaders: [Array],
        trailers: {},
        rawTrailers: [],
        aborted: false,
        upgrade: false,
        url: '',
        method: null,
        statusCode: 302,
        statusMessage: 'Found',
        client: [TLSSocket],
        _consuming: true,
        _dumped: false,
        req: [Circular] },
     aborted: undefined,
     timeoutCb: null,
     upgradeOrConnect: false,
     parser: null,
     maxHeadersCount: null,
     [Symbol(isCorked)]: false,
     [Symbol(outHeadersKey)]:
      [Object: null prototype] {
        accept: [Array],
        'content-type': [Array],
        'user-agent': [Array],
        'content-length': [Array],
        host: [Array] } },
  data:
   '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n<html><head>\n<title>302 Found</title>\n</head><body>\n<h1>Found</h1>\n<p>The document has moved <a href="/messages/inexistant.html">here</a>.</p>\n</body></html>\n' }
{ date: 'Wed, 08 Apr 2020 10:00:49 GMT',
  location: '/messages/inexistant.html',
  'content-length': '209',
  connection: 'close',
  'content-type': 'text/html; charset=iso-8859-1' }
(node:9389) UnhandledPromiseRejectionWarning: Error: Unexpected login response (3) - Check the Enedis website if the error persists
    at Object.login (/home/guims/node_modules/@bokub/linky/index.js:31:10)
    at process._tickCallback (internal/process/next_tick.js:68:7)
(node:9389) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:9389) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Avant d’aller plus loin à passer des jours sur un malheureux bout de code qui pour moi est un vrai casse-tête, je fouine sur github, google et npm search pour voir s’il y a quelque chose de plus récent. Je n’aboutis pas à grand-chose, donc j’élargis ma recherche google et là je tombe sur un ticket support qui confirme mes dires au sujet des url’s changées mais aussi l’ajout d’un capcha. mon souci doit aussi venir de là…

J’ai vu aussi qu’Enedis avait développé une API ==> https://datahub-enedis.fr/ . Est-ce que je peux m’en servir pour faire ce service ?

Quand j’étais sur Gladys 3 j’avais testé le module de bokud et il fonctionnait plutôt bien, alors j’aurais pensé m’en sortir sur la V4, pour sortir quelque chose de fonctionnel.

Merci d’avance pour vos réponses.

1 Like

On parle pas teleinfo on est d’accord ?

Oui on est d’accord, uniquement du compteur linky

Je pense que tu devrais essayer cette API, voir si elle correspond àtes besoins, mais note bien toutes les étapes de création de compte pour pouvoir completer la doc Gladys pour l’utilisation de ton service (screenshots et tout ce qui va avec). Par la suite, on verra s’il est possible de concentrer toutes ces clés d’API dans la gateway, pour simplifier la vie des inscrits.

1 Like