Deblan blog

Développement

Uzbl - Importer ses bookmarks Firefox

Si vous avez une masse importante de bookmarks et que vous aimeriez les retrouver dans Uzbl alors je vous propose ce script d'importation. Il se repose sur les bookmarks de la "Barre personnelle" uniquement, et configure les bookmarks avec des tags (cf le précédent article).

Pour que le script fonctionne, vous devez posséder php et vous aurez besoin de faire un export (au format json) à la racine de votre répertoire personnelle.

Il suffira de lancer le script et tout se fera automatiquement :)

#!/usr/bin/php5
<?php

$bookmarks_file = array_pop(glob(getenv('HOME').'/bookmarks-*.json'));

if (empty($bookmarks_file)) {
    echo sprintf("No bookmarks found in \"%s\"/\n", getenv('HOME'));
    exit(0);
}

$datas = json_decode(file_get_contents($bookmarks_file));

function get_firefox_bookmarks_for_uzbl($node, $prefix)
{
    $config = '';

    if (isset($node->children)) {
        foreach ($node->children as $child) {
            $config.= get_firefox_bookmarks_for_uzbl($child, $prefix.'/'.$node->title);
        }
    } else {
        if (isset($node->uri)) {
            $config.= str_replace(
                array(
                    '/Barre personnelle/',
                    '/Barre personnelle'
                ),
                array(
                    '/Barre personnelle',
                    '/'
                ),
                "$node->uri $prefix $node->title\n"
            );
        }
    }

    return $config;
}

file_put_contents(
    getenv('HOME').'/.local/share/uzbl/bookmarks',
    get_firefox_bookmarks_for_uzbl($datas->children[2], '')
);

Uzbl-tabbed, mon nouveau navigateur hors développement, scripts

J'utilise à présent Uzbl-tabbed comme navigateur (hormis quand je fais du développement web). Il fonctionne à merveille, sans compter l'incroyable facilité de personnalisation.

Voici quelques éléments de ma configuration :

Clique molette : ouverture dans un onglet
@bind  <Button2>  = sh 'if [ "$1" ]; then echo "event NEW_TAB $1" > "$UZBL_FIFO"; else echo "uri $(xclip -o | sed s/\\\@/%40/g)" > "$UZBL_FIFO"; fi' '\@SELECTED_URI'
Divers binds
@cbind <Ctrl>n = event REQ_NEW_WINDOW # nouvelle fenêtre
@cbind <Ctrl>r = reload # rechargement
@cbind <Ctrl>R = reload_ign_cache # rechargement sans cache
@cbind <Mod1>b = back # précédent
@cbind <Mod1>n = forward  suivant 
@cbind <Ctrl>w = exit # fermerture d'onglet/fenêtre

@on_event NEW_WINDOW  sh 'uzbl-tabbed "$1"' %r
@cbind  clone = event REQ_NEW_WINDOW \@uri # copie de fenêtre/fenêtre
Gestion de bookmarks avec des tags

Ce script est un hack de celui présent sur le site d'Uzbl.

  • Emplacement : @scripts_dir/bookmark_with_tags_for_folders.sh
  • Bind : @cbind <Ctrl>d = spawn @scripts_dir/bookmark_with_tags_for_folders.sh
#!/bin/bash
# @scripts_dir/bookmark_with_tags_for_folders.sh
#NOTE: it's the job of the script that inserts bookmarks to make sure there are no dupes.

file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/bookmarks

[ -r "$file" ] || exit

DMENU_SCHEME="bookmarks"
DMENU_OPTIONS="xmms vertical resize"

. "$UZBL_UTIL_DIR/dmenu.sh"
. "$UZBL_UTIL_DIR/uzbl-dir.sh"

TAG=`cat $file | cut -d" " -f2 | sort -u | $DMENU -nb \#303030 -nf khaki -sb \#CCFFAA -sf \#303030`

goto=`grep $TAG $file | cut -d" " -f1,3- | while read url name; do [ -z "\$name" ] || name="\$name "; echo "\$name: $url"; done | $DMENU $COLORS | cut -d":" -f2-`

[ -n "$goto" ] && echo "uri $goto" > "$UZBL_FIFO"
Le contrôle+L de Firefox
  • Emplacement : @scripts_dir/prompt_url.sh
  • Bind : @cbind <Ctrl>l = spawn @scripts_dir/prompt_url.sh
#!/bin/sh

goto=$(zenity --entry --text "$UZBL_TITLE" --entry-text "$UZBL_URI" --title "$UZBL_TITLE")

if [ $? -eq 0 ]; then
	echo "uri $goto" > "$UZBL_FIFO"	
fi
Le contrôle+K de Firefox
  • Emplacement : @scripts_dir/prompt_search.sh
  • Bind : @cbind <Ctrl>k = spawn @scripts_dir/prompt_search.sh
#!/bin/sh

search=$(zenity --entry --text "Effectuer une recherche" --entry-text "" --title "UZBL : Effectuer une recherche")

if [ $? -eq 0 ]; then
	echo "uri http://www.google.fr/search?q=$(echo "$search" | sed 's/ /%20/g')" > "$UZBL_FIFO"	
fi
Gestion du zoom à la Firefox (contenu + conteneur)
Binds :
@cbind <Ctrl>i         = js (function() { var event = document.createEvent('HTMLEvents'); event.initEvent('uzbl_zoom_in', true, true); document.dispatchEvent(event); } )();
@cbind <Ctrl><Shift>I  = js (function() { var event = document.createEvent('HTMLEvents'); event.initEvent('uzbl_zoom_out', true, true); document.dispatchEvent(event); } )();
@cbind <Ctrl>à         = js (function() { var event = document.createEvent('HTMLEvents'); event.initEvent('uzbl_zoom_init', true, true); document.dispatchEvent(event); } )();

@on_event   LOAD_COMMIT    script @scripts_dir/zoom.js

Dans @scripts_dir/zoom.js :

function get_cookie(name) {
	var dc = document.cookie;
	var prefix = name + '=';
	var begin = dc.indexOf('; ' + prefix);

	if (begin == -1) {
		begin = dc.indexOf(prefix);

		if (begin!= 0) {
			return null;
		}

	} 
	else {
		begin += 2;
	}

	var end = document.cookie.indexOf(';', begin);

	if (end == -1) {
		end = dc.length;
	}

	return unescape(dc.substring(begin + prefix.length, end));
}

var step = function()
{
	return 0.1;
}

var get_body = function()
{
	var body = document.getElementsByTagName('body');

	return body ? body[0] : null;
}

var get_body_zoom = function()
{
	return get_body().getAttribute('data-zoom') ? parseFloat(get_body().getAttribute('data-zoom')) : 1;
}

var set_body_zoom = function(zoom)
{
	get_body().setAttribute('data-zoom', zoom);
	get_body().style.zoom = zoom;
	document.cookie='uzbl_zoom='+zoom+';path=/;expires=Tue, 2 Jun 2020 00:00:00 UTC;'	
}

var zoom_in = function()
{
	set_body_zoom(get_body_zoom() + step());
}

var zoom_out = function()
{
	set_body_zoom(get_body_zoom() - step());
}

var zoom_init = function()
{
	set_body_zoom(1);
}

var zoom_retrieve = function()
{
	var zoom = get_cookie('uzbl_zoom');

	if (zoom) {
		set_body_zoom(zoom);
	}
}

document.addEventListener('uzbl_zoom_in', zoom_in, true);
document.addEventListener('uzbl_zoom_out', zoom_out, true);
document.addEventListener('uzbl_zoom_init', zoom_init, true);
document.addEventListener('DOMContentLoaded', zoom_retrieve, true);

J'espère que ça vous sera utile :)


DebFlux a migré de version du BilboPlanet, mais...

DebFlux est mon outil principal pour lire les différents flux RSS auxquels je souscris. Il fonctionne pas trop mal et me permet, quelque soit la machine sur laquelle je me trouve, de suivre l'actualité des sites internet qui m'intéressent. Il s'appuie sur BilboPlanet, un logiciel libre que je hack pour le sortir (un peu) de son objectif initial : gérer un planet.

Il me sert énormément mais je dois dire que cette nouvelle version me surprend, surtout d'un point de vue technique. En effet, les développeurs ont choisi de faire un peu d'objet, un peu de procédural et de coller par dessus un moteur de template un peu dépassé. Peut-être voulaient-ils se passer de la gestion du cache et la déléguer à ce moteur ? Ou bien est-ce un choix pour rendre plus simple la modification du rendu et de gérer plusieurs thèmes ? Je ne sais pas, mais je reste sur ma faim.

Mon objectif au travers de ce billet n'est pas descendre le boulot abattu puisque j'apprécie le service qu'il me rend. Cependant, il me paraît nécessaire d'élever une critique qui pourrait aider à son développement, surtout d'un point de vue communautaire.

Dans un premier temps, je regrette qu'il ne repose pas sur un framework. Il aurait été vraiment intéressant que derrière ce CMS, un outil tels que Symfony, CakePHP, Zend Framework ou CodeIgniter ait été utilisé. La raison est simple : les méthodes de développement auraient été standardisées et ce travail aurait permis une intégration plus simple dans d'autres plateformes. Demain je souhaite intégrer le moteur de gestion des flux sur une application et je devrai me coltiner la réécriture du BilboPlanet.

Le choix du moteur de template me dérange. J'ai déjà utilisé un tel outil et ma conclusion est simple : quand on doit polluer son code PHP de méthodes ou fonctions propres au moteur de template, alors il faut changer d'outil. La couche de rendu doit être dissociée de la gestion des données et coller des $core->tpl-> partout dans son code, ça craint : je ne connais pas Hyla et ça me gonfle de devoir lire sa documentation alors que je dois simplement modifier la récupération des données. Il aurait été judicieux de travailler avec des méthodes proches de celles de Symfony : on a des données générées et stockées dans des tableaux simples (array) et le moteur de rendu pourra s'appuyer sur ce qu'il veut : PHP, Twig ou n'importe quoi d'autres. Qui plus est, ça facilite grandement l'intégration du code à l'intérieur d'autres projets.

D'un point de vue sécurité, j'aurais également apprécié d'avoir un grain de sel qui se paramètre à l'installation ou dans un fichier de conf. Avec la gestion de base, on peut brute forcer sans problème puisque qu'on connaît la clé définie en dure dans l'appel de la méthode de hash.

Enfin, pourquoi éparpiller les fonctions un peu partout dans le code ? C'est quand même intéressant de retrouver ses billes quand on connaît les répertoires où elles sont déclarées. Dans le code actuel, il y a un répertoire lib/ mais des fonctions sont écrites un peu partout ailleurs, typiquement l'index de l'administration. Heureusement que ctags est performant !

C'est le genre de projet qui mérite de recevoir des contributions et en ce qui me concerne, c'est le seul qui me convient pour gérer mes flux. Mais de mon point de vue, c'est une réécriture complète à faire.

Que faire ? Est-ce que je vais passer pour un élitiste après m'avoir lu, ou un gros con qui fait chier son monde car on ne fait pas comme il aime ? Faut-il forker ou bien contribuer en faisant un gros pull request avec moultes modifications ? No idea.


[Astuce] i3wm : traverser les workspaces

Je suis devenu un vrai fan du gestionnaire de fenêtres i3wm et pour cause, il est ultra simple à scripter :)

Jusqu'à aujourd'hui une fonctionnalité majeure me manquait : pouvoir naviguer vers le workspace qui précède ou qui suit celui courant. On peut switcher entre les deux derniers workspaces qui ont été focused ou se déplacer directement vers un workspace (pour autant qu'un mapping soit prévu).

Pour arriver à combler ce manque, j'ai utilisé i3-msg qui permet de récupérer des informations sur i3wm et de lui donner des instructions. Je script en php car il faut lire du json...et en shell c'est compliqué !

L'algo est simple :

  • je récupère les workspaces avec : i3-msg -t get_workspaces
  • je les ordonne par ordre alphabétique (c'est comme ça que i3 les affiche)
  • je récupère le workspace courant
  • suivant ce qu'on demande, je retourne le workspace qui précède ou suit le workspace courant
  • je demande à i3 de switcher vers le workspace via : i3-msg 'workspace "le nom du workspace cible"'

Le script est disponible ici. Placez-le où vous voulez et renseignez un mapping tel que celui-ci (dans ~/.i3/config) :

bindsym $mod+p exec ~/bin/i3_switch_workspace.php previous
bindsym $mod+n exec ~/bin/i3_switch_workspace.php next

Note : dans ce mapping, mes raccourcis sont window+P pour aller au workspace précédant et window+N pour aller au suivant. Mon script se trouve dans ~/bin/ et le fichier est nommé i3_switch_workspace.php.

N'oubliez pas de rendre le script exécutable (chmod +x).


[Test] Arduino Uno, prêt pour de la domotique accessible ?

Il y a quelques temps, on ma confié un Arduino Uno afin que j'en produise un test. Pour les gens qui ne connaissent pas ce type de produit, il s'agit d'une carte électronique capable de recevoir de l'information via divers capteurs et de contrôler plusieurs sorties électroniques. L'objectif d'un tel matériel est de faire ni plus ni moins que de la domotique.

Je ne suis pas du tout électronicien mais j'ai cependant quelques bases suite à ma filière S-SI au lycée. J'ai donc porté un intérêt tout particulier à cette carte et ce qu'elle pouvait fournir. Il n'est pas inutile de rappeler que je suis sur une Debian et que l'ensemble de mon travail devait pouvoir se faire à travers cet environnement. En parcourant la documentation, il s'avère qu'on peut développer sur cette carte qu'on soit sur une distribution Linux, un Windows ou bien sur un Mac OS X. L'installation des logiciels sur mon environnement était très simple. En effet Debian fournit des packages dédiés pour jouer avec les cartes Arduino. Il faut noter que pour les autres grandes distributions, c'est du pareil au même, c'est donc un premier bon point.

J'avais une grande appréhension quant au langage de développement qui allait être utilisé. Des lectures que j'ai pu faire de la domotique, le C est beaucoup utilisé et je n'ai pas spécialement d'affinité avec ce langage (je ne l'utilise pour ainsi dire jamais). Au final, j'ai été une nouvelle fois étonné puisque Arduino a pensé aux gens comme moi qui n'auraient pas eu les compétences requises en C. Arduino fournit en effet un outil de développement dont le langage se rapproche énormément de Processing. Sans aller à les comparer en terme de fonctionnalité, je m'arrêterai simplement sur l'écriture qui s'en rapproche beaucoup. Le langage facilite énormément le développement puisque une fois la carte branchée, il suffit de commencer à développer en ne se posant que des questions fonctionnelles. On déploie le code quasi instantanément et le rendu est direct. La documentation est évidement très complète et permet de développer rapidement des choses sans se prendre la tête. Cependant, pour les allergiques au Java, vous devrez quand même installer une jvm pour pour lancer l'éditeur et je suppose que c'est ce même langage qui est envoyé et exécuté sur la bestiole.

IDE Arduino

Voici un bout de code faire clignoter aléatoirement 3 leds :

// Les 3 sorties de la carte qui identifient les entrées des 3 leds
int leds[] = {11, 12, 13};
int i;

// code exécuté que la carte est réinitialisée 
void setup() {
  for (i = 0; i < 4; i++) {         
    pinMode(leds[i], OUTPUT);
  }
}

// code exécuté à fréquence de temps régulière
void loop() {
  int level = random(3);
  
  // suivant le nombre aléatoire tiré, on aliment ou pas les leds
  for(i = 0; i<4; i++) {
    if(i == level) {
      digitalWrite(leds[i], HIGH);
    }
    else {
      digitalWrite(leds[i], LOW);
    }
  }

  delay(100);
}
// Ce n'est vraiment pas compliqué :)

La carte n'était pas livrée seule, c'est une plaque d'essais et une belle quantité de composants qui l'accompagnaient. On reconnaîtra des fils de connexions, des résistances, des leds, des boutons poussoirs et bien d'autres choses.

Arduino, composants électroniques

C'est à ce moment que ce glisse le vrai problème rencontré. Bien que l'ensemble de ces composants se suffit pour bidouiller et faire plein de choses, sans matériels complémentaires, ça devient compliqué d'assembler les circuits. Ce n'est pas de la faute d'Arduino mais si vous deviez faire l'acquisition d'un tel outil, ne serait-ce que dans un objectif pédagogique, prenez soin de vous munir d'outils en plus.

Les quelques circuits que j'ai pu bricoler étaient des modifications simples des exemples de la documentation. J'envisage de m'acheter la carte Arduino Ethernet : je souhaite effectivement travailler sur un monitoring de mes serveurs. Cette version de carte (Arduino Uno) manque de l'interface réseau pour pouvoir rentrer vraiment dans mes besoins personnels et mes compétences en électronique trouvent vite leurs limites.

C'est un excellent produit et je présume que beaucoup de personnes se sont déjà empressées de jouer avec. Si vous êtes curieux et que le monde de la domotique vous intéresse, je vous recommande fortement cette marque qui a mis toutes les billes du coté du développeur : vous.

Une note supplémentaire qui donnera le sourire aux anglophobes (la doc officielle n'existe pas en français), vous pourrez trouver un tas d'informations et notamment des tutoriels videos sous-titrés sur Arduino de RS Components.