> For the complete documentation index, see [llms.txt](https://portal-integracao.intelbras.com.br/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://portal-integracao.intelbras.com.br/ct-5000-4pb/modo-online/modo-online-1.0.md).

# Modo Online 1.0

<details>

<summary>Lista de Dispositivos com Suporte a Função</summary>

| Dispositivos   | Firmware             |
| -------------- | -------------------- |
| SS 5530        | 220708 ou superior   |
| SS 5530 LITE   | 230307 ou superior   |
| SS 3532 MF W   | 20231018 ou superior |
| SS 3542 MF W   | 20231018 ou superior |
| SS 5531 MF W   | 20231018 ou superior |
| SS 5541 MF W   | 20231018 ou superior |
| SS 5532 MF W   | 20231018 ou superior |
| SS 5542 MF W   | 20231018 ou superior |
| SS 3540 MF BIO | 231129 ou superior   |
| SS 3540 MF     | 231129 ou superior   |
| SS 3530 MF     | 20240601 ou superior |
| SS 3530 MF W   | 20240601 ou superior |
| SS 3430 Bio    | 20240315 ou superior |
| SS 3430 MF Bio | 20240315 ou superior |
| SS 1530/1540   | 20240201 ou superior |

</details>

{% hint style="warning" %}
**Importante:** A rota de solicitação de acesso suporta o protocolo HTTPS, porem o KeepAlive não suporta o protocolo HTTPS.
{% endhint %}

{% hint style="warning" %}
**Importante:** Com exceção da linha SS 3430 e SS 3530, para o funcionamento correto do Modo Online é necessário que o dispositivo esteja configurado com o Feedback no modo "Personalizado".
{% endhint %}

O Modo Online permite que o dispositivo sempre “pergunte” ao servidor se pode liberar ou negar acesso ao usuário que está tentando acessar determinado ponto de acesso e caso a conexão com o servidor for perdida, operar com lista de usuários liberados (Lista Branca).

O Modo Online possui dois estados: Online e Offline. Cada um terá um fluxo de decisão específico.

Através da integração do modo online algumas funções são possiveis de implementação, tais como:

* Antipassback global
* Intertravemento global
* Acesso manual
* Antipassback apenas com supervisão
* Bloqueio de acesso
* Chamada antecipada
* Controle de refeição
* Controle de trânsito
* Escolta
* Escolta com dupla passagem
* Revista aleatória

#### Keep Alive

O dispositivo tentará uma requisição GET no “Keep alive path”, se a resposta retornada pelo servidor for Código 200 OK, então o dispositivo estará no “Estado Online”, caso a resposta do servidor for algo diferente ou atingir o tempo limite, então o dispositivo entrará no “Estado Offline”. O dispositivo repetirá esse processo com base no “Keep Alive Time”.

![KeepAlive](https://i.imgur.com/Gqja4KJ.png)

Diagrama de operação do Keep Alive:

![KeepAlive2](https://i.imgur.com/B21ta2A.png)

#### Estado Online

Quando o dispositivo estiver no estado online, a todos que tentarem acessar o dispositivo, ele postará o evento e aguardará um response do servidor (resposta de autorização) contendo três campos: `id`, `auth` e `message`.

Para credenciais biométricas, o dispositivo verificará se o usuário está cadastrado antes de enviar para o servidor. Se não for, acesso negado.

Para senhas e cartões, mesmo que o usuário não esteja cadastrado, a solicitação de autorização será enviada ao servidor. O servidor responderá à solicitação com um JSON conforme descrito anteriormente. No caso de um `TIMEOUT`, o dispositivo decidirá com base nos usuários inscritos e o dispositivo mudará para o estado offline. O `TIMEOUT` é um parâmetro na interface web.

O fluxo abaixo ilustra a explicação.

![OnlineState](https://i.imgur.com/3g63rwi.png)

As requisições POST realizadas pelo dispositivo ao servidor deverão ser respondidas de acordo com o seguinte JSON, contendo as informações:

\*\*Importante:\*\* Certifique-se de responder com os cabeçalhos de Content-Type e Content-Lenght corretamente.

```json
{ 
    "message": "Seja Bem vindo!", 
    "code": "200", 
    "auth": "true"
}
```

| param        | type   | description                                                       |
| ------------ | ------ | ----------------------------------------------------------------- |
| `message` \* | String | Mensagem que será exibida no display                              |
| `code` \*    | Number | Código sempre é 200                                               |
| `auth` \*    | String | String true/false corresponde se a porta irá ser acionada ou não. |

#### Estado Offline

Quando o dispositivo estiver no estado offline, para todas as tentativas de acesso ao dispositivo, ele decidirá com base nos usuários cadastrados. No estado offline, todos os usuários têm privilégios VIP, 24 horas por dia, 7 dias por semana.

![OfflineState](https://i.imgur.com/oEHus7L.png)

### Servidor de Exemplo

O servidor de exemplo realiza a configuração de duas rotas, sendo a /notifications para receber os eventos e aprovar/negar os acessos e /keepalive para a verificação pelo dispositivo do estado do servidor.

#### [DOWNLOAD SERVIDOR DE TESTES (WINDOWS)](https://intelbras-caco-api.intelbras.com.br/ServidorDeTeste.exe)

{% tabs %}
{% tab title="Python" %}

```python
# -*- coding:utf-8 -*-
from flask import Flask,request, jsonify
import json
import ast
import sys, os
import time
 
app = Flask(__name__)
 
@app.route('/notification', methods = ['POST'])
def event_receiver():
    if request.method == 'POST':
        
        
        res = request.data
        data_list = res.split(b"--myboundary\r\n")
        
        if data_list:
            for a_info in data_list:
                if b"Content-Type" in a_info:
                    lines = a_info.split(b"\r\n")
                    a_type = lines[0].split(b": ")[1]
 
                    if a_type == b"image/jpeg":
                        image_data = b"\r\n".join(lines[3:-3])
                    else:
                        text_data = b"\r\n".join(lines[3:-1])
        
 
        evento_str = text_data.decode("utf-8")
        evento_dict = ast.literal_eval(evento_str.replace("--myboundary--", " "))
        json_object = json.dumps(evento_dict, indent = 4) 
        resp_dict = json.loads(json_object)
 
        print(resp_dict)
 
        event_code = resp_dict.get("Events")[0].get('Code')
        print("################## ", event_code, " ##################")
 
        if event_code == "AccessControl":
            event_data = resp_dict.get("Events")[0].get('Data')
            
            card_name = event_data.get('CardName')
            card_no = event_data.get('CardNo')
            card_type = event_data.get('CardType')
            door = event_data.get('Door')
            error_code = event_data.get('ErrorCode')
            method = event_data.get('Method')
            reader_id = event_data.get('ReaderID')
            event_status = event_data.get('Status')
            event_type = event_data.get('Type')
            event_entry = event_data.get('Entry')
            event_utc = event_data.get('UTC')
            user_id = event_data.get('UserID')
            user_type = event_data.get('UserType')
            pwd = event_data.get('DynPWD')
            
            
            print("UserID: ",  user_id)
            print("UserType", user_type)
            print("CardName: ", card_name)
            print("CardNo: ", card_no)
            print("CardType: ", card_type)
            print("Password: ", pwd)
            print("Door: ", door)
            print("ErrorCode: ", error_code)
            print("Method: ", method)
            print("ReaderID: ", reader_id)
            print("Status: ", event_status)
            print("Type: ", event_type)
            print("Entry: ", event_entry)
            print("UTC: ",  event_utc)
            print(49 * "#")
 
            # TESTE DE REGRAS
            time.sleep(1)
            if user_id == 6:
                return jsonify({"message": "Pagamento não realizado! Consulte a secretaria!", "code": "200", "auth": "false"})
            elif card_no in ["EC56D271", "09201802"]:
                return jsonify({"message": "Voce está bloqueado", "code": "200", "auth": "false"})
            elif pwd != None:
                if int(pwd) == 22:
                    return jsonify({"message": "Acesso Liberado", "code": "200", "auth": "true"})
 
        elif event_code == "DoorStatus":
            event_data = resp_dict.get("Events")[0].get('Data')
            
            door_status = event_data.get('Status')
            door_utc = event_data.get('UTC')
            
            print("Door Status: ",  door_status)
            print("UTC", door_utc)
            print(49 * "#")
            return jsonify({"message": "", "code": "200", "auth": "false"})
        
        elif event_code == "BreakIn":
            event_data = resp_dict.get("Events")[0].get('Data')
            
            door_name = event_data.get('Name')
            door_utc = event_data.get('UTC')
            
            print("Door Name: ",  door_name)
            print("UTC", door_utc)
            print(49 * "#")
            return jsonify({"message": "", "code": "200", "auth": "false"})
        
        
    return jsonify({"message": "", "code": "200", "auth": "false"})
 
 
    '''
    
    O retorno deverá ser um JSON, contendo as informações:
    
    "message": "", // Mensagem que será exibida no display
    "code": "200", // Código sempre é 200.
    "auth": "", Boolean, corresponde se a porta irá ser acionada ou não. 
    
    '''
 
@app.route('/keepalive', methods = ['GET'])
def keep_alive():
    return "OK"
 
    '''
    
    Deverá ser retornado qualquer request que contenha código 200.
 
    '''
 
# Server Start
if __name__ == '__main__':
    app.run(host='192.168.3.14', debug=True, port=3000)
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
var log = console.log;
 
console.log = function(){
    let ts = Date.now();
    let date_ob = new Date(ts);
    log.apply(console, [date_ob.toLocaleString()].concat(arguments));
};
 
var http = require('http');
var server = http.createServer ( function(request,response){
 
response.writeHead(200,{"Content-Type":"text\plain"});
if(request.method == "GET")
    {
        console.log('GET at ' + request.url)
        var body = ''
        request.on('data', function(data) {
            body += data
            //console.log('Partial body: ' + body)
        })
        request.on('end', function() {
            //console.log('header: ' + request.rawHeaders)
            
            var waitTill = new Date(new Date().getTime() + 4 * 1000);
            while(waitTill > new Date()){}
            console.log('Body: ' + body)
            response.end("OK")
        })
        
    }
else if(request.method == "POST")
    {
        //response.end("received POST request.");
        //console.log("received POST request: "+request.url + " ");
        console.log('POST at ' + request.url)
        var body = ''
        request.on('data', function(data) {
            body += data
            //console.log('Partial body: ' + body)
        })
        request.on('end', function() {
            //console.log('header: ' + request.rawHeaders)
            console.log('Body: ' + body)
            response.writeHead(200, {'Content-Type': 'application/json'})
            response.end(JSON.stringify({"message":"ABBCASDF","code":200,"auth":"true"}))
        })
        
    }
else
    {
        response.end("Undefined request .");
    }
});
 
server.listen(80);
console.log("Server running on port 80");
 
```

{% endtab %}

{% tab title="PHP" %}

```php
<?php
 
// Obter o método HTTP e a rota solicitada
$method = $_SERVER['REQUEST_METHOD'];
$uri = $_SERVER['REQUEST_URI'];
 
// printa o method e uri no console 
error_log("Method: $method, URI: $uri");
 
$data = json_encode([
    'auth' => 'true',
    'code' => '200',
    'message' => 'Notification received successfully'
])
 
// Rota: /notification (POST)
if ($method === 'POST') {
    header('Content-Type: application/json');
    // content length é essencial para o dispositivo.
    header('Content-Length: '. strlen($data));
    echo $data;
    http_response_code(200);
    exit;
}
 
// rota /keepalive (GET)
if ($method === 'GET') {
    // content length é essencial para o dispositivo.
    header('Content-Length: 2');
    header('Content-Type: text/html; charset=UTF-8');
    http_response_code(200);
    echo "OK";
    exit;
}
 
// Resposta padrão para rotas desconhecidas
http_response_code(404);
echo json_encode(['error' => 'Route not found'])
?>
```

{% endtab %}
{% endtabs %}

### Comando para Limpar Eventos em Cache

Caso você possua muitos eventos, tanto no modo servidor de envio de eventos quanto no modo online que não foi feito a confirmação de envio ao servidor e deseja apagar todos os eventos em cache, você pode utilizar o comando abaixo.

#### Requisição - GET

`http://19.168.3.87/cgi-bin/recordUpdater.cgi?action=clear&name=AccessOffline`

| param      | type   | description         |
| ---------- | ------ | ------------------- |
| `clear` \* | String | Comando para Limpar |

#### Exemplo de Retorno - *text/plain*:

```sh
OK
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://portal-integracao.intelbras.com.br/ct-5000-4pb/modo-online/modo-online-1.0.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
