Pittini / iobroker-nodemihome

Steuerung von bisher nicht unterstützten Xiaomi Geräten in Iobroker via node-mihome
MIT License
27 stars 15 forks source link

Cannot read property 'forEach' of undefined #59

Open w-marco opened 2 years ago

w-marco commented 2 years ago

Moin, erstmal tolles Skript. leider scheitert meine Einrichtung des Mi Smartfan 2. Ich hab die Def Datei eingefuegt und ioBroker neugestartet. Er findet diese auch, aber bekommt dann den Error aus dem Title.

Hier mal ein Log:

18:53:55.718    info    javascript.0 (31346) Start javascript script.js.common.MiHomeAll
18:53:56.952    info    javascript.0 (31346) script.js.common.MiHomeAll: Starting AllMyMi V.0.2.29
18:53:56.957    info    javascript.0 (31346) script.js.common.MiHomeAll: Reaching init
18:53:57.144    info    javascript.0 (31346) script.js.common.MiHomeAll: registered 0 subscriptions and 0 schedules
18:53:58.274    info    javascript.0 (31346) script.js.common.MiHomeAll: Retrieving your in de registered MiHome Devices
18:53:58.431    info    javascript.0 (31346) script.js.common.MiHomeAll: Found 1 MiHome Devices, those are:
18:53:58.432    info    javascript.0 (31346) script.js.common.MiHomeAll: Mi Smart Standing Fan 2
18:53:58.433    info    javascript.0 (31346) script.js.common.MiHomeAll: Now searching for supported Devices...
18:53:58.434    info    javascript.0 (31346) script.js.common.MiHomeAll: Device Mi Smart Standing Fan 2 is supported, creating DataPoints if necessary
18:53:58.435    info    javascript.0 (31346) script.js.common.MiHomeAll: Reaching PrepareDeviceDps, did=ID model=dmaker.fan.p18
18:53:58.437    info    javascript.0 (31346) script.js.common.MiHomeAll: Reaching CreateStates()
18:53:58.616    info    javascript.0 (31346) script.js.common.MiHomeAll: 17 States created, now setting up channels!
18:53:58.618    info    javascript.0 (31346) script.js.common.MiHomeAll: Reaching main
18:53:58.619    info    javascript.0 (31346) script.js.common.MiHomeAll: Reaching WriteGenericDpValues()
18:53:58.620    info    javascript.0 (31346) script.js.common.MiHomeAll: Reaching CreateDevices
18:53:58.621    info    javascript.0 (31346) script.js.common.MiHomeAll: Now creating device for dmaker.fan.p18 / 409699151 / 192.168.30.27 / TOKEN / 10000
18:53:58.624    info    javascript.0 (31346) script.js.common.MiHomeAll: Created device {"_events":{},"_eventsCount":0,"_maxListeners":100,"id":"ID","address":"192.168.30.27","token":"TOKEN","protocol":"local","refresh":10000,"_properties":{},"_propertiesToMonitor":["fan:on","fan:mode","fan:fan-level","fan:horizontal-swing","fan:horizontal-angle","fan:status","alarm:alarm","motor-controller:motor-control","physical-controls-locked:physical-controls-locked","off-delay-time:off-delay-time"],"_miotSpec":null,"_miotSpecType":"urn:miot-spec-v2:device:fan:0000A005:dmaker-p18:1","model":"dmaker.fan.p18","setter":{},"definition":{"info":[{"id":"localip","initial":"","forceCreation":false,"common":{"read":true,"write":true,"name":"Ip Adress","type":"string","role":"value","def":""}},{"id":"token","initial":"","forceCreation":false,"common":{"read":true,"write":true,"name":"Token","type":"string","role":"value","def":""}},{"id":"did","initial":"","forceCreation":false,"common":{"read":true,"write":true,"name":"Device Id","type":"string","role":"value","def":""}},{"id":"model","initial":"","forceCreation":false,"common":{"read":true,"write":true,"name":"Model","type":"string","role":"value","def":""}},{"id":"rssi","initial":0,"forceCreation":false,"common":{"read":true,"write":false,"name":"rssi","type":"number","role":"value.rssi","def":0}},{"id":"name","initial":"","forceCreation":false,"common":{"read":true,"write":true,"name":"Name","type":"string","role":"value","def":""}},{"id":"isOnline","initial":false,"forceCreation":false,"common":{"read":true,"write":true,"name":"Is online","type":"boolean","role":"value","def":false}}],"model":"dmaker.fan.p18","description":"Mi Smart Standing Fan 2","setter":{},"common":[{"name":"fan.on","type":"boolean","role":"switch","read":true,"write":true},{"name":"fan.mode","type":"number","role":"switch","read":true,"write":true,"min":0,"max":1,"states":{"0":"Straight Wind","1":"Natural Wind"}},{"name":"fan.fan-level","type":"number","role":"switch","read":true,"write":true,"min":1,"max":4,"states":{"1":"Slow","2":"Middle","3":"High","4":"Turbo"}},{"name":"fan.horizontal-swing","type":"boolean","role":"switch","read":true,"write":true},{"name":"fan.horizontal-angle","type":"number","role":"switch","read":true,"write":true,"min":30,"max":140,"unit":"°","states":{"30":"30°","60":"60°","90":"90°","120":"120°","140":"140°"}},{"name":"fan.status","type":"number","role":"indicator","read":true,"write":false,"min":1,"max":100},{"name":"alarm.alarm","type":"boolean","role":"switch","read":true,"write":true},{"name":"motor-controller.motor-control","type":"number","role":"switch","read":false,"write":true,"min":0,"max":2,"states":{"0":"None","1":"Left","2":"Right"}},{"name":"physical-controls-locked.physical-controls-locked","type":"boolean","role":"switch","read":true,"write":true,"min":false,"max":true},{"name":"off-delay-time.off-delay-time","type":"number","role":"switch","read":true,"write":true,"min":0,"max":480,"unit":"m"}]},"firstrun":true,"rssi":0,"isOnline":true} now fetching data
18:53:59.095    error   javascript.0 (31346) script.js.common.MiHomeAll: TypeError: Cannot read property 'forEach' of undefined
18:53:59.100    error   javascript.0 (31346) at CreateDevices (script.js.common.MiHomeAll:924:17)
18:53:59.101    error   javascript.0 (31346) at main (script.js.common.MiHomeAll:842:5)

Leider erschließt sich mir nicht wieso das passiert.

Pittini commented 2 years ago

Nach dem einfügen der Datei auch den JS Adapter neu gestartet?

w-marco commented 2 years ago

Sicher, auch versucht komplett ioBroker neu zu starten. Hat beides nicht geholfen.

Pittini commented 2 years ago

Für mich grad nicht wirklich nachvollziehbar. Kanns auch nicht testen, hab das Ding ja nicht. Aber setz Dich doch mal mit dem ersteller der Def Datei in Verbindung, evtl. weis der was: https://github.com/Pittini/iobroker-nodemihome/issues/37

w-marco commented 2 years ago

Ich hab es in dem Issue mal versucht, eine Email oder sowas von ihm konnte ich nicht finden. Trotzdem seltsam dieser Fehler, ich verstehe aber auch nicht an welcher Stelle es kaputt geht, hast du da keine Idee ? ich hab das Teil ja hier, also wenn es Ideen zum testen gibt kann ich gerne dabei helfen.

Pittini commented 2 years ago

Ich hab es in dem Issue mal versucht, eine Email oder sowas von ihm konnte ich nicht finden. Trotzdem seltsam dieser Fehler, ich verstehe aber auch nicht an welcher Stelle es kaputt geht, hast du da keine Idee ? ich hab das Teil ja hier, also wenn es Ideen zum testen gibt kann ich gerne dabei helfen.

Du kannst versuchen die Def Datei erstmal auf was sicheres wie on/off zu reduzieren und dann nach und nach wieder Funktionen dazuzunehmen bis Du wieder den Fehler hast, dann weiste was das Problem verursacht. Also den Teil:

    this._propertiesToMonitor = [
      'fan:on',
      'fan:mode',
      'fan:fan-level',
      'fan:horizontal-swing',
      'fan:horizontal-angle',
      'fan:status',
      'alarm:alarm',
      'motor-controller:motor-control',
      'physical-controls-locked:physical-controls-locked',
      'off-delay-time:off-delay-time'
      ];

ändern zu:

    this._propertiesToMonitor = [
      'fan:on'
      ];

Danach nicht vergessen den JS Controller nach jeder Änderung neuzustarten.

Wennde den Fehler schon bei on/off haben solltest, liegts an was anderem, was Du aber nicht testen kannst weil Du scheinbar nur das eine Xiaomi Gerät hast.

w-marco commented 2 years ago

Ich hab das mal getan und weiterhin den selben Fehler. Ich hab dann aber mal mit python-miio versucht mit dem Ventilator zu reden und das geht, also liegt es nicht am Netzwerk oder dem Gerät. Ich konnte ihn damit auch steuern.

Der Fehler muss also im Script oder der Def Datei liegen. Vielleicht hilft das ja weil es das Debugging sehr einschränkt und es kein äußerer Fehler ist. Wie gesagt, ich stehe gerne (auch im Discord oder sowas) zum testen bereit.

cacherwolf commented 2 years ago

Hallo, Danke für das tolle Skript.

Ich habe auch den Mi Smart Fan 2 und genau die gleiche Fehlermeldung.

Gibt es inzwischen schon eine Lösung?

ChristopherGier commented 2 years ago

Hey, die Lösung ist relativ einfach: in dem device File vom importierten Modul ist ein Fehler. Wenn die Attribute nicht gefüllt sind checkt er nicht ob das Ding leer ist sondern versucht gleich drüber zu interieren - das führt zu besagtem Problem. Kann man mit optional chaining lösen. Also quasi Variable?.forEach start Variable.forEach Das hat's bei mir gelöst. Dazu noch ein paar Veränderungen an dem Schema, da das nicht in Ganze zu passen scheint Ich kann morgen Mal genauer schreiben falls es euch interessiert, bin gerade noch unterwegs

cacherwolf commented 2 years ago

Das wäre super wenn du das machen würdest, vielen Dank :-)

ChristopherGier commented 2 years ago

Sehr gern, hoffe es klappt bei dir dann auch. Als erstes schau dir Mal die folgende Zeile bei dir in den Modemodules an: https://github.com/maxinminax/node-mihome/blob/e60c287d9bb2d9ba9123543d92ec481be5a62b5e/lib/device-miio.js#L101

Wenn du die änderst in: props?.forEach((prop, i) => { Sollte der Fehler nicht mehr auftauchen und das an/ausschalten sollte funktionieren. Kannst du ja vielleicht Mal testen. Falls es klappt kannst du die anderen Bedienungsmöglichkeiten Mal durchtesten und sagen welche klappen und welche nicht - bin mir nicht ganz sicher ob das nur bei mir nicht funktioniert hatte

cacherwolf commented 2 years ago

Vielen Dank für deine Mühe, aber leider hat es nicht geklappt. Ich habe die Änderungen in Zeile 101 gemacht, leider kommt die gleiche Fehlermeldung wie vorher und das an/ausschalten funktioniert auch nicht. Es werden zwar die Datenpunkte angelegt aber danach nicht mehr aktualisiert.

2022-07-31 12:53:42.254 - error: javascript.0 (1414) script.js.Xiaomi_Home_: TypeError: Cannot read properties of undefined (reading 'forEach') -- 2022-07-31 12:53:42.255 - error: javascript.0 (1414) at /opt/iobroker/node_modules/iobroker.javascript/node_modules/node-mihome/lib/device-miio.js:192:18 2022-07-31 12:53:42.255 - error: javascript.0 (1414) at Array.forEach () 2022-07-31 12:53:42.255 - error: javascript.0 (1414) at module.exports.miotFetchSpec (/opt/iobroker/node_modules/iobroker.javascript/node_modules/node-mihome/lib/device-miio.js:190:14) 2022-07-31 12:53:42.256 - error: javascript.0 (1414) at runMicrotasks () 2022-07-31 12:53:42.256 - error: javascript.0 (1414) at processTicksAndRejections (node:internal/process/task_queues:96:5) 2022-07-31 12:53:42.256 - error: javascript.0 (1414) at module.exports.init (/opt/iobroker/node_modules/iobroker.javascript/node_modules/node-mihome/lib/device-miio.js:43:7) 2022-07-31 12:53:42.257 - error: javascript.0 (1414) at CreateDevices (script.js.Xiaomi_Home_:922:17) 2022-07-31 12:53:42.257 - error: javascript.0 (1414) at main (script.js.Xiaomi_Home_:840:5)
ChristopherGier commented 2 years ago

Das sollte eig nicht passieren.. ich schau es mir nochmal an und Versuch es Mal zu reproduzieren. Ist der Code denn noch wie verändert und hast du den Adapter neugestartet? Problem ist momentan eh das bei jedem Update etc die customizings überschrieben werden - vermutlich auch die device Schemas

cacherwolf commented 2 years ago

Ja, der ist noch verändert und ich habe sowohl den JS Adapter als auch IOB neu gestartet.

Ich habe jetzt in der Datei dmaker.fan.p18.js die Zeile "this._miotSpecType = 'urn:miot-spec-v2:device:fan:0000A005:dmaker-p18:1';" entfernt. Jetzt läuft das Skript durch, allerdings kann ich den Ventilator immer noch nicht an/ausschalten und die Datenpunkte werden auch nicht aktualiesiert.

ChristopherGier commented 2 years ago

Komisch.. ich schau es mir Mal in Ruhe morgen an - falls ich es nicht nachstellen kann, können wir es uns ja ggf. Mal über discord zusammen anschauen

cacherwolf commented 2 years ago

Super, vielen Dank

ChristopherGier commented 2 years ago

Okay, doof von mir.. Falsche Line. Müsste Line 192 sein:

properties.forEach(property => {

Zu:

properties?.forEach(property => {
cacherwolf commented 2 years ago

Super von Dir, es war die Line 192 Vielen Dank für die Hilfe.

Bei mir funktionieren jetzt fan-level, horizontal-angle, horizontal-swing, mode, on und physical-controls-locked

Allerdings funktioniert die Rückmeldung nicht. Wenn ich im in der App oder am Gerät etwas ändere (z.B. den Ventilator einschalte) wir die Änderung nicht in den IOB übertragen. Hast du dafür auch eine Lösung?

ChristopherGier commented 2 years ago

Der Rest ist leider falsch gemapped.. muss man in den device files und im Skript anpassen. Das war leider etwas fummelig aber ich schick nachher Mal die Veränderungen die ich gemacht habe (ohne Garantie auf Richtigkeit) :D

Um den aktuellen Status hatte ich mich nicht gekümmert, wollte das Gerät nur über Alexa schalten können ohne das es in die China Cloud kommuniziert, deshalb hatte ich mir den Rest nicht mehr angeschaut :/

ChristopherGier commented 2 years ago

Im Script selber habe ich das entsprechende DefineDevice geändert zu:

{ // Tested and working
    info: {},
    model: "dmaker.fan.p18",// https://miot-spec.org/miot-spec-v2/instance?type=urn:miot-spec-v2:device:fan:0000A005:dmaker-p18:1  
    description: "Mi Smart Standing Fan 2",
    setter: {
        "fan.on": async function (obj, val) { await device[obj].setPower(val) },
        "fan.mode": async function (obj, val) { await device[obj].setMode(val) },
        "fan.fan-level": async function (obj, val) { await device[obj].setFanLevel(val) },
        "fan.horizontal-swing": async function (obj, val) { await device[obj].setHorizontalSwing(val) },
        "fan.horizontal-angle": async function (obj, val) { await device[obj].setHorizontalAngle(val) },
        "fan.alarm": async function (obj, val) { await device[obj].setAlarm(val) },
        "fan.motor-control": async function (obj, val) { await device[obj].setMotorController(val) },
        "physical-controls-locked.physical-controls-locked": async function (obj, val) { await device[obj].setChildLock(val) },
        "fan.off-delay-time": async function (obj, val) { await device[obj].setOffDelayTime(val) },
        "fan.brightness": async function (obj, val) { await device[obj].setBrightness(val) }
    },
    common:
        [{ name: "fan.on", type: "boolean", role: "switch", read: true, write: true },
        { name: "fan.mode", type: "number", role: "switch", read: true, write: true, min: 0, max: 1, states: { 0: "Straight Wind", 1: "Natural Wind" } },
        { name: "fan.fan-level", type: "number", role: "switch", read: true, write: true, min: 1, max: 4, states: { 1: "Slow", 2: "Middle", 3: "High", 4: "Turbo" } },
        { name: "fan.horizontal-swing", type: "boolean", role: "switch", read: true, write: true },
        { name: "fan.horizontal-angle", type: "number", role: "switch", read: true, write: true, min: 30, max: 140, unit: "°", states: { 30: "30°", 60: "60°", 90: "90°", 120: "120°", 140: "140°" } },
        { name: "fan.status", type: "number", role: "indicator", read: true, write: false, min: 1, max: 100 },
        { name: "fan.alarm", type: "boolean", role: "switch", read: true, write: true },
        { name: "fan.motor-control", type: "number", role: "switch", read: false, write: true, min: 0, max: 2, states: { 0: "None", 1: "Left", 2: "Right" } },
        { name: "physical-controls-locked.physical-controls-locked", type: "boolean", role: "switch", read: true, write: true, min: false, max: true },
        { name: "fan.off-delay-time", type: "number", role: "switch", read: true, write: true, min: 0, max: 480, unit: "m" },
        { name: "fan.brightness", type: "boolean", role: "switch", read: true, write: true }
        ]
};

Zusätzlich im entsprechenden Device File:

const Device = require('../device-miio');

module.exports = class extends Device {

  static model = 'dmaker.fan.p18';
  static name = 'Mi Smart Standing Fan 2';
  static image = 'http://static.home.mi.com/app/image/get/file/developer_1541408255kg3xtr1j.png';

  constructor(opts) {
    super(opts);

    this._miotSpecType = 'urn:miot-spec-v2:device:fan:0000A005:dmaker-p18:1';
    this._propertiesToMonitor = [
      'fan:on',
      'fan:mode',
      'fan:fan-level',
      'fan:horizontal-swing',
      'fan:horizontal-angle',
      'fan:status',
      'fan:brightness',
      'fan:alarm',
      'fan:motor-control',
      'physical-controls-locked:physical-controls-locked',
      'fan:off-delay-time'
    ];
  }

  getPower() {
    power = this.properties['fan:on'];
    if (power === 1) return true;
    if (power === 0) return false;
    return undefined;
  }

  getFanLevel() {
    const fanLevel = parseInt(this.properties['fan:fan-level'], 10);
    if (fanLevel > 0) return fanLevel;
    return undefined;
  }

  getMode() {
    return this.properties['fan:mode'];
  }

  getBrightness() {
    return this.properties['fan:brightness'];
  }

  getHorizontalSwing() {
    return this.properties['fan:horizontal-swing'];
  }

  setHorizontalAngle() {
    return this.properties['fan:horizontal-angle'];
  }

  getAlarm() {
    return this.properties['fan:alarm'];
  }

  getMotorController() {
    return this.properties['fan:motor-control'];
  }

  getChildLock() {
    return this.properties['physical-controls-locked:physical-controls-locked'];
  }

  getOffDelayTime() {
    return this.properties['fan:off-delay-time'];
  }

  setPower(v) {
    return this.miotSetProperty('fan:on', v);
  }

  setMode(v) {
    return this.miotSetProperty('fan:mode', v);
  }

  setFanLevel(v) {
    return this.miotSetProperty('fan:fan-level', v);
  }

  setHorizontalSwing(v) {
    return this.miotSetProperty('fan:horizontal-swing', v);
  }

  setHorizontalAngle(v) {
    return this.miotSetProperty('fan:horizontal-angle', v);
  }

  setBrightness(v) {
    return this.miotSetProperty('fan:brightness', v);
  }

  setAlarm(v) {
    return this.miotSetProperty('fan:alarm', v);
  }

  setMotorController(v) {
    return this.miotSetProperty('fan:motor-control', v);
  }

  setChildLock(v) {
    return this.miotSetProperty('physical-controls-locked:physical-controls-locked', v);
  }

  setOffDelayTime(v) {
    return this.miotSetProperty('fan:off-delay-time', v);
  }
};
Pittini commented 2 years ago

Sehr toll, dass Ihr hier eine Lösung gefunden habt. Wäre super nett wenn (wenn keine Fehler in den Tests auftreten) @ChristopherGier daraus nen PullRequest machen könnte.

Tableautin commented 2 years ago

Hi. Ich hatte im ioBroker Forum auch schon mal das Problem gemeldet und bin gerade dazu gekommen, es nach dem Thread hier zu testen.

Inzwischen habe ich beim Ausführen des Scripts keine Fehler mehr, die Datenpunkte werden korrekt angelegt, aber leider stehen alle Datenpunkte in "Fan" auf "NULL". Ich bekomme also keine Rückmeldung vom Gerät zum Status, und auch das Steuern funktioniert bei mir nicht. IP Adresse, Name etc funktionier

Ich hoffe ich habe alle Änderungen korrekt dem Textverlauf entnommen und eingebaut. Habe ich da noch was vergessen?

CyrussM commented 2 years ago

Mit dem Fix von ChristopherGier geht es so halb, ich kann den Fan steuern und bekomme die Werte unter "Info" angezeigt (IP, Name etc.). Leider fehlen alle Status Meldungen. Die Abfrage funktioniert einfach nicht. Das An und Aus schalten per setzten von true oder false unter dem on Objekt, wird aber ausgeführt. Fehler Meldungen hab ich nicht. Steuer ich also mit der App, bekommt iobroker davon nichts mit. Jemand ne Idee?

Pittini commented 2 years ago

Jemand ne Idee?

Nicht wirklich. Aber ich kann zumindest mal erklären warum IP, Name etc da sind aber sonst nix. Diese "statischen" Infos wie IP, kommen für alle Geräte bei der Anmeldung am Xiaomi Server für alle Geräte in Deinem Besitz. Jetzt guggt im zweiten Schritt, das Skript, welche Geräte davon, vom Skript unterstützt werden und legt dafür die DAtenpunkte an und erstellt ein Object dass normaler weise dann regelmäßig Datenupdates erhält. Und genau das scheint nicht zu klappen. Warum weis ich nicht wirklich.

cacherwolf commented 2 years ago

Ich habe im Device File alle Anweisung gelöscht und nach und nach wieder eingeführt. Das hat dann auch geklappt. Bin gerade im Urlaub und habe keinen Zugriff auf die Datei, werde sie nächste Woche hier einstellen.

Fritzelspitze commented 1 year ago

Gibts da inzwischen eine Lösung für?