La semaine dernière, nous avons pu tester l’écran ePaper (eInk) 2,7 pouces du fabricant chinois Waveshare. Nous n’étions pas allé très loins dans la présentation. Il me fallait un peu de temps pour apprendre à maitriser la librairie Python Imaging Library (ou le fork Pillow). Je vous propose dans ce nouveau tutoriel d’aller beaucoup plus loin. Nous allons réaliser une station météo connectée à un serveur domotique Jeedom. Le plugin météo (gratuit) de Jeedom permet de récupérer très facilement les prévisions sur 5 jours sur le site openweathermap.org. Bien évidemment, ce n’est qu’un prétexte. L’idée étant de montrer les différents mécanismes à mettre en place pour interroger le serveur Jeedom, récupérer les données d’un appareil et comment générer l’affichage ePaper.
Remarque. Vous pouvez tester et mettre au point votre code sur n’importe quel PC Windows ou Linux, Mac ou Raspberry Pi sans avoir d’écran eInk. Il suffira d’enregistrer l’image générée par la librairie Python Imaging.
Matériel utilisé pour la station météo ePaper
Pour ce projet, j’ai utilisé le matériel suivant. L’écran mesure 2,7 pouces de diagonale avec une résolution de 264 x 176 pixels. Il est assez grand pour afficher les prévisions météo en détail. Il possède 4 boutons sur le coté que l’on va utiliser pour naviguer entre les écrans. On pourrait également les utiliser pour déclencher un scénario domotique ou afficher d’autres états.
Aliexpress.com
57,99€
4 nouveau
de 57,99€
Relevé le 25 juin 2018 18 h 57 min
Amazon.fr
Livraison gratuite
Vous pouvez également choisir un autre écran dans la gamme Waveshare
Ecran
|
Couleurs
|
Niveau de gris
|
Résolution (pixels)
|
Taille écran (mm)
|
Taille totale (mm)
|
Temps de rafraîchissement (s)
|
Interface
|
Compatible HAT(1)
|
1.54 pouces
|
noir, blanc
|
2
|
200×200
|
27.60 × 27.60
|
48.0 × 33.0
|
2
|
SPI
|
x
|
1.54 pouces (B)
|
rouge, noir, blanc
|
2
|
200×200
|
27.60 × 27.60
|
48.0 × 33.0
|
8
|
SPI
|
x
|
2.13 pouces
|
noir, blanc
|
2
|
250×122
|
48.55 × 23.71
|
65.0 × 30.2
|
2
|
SPI
|
√
|
2.13 pouces (B)
|
rouge, noir, blanc
|
2
|
212×104
|
48.55 × 23.71
|
65.0 × 30.2
|
15
|
SPI
|
√
|
2.7 pouces
|
noir, blanc
|
2
|
264×176
|
57.29 × 38.19
|
85.0 × 56.0
|
6
|
SPI
|
√
|
2.7 pouces (B)
|
rouge, noir, blanc
|
2
|
264×176
|
57.29 × 38.19
|
85.0 × 56.0
|
15
|
SPI
|
√
|
2.9 pouces
|
noir, blanc
|
2
|
296×128
|
66.89 × 29.05
|
89.5 × 38.0
|
2
|
SPI
|
x
|
2.9 pouces (B)
|
rouge, noir, blanc
|
2
|
296×128
|
66.89 × 29.05
|
89.5 × 38.0
|
15
|
SPI
|
x
|
4.2 pouces
|
noir, blanc
|
2
|
400×300
|
84.80 × 63.60
|
103.0 × 78.5
|
4
|
SPI
|
x
|
4.2 pouces (B)
|
rouge, noir, blanc
|
2
|
400×300
|
84.80 × 63.60
|
103.0 × 78.5
|
15
|
SPI
|
x
|
4.3 pouces
|
noir, blanc
|
4
|
800×600
|
88.00 × 66.00
|
118.0 × 75.0
|
1.5
|
UART
|
x
|
7.5 pouces
|
noir, blanc
|
2
|
640×384
|
163.20×97.92
|
170.2×111.2 |
6
|
SPI
|
√
|
7.5 pouces (B)
|
rouge, noir, blanc
|
2
|
640×384
|
163.20×97.92
|
170.2×111.2 |
31
|
SPI
|
√
|
(1) Connecteur compatible avec Raspberry Pi 2B/3B/Zero/Zero W. Les autres écrans doivent être connectés à l’aide de Jumpers.
Librairies Python à installer
Si vous débutez avec les écrans ePaper de Waveshare, je vous conseille de lire l’article précédent qui explique comment les utiliser avec un ou deux exemples simples.
Le driver epd (pour EPaper Display j’imagine) ne fait “que” l’afficher une image. On va donc devoir la générer en Python au préalable. La librairie Python Imaging (Pil) ou son fork Pillow semble la plus puissante dans le domaine. Attention, Pil et Pillow ne peuvent pas cohabiter sur la même machine.
Pour installer Pil vous pouvez utiliser la commande pip. Vous aurez peut être besoin de faire précéder la commande pip d’un sudo.
pip install python-pil request
Sur macOS, le plus facile est d’utiliser brew
brew install pil request
La librairie Python Pil / Pillow rapidement
La librairie Pil ou Pillow permettent de manipuler des images (convertir, redimensionner, orienter…), ajouter des formes géométriques (ligne, ellipse, polygone, arc de cercle…) ou du texte sur une image existante. Ici nous allons créer un masque ayant comme dimension celle de l’écran ePaper 2,7”.
mask = Image.new('1', (EPD_HEIGHT,EPD_WIDTH), 255)
Comme nous l’avons vu dans l’article précédent, l’image produite est verticale. Si on veut un affichage horizontal, l’astuce consiste à créer une image dans les dimensions horizontales et verticales sont inversée puis la tourner de 90° avant de l’afficher sur l’écran ePaper. Créez un nouveau script nommé demopill.py et collez le code suivant
#Librairies nécessaires
from PIL import Image
from PIL import ImageDraw
#Dimensions de l'image
EPD_WIDTH = 176
EPD_HEIGHT = 264
# Créé un masque avec un fond blanc
mask = Image.new('1', (EPD_HEIGHT,EPD_WIDTH), 255)
#Créé un objet Draw qui va permettre d'ajouter des éléments sur le masque
draw = ImageDraw.Draw(mask)
#En exemple de texte
draw.text((EPD_HEIGHT/4,EPD_WIDTH/2), 'Demo Python PILL', fill = 0)
#Une ligne horizontale
draw.line((0,EPD_WIDTH/2 + 12, EPD_HEIGHT, EPD_WIDTH/2 + 12), fill = 0)
#On enregistre l'image générée
mask.save('demopill.bmp',"bmp")
Enregistrez le script et exécutez le avec la commande
python demopill.py
.
Voici l’image générée par le script. Comme vous pouvez le voir, elle est horizontale car j’ai inversé la hauteur (Height) et la largeur (Width). Ici, il n’est pas nécessaire de tourner l’image.
![]()
Récupérer des images et des fonts Open Source
J’ai utilisé des images gratuites et Open Source pour ce projet. Vous pouvez en trouver un peu partout sur internet. Voici les sites que j’ai utilisé pour ce projet :
- Les icônes Open Source de Kickstand Apps disponibles sur Github pour les symboles météo. Pratique, elles sont déjà au format PNG. Utilisez plutôt la version épaisse pour un meilleur rendu sur l’écran ePaper
- icones8.fr pour les pictogrammes température, humidité, pression atmosphérique, direction du vent
- La police FreeMonoBold.ttf peut être récupérée un peu partout sur internet comme ici sur GitHub
Installer et configurer le plugin météo sur Jeedom
Ouvrez le gestionnaire de plugin de Jeedom et recherchez le plugin Meteo officiel
![]()
Activez le plugin et configurez votre ville et attribuez un objet
![]()
Activer l’API JSON RPC et récupérer la clé API
Jeedom dispose de deux interfaces de communication. La première permet d’interroger Jeedom avec des requêtes HTTP. C’est cette interface qui avait été utilisé pour réaliser ce mini affichage déporté.
Pour varier les plaisirs (et aussi parce que l’API HTTP offre moins de possibilités), nous allons utiliser l’API JSON RPC pour ce projet. Le JSON RPC est un standard qui est documenté ici. Toutes les commandes exposées par Jeedom sont expliquées en détail sur la documentation en ligne.
Pour récupérer votre clé API et vérifier que les API sont activées, allez dans le menu de configuration (roue crantée) puis configuration. Ouvrez l’onglet API. Votre clé est la grande chaine de caractère. Vous pouvez en générée une nouvelle si vous pensez que la sécurité de votre serveur domotique a été corrompue. L’accès JSONPRC doit être activé. N’oubliez pas de sauvegarder si vous faites une modification.
![]()
Comment récupérer les prévisions météo avec l’API JSON RPC de Jeedom ?
Maintenant que tout est prêt, il est temps de commencer. Il existe (très grossièrement) deux types d’objets Jeedom qui vont nous intéresser ici. Les commandes et les équipements. Pour faire simple, le widget météo est un équipement. Un équipement est lui même attaché à un objet (le terme de groupe aurait été mieux adapté mais ce n’est pas très important). Ensuite chaque équipement peut posséder des commandes. Pour récupérer les identifiants de l’équipement et des commandes, il suffit d’ouvrir le panneau de Configuration avancée qui se trouve systématiquement dans le coins supérieur droit.
Ici l’équipement widget météo a l’IDentifiant 3.
![id equipement jeedom json rpc api]()
Cliquez ensuite sur la roue crantée de chaque commande pour récupérer son identifiant (ici 17).
![id commande equipement jeedom api json rpc]()
On peut aussi interroger l’API pour récupérer tous les identifiants mais la réponse renvoyée est très (trop) détaillée. Si vous avez de nombreux équipements vous risquez vite de vous noyer.
Créez un nouveau script python (par exemple jeedomrpc.py) et collez le code ci-dessous. Avant de l’exécuter vous devez modifier les paramètres suivants :
- ip_jeedom : l’adresse IP du serveur Jeedom
- Api_key : votre clé API récupérée précédemment
- conditiontxt : l’IDentifiant de la commande condition. Elle renvoi la prévision sous la forme d’une chaine
- condition: l’IDentifiant de la commande condition_numero. On récupère le code de la prévision d’OpenWeatherMap. On l’utilisera pour définir l’icône de la prévision à afficher
# coding: utf-8
import requests
import json
ip_jeedom = 'XXX.XXX.XXX.XXX'
Api_key = 'XXXX_JEEDOM_API_KEY_XXXX'
url = "http://%s/core/api/jeeApi.php"% ( ip_jeedom)
headers = {'content-type': 'application/json'}
#Dictionnaire qui contiendra les prévisions récupérées sur Jeedom
prevision = {}
def updateParameter(id, method):
# Toutes les méthodes json rpc de Jeedom sont disponibles ici
# https://jeedom.github.io/core/fr_FR/jsonrpc_api#tocAnchor-1-30-2
parameters = {
"jsonrpc" : "2.0",
"method" : method,
"params": {
"apikey": Api_key,
"id" : id
}
}
return parameters
def getDataFromJeedom():
#Identifiants de commandes à récupérer
idCmd = {
"conditiontxt": "17",
"condition" : "18"
}
#On interroge l'API JSON RPC de Jeedom pour chaque commande
for key, value in idCmd.iteritems():
param = updateParameter(value, "cmd::byId")
response = requests.post(url, data=json.dumps(param), headers=headers).json()
#Affichage la réponse renvoyée par Jeedom
print response
#Ajoute les valeur actuelle et l'unité dans le dictionnaire
prevision[key] = {
'value' : response['result']['currentValue'],
'unit' : response['result']['unite'],
}
#Affichage les données récupérées et stockées dans le dictionnaire
print prevision
getDataFromJeedom()
Exécutez le script (commande
python jeedomrpc.py
). La réponse d’affiche directement dans le Terminal (ou l’invite de commande sous Windows). Pour “déplier” la réponse au format JSON et la rendre plus lisible pour nous pauvres humains, vous pouvez utiliser
jsonlint.com par exemple.
{
u 'jsonrpc': u '2.0', u 'id': None, u 'result': {
u 'generic_type': u 'WEATHER_CONDITION',
u 'currentValue': u 'L\xe9g\xe8res chutes de neige',
u 'configuration': None,
u 'name': u 'Condition',
u 'display': {
u 'generic_type': u 'WEATHER_CONDITION'
},
u 'isHistorized': u '0',
u 'eqLogic_id': u '3',
u 'unite': u '',
u 'id': u '17',
u 'subType': u 'string',
u 'html': None,
u 'alert': None,
u 'value': None,
u 'template': None,
u 'isVisible': u '1',
u 'eqType': u 'weather',
u 'logicalId': u 'condition',
u 'type': u 'info',
u 'order': u '0'
}
} {
'conditiontxt': {
'unit': u '',
'value': u 'L\xe9g\xe8res chutes de neige'
}
}
Il ne faut pas s’inquiéter des ‘u’ devant chaque clé. Par contre ce qui est plus gênant c’est l’encodage des chaînes. Jeedom n’encode pas en utf-8 mais en latin-1. Cala risque de vous poser pas mal de problèmes. Voici quelques astuces :
- Forcer l’encodage en utf-8 au début du script en ajoutant le paramètre # coding: utf-8
- Créez une variable intermédiaire qui contient la condition avant de créer un objet texte avec la librairie Pill.
Pour info, voici également le dictionnaire (une variable) qui a été créé par ce script. On y retrouve la prévision et le code correspondant.
{
'conditiontxt': {
'unit': u '',
'value': u 'L\xe9g\xe8res chutes de neige'
},
'condition': {
'unit': u '',
'value': 600
}
}
Code python complet
Voilà, maintenant il ne reste plus qu’à tout assembler. Nous savons interroger l’API JSON RPC de Jeedom pour récupérer des états et des informations sur des équipements. Nous savons comment générer une image à l’aide de la libraire Python Pil. Enfin, nous savons intercepter les boutons Key1 à Key2 sur le coté de l’écran pour modifier l’affichage ou déclencher un scénario.
Créez un nouveau script et collez le code ci-dessous. Modifiez les paramètres suivants dans le code
- ip_jeedom : l’adresse IP du serveur Jeedom
- Api_key : votre clé API
- Dictionnaire idCmd : changez les identifiants des différentes commandes pour celles de votre équipement météo
- modeTest : permet de générer l’image sur un ordinateur (PC Windows, Linux, Mac, Raspberry Pi) sans utiliser le GPIO et l’écran ePaper. Le script génère juste une image en sortie
- Toutes les images et la police FreeMonoBold.ttf doivent être stockées dans le dossier image.
Téléchargez les images et la police FreeMonoBold.ttf depuis le blog en cliquant sur ce lien. Décompressez l’archive et placez le dossier à la racine du code python. La variable folder_img permet de modifier le dossier des ressources.
# coding: utf-8
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
import requests
import json
import math
import time
from datetime import datetime
from datetime import timedelta
import locale
locale.setlocale(locale.LC_TIME,'')
ip_jeedom = 'xxxx.xxx.xxx.xxx'
Api_key = 'XXXX_JEEDOM_API_KEY_XXXX'
url = "http://%s/core/api/jeeApi.php"% ( ip_jeedom)
headers = {'content-type': 'application/json'}
folder_img = 'images/'
H_condition = 100
W_condition = 100
H_Big = 15
H_icone = 25
W_icone = 25
Bord = 5
Col1 = 66
Col2 = 132
Col3 = 198
LiBaCd = 140
DiamPastille = 10
modeTest = False
prevision = {}
if modeTest:
EPD_WIDTH = 176
EPD_HEIGHT = 264
else:
print 'mode epd2in7'
import RPi.GPIO as GPIO
import epd2in7
epd = epd2in7.EPD()
epd.init()
EPD_WIDTH = epd2in7.EPD_WIDTH
EPD_HEIGHT = epd2in7.EPD_HEIGHT
GPIO.setmode(GPIO.BCM)
key1 = 5
key2 = 6
key3 = 13
key4 = 19
GPIO.setup(key1, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(key2, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(key3, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(key4, GPIO.IN, pull_up_down=GPIO.PUD_UP)
#Charge les fonts et les images
fontSmall = ImageFont.truetype(folder_img + 'FreeMonoBold.ttf', 9)
fontMedium = ImageFont.truetype(folder_img + 'FreeMonoBold.ttf', 12)
fontBig = ImageFont.truetype(folder_img + 'FreeMonoBold.ttf', H_Big)
temperature = Image.open(folder_img + 'temperature.png')
humidity = Image.open(folder_img + 'humidity.png')
pressure = Image.open(folder_img + 'pressure.png')
direction = Image.open(folder_img + 'direction.png')
lever = Image.open(folder_img + 'lever.png')
coucher = Image.open(folder_img + 'coucher.png')
#Redimensionnement des images
temperature = temperature.resize((H_icone,W_icone))
humidity = humidity.resize((H_icone,W_icone))
pressure = pressure.resize((H_icone,W_icone))
direction = direction.resize((H_icone,W_icone))
lever = lever.resize((H_icone ,W_icone ))
coucher = coucher.resize((H_icone ,W_icone ))
#w,h = condition.size
def updateParameter(id, method):
# Toutes les méthodes json rpc Jeedom disponibles https://jeedom.github.io/core/fr_FR/jsonrpc_api#tocAnchor-1-30-2
parameters = {
"jsonrpc" : "2.0",
"method" : method,
"params": {
"apikey": Api_key,
"id" : id
}
}
return parameters
def getSunTime(timestring):
if len(timestring) == 3:
#timestring[1:] +
return timestring[0:1] + 'h' + timestring[-2:]
else:
return timestring[0:2] + 'h' + timestring[-2:]
def getDirWind(WindDir):
#D'après https://www.campbellsci.com/blog/convert-wind-directions
Direction = ["N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW","N"]
CompassDir = int(round((math.fmod(WindDir,360))/ 22.5,0)+1)
return Direction[CompassDir]
def findIcone(condition_id):
# Toutes les conditions renvoyees par le plugin météo de Jeedom https://github.com/jeedom/plugin-weather/blob/beta/core/class/weather.class.php
# Prevision d apres le code openweathermap.org
# Icones Open Source https://github.com/kickstandapps/WeatherIcons
# Icones température, humidité, pression atmosphérique récupérées sur https://icones8.fr
if condition_id >= 200 and condition_id <= 299:
return 'Storm'
if condition_id >= 300 and condition_id <= 399:
return 'Haze'
if condition_id >= 500 and condition_id <= 510:
return 'PartlySunny'
if condition_id >= 520 and condition_id <= 599:
return 'Rain'
if condition_id >= 600 and condition_id <= 699 or condition_id == 511:
return 'Snow'
if condition_id >= 700 and condition_id <= 799:
return 'wind'
if condition_id >= 800 and condition_id <= 899:
return 'Cloud'
if condition_id == 800:
return 'Sun'
def getDataFromJeedom():
# ID des commandes de l'équipement météo
idCmd = {
"conditiontxt": "17",
"condition" : "18",
"leverSoleil" : "14",
"coucherSoleil" : "13",
"pa" : "10",
"humidite" : "9",
"tempMin" : "15",
"tempMax" : "16",
"vitVent" : "11",
"dirVent" : "12",
"conditionJ1" : "28",
"conditionJ2" : "30",
"conditionJ3" : "32",
"conditionJ4" : "34",
"condJ1Txt" : "27",
"condJ2Txt" : "29",
"condJ3Txt" : "31",
"condJ4Txt" : "33",
"tempMinJ1" : "19",
"tempMinJ2" : "21",
"tempMinJ3" : "23",
"tempMinJ4" : "25",
"tempMaxJ1" : "20",
"tempMaxJ2" : "22",
"tempMaxJ3" : "24",
"tempMaxJ4" : "26"
}
#Recupère le nom de la ville, modifier le 3 par l'ID de l'équipement météo
_parameters = updateParameter(3, "eqLogic::byId")
response = requests.post(url, data=json.dumps(_parameters), headers=headers).json()
prevision['city'] = response['result']['name']
#Récupère les prévisions
for key, value in idCmd.iteritems():
_parameters = updateParameter(value, "cmd::byId")
response = requests.post(url, data=json.dumps(_parameters), headers=headers).json()
#print response
if ( key == 'condition' or key == 'conditionJ1' or key == 'conditionJ2' or key == 'conditionJ3' or key == 'conditionJ4'):
prevision[key] = findIcone(response['result']['currentValue'])
print 'condition_id '+ str(response['result']['currentValue']) + " => " + str(prevision[key])
else:
prevision[key] = {
'value' : response['result']['currentValue'],
'unit' : response['result']['unite'],
}
print prevision
# Dessine l'écran du jour
def updateFrame1():
mask = Image.new('1', (EPD_HEIGHT,EPD_WIDTH), 255)
draw = ImageDraw.Draw(mask)
#Format date heure en Python https://www.cyberciti.biz/faq/howto-get-current-date-time-in-python/
#Entete
condition = Image.open(folder_img + prevision['condition'] + '.png')
condition = condition.resize((H_condition,W_condition))
mask.paste(condition, (0,0), condition)
mask.paste(lever, (W_condition + Bord,70), lever)
mask.paste(coucher, (W_condition + 90,70), coucher)
date = unicode(time.strftime("%a %d %B") + " " + time.strftime("%H:%M"),'UTF-8')
draw.text((W_condition + Bord,5), date, font = fontMedium, fill = 0)
condGauche = prevision['conditiontxt']['value'][0:16]
condDroite = prevision['conditiontxt']['value'][16:]
draw.text((W_condition + Bord,25), condGauche, font = fontBig, fill = 0)
draw.text((W_condition + Bord,45), condDroite, font = fontBig, fill = 0)
draw.text((W_condition + 45,75), getSunTime(str(prevision['leverSoleil']['value'])), font = fontSmall, fill = 0)
draw.text((W_condition + 125,75), getSunTime(str(prevision['coucherSoleil']['value'])), font = fontSmall, fill = 0)
#Prévision du jour en détail
#Tourne la bousole dans la direction du vent
direction.rotate(float(prevision['dirVent']['value']))
mask.paste(temperature, (Bord,110), temperature)
mask.paste(humidity, (Col1,110), humidity)
mask.paste(pressure, (Col2,110), pressure)
mask.paste(direction, (Col3,110), direction)
draw.text((Bord,LiBaCd), str(prevision['tempMax']['value']) + '%C', font = fontMedium, fill = 0)
draw.text((Col1,LiBaCd), str(prevision['humidite']['value'])+'%', font = fontMedium, fill = 0)
draw.text((Col2,LiBaCd), str(prevision['pa']['value'])+str(prevision['pa']['unit']), font = fontMedium, fill = 0)
draw.text((Col3 + 35,110), getDirWind(float(prevision['dirVent']['value'])), font = fontMedium, fill = 0)
draw.text((Col3,LiBaCd), str(prevision['vitVent']['value'])+str(prevision['vitVent']['unit']),font = fontMedium, fill = 0)
#Lignes
draw.line((0,H_condition,EPD_HEIGHT,H_condition), fill=0)
draw.line((0,LiBaCd + 20,EPD_HEIGHT,LiBaCd + 20), fill=0)
draw.line((W_condition,0,W_condition,H_condition), fill=0)
#Bas de page
draw.text((Bord,EPD_WIDTH - 18), str(prevision['city']),font = fontMedium, fill = 0)
draw.text((180,EPD_WIDTH - 17), "projetsdiy.fr",font = fontSmall, fill = 0)
draw.ellipse((97,EPD_WIDTH - DiamPastille - 2,107,EPD_WIDTH - 2), fill=0, outline=0)
draw.ellipse((117,EPD_WIDTH - DiamPastille - 2,127,EPD_WIDTH - 2), fill=255, outline=0)
draw.ellipse((137,EPD_WIDTH - DiamPastille - 2,147,EPD_WIDTH - 2), fill=255, outline=0)
draw.ellipse((157,EPD_WIDTH - DiamPastille - 2,167,EPD_WIDTH - 2), fill=255, outline=0)
out = mask.rotate(90)
out.save('frame1.bmp',"bmp")
if modeTest == False:
epd.display_frame(epd.get_frame_buffer(out))
#Prévisions des 4 prochains jours
def updateFrame2():
mask = Image.new('1', (EPD_HEIGHT,EPD_WIDTH), 255)
date = datetime.now()
draw = ImageDraw.Draw(mask)
pasHoriz = 16
#Format date heure en Python https://www.cyberciti.biz/faq/howto-get-current-date-time-in-python/
#Entete
draw.text((Bord,0), unicode("Prévisions à 4 jours",'utf-8'), font = fontBig, fill = 0)
draw.line((0,2*Bord + H_Big,EPD_HEIGHT,2*Bord + H_Big), fill=0)
draw.line((Col1, 2*Bord + H_Big, Col1, EPD_WIDTH - 15), fill=0)
draw.line((Col2, 2*Bord + H_Big, Col2, EPD_WIDTH - 20), fill=0)
draw.line((Col3, 2*Bord + H_Big, Col3, EPD_WIDTH - 20), fill=0)
draw.line((0,EPD_WIDTH - 15,EPD_HEIGHT,EPD_WIDTH - 15), fill=0)
#J+1
date = datetime.now() + timedelta(days=1)
draw.text((Bord, 3*Bord + H_Big), unicode(date.strftime("%A"),'utf-8'), font = fontMedium, fill = 0)
c1 = Image.open(folder_img + prevision['conditionJ1'] + '.png')
c1 = c1.resize((H_icone * 2,W_icone * 2))
mask.paste(c1, (Bord, 5*Bord + H_Big), c1)
prev1 = prevision['condJ1Txt']['value'][0:12]
prev2 = prevision['condJ1Txt']['value'][12:24]
print prev2
draw.text((Bord, 5 * pasHoriz), prev1, font = fontSmall, fill = 0)
draw.text((Bord, 6 * pasHoriz), prev2, font = fontSmall, fill = 0)
draw.text((Bord, 7 * pasHoriz), unicode(str(prevision['tempMinJ1']['value'])+'°C','utf-8'), font = fontMedium, fill = 0)
draw.text((Bord, 8 * pasHoriz), unicode(str(prevision['tempMaxJ1']['value'])+'°C','utf-8'), font = fontMedium, fill = 0)
#J+2
date = datetime.now() + timedelta(days=2)
draw.text((Bord + Col1, 3*Bord + H_Big), unicode(date.strftime("%A"),'utf-8'), font = fontMedium, fill = 0)
c1 = Image.open(folder_img + prevision['conditionJ2'] + '.png')
c1 = c1.resize((H_icone * 2,W_icone * 2))
mask.paste(c1, (Bord + Col1, 5*Bord + H_Big), c1)
prev1 = prevision['condJ2Txt']['value'][0:12]
prev2 = prevision['condJ2Txt']['value'][12:24]
draw.text((Bord + Col1, 5 * pasHoriz), prev1, font = fontSmall, fill = 0)
draw.text((Bord + Col1, 6 * pasHoriz), prev2, font = fontSmall, fill = 0)
draw.text((Bord + Col1, 7 * pasHoriz), unicode(str(prevision['tempMinJ2']['value'])+'°C','utf-8'), font = fontMedium, fill = 0)
draw.text((Bord + Col1, 8 * pasHoriz), unicode(str(prevision['tempMaxJ2']['value'])+'°C','utf-8'), font = fontMedium, fill = 0)
#J+3
date = datetime.now() + timedelta(days=3)
draw.text((Bord + Col2, 3*Bord + H_Big), unicode(date.strftime("%A"),'utf-8'), font = fontMedium, fill = 0)
c1 = Image.open(folder_img + prevision['conditionJ3'] + '.png')
c1 = c1.resize((H_icone * 2,W_icone * 2))
mask.paste(c1, (Bord + Col2, 5*Bord + H_Big), c1)
prev1 = prevision['condJ3Txt']['value'][0:12]
prev2 = prevision['condJ3Txt']['value'][12:24]
draw.text((Bord + Col2, 5 * pasHoriz), prev1, font = fontSmall, fill = 0)
draw.text((Bord + Col2, 6 * pasHoriz), prev2, font = fontSmall, fill = 0)
draw.text((Bord + Col2, 7 * pasHoriz), unicode(str(prevision['tempMinJ3']['value'])+'°C','utf-8'), font = fontMedium, fill = 0)
draw.text((Bord + Col2, 8 * pasHoriz), unicode(str(prevision['tempMaxJ3']['value'])+'°C','utf-8'), font = fontMedium, fill = 0)
#J+4
date = datetime.now() + timedelta(days=4)
draw.text((Bord + Col3, 3*Bord + H_Big), unicode(date.strftime("%A"),'utf-8'), font = fontMedium, fill = 0)
c1 = Image.open(folder_img + prevision['conditionJ4'] + '.png')
c1 = c1.resize((H_icone * 2,W_icone * 2))
mask.paste(c1, (Bord + Col3, 5*Bord + H_Big), c1)
prev1 = prevision['condJ4Txt']['value'][0:12]
prev2 = prevision['condJ4Txt']['value'][12:24]
draw.text((Bord + Col3, 5 * pasHoriz), prev1, font = fontSmall, fill = 0)
draw.text((Bord + Col3, 6 * pasHoriz), prev2, font = fontSmall, fill = 0)
#draw.text((Bord + Col1, 6 * pasHoriz), str(prevision['condJ4Txt']['value'][0:10]), font = fontSmall, fill = 0)
draw.text((Bord + Col3, 7 * pasHoriz), unicode(str(prevision['tempMinJ4']['value'])+'°C','utf-8'), font = fontMedium, fill = 0)
draw.text((Bord + Col3, 8 * pasHoriz), unicode(str(prevision['tempMaxJ4']['value'])+'°C','utf-8'), font = fontMedium, fill = 0)
#Bas de page
draw.text((Bord,EPD_WIDTH - 18), str(prevision['city']),font = fontMedium, fill = 0)
draw.text((180,EPD_WIDTH - 17), "projetsdiy.fr",font = fontSmall, fill = 0)
draw.ellipse((97,EPD_WIDTH - DiamPastille - 2,107,EPD_WIDTH - 2), fill=255, outline=0)
draw.ellipse((117,EPD_WIDTH - DiamPastille - 2,127,EPD_WIDTH - 2), fill=0, outline=0)
draw.ellipse((137,EPD_WIDTH - DiamPastille - 2,147,EPD_WIDTH - 2), fill=255, outline=0)
draw.ellipse((157,EPD_WIDTH - DiamPastille - 2,167,EPD_WIDTH - 2), fill=255, outline=0)
out = mask.rotate(90)
out.save('frame2.bmp',"bmp")
if modeTest==False:
epd.display_frame(epd.get_frame_buffer(out))
def main():
if modeTest:
getDataFromJeedom()
updateFrame1()
#updateFrame2()
else:
while True:
key1state = GPIO.input(key1)
key2state = GPIO.input(key2)
key3state = GPIO.input(key3)
key4state = GPIO.input(key4)
if key1state == False:
print('Update frame 1')
getDataFromJeedom()
updateFrame1()
time.sleep(0.5)
if key2state == False:
print('Update frame 2')
getDataFromJeedom()
updateFrame1()
time.sleep(0.5)
if key3state == False:
print('Key3 Pressed')
time.sleep(0.2)
if key4state == False:
print('Key4 Pressed')
if __name__ == "__main__":
#Met à jour l'écran au démarrage
getDataFromJeedom()
#updateFrame2()
updateFrame1()
#puis attend un événement sur les touches Key1 à Key4 (sur un Raspberry Pi uniquement)
main()
Remarque. J’ai juste un doute sur l’orientation correcte de la boussole en fonction de la direction du vent. Je compte sur vous pour le vérifier.
Le dashboard météo ePaper pour Jeedom obtenu
Il ne reste plus qu’à lancer le script. Au premier lancement, le script génère et affiche immédiatement la première page de la station météo. Pour afficher le second panneau, appuyer sur le bouton Key2. Les autres écrans sont disponibles pour afficher d’autres informations en provenance de Jeedom. Etat des lampes, des portes, niveau de CO2, température, consommation électrique…
La première page (Key1) récapitule la météo du jour
![frame1 jeedom station meteo epaper waveshare eink jsin rpc]()
Le second écran (key2) permet d’afficher les prévisions à 4 jours. Et oui, c’est l’hivers ![🙁]()
![frame2 jeedom station meteo epaper waveshare eink jsin rpc prevision 4 jours]()
La même chose en vrai avec l’écran Waveshare 2,7” installé sur un Raspbery Pi3 qui fait tourner le serveur Jeedom !
![jeedom epaper waveshare display dashboard weather station]()
![jeedom epaper waveshare display dashboard weather station frame2]()
Voilà, il ne plus qu’à faire la même chose avec Domoticz ! J’espère que ce projet vous donnera plein de nouvelles idées. Pour le moment, j’ai réalisé les deux tutoriels avec un Raspberry Pi 3. L’intérêt principal des écrans ePaper étant l’absence de consommation électrique après le rafraichissement de l’affichage, il est temps de le tester avec un micro-contrôleur ESP8266 ou ESP32.