Vielleicht ist das Programm für den Einen oder Anderen hilfreich. Deshalb möchte ich es der Community zur Verfügung stellen.
Ziel - Was kann das Programm?
- Kaffeemaschine über Sprachassistenten ein- und ausschalten.
- Status und Hinweise der Kaffeemaschine soll über einen Sprachassistenten ausgegeben werden.
- Wechselnde Sprachausgaben für bestimmte Statusmeldungen.
- Favoriten (Profil) der Kaffeespezialitäten (Kaffee Crema, Cappuccino, …) mit unterschiedlichen Parametern (Kaffeemenge, Wassertemperatur, …) erstellen.
- Kaffeefavoriten per Sprachassistent ausgeben.
- Pflegestatus (Entkalken, Reinigen, Wasserfilter wechseln) bereitstellen für Ausgabe mit Sprachassistenten oder VIS.
Vorraussetzung
- Eine Home Connect kompatible Kaffeemaschine.
Getestet wurde das Script mit der Kaffeemaschine Siemens EQ.9 plus connect s500. Vermutlich funktioniert es auch mit ähnlichen Kaffeemaschinen von Siemens oder vielleicht auch mit Kaffeemaschinen von anderen Herstellern. Das wurde aber nicht getestet. Wenn ihr das Script benutzt, bitte um ein Feedback mit welcher Kaffeemaschine.
- Adapter cloudless-homeconnect
Der Adapter ist (noch) nicht im ioBroker Repository. Deshalb muss er über die Katze installiert werden.
https://github.com/eifel-tech/ioBroker.cloudless-homeconnect
Eine Instanz wird mit dem + am Adapter zugefügt.
Der Adapter steuert die Kaffeemaschine ohne Cloud wie es zum Beispiel für die Home Connect APP notwendig ist. Nur beim ersten Start der Instanz wird eine Konfiguration aus der Cloud geladen. Deshalb ist eine Anmeldung an der Home Connect Cloud nötig.
- Adapter iot
Für Geräte-Erstellung in Alexa.
Nach dem ersten Start des Scripts oder nach Änderung der Favoriten können über Alexa die neuen Geräte gesucht werden.
- Adapter alexa2
Für Sprachbefehle und Sprachausgabe.
Tipp: Wenn man für den Sprachbefehl „Alexa, Erichs Kaffee ein“ unschick findet, kann man eine Routine „Erichs Kaffee“ erstellen, was das entsprechende „Gerät“ einschaltet.
- Einstellung in der Javascript Instanz:
Kommando "setObject" erlauben - muss aktiviert sein.
Bedienung und Konfiguration
- Vor dem ersten Start bitte die Voraussetzungen prüfen.
- Im Programm im Abschnitt Settings die Variablen der Kanäle und Datenpunkte richtig setzen.
** In myConfig eventuell den Smartnamen nach eigenen Vorlieben ändern.
** In myProfile die Favoriten der Kaffeespezialitäten definieren.
** In myPowerState die Texte für Kaffeemaschine Ein/Aus definieren.
** In myStatus die Texte für die Statusmeldungen anpassen.
- Programm starten.
- Nach dem Start unter 0_userdata.0.Home-Connect.Kaffeemaschine.Profile die Kaffeespezialität konfigurieren.
Bekannte Probleme
- Betrifft Datenpunkt DisplayName für eine gespeicherte Kaffeespezialität
Ich vermute, dass dieser Wert im Display der Kaffeemaschine erscheinen soll. Tut es aber bei meiner Kaffeemaschine nicht. Eventuell könnte es bei einem anderen Model angezeigt werden.
Script Home-Connect-Kaffeemaschine.js:
//
// Home-Connect-Kaffeemaschine.js
//
// 09.10.2024 Version 1.0
// Initialversion
//
//
//
//--------------------------- Settings ------------------------
const hcDevice = "cloudless-homeconnect.0.SIEMENS-TI9558X1DE-xxxxxxxxxxxx"; // Kanal für Kaffemaschine im cloudless-homeconnect
const echoSpeak = "alexa2.0.Echo-Devices.xxxxxxxxxxxxxxxx.Commands.speak"; // speak-Datenpunkt des Echo-Device für die Sprachausgabe im alexa2
const dpUserdata = "0_userdata.0.Home-Connect.Kaffeemaschine"; // Namespace im Userdata
//
// Datenpunkte im 0_userdata
//
const myConfig = {
"row": [
{
"dp": "status-speak", // Datenpunkt für Statusmeldungen
"name": "Displaymeldung für Sprachassistent",
"read": true, "write": true,
"type": "string", "role": "value",
"value": "", "unit": "",
"smartName": ""
},
{
"dp": "maintenance-speak", // Datenpunkt für Pflegeanfrage
"name": "Status der Kaffeemaschine",
"read": true, "write": true,
"type": "string", "role": "value",
"value": "", "unit": "",
"smartName": ""
},
{
"dp": "kaffeemaschine", // Datenpunkt für Ein/Aus
"name": "Kaffemaschine Ein/Aus",
"read": true, "write": true,
"type": "boolean", "role": "switch",
"value": false, "unit": "",
"smartName": { de: "Kaffeemaschine, Kaffee" , // Smartname für iot zur Übergabe an Alexa
smartType: "SWITCH"}
}
]
}
//
// Datenpunkte für Profile (Favoriten) im 0_userdata
//
const myProfile = {
"row": [
{
"profile": "Erich1", // beliebiger Favoritenname
"DisplayName": "Erichs Kaffee Crema",
"Program": "Beverage_Coffee", // Kaffeespezialität aus cloudless-homeconnect.0.SIEMENS-TI9558X1DE-68A40E7DD334.Program
"smartName": { de: "Erichs Kaffee", // Smartname für iot zur Übergabe an Alexa
smartType: "SWITCH" },
},
{
"profile": "Erich2",
"DisplayName": "Erichs Cappuccino",
"Program": "Beverage_Cappuccino",
"smartName": { de: "Erichs Cappuccino",
smartType: "SWITCH" },
},
{
"profile": "Sonja1",
"DisplayName": "Sonjas Kaffee Crema",
"Program": "Beverage_Coffee",
"smartName": { de: "Sonjas Kaffee",
smartType: "SWITCH" },
},
{
"profile": "Sonja2",
"DisplayName": "Sonjas großer Kaffee Crema",
"Program": "Beverage_Coffee",
"smartName": { de: "Sonjas großer Kaffee",
smartType: "SWITCH" },
}
]
}
//
// Texte zum Anpassen für Ein/Aus
// Texte im Array mit Komma getrennt werden zufällig wiedergegeben.
//
const myPowerState ={
"2": ["Is <emphasis>schowieder</emphasis> Kaffeezeit!", "Is schowieder Kaffee!"], // On
"3": ["Servus.","An scheener Kaffee!"], // Standby
};
//
// Texte zum Anpassen der Statusmeldungen. Mögliche Status für andere Maschinen
// zu finden unter dem Datenpunkt deiner Kaffeemaschine:
// cloudless-homeconnect.0.SIEMENS-TI9558X1DE-xxxxxxxx.Event
// Texte im Array mit Komma getrennt werden zufällig wiedergegeben.
//
const myStatus = {
"AdjustGrindSetting": [""],
"BeanContainerEmpty": ["Bohnenbehälter ist leer. Bitte auffüllen."],
"BrewingUnitIsMissing": [""],
"CallHotline": [""],
"CleanBrewingUnit": ["Bitte Brüheinheit reinigen und einfetten."],
"CleanFillWaterTank": ["Bitte Wassertank reinigen."],
"CleanMilkTank": ["Bitte Milchbehälter reinigen."],
"CloseDoor": ["Bitte Türe schließen."],
"CoffeeOutletMissing": [""],
"CustomerServiceRequest": [""],
"DeactivatePlaylist": [""],
"DeviceDescalingBlockage": [""],
"DeviceDescalingOverdue": [""],
"DeviceIsToCold4C": [""],
"DeviceShouldBeCalcNCleaned": ["Bitte Kaffemaschine entkalken und reinigen."],
"DeviceShouldBeCleaned": ["Bitte Kaffeemaschine reinigen."],
"DeviceShouldBeDescaled": ["Bitte Kaffeemaschine entkalken."],
"DripTrayFull": ["Tropfschale ist voll. Bitte leeren."],
"DripTrayNotInserted": ["Bitte Tropfschale einsetzten."],
"EmptyDripTray": [""],
"EmptyDripTrayRemoveContainer": [""],
"EmptyMilkTank": ["Michlbehälter ist leer. Bitte auffüllen."],
"Enjoy": ["Genieße deinen Kaffee.","Möge Dein Kaffee stark und Dein Montag kurz sein.","Alles vor dem ersten Kaffee ist Notwehr.",
"Du könntest auf Kaffee verzichten. Aber du bist kein Aufgeber.","Guter Kaffee ist wie gute Musik – beides berührt die Seele.",
"Mit Kaffee und Humor kommt man allem Stress zuvor.","Es sprüht Dein Geist nur träge Funken, hast Du noch nicht Kaffee getrunken.",
"Ein guter Tag beginnt mit Kaffee und einem Lächeln.","Halte Kurs und trink Kaffee, dann wird das schon!","Wach zu werden und zu leben, beginnt mit Kaffee.",
"Jede Tasse Kaffee ist eine frische Chance.","Leben passiert, Kaffee hilft."],
"FillDescaler": [""],
"InsertWaterFilter": ["Bitte Wasserfilter einsetzen."],
"MilkContainerConnected": [""],
"MilkContainerRemoved": [""],
"MilkReminder": ["Ich erinnere dich, den Milchbehälter zu entfernen."],
"MilkStillOK": [""],
"MilkTubeRemoved": [""],
"NotEnoughPomaceCapacityForThisKindOfBeverage": [""],
"NotEnoughWaterForThisKindOfBeverage": ["Nicht genügend Wasser für dieses Getränk vorhanden. Bitte auffüllen."],
"PlaceContainerUnderOutlet": ["Bitte einen Behälter unter dem Auslass stellen."],
"PlaceEmptyGlassUnderOutlet": [""],
"PowerSupplyError": [""],
"PressOnOff": [""],
"ProgramFinished": ["Das Programm ist beendet. Du hast es geschafft."],
"RefillEmptyBeanContainer": ["Bitte Bohnenbehälter auffüllen."],
"RefillEmptyWaterTank": ["Bitte Wassertank auffüllen."],
"RefillRemovedMilkContainer": [""],
"RemoveContainerUnderOutlet": ["Bitte Behälter unter dem Auslass entfernen."],
"RemoveMilkContainer": ["Bitte Milchbehälter entfernen."],
"RemoveWaterFilter": ["Bitte Wasserfilter entfernen."],
"RestartAppliance": [""],
"ServiceProgramFinished": [""],
"SoftwareUpdateAvailable": ["Ein Softwareupdate ist verfügbar."],
"SoftwareUpdateSuccessful": ["Das Softwareupdate ist installiert."],
"SpecialRinsing": ["Spülen ist notwendig."],
"SwitchOffPower30sekBackOn": [""],
"SystemHasRunDry": ["Das System ist trocken gefahren. Bitte Wasser nachfüllen oder Filter entfernen."],
"ThrowCleaningDiscInTheDrawer": ["Bitte gib eine Reinigungstablette in das Fach für Pulverkaffee."],
"UnderOverVoltage": [""],
"WaterFilterShouldBeChanged": ["Der Wasserfilter muss gewechselt werden."],
"WaterTankEmpty": ["Der Wassertank ist leer. Bitte auffüllen."],
"WaterTankNearlyEmpty": ["Der Wassertank ist fast leer. Für das Spülen reicht es noch."],
"WaterTankNotInserted": ["Bitte Wassertank einsetzen."],
}
//--------------------------- Settings end ---------------------
const hcEvent = hcDevice+".Event";
const hcProgram = hcDevice+".Program";
const hcStatus = hcDevice+".Status";
const hcPowerState = hcDevice+".Setting.PowerState";
const dpProfile = dpUserdata+".Profile";
init();
async function init() {
await createMyConfig(myConfig, dpUserdata);
await createMyProfile(myProfile, dpProfile);
await sleep(5 * 1000); // 5 Sekunden Pause
//
// Datenpunkt für Start der Kaffeeausgabe im Profile triggern
//
on({ id: [].concat(Array.prototype.slice.apply($(dpProfile + ".*.Start"))), change: 'any' }, async (dp) => {
if (getState(dp.id).val) {
// Wenn Start = true
var length = dp.id.split(".").length;
var program = dp.id.split(".")[length - 2]; // Programmordnername
var parameter = dp.id.split(".").pop();
var srcFolder = dp.id.replace("." + parameter, ""); // Programmordner ID
// Datenpunkte im Quellverzeichnis/Userdata scannen
$(srcFolder + ".*").each(function (id, i) {
//console.log(id);
var parameter = id.split(".").pop();
// Parameter übertragen
if (parameter != "Start") {
var value = getState(id).val;
var hcProgramID = hcProgram + "." + program + "." + parameter;
setState(hcProgramID, value, true);
}
})
await sleep(1 * 1000); // 1 Sekunden Pause
var hcProgramID = hcProgram + "." + program + ".Start";
setState(hcProgramID, true, true);
}
});
//
// Datenpunkte für Event/Statusansagen triggern bei Änderung
//
const mySelector = $(hcEvent + ".*");
mySelector.on(async function (dp) { // triggert bei Wertänderung der Datenpunkte des Selectors
var event = dp.id.substring(hcEvent.length + 1,)
if (typeof myStatus[event] !== "undefined" && myStatus[event] != "") {
if (dp.state.val == 1) {
var text = await randomText(myStatus[event]);
setMyState(dpUserdata + "." + "status-speak", text);
}
} else {
console.log("Keine Übersetzung für: " + event);
}
});
//
// Pflege triggern
//
const maintenance = [hcEvent + ".DeviceDescalingOverdue", hcEvent + ".DeviceShouldBeCleaned",
hcEvent + ".DeviceShouldBeCalcNCleaned", hcEvent + ".WaterFilterShouldBeChanged",
hcStatus + ".BeverageCountdownDescaling",hcStatus + ".BeverageCountdownCleaning",
hcStatus + ".BeverageCountdownCalcNClean",hcStatus + ".BeverageCountdownWaterfilter"]
on({ id: maintenance, change: "ne" }, async (dp) => { // triggert bei Änderung der Datenpunkte
var maintenanceSpeak = ""
if(getState(hcEvent + ".DeviceDescalingOverdue").val){
maintenanceSpeak = maintenanceSpeak + "Entkalken ist notwendig. ";
} else if (getState(hcEvent + ".DeviceDescalingOverdue").val){
maintenanceSpeak = maintenanceSpeak + "Reinigen ist notwendig. ";
} else if (getState(hcEvent + ".DeviceShouldBeCalcNCleaned").val){
maintenanceSpeak = maintenanceSpeak + "Calc’nClean ist notwendig. ";
} else if (getState(hcEvent + ".WaterFilterShouldBeChanged").val){
maintenanceSpeak = maintenanceSpeak + "Wasserfilter wechseln ist notwendig. ";
} else {
maintenanceSpeak = "Keine Pflegemaßnahmen für die Kaffeemaschine notwendig.";
}
// Calc’nClean
let dummy = getState(hcStatus + ".BeverageCountdownCalcNClean").val;
let countCups = dummy;
let nextMaitenance = "Calc’nClean notwendig in "+countCups+" Tassen."
// Entkalken
dummy = getState(hcStatus + ".BeverageCountdownDescaling").val;
if (dummy < countCups){
countCups = dummy;
nextMaitenance = "Entkalken notwendig in "+countCups+" Tassen."
}
// Reinigen
dummy = getState(hcStatus + ".BeverageCountdownCleaning").val;
if (dummy < countCups){
countCups = dummy;
nextMaitenance = "Reinigen notwendig in "+countCups+" Tassen."
}
// Wasserfilter
dummy = getState(hcStatus + ".BeverageCountdownWaterfilter").val;
if (dummy < countCups){
countCups = dummy;
nextMaitenance = "Wasserfilter wechseln in "+countCups+" Tassen."
}
maintenanceSpeak = maintenanceSpeak + " " +nextMaitenance;
setMyState(dpUserdata + "." + "maintenance-speak", maintenanceSpeak);
});
//
// PowerState triggern
//
on({ id: hcPowerState, change: "ne" }, async (dp) => { // triggert bei Änderung der Datenpunkte
if (typeof myPowerState[dp.state.val] !== "undefined" && myPowerState[dp.state.val] != "") {
var text = await randomText(myPowerState[dp.state.val]);
setState(echoSpeak, text, false);
}
});
//
// Datenpunkt für Kaffeemaschine triggern bei Aktualisierung
//
on({ id: dpUserdata + ".kaffeemaschine", change: "any" }, async (dp) => { // triggert bei Aktualisierung der Datenpunkte
if (dp.state.val) {
setState(hcPowerState, 2, true); // On
} else {
setState(hcPowerState, 3, true); // Standby
}
});
//
// Datenpunkt für Speak triggern bei Aktualisierung
//
on({ id: dpUserdata + ".status-speak", change: "any" }, async (dp) => { // triggert bei Aktualisierung der Datenpunkte
setState(echoSpeak, dp.state.val, false);
});
}
/**
* Objekte erzeugen
* @param {Object} MyConfig Json-Objekt
* @param {String} dpUserdata Workspace
*/
async function createMyConfig(MyConfig, dpUserdata) {
// Config scannen
for (let i = 0; i < MyConfig.row.length; i++) {
//console.log('Erzeuge: '+myConfig.row[i].dp);
await createMyState(dpUserdata+"."+myConfig.row[i].dp, myConfig.row[i].name, myConfig.row[i].name, myConfig.row[i].read, myConfig.row[i].write, myConfig.row[i].type, myConfig.row[i].role, myConfig.row[i].unit, myConfig.row[i].smartName, myConfig.row[i].value);
}
}
/**
* Profile Objekte erzeugen
* @param {Object} myProfile Json-Objekt
* @param {String} dpProfile Workspace
*/
async function createMyProfile(myProfile, dpProfile) {
// Profile scannen
for (let i = 0; i < myProfile.row.length; i++) {
//console.log('Erzeuge Profil: '+myProfile.row[i].profile);
var srcid = hcProgram+"."+myProfile.row[i].Program;
var dstid = dpProfile+"."+myProfile.row[i].profile+"."+myProfile.row[i].Program;
// Datenpunkte im Quellverzeichnis scannen
$(srcid+".*").each(async function(id) {
//console.log("id: "+id);
var obj = getObject(id);
if(obj && obj.type === 'state') {
var lastElement = id.split(".").pop(); // letztes Element des Datenpunktes
setObject(dstid+"."+lastElement, obj);
if ( !existsState(dstid+"."+lastElement)) {
if (lastElement == "Start"){
obj.common.smartName = myProfile.row[i].smartName;
}
setTimeout(function() {
setState(dstid+"."+lastElement, obj.common.def,true);
}, 200);
}
};
})
await sleep(1*1000);
// console.log(dstid+".DisplayName: ->"+myProfile.row[i].DisplayName);
setState(dstid+".DisplayName", myProfile.row[i].DisplayName,true);
}
}
/**
* Zufälliger Text
* @param {String} text - Array aus mehreren Texten
* @returns {String} return - gibt einen zufälligen Text aus dem Array zurück
*/
async function randomText(text) {
var length = text.length;
var min = 0;
var max = length-1;
var zahl = Math.floor(Math.random() * (max - min + 1)) + min;
return text[zahl];
}
/**
* Objekt setzen
* @param {String} state Objektpfad
* @param {any} value Inhalt
*/
async function setMyState(state, value) {
setState(state, value, true);
}
/**
* Objekt erzeugen
* @param {String} state Objektpfad
* @param {String} name Name des Objektes
* @param {String} desc Beschreibung des Objektes
* @param {String} type Type des Objektes string,number
* @param {Boolean} read Leserechte des Objektes
* @param {Boolean} write Schreibrechte des Objektes
* @param {String} role Rolle des Objektes button,...
* @param {String} unit Rolle des Objektes button,...
* @param {Object} smartName Smartname für Alexa
* @param {any} value Inhalt
*/
async function createMyState(state, name, desc, read, write, type, role, unit, smartName, value) {
if (type == "number") {
//value = Number(value);
await createStateAsync(state, 0, {
name: name,
desc: desc,
read: read,
write: write,
type: type,
role: role,
unit: unit
});
} else {
if (smartName == "") {
// ohne Smartnamen
await createStateAsync(state, value, {
name: name,
desc: desc,
read: read,
write: write,
type: type,
role: role,
unit: unit
});
} else {
await createStateAsync(state, value, {
// mit Smartnamen
name: name,
desc: desc,
read: read,
write: write,
type: type,
role: role,
unit: unit,
smartName: smartName
});
}
}
}
Benutzung auf eigene Gefahr.
Support kann ich nur eingeschränkt bieten.
Über Feedback würde ich mich freuen.
View
Als kostenlose Zugabe eine View für die Vis
Voraussetzung
- Adapter Material Design Widgets
Für mich das flexibelste und beste Material Widget.
- Vis 1
Leider findet sich niemand, der die Material Design Widgets in Vis 2 anpasst. Das ist sehr schade. Für mich ist das unverzichtbar, deshalb muss ich bei Vis 1 bleiben.
View CardHaushaltEGKücheKaffee.json:
CardHaushaltEGKücheKaffee.json