Boas,
De forma a visualizar o saldo do cartão refeição do Santander no Home Assistant, configurei e desenvolvi o seguinte script aqui disponibilizado para quem necessitar de fazer o mesmo ou até para novas ideias.
Este projeto ainda está em fase de desenvolvimento/testes e melhoria, pelo que todas as opiniões, críticas ou sugestões serão sempre bem vindas.
Add-On utilizados:
- AppDaemon
- Visual Studio Code ou File Editor
1º - Criar Sensor e Switch de atualização
De forma a termos presente o valor do saldo no dashboard, necessitamos de criar um sensor para o efeito. Utilizaremos o “Sensor Template”, bastando adicionar as linhas abaixo no nosso configuration.yaml
# Santander Balance Value
sensor:
- platform: template
sensors:
santander_balance:
value_template: "00,00"
O Switch é opcional, mas eu tinha em mente a possibilidade de querer despoletar manualmente ou até por uma automatização a atualização. Para o efeito recorremos ao “Switch Template” e adicionamos as linhas abaixo ao nosso configuration.yaml
switch:
# Santander Balance Update Switch
- platform: template
switches:
updatebalance:
turn_on:
service: switch.turn_on
target:
entity_id: switch.updatebalance_on
turn_off:
service: switch.turn_off
target:
entity_id: switch.updatebalance_off
Como em qualquer outra alteração ao configuration.yaml, será necessário reiniciar o Home Assistant para que os sensores sejam carregados
2º - Criar cartão de visualização
No meu caso, criei um Dashboard específico para visualizar os dados presentes e adicionei um “Entities Card”
type: entities
entities:
- entity: switch.updatebalance
name: Atualizar
secondary_info: last-changed
- entity: sensor.santander_balance
icon: mdi:currency-eur
name: Saldo
secondary_info: last-updated
state_color: true
show_header_toggle: false
3º - Configurar dependências Add-On AppDaemon
Após o download e instalação do Add-On AppDaemon, terão de ser configuradas algumas necessidades e dependências, nomeadamente o Chromium e o Selenium
system_packages:
- chromium-chromedriver
- chromium
python_packages:
- selenium
init_commands: []
Aquando do arranque do Add-On, estas dependências irão ser instaladas e poderão ser importadas nos scripts.
4º - Configurar App no AppDaemon
Através do Visual Studio Code/File Editor, criar no diretório “/config/appdaemon/apps/” um ficheiro com o código da nossa App, nomeadamente “GetSantanderBalance.py”.
Abrir o ficheiro apps.yaml e adicionar a configuração da nossa app
SantanderBalance:
module: GetSantanderBalance
class: GetSantanderBalance
Gravar o ficheiro e abrir o GetSantanderBalance.py
Copiar o código abaixo para o ficheiro e alterar os valores identificados na função “GetBalance”
Na função "GetBalance" existem dois campos de texto a preencher:
"************** CARD NUMBER ***********" - Substituir pelo número de cartão sem espaços, exemplo "000000000"
"************** CARD CODE ***********" - Substituir pelo código do cartão, exemplo "123123"
Código:
Notas:
- Sim, tem melhorias a fazer , foi desenvolvido por mim já a pensar em ir para a cama
- O código espera que tenham o switch de atualização configurado, mas poderão adaptar o código para simplesmente atualizar o valor quando quiserem
- Para a navegação no código HTML foi utilizado o XPATH bem como funções do Selenium
- Devido aos iFrames presentes no site do Santander, é necessário saltar entre os mesmos para o contexto do seletor
- Está previsto no código aceitar todos os cookies presentes no site
Melhorias a fazer quando possível:
- Retirar timeout, isto torna o script mais lento, o mais correto seria a utilização de um wait for element com o devido seletor
- Colocar o número de cartão numa variável mais acessível e de fácil configuração (usar ficheiro !secrets)
- Colocar o código do cartão numa variável mais acessível e de fácil configuração(usar ficheiro !secrets)
- Para testes, recorri ao run_minutely para executar a cada minuto, mas tal não é necessário podendo este tempo ser alargado a 30min ou mesmo 1h, irei ver no futuro com que recorrência necessito
Pequeno resumo:
- Função initialize: Responsável por iniciar a aplicação e gerar as threads por minuto
- Função CallListenProcess: É invocada pela inicialização da thread e é responsável por esperar que o switch de atualização mude para o estado ON, tendo um timeout de 59 segundos. Caso o switch não seja ativado, a thread é cancelada.
- Função StartProcess: Arranca quando o swith é ligado e atribui os novos valores ao sensor e ao switch, no caso do switch irá desligá-lo
- Função GetBalance: É responsável por toda a extração e movimentação no site Santander. Retorna o valor do saldo existente. No caso da existência de um erro, o saldo ficará a “0,00”
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import appdaemon.plugins.hass.hassapi as hass
import time
import datetime
class GetSantanderBalance(hass.Hass):
def initialize(self):
self.log("Starting Santander App")
runtime = datetime.time(0 ,0 ,0)
self.run_minutely(self.CallListenProcess,runtime)
def CallListenProcess(self, kwargs):
self.log("--------- SANTANDER BALANCE ---------")
# listen for state ON for update balance
self.listen_state(self.StartProcess,"switch.updatebalance", new = "on", timeout = 59)
#Callback Function to Start the process
def StartProcess(self, entity, attribute, old, new, kwargs):
self.log("-- SWITCH ON --")
balance = self.GetBalance()
self.log("Balance: " + balance)
try:
self.set_state('sensor.santander_balance', state = balance)
except Exception as e:
self.log("Set Sensor State Error: " + str(e))
try:
self.set_state("switch.updatebalance", state = old)
except Exception as e:
self.log("Set Swith State Error: " + str(e))
def GetBalance(self):
timeout = 4
balance = "0,00"
try:
self.log("Start GetBalance")
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--disable-gpu')
chrome_options.add_argument('--disable-dev-shm-usage')
browser = webdriver.Chrome('chromedriver', options=chrome_options)
try:
self.log("Start Request")
browser.get('https://www.particulares.santander.pt/pagina/indice/0,,840_1_2,00.html')
time.sleep(timeout)
try:
browser.find_element_by_id('onetrust-accept-btn-handler').click()
except Exception as e:
self.log("No cookies")
self.log("Login form")
browser.switch_to.frame('ws')
browser.find_element_by_xpath('//input[@autocomplete="userNameInputId"]').send_keys("************** CARD NUMBER ***********")
browser.find_element_by_xpath('//input[@autocomplete="passwordInputId"]').send_keys("************** CARD CODE ***********")
browser.find_element_by_id('submit_button').click()
time.sleep(timeout)
self.log("Success Login")
browser.switch_to.frame('ws')
time.sleep(timeout)
self.log("Extracted Balance")
balance = str(browser.find_element_by_xpath('//p[@class="balance-value text-green"]').text)
browser.switch_to.default_content()
self.log("Start Logout")
browser.find_element_by_id('quick-logout').click()
browser.close()
self.log("Closed")
except Exception as e:
self.log("Request Error: " + str(e))
browser.close()
except Exception as e:
self.log("GetBalance Error: " + str(e))
return balance
Após guardar o ficheiro, o AppDaemon irá reiniciar o módulo e será possível visualizar nos logs do Add-On se tudo está conforme o esperado.
Próximo objetivo:
- Executar as melhorias identificadas
- Extrair os movimentos apresentados no banco
- Apresentar os movimentos num cartão para posterior visualização no Dashboard
Notas finais:
- Este é um projeto pessoal que disponibilizo a toda a gente na comunidade, poderão editar/alterar o código
- Foi a minha primeira abordagem ao AppDaemon, pelo que poderão existir melhorias nesse aspeto também
- O código está dependente dos seletores do site, pelo que, se o mesmo alterar, o script poderá deixar de funcionar
Estarei ao dispor para qualquer esclarecimento
Abraço