Fragen zum Setzen von Capabilities aus HomeyScript / COP-Berechnung Klimaanlage

Moin,

vielleicht stehe ich auf dem Schlauch, aber ich kriege es nicht hin mein eigens erzeugtes Gerät mit der App “Device Capabilities” aus HomeyScript zu beschreiben. Hat da jemand ein best practice?

Natürlich mache ich das ganze mit ChatGPT. Es geht um die Berechnung des COP-Wertes einer Klimaanlage.

Vielen Dank.

Gruß
Robin

Das ist eine zu allgemeine Frage mit zu wenig Hintergrund, um sie zu beantworten.

Naja, ich möchte gerne 3 number capabilities vom erzeugten Gerät mit Werten aus dem Skript füllen. Hier das Skript:

'use strict';

////////////////////////////////////////////////////////////////////////////////
// KOPFBEREICH: Konstanten und Mapping
////////////////////////////////////////////////////////////////////////////////

// Hersteller‐Luftströme (Heizbetrieb SRK50ZS‐WF) in m³/min pro Stufe
const AIRFLOW_MAP_M3_MIN = {
  'highest': 13.9,  // P-Hi
  'high':    11.2,  // Hi
  'low':      9.1,  // Me
  'lowest':   7.4   // Lo
};

// Spezifische Wärmekapazität Luft (J/(kg·K))
const CP_AIR      = 1005;
// Luftdichte (kg/m³)
const AIR_DENSITY = 1.2;

// Keys für Homey.settings (im Flow-Kontext)
const SUM_KEY          = 'cop_sum';
const COUNT_KEY        = 'cop_count';
const LAST_AIRFLOW_KEY = 'cop_last_airflow_m3s';

// IDs anpassen:
const INDOOR_ID        = '094474a6-7a35-404a-8f35-4f1f280b0cbc'; // Device-ID der Inneneinheit
const SHELLY_ID        = 'cad4c083-e05f-4567-8fbd-35273844b677'; // Device-ID des Shelly (Power-Messung)
const VIRT_DEVICE_ID   = '829d64bd-8cb4-4bb3-b0ae-165018899811'; // Device-ID „COP“

// Capability-IDs im virtuellen „COP“-Gerät
const CAP_THERM_POWER  = 'measure_power.number3';
const CAP_INSTANT_COP  = 'devicecapabilities_number-custom_11.number1';
const CAP_AVG_COP      = 'devicecapabilities_number-custom_12.number2';

////////////////////////////////////////////////////////////////////////////////
// Hilfsfunktion: Heiz-Luftstrom (String) → m³/s
////////////////////////////////////////////////////////////////////////////////
function mapAirflowToM3s(airflowString) {
  if (!airflowString) return null;
  const key = airflowString.toLowerCase();
  if (key === 'auto') return null;
  const m3_min = AIRFLOW_MAP_M3_MIN[key];
  return (m3_min != null) ? (m3_min / 60) : null;
}

////////////////////////////////////////////////////////////////////////////////
// Hauptfunktion
////////////////////////////////////////////////////////////////////////////////
async function run() {
  // Prüfen, ob Homey.settings existiert → Flow-Kontext
  const isFlowContext = (typeof Homey.settings === 'object' && typeof Homey.settings.get === 'function');
  if (!isFlowContext) {
    console.log('▶ Script läuft im Developer Scripts (kein Flow). Nur Instant-COP wird ausgegeben.\n');
  }

  // 1) Messwerte einlesen
  let airflowString, tempIn, tempOut, powerInput;
  try {
    airflowString = await Homey.devices.getCapabilityValue({
      deviceId:     INDOOR_ID,
      capabilityId: 'fan_speed'
    });
    // Indoor-Temperatur
    tempIn     = await Homey.devices.getCapabilityValue({
      deviceId:     INDOOR_ID,
      capabilityId: 'measure_temperature'
    });
    // Außentemperatur
    tempOut    = await Homey.devices.getCapabilityValue({
      deviceId:     INDOOR_ID,
      capabilityId: 'measure_temperature.outdoor'
    });
    powerInput = await Homey.devices.getCapabilityValue({
      deviceId:     SHELLY_ID,
      capabilityId: 'measure_power'
    });
  } catch (err) {
    console.error('‼ Keine gültigen Device-IDs oder Capabilities gefunden. Abbruch.', err.message);
    return;
  }

  console.log(`• Lüfterstufe: ${airflowString}`);
  console.log(`• Temperatur Innen: ${tempIn} °C`);
  console.log(`• Temperatur Außen: ${tempOut} °C`);
  console.log(`• Shelly Power: ${powerInput} W`);

  // 2) Luftstrom (m³/s) berechnen oder letzten festen Wert holen
  let airflow_m3s = mapAirflowToM3s(airflowString);
  if (airflow_m3s !== null) {
    if (isFlowContext) {
      await Homey.settings.set(LAST_AIRFLOW_KEY, airflow_m3s);
    }
    console.log(`→ Fester Luftstrom (m³/s): ${airflow_m3s.toFixed(4)}`);
  } else {
    if (isFlowContext) {
      const last = await Homey.settings.get(LAST_AIRFLOW_KEY);
      if (last != null) {
        airflow_m3s = last;
        console.log(`→ Auto: letzter bekannter fester Luftstrom (m³/s): ${airflow_m3s.toFixed(4)}`);
      } else {
        console.log('‼ Auto-Modus, aber kein gespeicherter fester Luftstrom. COP-Abbruch.');
        return;
      }
    } else {
      console.log('‼ Auto-Modus im Script-Editor: kein vorheriger Wert vorhanden. COP-Abbruch.');
      return;
    }
  }

  // 3) Thermische Leistung (W) berechnen: Q̇ = ρ · V̇ · cp · ΔT
  const deltaT   = tempIn - tempOut;
  const qThermal = airflow_m3s * AIR_DENSITY * CP_AIR * deltaT;
  console.log(`→ Thermische Leistung: ${qThermal.toFixed(1)} W`);

  // 4) Instant-COP
  const instantCOP = (powerInput > 0) ? (qThermal / powerInput) : 0;
  console.log(`→ Instant-COP: ${instantCOP.toFixed(2)}\n`);

  // 5) Falls Flow-Kontext, Durchschnitt berechnen und ins virtuelle Gerät schreiben
  if (isFlowContext) {
    // a) Summe + Count aus Settings lesen
    let sum   = await Homey.settings.get(SUM_KEY)   || 0;
    let count = await Homey.settings.get(COUNT_KEY) || 0;

    // b) Aktualisieren
    sum   += instantCOP;
    count += 1;
    await Homey.settings.set(SUM_KEY, sum);
    await Homey.settings.set(COUNT_KEY, count);

    // c) Durchschnitt berechnen
    const avgCOP = sum / count;
    console.log(`→ Durchschnitts-COP über ${count} Messungen: ${avgCOP.toFixed(2)}`);

    // d) Werte ins virtuelle Gerät schreiben (direkt, kein makeCapabilityInstance!)
    let device;
    try {
      device = await Homey.devices.getDevice({ id: VIRT_DEVICE_ID });
    } catch (err) {
      console.error('‼ Virtuelles Gerät nicht gefunden:', err.message);
      return;
    }

    try {
      await device.setCapabilityValue(CAP_THERM_POWER, Number(qThermal.toFixed(1)));
      console.log('✓ Thermische Leistung geschrieben');
    } catch (err) {
      console.error('‼ Fehler beim Schreiben therm. Leistung:', err.message);
    }

    try {
      await device.setCapabilityValue(CAP_INSTANT_COP, Number(instantCOP.toFixed(2)));
      console.log('✓ Instant-COP geschrieben');
    } catch (err) {
      console.error('‼ Fehler beim Schreiben Instant-COP:', err.message);
    }

    try {
      await device.setCapabilityValue(CAP_AVG_COP, Number(avgCOP.toFixed(2)));
      console.log('✓ Durchschnitts-COP geschrieben\n');
    } catch (err) {
      console.error('‼ Fehler beim Schreiben Durchschnitts-COP:', err.message);
    }
  } else {
    console.log('ℹ️ Script im Console-Modus beendet.');
  }
}

// Skript ausführen
run().catch(err => console.error('‼ Ungefangener Fehler:', err.message));

Euhhhh, ich glaube, das ist es, was du meinst, einen Wert in ein Feld eines “erweiterten virtuellen Geräts” schreiben?

Die Nummer bei Gerät ist die Geräte-ID
Hier findest du sie:
https://tools.developer.homey.app/tools/devices

Oder,
finde es mit diesem Skript:

// Change "my_device_name" into device name you want to find the ID of
const deviceName = "my_device_name";
// End of user input

const devices = await Homey.devices.getDevices();

for (const device of Object.values(devices)) {  
  if (device.name === deviceName) {
    // Return some properties. Just remove what you don't need
    log(`\nName:\t\t ${device.name} \nZone:\t\t ${device.zoneName} \nID:\t\t ${device.id} \nDriver:\t\t ${device.driverId} \nCapabilities:\t ${device.capabilities} \nBattery:\t ${device.energyObj.batteries} \nNote\t\t ${device.note}`);
    // To show ALL device data, uncomment the next lime:
    //log(device);
  }
};
return(true);

Also das hat alles nicht geklappt, ich mache jetzt folgendes:

Ich schreibe die Werte in Logik-Variablen und lasse anschließend einen Advanced Flow laufen mit dem Trigger, wenn sich die Logik-Variable COP_actual verändert, dass er anschließend das Custom-Gerät “COP” von der Device Capabilities-App mit den Werten der Variablen aktualisiert. Das funktioniert ohne Probleme. Ein normaler Flow startet das Homey-Script alle 30 Sekunden.

Hier nochmal das Skript:

'use strict';

// Lüfterstufen-Mapping
const AIRFLOW_MAP_M3_MIN = {
  'highest': 13.9,
  'high':    11.2,
  'low':      9.1,
  'lowest':   7.4
};

const CP_AIR      = 1005;
const AIR_DENSITY = 1.2;
const DEFAULT_AIRFLOW_M3S = AIRFLOW_MAP_M3_MIN["high"] / 60;

const INDOOR_ID  = '094474a6-7a35-404a-8f35-4f1f280b0cbc';
const SHELLY_ID  = 'cad4c083-e05f-4567-8fbd-35273844b677';

// Hilfsfunktion: Heiz-Luftstrom (String) → m³/s
function mapAirflowToM3s(airflowString) {
  if (!airflowString) return null;
  const key = airflowString.toLowerCase();
  if (key === 'auto') return null;
  const m3_min = AIRFLOW_MAP_M3_MIN[key];
  return (m3_min != null) ? (m3_min / 60) : null;
}

// Hilfsfunktionen: Logikvariablen lesen & schreiben (deine Methode)
async function ReadLogicVar(name){
  let Logic = await Homey.logic.getVariables();
  let idArr = Object.entries(Logic);
  let filtered = idArr.filter(([key,value]) => value.name==name);
  let [[,{id:varID}]] = filtered;
  var varArr = await Homey.logic.getVariable({id:varID});
  return varArr.value;
}

async function WriteLogicVar(name,value){
  let Logic = await Homey.logic.getVariables();
  let idArr = Object.entries(Logic);
  let filtered = idArr.filter(([key,value]) => value.name==name);
  let [[,{id:varID}]] = filtered;
  var varArr = await Homey.logic.getVariable({id:varID});
  await Homey.logic.updateVariable({id:varID, variable:{value:value}});
  return true;
}

async function run() {
  // 1. Zuerst "onoff" prüfen
  let isOn = false;
  try {
    isOn = await Homey.devices.getCapabilityValue({
      deviceId:     INDOOR_ID,
      capabilityId: 'onoff'
    });
  } catch (err) {
    console.error('‼ Fehler beim Lesen der onoff-Capability:', err.message);
    return;
  }

  if (!isOn) {
    console.log("Klimaanlage ist aus (onoff: false) – keine Berechnung, keine Variablen-Änderung.");
    return;
  }

  // Messwerte holen
  let airflowString, tempIn, tempOut, powerInput;
  try {
    airflowString = await Homey.devices.getCapabilityValue({
      deviceId:     INDOOR_ID,
      capabilityId: 'fan_speed'
    });
    tempIn     = await Homey.devices.getCapabilityValue({
      deviceId:     INDOOR_ID,
      capabilityId: 'measure_temperature'
    });
    tempOut    = await Homey.devices.getCapabilityValue({
      deviceId:     INDOOR_ID,
      capabilityId: 'measure_temperature.outdoor'
    });
    powerInput = await Homey.devices.getCapabilityValue({
      deviceId:     SHELLY_ID,
      capabilityId: 'measure_power'
    });
  } catch (err) {
    console.error('‼ Gerätefehler:', err.message);
    return;
  }

  // Luftstrom bestimmen
  let airflow_m3s = mapAirflowToM3s(airflowString);
  if (airflow_m3s == null) airflow_m3s = DEFAULT_AIRFLOW_M3S;

  const deltaT   = tempIn - tempOut;
  const qThermal = airflow_m3s * AIR_DENSITY * CP_AIR * deltaT;
  const instantCOP = (powerInput > 0) ? (qThermal / powerInput) : 0;

  // Aktuelle Werte in Logik schreiben
  await WriteLogicVar("COP_actual", Number(instantCOP.toFixed(2)));
  await WriteLogicVar("COP_thermal", Number(qThermal.toFixed(1)));

  // Summe und Zähler auslesen
  let sum = Number(await ReadLogicVar("COP_sum")) || 0;
  let count = Number(await ReadLogicVar("COP_count")) || 0;

  // Neue Werte berechnen
  sum += Number(instantCOP);
  count += 1;
  let avg = sum / count;

  // Neue Werte schreiben
  await WriteLogicVar("COP_sum", sum);
  await WriteLogicVar("COP_count", count);
  await WriteLogicVar("COP_average", Number(avg.toFixed(2)));

  // Logging
  console.log(`→ COP_actual: ${instantCOP.toFixed(2)}`);
  console.log(`→ COP_thermal: ${qThermal.toFixed(1)} W`);
  console.log(`→ COP_sum: ${sum}, COP_count: ${count}`);
  console.log(`→ COP_average: ${avg.toFixed(2)}`);
}

run().catch(err => console.error('‼ Ungefangener Fehler:', err.message));