Servidor Raspberry Pi com Flask – Final da Estrutra
Neste artigo, iremos terminar o servidor desenvolvido nos posts anteriores, aconselhamos fortemente a visitar os tutoriais anteriores para que possa estar a par dos conceitos abordados e desenvolvidos neste artigo.
Pode consultar os posts anteriores aqui e aqui.
Estrutura do Ambiente
Iremos começar por preparar o nosso ambiente com a seguinte estrutura:
- Pasta: Servidor
- Ficheiro: run.py
- Pasta: servidor
- Ficheiro: __init__.py
- Ficheiro: config.py
- Ficheiro: getCuriosidades.py
- Ficheiro: getNoticias.py
- Ficheiro: getTempo.py
- Ficheiro: routes.py
- Pasta: Templates:
- Ficheiro: base.html
- Ficheiro: home.html
- Ficheiro: noticias.html
- Ficheiro: sensores.html
- Pasta: static:
- Pasta: styles
- Ficheiro: style.css
- Pasta: styles
Sobre os Ficheiros – Pasta Servidor
run.py
Responsável por inicializar o nosso servidor.
from servidor import app if __name__ == "__main__": app.run(debug = True, host="0.0.0.0", port=80)
from flask import Flask app = Flask(__name__) from servidor import routes
config.py
def userData(): userData.user = "Nome"
getCuriosidades.py
Responsável por mostrar uma curiosidade aleatória sempre que a página principal é aberta.
import randfacts from googletrans import Translator def getCuriosidade(): fact = randfacts.get_fact(filter_enabled=filter) translator = Translator() fact_pt = translator.translate(fact, dest="pt").text fatosDic = {"Fact_PT":fact_pt, "Fact_EN": fact} print(fatosDic) return fatosDic
getNoticias.py
Responsável por fazer webscraping as noticias mais recentes – A criação e explicação deste ficheiro foi desenvolvida aqui.
import os try: from bs4 import BeautifulSoup as sp except: print("Necessario Instalar BS4") print("Execute o seguinte comando:") print(" python3 -m pip install bs4") print("E aguarde a instalação do pacote") import requests def getNoticias(): arrCategoriasJN = ['nacional','justica', 'mundo', 'economia','desporto', 'inovacao', 'artes', 'opiniao'] arrNoticias = [] firstTime = True for cat in arrCategoriasJN: if firstTime == True: active = "active" firstTime = False else: active = "" linkNoticia = f"https://www.jn.pt/{cat}.html" r = requests.get(linkNoticia) soup = sp(r.content, "html.parser") mainDivNoticia = soup.find(class_ = "t-grid-1-featured-2") tituloNoticia = mainDivNoticia.find("h2").text linkNoticia = "https://www.jn.pt" + str(mainDivNoticia.find( 'h2').find('a')['href']) linkImagemNoticia = mainDivNoticia.find('figure').find('source')['data-srcset'] descNoticia = mainDivNoticia.find('h4').text noticia = {"titulo":tituloNoticia, "link":linkNoticia, "desc":descNoticia, "img":linkImagemNoticia, "actv":active} arrNoticias.append(noticia) return arrNoticias
getTempo.py
Responsive por fazer webscrape as previsões meteorológicas dos próximos 3 dias em função da sua localidade.
from bs4 import BeautifulSoup as sp from datetime import date import requests import geocoder def getMetereologia(): myLocation = geocoder.ip('me').city myLocationFormatted = myLocation.replace(" ", "") site = f"https://www.weatheronline.pt/Portugal/{myLocationFormatted}.htm" r = requests.get(site) soup = sp(r.content) arrayPrevisoes = [] table = soup.find("table") for x in range(1,4): data = table.find_all("tr")[0].find_all("td")[x].text data_min = table.find_all("tr")[1].find_all("td")[x].text data_max = table.find_all("tr")[2].find_all("td")[x].text #Data 1 Manha data_manha_img_link = table.find_all("tr")[4].find_all("td")[x].find("img")['src'] data_manha_img_title = table.find_all("tr")[4].find_all("td")[x].find("img")['title'].replace(" ", " ").replace(" , ", ", ").capitalize() #Data 1 Tarde data_tarde_img_link = table.find_all("tr")[5].find_all("td")[x].find("img")["src"] data_tarde_img_title = table.find_all("tr")[5].find_all("td")[x].find("img")["title"].replace(" ", " ").replace(" , ", ", ").capitalize() #Data 1 Noite data_noite_link = table.find_all("tr")[6].find_all("td")[x].find("img")["src"] data_noite_title = table.find_all("tr")[6].find_all("td")[x].find("img")["title"].replace(" ", " ").replace(" , ", ", ").capitalize() temperatura = {"cidade":myLocation, "data":data, "temp_max":data_max, "temp_min":data_min,"data_manha_img_link":data_manha_img_link, "data_manha_img_title":data_manha_img_title, "data_tarde_img_link":data_tarde_img_link, "data_tarde_img_title":data_tarde_img_title, "data_noite_link":data_noite_link, "data_noite_title":data_noite_title} arrayPrevisoes.append(temperatura) return arrayPrevisoes
routes.py
Ficheiro que possui os diretórios de todo o nosso servidor
from flask import render_template, url_for, request from servidor import app from servidor.getNoticias import getNoticias from servidor.sendRequest import sendRequest from servidor.getCuriosidade import getCuriosidade from servidor.getTempo import getMetereologia from servidor.config import userData import socket userData() @app.route("/") def homepage(): username = userData.user dadosTempo = getMetereologia() randomFact = getCuriosidade() return render_template("home.html", username = username, dadosTempo = dadosTempo, randomFact = randomFact) @app.route("/noticias") def noticias(): noticias = getNoticias() return render_template("noticias.html", noticias = noticias) @app.route("/sensores") def sensores(): return render_template("sensores.html") #Interpretar Requests #Quarto #Quarto Luz Principal #Ligar @app.route("/ligar_Luz", methods=['POST']) def ligar_LuzQuarto (): if request.method == "POST": requestType = "ligar_Luz" sendRequest("IP", requestType) return render_template("sensores.html") @app.route("/desligar_Luz", methods=['POST']) def desligar_LuzQuarto (): if request.method == "POST": requestType = "desligar_Luz" sendRequest("IP", requestType) return render_template("sensores.html") #Quarto Luz Candeeiro #Ligar @app.route("/ligar_LuzCandeeiro", methods=['POST']) def ligar_LuzCandeeiro(): if request.method == "POST": requestType = "ligar_LuzCandeeiro" sendRequest("IP", requestType) return render_template("sensores.html") #Desligar @app.route("/desligar_LuzCandeeiro", methods=['POST']) def desligar_LuzCandeeiro(): if request.method == "POST": requestType = "desligar_LuzCandeeiro" sendRequest("IP", requestType) return render_template("sensores.html") #Sala #Luz Sala de Estar #Ligar @app.route("/ligar_LuzSaladeEstar", methods=['POST']) def ligar_LuzSaladeEstar(): if request.method == "POST": requestType = "ligar_LuzSaladeEstar" sendRequest("IP", requestType) return render_template("sensores.html") #Desligar @app.route("/desligar_LuzSaladeEstar", methods=['POST']) def desligar_LuzSaladeEstar(): if request.method == "POST": requestType = "desligar_LuzSaladeEstar" sendRequest("IP", requestType) return render_template("sensores.html") #Luz Sala de Jantar #Ligar @app.route("/ligar_LuzSaladeJantar", methods=['POST']) def ligar_LuzSaladeJantar(): if request.method == "POST": requestType = "ligar_LuzSaladeJantar" sendRequest("IP", requestType) return render_template("sensores.html") @app.route("/desligar_LuzSaladeJantar", methods=['POST']) def desligar_LuzSaladeJantar(): if request.method == "POST": requestType = "desligar_LuzSaladeJantar" sendRequest("IP", requestType) return render_template("sensores.html") @app.route("/ligar_FichaCarregamento", methods=['POST']) def ligar_FichaCarregamento(): if request.method == "POST": requestType = "ligar_FichaCarregamento" sendRequest("IP", requestType) return render_template("sensores.html") @app.route("/desligar_FichadeCarregamento", methods=['POST']) def desligar_FichadeCarregamento(): if request.method == "POST": requestType = "desligar_FichadeCarregamento" sendRequest("IP", requestType) return render_template("sensores.html")
Sobre os Ficheiros – Pasta Templates
Base.html
Ficheiro template base que será utilizado nas restantes páginas do nosso servidor como estrutura básica
<!DOCTYPE html> <html charset="utf-8" lang="pt"> <head> <title>{% block title %}{% endblock %}</title> <!--Link Documento CSS--> <link rel="stylesheet" type="text/css" href= "{{ url_for('static',filename='styles/style.css') }}"> <!-- CSS only --> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!--BootStrap 4--> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> <!--Google Fonts--> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Glory&display=swap" rel="stylesheet"> <link rel="icon" href="https://www.electrofun.pt/img/favicon.ico?1528822022" > </head> <body> <!--Barra de Nav.--> <nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <a class="navbar-brand" href="{{ url_for('homepage') }}"> <img id="logo" src="https://www.electrofun.pt/img/favicon.ico?1528822022" width="18px" height="20px"> Servidor Local </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav"> <li class="nav-item hover-menu"> <a class="nav-link {% block homepageActive %} {% endblock %}" href="{{ url_for('homepage') }}">Home</a> </li> <li class="nav-item hover-menu"> <a class="nav-link {% block noticiasActive %} {% endblock %}" href="{{ url_for('noticias') }}">Noticias</a> </li> <li class="nav-item hover-menu"> <a class="nav-link {% block sensoresActive %} {% endblock %}" href="{{ url_for('sensores') }}">Casa</a> </li> <li class="nav-item hover-menu"> <a class="nav-link" target="_blank" href="https://www.electrofun.pt/">ElectroFun</a> </li> <li class="nav-item hover-menu"> <a class="nav-link" target="_blank" href="https://www.electrofun.pt/blog/">+ Projetos</a> </li> <li class="nav-item hover-menu"> <a class="nav-link" target="_blank" href="https://www.instagram.com/electrofun.pt/">Instagram</a> </li> <li class="nav-item hover-menu"> <a class="nav-link" target="_blank" href="https://www.youtube.com/channel/UCwD7RHUJu6z1WX8PRb99iYQ">Youtube</a> </li> </ul> </div> </nav> {% block content %} {% endblock %} <style> body{ background-color:#2a2c2e; color:rgb(236, 236, 236); } .active{ color: #e7b851 !important; } #logo{ padding-bottom:1%; } a:hover { color:#ddb867 !important; } </style> </body> </html> <style>
body{ background-color:#2a2c2e; color:rgb(236, 236, 236); } .active{ color: #e7b851 !important; } #logo{ padding-bottom:1%; } a:hover { color:#ddb867 !important; } </style> </body> </html>
home.html
{% extends "base.html" %} {% block title %} HomePage {% endblock %} {% block homepageActive %} active {% endblock %} {% block content %} <div class = "container"> <div class = "row"> <div class="col-lg"> <h2 style = "padding-top: 2%;">Olá <span style="color:#ddb867;">{{username}}</span></h2> </div> </div> </div> <div class = "container"> <div class = "row"> {%for prev in dadosTempo%} <div class = "col-lg" style="width: 25%; padding-top:2%;"> <div class="card bg-dark text-white" style="border-radius: 5%;"> <div class="card-header text-center" style="color:#e7b851;"> <strong>Previsão {{prev.data}}, {{prev.cidade}}</strong> </div> <div class="card-body text-left"> <p class="card-title">Temperatura Máxima: {{prev.temp_max}} </p> <p class="card-title">Temperatura Mínima: {{prev.temp_min}} </p> <p class="card-text" style="padding-top:2%;"> <table class="table text-center" > <thead class = "thead-dark" > <tr> <th scope="col">Manhã</th> <th scope="col">Tarde</th> <th scope="col">Noite</th> </tr> </thead> <tbody> <tr> <td><img id = "tempoImagem" src="{{prev.data_manha_img_link}}" alt="{{prev.data_manha_img_title}}" title = "{{prev.data_manha_img_title}}"></td> <td><img id = "tempoImagem" src="{{prev.data_tarde_img_link}}" alt="{{prev.data_tarde_img_title}}" title = "{{prev.data_tarde_img_title}}"></td> <td><img id = "tempoImagem" src="{{prev.data_noite_link}}" alt="{{prev.data_noite_title}}" title = "{{prev.data_noite_title}}"></td> </tr> </tbody> </table> </p> </div> </div> </div> {%endfor%} </div> </div> <div class = "container"> <div class = "row"> <div class="col-lg"> <div class="card bg-dark text-white text-center " style="margin-top:3%; border-radius:5%; max-width: 100%; min-width: 100%;"> <div class="card-header" style="color:#e7b851"> <strong>Curiosidade</strong> </div> <div class="card-body"> <h5 class="card-title">{{randomFact.Fact_PT}}</h5> <p class="card-text" style="color:rgb(167, 167, 167)">{{randomFact.Fact_EN}}</p> </div> </div> </div> </div> </div> <style> #tempoImagem{ margin-top:20%; filter: brightness(0.80); } </style> {% endblock %}
noticias.html
Template que constitui um carrousel fornecido pelo Bootstrap com as noticias.
{% extends "base.html" %} {% block title %} Noticias {% endblock %} {% block noticiasActive %} active {% endblock %} {% block content %} <section id="news"> <h2 id="tituloSection">Últimas Notícias </h2> <div id="carouselExampleControls" class="carousel slide" data-ride="carousel"> <div class="carousel-inner"> {% for item in noticias %} <div class="carousel-item {{item.actv}}"> <img class="news-img rounded-circle news-img" src="{{item.img}}" alt="noticia"> <h2 id="tituloNoticia">{{item.titulo}}</h2> <p id="descricaoNoticia">{{item.desc}}</p> <a class="linkContinuarLer" href="{{item.link}}">Continuar a Ler</a> </div> {% endfor %} <a class="carousel-control-prev" href="#carouselExampleControls" role="button" data-slide="prev"> <span class="carousel-control-prev-icon" aria-hidden="true"></span> <span class="sr-only">Previous</span> </a> <a class="carousel-control-next" href="#carouselExampleControls" role="button" data-slide="next"> <span class="carousel-control-next-icon" aria-hidden="true"></span> <span class="sr-only">Next</span> </a> </div> </section> {% endblock %}
sensores.html
Página com vários formulários que num futuro tutorial iram efetuar a comunicação com NodeMCU/Esp8266-Esp-01 de forma a poder controlar os equipamentos da sua casa.
{% extends "base.html" %} {% block title %} Sensores Locais {% endblock %} {% block sensoresActive %} active {% endblock %} {% block content %} <div style ="padding-top:2%;color:#e7b851; padding-left:5%;"> <h3>Quarto Principal</h3> </div> <div class="row" style ="padding-top:2%; padding-left:5%; padding-right:5%;"> <div class="col-sm-3"> <div class="card border-0"> <div class="card-body"> <form class="text-center" method = "post"> <h5 class="card-title">Luz Principal</h5> <input type = "submit" class="btn btn-secondary" value = "Ligar" formaction="/ligar_Luz"> <input type = "submit" class="btn btn-secondary" value = "Desligar" formaction="/desligar_Luz"> </form> </div> </div> </div> <div class="col-sm-3"> <div class="card border-0"> <div class="card-body"> <form class="text-center" method = "post"> <h5 class="card-title">Luz Candeeiro</h5> <input type = "submit" class="btn btn-secondary" value = "Ligar" formaction="/ligar_LuzCandeeiro"> <input type = "submit" class="btn btn-secondary" value = "Desligar" formaction="/desligar_LuzCandeeiro"> </form> </div> </div> </div> </div> <div style ="padding-top:2%;color:#e7b851; padding-left:5%;"> <h3>Sala</h3> </div> <div class="row" style ="padding-top:2%; padding-left:5%; padding-right:5%;"> <div class="col-sm-3"> <div class="card border-0"> <div class="card-body"> <form class="text-center" method = "post"> <h5 class="card-title">Luz Sala de Estar</h5> <input type = "submit" class="btn btn-secondary" value = "Ligar" formaction="/ligar_LuzSaladeEstar"> <input type = "submit" class="btn btn-secondary" value = "Desligar" formaction="/desligar_LuzSaladeEstar"> </form> </div> </div> </div> <div class="col-sm-3"> <div class="card border-0"> <div class="card-body"> <form class="text-center" method = "post"> <h5 class="card-title">Luz Sala de Jantar</h5> <input type = "submit" class="btn btn-secondary " value = "Ligar" formaction="/ligar_LuzSaladeJantar"> <input type = "submit" class="btn btn-secondary" value = "Desligar" formaction="/desligar_LuzSaladeJantar"> </form> </div> </div> </div> <div class="col-sm-3"> <div class="card border-0"> <div class="card-body"> <form class="text-center" method = "post"> <h5 class="card-title">Ficha Sala</h5> <input type = "submit" class="btn btn-secondary" value = "Ligar" formaction="/ligar_FichaCarregamento"> <input type = "submit" class="btn btn-secondary" value = "Desligar" formaction="/desligar_FichadeCarregamento"> </form> </div> </div> </div> </div>
<style> .card-body{ background-color: #343a40; color:rgb(236, 236, 236); } .btn-secondary:hover{ background-color:#c0a469!important; } </style> {% endblock %}
Pasta Styles
styles.css
Responsável por fazer algumas alterações a nível gráfico do nosso servidor.
/* Estilos NavBar*/ .navbar{ font-family: 'Glory', sans-serif; } .nav-item { padding: 0 18px; } .nav-link { font-size: 1.2rem; font-family: 'Glory', sans-serif; } /*Estilos de noticias.htmk*/ #tituloSection { padding-top:10px; color: #ffffff; font-size: 26px; font-family: 'Glory', sans-serif; } /* Titulo Noticia*/ #tituloNoticia { color: #e7b851; text-align: center; font-size: 24px; font-weight: bold; width: 50%; margin-left: 24%; font-family: 'Glory', sans-serif; } /*Desc. Noticia*/ #descricaoNoticia { color: #ffffff; } .linkContinuarLer { color: #ececec; } .linkContinuarLer:hover { color: #ffffff; font-style: none; font-weight: bold; text-decoration: none; } /*Containers*/ #news { position: static; padding: 0 15% 0 15%; text-align: center; } #news p { text-align: center; font-size: 16px; width: 50%; margin-left: 24%; position: static; } .news-img { margin: 15px; margin-bottom: 30px; }
Conclusão
Como mencionado nos tutoriais anteriores, aquilo que estamos a ilustrar é apenas uma forma de efetuar este projeto, o HTML pode ser modificado ao seu gosto de forma a ficar mais do seu grado. Pode também adicionar outras páginas ao ficheiro routes.py de forma a criar outras páginas que ache interessantes.
Para mais projetos, percorram o nosso blog, onde podem encontrar vários artigos interessantes relacionados com eletrónica, robótica e muito mais! Visitem também o nosso site, onde encontram tudo para eletrónica e robótica!