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:
- 7090 – port programu DloadX do administracji alarmem
- 7091 – port dla programu GuardX dla użytkowników alarmu (też dla aplikacji mobilnych)
- 7092 – port dla sterowania przez www
- 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; } |