Weiter zum Inhalt
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Hell
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dunkel
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Tester
  4. ...nicht in offiziellem Repo
  5. Life360 NextGeneration

NEWS

  • Neuer ioBroker-Blog online: Monatsrückblick März/April 2026
    BluefoxB
    Bluefox
    8
    1
    849

  • Verwendung von KI bitte immer deutlich kennzeichnen
    HomoranH
    Homoran
    10
    1
    637

  • Monatsrückblick Januar/Februar 2026 ist online!
    BluefoxB
    Bluefox
    18
    1
    1.1k

Life360 NextGeneration

Geplant Angeheftet Gesperrt Verschoben ...nicht in offiziellem Repo
copilotstandortgeo locationmap
155 Beiträge 17 Kommentatoren 4.4k Aufrufe 18 Beobachtet
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • M Offline
    M Offline
    mihas
    schrieb am zuletzt editiert von
    #92

    @skvarel ich weiß nicht, wie das passiert ist, aber ich bin weggegangen, nach einer Stunde zurückgekommen und dann hat schon alles funktioniert, komisch. Trotzdem danke für die Hilfe.

    skvarelS 1 Antwort Letzte Antwort
    1
    • M mihas

      @skvarel ich weiß nicht, wie das passiert ist, aber ich bin weggegangen, nach einer Stunde zurückgekommen und dann hat schon alles funktioniert, komisch. Trotzdem danke für die Hilfe.

      skvarelS Online
      skvarelS Online
      skvarel
      Developer
      schrieb am zuletzt editiert von
      #93

      @mihas sagte:

      @skvarel ich weiß nicht, wie das passiert ist, aber ich bin weggegangen, nach einer Stunde zurückgekommen und dann hat schon alles funktioniert, komisch. Trotzdem danke für die Hilfe.

      Schön das es jetzt läuft :)

      #TeamInventwo
      Unsere Adapter:
      Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

      Wer uns mit einem Kaffee unterstützen möchte: PayPal

      1 Antwort Letzte Antwort
      1
      • B Offline
        B Offline
        buflug453
        schrieb am zuletzt editiert von
        #94

        Bei den Datenpunkten unter myplaces gibt es Unstimmigkeiten bei mir. Ich habe 9 Orte definiert. Nur bei 4 Orten werden die Personen in Unterordnern angezeigt mit z.B. is_presence, distance, timestamp. Wenn ich an einem Ort bin is_presence = true sind die Spalten members_count, members_present für den Ort leer.

        skvarelS 1 Antwort Letzte Antwort
        0
        • B buflug453

          Bei den Datenpunkten unter myplaces gibt es Unstimmigkeiten bei mir. Ich habe 9 Orte definiert. Nur bei 4 Orten werden die Personen in Unterordnern angezeigt mit z.B. is_presence, distance, timestamp. Wenn ich an einem Ort bin is_presence = true sind die Spalten members_count, members_present für den Ort leer.

          skvarelS Online
          skvarelS Online
          skvarel
          Developer
          schrieb am zuletzt editiert von
          #95

          @buflug453 .. schaue ich mir nachher an

          #TeamInventwo
          Unsere Adapter:
          Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

          Wer uns mit einem Kaffee unterstützen möchte: PayPal

          1 Antwort Letzte Antwort
          0
          • B Offline
            B Offline
            buflug453
            schrieb am zuletzt editiert von buflug453
            #96

            Danke :) Als Ergänzung: Ich habe einen weiteren Ort angelegt. Ein Familienmitglied war dort und nur für diese Person wurden zwei DP erstellt: startTimestamp und endTimestamp.
            Edit: Im Log sind mir diese Meldungen aufgefallen:

            • Life360 places response body: "error code: 525"
            • Life360 places response body: "error code: 520"
            skvarelS 1 Antwort Letzte Antwort
            0
            • B buflug453

              Danke :) Als Ergänzung: Ich habe einen weiteren Ort angelegt. Ein Familienmitglied war dort und nur für diese Person wurden zwei DP erstellt: startTimestamp und endTimestamp.
              Edit: Im Log sind mir diese Meldungen aufgefallen:

              • Life360 places response body: "error code: 525"
              • Life360 places response body: "error code: 520"
              skvarelS Online
              skvarelS Online
              skvarel
              Developer
              schrieb am zuletzt editiert von
              #97

              @buflug453 .. ich bin gleich am Rechner und gucke, ob ich da was finde.

              Den Haken hast du aber drin?
              2a69361f-0a75-4d02-b776-ee27324d2d20-image.jpeg

              #TeamInventwo
              Unsere Adapter:
              Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

              Wer uns mit einem Kaffee unterstützen möchte: PayPal

              1 Antwort Letzte Antwort
              0
              • B Offline
                B Offline
                buflug453
                schrieb am zuletzt editiert von
                #98

                Standortverfolgung für Personen ist gesetzt. Ich hatte Life360-Kreise und -Orte deaktiviert, weil ich in der App keine Orte eingetragen habe. Ich habe es nun testweise aktiviert.

                1 Antwort Letzte Antwort
                0
                • B Offline
                  B Offline
                  buflug453
                  schrieb am zuletzt editiert von
                  #99

                  In locationName (Ordner: people ) ist, wenn ich mich in einem unter myplaces definierten Ort befinde, ebenfalls nichts eingetragen.

                  skvarelS 1 Antwort Letzte Antwort
                  0
                  • B buflug453

                    In locationName (Ordner: people ) ist, wenn ich mich in einem unter myplaces definierten Ort befinde, ebenfalls nichts eingetragen.

                    skvarelS Online
                    skvarelS Online
                    skvarel
                    Developer
                    schrieb am zuletzt editiert von skvarel
                    #100

                    @buflug453 sagte:

                    In locationName (Ordner: people ) ist, wenn ich mich in einem unter myplaces definierten Ort befinde, ebenfalls nichts eingetragen.

                    Dieser Ort kommt nur aus der App und nie aus myplaces
                    3a65d8e0-eeb8-429b-8939-bf3f11a08a0e-image.jpeg

                    In der App heisst der Ort 'Refugium340a' .... in myplaces heisst er 'zuhause'

                    So sollte es aussehen, wenn myplaces als Ort zutrifft
                    f8b3f575-c5b6-4515-b080-7c56b56c4d1f-image.jpeg

                    Kannst du mir einen Screenshot von deinem myplace zeigen?

                    So sieht es bei mir aus:
                    f4cf8017-3149-4cce-a3d8-c5c425dc67ad-image.jpeg

                    #TeamInventwo
                    Unsere Adapter:
                    Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                    Wer uns mit einem Kaffee unterstützen möchte: PayPal

                    1 Antwort Letzte Antwort
                    0
                    • B Offline
                      B Offline
                      buflug453
                      schrieb am zuletzt editiert von buflug453
                      #101

                      Das hat sich von selbst erledigt. Die anderen Datenpunkte sind nun auch für die anderen Orte verfügbar. MembersPresentCount ist nun auch korrekt befüllt.
                      Es kann mit der Einstellung zu tun haben, dass ich Life360-Kreise und -Orte nicht aktiv hatte. Das kann ich aber nicht mehr nachvollziehen.

                      skvarelS 1 Antwort Letzte Antwort
                      1
                      • B buflug453

                        Das hat sich von selbst erledigt. Die anderen Datenpunkte sind nun auch für die anderen Orte verfügbar. MembersPresentCount ist nun auch korrekt befüllt.
                        Es kann mit der Einstellung zu tun haben, dass ich Life360-Kreise und -Orte nicht aktiv hatte. Das kann ich aber nicht mehr nachvollziehen.

                        skvarelS Online
                        skvarelS Online
                        skvarel
                        Developer
                        schrieb am zuletzt editiert von
                        #102

                        @buflug453 .. super. Das freut mich :)

                        #TeamInventwo
                        Unsere Adapter:
                        Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                        Wer uns mit einem Kaffee unterstützen möchte: PayPal

                        1 Antwort Letzte Antwort
                        0
                        • stefu87_CHS Online
                          stefu87_CHS Online
                          stefu87_CH
                          schrieb am zuletzt editiert von stefu87_CH
                          #103

                          Beitrag erledigt

                          Model : AMD Ryzen 7 8745HS w/ Radeon 780M Graphics
                          Cores: 1 × 8 = 8 cores
                          Threads: 16
                          mit Proxmox
                          LXC Container mit Iobroker

                          1 Antwort Letzte Antwort
                          0
                          • Merlin123M Offline
                            Merlin123M Offline
                            Merlin123
                            schrieb am zuletzt editiert von Merlin123
                            #104

                            Kennt jemand ne Lösung um zu den Koordinaten den Ort automatisch ermitteln zu lassen?
                            Also man übergibt den Standort und bekommt die Adresse oder z.B. "Rewe Markt" oder sowas zurück?
                            Wäre ne nette Ergänzung 😀

                            UPDATE:
                            Hab das grad ne Idee.... Ich probier das mal und meld mich. Weiß aber nicht, ob ich das heute schaffe....

                            UPDATE 2:
                            hab was, läuft grad der Test

                            Beta-Tester

                            skvarelS 1 Antwort Letzte Antwort
                            1
                            • Merlin123M Merlin123

                              Kennt jemand ne Lösung um zu den Koordinaten den Ort automatisch ermitteln zu lassen?
                              Also man übergibt den Standort und bekommt die Adresse oder z.B. "Rewe Markt" oder sowas zurück?
                              Wäre ne nette Ergänzung 😀

                              UPDATE:
                              Hab das grad ne Idee.... Ich probier das mal und meld mich. Weiß aber nicht, ob ich das heute schaffe....

                              UPDATE 2:
                              hab was, läuft grad der Test

                              skvarelS Online
                              skvarelS Online
                              skvarel
                              Developer
                              schrieb am zuletzt editiert von
                              #105

                              @Merlin123 .. gern mehr dazu :)

                              #TeamInventwo
                              Unsere Adapter:
                              Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                              Wer uns mit einem Kaffee unterstützen möchte: PayPal

                              1 Antwort Letzte Antwort
                              0
                              • R Online
                                R Online
                                RikDRS
                                schrieb am zuletzt editiert von RikDRS
                                #106

                                ich muss gestehen, ich verstehe die App noch nicht... :/
                                Wann werden neue Standorte registriert als ein häufig besuchter Ort? Oder muss ich alle "relevanten" Standorte in der App oder im Adapter erfassen? Wie kommen "Location" in den Adapter von weiteren Anwendern (zB von meiner Frau, ohne das ich vor Ort gewesen oder sein muss
                                Ich habe zB die Arbeitsstätte meiner Frau im Adapter angelegt (so wie meine auch), allerdings wird der Standort meiner Frau bzw dem Handy dort nicht bzw als "noch nicht registriert" erfasst

                                ich habe nen Knoten im Kopf ;)

                                skvarelS 1 Antwort Letzte Antwort
                                0
                                • Merlin123M Offline
                                  Merlin123M Offline
                                  Merlin123
                                  schrieb am zuletzt editiert von
                                  #107

                                  hab mal ChatGPT genutzt (ganz nach dem Vorbild eines hier auch aktiven Mitglieds 😀 )
                                  Herausgekommen ist ein Script, dass aus den Koordinaten zum einen ein Lookup nach der Adresse macht und schaut, ob es im näheren Umkreis einen benannten Ort gibt.
                                  Das ganze ist so gemacht, dass die Abfrageintervalle ausreichend groß sind und auch nur bei Positionsänderungen ein Lookup gemacht wird, damit die Dienste keinen Streß machen.
                                  Das ganze wird dann in Datenpunkten gespeichert und kann in der VIS genutzt werden.
                                  Ist jetzt die 2. Version, hat sicher noch Verbesserungspotential.
                                  Hab mal meine Laufrunde für nen Test benutzt:
                                  pos1.jpeg

                                  Der erste Standort ist der aus dem Adapter, die Adresse und der Name darunter aus dem Script. Wenn kein Ort gefunden wird ist das entsprechende Feld leer

                                  in den DPs sieht es so aus:

                                  pos2.png

                                  Und hier ist das Script.
                                  ACHTUNG: Bitte im USER_AGENT eine echte Kontaktmöglichkeit eintragen. Sonst kann es sein, dass die Dienste Stress machen....

                                  Gibt auch diverse Parameter, die man anpassen kann.
                                  Ihr müsst halt die Datenpunkte im Script entsprechend euren DPs anpassen...
                                  Ich werde auch mal schauen, wie gut man das Script auf mehrere Personen skalieren kann.

                                  /************************************************************
                                   * Oliver Standort aus Life360 -> menschenlesbarer Text
                                   * 
                                   * Ausgabe:
                                   *   0_userdata.0.VIS.Position.Oliver.Standort_Oliver
                                   *
                                   * Beispiel-Ergebnis:
                                   *   "REWE, Musterstraße 1, 12345 Musterstadt"
                                   * oder
                                   *   "Musterstraße 1, 12345 Musterstadt"
                                   *
                                   * Voraussetzungen:
                                   * - JavaScript-Adapter in ioBroker
                                   * - Internetzugriff
                                   *
                                   * WICHTIG:
                                   * Bitte im USER_AGENT eine echte Kontaktmöglichkeit eintragen.
                                   ************************************************************/
                                  
                                  const LAT_STATE = 'life360ng.0.people.bxxxxxxe.latitude';
                                  const LON_STATE = 'life360ng.0.people.bxxxxxa7e.longitude';
                                  
                                  const TARGET_STATE = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver';
                                  
                                  // Optional: zusätzliche Debug-/Hilfsstates
                                  const ADDRESS_STATE = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver_Adresse';
                                  const POI_STATE     = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver_POI';
                                  const LASTLAT_STATE = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver_LastLat';
                                  const LASTLON_STATE = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver_LastLon';
                                  const LASTTS_STATE  = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver_LastLookupTs';
                                  
                                  // Einstellungen
                                  const MIN_INTERVAL_MS   = 60 * 1000; // max. 1 Lookup pro Minute
                                  const MIN_DISTANCE_M    = 40;        // nur neu abfragen, wenn mind. 40 m Bewegung
                                  const DEBOUNCE_MS       = 5000;      // 5 Sekunden warten, falls lat/lon nacheinander aktualisiert werden
                                  const POI_RADIUS_M      = 15;        // POI-Suche im Umkreis von 40 m
                                  const SECOND_TRY_RADIUS = 25;       // optionaler zweiter Radius, falls bei 40 m nichts gefunden
                                  const GAP_BETWEEN_CALLS = 1500;      // 1,5 s Pause zwischen Nominatim und Overpass
                                  
                                  // WICHTIG: bitte anpassen
                                  const USER_AGENT = 'ioBroker-Standortscript/1.0 (Kontakt: deine@mailadresse.de)';
                                  
                                  let debounceTimer = null;
                                  let lookupRunning = false;
                                  
                                  /* ---------------------------------------------------------
                                   * Initiale States anlegen
                                   * --------------------------------------------------------- */
                                  createStateIfMissing(TARGET_STATE, '', 'string', 'Oliver Standort');
                                  createStateIfMissing(ADDRESS_STATE, '', 'string', 'Oliver Adresse');
                                  createStateIfMissing(POI_STATE, '', 'string', 'Oliver POI');
                                  createStateIfMissing(LASTLAT_STATE, null, 'number', 'Oliver letzte Lookup-Latitude');
                                  createStateIfMissing(LASTLON_STATE, null, 'number', 'Oliver letzte Lookup-Longitude');
                                  createStateIfMissing(LASTTS_STATE, 0, 'number', 'Oliver letzter Lookup-Zeitstempel');
                                  
                                  /* ---------------------------------------------------------
                                   * Trigger
                                   * --------------------------------------------------------- */
                                  on({id: LAT_STATE, change: 'ne'}, scheduleLookup);
                                  on({id: LON_STATE, change: 'ne'}, scheduleLookup);
                                  
                                  // optional einmal beim Scriptstart prüfen
                                  scheduleLookup();
                                  
                                  /* ---------------------------------------------------------
                                   * Hauptlogik
                                   * --------------------------------------------------------- */
                                  function scheduleLookup() {
                                      if (debounceTimer) clearTimeout(debounceTimer);
                                      debounceTimer = setTimeout(() => {
                                          runLookup().catch(err => log('Standortscript Fehler: ' + err, 'error'));
                                      }, DEBOUNCE_MS);
                                  }
                                  
                                  async function runLookup() {
                                      if (lookupRunning) {
                                          log('Lookup läuft bereits, überspringe parallelen Aufruf.', 'debug');
                                          return;
                                      }
                                  
                                      const lat = parseFloat(getState(LAT_STATE).val);
                                      const lon = parseFloat(getState(LON_STATE).val);
                                  
                                      if (!isFinite(lat) || !isFinite(lon)) {
                                          log('Ungültige Koordinaten, Lookup abgebrochen.', 'warn');
                                          return;
                                      }
                                  
                                      if (!shouldLookup(lat, lon)) {
                                          log('Lookup übersprungen (Cooldown oder Bewegung zu gering).', 'debug');
                                          return;
                                      }
                                  
                                      lookupRunning = true;
                                  
                                      try {
                                          // 1) Adresse holen
                                          const addressInfo = await getAddressFromNominatim(lat, lon);
                                          const addressText = buildAddressText(addressInfo);
                                  
                                          setState(TARGET_STATE, addressText || 'Unbekannter Standort', true);
                                          setState(ADDRESS_STATE, addressText || '', true);
                                          setState(POI_STATE, '', true);
                                          
                                          // Lookup-Metadaten sofort speichern, sobald die Adresse erfolgreich ermittelt wurde
                                          setState(LASTLAT_STATE, lat, true);
                                          setState(LASTLON_STATE, lon, true);
                                          setState(LASTTS_STATE, Date.now(), true);
                                  
                                          // 2) bewusste Pause zwischen den Requests
                                          await sleep(GAP_BETWEEN_CALLS);
                                  
                                          // 3) generischen POI im Umkreis suchen
                                          let poi = await getNearbyNamedPoi(lat, lon, POI_RADIUS_M);
                                  
                                          // optional zweiter Versuch mit größerem Radius
                                          if (!poi) {
                                              poi = await getNearbyNamedPoi(lat, lon, SECOND_TRY_RADIUS);
                                          }
                                  
                                          let finalText = addressText || 'Unbekannter Standort';
                                  
                                          if (poi && poi.name) {
                                              setState(POI_STATE, poi.name, true);
                                  
                                              // Doppelte Infos vermeiden
                                              if (addressText && !addressText.toLowerCase().includes((poi.name || '').toLowerCase())) {
                                                  finalText = poi.name + ', ' + addressText;
                                              } else {
                                                  finalText = poi.name;
                                              }
                                          }
                                  
                                          setState(TARGET_STATE, finalText, true);
                                  
                                          log('Standort aktualisiert: ' + finalText, 'info');
                                      } catch (err) {
                                          log('Fehler beim Standort-Lookup: ' + err, 'error');
                                      } finally {
                                          lookupRunning = false;
                                      }
                                  }
                                  
                                  /* ---------------------------------------------------------
                                   * Entscheiden, ob Lookup nötig ist
                                   * --------------------------------------------------------- */
                                  function shouldLookup(lat, lon) {
                                      const lastTs  = Number(getState(LASTTS_STATE).val || 0);
                                      const lastLat = parseFloat(getState(LASTLAT_STATE).val);
                                      const lastLon = parseFloat(getState(LASTLON_STATE).val);
                                  
                                      const now = Date.now();
                                  
                                      if (lastTs && (now - lastTs) < MIN_INTERVAL_MS) {
                                          return false;
                                      }
                                  
                                      if (isFinite(lastLat) && isFinite(lastLon)) {
                                          const dist = distanceMeters(lastLat, lastLon, lat, lon);
                                          if (dist < MIN_DISTANCE_M) {
                                              return false;
                                          }
                                      }
                                  
                                      return true;
                                  }
                                  
                                  /* ---------------------------------------------------------
                                   * Nominatim Reverse Geocoding
                                   * --------------------------------------------------------- */
                                  async function getAddressFromNominatim(lat, lon) {
                                      const url =
                                          'https://nominatim.openstreetmap.org/reverse' +
                                          '?format=jsonv2' +
                                          '&lat=' + encodeURIComponent(lat) +
                                          '&lon=' + encodeURIComponent(lon) +
                                          '&addressdetails=1' +
                                          '&zoom=18';
                                  
                                      const response = await httpGetAsync(url, {
                                          'User-Agent': USER_AGENT,
                                          'Accept': 'application/json'
                                      });
                                  
                                      if (response.statusCode < 200 || response.statusCode >= 300) {
                                          throw new Error('Nominatim HTTP ' + response.statusCode);
                                      }
                                  
                                      let data;
                                      try {
                                          data = JSON.parse(response.data);
                                      } catch (e) {
                                          throw new Error('Nominatim JSON konnte nicht geparst werden');
                                      }
                                  
                                      return data;
                                  }
                                  
                                  function buildAddressText(data) {
                                      if (!data) return '';
                                  
                                      const a = data.address || {};
                                  
                                      const road =
                                          a.road ||
                                          a.pedestrian ||
                                          a.footway ||
                                          a.path ||
                                          a.cycleway ||
                                          '';
                                  
                                      const houseNumber = a.house_number || '';
                                      const postcode = a.postcode || '';
                                      const city =
                                          a.city ||
                                          a.town ||
                                          a.village ||
                                          a.hamlet ||
                                          a.municipality ||
                                          '';
                                  
                                      let result = '';
                                  
                                      if (road) {
                                          result += road;
                                          if (houseNumber) result += ' ' + houseNumber;
                                      }
                                  
                                      if (postcode || city) {
                                          if (result) result += ', ';
                                          result += [postcode, city].filter(Boolean).join(' ');
                                      }
                                  
                                      // Fallback auf display_name, falls die Einzelteile fehlen
                                      if (!result && data.display_name) {
                                          result = data.display_name;
                                      }
                                  
                                      return cleanText(result);
                                  }
                                  
                                  /* ---------------------------------------------------------
                                   * Overpass: benannten Ort/POI in der Nähe finden
                                   * --------------------------------------------------------- */
                                  async function getNearbyNamedPoi(lat, lon, radius) {
                                      // Priorisiert benannte Objekte, die für Menschen sinnvoll sind
                                      const query = `
                                  [out:json][timeout:10];
                                  (
                                    nwr(around:${radius},${lat},${lon})["name"]["shop"];
                                    nwr(around:${radius},${lat},${lon})["name"]["amenity"];
                                    nwr(around:${radius},${lat},${lon})["name"]["tourism"];
                                    nwr(around:${radius},${lat},${lon})["name"]["leisure"];
                                    nwr(around:${radius},${lat},${lon})["name"]["office"];
                                    nwr(around:${radius},${lat},${lon})["name"]["building"];
                                  );
                                  out center tags;
                                  `;
                                  
                                      const response = await httpPostAsync(
                                          'https://overpass-api.de/api/interpreter',
                                          query,
                                          {
                                              'User-Agent': USER_AGENT,
                                              'Content-Type': 'text/plain; charset=utf-8',
                                              'Accept': 'application/json'
                                          }
                                      );
                                  
                                      if (response.statusCode < 200 || response.statusCode >= 300) {
                                          throw new Error('Overpass HTTP ' + response.statusCode);
                                      }
                                  
                                      let data;
                                      try {
                                          data = JSON.parse(response.data);
                                      } catch (e) {
                                          throw new Error('Overpass JSON konnte nicht geparst werden');
                                      }
                                  
                                      if (!data.elements || !data.elements.length) {
                                          return null;
                                      }
                                  
                                      // nächsten sinnvollen Treffer bestimmen
                                      const candidates = data.elements
                                          .map(el => {
                                              const cLat = el.lat || (el.center && el.center.lat);
                                              const cLon = el.lon || (el.center && el.center.lon);
                                              const name = el.tags && el.tags.name ? String(el.tags.name).trim() : '';
                                              const dist = (isFinite(cLat) && isFinite(cLon))
                                                  ? distanceMeters(lat, lon, cLat, cLon)
                                                  : 999999;
                                  
                                              return {
                                                  name: cleanText(name),
                                                  tags: el.tags || {},
                                                  lat: cLat,
                                                  lon: cLon,
                                                  dist: dist
                                              };
                                          })
                                          .filter(x => x.name);
                                  
                                      if (!candidates.length) return null;
                                  
                                      // etwas priorisieren: shop/amenity/tourism/leisure vor building
                                      candidates.sort((a, b) => {
                                          const pa = poiPriority(a.tags);
                                          const pb = poiPriority(b.tags);
                                          if (pa !== pb) return pa - pb;
                                          return a.dist - b.dist;
                                      });
                                  
                                      return candidates[0];
                                  }
                                  
                                  function poiPriority(tags) {
                                      if (tags.shop) return 1;
                                      if (tags.amenity) return 2;
                                      if (tags.tourism) return 3;
                                      if (tags.leisure) return 4;
                                      if (tags.office) return 5;
                                      if (tags.building) return 9;
                                      return 99;
                                  }
                                  
                                  /* ---------------------------------------------------------
                                   * HTTP-Helfer
                                   * --------------------------------------------------------- */
                                  function httpGetAsync(url, headers) {
                                      return new Promise((resolve, reject) => {
                                          httpGet(url, { headers: headers, timeout: 15000 }, (err, response) => {
                                              if (err) return reject(err);
                                              resolve(response);
                                          });
                                      });
                                  }
                                  
                                  function httpPostAsync(url, body, headers) {
                                      return new Promise((resolve, reject) => {
                                          httpPost(url, body, { headers: headers, timeout: 20000 }, (err, response) => {
                                              if (err) return reject(err);
                                              resolve(response);
                                          });
                                      });
                                  }
                                  
                                  function sleep(ms) {
                                      return new Promise(resolve => setTimeout(resolve, ms));
                                  }
                                  
                                  /* ---------------------------------------------------------
                                   * Mathe / Hilfsfunktionen
                                   * --------------------------------------------------------- */
                                  function distanceMeters(lat1, lon1, lat2, lon2) {
                                      const R = 6371000;
                                      const toRad = deg => deg * Math.PI / 180;
                                  
                                      const dLat = toRad(lat2 - lat1);
                                      const dLon = toRad(lon2 - lon1);
                                  
                                      const a =
                                          Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                                          Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
                                          Math.sin(dLon / 2) * Math.sin(dLon / 2);
                                  
                                      return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                                  }
                                  
                                  function cleanText(str) {
                                      return String(str || '')
                                          .replace(/\s+/g, ' ')
                                          .replace(/\s+,/g, ',')
                                          .trim();
                                  }
                                  
                                  /* ---------------------------------------------------------
                                   * State-Helfer
                                   * --------------------------------------------------------- */
                                  function createStateIfMissing(id, def, type, name) {
                                      if (!existsState(id)) {
                                          createState(id, def, {
                                              name: name,
                                              type: type,
                                              role: 'text',
                                              read: true,
                                              write: true
                                          });
                                      }
                                  }
                                  

                                  Das ganz hat komplett ChatGPT gemacht... Bin durchaus sehr begeistert...... Meine JS Kenntnisse sind nur sehr rudimentär über "nicht vorhanden"....

                                  Beta-Tester

                                  skvarelS 1 Antwort Letzte Antwort
                                  1
                                  • Merlin123M Merlin123

                                    hab mal ChatGPT genutzt (ganz nach dem Vorbild eines hier auch aktiven Mitglieds 😀 )
                                    Herausgekommen ist ein Script, dass aus den Koordinaten zum einen ein Lookup nach der Adresse macht und schaut, ob es im näheren Umkreis einen benannten Ort gibt.
                                    Das ganze ist so gemacht, dass die Abfrageintervalle ausreichend groß sind und auch nur bei Positionsänderungen ein Lookup gemacht wird, damit die Dienste keinen Streß machen.
                                    Das ganze wird dann in Datenpunkten gespeichert und kann in der VIS genutzt werden.
                                    Ist jetzt die 2. Version, hat sicher noch Verbesserungspotential.
                                    Hab mal meine Laufrunde für nen Test benutzt:
                                    pos1.jpeg

                                    Der erste Standort ist der aus dem Adapter, die Adresse und der Name darunter aus dem Script. Wenn kein Ort gefunden wird ist das entsprechende Feld leer

                                    in den DPs sieht es so aus:

                                    pos2.png

                                    Und hier ist das Script.
                                    ACHTUNG: Bitte im USER_AGENT eine echte Kontaktmöglichkeit eintragen. Sonst kann es sein, dass die Dienste Stress machen....

                                    Gibt auch diverse Parameter, die man anpassen kann.
                                    Ihr müsst halt die Datenpunkte im Script entsprechend euren DPs anpassen...
                                    Ich werde auch mal schauen, wie gut man das Script auf mehrere Personen skalieren kann.

                                    /************************************************************
                                     * Oliver Standort aus Life360 -> menschenlesbarer Text
                                     * 
                                     * Ausgabe:
                                     *   0_userdata.0.VIS.Position.Oliver.Standort_Oliver
                                     *
                                     * Beispiel-Ergebnis:
                                     *   "REWE, Musterstraße 1, 12345 Musterstadt"
                                     * oder
                                     *   "Musterstraße 1, 12345 Musterstadt"
                                     *
                                     * Voraussetzungen:
                                     * - JavaScript-Adapter in ioBroker
                                     * - Internetzugriff
                                     *
                                     * WICHTIG:
                                     * Bitte im USER_AGENT eine echte Kontaktmöglichkeit eintragen.
                                     ************************************************************/
                                    
                                    const LAT_STATE = 'life360ng.0.people.bxxxxxxe.latitude';
                                    const LON_STATE = 'life360ng.0.people.bxxxxxa7e.longitude';
                                    
                                    const TARGET_STATE = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver';
                                    
                                    // Optional: zusätzliche Debug-/Hilfsstates
                                    const ADDRESS_STATE = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver_Adresse';
                                    const POI_STATE     = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver_POI';
                                    const LASTLAT_STATE = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver_LastLat';
                                    const LASTLON_STATE = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver_LastLon';
                                    const LASTTS_STATE  = '0_userdata.0.VIS.Position.Oliver.Standort_Oliver_LastLookupTs';
                                    
                                    // Einstellungen
                                    const MIN_INTERVAL_MS   = 60 * 1000; // max. 1 Lookup pro Minute
                                    const MIN_DISTANCE_M    = 40;        // nur neu abfragen, wenn mind. 40 m Bewegung
                                    const DEBOUNCE_MS       = 5000;      // 5 Sekunden warten, falls lat/lon nacheinander aktualisiert werden
                                    const POI_RADIUS_M      = 15;        // POI-Suche im Umkreis von 40 m
                                    const SECOND_TRY_RADIUS = 25;       // optionaler zweiter Radius, falls bei 40 m nichts gefunden
                                    const GAP_BETWEEN_CALLS = 1500;      // 1,5 s Pause zwischen Nominatim und Overpass
                                    
                                    // WICHTIG: bitte anpassen
                                    const USER_AGENT = 'ioBroker-Standortscript/1.0 (Kontakt: deine@mailadresse.de)';
                                    
                                    let debounceTimer = null;
                                    let lookupRunning = false;
                                    
                                    /* ---------------------------------------------------------
                                     * Initiale States anlegen
                                     * --------------------------------------------------------- */
                                    createStateIfMissing(TARGET_STATE, '', 'string', 'Oliver Standort');
                                    createStateIfMissing(ADDRESS_STATE, '', 'string', 'Oliver Adresse');
                                    createStateIfMissing(POI_STATE, '', 'string', 'Oliver POI');
                                    createStateIfMissing(LASTLAT_STATE, null, 'number', 'Oliver letzte Lookup-Latitude');
                                    createStateIfMissing(LASTLON_STATE, null, 'number', 'Oliver letzte Lookup-Longitude');
                                    createStateIfMissing(LASTTS_STATE, 0, 'number', 'Oliver letzter Lookup-Zeitstempel');
                                    
                                    /* ---------------------------------------------------------
                                     * Trigger
                                     * --------------------------------------------------------- */
                                    on({id: LAT_STATE, change: 'ne'}, scheduleLookup);
                                    on({id: LON_STATE, change: 'ne'}, scheduleLookup);
                                    
                                    // optional einmal beim Scriptstart prüfen
                                    scheduleLookup();
                                    
                                    /* ---------------------------------------------------------
                                     * Hauptlogik
                                     * --------------------------------------------------------- */
                                    function scheduleLookup() {
                                        if (debounceTimer) clearTimeout(debounceTimer);
                                        debounceTimer = setTimeout(() => {
                                            runLookup().catch(err => log('Standortscript Fehler: ' + err, 'error'));
                                        }, DEBOUNCE_MS);
                                    }
                                    
                                    async function runLookup() {
                                        if (lookupRunning) {
                                            log('Lookup läuft bereits, überspringe parallelen Aufruf.', 'debug');
                                            return;
                                        }
                                    
                                        const lat = parseFloat(getState(LAT_STATE).val);
                                        const lon = parseFloat(getState(LON_STATE).val);
                                    
                                        if (!isFinite(lat) || !isFinite(lon)) {
                                            log('Ungültige Koordinaten, Lookup abgebrochen.', 'warn');
                                            return;
                                        }
                                    
                                        if (!shouldLookup(lat, lon)) {
                                            log('Lookup übersprungen (Cooldown oder Bewegung zu gering).', 'debug');
                                            return;
                                        }
                                    
                                        lookupRunning = true;
                                    
                                        try {
                                            // 1) Adresse holen
                                            const addressInfo = await getAddressFromNominatim(lat, lon);
                                            const addressText = buildAddressText(addressInfo);
                                    
                                            setState(TARGET_STATE, addressText || 'Unbekannter Standort', true);
                                            setState(ADDRESS_STATE, addressText || '', true);
                                            setState(POI_STATE, '', true);
                                            
                                            // Lookup-Metadaten sofort speichern, sobald die Adresse erfolgreich ermittelt wurde
                                            setState(LASTLAT_STATE, lat, true);
                                            setState(LASTLON_STATE, lon, true);
                                            setState(LASTTS_STATE, Date.now(), true);
                                    
                                            // 2) bewusste Pause zwischen den Requests
                                            await sleep(GAP_BETWEEN_CALLS);
                                    
                                            // 3) generischen POI im Umkreis suchen
                                            let poi = await getNearbyNamedPoi(lat, lon, POI_RADIUS_M);
                                    
                                            // optional zweiter Versuch mit größerem Radius
                                            if (!poi) {
                                                poi = await getNearbyNamedPoi(lat, lon, SECOND_TRY_RADIUS);
                                            }
                                    
                                            let finalText = addressText || 'Unbekannter Standort';
                                    
                                            if (poi && poi.name) {
                                                setState(POI_STATE, poi.name, true);
                                    
                                                // Doppelte Infos vermeiden
                                                if (addressText && !addressText.toLowerCase().includes((poi.name || '').toLowerCase())) {
                                                    finalText = poi.name + ', ' + addressText;
                                                } else {
                                                    finalText = poi.name;
                                                }
                                            }
                                    
                                            setState(TARGET_STATE, finalText, true);
                                    
                                            log('Standort aktualisiert: ' + finalText, 'info');
                                        } catch (err) {
                                            log('Fehler beim Standort-Lookup: ' + err, 'error');
                                        } finally {
                                            lookupRunning = false;
                                        }
                                    }
                                    
                                    /* ---------------------------------------------------------
                                     * Entscheiden, ob Lookup nötig ist
                                     * --------------------------------------------------------- */
                                    function shouldLookup(lat, lon) {
                                        const lastTs  = Number(getState(LASTTS_STATE).val || 0);
                                        const lastLat = parseFloat(getState(LASTLAT_STATE).val);
                                        const lastLon = parseFloat(getState(LASTLON_STATE).val);
                                    
                                        const now = Date.now();
                                    
                                        if (lastTs && (now - lastTs) < MIN_INTERVAL_MS) {
                                            return false;
                                        }
                                    
                                        if (isFinite(lastLat) && isFinite(lastLon)) {
                                            const dist = distanceMeters(lastLat, lastLon, lat, lon);
                                            if (dist < MIN_DISTANCE_M) {
                                                return false;
                                            }
                                        }
                                    
                                        return true;
                                    }
                                    
                                    /* ---------------------------------------------------------
                                     * Nominatim Reverse Geocoding
                                     * --------------------------------------------------------- */
                                    async function getAddressFromNominatim(lat, lon) {
                                        const url =
                                            'https://nominatim.openstreetmap.org/reverse' +
                                            '?format=jsonv2' +
                                            '&lat=' + encodeURIComponent(lat) +
                                            '&lon=' + encodeURIComponent(lon) +
                                            '&addressdetails=1' +
                                            '&zoom=18';
                                    
                                        const response = await httpGetAsync(url, {
                                            'User-Agent': USER_AGENT,
                                            'Accept': 'application/json'
                                        });
                                    
                                        if (response.statusCode < 200 || response.statusCode >= 300) {
                                            throw new Error('Nominatim HTTP ' + response.statusCode);
                                        }
                                    
                                        let data;
                                        try {
                                            data = JSON.parse(response.data);
                                        } catch (e) {
                                            throw new Error('Nominatim JSON konnte nicht geparst werden');
                                        }
                                    
                                        return data;
                                    }
                                    
                                    function buildAddressText(data) {
                                        if (!data) return '';
                                    
                                        const a = data.address || {};
                                    
                                        const road =
                                            a.road ||
                                            a.pedestrian ||
                                            a.footway ||
                                            a.path ||
                                            a.cycleway ||
                                            '';
                                    
                                        const houseNumber = a.house_number || '';
                                        const postcode = a.postcode || '';
                                        const city =
                                            a.city ||
                                            a.town ||
                                            a.village ||
                                            a.hamlet ||
                                            a.municipality ||
                                            '';
                                    
                                        let result = '';
                                    
                                        if (road) {
                                            result += road;
                                            if (houseNumber) result += ' ' + houseNumber;
                                        }
                                    
                                        if (postcode || city) {
                                            if (result) result += ', ';
                                            result += [postcode, city].filter(Boolean).join(' ');
                                        }
                                    
                                        // Fallback auf display_name, falls die Einzelteile fehlen
                                        if (!result && data.display_name) {
                                            result = data.display_name;
                                        }
                                    
                                        return cleanText(result);
                                    }
                                    
                                    /* ---------------------------------------------------------
                                     * Overpass: benannten Ort/POI in der Nähe finden
                                     * --------------------------------------------------------- */
                                    async function getNearbyNamedPoi(lat, lon, radius) {
                                        // Priorisiert benannte Objekte, die für Menschen sinnvoll sind
                                        const query = `
                                    [out:json][timeout:10];
                                    (
                                      nwr(around:${radius},${lat},${lon})["name"]["shop"];
                                      nwr(around:${radius},${lat},${lon})["name"]["amenity"];
                                      nwr(around:${radius},${lat},${lon})["name"]["tourism"];
                                      nwr(around:${radius},${lat},${lon})["name"]["leisure"];
                                      nwr(around:${radius},${lat},${lon})["name"]["office"];
                                      nwr(around:${radius},${lat},${lon})["name"]["building"];
                                    );
                                    out center tags;
                                    `;
                                    
                                        const response = await httpPostAsync(
                                            'https://overpass-api.de/api/interpreter',
                                            query,
                                            {
                                                'User-Agent': USER_AGENT,
                                                'Content-Type': 'text/plain; charset=utf-8',
                                                'Accept': 'application/json'
                                            }
                                        );
                                    
                                        if (response.statusCode < 200 || response.statusCode >= 300) {
                                            throw new Error('Overpass HTTP ' + response.statusCode);
                                        }
                                    
                                        let data;
                                        try {
                                            data = JSON.parse(response.data);
                                        } catch (e) {
                                            throw new Error('Overpass JSON konnte nicht geparst werden');
                                        }
                                    
                                        if (!data.elements || !data.elements.length) {
                                            return null;
                                        }
                                    
                                        // nächsten sinnvollen Treffer bestimmen
                                        const candidates = data.elements
                                            .map(el => {
                                                const cLat = el.lat || (el.center && el.center.lat);
                                                const cLon = el.lon || (el.center && el.center.lon);
                                                const name = el.tags && el.tags.name ? String(el.tags.name).trim() : '';
                                                const dist = (isFinite(cLat) && isFinite(cLon))
                                                    ? distanceMeters(lat, lon, cLat, cLon)
                                                    : 999999;
                                    
                                                return {
                                                    name: cleanText(name),
                                                    tags: el.tags || {},
                                                    lat: cLat,
                                                    lon: cLon,
                                                    dist: dist
                                                };
                                            })
                                            .filter(x => x.name);
                                    
                                        if (!candidates.length) return null;
                                    
                                        // etwas priorisieren: shop/amenity/tourism/leisure vor building
                                        candidates.sort((a, b) => {
                                            const pa = poiPriority(a.tags);
                                            const pb = poiPriority(b.tags);
                                            if (pa !== pb) return pa - pb;
                                            return a.dist - b.dist;
                                        });
                                    
                                        return candidates[0];
                                    }
                                    
                                    function poiPriority(tags) {
                                        if (tags.shop) return 1;
                                        if (tags.amenity) return 2;
                                        if (tags.tourism) return 3;
                                        if (tags.leisure) return 4;
                                        if (tags.office) return 5;
                                        if (tags.building) return 9;
                                        return 99;
                                    }
                                    
                                    /* ---------------------------------------------------------
                                     * HTTP-Helfer
                                     * --------------------------------------------------------- */
                                    function httpGetAsync(url, headers) {
                                        return new Promise((resolve, reject) => {
                                            httpGet(url, { headers: headers, timeout: 15000 }, (err, response) => {
                                                if (err) return reject(err);
                                                resolve(response);
                                            });
                                        });
                                    }
                                    
                                    function httpPostAsync(url, body, headers) {
                                        return new Promise((resolve, reject) => {
                                            httpPost(url, body, { headers: headers, timeout: 20000 }, (err, response) => {
                                                if (err) return reject(err);
                                                resolve(response);
                                            });
                                        });
                                    }
                                    
                                    function sleep(ms) {
                                        return new Promise(resolve => setTimeout(resolve, ms));
                                    }
                                    
                                    /* ---------------------------------------------------------
                                     * Mathe / Hilfsfunktionen
                                     * --------------------------------------------------------- */
                                    function distanceMeters(lat1, lon1, lat2, lon2) {
                                        const R = 6371000;
                                        const toRad = deg => deg * Math.PI / 180;
                                    
                                        const dLat = toRad(lat2 - lat1);
                                        const dLon = toRad(lon2 - lon1);
                                    
                                        const a =
                                            Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                                            Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
                                            Math.sin(dLon / 2) * Math.sin(dLon / 2);
                                    
                                        return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                                    }
                                    
                                    function cleanText(str) {
                                        return String(str || '')
                                            .replace(/\s+/g, ' ')
                                            .replace(/\s+,/g, ',')
                                            .trim();
                                    }
                                    
                                    /* ---------------------------------------------------------
                                     * State-Helfer
                                     * --------------------------------------------------------- */
                                    function createStateIfMissing(id, def, type, name) {
                                        if (!existsState(id)) {
                                            createState(id, def, {
                                                name: name,
                                                type: type,
                                                role: 'text',
                                                read: true,
                                                write: true
                                            });
                                        }
                                    }
                                    

                                    Das ganz hat komplett ChatGPT gemacht... Bin durchaus sehr begeistert...... Meine JS Kenntnisse sind nur sehr rudimentär über "nicht vorhanden"....

                                    skvarelS Online
                                    skvarelS Online
                                    skvarel
                                    Developer
                                    schrieb am zuletzt editiert von
                                    #108

                                    @Merlin123 .. ich mag Chat GPT nicht so wirklich beim Scripten. Ich schaue mit das aber mal mit Copilot zusammen an ;)

                                    #TeamInventwo
                                    Unsere Adapter:
                                    Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                                    Wer uns mit einem Kaffee unterstützen möchte: PayPal

                                    1 Antwort Letzte Antwort
                                    1
                                    • R RikDRS

                                      ich muss gestehen, ich verstehe die App noch nicht... :/
                                      Wann werden neue Standorte registriert als ein häufig besuchter Ort? Oder muss ich alle "relevanten" Standorte in der App oder im Adapter erfassen? Wie kommen "Location" in den Adapter von weiteren Anwendern (zB von meiner Frau, ohne das ich vor Ort gewesen oder sein muss
                                      Ich habe zB die Arbeitsstätte meiner Frau im Adapter angelegt (so wie meine auch), allerdings wird der Standort meiner Frau bzw dem Handy dort nicht bzw als "noch nicht registriert" erfasst

                                      ich habe nen Knoten im Kopf ;)

                                      skvarelS Online
                                      skvarelS Online
                                      skvarel
                                      Developer
                                      schrieb am zuletzt editiert von skvarel
                                      #109

                                      @RikDRS sagte:

                                      ich muss gestehen, ich verstehe die App noch nicht... :/
                                      Wann werden neue Standorte registriert als ein häufig besuchter Ort? Oder muss ich alle "relevanten" Standorte in der App oder im Adapter erfassen? Wie kommen "Location" in den Adapter von weiteren Anwendern (zB von meiner Frau, ohne das ich vor Ort gewesen oder sein muss
                                      Ich habe zB die Arbeitsstätte meiner Frau im Adapter angelegt (so wie meine auch), allerdings wird der Standort meiner Frau bzw dem Handy dort nicht bzw als "noch nicht registriert" erfasst

                                      ich habe nen Knoten im Kopf ;)

                                      Die App kann ich dir hier nicht erklären, das würde den Rahmen sprengen.

                                      Von allein wird da kein Standort angelegt, das muss man händisch machen. Entweder in der App, dann kommen die Orte auch in den ioBroker oder aber direkt im ioBroker, die gehen aber nicht in die App über! Der Adapter ist eine Einbahnstraße und ließt nur die App aus.

                                      #TeamInventwo
                                      Unsere Adapter:
                                      Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                                      Wer uns mit einem Kaffee unterstützen möchte: PayPal

                                      1 Antwort Letzte Antwort
                                      1
                                      • Merlin123M Offline
                                        Merlin123M Offline
                                        Merlin123
                                        schrieb am zuletzt editiert von
                                        #110

                                        Hier ne version für mehrere Personen, aber noch nicht vollständig getestet...

                                        /************************************************************
                                         * Mehrpersonen-Standortscript für ioBroker + Life360
                                         *
                                         * Für jede Person:
                                         * - Reverse Geocoding per Nominatim
                                         * - optional POI-Suche per Overpass
                                         * - Ausgabe in eigenen States
                                         *
                                         * WICHTIG:
                                         * USER_AGENT bitte mit echter Kontaktmöglichkeit anpassen.
                                         ************************************************************/
                                        
                                        const PERSONS = [
                                            {
                                                key: 'Oliver',
                                                latState: 'life360ng.0.people.bxxxe.latitude',
                                                lonState: 'life360ng.0.people.b9xxxxx7e.longitude',
                                                targetState: '0_userdata.0.VIS.Position.Standort_Oliver',
                                                baseState: '0_userdata.0.VIS.Position.Oliver'
                                            },
                                            {
                                                key: 'Hannah',
                                                latState: 'life360ng.0.people.f7xxx9d.latitude',
                                                lonState: 'life360ng.0.people.f7fxxx9d.longitude',
                                                targetState: '0_userdata.0.VIS.Position.Standort_Hannah',
                                                baseState: '0_userdata.0.VIS.Position.Hannah'
                                            },
                                            {
                                                key: 'Marleen',
                                                latState: 'life360ng.0.people.88xxxea.latitude',
                                                lonState: 'life360ng.0.people.88xxxaea.longitude',
                                                targetState: '0_userdata.0.VIS.Position.Standort_Marleen',
                                                baseState: '0_userdata.0.VIS.Position.Marleen'
                                            }
                                        ];
                                        
                                        // Einstellungen
                                        const MIN_INTERVAL_MS            = 60 * 1000; // max. 1 Lookup pro Person pro Minute
                                        const MIN_DISTANCE_M             = 40;        // nur neu abfragen, wenn mind. 40 m Bewegung
                                        const DEBOUNCE_MS                = 5000;      // 5 Sekunden warten, falls lat/lon nacheinander aktualisiert werden
                                        const POI_RADIUS_M               = 15;        // erste POI-Suche
                                        const SECOND_TRY_RADIUS          = 25;        // optionaler zweiter Versuch
                                        const MAX_ACCEPTED_POI_DISTANCE  = 20;        // nur wirklich nahe Treffer übernehmen
                                        const GAP_BETWEEN_CALLS          = 1500;      // Pause zwischen Nominatim und Overpass
                                        
                                        // WICHTIG: anpassen
                                        const USER_AGENT = 'ioBroker-Standortscript/1.0 (Kontakt: deine-mail@example.com)';
                                        
                                        // Laufzeitstatus pro Person
                                        const runtime = {};
                                        
                                        /* ---------------------------------------------------------
                                         * Initialisierung
                                         * --------------------------------------------------------- */
                                        for (const person of PERSONS) {
                                            person.addressState = `${person.baseState}.Standort_${person.key}_Adresse`;
                                            person.poiState     = `${person.baseState}.Standort_${person.key}_POI`;
                                            person.lastLatState = `${person.baseState}.Standort_${person.key}_LastLat`;
                                            person.lastLonState = `${person.baseState}.Standort_${person.key}_LastLon`;
                                            person.lastTsState  = `${person.baseState}.Standort_${person.key}_LastLookupTs`;
                                        
                                            runtime[person.key] = {
                                                debounceTimer: null,
                                                lookupRunning: false
                                            };
                                        
                                            createStateIfMissing(person.targetState,  '', 'string', `${person.key} Standort`);
                                            createStateIfMissing(person.addressState, '', 'string', `${person.key} Adresse`);
                                            createStateIfMissing(person.poiState,     '', 'string', `${person.key} POI`);
                                            createStateIfMissing(person.lastLatState, 0,  'number', `${person.key} letzte Lookup-Latitude`);
                                            createStateIfMissing(person.lastLonState, 0,  'number', `${person.key} letzte Lookup-Longitude`);
                                            createStateIfMissing(person.lastTsState,  0,  'number', `${person.key} letzter Lookup-Zeitstempel`);
                                        
                                            on({ id: person.latState, change: 'ne' }, () => scheduleLookup(person));
                                            on({ id: person.lonState, change: 'ne' }, () => scheduleLookup(person));
                                        
                                            scheduleLookup(person);
                                        }
                                        
                                        /* ---------------------------------------------------------
                                         * Hauptlogik
                                         * --------------------------------------------------------- */
                                        function scheduleLookup(person) {
                                            const rt = runtime[person.key];
                                        
                                            if (rt.debounceTimer) clearTimeout(rt.debounceTimer);
                                        
                                            rt.debounceTimer = setTimeout(() => {
                                                runLookup(person).catch(err => log(`${person.key}: Standortscript Fehler: ${err}`, 'error'));
                                            }, DEBOUNCE_MS);
                                        }
                                        
                                        async function runLookup(person) {
                                            const rt = runtime[person.key];
                                        
                                            if (rt.lookupRunning) {
                                                log(`${person.key}: Lookup läuft bereits, überspringe parallelen Aufruf.`, 'debug');
                                                return;
                                            }
                                        
                                            const lat = parseFloat(getState(person.latState).val);
                                            const lon = parseFloat(getState(person.lonState).val);
                                        
                                            if (!isFinite(lat) || !isFinite(lon)) {
                                                log(`${person.key}: Ungültige Koordinaten, Lookup abgebrochen.`, 'warn');
                                                return;
                                            }
                                        
                                            if (!shouldLookup(person, lat, lon)) {
                                                log(`${person.key}: Lookup übersprungen (Cooldown oder Bewegung zu gering).`, 'debug');
                                                return;
                                            }
                                        
                                            rt.lookupRunning = true;
                                        
                                            try {
                                                const addressInfo = await getAddressFromNominatim(lat, lon);
                                                const addressText = buildAddressText(addressInfo);
                                        
                                                setState(person.addressState, addressText || '', true);
                                                setState(person.targetState, addressText || 'Unbekannter Standort', true);
                                                setState(person.poiState, '', true);
                                        
                                                setState(person.lastLatState, lat, true);
                                                setState(person.lastLonState, lon, true);
                                                setState(person.lastTsState, Date.now(), true);
                                        
                                                await sleep(GAP_BETWEEN_CALLS);
                                        
                                                let poi = await getNearbyNamedPoi(lat, lon, POI_RADIUS_M);
                                        
                                                if (!poi) {
                                                    poi = await getNearbyNamedPoi(lat, lon, SECOND_TRY_RADIUS);
                                                }
                                        
                                                let finalText = addressText || 'Unbekannter Standort';
                                        
                                                if (poi && poi.name) {
                                                    setState(person.poiState, poi.name, true);
                                        
                                                    if (addressText && !addressText.toLowerCase().includes(poi.name.toLowerCase())) {
                                                        finalText = poi.name + ', ' + addressText;
                                                    } else {
                                                        finalText = poi.name;
                                                    }
                                                }
                                        
                                                setState(person.targetState, finalText, true);
                                                log(`${person.key}: Standort aktualisiert: ${finalText}`, 'info');
                                        
                                            } catch (err) {
                                                log(`${person.key}: Fehler beim Standort-Lookup: ${err}`, 'error');
                                            } finally {
                                                rt.lookupRunning = false;
                                            }
                                        }
                                        
                                        function shouldLookup(person, lat, lon) {
                                            const lastTsRaw  = getState(person.lastTsState).val;
                                            const lastLatRaw = getState(person.lastLatState).val;
                                            const lastLonRaw = getState(person.lastLonState).val;
                                        
                                            const lastTs  = Number(lastTsRaw || 0);
                                            const lastLat = parseFloat(lastLatRaw);
                                            const lastLon = parseFloat(lastLonRaw);
                                        
                                            const now = Date.now();
                                        
                                            if (lastTs > 0 && (now - lastTs) < MIN_INTERVAL_MS) {
                                                return false;
                                            }
                                        
                                            if (isFinite(lastLat) && isFinite(lastLon) && !(lastLat === 0 && lastLon === 0)) {
                                                const dist = distanceMeters(lastLat, lastLon, lat, lon);
                                                if (dist < MIN_DISTANCE_M) {
                                                    return false;
                                                }
                                            }
                                        
                                            return true;
                                        }
                                        
                                        /* ---------------------------------------------------------
                                         * Nominatim Reverse Geocoding
                                         * --------------------------------------------------------- */
                                        async function getAddressFromNominatim(lat, lon) {
                                            const url =
                                                'https://nominatim.openstreetmap.org/reverse' +
                                                '?format=jsonv2' +
                                                '&lat=' + encodeURIComponent(lat) +
                                                '&lon=' + encodeURIComponent(lon) +
                                                '&addressdetails=1' +
                                                '&zoom=18';
                                        
                                            const response = await httpGetAsync(url, {
                                                'User-Agent': USER_AGENT,
                                                'Accept': 'application/json'
                                            });
                                        
                                            if (response.statusCode < 200 || response.statusCode >= 300) {
                                                throw new Error('Nominatim HTTP ' + response.statusCode);
                                            }
                                        
                                            let data;
                                            try {
                                                data = JSON.parse(response.data);
                                            } catch (e) {
                                                throw new Error('Nominatim JSON konnte nicht geparst werden');
                                            }
                                        
                                            return data;
                                        }
                                        
                                        function buildAddressText(data) {
                                            if (!data) return '';
                                        
                                            const a = data.address || {};
                                        
                                            const road =
                                                a.road ||
                                                a.pedestrian ||
                                                a.footway ||
                                                a.path ||
                                                a.cycleway ||
                                                '';
                                        
                                            const houseNumber = a.house_number || '';
                                            const postcode = a.postcode || '';
                                            const city =
                                                a.city ||
                                                a.town ||
                                                a.village ||
                                                a.hamlet ||
                                                a.municipality ||
                                                '';
                                        
                                            let result = '';
                                        
                                            if (road) {
                                                result += road;
                                                if (houseNumber) result += ' ' + houseNumber;
                                            }
                                        
                                            if (postcode || city) {
                                                if (result) result += ', ';
                                                result += [postcode, city].filter(Boolean).join(' ');
                                            }
                                        
                                            if (!result && data.display_name) {
                                                result = data.display_name;
                                            }
                                        
                                            return cleanText(result);
                                        }
                                        
                                        /* ---------------------------------------------------------
                                         * Overpass: benannten Ort/POI in der Nähe finden
                                         * --------------------------------------------------------- */
                                        async function getNearbyNamedPoi(lat, lon, radius) {
                                            const query = `
                                        [out:json][timeout:10];
                                        (
                                          nwr(around:${radius},${lat},${lon})["name"]["shop"];
                                          nwr(around:${radius},${lat},${lon})["name"]["amenity"];
                                          nwr(around:${radius},${lat},${lon})["name"]["tourism"];
                                          nwr(around:${radius},${lat},${lon})["name"]["leisure"];
                                          nwr(around:${radius},${lat},${lon})["name"]["office"];
                                        );
                                        out center tags;
                                        `;
                                        
                                            const response = await httpPostAsync(
                                                'https://overpass-api.de/api/interpreter',
                                                query,
                                                {
                                                    'User-Agent': USER_AGENT,
                                                    'Content-Type': 'text/plain; charset=utf-8',
                                                    'Accept': 'application/json'
                                                }
                                            );
                                        
                                            if (response.statusCode < 200 || response.statusCode >= 300) {
                                                throw new Error('Overpass HTTP ' + response.statusCode);
                                            }
                                        
                                            let data;
                                            try {
                                                data = JSON.parse(response.data);
                                            } catch (e) {
                                                throw new Error('Overpass JSON konnte nicht geparst werden');
                                            }
                                        
                                            if (!data.elements || !data.elements.length) {
                                                return null;
                                            }
                                        
                                            const candidates = data.elements
                                                .map(el => {
                                                    const cLat = el.lat || (el.center && el.center.lat);
                                                    const cLon = el.lon || (el.center && el.center.lon);
                                                    const name = el.tags && el.tags.name ? String(el.tags.name).trim() : '';
                                                    const dist = (isFinite(cLat) && isFinite(cLon))
                                                        ? distanceMeters(lat, lon, cLat, cLon)
                                                        : 999999;
                                        
                                                    return {
                                                        name: cleanText(name),
                                                        tags: el.tags || {},
                                                        dist: dist
                                                    };
                                                })
                                                .filter(x => x.name);
                                        
                                            if (!candidates.length) return null;
                                        
                                            candidates.sort((a, b) => {
                                                const pa = poiPriority(a.tags);
                                                const pb = poiPriority(b.tags);
                                                if (pa !== pb) return pa - pb;
                                                return a.dist - b.dist;
                                            });
                                        
                                            const best = candidates[0];
                                            if (!best || best.dist > MAX_ACCEPTED_POI_DISTANCE) {
                                                return null;
                                            }
                                        
                                            return best;
                                        }
                                        
                                        function poiPriority(tags) {
                                            if (tags.shop) return 1;
                                            if (tags.amenity) return 2;
                                            if (tags.tourism) return 3;
                                            if (tags.leisure) return 4;
                                            if (tags.office) return 5;
                                            return 99;
                                        }
                                        
                                        /* ---------------------------------------------------------
                                         * HTTP-Helfer
                                         * --------------------------------------------------------- */
                                        function httpGetAsync(url, headers) {
                                            return new Promise((resolve, reject) => {
                                                httpGet(url, { headers: headers, timeout: 15000 }, (err, response) => {
                                                    if (err) return reject(err);
                                                    resolve(response);
                                                });
                                            });
                                        }
                                        
                                        function httpPostAsync(url, body, headers) {
                                            return new Promise((resolve, reject) => {
                                                httpPost(url, body, { headers: headers, timeout: 20000 }, (err, response) => {
                                                    if (err) return reject(err);
                                                    resolve(response);
                                                });
                                            });
                                        }
                                        
                                        function sleep(ms) {
                                            return new Promise(resolve => setTimeout(resolve, ms));
                                        }
                                        
                                        /* ---------------------------------------------------------
                                         * Hilfsfunktionen
                                         * --------------------------------------------------------- */
                                        function distanceMeters(lat1, lon1, lat2, lon2) {
                                            const R = 6371000;
                                            const toRad = deg => deg * Math.PI / 180;
                                        
                                            const dLat = toRad(lat2 - lat1);
                                            const dLon = toRad(lon2 - lon1);
                                        
                                            const a =
                                                Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                                                Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
                                                Math.sin(dLon / 2) * Math.sin(dLon / 2);
                                        
                                            return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                                        }
                                        
                                        function cleanText(str) {
                                            return String(str || '')
                                                .replace(/\s+/g, ' ')
                                                .replace(/\s+,/g, ',')
                                                .trim();
                                        }
                                        
                                        function createStateIfMissing(id, def, type, name) {
                                            if (!existsState(id)) {
                                                createState(id, def, {
                                                    name: name,
                                                    type: type,
                                                    role: type === 'number' ? 'value' : 'text',
                                                    read: true,
                                                    write: true
                                                });
                                            }
                                        }
                                        

                                        Beta-Tester

                                        skvarelS 1 Antwort Letzte Antwort
                                        0
                                        • Merlin123M Merlin123

                                          Hier ne version für mehrere Personen, aber noch nicht vollständig getestet...

                                          /************************************************************
                                           * Mehrpersonen-Standortscript für ioBroker + Life360
                                           *
                                           * Für jede Person:
                                           * - Reverse Geocoding per Nominatim
                                           * - optional POI-Suche per Overpass
                                           * - Ausgabe in eigenen States
                                           *
                                           * WICHTIG:
                                           * USER_AGENT bitte mit echter Kontaktmöglichkeit anpassen.
                                           ************************************************************/
                                          
                                          const PERSONS = [
                                              {
                                                  key: 'Oliver',
                                                  latState: 'life360ng.0.people.bxxxe.latitude',
                                                  lonState: 'life360ng.0.people.b9xxxxx7e.longitude',
                                                  targetState: '0_userdata.0.VIS.Position.Standort_Oliver',
                                                  baseState: '0_userdata.0.VIS.Position.Oliver'
                                              },
                                              {
                                                  key: 'Hannah',
                                                  latState: 'life360ng.0.people.f7xxx9d.latitude',
                                                  lonState: 'life360ng.0.people.f7fxxx9d.longitude',
                                                  targetState: '0_userdata.0.VIS.Position.Standort_Hannah',
                                                  baseState: '0_userdata.0.VIS.Position.Hannah'
                                              },
                                              {
                                                  key: 'Marleen',
                                                  latState: 'life360ng.0.people.88xxxea.latitude',
                                                  lonState: 'life360ng.0.people.88xxxaea.longitude',
                                                  targetState: '0_userdata.0.VIS.Position.Standort_Marleen',
                                                  baseState: '0_userdata.0.VIS.Position.Marleen'
                                              }
                                          ];
                                          
                                          // Einstellungen
                                          const MIN_INTERVAL_MS            = 60 * 1000; // max. 1 Lookup pro Person pro Minute
                                          const MIN_DISTANCE_M             = 40;        // nur neu abfragen, wenn mind. 40 m Bewegung
                                          const DEBOUNCE_MS                = 5000;      // 5 Sekunden warten, falls lat/lon nacheinander aktualisiert werden
                                          const POI_RADIUS_M               = 15;        // erste POI-Suche
                                          const SECOND_TRY_RADIUS          = 25;        // optionaler zweiter Versuch
                                          const MAX_ACCEPTED_POI_DISTANCE  = 20;        // nur wirklich nahe Treffer übernehmen
                                          const GAP_BETWEEN_CALLS          = 1500;      // Pause zwischen Nominatim und Overpass
                                          
                                          // WICHTIG: anpassen
                                          const USER_AGENT = 'ioBroker-Standortscript/1.0 (Kontakt: deine-mail@example.com)';
                                          
                                          // Laufzeitstatus pro Person
                                          const runtime = {};
                                          
                                          /* ---------------------------------------------------------
                                           * Initialisierung
                                           * --------------------------------------------------------- */
                                          for (const person of PERSONS) {
                                              person.addressState = `${person.baseState}.Standort_${person.key}_Adresse`;
                                              person.poiState     = `${person.baseState}.Standort_${person.key}_POI`;
                                              person.lastLatState = `${person.baseState}.Standort_${person.key}_LastLat`;
                                              person.lastLonState = `${person.baseState}.Standort_${person.key}_LastLon`;
                                              person.lastTsState  = `${person.baseState}.Standort_${person.key}_LastLookupTs`;
                                          
                                              runtime[person.key] = {
                                                  debounceTimer: null,
                                                  lookupRunning: false
                                              };
                                          
                                              createStateIfMissing(person.targetState,  '', 'string', `${person.key} Standort`);
                                              createStateIfMissing(person.addressState, '', 'string', `${person.key} Adresse`);
                                              createStateIfMissing(person.poiState,     '', 'string', `${person.key} POI`);
                                              createStateIfMissing(person.lastLatState, 0,  'number', `${person.key} letzte Lookup-Latitude`);
                                              createStateIfMissing(person.lastLonState, 0,  'number', `${person.key} letzte Lookup-Longitude`);
                                              createStateIfMissing(person.lastTsState,  0,  'number', `${person.key} letzter Lookup-Zeitstempel`);
                                          
                                              on({ id: person.latState, change: 'ne' }, () => scheduleLookup(person));
                                              on({ id: person.lonState, change: 'ne' }, () => scheduleLookup(person));
                                          
                                              scheduleLookup(person);
                                          }
                                          
                                          /* ---------------------------------------------------------
                                           * Hauptlogik
                                           * --------------------------------------------------------- */
                                          function scheduleLookup(person) {
                                              const rt = runtime[person.key];
                                          
                                              if (rt.debounceTimer) clearTimeout(rt.debounceTimer);
                                          
                                              rt.debounceTimer = setTimeout(() => {
                                                  runLookup(person).catch(err => log(`${person.key}: Standortscript Fehler: ${err}`, 'error'));
                                              }, DEBOUNCE_MS);
                                          }
                                          
                                          async function runLookup(person) {
                                              const rt = runtime[person.key];
                                          
                                              if (rt.lookupRunning) {
                                                  log(`${person.key}: Lookup läuft bereits, überspringe parallelen Aufruf.`, 'debug');
                                                  return;
                                              }
                                          
                                              const lat = parseFloat(getState(person.latState).val);
                                              const lon = parseFloat(getState(person.lonState).val);
                                          
                                              if (!isFinite(lat) || !isFinite(lon)) {
                                                  log(`${person.key}: Ungültige Koordinaten, Lookup abgebrochen.`, 'warn');
                                                  return;
                                              }
                                          
                                              if (!shouldLookup(person, lat, lon)) {
                                                  log(`${person.key}: Lookup übersprungen (Cooldown oder Bewegung zu gering).`, 'debug');
                                                  return;
                                              }
                                          
                                              rt.lookupRunning = true;
                                          
                                              try {
                                                  const addressInfo = await getAddressFromNominatim(lat, lon);
                                                  const addressText = buildAddressText(addressInfo);
                                          
                                                  setState(person.addressState, addressText || '', true);
                                                  setState(person.targetState, addressText || 'Unbekannter Standort', true);
                                                  setState(person.poiState, '', true);
                                          
                                                  setState(person.lastLatState, lat, true);
                                                  setState(person.lastLonState, lon, true);
                                                  setState(person.lastTsState, Date.now(), true);
                                          
                                                  await sleep(GAP_BETWEEN_CALLS);
                                          
                                                  let poi = await getNearbyNamedPoi(lat, lon, POI_RADIUS_M);
                                          
                                                  if (!poi) {
                                                      poi = await getNearbyNamedPoi(lat, lon, SECOND_TRY_RADIUS);
                                                  }
                                          
                                                  let finalText = addressText || 'Unbekannter Standort';
                                          
                                                  if (poi && poi.name) {
                                                      setState(person.poiState, poi.name, true);
                                          
                                                      if (addressText && !addressText.toLowerCase().includes(poi.name.toLowerCase())) {
                                                          finalText = poi.name + ', ' + addressText;
                                                      } else {
                                                          finalText = poi.name;
                                                      }
                                                  }
                                          
                                                  setState(person.targetState, finalText, true);
                                                  log(`${person.key}: Standort aktualisiert: ${finalText}`, 'info');
                                          
                                              } catch (err) {
                                                  log(`${person.key}: Fehler beim Standort-Lookup: ${err}`, 'error');
                                              } finally {
                                                  rt.lookupRunning = false;
                                              }
                                          }
                                          
                                          function shouldLookup(person, lat, lon) {
                                              const lastTsRaw  = getState(person.lastTsState).val;
                                              const lastLatRaw = getState(person.lastLatState).val;
                                              const lastLonRaw = getState(person.lastLonState).val;
                                          
                                              const lastTs  = Number(lastTsRaw || 0);
                                              const lastLat = parseFloat(lastLatRaw);
                                              const lastLon = parseFloat(lastLonRaw);
                                          
                                              const now = Date.now();
                                          
                                              if (lastTs > 0 && (now - lastTs) < MIN_INTERVAL_MS) {
                                                  return false;
                                              }
                                          
                                              if (isFinite(lastLat) && isFinite(lastLon) && !(lastLat === 0 && lastLon === 0)) {
                                                  const dist = distanceMeters(lastLat, lastLon, lat, lon);
                                                  if (dist < MIN_DISTANCE_M) {
                                                      return false;
                                                  }
                                              }
                                          
                                              return true;
                                          }
                                          
                                          /* ---------------------------------------------------------
                                           * Nominatim Reverse Geocoding
                                           * --------------------------------------------------------- */
                                          async function getAddressFromNominatim(lat, lon) {
                                              const url =
                                                  'https://nominatim.openstreetmap.org/reverse' +
                                                  '?format=jsonv2' +
                                                  '&lat=' + encodeURIComponent(lat) +
                                                  '&lon=' + encodeURIComponent(lon) +
                                                  '&addressdetails=1' +
                                                  '&zoom=18';
                                          
                                              const response = await httpGetAsync(url, {
                                                  'User-Agent': USER_AGENT,
                                                  'Accept': 'application/json'
                                              });
                                          
                                              if (response.statusCode < 200 || response.statusCode >= 300) {
                                                  throw new Error('Nominatim HTTP ' + response.statusCode);
                                              }
                                          
                                              let data;
                                              try {
                                                  data = JSON.parse(response.data);
                                              } catch (e) {
                                                  throw new Error('Nominatim JSON konnte nicht geparst werden');
                                              }
                                          
                                              return data;
                                          }
                                          
                                          function buildAddressText(data) {
                                              if (!data) return '';
                                          
                                              const a = data.address || {};
                                          
                                              const road =
                                                  a.road ||
                                                  a.pedestrian ||
                                                  a.footway ||
                                                  a.path ||
                                                  a.cycleway ||
                                                  '';
                                          
                                              const houseNumber = a.house_number || '';
                                              const postcode = a.postcode || '';
                                              const city =
                                                  a.city ||
                                                  a.town ||
                                                  a.village ||
                                                  a.hamlet ||
                                                  a.municipality ||
                                                  '';
                                          
                                              let result = '';
                                          
                                              if (road) {
                                                  result += road;
                                                  if (houseNumber) result += ' ' + houseNumber;
                                              }
                                          
                                              if (postcode || city) {
                                                  if (result) result += ', ';
                                                  result += [postcode, city].filter(Boolean).join(' ');
                                              }
                                          
                                              if (!result && data.display_name) {
                                                  result = data.display_name;
                                              }
                                          
                                              return cleanText(result);
                                          }
                                          
                                          /* ---------------------------------------------------------
                                           * Overpass: benannten Ort/POI in der Nähe finden
                                           * --------------------------------------------------------- */
                                          async function getNearbyNamedPoi(lat, lon, radius) {
                                              const query = `
                                          [out:json][timeout:10];
                                          (
                                            nwr(around:${radius},${lat},${lon})["name"]["shop"];
                                            nwr(around:${radius},${lat},${lon})["name"]["amenity"];
                                            nwr(around:${radius},${lat},${lon})["name"]["tourism"];
                                            nwr(around:${radius},${lat},${lon})["name"]["leisure"];
                                            nwr(around:${radius},${lat},${lon})["name"]["office"];
                                          );
                                          out center tags;
                                          `;
                                          
                                              const response = await httpPostAsync(
                                                  'https://overpass-api.de/api/interpreter',
                                                  query,
                                                  {
                                                      'User-Agent': USER_AGENT,
                                                      'Content-Type': 'text/plain; charset=utf-8',
                                                      'Accept': 'application/json'
                                                  }
                                              );
                                          
                                              if (response.statusCode < 200 || response.statusCode >= 300) {
                                                  throw new Error('Overpass HTTP ' + response.statusCode);
                                              }
                                          
                                              let data;
                                              try {
                                                  data = JSON.parse(response.data);
                                              } catch (e) {
                                                  throw new Error('Overpass JSON konnte nicht geparst werden');
                                              }
                                          
                                              if (!data.elements || !data.elements.length) {
                                                  return null;
                                              }
                                          
                                              const candidates = data.elements
                                                  .map(el => {
                                                      const cLat = el.lat || (el.center && el.center.lat);
                                                      const cLon = el.lon || (el.center && el.center.lon);
                                                      const name = el.tags && el.tags.name ? String(el.tags.name).trim() : '';
                                                      const dist = (isFinite(cLat) && isFinite(cLon))
                                                          ? distanceMeters(lat, lon, cLat, cLon)
                                                          : 999999;
                                          
                                                      return {
                                                          name: cleanText(name),
                                                          tags: el.tags || {},
                                                          dist: dist
                                                      };
                                                  })
                                                  .filter(x => x.name);
                                          
                                              if (!candidates.length) return null;
                                          
                                              candidates.sort((a, b) => {
                                                  const pa = poiPriority(a.tags);
                                                  const pb = poiPriority(b.tags);
                                                  if (pa !== pb) return pa - pb;
                                                  return a.dist - b.dist;
                                              });
                                          
                                              const best = candidates[0];
                                              if (!best || best.dist > MAX_ACCEPTED_POI_DISTANCE) {
                                                  return null;
                                              }
                                          
                                              return best;
                                          }
                                          
                                          function poiPriority(tags) {
                                              if (tags.shop) return 1;
                                              if (tags.amenity) return 2;
                                              if (tags.tourism) return 3;
                                              if (tags.leisure) return 4;
                                              if (tags.office) return 5;
                                              return 99;
                                          }
                                          
                                          /* ---------------------------------------------------------
                                           * HTTP-Helfer
                                           * --------------------------------------------------------- */
                                          function httpGetAsync(url, headers) {
                                              return new Promise((resolve, reject) => {
                                                  httpGet(url, { headers: headers, timeout: 15000 }, (err, response) => {
                                                      if (err) return reject(err);
                                                      resolve(response);
                                                  });
                                              });
                                          }
                                          
                                          function httpPostAsync(url, body, headers) {
                                              return new Promise((resolve, reject) => {
                                                  httpPost(url, body, { headers: headers, timeout: 20000 }, (err, response) => {
                                                      if (err) return reject(err);
                                                      resolve(response);
                                                  });
                                              });
                                          }
                                          
                                          function sleep(ms) {
                                              return new Promise(resolve => setTimeout(resolve, ms));
                                          }
                                          
                                          /* ---------------------------------------------------------
                                           * Hilfsfunktionen
                                           * --------------------------------------------------------- */
                                          function distanceMeters(lat1, lon1, lat2, lon2) {
                                              const R = 6371000;
                                              const toRad = deg => deg * Math.PI / 180;
                                          
                                              const dLat = toRad(lat2 - lat1);
                                              const dLon = toRad(lon2 - lon1);
                                          
                                              const a =
                                                  Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                                                  Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
                                                  Math.sin(dLon / 2) * Math.sin(dLon / 2);
                                          
                                              return 2 * R * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
                                          }
                                          
                                          function cleanText(str) {
                                              return String(str || '')
                                                  .replace(/\s+/g, ' ')
                                                  .replace(/\s+,/g, ',')
                                                  .trim();
                                          }
                                          
                                          function createStateIfMissing(id, def, type, name) {
                                              if (!existsState(id)) {
                                                  createState(id, def, {
                                                      name: name,
                                                      type: type,
                                                      role: type === 'number' ? 'value' : 'text',
                                                      read: true,
                                                      write: true
                                                  });
                                              }
                                          }
                                          
                                          skvarelS Online
                                          skvarelS Online
                                          skvarel
                                          Developer
                                          schrieb am zuletzt editiert von
                                          #111

                                          @Merlin123 .. ich habe da ein paar Fragen. Ich schreibe dich morgen dazu mal privat an.

                                          #TeamInventwo
                                          Unsere Adapter:
                                          Autodarts, FoxESS, Enpal, Life360ng, Tidy, vis-inventwo, vis-2-widgets-inventwo, vis-icontwo, vis-2-widgets-icontwo

                                          Wer uns mit einem Kaffee unterstützen möchte: PayPal

                                          Merlin123M 1 Antwort Letzte Antwort
                                          0

                                          Hey! Du scheinst an dieser Unterhaltung interessiert zu sein, hast aber noch kein Konto.

                                          Hast du es satt, bei jedem Besuch durch die gleichen Beiträge zu scrollen? Wenn du dich für ein Konto anmeldest, kommst du immer genau dorthin zurück, wo du zuvor warst, und kannst dich über neue Antworten benachrichtigen lassen (entweder per E-Mail oder Push-Benachrichtigung). Du kannst auch Lesezeichen speichern und Beiträge positiv bewerten, um anderen Community-Mitgliedern deine Wertschätzung zu zeigen.

                                          Mit deinem Input könnte dieser Beitrag noch besser werden 💗

                                          Registrieren Anmelden
                                          Antworten
                                          • In einem neuen Thema antworten
                                          Anmelden zum Antworten
                                          • Älteste zuerst
                                          • Neuste zuerst
                                          • Meiste Stimmen


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          544

                                          Online

                                          32.8k

                                          Benutzer

                                          82.9k

                                          Themen

                                          1.3m

                                          Beiträge
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                          ioBroker Community 2014-2025
                                          logo
                                          • Anmelden

                                          • Du hast noch kein Konto? Registrieren

                                          • Anmelden oder registrieren, um zu suchen
                                          • Erster Beitrag
                                            Letzter Beitrag
                                          0
                                          • Home
                                          • Aktuell
                                          • Tags
                                          • Ungelesen 0
                                          • Kategorien
                                          • Unreplied
                                          • Beliebt
                                          • GitHub
                                          • Docu
                                          • Hilfe