Weiter zum Inhalt

Off Topic

3.3k Themen 51.1k Beiträge
Hier kann über alles geredet werden was sonst nirgendwo hinpasst

NEWS

Unterkategorien


  • 107 Themen
    2k Beiträge
    Thomas BraunT
    @da_Woody System is booting into 'graphical.target'. Usually a server is running in 'multi-user.target'. Please set BootTarget to 'multi-user.target' or run 'iobroker fix'
  • 260 4k
    260 Themen
    4k Beiträge
    Meister MopperM
    @bloop Ich bin von v13 erstmal wieder zurück auf v12.4+. Bisher hatte ich bei jedem Major Release breaking changes Probleme, die erst mit einem vX.1 behoben wurden. Um das Major Upgrade zu verhindern und trotzdem die neuen Versionen des aktuellen Zweiges bekommen, habe ich eine *.pref Datei in /etc/apt/preferences.d erstellt. Das funktioniert, und ich lasse mir von GitHub die Änderungen der Grafana releases zumailen, und schaue, was sich tut. Ja, das ist jetzt auch mal ein Plädoyer dafür, dass man nicht immer (ohne Sicherheitshintergrund!) sofort auf die nächste Major springen muss.
  • 153 3k
    153 Themen
    3k Beiträge
    HomoranH
    @iceman8080 dann sollten höchstens noch bei Neustart der influx-instanz Einträge mit influxdb als Quelle auftreten. Alles andere müsste javascript als Quelle angeben
  • Fragen und Hilfe zur externen SQL Datenbank

    1 5
    1 Themen
    5 Beiträge
    fuzzy1955F
    Teil 5: Verwenden der SQL-Daten in Diagrammen bzw. im VIS-2 Der Hauptzweck der Datenhistorisierung liegt natürlich in der grafischen bzw. tabellarischen Darstellung, um daraus mögliche Entscheidungen abzuleiten, etwa wo setzte ich beim Börsenstrompreis mein Bezugslimit. Eine einfache Möglichkeit zur grafischen Darstellung bieten die DIAGRAMME. Man wählt als Datenquellen die gewünschten SQL-Historisierungspunkte, stellt noch ein paar Dinge ein und … fertig. [image: 1770367644381-664a0f95-2fc3-48ae-babe-1a8ffafab1ad-image.png] Um das Ganze im VIS-2 anzuzeigen, nimmt man dort das Widget „Basic -IFrame“ und gibt bei Allgemein – Quelle den Link aus den Diagrammen an. [image: 1770367662940-281a33d1-fcdc-49bb-a3d9-c0fb7ea8ecd1-image.png] So kann man sich ein VIS-2-View nur für Diagramme bauen, bei der die gewünschte Art des Diagrammes angetippt wird: [image: 1770367682322-9328d9e4-3448-4e5a-8c92-8eeec3f2b52c-image.png] Die tabellarischen Darstellung im VIS-2 geschieht mit dem Widget „Basic Table“, bei dem man einen separaten Datenpunkt (Table Object ID) mit JSON-Daten befüllt. Dieses Beispiel zeigt Tages-, Monats- und Jahreswerte einer Strombilanz untereinander. [image: 1770367711812-00a9e22f-d74a-43e7-982f-0e35c37db84a-image.png] Die Datenpunkte dazu: { "alias.0.Diagrammdaten.StrombilanzJahre": { "common": { "name": "StrombilanzJahre", "desc": "Manuell erzeugt", "role": "state", "type": "json", "read": true, "write": true, "alias": { "id": "javascript.0.variables.j006" } }, "type": "state", "native": {}, "_id": "alias.0.Diagrammdaten.StrombilanzJahre", "acl": { "object": 1636, "state": 1636, "owner": "system.user.admin", "ownerGroup": "system.group.administrator" }, "val": "", "from": "system.adapter.admin.0", "user": "system.user.admin", "ts": 1768340995710, "ack": false }, "alias.0.Diagrammdaten.StrombilanzMonate": { "common": { "name": "StrombilanzMonate", "desc": "Manuell erzeugt", "role": "state", "type": "json", "read": true, "write": true, "alias": { "id": "javascript.0.variables.j005" } }, "type": "state", "native": {}, "_id": "alias.0.Diagrammdaten.StrombilanzMonate", "acl": { "object": 1636, "state": 1636, "owner": "system.user.admin", "ownerGroup": "system.group.administrator" }, "val": "", "from": "system.adapter.admin.0", "user": "system.user.admin", "ts": 1768340995710, "ack": false }, "alias.0.Diagrammdaten.StrombilanzTage": { "common": { "name": "StrombilanzTage", "desc": "Manuell erzeugt", "role": "state", "type": "json", "read": true, "write": true, "alias": { "id": "javascript.0.variables.j004" } }, "type": "state", "native": {}, "_id": "alias.0.Diagrammdaten.StrombilanzTage", "acl": { "object": 1636, "state": 1636, "owner": "system.user.admin", "ownerGroup": "system.group.administrator" }, "val": "", "from": "system.adapter.admin.0", "user": "system.user.admin", "ts": 1768340995710, "ack": false } } Mit einem Javascript werden die Datenpunkte zeitgesteuert befüllt. strSQL = "SELECT DATE_FORMAT (tag, '%Y-%m-%d') AS Tag, " + "ROUND(SUM(pv) / 1000.0,1) AS PV, " + "ROUND(AVG(akku)) AS Hausakku, " + "ROUND(SUM(haus) / 1000.0,1) AS Haus, " + "ROUND(SUM(eauto) / 1000.0,1) AS eAuto, " + "ROUND(SUM(gen) / 1000.0,1) AS Gen, " + "ROUND(SUM(nots) / 1000.0,1) AS Nots, " + "ROUND(SUM(gesamt) / 1000.0,1) AS gesamt, " + "ROUND(COUNT(autark)) AS autark " + "FROM iobroker.tagesstatistik " + "GROUP BY DATE_FORMAT (tag, '%Y-%m-%d') " + "ORDER BY DATE_FORMAT (tag, '%Y-%m-%d') desc;"; sendTo('sql.0', 'query', strSQL, function (result) { if (result.error) { console.error(result.error); } else { MyTrigger (idStrombilanzTage,JSON.stringify(result.result)); } }); --------------------------- Voila , das war die ganze Zauberei --------------------------- Anregungen, Fragen etc. bitte hier anhängen.
  • 276 Themen
    5k Beiträge
    ubeckerU
    @MartinP Ich habe jetzt bei mit 2 Stück ESP32 S3 ETH in Betrieb. Einen für analoge Werte den anderen als 8fach Relais mit Timer. Allerdings habe ich die Software dazu so weit wie möglich selbst geschrieben. (Librarys außen vor) Da ich kein W-Lan wollte direkt per LAN angebunden. Per MQTT dann in IOBroker. Eine kleine Web-UI hab ich auch dazu gebastelt. Läuft absolut gut. Die Relaiskarte ist allerdings erst 2 Wochen online. Das analoge Teil jetzt 1 Monat. Da ich in dem analogen Teil etliche Berechnungen drin habe war auch meine Befürchtung das die Leistung nicht ausreicht. Im Hintergrung laufen dann noch ADC und DAC mit einer eigenen Soft SPI. (die interne habe ich nicht zum laufen gebracht) Mit 4Mikrosekunden Takt. Ich habe nicht gemessen wie schnell die antwortzeiten sind, aber eine Änderung im IOBroker für den analogwert ist gefühlt sofort da. Ausgabe des analogen Wert und Anzeige auf der Web-UI. Denke der ist noch weit weg von ausgelastet.
  • 196 6k
    196 Themen
    6k Beiträge
    ilovegymI
    danke für den hinweis, funktioniert hier auch besser als vorher.. ( vorher war mehr failover als balancer..)
  • Fragen zu Docker-Installationen

    23 342
    23 Themen
    342 Beiträge
    Marc BergM
    @juggi1962 sagte: Falls jemand eine Andere Erklärung dafür hat, bitte gern hier kund tun Du musst noch einen weiteren Container-Port freigeben, damit die Erfolgsmeldung angezeigt wird. Steht auch so in der Backitup Anleitung / Wiki. https://github.com/simatec/ioBroker.backitup/blob/master/docs/de/backitup.md#docker-unterstützung
  • 54 721
    54 Themen
    721 Beiträge
    DasBo1975D
    @ilovegym sagte: 2 Hände, 2 Augen, 2 Rechner, 😎 immer 2 Dinge gleichzeitig machen HAHA, meine Frau würde sagen "noch ein PC, wieviele Server willst du noch, es ist 1:30 Uhr willst du nicht ins Bett"
  • Frigate und/oder AgentDVR Personen/Kennzeichen Erkennung

    Verschoben frigate agent dvr
    8
    0 Stimmen
    8 Beiträge
    2k Aufrufe
    wendy2702W
    @martinp Gedanke war erstmal die Kennzeichenerkennung in Verbindung mit anderen Triggern zum Garagentor öffnen zu verwenden. Eine Haustüröffnung ist nicht geplant, glaube da würde ich mich nicht drauf verlassen wollen. Allerdings würde ich gerne ein gezielte Nachricht bekommen wenn z.B. meine Kinder unser Haus besuchen wenn wir z.B. nicht zu Hause sind oder eben jemand fremdes.
  • Aufnahmen von IP Kamera auf TrueNAS

    10
    0 Stimmen
    10 Beiträge
    1k Aufrufe
    S
    @martinp sagte in Aufnahmen von IP Kamera auf TrueNAS: @saeft_2003 in den Reolink Kameras kann man doch direkt einen Netzwerkspeicherort hinterlegen [image: 1730550921644-fb1bdad1-8473-4f56-9aa2-159e99feeac3-grafik.png] Ok guter Tipp muss ich mir mal anschauen
  • Nodered Modbus Palette installieren -> Fehler

    Verschoben
    27
    0 Stimmen
    27 Beiträge
    2k Aufrufe
    G
    Danke für eure Hilfe Ich habe jetzt alles neu geflasht auf BookWorm und alles läuft. Danke
  • Leistung des Wechselrichters mit openDTU anpassen

    Verschoben
    22
    0 Stimmen
    22 Beiträge
    2k Aufrufe
    M
    @codierknecht Ich habe die matt-Instanz gestoppt. Wenn ich in der openDTU den Wert ändere dann wird der Wert in innen iobroker geschrieben (hier: 1950W). Möchte ich aber im iobroker den Wert auf 800W ändern dann erfolgt keine Übertragung in die openDTU-Einheit bzw. Wechselrichter) [image: 1730213203664-img_0118-resized.jpeg]
  • Upgrade von Debian 11/Bullseye auf 12/Bookworm

    upgrade bullseye bookworm
    351
    30 Stimmen
    351 Beiträge
    107k Aufrufe
    Pedder007P
    @thomas-braun grundsätzlich hast Du Recht, aber das soll dann der Junior entscheiden/machen ;-)
  • Anzeige ob Auto passend steht

    33
    0 Stimmen
    33 Beiträge
    5k Aufrufe
    Albert KA
    @markus-7 LOL..für mich stimmt das schon
  • Erweiterung der Anlage von 1p auf 3p - passt das?

    Verschoben
    2
    1
    0 Stimmen
    2 Beiträge
    324 Aufrufe
    HomoranH
    @stefan-falt sagte in Erweiterung der Anlage von 1p auf 3p - passt das?: Reicht es wenn ich mit 50mm² verkable? Victron schreibt 70 mm² vor
  • UniFi Network Application 8.3.x -> NAT deaktivieren

    unifi ubiquiti nat
    10
    0 Stimmen
    10 Beiträge
    2k Aufrufe
    Samson71S
    @ilovegym "etwas" übertrieben trifft es vermutlich [image: 1729933859153-37232481-e5a7-491e-91f6-5c6a6ed7187c-image.png]
  • Erfahrungen zu tibber und Hardware-Voraussetzungen

    16
    0 Stimmen
    16 Beiträge
    3k Aufrufe
    HomoranH
    @xxjooo sagte in Erfahrungen zu tibber und Hardware-Voraussetzungen: Wir sind mit einem Haus mit 4 Leuten in der Mitte zwischen K und BN immer bei deutlich über 5.000kWh/Jahr. nicht weit davon etwas nördlich Mit unseren beiden Töchtern lagen wir damals bei 14.000kWh/anno. Warmwasser und Kochen über Strom. und geduscht wurde bis die Sicherung ausstieg. (ok, diverse große Warmwasseraquarien, ein Blechpool und ein HomeOffice waren auch noch dabei.) inzwischen lagen wir bei etwa 5.000, als dann die Stromppreise 2022 stiegen, nochmal radikal gesenkt auf etwa 3500netto. Netto deswegen, weil dann die Wärmepumpe und das eAuto kamen. liegen jetzt wieder zwischen 6000 und 6500 Verbrauch und dank Solar und Batterie bei irgendwas um 3500 Bezug
  • zigbee2MQTT server + Lidl Fluter HG08010 (Geräte Bug?)

    4
    1
    0 Stimmen
    4 Beiträge
    246 Aufrufe
    MartinPM
    @arteck Sie melden sich ja ... nur, wenn man sie nicht lange genug vom Netz trennt mit dem falschen Zustand ... Ist eben Discounterware ;-) Jetzt noch mal das Logging mit nur kurzem simulierten Netzausfall (Schalte vorher kurz An und wieder Aus) 2024-10-23 14:33:32z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/Lidl Fluter', payload '{"brightness":254,"color":{"x":0.3131,"y":0.3232},"color_mode":"color_temp","color_power_on_behavior":"initial","color_temp":153,"last_seen":"2024-10-23T14:33:32+02:00","linkquality":184,"state":"OFF"}' info 2024-10-23 14:33:32z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/Lidl Fluter', payload '{"brightness":254,"color":{"x":0.3131,"y":0.3232},"color_mode":"color_temp","color_power_on_behavior":"initial","color_temp":153,"last_seen":"2024-10-23T14:33:32+02:00","linkquality":184,"state":"ON"}' info 2024-10-23 14:33:33z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/Lidl Fluter', payload '{"brightness":254,"color":{"x":0.3131,"y":0.3232},"color_mode":"color_temp","color_power_on_behavior":"initial","color_temp":153,"last_seen":"2024-10-23T14:33:33+02:00","linkquality":183,"state":"ON"}' info 2024-10-23 14:33:33z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/Lidl Fluter', payload '{"brightness":254,"color":{"x":0.3131,"y":0.3232},"color_mode":"color_temp","color_power_on_behavior":"initial","color_temp":153,"last_seen":"2024-10-23T14:33:33+02:00","linkquality":183,"state":"OFF"}' info 2024-10-23 14:33:54z2m:mqtt: MQTT publish: topic 'zigbee2mqtt/Lidl Fluter', payload '{"brightness":254,"color":{"x":0.3131,"y":0.3232},"color_mode":"color_temp","color_power_on_behavior":"initial","color_temp":153,"last_seen":"2024-10-23T14:33:54+02:00","linkquality":171,"state":"OFF"}' Die Lampe ist "ON" (!), aber state wird nach "Besaften" mit "OFF" (!) gemeldet ... Ändert sich auf in den ersten 2 Minuten nach dem Einschalten der Netzversorgung der Lampe nicht ... Es gibt also eine Meldung beim Einschalten, die ist aber falsch...
  • komme nicht mehr in den Unifi-Controller

    20
    0 Stimmen
    20 Beiträge
    1k Aufrufe
    NegaleinN
    @meister-mopper sagte in komme nicht mehr in den Unifi-Controller: Schau doch mal nach, ob man es von der cli anschieben kann. gefunden: (https://community.ui.com/questions/UniFi-How-do-I-reset-the-UAP-to-factory-default-settings/39013761-714c-4e1a-a428-aa56e1b99f40) :)
  • Bekomme mount nicht in fsfab

    7
    0 Stimmen
    7 Beiträge
    349 Aufrufe
    CodierknechtC
    @david-g Habe auch lange daran rumgedoktert und bin zu folgender Lösung gekommen: //192.168.178.5/Docker /mnt/nas cifs x-systemd.automount,x-systemd.requires=network-online.target,file_mode=0666,dir_mode=0777,credentials=/etc/smbcredentials 0 0
  • COMET DECT Thermostat retten

    13
    1
    0 Stimmen
    13 Beiträge
    1k Aufrufe
    BananaJoeB
    @martinp sagte in COMET DECT Thermostat retten: @bananajoe Da muss man aufpassen, dass man die Dämmung mit einer Sperrbahn gegen den Raum abdichtet, sonst kriecht die Feuchtigkeit in die Dämmung und kondensiert dann durch das Wärmegefälle nach außen am Taupunkt ... öhm, jupp. Folie/Dampfsperre ist da auch hinter
  • Shelly BLU USB Gateway als Beacon? suche USB Beacon

    4
    0 Stimmen
    4 Beiträge
    588 Aufrufe
    D
    @schmello said in Shelly BLU USB Gateway als Beacon? suche USB Beacon: @dieter_p Muss ich in der App vom Feasycom Stick noch etwas Einstellen? Aktuell läuft soweit ich weiß die feasycom app nicht auf meinem Pixel mit Android 14 bzw. hab schon was länger nicht mehr versucht, von daher alles ausm Gedächtnis. Läuft aber hab keinen Beacon am Mann zum testen. Ich glaube, "irgendwas" war in den beacons die ich gekauft hab bereits konfiguriert und ob das in deinem Stick genauso ist, nicht zu sagen. In jeden Fall würde ich es prüfen, aus Sicherheitsgründen und auch weil es Sinn macht und du dort Sendeintervalle etc ganz genau einstellen kannst. Ebenso unterstützen die Beacons von feasycom verschiedene Typen (ibeacon, Eddystone etc etc.) die dort angelegt werden können. Ungenutztes würde ich sowieso löschen. Ich verwende derzeit noch ESPHome und hab noch nicht Tasmota mit BLE Scanner in Betrieb genommen. Prinzipell das Ganze über iBeacon konfiguriert und ich prüfe mit dem Scanner auf die MAC-Adresse des Beacon. Ablesbar auch mit der feasycom app wenn ein iBeacon konfiguriert ist. Unter Tasmota sehe ich keinen Grund warum es nicht dort genauso gehen sollte. Ein Eindruck zur App, kannst Du hier bekommen: https://www.youtube.com/watch?v=4r0E7_mBHVQ
  • Wie mittels LUXTRONIK-Adapter Wassertemperatur einstellen?

    5
    3
    0 Stimmen
    5 Beiträge
    680 Aufrufe
    stolly82S
    Hey zusammen, ich habe ein Skript geschrieben, das die Steuerung der Wassertemperatur über MyUplink ermöglicht, da die Luxtronik Weboberfläche (ab Version 3.89.4) keine direkte Option dazu bietet. Der Plan ist, daraus später einen richtigen Adapter zu machen, aber hier ist schon einmal die Skriptversion zum Ausprobieren. Was macht das Skript? Die Luxtronik ab Version 3.89.4 kann MyUplink integrieren, und genau das nutzt dieses Skript aus. Das Skript erlaubt es euch, die Heizung und das Warmwasser über die MyUplink-Cloud zu steuern, und stellt alle Informationen zur Verfügung, die dort abgerufen werden können. Das bedeutet, ihr könnt die Temperatur direkt anpassen, ohne die Einschränkungen der normalen Weboberfläche. Mehr Informationen zu MyUplink findet ihr hier: MyUplink Cloud – Alpha Innotec So probiert ihr das Skript aus: Öffnet den IoBroker > JavaScript Adapter > Neu > JavaScript > Hinzufügen. Fügt das Skript ein. Tragt eure MyUplink-Zugangsdaten ein. Startet das Skript – und schon seid ihr einsatzbereit! Funktionsumfang Heizung & Warmwasser: Ihr könnt die gewünschten Temperaturen einstellen. Informationsabruf: Alle relevanten Daten, die MyUplink zur Verfügung stellt, werden abgerufen und angezeigt. Falls ihr das Skript ausprobiert, würde ich mich sehr über euer Feedback freuen. Viel Spaß damit! // Konfigurationseinstellungen const CONFIG = { email: 'EMAIL@MYMAIL.DE', password: 'GEHEIM', clientId: 'My-Uplink-iOS', clientSecret: '992EFE7C-9CDC-435B-8BA3-2EF8E81BEF14', interval: 30000, // Standardintervall in Millisekunden für wiederkehrende Abfragen intervalFunctions: [ 'getWeatherTileData', 'getAndCreateMenu' ] }; const axios = require('axios'); class MyUplinkAdapter { constructor() { this.token = null; this.userData = {}; // Hier werden die Benutzerdaten gespeichert this.devices = {}; // Hier werden die Gerätedaten gespeichert this.groups = []; // Hier werden die Gruppendaten gespeichert this.failedRequests = []; // Warteschlange für fehlgeschlagene Anfragen log('Konstruktor von MyUplinkAdapter wurde aufgerufen!', 'info'); // Initialisieren des Verbindungsstatus setObject('myUplink.info.connection', { type: 'state', common: { name: 'API-Verbindungsstatus', type: 'boolean', role: 'indicator.connected', read: true, write: false }, native: {} }); setState('myUplink.info.connection', { val: false, ack: true }); } // Authentifizierungsmethode authenticate() { log("Starte Authentifizierung...", 'info'); log(`Authentifizierungsparameter: email=${CONFIG.email}, clientId=${CONFIG.clientId}`, 'debug'); return axios.post('https://internalapi.myuplink.com/v2/users/validate', { email: CONFIG.email, password: CONFIG.password }, { headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Accept-Language': 'de-DE' } }) .then(response => { log("Benutzer validiert. Antwort: " + JSON.stringify(response.data), 'debug'); const validationData = response.data; if (validationData.id) { this.userData = validationData; // Speichert die Benutzerdaten log(`Authentifizierung erfolgreich. Benutzer-ID: ${validationData.id}`, 'info'); setState('myUplink.info.connection', { val: true, ack: true }); // Verbindung erfolgreich return this.getAccessToken(validationData.id); } else { log("Fehlende ID im Validierungsschritt. Antwort: " + JSON.stringify(validationData), 'error'); throw new Error('User validation failed: Missing ID'); } }) .catch(error => { this.handleError(error); setState('myUplink.info.connection', { val: false, ack: true }); // Verbindung fehlgeschlagen throw error; }); } // Methode zum Abrufen des Access Tokens getAccessToken(userId) { log("Abrufen des Access Tokens...", 'info'); log(`Tokenparameter: clientId=${CONFIG.clientId}, userId=${userId}`, 'debug'); return axios.post('https://internalapi.myuplink.com/oauth/token', `password=${encodeURIComponent(CONFIG.password)}&client_secret=${CONFIG.clientSecret}&grant_type=password&client_id=${CONFIG.clientId}&username=${encodeURIComponent(CONFIG.email)}`, { headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'Authorization': `Bearer ${userId}` } } ) .then(response => { log("Token erfolgreich erhalten. Antwort: " + JSON.stringify(response.data), 'debug'); const tokenData = response.data; if (tokenData.access_token) { this.token = tokenData.access_token; log(`Access-Token erfolgreich gespeichert.`, 'info'); return tokenData; } else { log("Fehlender Access-Token in der Antwort: " + JSON.stringify(tokenData), 'error'); throw new Error('Access token not received'); } }) .catch(error => { this.handleError(error); throw error; }); } // Methode zum Speichern der Benutzerdaten saveUserData() { return this.ensureTokenIsValid().then(() => { log("Speichern der Benutzerdaten...", 'info'); const userUrl = 'https://internalapi.myuplink.com/v2/users/me'; log(`Anfrage-URL für Benutzerdaten: ${userUrl}`, 'debug'); return axios.get(userUrl, { headers: { 'Authorization': `Bearer ${this.token}`, 'Accept': 'application/json' } }) .then(response => { log("Benutzerdaten erfolgreich abgerufen. Antwort: " + JSON.stringify(response.data), 'debug'); this.userData = response.data; // Dynamische Erstellung der Datenpunkte in myUplink.users und Setzen der Werte Object.keys(this.userData).forEach(key => { if (typeof this.userData[key] === 'object' && this.userData[key] !== null) { Object.keys(this.userData[key]).forEach(subKey => { const stateId = `myUplink.User.${key}.${subKey}`; setObject(stateId, { type: 'state', common: { name: `${key} ${subKey}`, type: typeof this.userData[key][subKey], read: true, write: false } }); setState(stateId, { val: this.userData[key][subKey], ack: true }); }); } else { const stateId = `myUplink.User.${key}`; setObject(stateId, { type: 'state', common: { name: key, type: typeof this.userData[key], read: true, write: false } }); setState(stateId, { val: this.userData[key], ack: true }); } }); return this.userData; }) .catch(error => { this.handleError(error); throw error; }); }); } // Methode für das Abrufen der Wetterdaten getWeatherTileData() { return this.ensureTokenIsValid().then(() => { const group = this.groups.find(g => g.id); // Nimmt die erste gültige Gruppe aus den gespeicherten Gruppendaten if (!group || !group.id) { log('Keine gültige Group ID gefunden.', 'error'); throw new Error('No valid Group ID found.'); } const groupId = group.id; const fetchWeatherData = () => { log("Abrufen der Wetterdaten...", 'info'); const weatherUrl = `https://internalapi.myuplink.com/v2/groups/${groupId}/weather-tile-data`; log(`Anfrage-URL für Wetterdaten: ${weatherUrl}`, 'debug'); return axios.get(weatherUrl, { headers: { 'Authorization': `Bearer ${this.token}`, 'Accept': 'application/json' } }) .then(response => { log("Wetterdaten erfolgreich abgerufen. Antwort: " + JSON.stringify(response.data), 'debug'); const weatherData = response.data; // Dynamische Erstellung der Datenpunkte in myUplink.Weather und Setzen der Werte const basePath = `myUplink`; Object.keys(weatherData).forEach(key => { if (typeof weatherData[key] === 'object' && weatherData[key] !== null) { if (Array.isArray(weatherData[key])) { weatherData[key].forEach((item, index) => { Object.keys(item).forEach(subKey => { const stateId = `${basePath}.Weather.${key}[${index}].${subKey}`; setObject(stateId, { type: 'state', common: { name: `${key} ${subKey}`, type: typeof item[subKey], read: true, write: false } }); setState(stateId, { val: item[subKey], ack: true }); }); }); } else { Object.keys(weatherData[key]).forEach(subKey => { const stateId = `${basePath}.Weather.${key}.${subKey}`; setObject(stateId, { type: 'state', common: { name: `${key} ${subKey}`, type: typeof weatherData[key][subKey], read: true, write: false } }); setState(stateId, { val: weatherData[key][subKey], ack: true }); }); } } else { const stateId = `${basePath}.Weather.${key}`; setObject(stateId, { type: 'state', common: { name: key, type: typeof weatherData[key], read: true, write: false } }); setState(stateId, { val: weatherData[key], ack: true }); } }); return weatherData; }) .catch(error => { this.handleError(error, fetchWeatherData); }); }; // Erstmaliger Aufruf und Intervall einrichten fetchWeatherData(); setInterval(fetchWeatherData, CONFIG.interval); }); } // Methode zum Sicherstellen, dass der Token noch gültig ist ensureTokenIsValid() { if (!this.token) { log("Token ist nicht vorhanden oder ungültig. Hole neuen Token...", 'warn'); return this.authenticate(); } return Promise.resolve(); } // Methode zum Abrufen der Gruppendaten getGroups() { return this.ensureTokenIsValid().then(() => { log("Abrufen der Gruppendaten...", 'info'); const groupsUrl = 'https://internalapi.myuplink.com/v2/groups/me'; log(`Anfrage-URL für Gruppendaten: ${groupsUrl}`, 'debug'); return axios.get(groupsUrl, { headers: { 'Authorization': `Bearer ${this.token}`, 'Accept': 'application/json' } }) .then(response => { log("Gruppendaten erfolgreich abgerufen. Antwort: " + JSON.stringify(response.data), 'debug'); this.groups = response.data.groups || []; // Speichert die Gruppendaten this.devices = this.groups.flatMap(group => group.devices) || []; // Speichert die Gerätedaten this.createObjectsFromGroupsAndDevices(); return this.groups; }) .catch(error => { this.handleError(error); throw error; }); }); } // Methode zum Erstellen der Baumstruktur in myUplink createObjectsFromGroupsAndDevices() { log("Erstelle Objekte für Gruppen und Geräte...", 'info'); this.groups.forEach(group => { const groupId = group.id; group.devices.forEach(device => { const deviceId = device.id; const groupPath = `myUplink.System.${groupId}.${deviceId}`; // Erstellt das Geräteobjekt innerhalb der Gruppe setObject(groupPath, { type: 'device', common: { name: device.name || 'Unbenanntes Gerät', desc: device.description || '' }, native: device }); }); }); log("Objekterstellung abgeschlossen.", 'info'); } // Methode zum Abrufen der Menüstruktur async getAndCreateMenu(menuId = 0, basePath = `myUplink`) { try { const device = this.groups.flatMap(group => group.devices).find(device => device.id); if (!device || !device.id) { log('Keine gültige Device ID gefunden.', 'error'); throw new Error('No valid Device ID found.'); } const menuUrl = `https://internalapi.myuplink.com/v3/devices/${device.id}/menu/${menuId}`; const response = await axios.get(menuUrl, { headers: { 'Authorization': `Bearer ${this.token}`, 'Accept': 'application/json' } }); const menuData = response.data; if (menuData.rows && menuData.rows.length > 0) { for (const row of menuData.rows) { let name = row.text?.text || `Menü_${row.id}`; name = this.normalizeName(name); // Normalisieren von Umlauten const path = `${basePath}.${name}`; // Falls es ein weiterer Menü-Link ist, rufe das nächste Menü ab if (row.type === 'uilink') { setObject(path, { type: 'channel', common: { name: name, desc: `Menü ID: ${row.id}` }, native: row }); await this.getAndCreateMenu(row.id, path); } // Falls es ein Parameter ist, erstelle das Parameter-Objekt if (row.type === 'uiinfoparameter' || row.type === 'uinumerical' || row.type === 'uidropdown' || row.type === 'uiboolean') { this.createParameterObject(path, row); } } } } catch (error) { this.handleError(error); } } // Methode zum Erstellen eines Parameter-Objekts createParameterObject(path, parameter) { const parameterName = parameter.text?.text || `Parameter_${parameter.parameterId}`; let value = parameter.value?.integerValue ?? parameter.value?.stringValue ?? null; let adjustedValue = value; // Divisor anwenden, falls vorhanden und nur auf den Wert im Objekt selbst if (parameter.metadata?.divisor) { if (typeof value === 'number') { adjustedValue = value / parameter.metadata.divisor; } } // Wert als Text anzeigen, falls enumValues vorhanden sind const states = {}; if (parameter.metadata?.enumValues && parameter.metadata.enumValues.length > 0) { parameter.metadata.enumValues.forEach(ev => { states[ev.value] = ev.text; }); adjustedValue = value; } // Setze den Objekt-Typ je nach Parameter-Typ const commonType = parameter.metadata?.variableType?.toLowerCase() || 'mixed'; const role = parameter.type === 'uiboolean' ? 'switch' : 'text'; setObject(path, { type: 'state', common: { name: parameterName, type: commonType, unit: parameter.metadata?.unit || '', read: true, write: parameter.metadata?.isWritable || false, role: role, states: Object.keys(states).length > 0 ? states : undefined, min: parameter.metadata?.minValue && parameter.metadata.minValue !== 0 ? parameter.metadata.minValue / (parameter.metadata.divisor || 1) : undefined, max: parameter.metadata?.maxValue && parameter.metadata.maxValue !== 0 ? parameter.metadata.maxValue / (parameter.metadata.divisor || 1) : undefined, step: parameter.metadata?.change ? parameter.metadata.change / (parameter.metadata.divisor || 1) : undefined }, native: parameter }); // Setze den Wert des Parameters als Zahl oder Enum-Wert setState(path, { val: typeof adjustedValue === 'number' ? adjustedValue : value, ack: true }); // Lausche auf Änderungen des Werts, um die PUT-Anfrage zu senden if (parameter.metadata?.isWritable) { on({id: path, change: 'ne'}, (obj) => { // 'ne' bedeutet, nur nicht bestätigte Änderungen lauschen if (obj.state.ack) { return; // Wenn die Änderung von der API bestätigt wurde, ignoriere sie } let newValue = obj.state.val; const divisor = parameter.metadata?.divisor || 1; const minValue = typeof parameter.metadata?.minValue !== 'undefined' && parameter.metadata.minValue !== 0 ? parameter.metadata.minValue / divisor : undefined; const maxValue = typeof parameter.metadata?.maxValue !== 'undefined' && parameter.metadata.maxValue !== 0 ? parameter.metadata.maxValue / divisor : undefined; const change = parameter.metadata?.change ? parameter.metadata.change / divisor : undefined; // Werte begrenzen und anpassen if (typeof minValue !== 'undefined' && newValue < minValue) { newValue = minValue; } if (typeof maxValue !== 'undefined' && newValue > maxValue) { newValue = maxValue; } if (typeof change !== 'undefined') { newValue = Math.round(newValue / change) * change; } const adjustedValueForPut = newValue * divisor; let currentPath = path; let menuId = null; while (!menuId && currentPath.includes('.')) { currentPath = currentPath.substring(0, currentPath.lastIndexOf('.')); const currentObject = getObject(currentPath); if (currentObject?.native?.id) { menuId = currentObject.native.id; } } if (menuId) { this.updateParameterValue(menuId, parameter.parameterId, adjustedValueForPut, parameter.metadata?.unit || '', path, newValue); } else { log(`Fehler: Menü-ID für Pfad ${path} nicht gefunden.`, 'error'); } }); } } // Methode zum Normalisieren von Umlauten normalizeName(name) { return name .replace(/ä/g, 'ae') .replace(/ö/g, 'oe') .replace(/ü/g, 'ue') .replace(/ß/g, 'ss') .replace(/[^a-zA-Z0-9]/g, '_'); } // Methode zur Aktualisierung der Parameterwerte (PUT-Call) updateParameterValue(menuId, parameterId, value, unit = "", path, newValue) { const device = this.groups.flatMap(group => group.devices).find(device => device.id); if (!device || !device.id) { log('Keine gültige Device ID gefunden.', 'error'); throw new Error('No valid Device ID found.'); } const updateUrl = `https://internalapi.myuplink.com/v2/devices/${device.id}/menu/${menuId}/rawpoints/${parameterId}`; log(`Sende PUT-Anfrage an URL: ${updateUrl} mit Wert: ${value} und Einheit: ${unit}`, 'info'); return axios.put(updateUrl, { value: value, unit: unit }, { headers: { 'Authorization': `Bearer ${this.token}`, 'Content-Type': 'application/json' } }) .then(response => { log(`Parameter erfolgreich aktualisiert: ${JSON.stringify(response.data)}`, 'debug'); setState(path, { val: newValue, ack: true }); // Bestätigt den Wert im Objekt }) .catch(error => { this.handleError(error, () => this.updateParameterValue(menuId, parameterId, value, unit, path, newValue)); }); } // Fehlerbehandlungs-Methode handleError(error, retryCallback = null) { if (error.response) { log('Fehler-Antwortdaten: ' + JSON.stringify(error.response.data, null, 2), 'error'); log('HTTP-Statuscode: ' + error.response.status, 'error'); if (error.response.status === 401) { log('Token abgelaufen, erneuere den Token...', 'warn'); this.authenticate().then(() => { if (retryCallback) { retryCallback(); } }); } } setState('myUplink.info.connection', { val: false, ack: true }); // Verbindung fehlgeschlagen } } // Beispiel für die Verwendung der MyUplinkAdapter-Klasse (alle Logs als Fehler oder Warnungen) log("Starte die Authentifizierung...", 'info'); const adapter = new MyUplinkAdapter(); adapter.authenticate() .then(() => adapter.getGroups()) .then(groups => { log("Gruppendaten empfangen: " + JSON.stringify(groups), 'debug'); log("Gerätedaten empfangen: " + JSON.stringify(adapter.devices), 'debug'); return adapter.saveUserData(); }) .then(userData => { log("Benutzerdaten gespeichert: " + JSON.stringify(userData), 'info'); return adapter.getWeatherTileData(); }) .then(weatherData => { log("Wetterdaten empfangen: " + JSON.stringify(weatherData), 'info'); }) .then(() => adapter.getAndCreateMenu()) .then(() => { log('Menüstruktur erfolgreich erstellt.', 'info'); }) .then(() => { // Dynamisches Registrieren der Intervall-Funktionen CONFIG.intervalFunctions.forEach(funcName => { if (typeof adapter[funcName] === 'function') { setInterval(() => { adapter[funcName](); }, CONFIG.interval); log(`${funcName} wurde als Intervallfunktion registriert.`, 'info'); } else { log(`Funktion ${funcName} existiert nicht im Adapter.`, 'error'); } }); }) .catch(err => { log('Allgemeiner Fehler: ' + (err.message || "Keine Fehlermeldung verfügbar"), 'error'); }); log("Ende des Skripts erreicht.", 'info');
  • Partitionsgröße automatisch erweitern deaktivieren

    Verschoben
    16
    0 Stimmen
    16 Beiträge
    1k Aufrufe
    mickymM
    Nun @ro75 sagte in Partitionsgröße automatisch erweitern deaktivieren: Für was diesen Murks? Nun ich versteh das schon - ich habe meine Partition auch minimal verkleinert, damit die images auf unterschiedliche Fabrikate von SD Karten passen. Installiere Dir parted und resize2fs. Mit parted verkleinere die Partition, anschließend das Dateisystem mit resize2fs anpassen.
  • Yahka Thermostat mit Temperatursensor u. Steckdose erstellen

    Verschoben
    5
    0 Stimmen
    5 Beiträge
    338 Aufrufe
    paul53P
    @dody0007 sagte: Die Steckdose soll dann einschalten und wenn die Zieltemp. erreicht ist, wieder ausschalten. Dafür benötigst du einen eigenen Datenpunkt für den Sollwert und einen Zweipunkt-Regler (Skript).
  • Empfehlung 19" Server gesucht

    30
    0 Stimmen
    30 Beiträge
    2k Aufrufe
    BananaJoeB
    @negalein sagte in Empfehlung 19" Server gesucht: War nur so ein Gedankt, da ich die sehr günstig bekommen hätte. naja, deshalb liegt mein Kauf hier ja auch rum, schön billig sind die ja. Ich habe diese Erfahrung also auch mit Geld erkauft. Ich hatte den in der Testumgebung in der Firma im Einsatz, da ist der Stromverbrauch "egal" bzw. fallen ein paar hundert Watt mehr oder weniger schlicht nicht auf.
  • HM-IP Heizkörperthermostat-Display sehr blass / schwach

    Verschoben
    7
    0 Stimmen
    7 Beiträge
    558 Aufrufe
    Samson71S
    @timmss sagte in HM-IP Heizkörperthermostat-Display sehr blass / schwach: @samson71 Das könnte sein ja, wobei die frisch angebaut sind und vor nichtmal einem Jahr bestellt :( Dann wäre ja noch Garantie drauf. Sollte sich der Blickwinkel nicht als Grund zeigen, dann würde ich das direkt reklamieren.
  • Samsung Tab A8 - Akku

    7
    1
    0 Stimmen
    7 Beiträge
    402 Aufrufe
    NegaleinN
    @samson71 sagte in Samsung Tab A8 - Akku: Die Netzteile meiner 4 Wandtablets werden von Schaltaktoren ein- und ausgeschaltet Hätte ich vor. Muss aber erst Platz finden. Wird wohl hinter dem Tablet eine tiefe UP werden.

650

Online

32.9k

Benutzer

83.2k

Themen

1.3m

Beiträge