Integracja Satel ETHM-1 z Raspberry Pi
Dwa i pół roku temu, gdy wybierałem sposób integracji systemu “inteligentnego domu” z centralą alarmową Satel Integra, zdecydowałem się na użycie modułu INT-RS . Stworzyłem wtedy skrypty w Node.js do integracji systemu automatyki domowej z centralą alarmową . Wybrałem komunikację przez port szeregowy a nie moduł IP, między innymi ze względu na koszty. Teraz, przy okazji awarii konwertera transmisji szeregowej, zdecydowałem się na zmianę medium na Ethernet, bo jest to wygodniejsze przy kablowaniu, a dodatkowo same moduły ETHM-1 można kupić używane w rozsądniejszych cenach. Nie bez znaczenia jest też fakt, że dzięki temu będę mógł też programować centralę zdalnie, bez przenoszenia laptopa i kombinowania z połączeniem szeregowym oraz używać aplikacji Satela do zdalnego sterowania centralą z telefonu komórkowego. Poza tym zawsze to coś nowego do wypróbowania.
Instalacja
Instalacja modułu ETHM-1 nie różni się znacząco od znanej z INT-RS. Przewody łączymy do tej samem magistrali manipulatorów. Mamy więc zasilanie, dwa przewody do komunikacji i ewentualnie przewód RS232, jeżeli chcemy programować centralę przez sieć. Tu od razu mała uwaga - potrzebny jest właściwy przewód, który Satel oznacza czerwoną etykietą. Zielony RJ/PIN5-LCD nie będzie działał, bo żyły są inaczej połączone. Poza tym pozostaje podłączenie przewodu Ethernet do switcha naszej sieci domowej.
Należy też w konfiguracji centrali, np. przez program DloadX, przypisać adres IP oraz włączyć opcję integracji i wybrać port (domyślnie 7094).
Integracja
Po sprawdzeniu, czy moduł odpowiada na pingi, można przejść do integracji. Pewną niespodzianką może być, jak niewiele zmian potrzebne jest, aby przystosować skrypty z wersji działającej z portem szeregowy m na tryb sieciowy. Całą klasę SatFrame pozostawiamy bez zmian, bo protokół jest taki sam. Nieznacznie zmieniamy tylko główny moduł, który ma otwierać połączenie TCP zamiast portu szeregowego.
var SatFrameClass = require("./satframe");
var dgram = require('dgram');
var http = require('http');
var child_process = require('child_process');
var dgram = require('dgram');
var net = require('net');
var bufsize=1024;
var buf=new Buffer(bufsize);
var bufpos=0;
var outputs=new Array;
var outputschange=new Array;
var armed=false;
//połączenie do modułu ETHM-1 - adres IP i port
var client = new net.Socket();
client.connect(7094, '192.168.1.17', function() {
console.log('Connected');
});
client.on('data', function(data) {
var receivedData = "";
if(bufpos+data.length>bufsize) bufpos=0;
for(w=0; w<data.length; w++ )
{
buf[bufpos]=data[w];
bufpos++;
}
oldbufpos=bufpos;
while( (bufpos=parseSatel(buf, bufpos))!=oldbufpos ) oldbufpos=bufpos;
});
client.on('close', function() {
console.log('Connection closed');
exit(1);
});
client.on("error", function() { console.log("error"); exit(1); });
// tworzymy zapytanie o stan wyjść
var sf=new SatFrameClass.SatFrame();
sf.addParam(0x17);
//inicjujemy tablice stanów i zmian wyjść
//numery wejść w cenrali są numerowane od 1 do 128 (w przypadku Integry 128)
for( w=1; w<=128; w++ )
{
outputs[w]=0;
outputschange[w]=0;
}
// cykliczne zapytania o stan wyjść
setInterval( function()
{
var fr=sf.getFrameAsBuffer();
client.write(fr);
}, 1000 );
// szuka pełnej ramki i dba, żeby bufor się nie przepełnił
function parseSatel(b, size)
{
var begin=-1;
var end=-1;
var frame=new Array;
for( w=0; w<size && end==-1; w++ )
{
if( b[w]==0xfe && b[w+1]==0xfe ) begin=w;
if( b[w]==0xfe && b[w+1]==0x0d ) end=w+1;
}
if( end!=-1 ) // jest pelna ramka
{
//przepisujemy gotowa ramke
q=0;
for(w=begin; w<=end; w++)
{
frame[q]=b[w];
q++;
}
//przepisujemy reszte na poczatek bufora
q=0;
for(w=end+1; w<size; w++)
{
b[q]=b[w];
q++;
}
runFrame(frame);
return(q);
}
return(size);
}
// uruchamia akcje zwiazana z ramka, gdy to potrzebne
function runFrame(f)
{
var tm = new Date();
var test=new SatFrameClass.SatFrame();
test.setFrame(f);
if( !test.checkCrc() )
{
console.log("Niepoprawny CRC dla ramki: "+f);
return;
}
if( f[2]==0x17 ) //wyjscie
{
for( w=0; w<128; w++ )
{
out=f[3+Math.floor(w/8)]&(Math.pow(2,(w%8)));
if( out>0 ) out=1;
outputschange[w+1]=0;
if( out!=outputs[w+1] ) {
outputschange[w+1]=1;
console.log(tm+" zmiana "+(w+1)+"="+out);
sendMain("s:"+out+":"+(w+1));
}
outputs[w+1]=out;
}
}
}
//wysłanie do modułu głównego przez UDP
function sendMain(code)
{
var message = new Buffer(code);
var client = dgram.createSocket("udp4");
client.send(message, 0, message.length, 8001, "127.0.0.1", function(err) {
client.close();
});
}
Cała zmiana od strony programistycznej zajęła może 15 minut z testowaniem. Oczywiście dla ETHM-1, gdzie znamy początek i koniec ramek, w przeciwieństwie do transmisji szeregowej, kod można nieco uprościć, ale nie ma takiej konieczności. Dzięki temu jest bardziej uniwersalny.
To czego nie zrobiliśmy, a co jest możliwe, to kodowanie transmisji wybranym kluczem dla podniesienia poziomu bezpieczeństwa. Oczywiście wszystko zależy od potrzeb i sieci, do której podłączona jest centrala.



Komentarze (7)
Archiwum komentarzy z poprzedniej wersji bloga.
Gratuluję bloga - świetnie się go czyta!
Piszę jednak w sprawie raspberry i integracji z satel.
Zrobiłem dwa pliki
satel.js oraz satframe.js. Satframe jest zrobiony na podstawie Twojego starszego wpisu https://techniczny.wordpress.com/2016/01/10/monitorowanie-wejsc-i-wyjsc-centrali-satel-integra-w-node-js/
Niestety nie umiem poprawić kodu - w linii 88 jest błąd tutaj--> this.frame.push((crc>>8ff); console.log('Connected}); <-- błąd składni? Potem też jest kilka miejsc, które się nie "kompilują".
Czy mógłbyś sprawdzić gdzie jest problem?
Mój node 8.9.4 - działa na nim sporo programów
Znalazłem problem - chodzi o formatownie w przeglądarkach na Windowsie. IE, Edge, Chrome - modyfikują Twój kod tak, że jest "niewykonalny".
Na szczęście jest Mac :)
Dziękuje za program.
Po niewielkich modyfikacjach działa super. U mnie nie łaczy się z sendLed ale wysyła webhooki - inny po naruszeniu, inny gdy brak naruszenia.
Działą rewelacyjnie :)
kod:
if(outputs[121]==1) {http.get('http://192.168.77.31:57847/?accessoryId=satel121&state=true', (resp)=>{return;})}
if(outputs[121]==0) {http.get('http://192.168.77.31:57847/?accessoryId=satel121&state=false', (resp)=>{return;})}
Oczywiście można to ładniej napisać ale dla moich 16 czujek sie nie "pofatygowałem".
Aż dziwnie czyta się kod, zaktualizuj wiedzę z ECMAScriptu :)
Super temat, ale dla laika dalej nie do ogarnięcia. Czy możesz wstawić (podesłać gotowe skrypty ) tylko do modyfikacji jakie wejscia/wyjscia mają być czytane i co dalej z tym robić? Ja np potrzebuję przy naruszeniu wejścia/wyjścia, załączeniu strefy, alarmu itd wysłąć link internetowy. Jest to możliwe do opisania krok po kroku na malinę?
Fajnie by było jakby można było z tego kodu stworzyć node dla node-red.
Hej oto sposób na integrację z NODE RED którą zastosowałem.
lekko zmodyfikowany skrypt - pisze do brokera mqtt pod topiki o nazwach wyjść centrali:
var mqtt = require('mqtt')
var client_mqtt = mqtt.connect('mqtt://adres_IP_twojego_MQTT_Brokera')
var SatFrameClass = require("./SatFrame");
var dgram = require('dgram');
var http = require('http');
var child_process = require('child_process');
var dgram = require('dgram');
var net = require('net');
var bufsize=1024;
var buf=new Buffer.alloc(bufsize);
var bufpos=0;
var outputs=new Array;
var outputschange=new Array;
var armed=false;
//------------------------------------------------------------------------------
//połączenie do modułu ETHM-1 - adres IP i port
var client = new net.Socket();
client.connect(7094, '192.168.11.102', function() {
console.log('Connected');
});
client.on('data', function(data) {
var receivedData = "";
if(bufpos+data.length>bufsize) bufpos=0;
for(w=0; w<data.length; w++ )
{
buf[bufpos]=data[w];
bufpos++;
}
oldbufpos=bufpos;
while( (bufpos=parseSatel(buf, bufpos))!=oldbufpos ) oldbufpos=bufpos;
// bufpos=parseSatel(buf, bufpos)
});
client.on('close', function() {
console.log('Connection closed');
exit(1);
});
client.on("error", function() { console.log("error"); exit(1); });
// tworzymy zapytanie o stan wyjść
var sf=new SatFrameClass.SatFrame();
sf.addParam(0x17);
//inicjujemy tablice stanów i zmian wyjść
//numery wejść w cenrali są numerowane od 1 do 64 (w przypadku Integry 64)
for( w=1; w<=64; w++ )
{
outputs[w]=0;
outputschange[w]=0;
}
// cykliczne zapytania o stan wyjść
const time = setInterval( function()
{
var fr=sf.getFrameAsBuffer();
client.write(fr);
}, 1000 );
// szuka pełnej ramki i dba, żeby bufor się nie przepełnił
function parseSatel(b, size)
{
var begin=-1;
var end=-1;
var frame=new Array;
for( w=0; w<size && end==-1; w++ )
{
if( b[w]==0xfe && b[w+1]==0xfe ) begin=w;
if( b[w]==0xfe && b[w+1]==0x0d ) end=w+1;
}
if( end!=-1 ) // jest pelna ramka
{
//przepisujemy gotowa ramke
q=0;
for(w=begin; w<=end; w++)
{
frame[q]=b[w];
q++;
}
//przepisujemy reszte na poczatek bufora
q=0;
for(w=end+1; w<size; w++)
{
b[q]=b[w];
q++;
}
runFrame(frame);
return(q);
}
return(size);
}
// uruchamia akcje zwiazana z ramka, gdy to potrzebne
function runFrame(f)
{
var tm = new Date();
var test=new SatFrameClass.SatFrame();
test.setFrame(f);
if( !test.checkCrc() )
{
console.log("Niepoprawny CRC dla ramki: "+f);
return;
}
if( f[2]==0x17 ) //wyjscie
{
for( w=0; w0 ) out=1;
outputschange[w+1]=0;
if( out!=outputs[w+1] ) {
outputschange[w+1]=1;
console.log(tm+" zmiana "+(w+1)+"="+out);
// polaczenie mqqt
temp=w+1;
client_mqtt.publish(temp.toString(),out.toString())
}
outputs[w+1]=out;
}
}
}