Je m'appelle Niamugi et je suis en charge du 4ème jour du Calendrier de l'Avent IoTLT 2019. "Afficher les prévisions météo sur la pile M5" est un contenu très solide, mais je voudrais le présenter car il a un mécanisme agréable et polyvalent. ** Il est populaire auprès de ma famille et je suis heureux qu'il soit utilisé quotidiennement. ** **
Affiche les prévisions météorologiques des 3 derniers jours. La marque météorologique a été écrite par mon fils aîné.

Il sera affiché comme ceci.

L'image des prévisions météo est divisée en "** générer " et " acquérir **".

Je vais énumérer les points.
J'obtiens des données en accédant à Prévisions météorologiques de l'Agence météorologique.
Si vous souhaitez que Cloud Functions s'exécutant sur le cloud gère vos fichiers, vous pouvez les enregistrer dans le / tmp folder. En d'autres termes, en enregistrant le fichier obtenu par Google Drive dans le dossier / tmp, vous pouvez gérer le fichier de la même manière que dans l'environnement local.
Obtenez à l'avance l'ID client, le secret client et le jeton d'actualisation requis pour l'accès. J'ai déjà écrit à ce sujet sur le blog de dot studio. [Comment télécharger des données de NefryBT vers Google Drive](https://dotstud.io/blog/update-nefrybt-to-googledrive/#%E3%82%A2%E3%83%83%E3%83%97% E3% 83% AD% E3% 83% BC% E3% 83% 89% E3% 81% BE% E3% 81% A7% E3% 81% AE% E6% 89% 8B% E9% A0% 86 )Prière de se référer à.
Obtenez le service pour accéder à Google Drive à l'aide de l'ID client, du secret client et du jeton d'actualisation. J'ai fait référence à "API Google de jeton d'actualisation python: obtenir les informations d'identification du jeton de mise à jour en utilisant oauth2client.client".
def getDriveService():
    CLIENT_ID = os.getenv("drive_client_id")
    CLIENT_SECRET = os.getenv("drive_client_secret")
    REFRESH_TOKEN = os.getenv("drive_refresh_token")
    creds = client.OAuth2Credentials(
        access_token=None,
        client_id=CLIENT_ID,
        client_secret=CLIENT_SECRET,
        refresh_token=REFRESH_TOKEN,
        token_expiry=None,
        token_uri=GOOGLE_TOKEN_URI,
        user_agent=None,
        revoke_uri=None,
    )
    http = creds.authorize(httplib2.Http())
    creds.refresh(http)
    service = build("drive", "v3", credentials=creds, cache_discovery=False)
    return service
Des identifiants sont attribués à chaque donnée dans Google Drive. La recherche d'identifiant est nécessaire pour acquérir et mettre à jour les données par identifiant.
def searchID(service, mimetype, nm):
    """Rechercher un identifiant correspondant sur Drive
    """
    query = ""
    if mimetype:
        query = "mimeType='" + mimetype + "'"
    page_token = None
    while True:
        response = (
            service.files()
            .list(
                q=query,
                spaces="drive",
                fields="nextPageToken, files(id, name)",
                pageToken=page_token,
            )
            .execute()
        )
        for file in response.get("files", []):
            if file.get("name") == nm:
                return True, file.get("id")
        page_token = response.get("nextPageToken", None)
        if page_token is None:
            break
Cloud Functions fonctionne sur le cloud, vous ne pourrez donc probablement pas utiliser de polices japonaises. (Je ne l'ai pas essayé) Alors récupérez la police sur Google Drive. mimetype est "application / octat-stream".
def getFontFromDrive(service, fontName):
    """Obtenez les polices de Drive et enregistrez-les dans le dossier tmp
    """
    ret, id = searchID(service, "application/octet-stream", fontName)
    if not ret:
        return None
    request = service.files().get_media(fileId=id)
    fh = io.FileIO("/tmp/" + fontName, "wb")  #Fichier
    downloader = MediaIoBaseDownload(fh, request)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
    return "/tmp/" + fontName
Obtenez la marque météo. Le type MIME est "image / png".
def getImageFromDrive(service, imageName):
    """Récupérez l'image de Drive et enregistrez-la dans le dossier tmp
    """
    ret, id = searchID(service, "image/png", imageName)
    if not ret:
        return False
    request = service.files().get_media(fileId=id)
    fh = io.FileIO("/tmp/" + imageName, "wb")  #Fichier
    downloader = MediaIoBaseDownload(fh, request)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
    return True
Téléchargez l'image de prévision météo générée sur Google Drive.
def uploadData(service, mimetype, fromData, toData, parentsID="root"):
    """Télécharger sur Drive
    """
    try:
        media = MediaFileUpload(fromData, mimetype=mimetype, resumable=True)
    except FileNotFoundError:
        return False
    #Recherchez l'ID et écrasez s'il existe des données applicables.
    ret, id = searchID(service, mimetype, toData)
    if ret:
        file_metadata = {"name": toData}
        file = (
            service.files()
            .update(fileId=id, body=file_metadata, media_body=media, fields="id")
            .execute()
        )
    else:
        file_metadata = {"name": toData, "parents": [parentsID]}
        file = (
            service.files()
            .create(body=file_metadata, media_body=media, fields="id")
            .execute()
        )
    return True
Utilisez la fonction préparée ci-dessus pour télécharger l'image des prévisions météorologiques sur Google Drive.
def CreateImgWeather(event, context):
    """ get weatherImage and upload to drive for M5stack
    """
    # 1.Obtenez un service pour accéder à Google Drive
    driveService = getDriveService()
    # 2.Obtenir la police
    fontPath = getFontFromDrive(driveService, "meiryo.ttc")
    if not fontPath:
        return False
    # 3.Obtenez la marque météo
    if not getImageFromDrive(driveService, "noImage.png "):
        return False
    if not getImageFromDrive(driveService, "fine.png "):
        return False
    if not getImageFromDrive(driveService, "cloud.png "):
        return False
    if not getImageFromDrive(driveService, "rain.png "):
        return False
    if not getImageFromDrive(driveService, "snow.png "):
        return False
    # 4.Générer une image de prévision météo
    weatherList = getWeekWeather()
    ret = createImg(fontPath, "/tmp/imgWeather.jpeg ", weatherList)
    if not ret:
        return False
    # 5.Télécharger sur Google Drive
    ret = uploadData(
        driveService, "image/jpeg", "/tmp/imgWeather.jpeg ", "imgWeather.jpeg "
    )
    if not ret:
        return False
    return True
Voir Source pour plus de détails.
Accédez aux fonctions Cloud Functions avec une requête HTTP POST. C'est aussi dotstudio "Lancer une requête via une communication HTTP" Je l'ai utilisé comme référence.
[nom d'hôte] = "[Nom du projet].cloudfunctions.net"
[Nom de la fonction] = "getDriveImage_M5stack";
[numéro de port] = 443;
      
POST /[Nom de la fonction] HTTP/1.1
Host: [nom d'hôte]:[numéro de port]
Connection: close
Content-Type: application/json;charset=utf-8
Content-Length:  + [Taille des données JSON à publier]
[Données Json à publier]
Faites la demande suivante au format de données json.
{
  "drive" : {
    "img" : "[nom de fichier]",
    "trim" : "[Numéro de division]"
  }
}
En raison de la quantité de données qui peuvent être acquises en même temps, elles sont divisées en 8 parties. Par conséquent, il effectue une requête POST 8 fois.
Acquiert l'image de prévision météo selon la demande POST de M5Stack. Ensuite, il renvoie les données binaires divisées en 8 parties.
Je vais mettre la source.
import sys
import os
import io
from io import BytesIO
import numpy as np
from PIL import Image
import httplib2
from googleapiclient.discovery import build
from oauth2client import client, GOOGLE_TOKEN_URI
from apiclient.http import MediaIoBaseDownload
def getDriveService():
    ~Identique à la génération d'image~
def searchID(service, mimetype, nm):
    ~Identique à la génération d'image~
def downloadData(mimetype, data):
    #Obtenez un service pour accéder à Google Drive
    drive_service = getDriveService()
    #Rechercher une pièce d'identité
    ret, id = searchID(drive_service, mimetype, data)
    if not ret:
        return False, None
    #Rechercher des images de prévisions météorologiques
    request = drive_service.files().get_media(fileId=id)
    fh = io.BytesIO()
    downloader = MediaIoBaseDownload(fh, request)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
    
    return True, fh.getvalue()
def devideImage_M5stack(imgBinary, _trim):
    """Divisez l'image pour M5Stack. La valeur de retour correspond aux données d'image
    """
    imgNumpy = 0x00
    #Confirmation des données d'entrée
    if _trim.isnumeric():
        trimPos = int(_trim)
        if trimPos <= 0 or trimPos > 8:
            return False
    else:
        return False
    #Image fractionnée
    # 1 2 3 4
    # 5 6 7 8
    Trim = [
        (0, 0, 80, 120),
        (80, 0, 160, 120),
        (160, 0, 240, 120),
        (240, 0, 320, 120),
        (0, 120, 80, 240),
        (80, 120, 160, 240),
        (160, 120, 240, 240),
        (240, 120, 320, 240),
    ]
    #Image PIL<-Données binaires
    img_pil = Image.open(BytesIO(imgBinary))
    #garniture
    im_crop = img_pil.crop(Trim[trimPos - 1])
    #tableau numpy(RGBA) <-Image PIL
    imgNumpy = np.asarray(im_crop)
    return True, imgNumpy
def getBinary(img):
    """Convertir des images en données binaires
    """
    ret = ""
    pilImg = Image.fromarray(np.uint8(img))
    output = io.BytesIO()
    pilImg.save(output, format="JPEG")
    ret = output.getvalue()
    return ret
def getDriveImg_Binary(imgName, trim):
    """Obtenez l'image enregistrée dans googleDrive. La valeur de retour est des données binaires.
    """
    img = 0x00
    #Image de Drive(Données binaires)Avoir
    ret, imgBinary = downloadData("image/jpeg", imgName)
    if not ret:
        print("...error")
        return ""
    print(ret, len(imgBinary))
    #Diviser l'image
    #* Pour M5 Stack uniquement
    if trim is not None:
        isGet, img = devideImage_M5stack(imgBinary, trim)
        if not isGet:
            return ""
        #Convertir en données binaires
        imgBinary = getBinary(img)
    return imgBinary
def getDriveImage_M5stack(request):
    imgName = ""
    trim = "0"
    #Demander des données(JSON)Convertir
    request_json = request.get_json()
    #Obtenez des informations d'accès à Google Drive
    if request_json and "drive" in request_json:
        imgName = request_json["drive"]["img"]
        trim = request_json["drive"]["trim"]
    else:
        return ""
    #Obtenez une image de prévisions météo découpée
    ret = getDriveImg_Binary(imgName, trim)
    return ret
L'avantage de ce mécanisme est que "** Vous pouvez l'afficher sur M5Stack si vous préparez une image **". En d'autres termes, il ne se limite pas aux prévisions météorologiques, mais peut gérer n'importe quoi, comme les horaires et les tâches. Côté M5Stack, définissez simplement le nom de l'image à acquérir. De plus, comme l'image est générée en dehors de M5Stack, il n'est pas nécessaire de toucher le programme M5Stack lorsque vous souhaitez modifier l'image.
Voici le modèle qui affiche le calendrier Google. (L'horaire est en mosaïque)

En créant un système d'affichage d'image qui correspond à M5Stack cette fois, j'en suis venu à réfléchir à certains modèles d'application. L'affichage de M5Stack est juste la bonne taille pour la table, je voudrais donc l'utiliser de différentes manières.
J'espère que cela vous sera utile. À bientôt.
[Comment télécharger des données de NefryBT vers Google Drive](https://dotstud.io/blog/update-nefrybt-to-googledrive/#%E3%82%A2%E3%83%83%E3%83%97% E3% 83% AD% E3% 83% BC% E3% 83% 89% E3% 81% BE% E3% 81% A7% E3% 81% AE% E6% 89% 8B% E9% A0% 86 ) API Google de jeton d'actualisation python: obtenez les informations d'identification du jeton de mise à jour à l'aide de oauth2client.client Lancer une requête via la communication HTTP
Recommended Posts