AutomationFlowsWeb Scraping › Daily Omie Analysis for Best Fixed Auto Rates

Daily Omie Analysis for Best Fixed Auto Rates

Original n8n title: Análise Omie - Melhor Fixo Auto

Análise OMIE - Melhor Fixo Auto. Uses httpRequest. Scheduled trigger; 7 nodes.

Cron / scheduled trigger★★★★☆ complexity7 nodesHTTP Request
Web Scraping Trigger: Cron / scheduled Nodes: 7 Complexity: ★★★★☆ Added:

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "An\u00e1lise OMIE - Melhor Fixo Auto",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 6
            }
          ]
        }
      },
      "id": "node-schedule",
      "name": "Diariamente",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Gera a lista de URLs OMIE para os \u00faltimos 30 dias\nconst dias = 30;\nconst hoje = new Date();\nconst items = [];\n\nfor (let i = dias; i >= 0; i--) {\n  const d = new Date(hoje);\n  d.setDate(d.getDate() - i);\n  const ano = d.getFullYear();\n  const mes = String(d.getMonth() + 1).padStart(2, '0');\n  const dia = String(d.getDate()).padStart(2, '0');\n  items.push({\n    json: {\n      url: `https://www.omie.es/en/file-download?parents=marginalpdbcpt&filename=marginalpdbcpt_${ano}${mes}${dia}.1`\n    }\n  });\n}\n\nreturn items;"
      },
      "id": "node-gerar-urls",
      "name": "Gerar URLs OMIE",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        220,
        300
      ]
    },
    {
      "parameters": {
        "url": "={{ $json.url }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "User-Agent",
              "value": "Mozilla/5.0"
            },
            {
              "name": "Referer",
              "value": "https://www.omie.es/"
            }
          ]
        },
        "options": {
          "allowUnauthorizedCerts": true,
          "response": {
            "response": {
              "responseFormat": "text"
            }
          },
          "timeout": 15000
        }
      },
      "id": "node-http-omie",
      "name": "HTTP Request OMIE",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        440,
        300
      ],
      "continueOnFail": true
    },
    {
      "parameters": {
        "jsCode": "// Agrega os 31 ficheiros OMIE num \u00fanico item com a m\u00e9dia\nconst todosPrecos = [];\n\nfor (const item of $input.all()) {\n  const texto = item.json.data;\n  if (!texto || typeof texto !== 'string') continue;\n  for (const linha of texto.split('\\n')) {\n    const partes = linha.trim().split(';');\n    // Formato OMIE: ANO;MES;DIA;HORA;PRECO_PT;PRECO_ES\n    if (partes.length >= 5 && /^\\d{4}$/.test(partes[0].trim())) {\n      const preco = parseFloat(partes[4].replace(',', '.'));\n      if (!isNaN(preco)) todosPrecos.push(preco);\n    }\n  }\n}\n\nif (todosPrecos.length === 0) {\n  return [{ json: { erro: 'Sem dados OMIE dispon\u00edveis' } }];\n}\n\nconst media = todosPrecos.reduce((a, b) => a + b, 0) / todosPrecos.length;\n\nreturn [{\n  json: {\n    media_omie_mwh: media,\n    num_precos: todosPrecos.length\n  }\n}];"
      },
      "id": "node-agregar-omie",
      "name": "Agregar OMIE",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        660,
        300
      ]
    },
    {
      "parameters": {
        "url": "https://huggingface.co/spaces/tiagofelicia/simulador-tarifarios-eletricidade/resolve/main/data/csv/Tarifarios_fixos.csv",
        "options": {
          "response": {
            "response": {
              "responseFormat": "text"
            }
          },
          "timeout": 30000
        }
      },
      "id": "node-http-csv",
      "name": "HTTP Request CSV Tarif\u00e1rios",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        880,
        300
      ],
      "continueOnFail": false
    },
    {
      "parameters": {
        "jsCode": "// \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n// \u2551                      \u2699\ufe0f  CONFIGURA\u00c7\u00c3O                            \u2551\n// \u2551              S\u00f3 precisas de editar esta sec\u00e7\u00e3o                   \u2551\n// \u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n\nconst CONFIG = {\n\n  // \u2500\u2500 Consumo e contrato \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  consumo_kwh:  700,    // kWh/m\u00eas\n  dias:          30,    // dias do per\u00edodo de fatura\u00e7\u00e3o\n  potencia_kva:  6.90,  // kVA contratados (filtra tarif\u00e1rios do CSV)\n  ciclo: \"Simples\",     // \"Simples\" | \"Bi-hor\u00e1rio\" | \"Tri-hor\u00e1rio\"\n\n  // \u2500\u2500 Tarif\u00e1rio FIXO \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // null  \u2192 escolhe automaticamente o mais barato do CSV do Tiago Fel\u00edcia\n  // {...} \u2192 usa estes valores directamente (ignora o CSV)\n  //\n  // ATEN\u00c7\u00c3O: os valores do CSV j\u00e1 incluem TAR (tar_incluida = true na maioria).\n  // Se colocares valores manuais, usa os valores FINAIS da tua fatura\n  // (ou seja, com TAR e TSE j\u00e1 inclu\u00eddas), e coloca tar_ja_incluida: true.\n  //\n  // Exemplo manual:\n  // fixo_manual: {\n  //   nome:           \"G9 | Vantagem+\",\n  //   energia_kwh:    0.1348,  // valor bruto do CSV (inclui TAR + TSE)\n  //   potencia_dia:   0.4498,  // valor bruto do CSV (inclui TAR)\n  //   tar_en_incluida:  true,\n  //   tar_pot_incluida: true,\n  //   tse_incluida:     true,\n  // },\n  fixo_manual: null,\n\n  // \u2500\u2500 Filtros para escolha autom\u00e1tica do melhor fixo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // (irrelevante se fixo_manual estiver preenchido)\n  // Usa string vazia \"\" para ignorar esse filtro.\n  filtros: {\n    segmento:  \"Residencial\",     // \"Residencial\" | \"Empresarial\" | \"\"\n    faturacao: \"eletr\u00f3nica\",      // palavra parcial (ex: \"eletr\u00f3nica\", \"papel\")\n    pagamento: \"D\u00e9bito\",          // palavra parcial (ex: \"D\u00e9bito\", \"Multibanco\")\n  },\n\n  // \u2500\u2500 Tarif\u00e1rio INDEXADO (OMIE) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Valores espec\u00edficos do contrato indexado.\n  // A TAR e TSE s\u00e3o SEMPRE adicionadas por cima no indexado (n\u00e3o est\u00e3o inclu\u00eddas).\n  indexado: {\n    nome:       \"G9 | Smart Index\",\n    // Margem do comercializador em \u20ac/kWh (G9_CGS + G9_AC do contrato)\n    margem_kwh: 0.0150,\n    // Fator de perdas de rede (G9_FA \u2014 tipicamente entre 1.0 e 1.20)\n    // Verifica no teu contrato. G9 usa ~1.16 no per\u00edodo de inverno.\n    // Se n\u00e3o souberes, usa 1.0 para estimativa conservadora.\n    fator_perdas: 1.0,\n  },\n\n  // \u2500\u2500 Tarifas reguladas ERSE 2026 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n  // Atualizar anualmente em Janeiro.\n  // Fonte: https://www.tiagofelicia.pt/tarifas-acesso-redes.html\n  tar: {\n    energia_kwh:  0.0607,   // \u20ac/kWh \u2014 tarifa simples BTN\n    potencia_dia: 0.3436,   // \u20ac/dia \u2014 para 6.9 kVA (alterar se mudares de pot\u00eancia)\n  },\n\n};\n\n// \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557\n// \u2551          N\u00c3O \u00c9 NECESS\u00c1RIO EDITAR ABAIXO DAQUI                   \u2551\n// \u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d\n\n// Constantes reguladas ERSE 2026 (n\u00e3o mudam a meio do ano)\nconst TSE = 0.0026;  // \u20ac/kWh \u2014 financiamento Tarifa Social Electricidade\n\nconst r4 = (v) => Math.round(v * 10000) / 10000;\nconst r2 = (v) => Math.round(v * 100) / 100;\n\n// \u2500\u2500 Normaliza\u00e7\u00e3o de texto para compara\u00e7\u00f5es \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst norm = (s) => (s || '')\n  .toLowerCase()\n  .normalize('NFD')\n  .replace(/[\\u0300-\\u036f]/g, '')\n  .trim();\n\n// \u2500\u2500 Parser CSV robusto (lida com campos entre aspas) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction parseCSVLine(linha) {\n  const cols = [];\n  let cur = '';\n  let inQ = false;\n  for (const ch of linha) {\n    if (ch === '\"') { inQ = !inQ; }\n    else if (ch === ',' && !inQ) { cols.push(cur.trim()); cur = ''; }\n    else { cur += ch; }\n  }\n  cols.push(cur.trim());\n  return cols;\n}\n\nconst parseBool = (v) => {\n  const s = (v || '').toLowerCase().trim();\n  return s === 'true' || s === 'sim' || s === '1';\n};\nconst parseNum = (v) => parseFloat((v || '0').replace(',', '.')) || 0;\n\n// \u2500\u2500 Leitura dos dados dos n\u00f3s anteriores \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst omieData = $('Agregar OMIE').first().json;\nif (omieData.erro) {\n  return [{ json: { erro: omieData.erro } }];\n}\n\nconst mediaOmie_MWh = omieData.media_omie_mwh; // \u20ac/MWh\nconst mediaOmie_kWh = mediaOmie_MWh / 1000;    // \u20ac/kWh\nconst csvTexto = $input.first().json.data;\n\n// \u2500\u2500 Parse do CSV e c\u00e1lculo do melhor fixo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// L\u00f3gica id\u00eantica ao simulador do Tiago Fel\u00edcia (calculos.js):\n//\n// 1. L\u00ea preco_energia_simples e preco_potencia_dia do CSV (valores brutos)\n// 2. Se tar_incluida_energia=true \u2192 energia_comercial = bruto - TAR_en\n//    Se tar_incluida_energia=false \u2192 energia_comercial = bruto\n// 3. energia_final = energia_comercial + TAR_en + (tse_incluida ? 0 : TSE)\n// 4. Mesmo processo para pot\u00eancia\n// 5. Custo = energia_final * consumo + potencia_final * dias\n//\nfunction obterTarifarios(csvTexto) {\n  const linhas = csvTexto.split('\\n').map(l => l.trim()).filter(Boolean);\n  if (linhas.length < 2) throw new Error('CSV vazio ou inv\u00e1lido');\n\n  // Mapear cabe\u00e7alho \u2192 \u00edndice de coluna\n  const cab = parseCSVLine(linhas[0]).map(c => c.replace(/\"/g, '').trim().toLowerCase());\n  const col = (nome) => cab.findIndex(c => c.includes(nome.toLowerCase()));\n\n  // \u00cdndices das colunas relevantes (nomes do CSV do Tiago Fel\u00edcia)\n  const C = {\n    comercializador: col('comercializador'),\n    nome:            col('nome'),\n    tipo:            col('tipo'),\n    ciclo:           col('opcao_horaria'),\n    potKva:          col('potencia_kva'),\n    energia:         col('preco_energia_simples'),\n    potDia:          col('preco_potencia_dia'),\n    tarEnIncl:       col('tar_incluida_energia'),\n    tarPotIncl:      col('tar_incluida_potencia'),\n    tseIncl:         col('financiamento_tse'),\n    segmento:        col('segmento'),\n    faturacao:       col('faturacao'),\n    pagamento:       col('pagamento'),\n    descFatura:      col('desconto_fatura'),\n  };\n\n  const cicloChave = norm(CONFIG.ciclo).split('-')[0].split(' ')[0]; // \"simples\", \"bi\", \"tri\"\n\n  const fixos = [];\n\n  for (let i = 1; i < linhas.length; i++) {\n    const cols = parseCSVLine(linhas[i]);\n    if (cols.length < 10) continue;\n\n    const tipo    = (cols[C.tipo]    || '').replace(/\"/g, '').trim();\n    const ciclo   = norm(cols[C.ciclo]   || '');\n    const potKva  = parseNum(cols[C.potKva]);\n    const segmento  = norm(cols[C.segmento]  || '');\n    const faturacao = norm(cols[C.faturacao] || '');\n    const pagamento = norm(cols[C.pagamento] || '');\n\n    // \u2500\u2500 Filtros obrigat\u00f3rios \u2500\u2500\n    if (norm(tipo) !== 'fixo') continue;\n    if (Math.abs(potKva - CONFIG.potencia_kva) > 0.1) continue;\n    if (!ciclo.includes(cicloChave)) continue;\n\n    // \u2500\u2500 Filtros opcionais \u2500\u2500\n    const f = CONFIG.filtros;\n    if (f.segmento === 'Residencial' && !segmento.includes('domestico')) continue;\n    if (f.segmento === 'Empresarial' && !segmento.includes('nao domestico') && !segmento.includes('nao-domestico')) continue;\n    if (f.faturacao && !faturacao.includes(norm(f.faturacao))) continue;\n    if (f.pagamento && !pagamento.includes(norm(f.pagamento))) continue;\n\n    // \u2500\u2500 Leitura dos pre\u00e7os brutos \u2500\u2500\n    const energiaBruta = parseNum(cols[C.energia]);\n    const potDiaBruta  = parseNum(cols[C.potDia]);\n    if (energiaBruta <= 0 || potDiaBruta <= 0) continue;\n\n    const tarEnIncl  = parseBool(cols[C.tarEnIncl]);\n    const tarPotIncl = parseBool(cols[C.tarPotIncl]);\n    const tseIncl    = parseBool(cols[C.tseIncl]);\n\n    // \u2500\u2500 Normaliza\u00e7\u00e3o exacta (igual ao simulador) \u2500\u2500\n    // Extrai componente comercial (sem TAR)\n    const energiaComercial = tarEnIncl\n      ? energiaBruta - CONFIG.tar.energia_kwh\n      : energiaBruta;\n\n    const potComercial = tarPotIncl\n      ? potDiaBruta - CONFIG.tar.potencia_dia\n      : potDiaBruta;\n\n    // Re-adiciona TAR regulada + TSE se n\u00e3o inclu\u00edda\n    const energiaFinal = energiaComercial + CONFIG.tar.energia_kwh + (tseIncl ? 0 : TSE);\n    const potFinal     = potComercial     + CONFIG.tar.potencia_dia;\n\n    // Desconto de fatura (se existir)\n    const descFatura = parseNum(cols[C.descFatura] || '0');\n    const descTotal  = descFatura * (CONFIG.dias / 30); // proporcional\n\n    const custoEnergia = energiaFinal * CONFIG.consumo_kwh;\n    const custoPot     = potFinal     * CONFIG.dias;\n    const custoTotal   = custoEnergia + custoPot - descTotal;\n\n    fixos.push({\n      comercializador:  (cols[C.comercializador] || '').replace(/\"/g, '').trim(),\n      nome:             (cols[C.nome]            || '').replace(/\"/g, '').trim(),\n      energia_kwh:      r4(energiaFinal),\n      potencia_dia:     r4(potFinal),\n      energia_comercial_kwh: r4(energiaComercial),\n      pot_comercial_dia:     r4(potComercial),\n      desconto_mes:     descFatura,\n      custo_estimado:   r2(custoTotal),\n      tar_en_incl:      tarEnIncl,\n      tar_pot_incl:     tarPotIncl,\n      tse_incl:         tseIncl,\n    });\n  }\n\n  if (fixos.length === 0) {\n    throw new Error(\n      `Nenhum tarif\u00e1rio fixo encontrado para ${CONFIG.potencia_kva} kVA / ` +\n      `${CONFIG.ciclo} com filtros: ${JSON.stringify(CONFIG.filtros)}`\n    );\n  }\n\n  fixos.sort((a, b) => a.custo_estimado - b.custo_estimado);\n  return fixos;\n}\n\n// \u2500\u2500 Obter melhor fixo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nlet melhorFixo;\nlet fonte;\n\nif (CONFIG.fixo_manual) {\n  // Manual: normaliza da mesma forma\n  const m = CONFIG.fixo_manual;\n  const tarEnIncl  = m.tar_en_incluida !== false;\n  const tarPotIncl = m.tar_pot_incluida !== false;\n  const tseIncl    = m.tse_incluida !== false;\n\n  const energiaComercial = tarEnIncl ? m.energia_kwh - CONFIG.tar.energia_kwh : m.energia_kwh;\n  const potComercial     = tarPotIncl ? m.potencia_dia - CONFIG.tar.potencia_dia : m.potencia_dia;\n  const energiaFinal     = energiaComercial + CONFIG.tar.energia_kwh + (tseIncl ? 0 : TSE);\n  const potFinal         = potComercial + CONFIG.tar.potencia_dia;\n\n  melhorFixo = {\n    nome:          m.nome,\n    comercializador: '',\n    energia_kwh:   r4(energiaFinal),\n    potencia_dia:  r4(potFinal),\n    custo_estimado: r2(energiaFinal * CONFIG.consumo_kwh + potFinal * CONFIG.dias),\n  };\n  fonte = 'manual';\n} else {\n  let todos;\n  try {\n    todos = obterTarifarios(csvTexto);\n  } catch (e) {\n    return [{ json: { erro: e.message } }];\n  }\n  melhorFixo = todos[0];\n  fonte = 'automatico (CSV Tiago Fel\u00edcia)';\n}\n\n// \u2500\u2500 C\u00e1lculo do custo indexado \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// L\u00f3gica do simulador para G9 Smart Index (e equivalentes):\n//   pre\u00e7o_kwh = OMIE_\u20ac/kWh * fator_perdas + margem + TAR_energia + TSE\n//   custo     = pre\u00e7o_kwh * consumo + TAR_potencia * dias\n//\n// A TAR e TSE s\u00e3o SEMPRE adicionadas no indexado (tar_e_incl = false no CSV)\n// O fator_perdas compensa as perdas de rede (varia ~1.10-1.20 conforme contrato)\n//\nconst precoKwhIndexado = (\n  mediaOmie_kWh * CONFIG.indexado.fator_perdas\n  + CONFIG.indexado.margem_kwh\n  + CONFIG.tar.energia_kwh\n  + TSE\n);\nconst custoIndexado = (\n  CONFIG.consumo_kwh * precoKwhIndexado\n  + CONFIG.dias * CONFIG.tar.potencia_dia\n);\n\n// \u2500\u2500 Break-even (a partir de que OMIE o fixo \u00e9 mais barato) \u2500\u2500\u2500\u2500\u2500\n//\n// Resolve: custo_fixo = custo_indexado\n// melhorFixo.custo = consumo * (OMIE_kWh * perdas + margem + TAR + TSE) + dias * TAR_pot\n// OMIE_kWh_breakeven = (custo_fixo/consumo - margem - TAR - TSE) / perdas\n//\nconst custoFixo = melhorFixo.custo_estimado;\nconst breakeven_kWh = (\n  (custoFixo - CONFIG.dias * CONFIG.tar.potencia_dia) / CONFIG.consumo_kwh\n  - CONFIG.indexado.margem_kwh\n  - CONFIG.tar.energia_kwh\n  - TSE\n) / CONFIG.indexado.fator_perdas;\nconst breakeven_MWh = r2(breakeven_kWh * 1000);\n\nconst poupanca   = r2(Math.abs(custoFixo - custoIndexado));\nconst fixoMaisBarato = custoFixo <= r2(custoIndexado);\nconst recomendacao   = fixoMaisBarato ? melhorFixo.nome : CONFIG.indexado.nome;\n\n// \u2500\u2500 Resumo formatado \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst data = new Date().toLocaleDateString('pt-PT');\nconst maisBaratoLabel = fixoMaisBarato\n  ? `\ud83c\udfc6 FIXO mais barato \u2014 poupa ${poupanca}\u20ac`\n  : `\ud83c\udfc6 INDEXADO mais barato \u2014 poupa ${poupanca}\u20ac`;\n\nlet resumo = `\ud83d\udcca *AN\u00c1LISE OMIE* (${data})\\n`;\nresumo += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n\\n`;\nresumo += `\u26a1 *MERCADO OMIE (\u00faltimos 30 dias):*\\n`;\nresumo += `\u2022 M\u00e9dia: *${r2(mediaOmie_MWh)} \u20ac/MWh*\\n`;\nresumo += `\u2022 Break-even: ${breakeven_MWh} \u20ac/MWh\\n\\n`;\nresumo += `\ud83d\udd12 *MELHOR FIXO (${fonte}):*\\n`;\nresumo += `\u2022 ${melhorFixo.nome}\\n`;\nresumo += `\u2022 Energia: ${melhorFixo.energia_kwh} \u20ac/kWh | Pot\u00eancia: ${melhorFixo.potencia_dia} \u20ac/dia\\n`;\nresumo += `\u2022 Custo estimado: *${custoFixo}\u20ac*\\n\\n`;\nresumo += `\ud83d\udcc8 *INDEXADO (${CONFIG.indexado.nome}):*\\n`;\nresumo += `\u2022 Pre\u00e7o kWh: ${r4(precoKwhIndexado)} \u20ac/kWh\\n`;\nresumo += `\u2022 Custo estimado: *${r2(custoIndexado)}\u20ac*\\n\\n`;\nresumo += `\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\\n`;\nresumo += `\ud83d\udca1 *${maisBaratoLabel}*\\n`;\nresumo += `   Recomenda\u00e7\u00e3o: *${recomendacao}*`;\n\nreturn [{\n  json: {\n    data_analise:              data,\n    media_omie_eur_mwh:        r2(mediaOmie_MWh),\n    breakeven_eur_mwh:         breakeven_MWh,\n    omie_acima_breakeven:      mediaOmie_MWh > breakeven_MWh,\n    // Melhor fixo\n    melhor_fixo_nome:          melhorFixo.nome,\n    melhor_fixo_comercializador: melhorFixo.comercializador || '',\n    melhor_fixo_fonte:         fonte,\n    melhor_fixo_energia_kwh:   melhorFixo.energia_kwh,\n    melhor_fixo_potencia_dia:  melhorFixo.potencia_dia,\n    custo_melhor_fixo_eur:     custoFixo,\n    // Indexado\n    indexado_nome:             CONFIG.indexado.nome,\n    indexado_preco_kwh:        r4(precoKwhIndexado),\n    custo_indexado_eur:        r2(custoIndexado),\n    // Compara\u00e7\u00e3o\n    poupanca_eur:              poupanca,\n    recomendacao:              recomendacao,\n    fixo_mais_barato:          fixoMaisBarato,\n    num_precos_omie:           omieData.num_precos,\n    resumo,\n  }\n}];"
      },
      "id": "node-calculos",
      "name": "C\u00e1lculos",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://example.com/webhook",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "content",
              "value": "={{ $json.resumo }}"
            }
          ]
        },
        "options": {}
      },
      "id": "node-notificacao",
      "name": "Notifica\u00e7\u00e3o",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1320,
        300
      ]
    }
  ],
  "connections": {
    "Diariamente": {
      "main": [
        [
          {
            "node": "Gerar URLs OMIE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gerar URLs OMIE": {
      "main": [
        [
          {
            "node": "HTTP Request OMIE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request OMIE": {
      "main": [
        [
          {
            "node": "Agregar OMIE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Agregar OMIE": {
      "main": [
        [
          {
            "node": "HTTP Request CSV Tarif\u00e1rios",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request CSV Tarif\u00e1rios": {
      "main": [
        [
          {
            "node": "C\u00e1lculos",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "C\u00e1lculos": {
      "main": [
        [
          {
            "node": "Notifica\u00e7\u00e3o",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": []
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Análise OMIE - Melhor Fixo Auto. Uses httpRequest. Scheduled trigger; 7 nodes.

Source: https://github.com/pmpbaptista/indexado-vs-fixo/blob/a8bfa7e2470bc81a5c1332deab7fe4ae62b81970/n8n/analise_omie.json — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

As n8n instances scale, teams often lose track of sub-workflows—who uses them, where they are referenced, and whether they can be safely updated. This leads to inefficiencies like unnecessary copies o

HTTP Request, n8n, N8N Trigger +1
Web Scraping

This workflow is an improvement of this workflow by Greg Brzezinka.

HTTP Request, Email Send, XML +1
Web Scraping

N8N-Workflow-Github-Manager. Uses github, httpRequest, n8n. Scheduled trigger; 38 nodes.

GitHub, HTTP Request, n8n
Web Scraping

This workflow uses KlickTipp community nodes, available for self-hosted n8n instances only.

N8N Nodes Klicktipp, Salesforce, Salesforce Trigger +1
Web Scraping

This workflow acts as an automated engagement bot. It sends a Direct Message (DM) with a link or resource to any follower who replies to your post with a specific target keyword.

HTTP Request