Saldo Cartão Refeição Santander - AppDaemon + Python + Selenium

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.

---------- SANTANDER BALANCE ----------

Capturar

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”.

createfile

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 :sweat_smile:, 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

2 Curtiram

Funcional para o Brasil?

1 Curtiu

Boas
Não sei dizer…
O site em si não deve alterar muito, tens de trocar o link. Mas só testando…
Abraço

Baos noites.
Onde posso estar a falhar?
satander

Boas,

Pode verificar os logs do AppDaemon?
O resultado esperado será este aqui quando o switch fica ON

Abraço

Dá-me este erro.

ERROR 'log' directive deprecated, please convert to new 'logs' syntax

Qual o AppDaemon que está a correr? o 3 ou o 4?
Esse erro acontece quando já correu o 3 e é necessário migrar para o 4

https://github.com/hassio-addons/addon-appdaemon/issues/2

Abraço

Estou a correr o 4.
Agora da-me este erro
2021-11-12 18:55:21.322272 WARNING HASS: Disconnected from Home Assistant, retrying in 5 seconds

Edit: Problema resolvido eram problemas com o https.
Muito bom trabalho amigo Obrigado.

Parece ser mesmo um problema do AppDaemon e não do Script em si… Visto que o AppDaemon nem chega a ser inicializado.

Instalou o AppDaemon de raiz?
Tem algum certificado do estilo Let’s encript?
Talvez tentar desinstalar e instalar…

https://community.home-assistant.io/t/hadashboard-and-appdaemon-error-warning-disconnected-from-home-assistant-retrying-in-5-seconds/43898

Abraço

1 Curtiu

Problema Resolvido Obrigado.

satander

1 Curtiu

Era bom saber como foi resolvido para que outros utilizadores tenham essa informação se necessário…

Mas eu já mencionei foi devido a eu usar https…

1 Curtiu

@Boxexas,

Excelente partilha!

Fez-me ver que estava claramente a complicar na abordagem que estava a seguir para atingir o mesmo resultado. Eu uso o selenium para recolher informação de várias fontes, mas o que fiz foi levantar um container à parte (via Portainer) com python + selenium, usando scripts no AppDaemon para comunicar com ele através de mqtt, integrando depois os resultados no HA.

Muito mais complexo do que colocar os packages directamente no AppDaemon!!
Vou certamente migrar o que tenho para a tua abordagem, bem mais direta.

Obrigado!

1 Curtiu

Copyright © 2017-2021. Todos os direitos reservados
CPHA.pt - info@cpha.pt


FAQ | Termos de Serviço/Regras | Política de Privacidade