Controlez vos lumières Triones Bluetooth Low Energy (BLE) avec votre Streamdeck (Partie 2)
Tags: streamdeck, triones, ble, csharp
Published: 2021-05-02 19:30:59

---
title: Controlez vos lumières Triones Bluetooth Low Energy (BLE) avec votre Streamdeck (Partie 2)
published: true
description: Comment controler des lumières Bluetooth Low Energy type Triones avec un epériphérique Streamdeck 
tags: 
- StreamDeck 
- Triones 
- BLE 
- Csharp   
//cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nu3zwtgx14vy4j9mo2ei.png
---

Avant d'aller plus loin, je tiens à apporter quelque précision sur ma démarche:

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. 
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.

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é.

Merci à vous <3

Ps: C'est la 2e partie de mes recherhes. La partie 1 se trouve [ici](https://dev.to/multiplaie/controlez-vos-lumieres-triones-bluetooth-low-energy-ble-avec-votre-streamdeck-partie-1-k3l)

# Comment j'ai controlé les lumieres BLE Triones avec un Streamdeck (Partie 2)

## Mise en place d'un Raspberry pi pour le controle Bluetooth des lumières

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 [Bluez](http://www.bluez.org/). Cette librairie m'a permis en quelques lignes de commandes de découvrir mon réseau, me connecter et communiquer.

***TODO : Détailler les opérations liés a l'installation, le scan, la connexion et les data***

***TODO : parler de la connection ssh + test coté windows avec streamdeck***

## Création du plugin Streamdeck en C#

###Préparation
Pour cette partie, j'ai simplement suivi le [tuto de Claudio Bernasconi](https://www.youtube.com/watch?v=yxtxwlnUCws) pour créer un plugin pour Streamdeck en C# avec [Toolkit](https://github.com/FritzAndFriends/StreamDeckToolkit).

Pour ce faire :

- Installez les pré-requis pour le "Streamdeck Toolkit", A savoir le [Dotnet Core SDK](https://dotnet.microsoft.com/download) et l'application "[Streamdeck Software](https://www.elgato.com/fr/downloads)".
- Installez également [Visual studio 2019 Community edition](https://visualstudio.microsoft.com/fr/) pour gérer le projet et pouvoir le compiler.
- Ouvre votre console préférée, rendez vous dans votre dossier de projet et tapez la ligne suivante: ```dotnet new -i StreamDeckPluginTemplate::0.5.2040```
- Créez le dossier de votre projet. (pour moi ça sera BtTrionesBLELightStreamdeckPlugin)
- ```mkdir BtTrionesBLELightStreamdeckPlugin```
- Entrez dedans ```cd BtTrionesBLELightStreamdeckPlugin```
- Saisissez la ligne suivante pour télécharger le template de création ```dotnet new streamdeck-plugin --plugin-name StreamdeckPluginTrionesBleLights --uuid com.khundar.streamdecktrionesblelights --skipRestore false```. Attention à adapter le "plugin-name" et l'uuid.
> L'uuid est important pour que l'application streamdeck reconnaisse votre plugin.
- Ouvrez le projet avec Visual Studio 2019 avec "Ouvrir un projet ou une solution" et sélectionnez le fichier BtTrionesBLELightStreamdeckPlugin.csproj
- Vous voici dans le projet Template. Il "ne reste plus qu'à..."

###Objectif 1: envoyer une commande
Mon but à ce moment-ci est d'effectuer la connexion ssh et lancer gatttool.
Pour exécuter une commande shell, j'ai trouvé cette façon de faire :

```Csharp
ProcessStartInfo processInfo;
Process process;

processInfo = new ProcessStartInfo("cmd.exe", "/c " + bashCmd);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.RedirectStandardError = true;
processInfo.RedirectStandardOutput = true;

process = Process.Start(processInfo);
process.WaitForExit();
process.Close();
```

Ensuite, pour effectuer ma commande de manipulation de la lumière, je remplace la variable bashCmd

```Csharp

string lightMacAddr = "ff:ff:11:11:bb:9b"; //adresse de la lumiere
string switch = "cc2333"; //commande d'allumage
string color = "ff0000";
string sshconnect = "ssh pi@192.168.0.32"; //Connexion ssh
string stophci = "sudo hciconfig hci0 down"; //fermeture de la connexion bluetooth
string starthci = "sudo hciconfig hci0 up"; //fermeture de la connexion bluetooth
string gatttoolpwr = "sudo gatttool -b " + lightMacAddr + " --char-write-req --handle=0x0007 --value=" + switch;
string gatttoolcolor = "sudo gatttool -b " + lightMacAddr + " --char-write-req --handle=0x0007 --value=56" + color + "00f0aa";
string bashCmd = "\"" + stophci + " && " + starthci + " && " + gatttoolpwr + " && " + gatttoolcolor + " && " + stophci + "\"";
```
Pour finir, je souhaite effectuer la commande sur un "keyup". Celle déjà en place suffira:

```Csharp

// ./StreamdeckPluginTrionesBleLightsAction.cs

public override async Task OnKeyUp(StreamDeckEventPayload args)
{

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

    process = Process.Start(processInfo);
    process.WaitForExit();
    process.Close();


    //update settings
    await Manager.SetSettingsAsync(args.context, SettingsModel);
}
```
Très bien, mon action est prête. 


###Objectif 2: Dynamiser cette commande
À présent, isolons les variables nécessaires à la configuration d'une lumière :
- l'adresse MAC de la lumière => lightMacAddr
- l'état de la lumière => switch
- la couleur => color

Commençons par modifier le model

```Csharp
 
// ./models/TrionesBLESettingsModel.cs

namespace StreamdeckPluginTrionesBleLights.Models
{
	public class TrionesBLESettingsModel
	{
		public string Switch { get; set; } = "000000";
		public string Color { get; set; } = "000000";
		public string LightMacAddr { get; set; } = "xx:xx:xx:xx:xx:xx";

	}
}

```
Ensuite, on ajoute les attributs du model dans le controleur.

```Csharp

// ./StreamdeckPluginTrionesBleLightsAction.cs

public override async Task OnKeyUp(StreamDeckEventPayload args)
{

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

    process = Process.Start(processInfo);
    process.WaitForExit();
    process.Close();


    //update settings
    await Manager.SetSettingsAsync(args.context, SettingsModel);
}

```

Et on fini avec la vue.

```html

<!-- ./property_inspector/property_inspector.html -->

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

			</select>
		</div>
		<!-- switch on/off -->
		<div class="sdpi-item" id="select_switch"> 
			<div class="sdpi-item-label">Switch</div>
			<select class="sdpi-item-value select" id="switch" onchange="setSettings(event.target.value, 'Switch')">
				<option value="cc2333">ON</option>
				<option value="cc2433">OFF</option>
				<option value="000000">--Select a Power--</option>
			</select>
		</div>
		<!-- color -->
		<div type="range" class="sdpi-item" id="colorslider" onchange="setSettings(event.target.value, 'Color')">
			<div class="sdpi-item-label">Color</div>
			<div class="sdpi-item-value">
				<input type="color" id="color" value="#000000">
			</div>
		</div>
	</div>
	<script src="js/property-inspector.js"></script>
</body>
```
et le js

```js
// global websocket, used to communicate from/to Stream Deck software
// as well as some info about our plugin, as sent by Stream Deck software 
var websocket = null,
	uuid = null,
	inInfo = null,
	actionInfo = {},
	settingsModel = {
		LightMacAddr: 'xx:xx:xx:xx:xx:xx',
		Switch: '000000',
		Color: '000000'
	};

function connectElgatoStreamDeckSocket(inPort, inUUID, inRegisterEvent, inInfo, inActionInfo) {
	uuid = inUUID;
	actionInfo = JSON.parse(inActionInfo);
	inInfo = JSON.parse(inInfo);
	websocket = new WebSocket('ws://localhost:' + inPort);

	//initialize values
	if (actionInfo.payload.settings.settingsModel) {
		settingsModel.LightMacAddr = actionInfo.payload.settings.settingsModel.LightMacAddr;
		settingsModel.Switch = actionInfo.payload.settings.settingsModel.Switch;
		settingsModel.Color = actionInfo.payload.settings.settingsModel.Color;
		settingsModel.Name = actionInfo.payload.settings.settingsModel.Name;

	}
	websocket.onopen = function () {
		console.log(settingsModel);
		setSelectField('light', settingsModel.LightMacAddr);
		setSelectField('switch', settingsModel.Switch);
		document.getElementById("color").value = '#' + settingsModel.Color;


		var json = { event: inRegisterEvent, uuid: inUUID };
		// register property inspector to Stream Deck
		websocket.send(JSON.stringify(json));

	};

	websocket.onmessage = function (evt) {
		// Received message from Stream Deck
		var jsonObj = JSON.parse(evt.data);
		var sdEvent = jsonObj['event'];
		switch (sdEvent) {
			case "didReceiveSettings":

				break;
			default:
				break;
		}
	};
	function setSelectField(id, value) {
		var allInputs = document.getElementById(id);
		for (var x = 0; x < allInputs.length; x++)
			if (allInputs[x].value == value) {
				allInputs[x].setAttribute('selected', 'selected');
				console.log(allInputs[x]);
			}

	}
}

const setSettings = (value, param) => {
	console.log(param + "=" + value);
	if (websocket) {

		if (param == 'Color')
			value = value.substring(1);

		settingsModel[param] = value;
		var json = {
			"event": "setSettings",
			"context": uuid,
			"payload": {
				"settingsModel": settingsModel
			}
		};

		websocket.send(JSON.stringify(json));
	}

};
```

A présent, mon plugin est prêt à être utiliser

![Vue du plugin](https://i.imgur.com/uRCSDa6.png)