<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Remy Jacquand</title>
    <description>The latest articles on DEV Community by Remy Jacquand (@multiplaie).</description>
    <link>https://dev.to/multiplaie</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F110225%2F53d6bcbf-0d1d-4d6c-b292-6c1977393b74.jpg</url>
      <title>DEV Community: Remy Jacquand</title>
      <link>https://dev.to/multiplaie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/multiplaie"/>
    <language>en</language>
    <item>
      <title>Mise en place d Outline et Authentik</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Thu, 12 Feb 2026 17:56:49 +0000</pubDate>
      <link>https://dev.to/multiplaie/mise-en-place-d-outline-et-authentik-34j8</link>
      <guid>https://dev.to/multiplaie/mise-en-place-d-outline-et-authentik-34j8</guid>
      <description>&lt;p&gt;Dans ce tutoriel, nous allons montrer comment déployer &lt;strong&gt;Outline&lt;/strong&gt;, le wiki collaboratif, avec &lt;strong&gt;Authentik&lt;/strong&gt; comme fournisseur d’authentification OpenID Connect (OIDC), le tout orchestré via Docker Compose. L’objectif est d’avoir Outline accessible sur &lt;code&gt;https://outline.multiplaie.work&lt;/code&gt; et sécurisé avec un login centralisé via Authentik.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Prérequis
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Serveur Linux avec Docker et Docker Compose&lt;/li&gt;
&lt;li&gt;Nom de domaine géré via Cloudflare (&lt;code&gt;multiplaie.work&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Certificat SSL (optionnel si HTTPS est géré par un reverse proxy)&lt;/li&gt;
&lt;li&gt;Connaissance de base de Docker et Authentik&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Structure des services Docker
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Outline
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt; pour Outline :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;outline&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.getoutline.com/outlinewiki/outline:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outline&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./docker.env&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;appflowy-proxy&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;storage-data:/var/lib/outline/data&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;

  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis:7&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outline-redis&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;appflowy-proxy&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outline-postgres&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;appflowy-proxy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outline&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outline&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outline&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database-data:/var/lib/postgresql/data&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;storage-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;database-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;appflowy-proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.2 Authentik
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;docker-compose.yml&lt;/code&gt; pour Authentik :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.8"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgresql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:16-alpine&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;authentik-postgres&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PG_DB:-authentik}&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PG_USER:-authentik}&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PG_PASS:?database password required}&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-d&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$${POSTGRES_DB}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-U&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$${POSTGRES_USER}"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;20s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database:/var/lib/postgresql/data&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;authentik-proxy&lt;/span&gt;

  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.12.3}&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;authentik-server&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgresql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AUTHENTIK_POSTGRESQL__HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql&lt;/span&gt;
      &lt;span class="na"&gt;AUTHENTIK_POSTGRESQL__NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PG_DB:-authentik}&lt;/span&gt;
      &lt;span class="na"&gt;AUTHENTIK_POSTGRESQL__USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PG_USER:-authentik}&lt;/span&gt;
      &lt;span class="na"&gt;AUTHENTIK_POSTGRESQL__PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PG_PASS}&lt;/span&gt;
      &lt;span class="na"&gt;AUTHENTIK_SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${AUTHENTIK_SECRET_KEY:?secret key required}&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${COMPOSE_PORT_HTTP:-9000}:9000&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${COMPOSE_PORT_HTTPS:-9443}:9443&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./data:/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./custom-templates:/templates&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;authentik-proxy&lt;/span&gt;

  &lt;span class="na"&gt;worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2025.12.3}&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;authentik-worker&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;postgresql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;service_healthy&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AUTHENTIK_POSTGRESQL__HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql&lt;/span&gt;
      &lt;span class="na"&gt;AUTHENTIK_POSTGRESQL__NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PG_DB:-authentik}&lt;/span&gt;
      &lt;span class="na"&gt;AUTHENTIK_POSTGRESQL__USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PG_USER:-authentik}&lt;/span&gt;
      &lt;span class="na"&gt;AUTHENTIK_POSTGRESQL__PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${PG_PASS}&lt;/span&gt;
      &lt;span class="na"&gt;AUTHENTIK_SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${AUTHENTIK_SECRET_KEY:?secret key required}&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/var/run/docker.sock:/var/run/docker.sock&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./data:/data&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./certs:/certs&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./custom-templates:/templates&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;authentik-proxy&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;authentik-proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Configuration des fichiers &lt;code&gt;.env&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Outline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_ENV=production
URL=&amp;lt;URL EXTERNE&amp;gt;
PORT=3000
WEB_CONCURRENCY=1
SECRET_KEY=&amp;lt;clé aléatoire&amp;gt;
UTILS_SECRET=&amp;lt;clé aléatoire&amp;gt;

DATABASE_URL=postgres://outline:outline@postgres:5432/outline
PGSSLMODE=disable

REDIS_URL=redis://outline-redis:6379

FILE_STORAGE=local
FILE_STORAGE_LOCAL_ROOT_DIR=/var/lib/outline/data

OIDC_CLIENT_ID=&amp;lt;Client ID Authentik&amp;gt;
OIDC_CLIENT_SECRET=&amp;lt;Client Secret Authentik&amp;gt;
OIDC_AUTH_URI=https://&amp;lt;URL vers Authentik&amp;gt;/application/o/authorize/
OIDC_TOKEN_URI=https://&amp;lt;URL vers Authentik&amp;gt;/application/o/token/
OIDC_USERINFO_URI=https://&amp;lt;URL vers Authentik&amp;gt;/application/o/userinfo/
OIDC_LOGOUT_URI=https://&amp;lt;URL vers Authentik&amp;gt;/application/o/outline/end-session/
OIDC_USERNAME_CLAIM=preferred_username
OIDC_DISPLAY_NAME=Authentik
OIDC_SCOPES=openid email profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 Authentik
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PG_PASS=&amp;lt;mot de passe PostgreSQL&amp;gt;
AUTHENTIK_SECRET_KEY=&amp;lt;clé aléatoire&amp;gt;

AUTHENTIK_EMAIL__HOST=smtp.gmail.com
AUTHENTIK_EMAIL__PORT=465
AUTHENTIK_EMAIL__USERNAME=&amp;lt;USERNAME&amp;gt;
AUTHENTIK_EMAIL__PASSWORD=&amp;lt;mot de passe Gmail&amp;gt;
AUTHENTIK_EMAIL__USE_SSL=true
AUTHENTIK_EMAIL__FROM="auth@URL"

AUTHENTIK_ERROR_REPORTING__ENABLED=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Configuration d’Authentik pour Outline
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Créer un Provider OIDC&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Name&lt;/code&gt; : Outline&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Client ID&lt;/code&gt; et &lt;code&gt;Client Secret&lt;/code&gt; générés&lt;/li&gt;
&lt;li&gt;Endpoints :

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Authorization&lt;/code&gt; : &lt;code&gt;/if/authorize/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Token&lt;/code&gt; : &lt;code&gt;/if/token/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Userinfo&lt;/code&gt; : &lt;code&gt;/if/userinfo/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Scopes : &lt;code&gt;openid email profile&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Créer l’application OIDC&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Redirect URI&lt;/code&gt; : &lt;code&gt;https://&amp;lt;URL OUTLINE&amp;gt;/auth/oidc/callback&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Post Logout Redirect URI&lt;/code&gt; : &lt;code&gt;&amp;lt;URL OUTLINE&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Associer au Provider Outline créé précédemment&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Copier le &lt;strong&gt;Client ID&lt;/strong&gt; et le &lt;strong&gt;Client Secret&lt;/strong&gt; dans le &lt;code&gt;.env&lt;/code&gt; de Outline.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  5. Démarrage des services
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Démarrer Authentik&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.authentik.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Démarrer Outline&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose.outline.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vérifier les logs si besoin :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; outline
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; authentik-server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Connexion à Outline
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Ouvrir &lt;code&gt;https://&amp;lt;URL OUTLINE&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Choisir &lt;strong&gt;Authentik&lt;/strong&gt; comme fournisseur de login&lt;/li&gt;
&lt;li&gt;Se connecter avec les identifiants configurés dans Authentik&lt;/li&gt;
&lt;li&gt;L’accès est maintenant sécurisé via OpenID Connect&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  7. Conclusion
&lt;/h2&gt;

&lt;p&gt;Cette configuration permet d’avoir :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un Outline fonctionnel et accessible sur un domaine public&lt;/li&gt;
&lt;li&gt;Authentification centralisée via Authentik (OIDC)&lt;/li&gt;
&lt;li&gt;Gestion des utilisateurs et des permissions via Authentik&lt;/li&gt;
&lt;li&gt;Stockage sécurisé des données via PostgreSQL et Redis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tout est orchestré avec &lt;strong&gt;Docker Compose&lt;/strong&gt;, ce qui facilite la maintenance et les mises à jour.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Pourquoi mon serveur est devenu lent : le cas du disque SMR</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Thu, 12 Feb 2026 17:52:35 +0000</pubDate>
      <link>https://dev.to/multiplaie/pourquoi-mon-serveur-est-devenu-lent-le-cas-du-disque-smr-27gf</link>
      <guid>https://dev.to/multiplaie/pourquoi-mon-serveur-est-devenu-lent-le-cas-du-disque-smr-27gf</guid>
      <description>&lt;p&gt;Récemment, mon serveur Lenovo Linux Mint est devenu &lt;strong&gt;très lent&lt;/strong&gt;, même pour se connecter en SSH. Après quelques minutes d’attente, j’ai commencé à diagnostiquer le problème. Voici ce que j’ai découvert et ce que cela signifie.&lt;/p&gt;




&lt;h2&gt;
  
  
  Symptômes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SSH extrêmement lent.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;apt update&lt;/code&gt; et autres commandes système qui mettent beaucoup de temps.&lt;/li&gt;
&lt;li&gt;Serveur globalement réactif mais “ramé” sur tout ce qui touche le disque.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Diagnostic initial
&lt;/h2&gt;

&lt;p&gt;J’ai commencé par vérifier :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAM / CPU&lt;/strong&gt; : usage normal, pas de saturation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disque&lt;/strong&gt; : utilisé à &lt;strong&gt;798 Go / 915 Go&lt;/strong&gt; (~87% de remplissage).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker / conteneurs&lt;/strong&gt; : beaucoup d’applications média (Jellyfin, qbittorrent, Sonarr, Radarr, AppFlowy…).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Le disque semblait être le principal coupable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Vérification SMART
&lt;/h2&gt;

&lt;p&gt;J’ai lancé :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;smartctl &lt;span class="nt"&gt;-a&lt;/span&gt; /dev/sda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Résultats clés :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modèle : &lt;code&gt;Western Digital Blue Mobile (SMR)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Capacité : 1 To&lt;/li&gt;
&lt;li&gt;SMART overall-health : &lt;strong&gt;PASSED&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Secteurs réalloués : 0&lt;/li&gt;
&lt;li&gt;Pending / erreurs : 0&lt;/li&gt;
&lt;li&gt;Heures de fonctionnement : 12 667 h&lt;/li&gt;
&lt;li&gt;Température : 47°C&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Interprétation&lt;/strong&gt; : le disque est &lt;strong&gt;en parfait état matériel&lt;/strong&gt;, rien ne justifie une lenteur mécanique.&lt;/p&gt;




&lt;h2&gt;
  
  
  Le problème réel : la technologie SMR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;SMR = Shingled Magnetic Recording, conçu pour &lt;strong&gt;stockage froid, peu d’écritures&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Quand le disque est presque plein (&amp;gt;80%) et qu’il reçoit des écritures continues (Docker, torrents, scans), il &lt;strong&gt;doit réécrire de grandes bandes de données&lt;/strong&gt; pour chaque petite écriture.&lt;/li&gt;
&lt;li&gt;Conséquence : latence énorme, même pour SSH ou des lectures simples.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Actions effectuées
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Libération d’espace disque (~100 Go).&lt;/li&gt;
&lt;li&gt;Redémarrage du serveur.&lt;/li&gt;
&lt;li&gt;Lancement d’un &lt;code&gt;**fstrim -av**&lt;/code&gt; pour indiquer au SMR quelles zones sont libres :
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/boot/efi: 504,8 MiB trimmed
/: 426,8 GiB trimmed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Résultat : le disque a reconnu 426 Go comme libres et peut désormais optimiser ses écritures.&lt;/li&gt;
&lt;li&gt;Après quelques minutes, le SSH et les mises à jour système sont redevenus &lt;strong&gt;plus fluides&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Analyse I/O
&lt;/h2&gt;

&lt;p&gt;Pendant le problème, avec &lt;code&gt;iostat&lt;/code&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;%util&lt;/code&gt; disque : ~96%&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;await&lt;/code&gt; lectures : ~50 ms&lt;/li&gt;
&lt;li&gt;CPU libre : 73%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conclusion : le &lt;strong&gt;goulot d’étranglement était le disque&lt;/strong&gt;, pas le CPU ni la RAM.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion et recommandations
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Les disques &lt;strong&gt;SMR ne sont pas adaptés&lt;/strong&gt; pour :

&lt;ul&gt;
&lt;li&gt;Docker intensif&lt;/li&gt;
&lt;li&gt;Torrents&lt;/li&gt;
&lt;li&gt;Bases de données&lt;/li&gt;
&lt;li&gt;Applications média avec scans fréquents&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Même après libération d’espace et &lt;code&gt;fstrim&lt;/code&gt;, le disque &lt;strong&gt;reste lent&lt;/strong&gt; si le remplissage et l’écriture continue reprennent.&lt;/li&gt;
&lt;li&gt;Pour un serveur Docker média, &lt;strong&gt;préférer un SSD ou un HDD CMR classique&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;SSD : rapide et fiable pour écritures continues&lt;/li&gt;
&lt;li&gt;HDD CMR : meilleur pour gros volumes mais encore limité pour Docker très actif&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;En cas d’achat futur, vérifier &lt;strong&gt;technologie SMR vs CMR&lt;/strong&gt; pour éviter ce type de goulot d’étranglement.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Mon serveur est lent non pas parce que le disque est mort, mais parce qu’un &lt;strong&gt;disque SMR plein et actif&lt;/strong&gt; ne peut pas suivre un usage Docker intensif. Libérer de l’espace et faire un &lt;code&gt;fstrim&lt;/code&gt; aide, mais la solution définitive est de passer sur &lt;strong&gt;SSD ou HDD CMR&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>devjournal</category>
      <category>linux</category>
      <category>performance</category>
      <category>sre</category>
    </item>
    <item>
      <title>Héberger Paheko chez soit derière Cloudflare</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Thu, 12 Feb 2026 17:48:31 +0000</pubDate>
      <link>https://dev.to/multiplaie/heberger-paheko-chez-soit-deriere-cloudflare-282</link>
      <guid>https://dev.to/multiplaie/heberger-paheko-chez-soit-deriere-cloudflare-282</guid>
      <description>&lt;h1&gt;
  
  
  Héberger Paheko chez soit derière Cloudflare
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Paheko
&lt;/h2&gt;

&lt;p&gt;Paheko aide a la gestion d'association, il se dit "simple, complet et efficace" Le logiciel est accentué par sont ethique (pas de pistage, logiciel libre, sans IA). Elle assiste sur la gestion des memebres de l'association, la comptabilité, la gestion de document et création d'un site web.&lt;/p&gt;

&lt;p&gt;La version en ligne est 100% gratuit avec toutes les fonctionnaliés, 10mo de stockage pour les documents Mais un seul exercice comptable et pas d'envoie d'email.&lt;/p&gt;

&lt;p&gt;Dans le cadre de l'auto-hébergement, la limite des exercices comptable et l'envoi d'email n'existe plus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflare
&lt;/h2&gt;

&lt;p&gt;Cloudflare est un prestataire offrant une solution gratuit pour sécurisé pour créer un tunnel appelé "zero trust" vers votre serveur.&lt;/p&gt;

&lt;p&gt;Son interface d'aministration est simple et propose beaucoup d'option et d'outils de mesure du trafic&lt;/p&gt;

&lt;p&gt;Pour communiquer jusqu'au serveur, cloudflare passe par l'intermédiaire d'un container dont la plateforme propose la commande pour le créer: cloudflared&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflared
&lt;/h2&gt;

&lt;p&gt;Cloudflared est une image Docker permettant la connexion, via tunnel, entre le serveur et Cloudlfare. Sa mise en place est expliqué lors de la déclaration d'un nouveau tunnel depuis l'interface administrateur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;Une fois la stack mise en place, chaque demande de connexion vers l'hébergement Paheko passera par le DNS de cloudflare.&lt;/p&gt;

&lt;p&gt;Utilisateur =&amp;gt; DNS Cloudflare =&amp;gt; Cloudflared =&amp;gt; Paheko&lt;/p&gt;

&lt;p&gt;Le nom de domaine utilisé pour la connexion depuis l'exterieur doit être configuré, au préalable, pour utiliser les DNS de Cloudflare. Il sera donc nécessaire de déclarer le nom de domaine dans l'interface utilisateur Cloudflare.com&lt;/p&gt;

&lt;p&gt;Sur le serveur, l’entièreté des projet docker seront contenu dans&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/Docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;et s'organisera de la façon suivante:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Docker/
├─ paheko/
│ ├─ config/
│ ├─ data/
| ├─ docker-compose.yaml
|
├─ cloudflared/
| ├─ docker-compose.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Intallation
&lt;/h2&gt;

&lt;p&gt;Partons du principe que docker est déjà installé sur le serveur.&lt;/p&gt;

&lt;p&gt;Préparons les dossiers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Dossier de base
mkdir -p ~/Docker

# Paheko
mkdir -p ~/Docker/paheko ~/Docker/paheko/config ~/docker/paheko/data

# Cloudflare
mkdir -p ~/Docker/cloudflared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mise en place de Paheko
&lt;/h3&gt;

&lt;p&gt;L'image docker de Paheko est disponible sur &lt;a href="https://hub.docker.com/r/libretic/paheko" rel="noopener noreferrer"&gt;hub.docker.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hub.docker.com/r/libretic/paheko" rel="noopener noreferrer"&gt;https://hub.docker.com/r/libretic/paheko&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  paheko:
    image: paheko/paheko
    restart: always
    volumes:
      - ./config.local.php:/var/www/paheko/config.local.php
      - ./php.ini:/usr/local/etc/php/php.ini
      - pahekodata:/var/www/paheko/data
      - /var/www/paheko/data/plugins
    ports:
      - 8080:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour adapter le fichier aux besoin actuel, la mise à jour ce présente ainsi:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/Docker/paheko/docker-compose.yaml

services:
  paheko:
    image: paheko/paheko
    container_name: paheko 
    restart: always
    volumes:
      - ./config/php.ini:/usr/local/etc/php/php.ini
      - ./data:/var/www/paheko/data
    networks:
      - paheko-proxy
    ports: 
      - "8080:80"

networks:
  paheko-proxy:
    external: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Un "container_name" a été ajouté
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;container_name: paheko
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Et un network "paheko-proxy" a été créer
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker network create paheko-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour finir, le dossier data doit être editable par www-data du container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chown 33:33 ~/Docker/paheko/data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Mise en place de Cloudflared
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/Docker/cloudflared/docker-compose.yaml

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: unless-stopped
    command: tunnel --no-autoupdate run --token [SECRET_TOKEN]
    networks:
      - paheko-proxy
networks:
  paheko-proxy:
    external: true


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;le SECRET_TOKEN est fourni durant le déclarion du tunnel dans l'interface utilisateur de Cloudflare&lt;/p&gt;

&lt;p&gt;Enfin, les containers peuvent être lancé:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Cloudflared
cd ~/Docker/cloudflared
docker compose up -d

# Paheko
cd ~/Docker/paheko
docker compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;et vérifier leur connexion réseau:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker network inspect paheko-proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Accès Exterieur
&lt;/h2&gt;

&lt;p&gt;Dans l'interface utilisateur de Cloudflare, déclarez un nouvel hôte publique en spécifiant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Type: HTTP
URL: paheko:80 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Une fois validé, le site sera accéssible depuis le nom de domaine déclaré dans Cloudflare&lt;/p&gt;

</description>
      <category>tuto</category>
      <category>paheko</category>
      <category>docker</category>
    </item>
    <item>
      <title>Streamer des fichiers audio avec homeassistant et mopidy</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Thu, 21 Apr 2022 13:23:08 +0000</pubDate>
      <link>https://dev.to/multiplaie/streamer-des-fichiers-audio-avec-homeassistant-et-mopidy-26i3</link>
      <guid>https://dev.to/multiplaie/streamer-des-fichiers-audio-avec-homeassistant-et-mopidy-26i3</guid>
      <description>&lt;p&gt;Dans ce billet, je vais expliquer la mise en place d'un système permettant la diffusion de fichier audio vers différents périphériques connectés.&lt;br&gt;
Je prendrai comme contexte les éléments suivants : je suis dans un lieu possédant 3 pièces.&lt;br&gt;
Dans les pièces 1 et 2 y est disposé un Raspberry pi (3 ou 4) raccordé a des enceintes.&lt;br&gt;
Dans la pièce 3 se trouve le serveur permettant de diffuser les fichiers audio vers les Raspberry pi des autres pièces.&lt;br&gt;
Les Raspberry pi sont sous la distribution &lt;a href="https://raspberry-pi.fr/telechargements/" rel="noopener noreferrer"&gt;raspbian lite&lt;/a&gt; et le serveur principal est sous &lt;a href="https://www.ubuntu-fr.org/download/" rel="noopener noreferrer"&gt;ubuntu 20.04 (LTS)&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  Préparation périphérique Mopidy
&lt;/h1&gt;

&lt;p&gt;Afin de pouvoir lire de la musique depuis un Raspberry pi, je vais installer l'application &lt;a href="https://mopidy.com" rel="noopener noreferrer"&gt;Mopidy&lt;/a&gt;.&lt;br&gt;
Mopidy est un lecteur de fichier audio avec plusieurs plugin intéressant tel qu'un plugin spotify, soundcloud, TuneIn et avec plusieurs thèmes ect...&lt;/p&gt;

&lt;p&gt;Via son plugin mopidy-mdp, je vais pouvoir contrôler la lecture depuis d'autres applications. Home Assistant dans notre cas.&lt;/p&gt;

&lt;p&gt;Je me connecte donc à un des deux Raspberry pi en gardant en tête que je devrai refaire la même oopération sur l'autre.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installation Mopidy
&lt;/h2&gt;

&lt;p&gt;Ajout de l'archive GPG&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mkdir -p /usr/local/share/keyrings
sudo wget -q -O /usr/local/share/keyrings/mopidy-archive-keyring.gpg \ 
    https://apt.mopidy.com/mopidy.gpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ajout de la nouvelle source de paquet&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo wget -q -O /etc/apt/sources.list.d/mopidy.list https://apt.mopidy.com/buster.list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Installation de Mopidy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install mopidy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.mopidy.com/en/latest/installation/debian/" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation des extensions
&lt;/h2&gt;

&lt;p&gt;Parmis les extensions, seulement 2 sont intéressantes pour le projet : mopidy-mpd et mopidy-iris&lt;/p&gt;

&lt;p&gt;Modipy-MPD permettra de contrôler la lecture depuis Home Assistant et Mopidy Iris est une interface graphique pour simplifier son utilisation direct&lt;/p&gt;

&lt;p&gt;La liste des extensions disponibles sont accessible au lien suivant : &lt;a href="https://mopidy.com/ext/" rel="noopener noreferrer"&gt;liste des extensions&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Mopidy-MDP
&lt;/h3&gt;

&lt;p&gt;pour installer l'extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo python3 -m pip install Mopidy-MPD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensuite, je passe à la configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/mopidy/mopidy.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;et j'y ajoute les lignes suivante&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[mpd]
enabled = true
hostname = 192.168.0.48
port = 6600

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"enabled" correspond a l'activation de l'extension.&lt;br&gt;
"hostname" est l'adresse IP de la machine sur laquelle je suis. &lt;br&gt;
"port" est à 6600, ce qui est le port par défaut de mpd.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mopidy.com/ext/mpd/" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Mopidy-Iris
&lt;/h3&gt;

&lt;p&gt;Voici la commande pour l'installation de l'interface graphique Iris&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo python3 -m pip install Mopidy-Iris
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rien n'oblige à utiliser spécifiquement cette interface. Elle est installé juste par soucis esthétique.&lt;/p&gt;

&lt;p&gt;Aucune configuration est requise.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mopidy.com/ext/iris/" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  configuration HTTP
&lt;/h3&gt;

&lt;p&gt;Afin d'accéder pour navigateur web a Mopidy, il faut configurer son accès. Pour cela, il faut ajouter les lignes suivantes dans le fichier "/etc/mopidy/mopidy.conf"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[http]
enabled = true
hostname = 192.168.0.48
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"enabled" active l'extension&lt;br&gt;
"hostname" est l'adresse de notre Raspberry pi&lt;/p&gt;

&lt;p&gt;Maintenant, mopidy est accessible par le navigateur à l'adresse du Raspberry pi et au port 6680 (pour ma part &lt;a href="http://192.168.0.48:6680" rel="noopener noreferrer"&gt;http://192.168.0.48:6680&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Il faut maintenant effectué la même installation sur l'autre Raspberry pi en prenant soin de changer l'adresse IP.&lt;/p&gt;
&lt;h1&gt;
  
  
  Préparation du serveur HomeAssistant
&lt;/h1&gt;

&lt;p&gt;Ce serveur Permettra de diffuser et de controller la lecture d'un ou plusieurs fichiers audio vers les lecteur Mopidy installer précédement.&lt;/p&gt;

&lt;p&gt;Pour commencer, il faut se rendre sur le serveur et y faire les mise à jour&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get upgrade -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensuite, installer les dependances&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install -y python3 python3-dev python3-venv python3-pip libffi-dev libssl-dev libjpeg-dev zlib1g-dev autoconf build-essential libopenjp2-7 libtiff5 libturbojpeg0-dev tzdata
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Créer un compte utilisateur sur le serveur&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo useradd -rm homeassistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ainsi qu'un environement virtuel&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mkdir /srv/homeassistant
sudo chown homeassistant:homeassistant /srv/homeassistant
sudo -u homeassistant -H -s
cd /srv/homeassistant
python3 -m venv .
source bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Une fois l'environement activé, il est necessaire d'installer le paquet python suivant&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 -m pip install wheel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Il est maintenant temps d'installer homeassistant&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install homeassistant
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour démarrer homeassistant&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et l'interface de Home Assistant est maintenant accessible depuis le navigateur à l'adresse IP du serveur et au port 8123 (&lt;a href="http://192.168.0.79:8123" rel="noopener noreferrer"&gt;http://192.168.0.79:8123&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.home-assistant.io/installation/linux#install-home-assistant-core" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Configuration de Home Assistant
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Configuration des contrôles des Raspberry pi par MPD
&lt;/h2&gt;

&lt;p&gt;Afin de pouvoir faire lire aux Raspberry pi des fichiers audio depuis Home Assistant, il faut déclarer les adresses d'accès des Raspberry pi dans la configuration de Home Assistante.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo vim /home/homeassistant/.homeassistant/configuration.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;et ajouter les lignes suivantes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;media_player:
        - platform: mpd
          host: 192.168.0.48
          port: 6600
          name: rpi48
        - platform: mpd
          host: 192.168.0.35
          port: 6600
          name: rpi35
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"platform": correspond à l'entité utilisé&lt;br&gt;
"host": c'est l'adresse IP de la machine à contrôler&lt;br&gt;
"port": le port d'accès&lt;br&gt;
"name": le nom qui apparaitra dans l'interface de Home Assistant afin de le repérer plus facilement&lt;/p&gt;

&lt;p&gt;Ici, j'ai déclaré les 2 Raspberry pi que j'ai configuré précédement&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.home-assistant.io/integrations/mpd/" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Mise en place de l'espace media
&lt;/h2&gt;

&lt;p&gt;Il existe un espace par defaut où Home Assistant va chercher les fichiers audio. Ce dossier "media" se trouvant à "/home/homeassistant/.homeassistant/"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mkdir /home/homeassistant/.homeassistant/media
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C'est dans ce dossier que vous pouvez disposer vos fichiers audio à diffuser&lt;/p&gt;

&lt;h1&gt;
  
  
  Test
&lt;/h1&gt;

&lt;p&gt;Une fois Home Assistant configuré et démarré, il faut placer les fichiers audio dans le dossier "/home/homeassistant/.homeassistant/" du serveur Home Assistant et ajouter les cartes de contrôle de chaque Raspberry pi sur le reseau. &lt;/p&gt;

&lt;h2&gt;
  
  
  Ajout des cartes de contrôle de média
&lt;/h2&gt;

&lt;p&gt;Pour cela il faut cliquer sur l'icone "menu" en haut à droite de la page puis sur modifier le tableau de bord.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8dpde2939iayqr54mvn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8dpde2939iayqr54mvn.png" alt="Modifier le tableau de bord" width="306" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clic sur le bouton "Ajouter une carte"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F681altloq6s1zxsqcx75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F681altloq6s1zxsqcx75.png" alt="bouton ajouter carte" width="238" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans la nouvelle fenêtre, il faut chercher le terme "media" et cliquer sur la carte "Contrôle des medias"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6osh7qrcpi17s618nqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx6osh7qrcpi17s618nqs.png" alt="Selection carte contrôle media" width="533" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Selectionner une des entités proposé que nous avons déclaré dans le fichier de configuration "/home/homeassistant/.homeassistant/configuration.yaml" &lt;br&gt;
Pour mon exemple, ça sera les entités rpi48 et rpi35.&lt;/p&gt;

&lt;p&gt;Une fois les cartes ajoutées, il est maitenant possible de diffuser un fichier audio vers le périphéque souhaité.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diffusion du fichier audio
&lt;/h2&gt;

&lt;p&gt;Pour diffuser un fichier audio que nous avons stocké au préalable sur le serveur Home Assistant, il faut repérer la carte du raspberry pi souhaité et cliquer sur le logo "media" en bas à droite de la carte.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7jcndwejfzwmnsqc3it.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7jcndwejfzwmnsqc3it.png" alt="logo media" width="491" height="184"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Selectioner "Local Media" et sélectionner le fichier audio souhaité.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45sefb7qgun140nda1pb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45sefb7qgun140nda1pb.png" alt="selection media" width="698" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;le fichier est maintenant lu sur le périphérique souhaité&lt;/p&gt;

&lt;h1&gt;
  
  
  lien supplémentaire
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=0YnSwYFpzDA" rel="noopener noreferrer"&gt;video explicative&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>stream</category>
      <category>modipy</category>
      <category>homeassistant</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Streamer une capture vidéo de sa webcam avec VLC</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Wed, 06 Apr 2022 11:36:08 +0000</pubDate>
      <link>https://dev.to/multiplaie/streamer-une-capture-video-de-sa-webcam-avec-vlc-216j</link>
      <guid>https://dev.to/multiplaie/streamer-une-capture-video-de-sa-webcam-avec-vlc-216j</guid>
      <description>&lt;p&gt;Dans cette note, je vais énumérer les étapes afin de streamer une webcam depuis VLC. Dans un premier temps, je détaillerai comment capturer la vidéo de la webcam avec VLC et ensuite comment l'envoyer dans un flux HTTP par l'interface graphique et, finalement, en ligne de commande dans le but de l'intégrer dans un projet annexe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;note à moi même :&lt;/strong&gt;&lt;br&gt;
Toute l'opération se passe dans un environnement Linux. Pour Windows, c'est sensiblement la même chose. Cependant, mes essais sous Windows 11 n'ont pas pu aboutir à cause du problème suivant :&lt;br&gt;
dshow:// non accéssible&lt;/p&gt;
&lt;h1&gt;
  
  
  Depuis l'interface graphique de VLC
&lt;/h1&gt;
&lt;h2&gt;
  
  
  La capture vidéo
&lt;/h2&gt;

&lt;p&gt;Pour capturer le flux vidéo de la webcam, ouvrir VLC et rendez-vous sur Média &amp;gt; Ouvrir un périphérique de capture (Ctrl+C)&lt;/p&gt;

&lt;p&gt;Dans cette fenêtre, l'onglet "Périphérique de capture" est sélectionné.&lt;/p&gt;

&lt;p&gt;Puis, configure les options suivantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sélectionner "Video Camera" dans "Capture mode"&lt;/li&gt;
&lt;li&gt;Nom du périphérique vidéo &amp;gt; /dev/video0&lt;/li&gt;
&lt;li&gt;Nom du périphérique audio &amp;gt; hw:0,0&lt;/li&gt;
&lt;li&gt;Standard vidéo &amp;gt; Tous&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ces options correspondent à mon système actuel est peuvent être différente en fonction des versions du soft.&lt;/p&gt;

&lt;p&gt;Terminer la configuration en cliquant sur le bouton "Lire" en bas de la fenêtre.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh14h7xb4fe62lmtcp1qq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh14h7xb4fe62lmtcp1qq.png" alt="ouvrir media capture" width="760" height="481"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Stream vidéo
&lt;/h2&gt;

&lt;p&gt;Afin de Streamer sa webcam depuis VLC&lt;br&gt;
Media &amp;gt; Stream (ou Ctrl+s)&lt;/p&gt;

&lt;p&gt;Sélectionner l'onglet "Périphérique de capture"&lt;/p&gt;

&lt;p&gt;La configuration à adopté est la même que la capture de vidéo.&lt;/p&gt;

&lt;p&gt;Puis cliquer sur "Stream"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2ilo6dnb6rft7pft79e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd2ilo6dnb6rft7pft79e.png" alt="ouvrir media stream" width="760" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Valider la fenêtre suivante "Source", valider sans changer les options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx5ywlnhxjnwiad8tflek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx5ywlnhxjnwiad8tflek.png" alt="configure source stream" width="745" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ensuite, dans la fenêtre "Destination setup", sélectionner la destination "HTTP" et cliquer sur "Ajouter". Un onglet HTTP va s'ajouter avec le paramétrage du port souhaité (par defaut 8080) ainsi que le chemin d'accès (par défaut /)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwa7q5wt8lp3oikhv7zcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwa7q5wt8lp3oikhv7zcd.png" alt="selection flux http stream" width="745" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7ldf0l11uyxudll84hl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi7ldf0l11uyxudll84hl.png" alt="configure flux http stream" width="745" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans la fenêtre correspondant à l'encodage, sélectionner le profil MP4.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6296r2wpf5gaifzgbj2l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6296r2wpf5gaifzgbj2l.png" alt="configure transcodage stream" width="745" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pour finir, dans la fenêtre des options, appuyer simplement sur le bouton stream.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwfdb2wvfc88zo8s4bwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqwfdb2wvfc88zo8s4bwv.png" alt="configure flux sortie stream" width="745" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Votre stream est en cours et accessible à l'adresse IP de la machine sur le port sélectionné précédemment.&lt;/p&gt;
&lt;h1&gt;
  
  
  En ligne de commande
&lt;/h1&gt;

&lt;p&gt;Voici de manière brève la commande correspondant à mon besoin et à la configuration effectué précédemment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vlc -vvv v4l2:///dev/video0 --sout '#transcode{vcodec=h264,acodec=mpga,vb=800,ab=128}:standard{access=http,mux=flv,dst=:8080}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En quelques mots:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vvv : verbose&lt;/li&gt;
&lt;li&gt;v4l2:///dev/video0 : la capture de ma webcam&lt;/li&gt;
&lt;li&gt;--sout : paramètre de sortie de video&lt;/li&gt;
&lt;li&gt;#transcode{vcodec=h264,acodec=mpga,vb=800,ab=128} : encodage&lt;/li&gt;
&lt;li&gt;:standard{access=http,mux=flv,dst=:8080} : ouverture http + format de la vidéos + adresse d'accès&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Fonctionnalités optionnelles
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Ajouter un filigrane
&lt;/h2&gt;

&lt;p&gt;Certaines options sont persistante et peuvent être configuré via l'interface graphique en amont.&lt;/p&gt;

&lt;p&gt;Pour ajouter un logo en filigrane des vidéos, il faut ouvrir VLC et faire "clic droit"&amp;gt; outils &amp;gt; effets et filtres (Ctrl+e)&lt;/p&gt;

&lt;p&gt;Sélectionner Effets vidéos dans le premier menu d'onglet et Incrustation dans le second.&lt;/p&gt;

&lt;p&gt;Cocher la case Ajouter un logo puis parcourir vos dossiers pour sélectionner le logo souhaité.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvguhimv4ow8ej6cn686.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyvguhimv4ow8ej6cn686.png" alt="Ajout filigrane" width="645" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Voila, le logo sera posé sur votre vidéo chaque fois que VLC sera utilisé.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limiter la durée de la diffusion
&lt;/h2&gt;

&lt;p&gt;Afin de déterminer un temps de diffusion, il est nécessaire d'utiliser la commande timeout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timeout 10 vlc -vvv v4l2:///dev/video0 --sout '#transcode{vcodec=h264,acodec=mpga,vb=800,ab=128}:duplicate{dst=http{mux=flv,dst=:8080}, dst=file{dst=/home/remy/Vidéos/video.mp4, no-overwrite}, dst=display{delay=6000}}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dans cette exemple, je diffuse dans un flux http en local, j'enregistre dans un fichier et j'affiche le retour de la webcam durant 10 secondes.&lt;/p&gt;

&lt;p&gt;Source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.videolan.org/VLC_command-line_help/" rel="noopener noreferrer"&gt;utilisation de vlc en ligne de command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.videolan.org/Documentation:Streaming_HowTo/" rel="noopener noreferrer"&gt;utilistation du stream&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>stream</category>
      <category>vlc</category>
      <category>ubuntu</category>
      <category>linux</category>
    </item>
    <item>
      <title>Déclancher un signal midi avec NodeJs sur Windows</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Thu, 17 Feb 2022 16:53:58 +0000</pubDate>
      <link>https://dev.to/multiplaie/declancher-un-signal-midi-avec-nodejs-sur-windows-4hnn</link>
      <guid>https://dev.to/multiplaie/declancher-un-signal-midi-avec-nodejs-sur-windows-4hnn</guid>
      <description>&lt;p&gt;Je vais présenter ici un script simpliste pour générer un signal midi depuis une application NodeJs.&lt;/p&gt;

&lt;h1&gt;
  
  
  Installation
&lt;/h1&gt;

&lt;p&gt;Pour cet exemple, nous avons besoin de :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NodeJs&lt;/li&gt;
&lt;li&gt;Express&lt;/li&gt;
&lt;li&gt;EasyMidi
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir Project
cd Project
npm init
npm install express
npm install easymidi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En complément, il nous faut installer 2 logiciels tiers :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.tobias-erichsen.de/software/loopmidi.html" rel="noopener noreferrer"&gt;LoopMidi&lt;/a&gt; qui nous permet de créer un périphérique midi virtuel&lt;br&gt;
et&lt;br&gt;
&lt;a href="http://www.midiox.com/" rel="noopener noreferrer"&gt;Midi-OX&lt;/a&gt; qui permet de visualiser les logs.&lt;/p&gt;
&lt;h1&gt;
  
  
  Configuration
&lt;/h1&gt;
&lt;h2&gt;
  
  
  LoopMidi
&lt;/h2&gt;

&lt;p&gt;Démarre LoopMidi et créé un nouveau périphérique en lui assignant un nom et en appuyant sur le bouton "+"&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fforyw3k2kzx8uh5d7utd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fforyw3k2kzx8uh5d7utd.png" alt="add loopmidi device" width="493" height="331"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Midi-OX
&lt;/h2&gt;

&lt;p&gt;Démarre Midi-OX et affiche le moniteur si il n'apparait pas&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1i5va76xniewug1kf5z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh1i5va76xniewug1kf5z.png" alt="open monitor midi-ox" width="691" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ensuite, plug LoopMidi avec midi-ox en allant dans Options-&amp;gt;Midi Devices&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frsi42oaspwp1vkpz0adt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frsi42oaspwp1vkpz0adt.png" alt="option midi-Ox" width="691" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pour finir, sélectionne le périphérique que vous avez créé avec LoopMidi dans la partie "MIDI Inputs" (1) et "Microsoft GS Wavetable Synth" dans "MIDI Outputs" (3). Si tout est ok, ils apparaîtront respectivement dans "Port Map Objects" (2) et "Port Mapping" (3).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd27nreaczmdldj7p2rna.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd27nreaczmdldj7p2rna.png" alt="midi-ox midi device" width="468" height="383"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Main.js
&lt;/h2&gt;

&lt;p&gt;Dans le dossier du projet, ajoute le fichier main.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let easymidi = require("easymidi")
let express = require('express')
let output = new easymidi.Output("NAME OF VIRTUAL DEVICE")
const app = express()
let is_on = false;
app.get('/', function (req, res) {
    res.sendFile(__dirname + '/index.html');
})

app.post('/click', (req, res)=&amp;gt;{
    console.log('received')

    output.send("cc", {
        controller: 37,
        value: (is_on) ? 0:127,
        channel: 0
    })

    is_on = !is_on;
    res.sendStatus(201);
})

app.listen(3000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce script génère un serveur http affichant simplement un bouton. &lt;br&gt;
Le but du script est de déclancher un signal midi chaque fois que le bouton "ON/OFF" est cliqué&lt;/p&gt;

&lt;p&gt;Pour lier le script au périphérique midi virtuel, il faut indiquer le nom du périphérique à la ligne suivant :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let output = new easymidi.Output("NAME OF VIRTUAL DEVICE")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensuite, création du fichier index.html suivant :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;button id="button"&amp;gt;ON/OFF&amp;lt;/button&amp;gt;
    &amp;lt;script&amp;gt;
        document.getElementById('button').onclick = ()=&amp;gt;{

            fetch('/click', {method: 'POST'})
        }
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Start
&lt;/h1&gt;

&lt;p&gt;Execute le script main.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et ouvre la page &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; pour afficher le bouton d'envoi de signal midi. Chaque fois que le bouton est cliqué, un signal midi est envoyé au périphérique LoopMidi et intercepté par Midi-OX.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa03ouc7q1wwmyi7u48zg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa03ouc7q1wwmyi7u48zg.png" alt="log midi-ox" width="689" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bravo, tu sais envoyer un signal midi avec NodeJs :)&lt;/p&gt;

&lt;h1&gt;
  
  
  Sources
&lt;/h1&gt;

&lt;p&gt;Pour cette présentation, je me suis beaucoup aidé de cette &lt;a href="https://www.youtube.com/watch?v=vW2Lve_hMzg" rel="noopener noreferrer"&gt;vidéo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pour en savoir plus sur les signaux possible à envoyer, je conseille de lire la section "Message Reference" sur le &lt;a href="https://github.com/dinchak/node-easymidi" rel="noopener noreferrer"&gt;github de easymidi&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Tous les codes pour cet exemple sont disponible sur &lt;a href="https://github.com/multiplaie/arduino-madmapper-js-control" rel="noopener noreferrer"&gt;mon github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>midi</category>
      <category>easymidi</category>
      <category>express</category>
    </item>
    <item>
      <title>Systeme de communication entre NodeJS, Madmapper et arduino</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Thu, 17 Feb 2022 11:16:26 +0000</pubDate>
      <link>https://dev.to/multiplaie/systeme-de-communication-entre-nodejs-madmapper-et-arduino-4fco</link>
      <guid>https://dev.to/multiplaie/systeme-de-communication-entre-nodejs-madmapper-et-arduino-4fco</guid>
      <description>&lt;p&gt;Dans le cadre de création d'une pièce d'escape game, nous expérimentons le logiciel Madmapper pour gérer toute la partie audio visuelle. La projection vidéo, le son et la lumière sont au centre de l'univers du jeu.&lt;/p&gt;

&lt;p&gt;Concernant les interactions avec les joueurs et les énigmes, nous souhaitons contrôler ces instruments par Arduino.&lt;/p&gt;

&lt;p&gt;Dans les prévisions, nous souhaitons contrôler la salle via une application web afin d'y accéder depuis n'importe quel périphérique (mobile, desktop...)&lt;/p&gt;

&lt;p&gt;Les énigmes sont gérés sur arduino, madmapper récupère l'information et une application Web nous informe du status de la room et éventuellement nous permet de déclancher .&lt;/p&gt;

&lt;p&gt;Bonne surprise, Madmapper intègre Firmata. C'est un outil de contrôle Arduino facilitant la communication entre ce dernier et un logiciel tier. Ce qui permet de nouvelles possibilités d'interaction.&lt;/p&gt;

&lt;p&gt;Concernant l'application Web, pour mon confort, elle est en NodeJS. &lt;/p&gt;

&lt;h1&gt;
  
  
  Problématique
&lt;/h1&gt;

&lt;p&gt;Intuitivement, aillant Firmata sur le Arduino, je pensais pouvoir contrôler ce dernier avec un montage parallèle entre Madmapper et NodeJS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwq10281sx1873frcr0sf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwq10281sx1873frcr0sf.jpg" alt="azdazd" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Malheureusement, si le Arduino est en communication avec Madmapper, NodeJS ne peut interagir avec le Arduino car le port et occupé. Je pars donc sur un montage en série des applications pour simplifier le flux d'information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faf93aewjeb8xnobswxdj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faf93aewjeb8xnobswxdj.jpg" alt="montage serie" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans cet exemple, nous allons préparer un interrupteur pour allumer et éteindre une LED.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note : Pour ce développement, je suis sous un environnement Windows, ce qui affect la création de périphérique virtuel par NodeJs. C'est pour cela que nous installerons des applications tier pour palier à certain problème.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Hardware et Software
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Arduino Uno &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.robot-maker.com/ouvrages/wp-content/uploads/2016/07/montageLed-1024x732.png" rel="noopener noreferrer"&gt;Petit montage électrique avec une LED et une résistance&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://madmapper.com/" rel="noopener noreferrer"&gt;MadMapper&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.midiox.com" rel="noopener noreferrer"&gt;Midi-OX&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tobias-erichsen.de/software/loopmidi.html" rel="noopener noreferrer"&gt;LoopMidi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.arduino.cc/en/software" rel="noopener noreferrer"&gt;IDE Arduino&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;NodeJs&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/easymidi" rel="noopener noreferrer"&gt;EasyMidi&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/express" rel="noopener noreferrer"&gt;Express&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  ​Preparation de l'environnement de travail
&lt;/h1&gt;

&lt;h2&gt;
  
  
  ​LoopMidi
&lt;/h2&gt;

&lt;p&gt;Le logiciel permet de créer un périphérique midi virtuel. Il nous permet de communiquer entre l'app NodeJs et Madmapper.&lt;br&gt;
Une fois l'installation effectuée, déclare un nouveau périphérique en lui indiquant un nom (1) en cliquant sur le "+" en bas à gauche de la fenêtre. (2)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsue9cz99zxwk9gnpro4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnsue9cz99zxwk9gnpro4.png" alt="ajouter un périphérique midi" width="493" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Personnellement, j'ai laissé le nom par défaut&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  ​Midi-OX
&lt;/h2&gt;

&lt;p&gt;Midi-OX est un logiciel de consultation de log Midi. Une fois le périphérique sélectionné, nous pouvons consulter les communications IN et OUT avec ce dernier.&lt;/p&gt;

&lt;p&gt;Ouvrez Midi-OX et le moniteur si ce dernier ne s'ouvre pas automatiquement&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F538djpbvu61uo6pnb6l5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F538djpbvu61uo6pnb6l5.png" alt="midi-ox monitor" width="691" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Allez dans Options-&amp;gt;Midi Devices pour lier Midi-OX à LoopMidi&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe838fdw2lblqa90rmpig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe838fdw2lblqa90rmpig.png" alt="midi-ox options" width="691" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sélectionne le périphérique que vous avez créé avec LoopMidi dans la partie "MIDI Inputs" (1) et "Microsoft GS Wavetable Synth" dans "MIDI Outputs" (3). Si tout est ok, ils apparaîtront respectivement dans "Port Map Objects" (2) et "Port Mapping" (3).&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm96sd4xfyig5h2ycyfsg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm96sd4xfyig5h2ycyfsg.png" alt="midi-ox midi inputs" width="468" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Validez la sélection avec le bouton "OK".&lt;/p&gt;

&lt;p&gt;Un message "Opened MIDI INPUT" et "Opened MIDI OUTPUT" apparaîtront dans le moniteur&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdn00jwag77f0wxtd23hm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdn00jwag77f0wxtd23hm.png" alt="monitor midi-ox" width="691" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lors des tests, nous pourrons observer les logs dans ce moniteur.&lt;/p&gt;
&lt;h2&gt;
  
  
  ​Installation de Firmata dans le Arduino Uno et communication avec MadMapper
&lt;/h2&gt;

&lt;p&gt;Une très bonne vidéo explique tout cela &lt;a href="https://www.youtube.com/watch?v=muosra5uIAs" rel="noopener noreferrer"&gt;ici&lt;/a&gt; :) &lt;/p&gt;
&lt;h1&gt;
  
  
  ​Communication NodeJs et MadMapper
&lt;/h1&gt;

&lt;p&gt;Pour communiquer avec MadMapper depuis NodeJs, nous allons envoyer un signal MIDI depuis NodeJs et l'interpréter dans MadMapper&lt;/p&gt;
&lt;h2&gt;
  
  
  EasyMidi
&lt;/h2&gt;

&lt;p&gt;Afin de déclencher un signal midi, nous allons utiliser le périphérique virtuel LoopMidi avec NodeJs.&lt;br&gt;
Dans votre dossier de projet :&lt;/p&gt;

&lt;p&gt;Initialisez le projet :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Installez easymidi :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm intall easymidi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et créé le script main.js :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let easymidi = require("easymidi") //on déclare easymidi
let output = new easymidi.Output("loopMIDI Port") //On connecte easymidi au periphérique midi virtuel loopMIDI grâce à son nom
// on envoi le signal midi
output.send("cc", {
    controller: 37, // numéro de la commande
    value: 127, // valeur de la commande [0-127]
    channel: 0 // channel midi
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dans le cadre de notre interrupteur, nous allons utiliser l'opérateur "change control" qui change d'état quand on l'active et reste dans ce même état jusqu'à ce qu'on le déclenche de nouveau (un interrupteur en somme).&lt;/p&gt;

&lt;p&gt;Le numéro contrôleur 37 est reconnue comme une commande "change control". En fonction de cette valeur, il peut être reconnu comme un changement de pitch ou de modulation ou beaucoup d'autre commande.&lt;br&gt;
Entre 30 et 40 (à vérifier) sont reconnues les commandes de type Gate.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note : il est possible de créer un périphérique midi virtuel à la volée avec easymidi. Cependant, Windows bloque sa création :&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Je n'ai pas essayé avec d'autres OS mais la plupart des tutos trouvé sur YouTube sont iOS et semble fonctionner sans trop de difficultés.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=vW2Lve_hMzg" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En lançant le script, une ligne apparaît dans la console de log de Midi-OX.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vmn5x5h4bng9ykyz1q0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vmn5x5h4bng9ykyz1q0.png" alt="log monitor" width="689" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nous pourrons donc par la suite configurer un déclencheur avec MadMappeur avec cette commande.&lt;/p&gt;

&lt;h2&gt;
  
  
  Création page web express
&lt;/h2&gt;

&lt;p&gt;À présent, nous allons modifier le script pour créer une page web et y afficher un bouton pour déclencher plus facilement la commande midi avec express.&lt;/p&gt;

&lt;p&gt;Installation de express&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ajoutez le fichier index.html pour le front&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- ./index.html --&amp;gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;
    &amp;lt;title&amp;gt;Panel Control&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;button id="button"&amp;gt;ON/OFF&amp;lt;/button&amp;gt;
    &amp;lt;script&amp;gt;
        document.getElementById('button').onclick = () =&amp;gt; {
            fetch('/click', {method: 'POST'})
        }
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et le back&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let easymidi = require("easymidi")
let express = require('express')
let output = new easymidi.Output("loopMIDI Port")
const app = express()
let is_on = false;
app.get('/', function (req, res) {
    res.sendFile(__dirname + '/index.html');
})

app.post('/click', (req, res)=&amp;gt;{
    console.log('received')

    output.send("cc", {
        controller: 37,
        value: (is_on) ? 0:127,
        channel: 0
    })

    is_on = !is_on;
    res.sendStatus(201);
})

app.listen(3000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coté front, le but est simple: lorsqu'on clique sur le bouton, on envoie une requête POST au endpoint "/click" pour allumer ou éteindre la LED.&lt;/p&gt;

&lt;p&gt;Coté back, nous mettons en place l'API:&lt;/p&gt;

&lt;p&gt;Implémentation de express&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let express = require('express') 
const app = express()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Création de l'index&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.get('/', function (req, res) {
    res.sendFile(__dirname + '/index.html');
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Création de l'endpoint&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let is_on = false;
app.post('/click', (req, res)=&amp;gt;{
    console.log('received')
    output.send("cc", {
        controller: 37,
        value: (is_on) ? 0:127,
        channel: 0
    })

    is_on = !is_on;
    res.sendStatus(201);
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;À présent, nous pouvons tester le script et voir le resultat dans la console de log de Midi-OX.&lt;/p&gt;

&lt;p&gt;Le script est prêt&lt;/p&gt;

&lt;h2&gt;
  
  
  ​Parametrage d'action midi sur MadMapper
&lt;/h2&gt;

&lt;p&gt;Rendez-vous sur MadMapper et connectez l'application au Arduino avec le module Firmata. Ensuite, choisissez un pin (je préconise le Pin3 pour le test) et cliquez sur le bouton "OUTPUT" pour activer la sortie.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo3n03v0f1npynghsisad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo3n03v0f1npynghsisad.png" alt="pin3" width="258" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La LED s'illumine. Dans le cas contraire, il faut sélectioner de nouveau le model du Arduino et le port pour forcer la connexion.&lt;/p&gt;

&lt;p&gt;Continuons avec click droit sur le bouton "OUTPUT" -&amp;gt; Add Control -&amp;gt; MIDI&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5u1qg6dj7ibqlm9p421y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5u1qg6dj7ibqlm9p421y.png" alt="learn midi 1" width="451" height="204"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans le nouveau panneau qui apparaît, clique sur le bouton "Learn"&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F537s6dyib57vj8plxz1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F537s6dyib57vj8plxz1k.png" alt="learn midi 2" width="346" height="108"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;execute l'application NodeJs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node main.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;et clique sur le bouton ON/OFF de votre page. MadMapper va alors capturer l'information et l'assigner au bouton "OUTPUT"&lt;/p&gt;

&lt;p&gt;Désormais, chaque fois que vous cliquerez sur ce bouton "ON/OFF" vous commanderez la LED :D&lt;/p&gt;

&lt;p&gt;Félicitations !&lt;/p&gt;

</description>
      <category>arduino</category>
      <category>madmapper</category>
      <category>node</category>
      <category>midi</category>
    </item>
    <item>
      <title>Installation mongoDB sur raspberry pi 3</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Tue, 26 Oct 2021 10:38:30 +0000</pubDate>
      <link>https://dev.to/multiplaie/installation-mongodb-sur-raspberry-pi-3-2oij</link>
      <guid>https://dev.to/multiplaie/installation-mongodb-sur-raspberry-pi-3-2oij</guid>
      <description>&lt;p&gt;Après de nombreuses tentatives, j'ai compris pourquoi le RPI3/Raspbian ne permet pas d'installer la dernière version de MongoDB:&lt;br&gt;
L'architecture n'est pas adaptée... Et ça ! Personne n'en parle.&lt;/p&gt;

&lt;p&gt;Nous allons donc voir comment contourner toutes ces problématiques.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 1 : installation d'Ubuntu sur le RPI3
&lt;/h2&gt;

&lt;p&gt;Téléchargez et installez &lt;a href="https://www.raspberrypi.com/software/" rel="noopener noreferrer"&gt;Raspberry Pi Imager&lt;/a&gt;.&lt;br&gt;
Choisir l'OS suivant : Other general purpose OS &amp;gt; Ubuntu &amp;gt; Ubuntu Server 20.04.3 LTS (64bits)&lt;br&gt;
Et installer tout ça sur la carte micro SD&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2 : Upgrade
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt-get update
$ sudo apt-get upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Tu connais les bails.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3 : installation MongoDB
&lt;/h2&gt;

&lt;p&gt;La meilleure succession de commande que j'ai trouvée, c'est &lt;a href="https://stackoverflow.com/questions/68937131/illegal-instruction-core-dumped-mongodb-ubuntu-20-04-lts" rel="noopener noreferrer"&gt;celle-là&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
$ echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
$ sudo apt-get update
$ sudo apt-get install mongodb-org=4.4.8 mongodb-org-server=4.4.8 mongodb-org-shell=4.4.8 mongodb-org-mongos=4.4.8 mongodb-org-tools=4.4.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vérifiez la version. (elle doit être au moins superieur a la versioin 3)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mongod --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Créez les dossiers pour le stockage des data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo mkdir /data
$ cd /data
$ sudo mkdir db
$ sudo pkill -f mongod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4 : Use as service
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl enable mongod.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;petite note:&lt;/strong&gt; la commande "mongod" permet de lancer un serveur mongodb et la commande "mongo" permet d'accéder a la console mongoDB. Il faut donc penser a lancer le service "mongod" avant d'accéder a la console :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 : accéder au serveur depuis l'exterieur
&lt;/h2&gt;

&lt;p&gt;Pour ma part, j'utilise l'application &lt;a href="https://www.mongodb.com/try/download/compass" rel="noopener noreferrer"&gt;compass&lt;/a&gt;. Pour connecter compass depuis un autre PC, il faut ouvrir le serveur MongoDB (sur le RPI) vers l'exterieur.&lt;/p&gt;

&lt;p&gt;Récupérez l'adresse IP du RPI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ifconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ouvre le fichier de configuration de MongoDB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nano /etc/mongod.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ajoutez l'adresse IP au fichier de configuration de MongoDB&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1,192.168.0.XX
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Redmarrez le service mongod&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo systemctl restart mongod.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aaaaaand it's done :)&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>mongodb</category>
      <category>french</category>
    </item>
    <item>
      <title>Gestion du son dans une salle d'escape game</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Mon, 25 Oct 2021 13:16:57 +0000</pubDate>
      <link>https://dev.to/multiplaie/gestion-du-son-dans-une-salle-descape-game-460b</link>
      <guid>https://dev.to/multiplaie/gestion-du-son-dans-une-salle-descape-game-460b</guid>
      <description>&lt;h1&gt;
  
  
  Le projet
&lt;/h1&gt;

&lt;h2&gt;
  
  
  La disposition
&lt;/h2&gt;

&lt;p&gt;Le projet principal s'articule de la façon suivante :&lt;/p&gt;

&lt;p&gt;Dans une salle, 5 joueurs et un maître du jeu prennent place. Cette salle accueillera un jeu de rôle sur table durant 1 h et plongera les joueurs dans une ambiance en lien avec les événements du jeu.&lt;br&gt;
Le maître du jeu sera le seul lien avec l'histoire. Il mettra en scène les situations et sera garant de l'interaction des joueurs dans le scénario.&lt;/p&gt;

&lt;p&gt;Le Maitre du jeu est éloigné des joueurs afin de gérer la mise en place des ambiances derrière son pupitre. Les joueurs, quant à eux, seront placés face au MJ derrière une table. Cette table est la seule interaction physique qu'il y aura entre les joueurs et l'histoire.&lt;/p&gt;
&lt;h2&gt;
  
  
  L'ambiance
&lt;/h2&gt;

&lt;p&gt;Pour générer une ambiance dans laquelle les joueurs seront plongés, il est nécessaire de d'utiliser les éléments suivants :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Les lumières&lt;/li&gt;
&lt;li&gt;Le son&lt;/li&gt;
&lt;li&gt;La décoration&lt;/li&gt;
&lt;li&gt;Les objets d'interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ce document se concentrera uniquement sur la diffusion du/des sons dans la salle.&lt;/p&gt;

&lt;p&gt;Pour créer une ambiance sonore, nous avons besoin des éléments suivants :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un système son (stéréo pour le moment)&lt;/li&gt;
&lt;li&gt;Des sons d'ambiances bouclé et permanant par scène&lt;/li&gt;
&lt;li&gt;Des sons d'objet ou d'animaux pouvant intervenir par-dessus la musique d'ambiance sur décision du MJ&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;
  
  
  Le materiel
&lt;/h1&gt;

&lt;p&gt;Principalement, tout sera géré par un Raspberry pi 3. Il jouera le rôle de serveur d'application. Sur ce dernier, seront installées VLC ainsi que les musiques à jouer.&lt;/p&gt;

&lt;p&gt;Les requêtes seront envoyées depuis une PC sous Windows depuis la console Powershell.&lt;/p&gt;
&lt;h2&gt;
  
  
  Petite astuce
&lt;/h2&gt;

&lt;p&gt;Pour télécharger des musiques depuis YouTube vers le RPI, la lib &lt;a href="https://doc.ubuntu-fr.org/youtube-dl" rel="noopener noreferrer"&gt;youtube-dl&lt;/a&gt; fais complètement le café.&lt;/p&gt;
&lt;h1&gt;
  
  
  Execution
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Installation du RPI
&lt;/h2&gt;

&lt;p&gt;Le RPI sera le serveur principal de toutes les musiques d'ambiance. C'est par lui que passeront toutes les requêtes de lecture des musiques ou des sons additionnel.&lt;/p&gt;
&lt;h3&gt;
  
  
  Connexion SSH
&lt;/h3&gt;

&lt;p&gt;La connexion SSH entre le pc windows et le rpi sous linux permettra aux deux periphérique de communiquer.&lt;/p&gt;
&lt;h4&gt;
  
  
  Sur le RPI
&lt;/h4&gt;

&lt;p&gt;Activons le service sur le RPI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
$ sudo raspi-config

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sélectionnez les éléments suivants :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
3 Interface Options &amp;gt; P2 SSH &amp;gt; YES

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le service est maintenant actif. Récupérez l'adresse IP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ifconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et passez sur le PC Windows.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sur Windows
&lt;/h4&gt;

&lt;p&gt;Testons la connexion ssh avec Powershell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh pi@XXX.XXX.X.XX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le Mot de passe par défaut est :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;raspberry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si la connexion n'aboutit pas, vérifiez vos connexions et &lt;a href="https://doc.ubuntu-fr.org/ssh" rel="noopener noreferrer"&gt;RTFM&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Afin d'éviter de rentrer le mot de passe à chaque connexion, nous allons créer une clé publique.&lt;/p&gt;

&lt;p&gt;Dans Powershell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-keygen.exe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Puis validez. Validez également sans modification tous les points qui vous seront présentés.&lt;/p&gt;

&lt;p&gt;Puis, envoyez votre clé publique au RPI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh pi@XXX.XXX.X.XX mkdir -p .ssh
$ pi@XXX.XXX.X.XX's password:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat .ssh/id_rsa.pub | ssh pi@XXX.XXX.X.XX 'cat &amp;gt;&amp;gt; .ssh/authorized_keys'
$ pi@XXX.XXX.X.XX's password:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vous pouvez maintenant vous connecter sans rentrer de mot de passe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ssh pi@XXX.XXX.X.XX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si un problème persiste -&amp;gt; &lt;a href="http://www.linuxproblem.org/art_9.html" rel="noopener noreferrer"&gt;RTFM&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  VLC
&lt;/h3&gt;

&lt;p&gt;Procèdons à l'installation de VLC sur le RPI&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://doc.ubuntu-fr.org/vlc" rel="noopener noreferrer"&gt;RPI&lt;/a&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
$ sudo apt-get install vlc

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... Classique&lt;/p&gt;

&lt;p&gt;VLC sera utilisé entierement par la console. En voici là &lt;a href="https://wiki.videolan.org/VLC_command-line_help/" rel="noopener noreferrer"&gt;doc des commandes avancées&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Téléchargement des musiques d'exemple
&lt;/h3&gt;

&lt;p&gt;La Lib &lt;a href="https://doc.ubuntu-fr.org/youtube-dl" rel="noopener noreferrer"&gt;youtube-dl&lt;/a&gt; permet de télécharger facilement des vidéos ou des audios à partir d'un lien YouTube.&lt;br&gt;
La lib dans les dépôts Ubuntu ne sont pas stable. Il faut donc télécharger le package depuis le site officiel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo wget https://yt-dl.org/latest/youtube-dl -O /usr/local/bin/youtube-dl
$ sudo chmod a+x /usr/local/bin/youtube-dl
$ hash -r
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour les essais, nous allons travailler avec les musiques et sons suivants :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=O1wtKpdgk9c" rel="noopener noreferrer"&gt;Musique d'ambiance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=-4_CIMDuoLM" rel="noopener noreferrer"&gt;Son chauve-souris&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour les télécharger&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd ~
$ youtube-dl -x --audio-format mp3 https://www.youtube.com/watch?v=O1wtKpdgk9c
$ mv Megan McDuffee - Don't Go In There _ Scary Dark Ambient Music-O1wtKpdgk9c.mp3 ambiance.mp3
$ youtube-dl -x --audio-format mp3 https://www.youtube.com/watch?v=-4_CIMDuoLM
$ mv BATS SOUND EFFECT IN HIGH QUALITY--4_CIMDuoLM.mp3 bats.mp3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;NB: Lors de l'application de ce tuto sur une version LTS d'ubuntu, il a fallut que j'effectue des opération supplémentaire:&lt;/p&gt;

&lt;p&gt;youtube-dl utilise python et par defaut, uniquement python3 est installé. j'ai donc utilisé la commande suivante:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install python-is-python3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://askubuntu.com/questions/320996/how-to-make-python-program-command-execute-python-3" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Il me manquait également une librairie ffmpeg&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt-get install ffmpeg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/30770155/ffprobe-or-avprobe-not-found-please-install-one" rel="noopener noreferrer"&gt;source&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Notez que les sons ont été téléchargés dans votre home :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/pi/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tests des sorties son
&lt;/h3&gt;

&lt;p&gt;Pour tester les sons téléchargés :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ vlc ambiance.mp3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Normalement, le son devrait sortir par la sortie audio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serveur web VLC
&lt;/h2&gt;

&lt;p&gt;Afin de pouvoir jouer plusieurs sons en même temps, nous devons clarifier les besoins :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Une musique ambiante tourne en continue durant toute une scène. Elle ne s'arrêtera que lorsque le besoin de changer de scène se fera ressentir. Néanmoins, la musique doit pouvoir être contrôlable pour être changé, baisser ou augmenter le volume, simplement stopper.&lt;/li&gt;
&lt;li&gt;Des sons court d'ambiance vont être joué de façon impromptu suivant les besoins du MJ. Le son ne sera joué qu'une seule fois juste le temps nécessaire.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Dans ces conditions, la solution s'offrant à nous est le suivant :&lt;br&gt;
Mettre la musique principale sur un serveur web audio VLC et les autres sons seront lancé par ligne de commande en fonction des besoins.&lt;/p&gt;
&lt;h3&gt;
  
  
  Pourquoi un serveur WEB
&lt;/h3&gt;

&lt;p&gt;Le Serveur, web VLC, contrairement à son utilisation par ligne de commande, permet de garder la main sur la lecture de la musique en cours par le biais de requeté web.&lt;br&gt;
La liste des requêtes disponible est énuméré dans cette &lt;a href="https://wiki.videolan.org/VLC_HTTP_requests/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pour lancer l'interface web de VLC, il faut exécuter la ligne de commande suivante :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cvlc -I http --http-password xxx -L -R
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Legende&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;-I:&lt;/td&gt;
&lt;td&gt;Sélection de l'interface. Ici, c'est http&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--http-password:&lt;/td&gt;
&lt;td&gt;Mise en place d'un mot de passe d'accès (ici, c'est xxx, mais c'est à vous de le changer.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-L:&lt;/td&gt;
&lt;td&gt;activation de la loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-R:&lt;/td&gt;
&lt;td&gt;activation du repeat&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Rendez-vous sur sur le PC Windows et sur votre navigateur preferé pour voir cette interface. Par défaut :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;192.168.X.XX:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un mot de passe vous sera demandé pour accéder à l'interface. Laissez le Username vide et le mot de passe est celui rentré dans la ligne de commande précédente. (ici xxx si vous ne l'avez pas changé)&lt;/p&gt;

&lt;p&gt;Si vous souhaitez jouer une musique directement au lancement de l'interface, vous pouvez ajouter la piste souhaitée dans la ligne de commande :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cvlc -I http --http-password xxx -L -R /home/pi/ambiance.mp3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mise en place du service
&lt;/h3&gt;

&lt;p&gt;Afin de ne pas avoir la console hors d'usage durant la mise en service du serveur web VLC, il est nécessaire de transformer la commande en service daemon au démarrage du RPI.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://linuxconfig.org/how-to-run-script-on-startup-on-ubuntu-20-04-focal-fossa-server-desktop" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;STEP 1 : Écrire le service&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nano /etc/systemd/system/vlc-web.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
After=network.service

[Service]
ExecStart=/usr/local/bin/vlc-web-startup.sh

[Install]
WantedBy=default.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;STEP 2 : Écrire le script&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo nano /usr/local/bin/vlc-web-startup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

cvlc -I http --http-password pwd -q -L -R
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;STEP 3 : Gérer les droits de lecture&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo chmod 744 /usr/local/bin/vlc-web-startup.sh
$ sudo chmod 664 /etc/systemd/system/disk-space-check.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;STEP 4 : Permettre le lancement de VLC par root&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo sed -i 's/geteuid/getppid/' /usr/bin/vlc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vous pouvez maintenant redémarrer votre RPI et le serveur se lancera automatiquement au démarrage à la même adresse que noter plus haut.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test des requêtes avec Postman
&lt;/h3&gt;

&lt;p&gt;Pour tester notre serveur, nous allons utiliser l'application &lt;a href="https://www.postman.com/" rel="noopener noreferrer"&gt;Postaman&lt;/a&gt; depuis notre PC Windows&lt;/p&gt;

&lt;p&gt;Durant les tests, n'oubliez pas de remplir les identifiants de connexion dans l'onglet "Authorization" et de sélection "Basic Auth". Laissez Username vide et saisissez votre mot de passe.&lt;/p&gt;

&lt;p&gt;Ajouter une musique dans la playlist et jouez la :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://192.168.X.XX:8080/requests/status.xml?command=in_play∈put=/home/pi/ambiance.mp3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Votre musique sera jouée maintenant en fond sonore et vous pouvez aussi profiter pleinement de votre shell :)&lt;/p&gt;

&lt;p&gt;Toutes les commandes par requête http sont disponible dans cette &lt;a href="https://wiki.videolan.org/VLC_HTTP_requests/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Lancement des sons additionnel
&lt;/h2&gt;

&lt;p&gt;Nous y voici enfin : lancer des sons additionnels au-dessus de la musique d'ambiance.&lt;/p&gt;

&lt;p&gt;Maintenant, que tout est optimisé, une simple requête permet de réaliser votre rêve.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cvlc --no-one-instance --play-and-exit -q /home/pi/bats.mp3 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Legende&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;cvlc:&lt;/td&gt;
&lt;td&gt;C'est le client console&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--no-one-instance:&lt;/td&gt;
&lt;td&gt;Permet de lancer plusieurs instance VLC en meme temps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;--play-and-exit:&lt;/td&gt;
&lt;td&gt;Joue le son et quitte le processus VLC automatiquement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-q:&lt;/td&gt;
&lt;td&gt;N'affiche aucune sortie.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/home/pi/bats.mp3:&lt;/td&gt;
&lt;td&gt;Le lien vers le son&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;amp;:&lt;/td&gt;
&lt;td&gt;Permet a la commande de ne pas bloquer la console durant l'exécution du processus.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;ENJOY&lt;/p&gt;

</description>
      <category>vlc</category>
      <category>music</category>
      <category>french</category>
    </item>
    <item>
      <title>Construction des données de contrôle de luminosité d'une lampe Neewer 660</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Fri, 10 Sep 2021 15:58:18 +0000</pubDate>
      <link>https://dev.to/multiplaie/construction-des-donnees-de-controle-de-luminosite-d-une-lampe-neewer-660-57a8</link>
      <guid>https://dev.to/multiplaie/construction-des-donnees-de-controle-de-luminosite-d-une-lampe-neewer-660-57a8</guid>
      <description>&lt;h1&gt;
  
  
  Construction des données de controle de luminosité d'une lampe Neewer 660
&lt;/h1&gt;

&lt;p&gt;Dans le cadre des recherches sur le développement d'une application Windows pour contrôler  un panneau LED Neewer 660, je réunis dans ce document les informations concernant la construction des données permettant de changer la luminosité de la lampe.&lt;/p&gt;

&lt;h2&gt;
  
  
  La lampe Neewer 660
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fencrypted-tbn2.gstatic.com%2Fshopping%3Fq%3Dtbn%3AANd9GcSWXAlPyC13wczNB5BGfxdVoqcewBYacr1-3AJVDcdZ_l8Jpg3Sc9w%26usqp%3DCAc" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fencrypted-tbn2.gstatic.com%2Fshopping%3Fq%3Dtbn%3AANd9GcSWXAlPyC13wczNB5BGfxdVoqcewBYacr1-3AJVDcdZ_l8Jpg3Sc9w%26usqp%3DCAc" alt="lampe neewer 660" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
La lampe Neewer est une lampe à LED RGB contrôlable par Bluetooth via l'application smartphone Android ou iPhone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://neewer.com/products/neewer-cri-97-50w-660-prorgb-led-light-10098770" rel="noopener noreferrer"&gt;Voici le lien officiel&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Les données
&lt;/h2&gt;

&lt;p&gt;Après des manipulations de reverse engineering et focalisé sur les données de la luminosité de la lampe, il est apparue un code hexadécimal de 16 caractères:&lt;br&gt;
xxxxxx xx xx xx xx xx&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;La constante&lt;/em&gt;&lt;/strong&gt;: Les 6 premiers caractères sont fixes tout le long des tests et ont la valeur suivante : 788604&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;La couleur&lt;/em&gt;&lt;/strong&gt;: Les 2 caractères suivants représentent la valeur hexadécimale de la position, en degrés, du curseur dans le cercle chromatique.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkp9s8zks77f4b4bjykys.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkp9s8zks77f4b4bjykys.jpg" alt="cercle chromatique" width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Le flag&lt;/em&gt;&lt;/strong&gt;: S'en suis 2 caractères variants de 01 à 00 (en hexa). Ce flag change d'état lorsque la couleur dépasse la valeur FF pour retourner à 00&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;La luminosité&lt;/em&gt;&lt;/strong&gt;: S'ajoute 2 caractères représentant la valeur hexadécimale du pourcentage de luminosité souhaité. Par exemple pour 100% =&amp;gt; 64, 50% =&amp;gt; 32, 25% =&amp;gt; 19 et 0% =&amp;gt; 0&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;La saturation&lt;/em&gt;&lt;/strong&gt;: Ensuite 2 caractères dont la valeur hexadécimale représente le pourcentage de saturation de la couleur sélectionnée.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Le checksum&lt;/em&gt;&lt;/strong&gt;: Concernant les 2 derniers caractères rien n'est sûr. Il semble représenter un genre de checksum allant de 0 à 255 avec un modificateur:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Checksum (base 10) = 2 (base 10)+ flag (base 10) + couleur (base 10) + luminosité (base 10) + saturation (base 10)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Puis convertie en hexadécimal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Resultat&lt;/em&gt;&lt;/strong&gt;: Une fois cet ensemble de caractère concaténé, nous obtenons les données permettant de contrôler la couleur, la saturation et la luminosité de la lampe.&lt;/p&gt;

&lt;p&gt;Voici toutes les données recueillient:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1eAsZmB00w6uzSIcGgNqXClOPmx4dTHzjtozGwSPP83E/edit?usp=sharing" rel="noopener noreferrer"&gt;Data light control for Neewer 660 RGB LED Lamp&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Les tests
&lt;/h2&gt;

&lt;p&gt;Les tests ont été effectués à l'aide d'un raspberry pi 4 avce &lt;a href="https://learn.adafruit.com/install-bluez-on-the-raspberry-pi/installation" rel="noopener noreferrer"&gt;Bluez installé&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pour me connecter, il a été nécessaire de procéder à quelque configuration préalable.&lt;/p&gt;

&lt;p&gt;Il semble important de modifier le fichier suivant :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/bluetooth/main.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;d'y ajouter cette ligne en fin de document&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DisablePlugins=pnat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;et de redémarrer votre systeme&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo reboot now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Une fois le système réveillé, vous pouvez vous connecter à la lampe avec la commande suivante :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gatttool -t random -b XX:XX:XX:XX:XX:XX -I
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notez que le parametre "-t" s'est vu affecter la valeur "random"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gatttool --help
Usage:
  gatttool [OPTION?]
...
-t, --addr-type=[public | random]         Set LE address type. Default: public
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Une fois l'interface affichée, vous pouvez vous connecter et envoyer les données de controle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pi@raspberrypi:~ $ gatttool -t random -b XX:XX:XX:XX:XX:XX -I
[XX:XX:XX:XX:XX:XX][LE]&amp;gt; connect
Attempting to connect to XX:XX:XX:XX:XX:XX
Connection successful
[XX:XX:XX:XX:XX:XX][LE]&amp;gt; char-write-cmd 0x000e 788604d100640037
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>neewer</category>
      <category>bluez</category>
      <category>linux</category>
      <category>bluetooth</category>
    </item>
    <item>
      <title>Controlez vos lumières Triones Bluetooth Low Energy (BLE) avec votre Streamdeck (Partie 2)</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Sun, 02 May 2021 19:30:59 +0000</pubDate>
      <link>https://dev.to/multiplaie/controlez-vos-lumieres-triones-bluetooth-low-energy-ble-avec-votre-streamdeck-partie-2-2589</link>
      <guid>https://dev.to/multiplaie/controlez-vos-lumieres-triones-bluetooth-low-energy-ble-avec-votre-streamdeck-partie-2-2589</guid>
      <description>&lt;p&gt;Avant d'aller plus loin, je tiens à apporter quelque précision sur ma démarche:&lt;/p&gt;

&lt;p&gt;Ce post n'est pas à objectif pédagogique. Je ne suis pas un professionnel et je me suis adapté en fonction de mes connaissances, du matériel à ma disposition et du temps qui m'étais imparti. &lt;br&gt;
Ce post, donc, est une explication de ma mise en place pour le sujet qui m'a été donné. Il sera plein de fautes, incomplet certainement voir même offrera des solutions plus compliqué que ce qu'il devrai être.&lt;/p&gt;

&lt;p&gt;J'espère tout de même que ma démarche aidera d'autres personnes à imaginer de meilleur solution et dégrossira les difficultés qui peuvent être rencontré.&lt;/p&gt;

&lt;p&gt;Merci à vous &amp;lt;3&lt;/p&gt;

&lt;p&gt;Ps: C'est la 2e partie de mes recherhes. La partie 1 se trouve &lt;a href="https://dev.to/multiplaie/controlez-vos-lumieres-triones-bluetooth-low-energy-ble-avec-votre-streamdeck-partie-1-k3l"&gt;ici&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Comment j'ai controlé les lumieres BLE Triones avec un Streamdeck (Partie 2)
&lt;/h1&gt;
&lt;h2&gt;
  
  
  Mise en place d'un Raspberry pi pour le controle Bluetooth des lumières
&lt;/h2&gt;

&lt;p&gt;Après plusieurs essaie, j'ai décidé de me servir d'un systeme Linux pour communiqué avec les lumières à l'aide de la librairie intégrée &lt;a href="http://www.bluez.org/" rel="noopener noreferrer"&gt;Bluez&lt;/a&gt;. Cette librairie m'a permis en quelques lignes de commandes de découvrir mon réseau, me connecter et communiquer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;TODO : Détailler les opérations liés a l'installation, le scan, la connexion et les data&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;TODO : parler de la connection ssh + test coté windows avec streamdeck&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Création du plugin Streamdeck en C
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Préparation
&lt;/h3&gt;

&lt;p&gt;Pour cette partie, j'ai simplement suivi le &lt;a href="https://www.youtube.com/watch?v=yxtxwlnUCws" rel="noopener noreferrer"&gt;tuto de Claudio Bernasconi&lt;/a&gt; pour créer un plugin pour Streamdeck en C# avec &lt;a href="https://github.com/FritzAndFriends/StreamDeckToolkit" rel="noopener noreferrer"&gt;Toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pour ce faire :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installez les pré-requis pour le "Streamdeck Toolkit", A savoir le &lt;a href="https://dotnet.microsoft.com/download" rel="noopener noreferrer"&gt;Dotnet Core SDK&lt;/a&gt; et l'application "&lt;a href="https://www.elgato.com/fr/downloads" rel="noopener noreferrer"&gt;Streamdeck Software&lt;/a&gt;".&lt;/li&gt;
&lt;li&gt;Installez également &lt;a href="https://visualstudio.microsoft.com/fr/" rel="noopener noreferrer"&gt;Visual studio 2019 Community edition&lt;/a&gt; pour gérer le projet et pouvoir le compiler.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ouvre votre console préférée, rendez vous dans votre dossier de projet et tapez la ligne suivante:&lt;br&gt;
&lt;br&gt;
&lt;code&gt;dotnet new -i StreamDeckPluginTemplate::0.5.2040&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  - Créez le dossier de votre projet. (pour moi ça sera BtTrionesBLELightStreamdeckPlugin)
&lt;/h2&gt;

&lt;p&gt;&lt;br&gt;
 &lt;code&gt;mkdir BtTrionesBLELightStreamdeckPlugin&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Entrez dedans&lt;br&gt;
&lt;br&gt;
&lt;code&gt;cd BtTrionesBLELightStreamdeckPlugin&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Saisissez la ligne suivante pour télécharger le template de création&lt;br&gt;
&lt;br&gt;
&lt;code&gt;dotnet new streamdeck-plugin --plugin-name StreamdeckPluginTrionesBleLights --uuid com.khundar.streamdecktrionesblelights --skipRestore false&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
. Attention à adapter le "plugin-name" et l'uuid.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;L'uuid est important pour que l'application streamdeck reconnaisse votre plugin.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ouvrez le projet avec Visual Studio 2019 avec "Ouvrir un projet ou une solution" et sélectionnez le fichier BtTrionesBLELightStreamdeckPlugin.csproj&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Vous voici dans le projet Template. Il "ne reste plus qu'à..."&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Objectif 1: envoyer une commande
&lt;/h3&gt;

&lt;p&gt;Mon but à ce moment-ci est d'effectuer la connexion ssh et lancer gatttool.&lt;br&gt;
Pour exécuter une commande shell, j'ai trouvé cette façon de faire :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;ProcessStartInfo&lt;/span&gt; &lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Process&lt;/span&gt; &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;processInfo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProcessStartInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cmd.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/c "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bashCmd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateNoWindow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseShellExecute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedirectStandardError&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedirectStandardOutput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensuite, pour effectuer ma commande de manipulation de la lumière, je remplace la variable bashCmd&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;lightMacAddr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ff:ff:11:11:bb:9b"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//adresse de la lumiere&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cc2333"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//commande d'allumage&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ff0000"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;sshconnect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ssh pi@192.168.0.32"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Connexion ssh&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;stophci&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo hciconfig hci0 down"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//fermeture de la connexion bluetooth&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;starthci&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo hciconfig hci0 up"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//fermeture de la connexion bluetooth&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gatttoolpwr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo gatttool -b "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;lightMacAddr&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" --char-write-req --handle=0x0007 --value="&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gatttoolcolor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo gatttool -b "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;lightMacAddr&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" --char-write-req --handle=0x0007 --value=56"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"00f0aa"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;bashCmd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"\""&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;stophci&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;starthci&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gatttoolpwr&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gatttoolcolor&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;stophci&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pour finir, je souhaite effectuer la commande sur un "keyup". Celle déjà en place suffira:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// ./StreamdeckPluginTrionesBleLightsAction.cs&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnKeyUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StreamDeckEventPayload&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;lightMacAddr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ff:ff:11:11:bb:9b"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//adresse de la lumiere&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"cc2333"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//commande d'allumage&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ff0000"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;sshconnect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ssh pi@192.168.0.32"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Connexion ssh&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;stophci&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo hciconfig hci0 down"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//fermeture de la connexion bluetooth&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;starthci&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo hciconfig hci0 up"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//fermeture de la connexion bluetooth&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gatttoolpwr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo gatttool -b "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;lightMacAddr&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" --char-write-req --handle=0x0007 --value="&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gatttoolcolor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo gatttool -b "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;lightMacAddr&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" --char-write-req --handle=0x0007 --value=56"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"00f0aa"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;bashCmd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"\""&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;stophci&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;starthci&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gatttoolpwr&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gatttoolcolor&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;stophci&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;processInfo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProcessStartInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cmd.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/c "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sshconnect&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bashCmd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateNoWindow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseShellExecute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedirectStandardError&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedirectStandardOutput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


    &lt;span class="c1"&gt;//update settings&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetSettingsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SettingsModel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Très bien, mon action est prête. &lt;/p&gt;

&lt;h3&gt;
  
  
  Objectif 2: Dynamiser cette commande
&lt;/h3&gt;

&lt;p&gt;À présent, isolons les variables nécessaires à la configuration d'une lumière :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;l'adresse MAC de la lumière =&amp;gt; lightMacAddr&lt;/li&gt;
&lt;li&gt;l'état de la lumière =&amp;gt; switch&lt;/li&gt;
&lt;li&gt;la couleur =&amp;gt; color&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Commençons par modifier le model&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// ./models/TrionesBLESettingsModel.cs&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;StreamdeckPluginTrionesBleLights.Models&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrionesBLESettingsModel&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Switch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"000000"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Color&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"000000"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;LightMacAddr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"xx:xx:xx:xx:xx:xx"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensuite, on ajoute les attributs du model dans le controleur.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// ./StreamdeckPluginTrionesBleLightsAction.cs&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnKeyUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StreamDeckEventPayload&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;lightMacAddr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SettingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LightMacAddr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//adresse de la lumiere&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SettingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LightMacAddr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//commande d'allumage&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SettingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;sshconnect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"ssh pi@192.168.0.32"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Connexion ssh&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;stophci&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo hciconfig hci0 down"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//fermeture de la connexion bluetooth&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;starthci&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo hciconfig hci0 up"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//fermeture de la connexion bluetooth&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gatttoolpwr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo gatttool -b "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;lightMacAddr&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" --char-write-req --handle=0x0007 --value="&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;gatttoolcolor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sudo gatttool -b "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;lightMacAddr&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" --char-write-req --handle=0x0007 --value=56"&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"00f0aa"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;bashCmd&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"\""&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;stophci&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;starthci&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gatttoolpwr&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gatttoolcolor&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp;&amp;amp; "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;stophci&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;processInfo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ProcessStartInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cmd.exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/c "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sshconnect&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;bashCmd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateNoWindow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseShellExecute&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedirectStandardError&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RedirectStandardOutput&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;process&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;processInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


    &lt;span class="c1"&gt;//update settings&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetSettingsAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SettingsModel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et on fini avec la vue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="c"&gt;&amp;lt;!-- ./property_inspector/property_inspector.html --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sdpi-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- select light --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sdpi-item"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"select_light"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sdpi-item-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Lights&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sdpi-item-value select"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"light"&lt;/span&gt; &lt;span class="na"&gt;onchange=&lt;/span&gt;&lt;span class="s"&gt;"setSettings(event.target.value, 'LightMacAddr');"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"xx:xx:xx:xx:xx:xx"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;--Select a light--&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"ff:ff:11:11:bb:9b"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"Light 1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Light 1&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"ff:ff:50:03:10:3c"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"Light 2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Light 2&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"ff:ff:66:60:cf:58"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"Light 3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Light 3&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"ff:ff:77:70:b1:a4"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"Light 4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Light 4&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"ff:ff:33:30:fe:ee"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"Light 5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Light 5&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"ff:ff:44:42:77:82"&lt;/span&gt; &lt;span class="na"&gt;data-name=&lt;/span&gt;&lt;span class="s"&gt;"Light 6"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Light 6&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;

            &lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- switch on/off --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sdpi-item"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"select_switch"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sdpi-item-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Switch&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;select&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sdpi-item-value select"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"switch"&lt;/span&gt; &lt;span class="na"&gt;onchange=&lt;/span&gt;&lt;span class="s"&gt;"setSettings(event.target.value, 'Switch')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"cc2333"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;ON&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"cc2433"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;OFF&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"000000"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;--Select a Power--&lt;span class="nt"&gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/select&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- color --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"range"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sdpi-item"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"colorslider"&lt;/span&gt; &lt;span class="na"&gt;onchange=&lt;/span&gt;&lt;span class="s"&gt;"setSettings(event.target.value, 'Color')"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sdpi-item-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Color&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"sdpi-item-value"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"color"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"color"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"#000000"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"js/property-inspector.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;et le js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// global websocket, used to communicate from/to Stream Deck software&lt;/span&gt;
&lt;span class="c1"&gt;// as well as some info about our plugin, as sent by Stream Deck software &lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;websocket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;inInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;actionInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="nx"&gt;settingsModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;LightMacAddr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xx:xx:xx:xx:xx:xx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;000000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;000000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;connectElgatoStreamDeckSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inUUID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inRegisterEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;inActionInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inUUID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;actionInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inActionInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;inInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inInfo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;websocket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ws://localhost:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;inPort&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;//initialize values&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actionInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LightMacAddr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actionInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LightMacAddr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actionInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actionInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;actionInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onopen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setSelectField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LightMacAddr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setSelectField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;switch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inRegisterEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inUUID&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="c1"&gt;// register property inspector to Stream Deck&lt;/span&gt;
        &lt;span class="nx"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nx"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Received message from Stream Deck&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;jsonObj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;sdEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonObj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sdEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;didReceiveSettings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setSelectField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;allInputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;allInputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allInputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;allInputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allInputs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setSettings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;param&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;param&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Color&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;settingsModel&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;setSettings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;context&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;payload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;settingsModel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;settingsModel&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="nx"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A présent, mon plugin est prêt à être utiliser&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvqqy32gkfmstfpentuj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvqqy32gkfmstfpentuj.png" alt="Vue du plugin" width="499" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>streamdeck</category>
      <category>triones</category>
      <category>ble</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Controlez vos lumières Triones Bluetooth Low Energy (BLE) avec votre Streamdeck (Partie 1)</title>
      <dc:creator>Remy Jacquand</dc:creator>
      <pubDate>Sun, 02 May 2021 19:02:51 +0000</pubDate>
      <link>https://dev.to/multiplaie/controlez-vos-lumieres-triones-bluetooth-low-energy-ble-avec-votre-streamdeck-partie-1-k3l</link>
      <guid>https://dev.to/multiplaie/controlez-vos-lumieres-triones-bluetooth-low-energy-ble-avec-votre-streamdeck-partie-1-k3l</guid>
      <description>&lt;p&gt;Avant d'aller plus loin, je tiens à apporter quelque précision sur ma démarche:&lt;/p&gt;

&lt;p&gt;Ce post n'est pas à objectif pédagogique. Je ne suis pas un professionnel et je me suis adapté en fonction de mes connaissances, du matériel à ma disposition et du temps qui m'étais imparti. &lt;br&gt;
Ce post, donc, est une explication de ma mise en place pour le sujet qui m'a été donné. Il sera plein de fautes, incomplet certainement voir même offrera des solutions plus compliqué que ce qu'il devrai être.&lt;/p&gt;

&lt;p&gt;J'espère tout de même que ma démarche aidera d'autres personnes à imaginer de meilleur solution et dégrossira les difficultés qui peuvent être rencontré.&lt;/p&gt;

&lt;p&gt;Merci à vous &amp;lt;3&lt;/p&gt;

&lt;h1&gt;
  
  
  Comment j'ai contrôle les lumières BLE Triones avec un Streamdeck (Partie 1)
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Voici la demande
&lt;/h2&gt;

&lt;p&gt;Mon Client est un Youtubeur qui souhaite créer un nouveau studio d'enregistrement et gérer son éclairage "facilement".&lt;br&gt;
Dans son idée, il souhaite pré-configurer un éclairage et une vidéo spécifique en fonction des tournages et des scènes :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tournage 1/ scène 1 =&amp;gt; ambiance plage

&lt;ul&gt;
&lt;li&gt;lumière chaude + vidéo d'une plage&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;tournage 1/ scène 2 =&amp;gt; ambiance Spatial

&lt;ul&gt;
&lt;li&gt;lumières tamisées + vidéo de la terre&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;tournage 2/ scène 1 =&amp;gt; ambiance volcan

&lt;ul&gt;
&lt;li&gt;lumière rouge + vidéo lave&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;.... (vous avez saisi l'idée.)&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Pour composer ces ambiances, le studio est équipé de 6 projecteurs lumineux et d'une énorme télévision afin de remplacer le fond vert.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffh3dfbie6snuj30aspjg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffh3dfbie6snuj30aspjg.jpg" alt="La lumière en question" width="432" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pour finir, le tout doit être contrôlé par un périphérique appelé le Streamdeck. Ainsi, il sera possible d'organiser les configurations en fonction des tournages et suffira d'appuyer sur un seul bouton pour automatiser les commandes aux lumières et à l’écran. Pour fonctionner, le Streamdek doit être connecté en permanence sur un pc (ici, un pc Windows) car le profil d'utilisation est enregistré sur le pc et non sur le périphérique.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comment tester ces périphériques facilement pour comprendre leur fonctionnement
&lt;/h2&gt;

&lt;p&gt;Après quelques recherches, j'ai rapidement compris que ces lumières BLE portait le nom &lt;strong&gt;Triones&lt;/strong&gt; (et aussi parce qu'une fois branchait, ce nom apparaissais dans la liste des périphériques disponibles).&lt;/p&gt;

&lt;p&gt;Le seul moyen proposé pour les manipuler était de télécharger l'application &lt;a href="https://play.google.com/store/apps/details?id=com.xiaoyu.hlight&amp;amp;hl=fr&amp;amp;gl=US" rel="noopener noreferrer"&gt;HappyLighting&lt;/a&gt; sur mon mobile Android. Effectivement, l'application fonctionne très bien et propose quelque fonctionnalité intéressante.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1l876juvumcbgqmcd4ee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1l876juvumcbgqmcd4ee.png" alt="Application HappyLighting" width="725" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Coté PC, j'ai eu la bonne surprise de découvrir une application nodeJs permettant d'effectuer 90% des fonctionnalité proposé par l'appli. &lt;a href="https://github.com/vinceroti" rel="noopener noreferrer"&gt;vinceroti&lt;/a&gt; s'est penché sur le problème et propose une interface très fonctionnel et simple d'utilisation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;TODO : Upload des images de l'appli bt-triones&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Malheureusement pour moi c'est encore insuffisant car je n'ai pas trouvé le moyen de le faire communiquer avec le Streamdeck.&lt;/p&gt;

&lt;p&gt;Puis, c'est en lisant le post détaillé de &lt;a href="https://urish.medium.com/" rel="noopener noreferrer"&gt;Uri Shaked&lt;/a&gt; que j'ai commencer à comprendre comment cette technologie est plutôt avare en information (&lt;a href="https://urish.medium.com/reverse-engineering-a-bluetooth-lightbulb-56580fcb7546" rel="noopener noreferrer"&gt;l'article en question&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;en Bref, l'article explique comment les différentes entité composants le Bluetooth Low Energy (BLE) sont sensé communiqué et comment l'auteur s'est débrouillé dans ce petit dédale. On y apprend l’existence d'une application mobile android appelé &lt;a href="https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp" rel="noopener noreferrer"&gt;nRF Connect for Mobile&lt;/a&gt; et qui permet de communiqué à l'aide du Bluetooth avec les appareils qui nous entours et voir leurs entrées et sorties d'information a notre disposition.&lt;/p&gt;

&lt;p&gt;En ce qui concerne ces données devant commander nos lumières Triones, &lt;a href="https://github.com/madhead" rel="noopener noreferrer"&gt;madhead&lt;/a&gt; à laissé une petite note récapitulative détaillant les données dont on connait leurs effets : &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/madhead/saberlight/blob/master/protocols/Triones/protocol.md" rel="noopener noreferrer"&gt;Cette documentation ici&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ici aussi, on y voit encore une documentation incomplète avec des données "Magic" mais l'essentiel y est :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;allumer ou éteindre les lumières&lt;/li&gt;
&lt;li&gt;choisir la couleur&lt;/li&gt;
&lt;li&gt;choisir la luminosité &lt;/li&gt;
&lt;li&gt;... et d'autre truc ne nous interagissant pas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A présent, les travaux Pratique !&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Une fois la lumière trouvé sur l'application "nRF Connect Mobile", cliquez sur le bouton noir "connect"&lt;/li&gt;
&lt;li&gt;Vous allez basculer sur l’onglet de communication avec le périphérique&lt;/li&gt;
&lt;li&gt;Dans l'onglet "CLIENT", déployez le service dont l'UUID est "0xFFD5" en cliquant dessus&lt;/li&gt;
&lt;li&gt;En appuyant sur le flèche pointant vers le haut, tout à droite, vous allez ouvrir la fenêtre d’envoi de data.&lt;/li&gt;
&lt;li&gt;Pour tester, saisissez la valeur

&lt;code&gt;cc2333&lt;/code&gt;

pour allumer ou

&lt;code&gt;cc2433&lt;/code&gt;

pour éteindre. Puis validez avec le bouton "SEND"
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64hux9ec687ama5btsz4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64hux9ec687ama5btsz4.jpg" alt="application pas à pas" width="800" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>streamdeck</category>
      <category>triones</category>
      <category>ble</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
