ioBroker / AdapterRequests

This Place is used to track the status of new Adapter-Requests.
249 stars 36 forks source link

Brunner EAS3 Wlan "Elektronische Abbrandsteuerung" #761

Open sharky-os opened 2 years ago

sharky-os commented 2 years ago

What kind of device or service you would like to see an adapter for? EAS 3 Wlan Brunner

Is the device connected to the internet or only in the local network available? only local

Is an official App or Website available? https://play.google.com/store/apps/details?id=de.brunner.apps.eas3&hl=de&gl=US https://apps.apple.com/de/app/brunner-eas3/id1588084582

Is an official API including documentation available? no

Are other libraries for an integration available? no

Is this device already integrated in other Smart Home systems? no

Is this device already integrated in homebridge? Might the ham adapter together with the homebridge plugin be sufficient? no

Additional context The EAS sends broadcast udp packet unencrypted in locale network. here is a wireshark recording

bdle eas="0" pin="0" cmd="410" stat="1">0;1;1;80;2;0;0;319;1;16384;0;0;32;</bdle Here is a short excerpt of the data. Each number is information from the oven. For example: 319 = firmware version, 32 = combustion chamber temperature, state = combustion status, the 3rd zero status of the door, etc.

I would like to help and provide everything I have read.

`

After you created the issue vote for yourself in the first post of the issue using the "+1"/"Thumbs up" button

mcm1957 commented 2 years ago

Could be this unit: https://www.primus-ofenshop.com/wp-content/uploads/Brunner-EAS-3-Bedienungsanleitung.pdf

An implementation would most likely require matching hardware

JR-Home commented 1 year ago

I do not have an adapter for EOS3, but a Java script which processes the broadcast messages from Brunner EOS3. Can anybody create an adpater out of it? Recommended: EOS3 Firmware 3.25 or newer.

console.log( "Skript Kachelofen Temperatur Version 1.16 started..." ); 

const cBrunnerEAS3HeizraumTemperatur = "javascript.0.BrunnerEAS3.HeizraumTemperatur";
const cBrunnerEAS3AbbrandStatus = "javascript.0.BrunnerEAS3.AbbrandtStatus";
const cBrunnerEAS3Broadcast = "javascript.0.BrunnerEAS3.Broadcast";
const cBrunnerEAS3Broadcast2 = "javascript.0.BrunnerEAS3.Broadcast2";
const cBrunnerEAS3LetzterAbbrand = "javascript.0.BrunnerEAS3.LetzterAbbrand";
const cBrunnerEAS3LetzterAbbrandMS = "javascript.0.BrunnerEAS3.LetzterAbbrandMS";

const cPortNumberEAS3Broadcast = 45454;

// Status Brunner:
// -1 - Status nicht verfügbar. WLAN unterbrochen
// 0 - Türe offen
// 1 - Abrand start
// 2-  Abbrand Stufe 2...
// 5 - Abbrand Ende
// 6 - Fehler/Timeout, Abbrandt Start wurde nicht erkannt
// 7 - Alles vorbei... Aus.

createState( cBrunnerEAS3HeizraumTemperatur, 0, false, {name: 'Brunner Kamin Brennraum Temperatur', type: 'number', unit: '°C'});
createState( cBrunnerEAS3AbbrandStatus, 0, false, {name: 'Brunner Kamin Abbrandt Status', type: 'number', unit: ''});
createState( cBrunnerEAS3Broadcast, "Init", true, {name: 'Brunner Kamin Broadcast', type: 'string', unit: ''});
// createState( cBrunnerEAS3Broadcast2, 0, false, {name: 'Brunner Kamin Broadcast2', type: 'string', unit: ''});
createState( cBrunnerEAS3LetzterAbbrand, formatDate(Date.now(), "TT.MM.JJJJ SS:mm"), false, {name: 'Brunner letzter Abbrand', type: 'string', unit: ''});
createState( cBrunnerEAS3LetzterAbbrandMS, Date.now(), false, {name: 'Brunner letzter Abbrand', type: 'number', unit: ''});

// Require dgram module.
var dgram = require('dgram');
// Create udp server socket object.
var server = dgram.createSocket("udp4");
// Mit der FW-Version 3.25 ist ein weiterer Parameter hinzugekommen. Bedeutung noch unklar. RegExpr. musste daher angepasst werden
var re = /<bdle eas.+stat="(\d+)">(\d+);(\d+);(\d+);(\d+);(\d+);(\d+);(\d+);(\d+);(\d+);(\-?\d+);(\d+);(\d+);(\d+);?(\d*).+;<\/bdle>/;

// Port Nummer der Brunner EAS3 Abbrandt Steuerung
server.bind( cPortNumberEAS3Broadcast );
// When udp server receive message.
server.on("message", function ( message ) {
    // Create output message.
    var sMessage = message.toString()
    var resultArray = re.exec( sMessage );
    if( resultArray != null ) {
        // console.log( resultArray );
        // Array Index 0 enthält den ganzen match, ab 1 dann den Inhalt der Klammern
        // Idnex 1 Status, Idnex 14 Temperatur (letzter Wert in Aufzählung)
        setState( cBrunnerEAS3HeizraumTemperatur, parseInt( resultArray[14] ) );
        var iAbbrandStatus = parseInt( resultArray[1] );
        var iAbbrandStatusAlt = getState( cBrunnerEAS3AbbrandStatus ).val;
        if( iAbbrandStatus >= 2 && iAbbrandStatusAlt >=0 && iAbbrandStatusAlt < 2 )
        {
            // Ein Abbrand ist zu Ende gegangen. Zeit merken
            setState( cBrunnerEAS3LetzterAbbrandMS, Date.now() );
            setState( cBrunnerEAS3LetzterAbbrand, formatDate(Date.now(), "TT.MM.JJJJ SS:mm") );
        }
        setState( cBrunnerEAS3AbbrandStatus, iAbbrandStatus );
        setState( cBrunnerEAS3Broadcast, sMessage, true );
    } 
    else {
        // console.log( "Udp receive unknown message : " + sMessage + "\n" );
        // setState( cBrunnerEAS3Broadcast2, sMessage, true );
    }
});

// When udp server started and listening.
server.on('listening', function () {
    // Get and print udp server listening ip address and port number in log console. 
    var address = server.address(); 
    console.log('UDP Server started and listening on ' + address.address + ":" + address.port);
});

/*
var s = '<bdle eas="0" pin="0" cmd="410" stat="7">0;1;1;52;2;0;0;319;1;-10;0;0;45;</bdle>'
var resultArray = re.exec( s );
if( resultArray != null ) {
        console.log( "Skript Test:" + resultArray );
        setState( cBrunnerEAS3HeizraumTemperatur, parseInt( resultArray[12] ) );
        setState( cBrunnerEAS3AbbrandStatus, parseInt( resultArray[0] ) );

    } 
    else {
        // console.log( "Udp receive unknown message : " + message + "\n" );
    } 

*/

// schedule("*/5 * * * *", function () { // Abfrage alle 5 Min
// Schedule funktioniert bei zyklischem Aufrufen nicht bei Zeitumstellungen. Wird nicht oder zu oft aufgerufen!! Daher setInterval verwenden 
setInterval( function () {
    // Wenn wir für mehr als 6 Minuten keine Verbindung mehr zur Brunner EAS 3 hatten, dann
    // haben wir die Verbindung verloren!
    if( getState( cBrunnerEAS3HeizraumTemperatur ).val > -99 )
    {
        if( Date.now() - getState( cBrunnerEAS3HeizraumTemperatur ).lc > 6 * 60 * 1000 )
        {
            setState( cBrunnerEAS3HeizraumTemperatur, -99, false );
            setState( cBrunnerEAS3AbbrandStatus, -1, false );
            setState( cBrunnerEAS3Broadcast, "Timeout, EAS3 connection lost!, false" );
        } 
    }
}, 5 * 60 * 1000 );

// close connection if script stopped
onStop( function () {
        server.close();
        log("Close Socket...")
}, 2000 /*ms*/);
JoMass commented 1 year ago

Thank you JR-Home

Your tips enabled me to read in the data streams provided by EAS3. I used NodeRed "UDP listen node" to capture, analyze and store the data. (The easier way for programming dummies).

maxleistner commented 1 year ago

@JR-Home how do you connect to the system? The shown IP address from the app is not connectable.

JR-Home commented 1 year ago

@JR-Home how do you connect to the system? The shown IP address from the app is not connectable.

I do not "connect" to the EAS3, I only receive the broadcast messages which are cyclically send by EAS3.

maxleistner commented 1 year ago

Ok ;) so how do you receive the signals? There's no API or anything that I know of.

JR-Home commented 1 year ago

Ok ;) so how do you receive the signals? There's no API or anything that I know of.

Correct. There is no API documentation. I have done reverse engineering by packet inspection with wireshark.

maxleistner commented 1 year ago

Do you have a working solution i could use? How would the setup look like. I would pay for getting a push notification as soon as i can put new wood in the oven 😂 the app is the worst I've ever seen🙈

thorstenfleischmann commented 7 months ago

Thank you JR-Home

Your tips enabled me to read in the data streams provided by EAS3. I used NodeRed "UDP listen node" to capture, analyze and store the data. (The easier way for programming dummies).

Hi. Would you share your node red flow? Would be awesome

thorstenfleischmann commented 7 months ago

I designed a flow for home assistant myself and published it here: https://github.com/thorstenfleischmann/brunner-eas3-node-red-home-assistant-connect/

krischan1410 commented 6 months ago

Just for your interest: With the info from JR-Home I created a Homebridge plugin which provides the EAS device to Apple HomeKit as a temperature sensor: https://www.npmjs.com/package/homebridge-brunner-eas

Draygv2 commented 2 months ago

@JR-Home do you have a new version of the script?

Draygv2 commented 1 month ago

I added some parameter to the js. Now it works with the Version 3.37

console.log( "Skript Kachelofen Temperatur Version 3.37 started..." ); 

const cBrunnerEAS3HeizraumTemperatur = "javascript.0.BrunnerEAS3.HeizraumTemperatur";
const cBrunnerEAS3Version = "javascript.0.BrunnerEAS3.Version";
const cBrunnerEAS3VersionParameter = "javascript.0.BrunnerEAS3.VersionParameter";
const cBrunnerEAS3VerlAbbrand = "javascript.0.BrunnerEAS3.S+";
const cBrunnerEAS3Oeko = "javascript.0.BrunnerEAS3.Oeko";
const cBrunnerEAS3Drosselklappe = "javascript.0.BrunnerEAS3.Drosselklappe";
const cBrunnerEAS3Display = "javascript.0.BrunnerEAS3.DisplayBri";
const cBrunnerEAS3Summer = "javascript.0.BrunnerEAS3.Summer";
const cBrunnerEAS3NLH = "javascript.0.BrunnerEAS3.NLH";
const cBrunnerEAS3Heizeinsatz = "javascript.0.BrunnerEAS3.Heizeinsatz";
const cBrunnerEAS3AbbrandStatus = "javascript.0.BrunnerEAS3.AbbrandtStatus";
const cBrunnerEAS3Broadcast = "javascript.0.BrunnerEAS3.Broadcast";
const cBrunnerEAS3Broadcast2 = "javascript.0.BrunnerEAS3.Broadcast2";
const cBrunnerEAS3LetzterAbbrand = "javascript.0.BrunnerEAS3.LetzterAbbrand";
const cBrunnerEAS3LetzterAbbrandMS = "javascript.0.BrunnerEAS3.LetzterAbbrandMS";

const cPortNumberEAS3Broadcast = 45454;

// Status Brunner:
// -1 - Status nicht verfügbar. WLAN unterbrochen
// 0 - Türe offen
// 1 - Abrand start
// 2-  Abbrand Stufe 2...
// 5 - Abbrand Ende
// 6 - Fehler/Timeout, Abbrandt Start wurde nicht erkannt
// 7 - Alles vorbei... Aus.

createState( cBrunnerEAS3HeizraumTemperatur, 0, false, {name: 'Brunner Kamin Brennraum Temperatur', type: 'number', unit: '°C'});
createState( cBrunnerEAS3AbbrandStatus, 0, false, {name: 'Brunner Kamin Abbrandt Status', type: 'number', unit: ''});
createState( cBrunnerEAS3Version, 0, true, {name: 'Brunner EAS 3 Version', type: 'number', unit: ''});
createState( cBrunnerEAS3VersionParameter, 0, true, {name: 'Brunner EAS 3 Version Parameter', type: 'number', unit: ''});
createState( cBrunnerEAS3VerlAbbrand, 0, true, {name: 'Brunner EAS 3 Verlängerter Abbrand S+', type: 'number', unit: ''});
createState( cBrunnerEAS3Oeko, 0, true, {name: 'Brunner EAS 3  Öko-Abbrand ', type: 'number', unit: ''});
createState( cBrunnerEAS3Drosselklappe, 0, true, {name: 'Brunner EAS 3 Drosselklappe aktiviert', type: 'number', unit: ''});
createState( cBrunnerEAS3Display, 0, true, {name: 'Brunner EAS 3 Hintergrund-Beleuchtung', type: 'number', unit: '%'});
createState( cBrunnerEAS3Summer, 0, true, {name: 'Brunner EAS 3  Intensität (Frequenz bzw. Lautstärke) des Summers', type: 'number', unit: ''});
createState( cBrunnerEAS3NLH, 0, true, {name: 'Brunner EAS 3 Anzeige der Hinweise zum Nachlegen', type: 'number', unit: ''});
createState( cBrunnerEAS3Heizeinsatz, 0, true, {name: 'Brunner EAS 3  Aktuelle Heizeinsatznummer', type: 'number', unit: ''});
/* Heizeinsätze mit Nummer
1. HKD 2, HKD 2.2, HKD 2.2 k  
2. SK, HKD 2.6 k SK  
3. HKD 4, HKD 4.1  
4. HKD 5, HKD 5.1  
5. HKD 6, HKD 6.1, HKD 2.6  
6. B4  
7. B5, B6  
8. HWM  
9. HKD 4sk, HKD 4w  
10. Kamin-Kessel, Kamin-Kessel Eck  
11. RF 55, RF 55.1  
12. RF 66, RF 66.1  
13. Kompakt-Kamin  
14. Stil-Kamin  
15. Eck-Kamin  
16. 180° Kamin  
17. Grundofen*  
18. HF 5  
19. HF 7  
20. HF10 / HF10w / HFG10 / HWM-HF10  
21. HF15, HF 15w, HFG15  
22. HF10sk  
23. SF7  
24. SF10  
25. SF10sk, SF20sk  
26. B7, B8  
27. Herdkessel  
28. KKE 33  
29. HKD 2.2 XL  
30. HKD 2.2 XL SK / HKD 2.2 SK  
31. KSO 25 / KSO 33  
32. WF 33  
33. GOT / GOT+GOF flach  
34. WF 25  
35. HKD 7/8/9/10/11/12  
36. Architektur Kessel  
37. WF 50  
38. GOT / GOT+GOF Eck  
39. Panorama  
40. GOT / GOT+GOF Tunnel  
41. Architektur  
42. DF 33  
43. KFR  
44. HKD3  
45. Scandinavian  
46. BKH
*/

createState( cBrunnerEAS3Broadcast, "Init", true, {name: 'Brunner Kamin Broadcast', type: 'string', unit: ''});
// createState( cBrunnerEAS3Broadcast2, 0, false, {name: 'Brunner Kamin Broadcast2', type: 'string', unit: ''});
createState( cBrunnerEAS3LetzterAbbrand, formatDate(Date.now(), "TT.MM.JJJJ SS:mm"), false, {name: 'Brunner letzter Abbrand', type: 'string', unit: ''});
createState( cBrunnerEAS3LetzterAbbrandMS, Date.now(), false, {name: 'Brunner letzter Abbrand', type: 'number', unit: ''});

// Require dgram module.
var dgram = require('dgram');
// Create udp server socket object.
var server = dgram.createSocket("udp4");
var re = /<bdle eas.+stat="(\d+)">(\d+);(\d+);(\d+);(\d+);(\d+);(\d+);(\d+);(\d+);(\d+);(\-?\d+);(\d+);(\d+);(\d+);(\d+);<\/bdle>/;

// Port Nummer der Brunner EAS3 Abbrandt Steuerung
server.bind( cPortNumberEAS3Broadcast);
// When udp server receive message.
server.on("message", function ( message ) {
    // Create output message.
    var sMessage = message.toString()
    var resultArray = re.exec( sMessage );
    if( resultArray != null ) {
        //console.log( resultArray );
        // Array Index 0 enthält den ganzen match, ab 1 dann den Inhalt der Klammern
        // Idnex 1 Status, Idnex 14 Temperatur (letzter Wert in Aufzählung)
        setState( cBrunnerEAS3HeizraumTemperatur, parseInt( resultArray[14] ) );
        setState( cBrunnerEAS3VerlAbbrand, parseInt( resultArray[2] ));
        setState( cBrunnerEAS3Oeko, parseInt( resultArray[3] ));
        setState( cBrunnerEAS3Drosselklappe, parseInt( resultArray[4] ));
        setState( cBrunnerEAS3Display, parseInt( resultArray[5] ));
        setState( cBrunnerEAS3Summer, parseInt( resultArray[6] ));
        setState( cBrunnerEAS3NLH, parseInt( resultArray[7] ));
        setState( cBrunnerEAS3Heizeinsatz, parseInt( resultArray[15] ));   
        setState( cBrunnerEAS3Version, parseInt( resultArray[9] ));
        setState( cBrunnerEAS3VersionParameter, parseInt( resultArray[10] ));
        var iAbbrandStatus = parseInt( resultArray[1] );
        var iAbbrandStatusAlt = getState( cBrunnerEAS3AbbrandStatus ).val;
        if( iAbbrandStatus >= 2 && iAbbrandStatusAlt >=0 && iAbbrandStatusAlt < 2 )
        {
            // Ein Abbrand ist zu Ende gegangen. Zeit merken
            setState( cBrunnerEAS3LetzterAbbrandMS, Date.now() );
            setState( cBrunnerEAS3LetzterAbbrand, formatDate(Date.now(), "TT.MM.JJJJ SS:mm") );
        }
        setState( cBrunnerEAS3AbbrandStatus, iAbbrandStatus );
        setState( cBrunnerEAS3Broadcast, sMessage, true );
        //console.log("EAS Breadcast "+sMessage)
    } 
    else {
        // console.log( "Udp receive unknown message : " + sMessage + "\n" );
        // setState( cBrunnerEAS3Broadcast2, sMessage, true );
    }
});

// When udp server started and listening.
server.on('listening', function () {
    // Get and print udp server listening ip address and port number in log console. 
    var address = server.address(); 
    console.log('UDP Server started and listening on ' + address.address + ":" + address.port);
});

// Schedule funktioniert bei zyklischem Aufrufen nicht bei Zeitumstellungen. Wird nicht oder zu oft aufgerufen!! Daher setInterval verwenden 
setInterval( function () {
    // Wenn wir für mehr als 6 Minuten keine Verbindung mehr zur Brunner EAS 3 hatten, dann
    // haben wir die Verbindung verloren!
    if( getState( cBrunnerEAS3HeizraumTemperatur ).val > -99 )
    {
        if( Date.now() - getState( cBrunnerEAS3HeizraumTemperatur ).lc > 6 * 60 * 1000 )
        {
            setState( cBrunnerEAS3HeizraumTemperatur, -99, false );
            setState( cBrunnerEAS3AbbrandStatus, -1, false );
            setState( cBrunnerEAS3Broadcast, "Timeout, EAS3 connection lost!, false" );
        } 
    }
}, 5 * 60 * 1000 );

// close connection if script stopped
onStop( function () {
        server.close();
        log("Close Socket...")
}, 2000 /*ms*/);