Centrala alarmowa Satel Integra posiada otwarty protokół komunikacyjny z innymi systemami. Oznacza to, że można z dowolnego komputera będącego w sieci lokalnej z centralą wyposażoną w moduł ETHM-1 napisać/uruchomić aplikację, która zintegruje się z alarmem.

Po co to robić? Jeśli próbujecie stworzyć inteligentny dom centrala alarmowa jest bardzo przydatnym zestawem czujek, pokazuje m. in. w których pomieszczeniach w tej chwili jest ruch (np. przy włączaniu światła), pokazuje też stan załączenia stref alarmu, można też wykorzystać Integrę do wzbudzenia alarmu w przypadku gdyby np. pojawiła się woda na podłodze lub zgłosiłby się czujnik gazu w pomieszczeniu.

Do rzeczy:
Centrala alarmowa udostępnia przez ETHM-1 zestaw portów dla różnych usług, standardowo są to:

  1. 7090 – port programu DloadX do administracji alarmem
  2. 7091 – port dla programu GuardX dla użytkowników alarmu (też dla aplikacji mobilnych)
  3. 7092 – port dla sterowania przez www
  4. 7094 – port otwartego protokołu komunikacyjnego

Ten ostatni interesuje nas najbardziej. W programie administracyjnym DloadX trzeba włączyć port integracji (można ustawić też inny nr portu) – bez tego nie skomunikujemy się z centralą.

DloadX: Dane – Struktura i sprzęt – ETH1- checkbox integracji (ale nie szyfrowana)

Komunikujemy się w sposób następujący – do serwera pod jego adres IP i nr portu wysyłamy żądanie skonstruowane wg schematu (jeden wpis – jeden bajt)

  • nagłówek – 2 bajty: 0xFE, 0xFE
  • polecenie zgodne z protokołem – 1 bajt (CMD)
  • parametry polecenia (może być 0 bajtów, a może być n bajtów).
  • 2 bajty suma kontrolna (CRC)
  • 2 bajty – koniec transmisji

Protokół dokładnie opisuje dokument PDF na stronie SATEL:
Dokumentacja protokołu integracji Ethm-1 Satel Integra.

Program nie wymaga uprawnień root.
UWAGA! Przed kompilacją i uruchomieniem należy otworzyć port (np. 7094) w programie DloadX bez szyfrowania transmisji, oraz wypełnić adres Integry i nr portu w poniższym kodzie w sekcji #define. Należy też zablokować wyjście tego portu na zewnątrz sieci lokalnej, by niemożliwe było stosowanie protokołu przez osoby postronne.

Kompilujemy poleceniem

1
gcc integra.c -o integra

Uruchamiamy poleceniem

1
./integra

W programie zaimplementowałem dwie funkcje z protokołu – pierwsza to wypisanie wersji Integry (daty, języka), a druga wypisuje uzbrojone aktualne strefy.

integra.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include<stdio.h>
#include<sys/socket.h>    //socket
#include<arpa/inet.h> //inet_addr

//Podstawowy konfig INTEGRY - adres IP Integry i port
#define ADRES "1.2.3.4"
#define PORT 7094

//UWAGA! Nie jest to port DloadX, ani GuradX, ani www, tylko specjalny inny
//należy go włączyć z programu DloadX

extern void exit(int);

unsigned short obliczCRC(const unsigned char* pCmd, unsigned int length)
//oblicza CRC dla transmisji bez 2 bsjtow na pocz i 2 na koncu.
 {
       unsigned short crc = 0x147A, i;

        for (i = 0; i < length; ++i)
        {
                crc = (crc << 1) | (crc >> 15);
                crc = crc ^ 0xFFFF;
                crc = crc + (crc >> 8) + pCmd[i];
        }

        return crc;
}

void blad(const char *komunikat)
{
        fprintf(stderr, "%s!\n", komunikat);
        exit (1);
}

int wyslijPolecenie( const unsigned char *polecenie, const unsigned int dlugosc, unsigned char *odpowiedz)
{
    int sock, i, rozmiar; //rozmiar odp
    struct sockaddr_in server;
    unsigned char message[1000];

    if (dlugosc>990)
        blad("Zbyt długie polecenie");
    sock = socket(AF_INET , SOCK_STREAM , 0);
    if (sock == -1)
        blad("Nie można stworzyć połączenia socket");
    server.sin_addr.s_addr = inet_addr(ADRES);
    server.sin_family = AF_INET;

   server.sin_port = htons(PORT);

    if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0)
        blad("Nie udało się nawiązać połączenia");
    printf("Połączony\n");

    //naglowek:
     message[0]=0xfe;
     message[1]=0xfe;
   //wiadomosc
     memcpy(message+2, polecenie, dlugosc); //+2 za naglowek
     unsigned short crc=obliczCRC(polecenie,dlugosc);
     message[2+dlugosc] = (crc >> 8) & 0xff; //crc1
     message[3+dlugosc] = crc & 0xff;   //crc2
     message[4+dlugosc] = 0xfe; //koniec transmisji 2 bajty
     message[5+dlugosc] = 0x0d;

     if (send(sock, message, dlugosc+6, 0) < 0)
            blad("Nie udało się wysłać wiadomości");

     rozmiar = recv(sock, odpowiedz, 2000, 0);
     if (rozmiar < 0) //rozmiar odp
            blad("Nie udało się pobrać odpowiedzi z serwera");
     if (odpowiedz[0]!=0xfe || odpowiedz[1]!=0xfe || odpowiedz[rozmiar-1]!=0x0d || odpowiedz[rozmiar-2]!=0xfe)
             blad("Urządzenie zajęte (busy) lub niewłaściwy format odpowiedzi");
     close(sock);
     return rozmiar;
}

const char *rodzajCentrali(unsigned char kod)
{
        switch(kod)
        {
         case 0: return "24";
         case 1: return "32";
         case 2: return "64";
         case 3: return "128";
         case 4: return "128-WRL SIM300";
         case 132: return "128-WRL LEON";
         case 66: return "64 PLUS";
         case 67: return "128 PLUS";
         case 72: return "256 PLUS";
         default: return "Nieznany typ";
        }
}

void wypiszOdpowiedzSerwera( unsigned char *komunikat, unsigned int dlugosc)
{
      int i;
       printf("Rozmiar odpowiedzi: %i\nOdpowiedz serwera:\n", dlugosc);
        for (i=0; i<dlugosc; i++)
        {
               if (i==3 || i==dlugosc-4)
                 printf("----------\n");
               printf("%#02i : %#04x\n", i, komunikat[i]);
        }
}



void info()
{
        unsigned char komunikat[1], odp[2000];
        komunikat[0] = 0x7e;
        int  dlugosc = wyslijPolecenie(komunikat, 1, odp);
        if (dlugosc<20)
           blad ("Za krótka odpowiedź z centrali na polecenie 0x7e");
        //wypiszOdpowiedzSerwera( odp, dlugosc );
        //interpretacja wynikow z centrali:
        const char *typ = rodzajCentrali(odp[3]);
        printf ("Typ centrali: INTEGRA %s\n", typ);
        printf ("Wersja: %c.%c%c z dnia: %c%c%c%c-%c%c-%c%c\n", odp[4], odp[5], odp[6], odp[7], odp[8], odp[9], odp[10], odp[11], odp[12], odp[13], odp[14]);
        printf( "Wersja językowa: %i (%s)\n", odp[15], (odp[15]==1 ? "angielska" : "inny język"));
        printf ("Dane zapisane w pamięci FLASH: %s \n", (odp[16]==255 ? "tak" : "nie"));
}

void uzbrojone()
{
        unsigned char komunikat[1], odp[2000];
        komunikat[0]=0x0a;
        int dlugosc=wyslijPolecenie(komunikat, 1, odp);
        wypiszOdpowiedzSerwera( odp, dlugosc);
        // osobne przykładowe strefy alarmu np.
        //1 - gora strefa1
        //2 - dol strefa2
        //3 - wszystkie strefy
        //0 - alarm rozbrojony
        switch (odp[3])
        {
                case 0:  printf("Alarm wyłączony"); break;
                case 1:  printf("Włączona strefa: Góra"); break;
                case 2:  printf("Włączona strefa: Dół"); break;
                case 3:  printf("Alarm włączony we wszystkich strefach"); break;
                default: blad("Błąd odczytu załączonych stref alarmu");
        }
        printf(".\n");
}

int main(int argc , char *argv[])
{
   info();
   uzbrojone();
   return 0;
}