NEWS
Memory sparen
-
Ich betreibe schon länger einen Raspberry Pi auf dem
ioBroker, piVCCU und tvheadend läuft. Dennoch sieht mein Memory immer noch ganz vernünftig aus.
Homematic
Ich betreibe die piVCCU als reines Gateway, habe keine HMIP Geräte und benötige keine virtuellen Geräte, deshalb konnte ich den Java Prozess, der beim Start der piVCCU gestartet wird, löschen bzw. habe verhindert, dass er in piVCC gestartet wird.
Dazu das File 'S62HMServer' unter '/var/lib/piVCCU/rootfs/etc/init.d' löschen.
Außerdem habe ich die Instanz von "hm-rega" angehalten.
Ping Adapter Ersatz
Statt des Ping Adapters verwende ich folgendes Script
//------------------------------- ping ----------------------------------------- var pingData = 'javascript.0.ping.'; var pings = { "Geraet1" :"192.168.178.41", "Geraet2" :"192.168.178.29", "Geraet3" :"192.168.178.37" // weitere Geräte, die abgefragt werden sollen }; // Geräte Objekte unter javascript.0.ping erzeugen initialize(); function initialize() {for (var device in pings) {initializeSubscribes(device);}} function initializeSubscribes(device) { createState(pingData + device, false, { name: device, read: true, write: true, desc: 'Ping Indikator', type: 'boolean' }); } // jede Minute Abfrage aller Geräte schedule("*/5 * * * *", function(obj){ function ping(device,ip) { exec('ping '+ ip + ' -c 1', function (error, stdout, stderr) { var tokens = stdout.split('errors'); setState(pingData + device,((tokens[1] === undefined) ? true : false)); }); } for (var device in pings) { ping(device, pings[device]); } });
Google Chromecast und Sayit Ersatz
Sayit und Google Chromecast Adapter habe ich folgendes Script ersetzt, das aber noch etwas mehr (und auch weniger) macht.
//Object secrets enthält private Daten und Passwörter var secrets = JSON.parse(getState("secrets").val); /*--------------- Sound und Text to Sound auf Google Home Speaker ----------- Installation cd /opt/iobroker/node_modules/iobroker.javascript/ sudo npm install castv2-client sudo npm install google-tts-api --save Basisfunktionen Lautstärke einstellen: volDeviceUp (<lautstärke 0-100="">,<ip google/home="">); Text wiedergeben toGoogle(<txt>,<ip google/home="">); Soundfile abspielen soundout(soundfile,vol,device_address) Text auf abspielen speechtospeach (txt,vol,device_address) Zwischen Lautstärkeeinstellung und weiteren Befehlen ist ein Delay von ca. 1 Sekunde erforderlich ----------------- Nutzereinstellungen ---------------------------------------- Ordner Soundfiles. Muss vom iobroker Server erreichbar sein. Das sind alle Verzeichnisse, die unter /opt/iobroker/iobroker-data/files angelegt sind. */ var path_of_soundfiles = '/sayit.1/tts.userfiles/'; // IP von iobroker Servers var iobroker_ip = secrets.network.iobroker_ip; // e.g. "192.168.178.50" // Übersetzung vom logischen Name des Google Home Lautspr. nach IP var googlehomes = { "Bad": secrets.network.google_speaker_Bad,// v. IP Google Speaker als String "WZ" : secrets.network.google_speaker_WZ // v. IP Google Speaker als String // hier können weitere Lautsprecher hinzugefügt werden }; /* Einschränkungen "Normale "Lautstärke, die nach Abschluß der Ausgabe eingestellt werden kann, wenn Lautstärke verändert wurde.Leider kann die Länge der Ausgabe nicht ermittelt werden kann, dehalb muss wieder manuell auf die normale Lautstärke zurückgesetzt werden. Außerdem wird nach Ende der Ausgabe leider nicht mit der Medienwiedergabe, die vorher verwendet wurde, vorgesetzt. */ createState('volume_back'); setState('volume_back',40); // normale Lautstärke //------------------- Ende der Nutzereinstellungen ------------------------------ const Client = require('castv2-client').Client; const DefaultMediaReceiver = require('castv2-client').DefaultMediaReceiver; const googletts = require('google-tts-api'); const language = 'de'; const debug = false; const toGoogle = function(message,device_address, callback) { getSpeechUrl(message, device_address, function(res) { if (debug) log(res + " --> " + message); }); }; //------------------ url für Sprachausgabe erzeugen ---------------------------- googletts("Hallo", language, 1).then(function (url) {log(url)}); const getSpeechUrl = function(text, device_address, callback) { googletts(text, language, 1).then(function (url) { onDeviceUp(device_address, url, function(res){ callback(res); }); }).catch(function (err) { console.error(err.stack); }); }; const onDeviceUp = function(device_address, url, callback) { var client = new Client(); client.connect(device_address, function() { client.launch(DefaultMediaReceiver, function(err, player) { var media = { contentId: url, contentType: 'audio/mp3', streamType: 'LIVE' // or STREAM }; player.load(media, { autoplay: true }, function(err, status) { client.close(); callback('TTS to GoogleHome'); }); }); }); client.on('error', function(err) { console.log('Error: ' + err.message); client.close(); callback('error'); }); }; const volDeviceUp = function(volume,device_address) { var client = new Client(); var obj; if(volume > 0){ obj = { level: volume/100 }; } else { obj = { muted: true }; } client.connect(device_address, function() { client.setVolume(obj, function(err, newvol){ if(err) { log("there was an error setting the volume",'error'); } if (debug) console.log("volume changed to " + Math.round(newvol.level * 100)); }); }); client.on('error', function(err) { log(err.message,'error'); client.close(); }); }; function soundout(soundfile,vol,device_address) { var delay = 0; if (vol !== undefined) {volDeviceUp (vol,device_address);delay = 3000} setTimeout(function(){ if(debug) log('http://'+ iobroker_ip + ':8082' + path_of_soundfiles + soundfile + '.mp3'); onDeviceUp(device_address, 'http://'+ iobroker_ip + ':8082' + path_of_soundfiles + soundfile + '.mp3', function(res){ if(debug) log(res);} ); },delay); } /* //---------------------- Lautstärkeeinstellung --------------------------------- Wenn man bei soundout oder speechout2 den Parameter vol setzt, wird dieser nach Ende der Ausgabe beibehalten. Man sollte deshalb immer manuell die Lautstärke auf einen "normalen" Wert zurücksetzen. Dieser Wert ist in dem Objekt "volume_back" gespeichert. setState("volume",JSON.stringify( { vol: 80, device: "Bad" })); device: default: WZ vol: default: die "normale" Lautstärke wird eingestellt. z.B. setState("volume","{}"); Lautstärke im WZ auf normale Lautstärke setState("volume",'{"device":"Bad"}'); Lautstärke im Bad auf normale Lautst. */ createState("volume"); on({id: "volume", change: "any"}, function (obj) { var parameter = JSON.parse(obj.state.val); var delay = 0; if (parameter.device === undefined) parameter.device = "WZ"; if (parameter.vol === undefined) parameter.vol = getState("volume_back").val; log("Lautstärke auf " + parameter.device + " auf " + parameter.vol + " geändert"); if (parameter.device === "ALL") { for (var speaker in googlehomes) { volDeviceUp (parameter.vol,googlehomes[speaker]); } } else volDeviceUp (parameter.vol,googlehomes[parameter.device]); }); //---------------------------- Soundfile ausgeben ------------------------------ /* setState("soundout",JSON.stringify( { sound: "doorbell", vol: 80, device: "Bad", night: true })); device: default: WZ. ALL spielt auf allen Lautsprechern night: default: true false: Ausgabe auch in der Nacht true: keine Ausgabe in der Nacht vol: default: die voreingestellt Lautstärke wird beibehalten */ on({id: "soundout", change: "any"}, function (obj) { var fs = require("fs"); function exists(path){ try{ fs.accessSync(path); } catch (err){ return false; } return true; } var parameter = JSON.parse(obj.state.val); var delay = 0; if (parameter.sound === undefined) { log("Soundout: kein Soundfile angegeben",'error'); return} if (!exists("/opt/iobroker/iobroker-data/files"+path_of_soundfiles + parameter.sound + ".mp3")) { log("Soundout: " + parameter.sound + ".mp3" + " fehlt.",'error'); return; } if (parameter.night === undefined) parameter.night = true; if (parameter.device === undefined) parameter.device = "WZ"; log("Spiele das File: " + parameter.sound + ".mp3" + " auf " + parameter.device); if (parameter.device === "ALL") { for (var speaker in googlehomes) { soundout(parameter.sound,parameter.vol,googlehomes[speaker]); } } else soundout(parameter.sound,parameter.vol,googlehomes[parameter.device]); }); /* //----------------- Speech to Text ------------------- setState("speechout2",'{"text":"Hallo","vol":"70","device":"WZ","night":false}'); oder setState("speechout2",JSON.stringify( { text: "Hallo Martin", vol:21, device: "ALL", night: false })); device: default: WZ. ALL spielt auf allen Lautsprechern night: default: true false: Ausgabe auch in der Nacht true: der Text wird in der Nacht gespeichert und nicht ausgegeben und dann später um 9 Uhr ausgegebenkeine Ausgabe. vol: default: die voreingestellt Lautstärke wird beibehalten Mit der Variable "block_speech" kann man die Sprachausgabe unterdrücken. Use Case: das lange Bellen für die Alarmierung */ createState("speechout_delayed",'') var block_speech = false; // avoid concurrency with soundout function texttospeach (txt,vol,night,device_address) { log("Sprachausgabe: "+ txt + " auf " + device_address + " mit Nachtparameter " + night); if (txt.length > 200) { txt = txt.substr(0, 199); log("speechout: Text für Sprachausgabe zu lang, wurde gekürzt",'warn'); } if ((new Date() >= getDateObject("8:59")) && (new Date() <= getDateObject("22:15")) || (night === false) ) { var delay = 0; if (vol !== undefined) {volDeviceUp (vol,device_address);delay = 3000} setTimeout(toGoogle(txt,device_address),delay); } else { setState("speechout_delayed",getState("speechout_delayed").val + "Die Sprachausgabe um " + formatDate(new Date(),'h') + " Uhr " + formatDate(new Date(),'m') + ' lautete ' + txt + '!'); } } on({id: "speechout2", change: "any"}, function (obj) { if (!block_speech && !getState("Reboot").val && getState("Presence").val) { var parameter = JSON.parse(obj.state.val); var delay = 0; if (parameter.text === undefined) {log("speechout: kein Text angegeben",'error'); return} if (parameter.text === "") {log("speechout: kein Text angegeben",'error'); return} if (parameter.night === undefined) parameter.night = true; if (parameter.device === undefined) parameter.device = "WZ"; if (parameter.device === "ALL") { for (var speaker in googlehomes) { texttospeach(parameter.text,parameter.vol,parameter.night,googlehomes[speaker]); } } else texttospeach(parameter.text,parameter.vol,parameter.night,googlehomes[parameter.device]); } else {log("speechout: Sprachausgabe wurde blockiert!",'error');} }); //--------------------------- Hundegebell ------------------------------------- createState("Hunde",false); on({id: "Hunde", change: "gt"}, function(obj) { function Bellen() { if (getState('Hunde').val === true) { setState("soundout",JSON.stringify( { sound: "hunde", device: "ALL", night: true })); } } block_speech = true; log("Bellen wurde ausgelöst"); var delay = 32000; setState("volume",JSON.stringify( { vol: 100, device: "ALL" })); setTimeout(function(){Bellen();},1000); setTimeout(function(){Bellen();},delay); setTimeout(function(){Bellen();},delay*2); setTimeout(function(){Bellen();},delay*3); }); on({id: "Hunde", change: "lt"}, function(obj) { // changed to false // Sound zuende spielen, aber volume auf 0 setState("volume",JSON.stringify( { vol: 0, device: "ALL" })); setStateDelayed("volume",JSON.stringify( { vol: getState('volume_back').val, device: "ALL" }),32000); }); //--------------------- einmal Doorbell ausgeben ------------------------------- createState("doorbell",false); //setState("doorbell",true); on({id: "doorbell", change: "any"}, function(obj) { setState("soundout",JSON.stringify( { sound: "doorbell", //scifi vol:20, device: "ALL", night: true })); setStateDelayed("volume",JSON.stringify( { vol: getState('volume_back').val, device: "ALL" }),10000); }); //------------ alle Audioausgaben der Nacht um 9 Uhr abspielen ---------------- schedule('0 9 * * *', function(obj){ if ((getState('speechout_delayed').val !== '') && (getState("Presence").val === true)) { setState("speechout2",'{"text":"' + "Das waren die Nachrichten in der Nacht!" + getState('speechout_delayed').val + '"}'); setState('speechout_delayed',''); } });</ip></txt></ip></lautstärke>
Telegram- und Email- Adapter Ersatz
Telegram und Email Adapter habe ich durch folgendes Script ersetzt.
Das Script kommuniziert mit dem ifttt Webhook Empfänger und verwendet, dann jeweils ein ifttt "Applet" zum Senden der Email bzw. zum Versenden der Botschaft an Telegram.
Beim Email will ifttt alle Zugriffsrechte auf das Email Konto. Es empfiehlt sich deshalb einen Email Account anzulegen.
`//----------------- Secrets einlesen ------------------------------------------- var secrets = JSON.parse(getState("secrets").val); //------------- Daten an IFTTT Webhook applet schicken ------------------------- function sendtoifttt(applet,data) { const https = require('https'); var postData = data; var options = { hostname: 'maker.ifttt.com', port: 443, path: '/trigger/'+ applet + '/with/key/' + secrets.iftttkey, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': postData.length } }; var req = https.request(options, (res) => { //console.log('statusCode:', res.statusCode); //console.log('headers:', res.headers); res.on('data', (d) => { //console.log(d); }); }); req.on('error', (e) => { console.error(e); }); req.write(postData); req.end(); } //----------------- Umlaute in HTML wandeln ------------------------------------ function HTMLEncode(str){ var char_names = { 160:'nbsp', 161:'iexcl', 220:'Uuml', 223:'szlig', 196:'Auml', 252:'uuml', }; var aStr = str.split(''), i = aStr.length, aRet = [], result; while (--i >= 0) { var iC = aStr[i].charCodeAt(); if (iC < 32 || (iC > 32 && iC < 65) || iC > 127 || (iC>90 && iC<97)) { if(char_names[iC]!== undefined) { aRet.push('&'+char_names[iC]+';'); } else { aRet.push('&#'+iC+';'); } } else { aRet.push(aStr[i]); } } result = aRet.reverse().join(''); result = result.replace(/ /g, " "); return result; } /*------------------ Telegram Message schicken --------------------------------- Aufruf setState("Botschaft",'Hallo World\n\Dies ist ein Test'); */ createState("Botschaft",''); on({id:"Botschaft", change: 'any'}, function (obj) { var data = "von UW: " + getState("Botschaft").val; sendtoifttt("telegram",'{"value1":"' + HTMLEncode(data) +'"}'); }); /*------------------- Email versenden ------------------------------------------ Funktioniert nur wenn ifttt VOLLEN Zugriff auf Email Account Habe deshalb ein zweites Gmail Konto eingerichtet Aufruf setState("Email",JSON.stringify( { to: "xxxx@outlook.com", subject:"Geburtstaggrüße", text: "Hallo Test\n\nIch hoffe es geht Dir gut!\n\nLiebe Grüße\n\n Testmann" })); */ createState("Email",''); on({id:"Email", change: 'any'}, function (obj) { var email_obj = JSON.parse(obj.state.val); email_obj.subject = HTMLEncode(email_obj.subject); email_obj.text = HTMLEncode(email_obj.text); var data = '{"value1":"' + email_obj.to +'","value2":"' + email_obj.subject +'","value3":"' + email_obj.text + '"}'; sendtoifttt("Email",data); });` **~~[b]~~Raspberry Adapter[/b]** Die für mich wichtigen Statusmeldung erzeuge ich mit folgendem Script. `~~[code]~~//------------ jede Minute Raspberry Statusinfos ermitteln --------------------- // Raspberry Adapter nicht verwendet, wegen RAM Begrenzung createState('UptimeFormatted_ioBroker',''); createState('UptimeFormatted_OS',''); createState("Pi_Temperature"); createState("Pi_Free_Mem"); createState("Pi_Available_Mem"); createState("FreeSDFormated",''); var piUptime; var CPU_Temp_block = false; var CPU_freemem_block = false; var CPU_SDMem_block = false; var CPU_availmem_block = false; //--- Nach Start einmalig die Uptime ermitteln. Danach Minütliches Hochzählen--- exec("awk '{print int($1/3600)\":\"int(($1%3600)/60)}' /proc/uptime", function (error, stdout, stderr) { var tokens = []; tokens = stdout.split(":"); var hours = parseInt(tokens[0]); var minutes = parseInt(tokens[1]); piUptime = hours*60 + minutes; }); schedule("* * * * *", function(obj){ //----------------------- Uptime ioBroker -------------------------------------- setState('UptimeFormatted_ioBroker',formatMinutes(getState("system.adapter.admin.0.uptime").val*1000)); //----------------------- Uptime OS hochzählen ---------------------------------- piUptime = piUptime + 1; setState('UptimeFormatted_OS',formatMinutes(piUptime*60000)); //---------- Bei geringem free Memory Warnung über Telegram senden ------------- var Pi_Free_Mem = getState("system.host.raspberrypi.freemem").val; if (CPU_freemem_block === false) { CPU_freemem_block = true; setTimeout(function() {CPU_freemem_block = false;}, 600*1000); if (Pi_Free_Mem < 15 ) { setState('Botschaft', "Achtung! Das freie Memory ist mit " + Pi_Free_Mem + " gering"); } } //------- Bei geringem available Memory Warnung über Telegram senden ------------ var CPU_Memavailable; exec('cat /proc/meminfo', function (error, stdout, stderr) { var tokens = stdout.split('kB'); CPU_Memavailable = (parseInt(tokens[2].substring(16, 25))/1000).toFixed(0); setState("Pi_Available_Mem",CPU_Memavailable); if (CPU_availmem_block === false) { CPU_availmem_block = true; setTimeout(function() {CPU_availmem_block = false;}, 600*1000); if (CPU_Memavailable < 30 ) { setState('Botschaft', "Achtung! Das verfügbarer Memory ist mit " + CPU_Memavailable + " MB gering"); } } }); //----------- Bei hoher CPU Temperatur Warnung über Telegram senden ------------ var CPU_Temp; exec('cat /sys/class/thermal/thermal_zone0/temp', function (error, stdout, stderr) { CPU_Temp = parseFloat(parseInt(stdout)/1000).toFixed(2); setState("Pi_Temperature",CPU_Temp); if (CPU_Temp_block === false) { CPU_Temp_block = true; setTimeout(function() {CPU_Temp_block = false;}, 600*1000); if (getState('Pi_Temperature').val > 65 ) { setState('Botschaft', "Achtung! Die CPU Temperatur ist mit " + getState('Pi_Temperature').val + " sehr hoch"); } } }); //------------------------- Free SD Card Space --------------------------------- exec('df -h /root', function (error, stdout, stderr) { var tokens = []; tokens = stdout.split('G'); var SDMemory_used = parseInt(tokens[1]); // used var SDMemory_free = parseInt(tokens[2]); // available setState("FreeSDFormated",SDMemory_free + ' GB ' +'(' + parseFloat(100*SDMemory_free/(SDMemory_free + SDMemory_used)).toFixed(0) + ' %)'); if (!CPU_SDMem_block ) { CPU_SDMem_block = true; setTimeout(function() {CPU_SDMem_block = false;}, 600*1000); if (SDMemory_free < 20 ) { setState('Botschaft', "Achtung! Speicher auf SD Karte ist mit " + SDMemory_free + " MB sehr niedrig"); } } }); }); [/code]` **~~[b]~~Backup[/b]** Statt des 'backitup' Adapter habe ich noch das "alte Backup Script hier aus dem Forum im Einsatz. Dabei mache ich kein Backup der piVCCU, weil die ohnehin so gut wie nie anfasse. `~~[code]~~//------------------------- Backup --------------------------------------------- // Backupscript basierend auf dem Script von // Kuddel: http://forum.iobroker.net/viewtopic.php?f=21&t=9861 und der // Erweiterung durch peoples: http://forum.iobroker.net/viewtopic.php?f=21&t=10110&hilit=backup var backup_pfad = secrets.backup.ftppath; var ftpuser = secrets.backup.ftpuser; var ftppasswd = secrets.backup.ftppasswd; var ftpip = secrets.backup.ftpip ; var logbackup = true; // Logging für Backup Vorgang var min_versions = 30; // Versionen für Min Backup im localen Folder var kom_versions = 4; // Version für Komplet Backup var bash_script = 'bash /opt/iobroker/backup.sh ';// Pfad zu backup.sh Datei createState("Backup_now_complete",false); createState("Backup_now_minimal",false); //----------- Manuell mit Vis Schalter Backups starten ------------------------- on({id: "Backup_now_complete", change: "gt"}, function (obj) { backup_erstellen('komplett','',kom_versions,ftpip,backup_pfad + 'full/',ftpuser,ftppasswd); }); on({id: "Backup_now_minimal", change: "gt"}, function (obj) { backup_erstellen('minimal','',min_versions,ftpip,backup_pfad + 'min/',ftpuser,ftppasswd); }); function backup_erstellen(typ, name, zeit, host, pfad, user, passwd) { exec((bash_script+' "'+typ+'|'+name+'|'+zeit+'|'+host+'|'+pfad+'|'+user+'|'+passwd+'"'), function(err, stdout, stderr) { if(logbackup){ if(err) log(stderr, 'error'); else log('exec: ' + stdout); } setState('Botschaft', typ + "es Backup am " + formatDate(new Date(), 'DD.MM.YYYY') +' um '+ formatDate(new Date(), 'hh:mm') + ' erstellt.'); }); } //--------- zeitlich getriggerte, wiederkehrende Backups ----------------------- // Jeden Tag um 23:00 Uhr wird ein minimales Backup erstellt schedule("0 23 * * 0", function(obj){ backup_erstellen('minimal','',min_versions,ftpip,backup_pfad + 'min/',ftpuser,ftppasswd); }); // am 1\. und 18\. des Monats um 1:30 Uhr Nacht wird ein vollständiges Backup erstellt schedule("00 1 1 * *", function(obj){ backup_erstellen('komplett','',kom_versions,ftpip,backup_pfad + 'full/',ftpuser,ftppasswd); });[/code]` **~~[b]~~Github Commit[/b]** Ich habe den 'js2fs' adapter installiert, damit ich meine Scripte auf GitHub spiegeln kann. Dabei starte ich "js2fs' nur dann, wenn ich ein GitHub Commit machen möchte. Danach wird der Adapter wieder gestoppt. Achtung: Dieses Script macht ab- und zu Zicken und ist nicht vollständig fehlerfrei. Feedback wäre fein. `~~[code]~~//------------------------- GitHub Commit und Push aus Vis --------------------- createState("Commit_Comment",''); on({id:"Commit_Comment", change: "any"},function(obj) { console.log("GitHub Commit started"); // j2fs einschalten und Grundsynchronisation aktivieren var adapt = getObject("system.adapter.js2fs.0"); adapt.common.enabled = true; adapt.common.basicSync = true; setObject("system.adapter.js2fs.0", adapt); // Nach einer Minute j2fs ausschalten adapt.common.enabled = false; setTimeout( function() { setObject("system.adapter.js2fs.0", adapt);}, 60000); // Nach einer Minunte und 10 Sekunden GitHub Synchronisation starten setTimeout( function() { exec("git -C /opt/iobroker/ferienwohnung/sources add . ", function (error, stdout, stderr) { if (!error) { console.log(stdout); exec("git -C /opt/iobroker/ferienwohnung/sources commit -m " + '"' + getState("Commit_Comment").val + '"', function (error, stdout, stderr) { if(!error) { console.log(stdout); exec("git -C /opt/iobroker/ferienwohnung/sources push https://"+ secrets.git.user + ":" + secrets.git.passwd + "@" + secrets.git.path, function (error, stdout, stderr) { if(!error) { console.log(stdout); } else console.log("Push Error: " + error); } ); } else console.log("Commit Error: " + error); } ); } else console.log("Add Error: " + error); } ); }, 70000); }); [/code]` Viel Spaß und Reuse auf eigene Gefahr :-))[/i][/i]