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 szeregowym 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.

Ten wpis został opublikowany w kategorii Inteligentny Dom i oznaczony tagami , , , , , . Dodaj zakładkę do bezpośredniego odnośnika.

7 odpowiedzi na Integracja Satel ETHM-1 z Raspberry Pi

  1. Wojtek pisze:

    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

    • Wojtek pisze:

      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”.

  2. Test123 pisze:

    Aż dziwnie czyta się kod, zaktualizuj wiedzę z ECMAScriptu 🙂

  3. cino111 pisze:

    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ę?

  4. xury pisze:

    Fajnie by było jakby można było z tego kodu stworzyć node dla node-red.

  5. Alojzy Bąbel pisze:

    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()
    {

  6. Alojzy Bąbel pisze:

    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;
    }

    }
    }

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.