Faire son thé en Powershell !

Une des plus vieilles blagues du monde en matière d’informatique, et au delà, en matière d’électronique, est la soi-disante impossibilité pour une machine de faire le café… Aujourd’hui, nous n’allons pas en faire non plus, mais plutôt faire infuser un délicieux thé grâce à Powershell ! Préparez une tasse, un sachet de thé et une bouilloire, car c’est parti !

Rendre à César…

Cette idée d’article saugrenu m’est venue en allant faire un tour sur le site de Raspberry, où je suis tombé nez à nez avec cette image :

Pas con du tout !

Ah ah, un lecteur de CD (ou DVD, ou Blu-ray), un bâtonnet d’esquimau, un sachet de thé, un mug, une tasse, de l’eau bouillante et… un Raspberry Pi. Bon, j’ai tout ça en stock sauf le Pi… Mais pas grave, un ordinateur est un ordinateur, n’importe lequel fera l’affaire, du moment que j’ai un lecteur de CD, non ?

Le script est en Python mais même pas besoin de voir à quoi ça ressemble : le principe est d’ouvrir le tiroir du CD, y placer le bâtonnet auquel sera suspendu le sachet de thé, on met la tasse en dessous, on verse l’eau et on referme le tiroir du lecteur pour que le sachet infuse. C’est tout bête, mais il fallait y penser. Visiblement l’auteur, Andrey Chilikin, a eu aussi la bonne idée d’utiliser une temporisation – le temps d’infusion du thé – avant de remonter le sachet.

Je vous laisse trouver le matériel et l’installer comme sur l’image. Évidemment, le lecteur de CD doit être relié au PC sur lequel vous exécuterez le script, sinon… ben ça ne marchera pas.

Le principe

Voilà ce qu’est censé faire notre script :

  1. Ouvrir le tiroir du lecteur de CD
  2. Attendre que l’utilisateur ait fini de suspendre le sachet de thé, de placer la tasse et de verser l’eau (et un morceau de sucre éventuellement). On attendra qu’il appuie sur une touche par exemple (ou si vous voulez faire un formulaire, qu’il clique sur un bouton).
  3. Fermer le tiroir du lecteur de CD
  4. Attendre un temps défini (par exemple 2 minutes)
  5. Rouvrir le tiroir du lecteur
  6. En bonus, prévenir l’utilisateur que son thé est prêt à être dégusté

Petit avertissement : il n’y a pas de cmdlet native en Powershell pour attendre l’appui sur une touche (le fameux “Press any key to continue”, où personne n’a jamais trouvé où ce trouve cette fichue touche “any” au passage). Il existe cependant une solution en utilisant la variable $host, mais elle a un GROS inconvénient : pour une raison totalement obscure et propre à Microsoft, cela ne fonctionne pas sous Powershell ISE. Il faudra donc exécuter le script directement en ouvrant une console Powershell, à moins de faire un joli formulaire (or comme vous avez lu mes précédents articles, vous savez très bien les faire désormais). C’est “by design”, donc ce doit être pour notre bien j’imagine…

Voici comment ça se passe :

$key=$host.UI.RawUI.ReadKey(“NoEcho,IncludeKeyDown”)
$host.UI.RawUI.Flushinputbuffer()

On peut récupérer la touche appuyée dans une variable $key en faisant tout simplement ceci :

$key=$host.UI.RawUI.ReadKey(“NoEcho,IncludeKeyDown”)
$host.UI.RawUI.Flushinputbuffer()

Venons-en au tiroir du lecteur de CD : pour le faire ouvrir et se refermer, il y a tout un tas de méthodes, notamment en utilisant les objets MediaPlayer (que nous avons vu dans DédéAmp). Mais hélas, selon la version de Windows, parfois ça marche, parfois non. Nous allons donc nous rabattre sur une méthode universelle, en utilisant l’API IMAPI (Image Mastering API). Image Mastering, ça voudrait dire que ?… Oui, ça peut servir à graver un CD, entre autres… mais aussi, et c’est ce qui nous intéresse pour l’instant, à ouvrir ce fichu tiroir. L’avantage de cette méthode est qu’IMAPI utilise COM, intégré à Windows, et donc qu’il n’y a rien à charger du tout pour que ça marche (c’est la bibliothèque système Imapi2.dll qui est utilisée). Il faut toutefois créer deux objets pour accéder aux méthodes qui vont bien. Le premier va nous servir à trouver le premier lecteur CD (ou DVD, ou Blu-ray) qui est connecté. Le second va nous servir à lui envoyer des commandes (via des méthodes) en y faisant référence :

$diskmaster = New-Object -ComObject IMAPI2.MsftDiscMaster2 
$diskrecorder = New-Object -ComObject IMAPI2.MsftDiscRecorder2 

Euh pourquoi “recorder” ? Il faut que ce soit un graveur ? Pas du tout, c’est seulement le nom donné à l’interface par Microsoft. Effectivement, s’il s’agit d’un graveur, on pourra se servir des méthodes de cet objet pour écrire des données sur un disque, mais on peut s’en servir aussi pour ouvrir et fermer le tiroir, alors autant ne pas se gêner.

Une fois qu’on a trouvé notre lecteur, il faut l’initialiser en indiquant l’identifiant unique $diskmaster :

$diskrecorder.InitializeDiscRecorder($diskmaster) 

Petit aparté : j’ai dit plus haut que nous prenions le premier lecteur qui traîne. Mais quid si le PC en a plusieurs ? Et bien c’est simple, comme l’interface IMAPI2.MsftDiscMaster2 énumère TOUS les lecteurs optiques, il suffit de stocker le résultat dans un tableau, et nous pourrons ensuite indiquer quel lecteur nous voulons piloter. Le premier élément d’un tableau ayant toujours l’index 0, le second lecteur aura l’index 1 etc. Voilà ce que cela nous donnerait (les deux lignes définissant $diskmaster et $diskrecorder ne changent pas) :

$diskrecorder.InitializeDiscRecorder($diskmaster.Item[1]) 

J’initialise donc ici mon objet $diskrecorder avec le second lecteur (l’objet d’index 1 du tableau $diskmaster).

Maintenant que nous avons un objet qui fonctionne, il ne reste plus qu’à appeler les méthodes de $diskrecorder qui vont bien :

$diskrecorder.EjectMedia() # pour ouvrir le tiroir
$diskrecorder.CloseTray() # pour fermer le tiroir 

Il ne nous manque plus qu’un élément dans notre script : comment lui faire attendre deux minutes avant d’ouvrir le tiroir. C’est comme d’habitude très simple, on utilise la cmdlet Start-Sleep en indiquant une durée en secondes :

Start-Sleep -s 120 # et 2mn de dodo gratuites

Bon, reste plus qu’à mettre en forme tout ce bazar, avec un peu de texte pour que ce soit un minimum convivial. Je vous présente donc en exclusivité mondiale DédéThé !

$dureeinfusion = 120 # durée d'infusion en secondes
$diskmaster = New-Object -ComObject IMAPI2.MsftDiscMaster2
$diskrecorder = New-Object -ComObject IMAPI2.MsftDiscRecorder2
$diskrecorder.InitializeDiscRecorder($diskmaster)

Write-Host "Bienvenu dans DédéThé !"
$diskrecorder.EjectMedia() # on ouvre le tiroir
Write-Host "Placez la tasse sous le bâtonnet, fixez-y le sachet de thé, versez l'eau à température puis appuyez sur une touche quand vous êtes prêt..."
$key=$host.UI.RawUI.ReadKey(“NoEcho,IncludeKeyDown”)
$host.UI.RawUI.Flushinputbuffer()
Write-Host "Et c'est parti, attention à vos doigts !"
$diskrecorder.CloseTray() # on ferme le tiroir
Start-Sleep -s $dureeinfusion # ça infuse...
$diskrecorder.EjectMedia() # et hop on rouvre le tiroir
Write-Host "Votre thé est prêt ! Bonne dégustation ! (faites gaffe, c'est chaud)"

Et voilà, à vous les joies du thé grâce à Powershell !

Seulement, il y a un hic. Ce script fonctionnera très bien avec les lecteurs de CD à tiroir de PC de bureau (les gros machins qui ne se font quasiment plus). Mais pour peu que vous ayez un lecteur slot-in (sans tiroir) ou de portable (avec tiroir, mais qu’il faut refermer à la main), ça ne va pas aller… et lors de l’appel de la méthode CloseTray(), bim, une jolie exception… C’est normal, on ne peut rien y faire…

Cadeau bonus

Bon comme j’ai un peu honte d’avoir fait un script assez nul, voyons ce qu’on peut faire de plus avec notre objet $diskrecorder. Pour cela, voyons les méthodes utilisables :

$diskrecorder | gm

Voilà ce que cela nous sort :

   TypeName : System.__ComObject#{27354133-7f64-5b0f-8f00-5d77afbe261e}

Name                   MemberType Definition
----                   ---------- ----------
AcquireExclusiveAccess Method     void AcquireExclusiveAccess (bool, string)
CloseTray              Method     void CloseTray ()
DisableMcn             Method     void DisableMcn ()
EjectMedia             Method     void EjectMedia ()
EnableMcn              Method     void EnableMcn ()
InitializeDiscRecorder Method     void InitializeDiscRecorder (string)
ReleaseExclusiveAccess Method     void ReleaseExclusiveAccess ()
ActiveDiscRecorder     Property   string ActiveDiscRecorder () {get}
CurrentFeaturePages    Property   SAFEARRAY(Variant) CurrentFeaturePages () {get}
CurrentProfiles        Property   SAFEARRAY(Variant) CurrentProfiles () {get}
DeviceCanLoadMedia     Property   bool DeviceCanLoadMedia () {get}
ExclusiveAccessOwner   Property   string ExclusiveAccessOwner () {get}
LegacyDeviceNumber     Property   int LegacyDeviceNumber () {get}
ProductId              Property   string ProductId () {get}
ProductRevision        Property   string ProductRevision () {get}
SupportedFeaturePages  Property   SAFEARRAY(Variant) SupportedFeaturePages () {get}
SupportedModePages     Property   SAFEARRAY(Variant) SupportedModePages () {get}
SupportedProfiles      Property   SAFEARRAY(Variant) SupportedProfiles () {get}
VendorId               Property   string VendorId () {get}
VolumeName             Property   string VolumeName () {get}
VolumePathNames        Property   SAFEARRAY(Variant) VolumePathNames () {get}

En voilà des choses intéressantes ! Tout d’abord, identifions notre lecteur :

$diskrecorder.VendorId
MATSHITA
$diskrecorder.ProductId
DVD-RAM UJ8E1

Ah cool, j’ai donc un Matshita DVD-RAM UJ8E1 dans mon laptop (un bon vieux graveur de DVD). Très bien. Et est-ce que je peux FERMER le tiroir (autrement dit, je peux charger un média) ?

$diskrecorder.DeviceCanLoadMedia()
False

Et bah non. Cela dit, on peut donc modifier DédéThé un peu pour qu’il gère ce cas de figure :

# DédéThé
# Par Dédé Lateur - 07/01/2017
# Sur une idée d'Andrey Chilikin trouvée sur le site Raspberry
#
$dureeinfusion = 120 # durée d'infusion en secondes
$diskmaster = New-Object -ComObject IMAPI2.MsftDiscMaster2
$diskrecorder = New-Object -ComObject IMAPI2.MsftDiscRecorder2
$diskrecorder.InitializeDiscRecorder($diskmaster)

Write-Host "Bienvenu dans DédéThé !"
if($diskrecorder.DeviceCanLoadMedia() -eq $true)
{
	$diskrecorder.EjectMedia() # on ouvre le tiroir
	Write-Host "Placez la tasse sous le bâtonnet, fixez-y le sachet de thé, versez l'eau à température puis appuyez sur une touche quand vous êtes prêt..."
	$key=$host.UI.RawUI.ReadKey(“NoEcho,IncludeKeyDown”)
	$host.UI.RawUI.Flushinputbuffer()
	Write-Host "Et c'est parti, attention à vos doigts !"
	$diskrecorder.CloseTray() # on ferme le tiroir
	Start-Sleep -s $dureeinfusion
	$diskrecorder.EjectMedia()
	Write-Host "Votre thé est prêt ! Bonne dégustation ! (faites gaffe, c'est chaud)"
}
else
{
	Write-Host "Désolé, votre lecteur optique n'est pas compatible avec DédéThé et est donc incapable de faire du thé ! Bouuuuh !"
}

Je ne me suis pas penché sur les autres méthodes de $diskrecorder, mais rien ne vous empêche de le faire, et même je vous encourage à jeter un œil au technet et au MSDN !

Sur ces bonnes paroles, buvez du thé, portez-vous bien et tchüss !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.