Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • 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. Skripten / Logik
  4. Alexa Shopping List mit Bring synchronisieren

NEWS

  • Jahresrückblick 2025 – unser neuer Blogbeitrag ist online! ✨
    BluefoxB
    Bluefox
    17
    1
    2.9k

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    13
    1
    1.1k

  • Weihnachtsangebot 2025! 🎄
    BluefoxB
    Bluefox
    25
    1
    2.4k

Alexa Shopping List mit Bring synchronisieren

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
178 Beiträge 31 Kommentatoren 37.5k Aufrufe 31 Watching
  • Ä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.
  • OreiderO Oreider

    @Sil4s

    Danke, hatte auch die regelmäßigen Aktualisierungen.
    Hab mal auf deine geänderte Version umgestellt. Bisher schaut alles gut aus!

    Danke dafür!

    S Offline
    S Offline
    Sil4s
    schrieb am zuletzt editiert von
    #169

    @Oreider Super das freut mich, bei mir läuft es auch stabil und meine Frau hat auch noch nicht gemeckert. :)

    1 Antwort Letzte Antwort
    0
    • M MartyBr

      @elron
      Kannst du bitte auf das originale Script verlinken? Ich finde es hier leider nicht.
      Danke für die Anpassung.

      E Offline
      E Offline
      elron
      schrieb am zuletzt editiert von
      #170

      @MartyBr sagte in Alexa Shopping List mit Bring synchronisieren:

      @elron
      Kannst du bitte auf das originale Script verlinken? Ich finde es hier leider nicht.
      Danke für die Anpassung.

      Hi Martin,

      hier bzw. auch im Threadverlauf.

      M 1 Antwort Letzte Antwort
      0
      • E elron

        @MartyBr sagte in Alexa Shopping List mit Bring synchronisieren:

        @elron
        Kannst du bitte auf das originale Script verlinken? Ich finde es hier leider nicht.
        Danke für die Anpassung.

        Hi Martin,

        hier bzw. auch im Threadverlauf.

        M Offline
        M Offline
        MartyBr
        schrieb am zuletzt editiert von
        #171

        @elron
        Der Link funktioniert leider nicht (zumindest bei mir). Ich habe das Script von @sil4s genommen.

        Vielen Dank

        Gruß
        Martin


        Intel NUCs mit Proxmox / Iobroker als VM unter Debian
        Raspeberry mit USB Leseköpfen für Smartmeter
        Homematic und Homematic IP

        E 1 Antwort Letzte Antwort
        0
        • M MartyBr

          @elron
          Der Link funktioniert leider nicht (zumindest bei mir). Ich habe das Script von @sil4s genommen.

          Vielen Dank

          E Offline
          E Offline
          elron
          schrieb am zuletzt editiert von
          #172

          @MartyBr sagte in Alexa Shopping List mit Bring synchronisieren:

          @elron
          Der Link funktioniert leider nicht (zumindest bei mir). Ich habe das Script von @sil4s genommen.

          Vielen Dank

          Okay, komisch, hier nochmal der gesamte Code inkl. der aktuellen Änderung:

          /***********************************************************************************************************************************************************************************
           * GENERIC INFO:
           *      Created by Daniel Drießen @ DDProductions
           * 
           *      Date of creation:   17.07.2024
           *      Version:            0.1.0.0    
           * 
           * DESCRIPTION:
           *      This script synchronizes the shopping lists of 'Alexa' and 'Bring'.
           *      It monitors changes in both shopping lists and ensures that they stay updated with each other.
           *      The script uses events to detect changes in the shopping lists of both 'Alexa' and 'Bring'.
           *      When changes are detected, it initiates a synchronization to reconcile any discrepancies between the lists.
           *      The synchronization process involves comparing the items on both lists and updating each list to reflect the combined items from both sources.
           * 
           * DEPENDENCIES:
           *      - Alexa2 adapter
           *      - Bring adapter
           *
           * LIMITATIONS:
           *      Due to the nature of the 'Bring' adapter (which does not store an 'added' or 'updated' timestamp for items on the shopping list), a 'real' synchronization is not possible
           *      because there is no way to detect the order of changes to the shopping lists of 'Alexa' and 'Bring'.
           * 
           * TODO & FUTURE GOALS:
           *      - Add better error handling.
           *      - Move functions 'getItemsOnAlexaShoppingList' & 'getItemsOnBringShoppingList' into classes 'AlexaShoppingList' & 'BringShoppingList'.
           *      - Move functions 'addItemToShoppingList', 'removeItemFromShoppingList' into classes 'AlexaShoppingList' & 'BringShoppingList'.
           *      - Enhance the synchronization logic to minimize potential syncing errors and improve reliability given the above limitations.
           *      - Maybe add a sync schedule for periodic syncing.
           * 
           * CHANGELOG:
           *      18.07.2024 - Initial version completed.
           * 
           ***********************************************************************************************************************************************************************************/
          
          export default this;
          
          
          
          
          
          /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
          +                                                                   SCRIPT SETUP                                                                           +
          *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
          /*---------- OBJECT-ID CONSTANTS ------------------------------------------------------------------------------------------------*/
          const objectID_alexa_shoppingList_folder = 'alexa2.0.Lists.SHOP';
          const objectID_alexa_shoppingList_addItem = 'alexa2.0.Lists.SHOP.#New';
          
          const objectID_bring_shoppingList_sentenceString = 'bring.0.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.enumSentence';
          const objectID_bring_shoppingList_addItem = 'bring.0.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.saveItem';
          const objectID_bring_shoppingList_removeItem = 'bring.0.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.removeItem';
          /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
          *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
          
          
          
          
          
          
          
          
          
          
          /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
          +                                                               START OF SCRIPT LOGIC                                                                      +
          *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
          var initComplete = false;
          var syncingInProgress = false;
          var resyncRequests = [];
          startScriptLogic();
          
          
          async function startScriptLogic() {
              try {
                  await init();
                  initComplete = true;
              } catch (error) {
                  console.error(error.message);
                  console.debug(`SCRIPT WILL TERMINATE NOW!`);
                  await this.stopScriptAsync();
              }
          }
          /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
          *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
          
          
          
          
          
          /*---------- INIT ---------------------------------------------------------------------------------------------------------------*/
          async function init(): Promise <void> {
              console.info(`Script initialization...`);
          
              await synchronizeShoppingLists('init', false);
          
              console.info(`Script initialization completed successfully!`);
          
              await checkForResyncRequests();
          }
          
          
          
          /*---------- TRIGGER ------------------------------------------------------------------------------------------------------------*/
          // Alexa
          on({ id: [].concat([objectID_alexa_shoppingList_folder + '.json']), change: 'ne' }, async (obj) => {
            let value = obj.state.val;
            let oldValue = obj.oldState.val;
          
            console.debug(`'Alexa' shopping list changed!`);
          
            if (initComplete && !syncingInProgress) {
                await synchronizeShoppingLists('alexa', false);
                await checkForResyncRequests();
            } else {
                resyncRequests.push('alexa');
            }
          });
          
          // Bring
          on({ id: [].concat([objectID_bring_shoppingList_sentenceString]), change: 'ne' }, async (obj) => {
            let value = obj.state.val;
            let oldValue = obj.oldState.val;
          
            console.debug(`'Bring' shopping list changed!`);
          
            if (initComplete && !syncingInProgress) {
                await synchronizeShoppingLists('bring', false);
                await checkForResyncRequests();
            } else {
                resyncRequests.push('bring');
            }
          });
          
          
          
          /*---------- SYNC ---------------------------------------------------------------------------------------------------------------*/
          async function synchronizeShoppingLists(syncInitiator:string, isResync:boolean) {
              console.info(`Sync started`);
              console.debug(`Sync initiator: '${capitalizeFirstLetter(syncInitiator)}'`);
              console.debug(`Sync is a resync: '${isResync}'`);
              syncingInProgress = true;
              
              var itemsOnAlexaShoppingList = await getItemsOnAlexaShoppingList();
              var itemsOnBringShoppingList = await getItemsOnBringShoppingList();
              console.debug(`Items on "Alexa" shopping list (${itemsOnAlexaShoppingList.length}): ${itemsOnAlexaShoppingList.map(item => item.value)}`);
              console.debug(`Items on "Bring" shopping list (${itemsOnBringShoppingList.length}): ${itemsOnBringShoppingList.map(item => item.value)}`);
          
              if (syncInitiator.toLowerCase() === 'init') {
                  // If sync initiator is "init"
                  // create a combined item list and add each item missing on each list to each list.
          
                  // Create combined shopping list
                  var itemsOnCombinedShoppingList = [];
                  for (const itemOnAlexaShoppingList of itemsOnAlexaShoppingList) {
                      if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnCombinedShoppingList, itemOnAlexaShoppingList)) {
                          itemsOnCombinedShoppingList.push(itemOnAlexaShoppingList);
                      }
                  }
                  for (const itemOnBringShoppingList of itemsOnBringShoppingList) {
                      if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnCombinedShoppingList, itemOnBringShoppingList)) {
                          itemsOnCombinedShoppingList.push(itemOnBringShoppingList);
                      }
                  }
                  //console.debug(`Items on "Combined" shopping list: ${itemsOnCombinedShoppingList.map(item => item.value)}`);
          
                  // Add each missing item on each list to each list
                  var itemsToAddToAlexaShoppingList = [];
                  var itemsToAddToBringShoppingList = [];
          
                  for (const itemOnCombinedShoppingList of itemsOnCombinedShoppingList) {
                      if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnAlexaShoppingList, itemOnCombinedShoppingList)) {
                          itemsToAddToAlexaShoppingList.push(itemOnCombinedShoppingList);
                      }
                      if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnBringShoppingList, itemOnCombinedShoppingList)) {
                          itemsToAddToBringShoppingList.push(itemOnCombinedShoppingList);
                      }
                  }
                  if (itemsToAddToAlexaShoppingList.length > 0) {
                      console.debug(`Items to add to "Alexa" shopping list (${itemsToAddToAlexaShoppingList.length}): ${itemsToAddToAlexaShoppingList.map(item => item.value)}`);
                  }
                  if (itemsToAddToBringShoppingList.length > 0) {
                      console.debug(`Items to add to "Bring" shopping list (${itemsToAddToBringShoppingList.length}): ${itemsToAddToBringShoppingList.map(item => item.value)}`);
                  }
          
                  for (const itemToAddToAlexaShoppingList of itemsToAddToAlexaShoppingList) {
                      await AlexaShoppingListItem.addItemToShoppingList(itemToAddToAlexaShoppingList);
                  }
                  for (const itemToAddToBringShoppingList of itemsToAddToBringShoppingList) {
                      await BringShoppingListItem.addItemToShoppingList(itemToAddToBringShoppingList);
                  }
              } else if (syncInitiator.toLowerCase() === 'alexa') {
                  // If sync initiator is "alexa"
                  // add each item from the alexa shopping list that is missing on the bring shopping list to the bring shopping list.
                  // Then remove each item from the bring shopping list that is not on the alexa shopping list.
                  
                  // Add each item from the alexa shopping list that is missing on the bring shopping list to the bring shopping list.
                  var itemsToAddToBringShoppingList = [];
          
                  for (const itemOnAlexaShoppingList of itemsOnAlexaShoppingList) {
                      if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnBringShoppingList, itemOnAlexaShoppingList)) {
                          itemsToAddToBringShoppingList.push(itemOnAlexaShoppingList);
                      }
                  }
                  if (itemsToAddToBringShoppingList.length > 0) {
                      console.debug(`Items to add to "Bring" shopping list (${itemsToAddToBringShoppingList.length}): ${itemsToAddToBringShoppingList.map(item => item.value)}`);
                  }
          
                  for (const itemToAddToBringShoppingList of itemsToAddToBringShoppingList) {
                      await BringShoppingListItem.addItemToShoppingList(itemToAddToBringShoppingList);
                  }
          
          
                  // Get an update of the bring shopping list
                  itemsOnBringShoppingList = await getItemsOnBringShoppingList();
          
          
                  // Remove each item from the bring shopping list that is not on the alexa shopping list.
                  var itemsToRemoveFromBringShoppingList = [];
          
                  for (const itemOnBringShoppingList of itemsOnBringShoppingList) {
                      if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnAlexaShoppingList, itemOnBringShoppingList)) {
                          itemsToRemoveFromBringShoppingList.push(itemOnBringShoppingList);
                      }
                  }
                  if (itemsToRemoveFromBringShoppingList.length > 0) {
                      console.debug(`Items to remove from "Bring" shopping list (${itemsToRemoveFromBringShoppingList.length}): ${itemsToRemoveFromBringShoppingList.map(item => item.value)}`);
                  }
          
                  for (const itemToRemoveFromBringShoppingList of itemsToRemoveFromBringShoppingList) {
                      await BringShoppingListItem.removeItemFromShoppingList(itemToRemoveFromBringShoppingList);
                  }
              } else if (syncInitiator.toLowerCase() === 'bring') {
                  // If sync initiator is "bring"
                  // add each item from the bring shopping list that is missing on the alexa shopping list to the alexa shopping list.
                  // Then remove each item from the alexa shopping list that is not on the bring shopping list.
          
                  // Add each item from the bring shopping list that is missing on the alexa shopping list to the alexa shopping list.
                  var itemsToAddToAlexaShoppingList = [];
          
                  for (const itemOnBringShoppingList of itemsOnBringShoppingList) {
                      if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnAlexaShoppingList, itemOnBringShoppingList)) {
                          itemsToAddToAlexaShoppingList.push(itemOnBringShoppingList);
                      }
                  }
                  if (itemsToAddToAlexaShoppingList.length > 0) {
                      console.debug(`Items to add to "Alexa" shopping list (${itemsToAddToAlexaShoppingList.length}): ${itemsToAddToAlexaShoppingList.map(item => item.value)}`);
                  }
          
                  for (const itemToAddToAlexaShoppingList of itemsToAddToAlexaShoppingList) {
                      await AlexaShoppingListItem.addItemToShoppingList(itemToAddToAlexaShoppingList);
                  }
          
          
                  // Get an update of the alexa shopping list
                  itemsOnAlexaShoppingList = await getItemsOnAlexaShoppingList();
          
          
                  // Remove each item from the alexa shopping list that is not on the bring shopping list.
                  var itemsToRemoveFromAlexaShoppingList = [];
          
                  for (const itemOnAlexaShoppingList of itemsOnAlexaShoppingList) {
                      if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnBringShoppingList, itemOnAlexaShoppingList)) {
                          itemsToRemoveFromAlexaShoppingList.push(itemOnAlexaShoppingList);
                      }
                  }
                  if (itemsToRemoveFromAlexaShoppingList.length > 0) {
                      console.debug(`Items to remove from "Alexa" shopping list (${itemsToRemoveFromAlexaShoppingList.length}): ${itemsToRemoveFromAlexaShoppingList.map(item => item.value)}`);
                  }
          
                  for (const itemToRemoveFromAlexaShoppingList of itemsToRemoveFromAlexaShoppingList) {
                      await AlexaShoppingListItem.removeItemFromShoppingList(itemToRemoveFromAlexaShoppingList);
                  }
              }
          
              console.info(`Sync completed!`);
              syncingInProgress = false;
          }
          
          
          
          async function checkForResyncRequests() {
              if (resyncRequests.length > 0) {
                  // Get the first resync request.
                  var firstResyncRequest = resyncRequests[0];
          
                  // Remove the first resync request from the array.
                  resyncRequests.shift();
          
                  // Remove all values from the array that match the firstResyncRequest value
                  resyncRequests = resyncRequests.filter(request => request !== firstResyncRequest);
          
                  // Perform resync
                  await synchronizeShoppingLists(firstResyncRequest, true);
              }
          }
          
          
          
          /*---------- GET SHOPPING LIST ITEMS --------------------------------------------------------------------------------------------*/
          // Alexa
          async function getItemsOnAlexaShoppingList(): Promise <Array<AlexaShoppingListItem>> {
              var alexaShoppingListItems = [];
              var completedAlexaShoppingListItemsToRemove = [];
          
              var jsonShoppingListString = await getStateAsync(objectID_alexa_shoppingList_folder + '.json');
              var jsonShoppingList = JSON.parse(jsonShoppingListString.val);
          
              jsonShoppingList.forEach((item, index) => {
                  var item_id = item.id;
                  var item_listID = item.listId;
                  var item_customerID = item.customerId;
                  var item_shoppingListItem = item.shoppingListItem;
                  var item_value = item.value;
                  var item_completed = item.completed;
                  var item_version = item.version;
                  var item_createdDateTime = item.createdDateTime;
                  var item_updatedDateTime = item.updatedDateTime;
                  var item_idOfFolderObject = objectID_alexa_shoppingList_folder + '.items.' + item.id;
                  var item_idOfDeleteDatapoint = objectID_alexa_shoppingList_folder + '.items.' + item.id + '.#delete';
                  
                  /*
                  console.debug(`item_id: ${item_id}`);
                  console.debug(`item_listID: ${item_listID}`);
                  console.debug(`item_customerID: ${item_customerID}`);
                  console.debug(`item_shoppingListItem: ${item_shoppingListItem}`);
                  console.debug(`item_value: ${item_value}`);
                  console.debug(`item_completed: ${item_completed}`);
                  console.debug(`item_version: ${item_version}`);
                  console.debug(`item_createdDateTime: ${item_createdDateTime}`);
                  console.debug(`item_updatedDateTime: ${item_updatedDateTime}`);
                  console.debug(`item_idOfFolderObject: ${item_idOfFolderObject}`);
                  console.debug(`item_idOfDeleteDatapoint: ${item_idOfDeleteDatapoint}`);
                  */
          
                  try {
                      const newAlexaShoppingListItem = new AlexaShoppingListItem(
                          item_id,
                          item_listID,
                          item_customerID,
                          item_shoppingListItem,
                          item_value,
                          item_completed,
                          item_version,
                          item_createdDateTime,
                          item_updatedDateTime,
                          item_idOfFolderObject,
                          item_idOfDeleteDatapoint);
                          
                          if (!newAlexaShoppingListItem.completed) {
                              alexaShoppingListItems.push(newAlexaShoppingListItem);
                          } else {
                              completedAlexaShoppingListItemsToRemove.push(newAlexaShoppingListItem);
                          }
                      } catch (error) {
                          console.error(`Error while creating Alexa Shopping-List Item Object! -  Original Error: ${error.message}`);
                      }
              });
          
              if (completedAlexaShoppingListItemsToRemove.length > 0) {
                  for (const completedAlexaShoppingListItemToRemove of completedAlexaShoppingListItemsToRemove) {
                      await AlexaShoppingListItem.removeItemFromShoppingList(completedAlexaShoppingListItemToRemove);
                  }
              }
          
              // Sort the array of alexa shopping list items (case insensitive sorting)
              alexaShoppingListItems.sort((a, b) => a.value.toLowerCase().localeCompare(b.value.toLowerCase()));
          
              return alexaShoppingListItems;
          }
          
          // Bring
          async function getItemsOnBringShoppingList(): Promise <Array<BringShoppingListItem>> {
              var bringShoppingListItems = [];
          
              try {
                  var humanReadableShoppingListObject = await getStateAsync(objectID_bring_shoppingList_sentenceString);
                  var humanReadableShoppingList = humanReadableShoppingListObject.val;
                  if (humanReadableShoppingList !== null && humanReadableShoppingList !== '' && humanReadableShoppingList.length > 0) {
                      humanReadableShoppingList = replaceAllOccurrencesOfASubstringWithinAString(humanReadableShoppingList, ' und ', ', ');
                      humanReadableShoppingList = replaceAllOccurrencesOfASubstringWithinAString(humanReadableShoppingList, ', ', ';');
                      
                      const bringShoppingListItemStrings = splitStringIntoArray(humanReadableShoppingList, ';');
                      
                      for (const bringShoppingListItemString of bringShoppingListItemStrings) {
                          try {
                              const newBringShoppingListItem = new BringShoppingListItem(bringShoppingListItemString);
                              bringShoppingListItems.push(newBringShoppingListItem);
                          } catch (error) {
                              console.error(`Error while creating Alexa Shopping-List Item Object! -  Original Error: ${error.message}`);
                              continue;
                          }
                      }
                  }
              } catch (error) {
                  console.error(`Error while getting Bring Shopping-List Items! - Original Error: ${error.message}`);
              }
          
              // Sort the array of alexa shopping list items (case insensitive sorting)
              bringShoppingListItems.sort((a, b) => a.value.toLowerCase().localeCompare(b.value.toLowerCase()));
          
              return bringShoppingListItems;
          }
          
          
          
          
          
          
          
          
          
          
          /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
          +                                                                     CLASSES                                                                              +
          *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
          class AlexaShoppingListItem {
              public id: string;
              public listID: string;
              public customerID: string;
              public shoppingListItem: boolean;
              public value: string;
              public completed: boolean;
              public version: number;
              public createdDateTime: number;
              public updatedDateTime: number;
              public idOfFolderObject: string;
              public idOfDeleteDatapoint: string;
              
              constructor(
                  id:string,
                  listID:string,
                  customerID:string,
                  shoppingListItem:boolean,
                  value:string,
                  completed:boolean,
                  version:number,
                  createdDateTime:number,
                  updatedDateTime:number,
                  idOfFolderObject:string,
                  idOfDeleteDatapoint:string) {
                      this.id = id;
                      this.listID = listID;
                      this.customerID = customerID;
                      this.shoppingListItem = shoppingListItem;
                      this.value = value;
                      this.completed = completed;
                      this.version = version;
                      this.createdDateTime = createdDateTime;
                      this.updatedDateTime = updatedDateTime;
                      this.idOfFolderObject = idOfFolderObject;
                      this.idOfDeleteDatapoint = idOfDeleteDatapoint;
              }
              
              /********************
               * Adds an item to the Alexa shopping list.
               * 
               * @param item - The item to save to the Alexa shopping list. Must be either an instance of 'AlexaShoppingListItem' or 'BringShoppingListItem'.
               * @return Promise<boolean> - True if the function completed running its code.
               ********************/
              static async addItemToShoppingList(item:AlexaShoppingListItem | BringShoppingListItem): Promise <boolean> {
                  // Parameter validation
                  if (!(item instanceof AlexaShoppingListItem || item instanceof BringShoppingListItem)) {
                      throw new Error("Invalid parameter: Parameter 'item' must be an instance of 'AlexaShoppingListItem' or 'BringShoppingListItem'!");
                  }
          
                  await setStateAsync(objectID_alexa_shoppingList_addItem, capitalizeFirstLetter(item.value), false);
                  return true;
              }
          
              /********************
               * Removes an item from the Alexa shopping list.
               * 
               * @param item - The item to remove from the Alexa shopping list. Must be an instance of 'AlexaShoppingListItem'.
               * @return Promise<boolean> - True if the function completed running its code.
               ********************/
               static async removeItemFromShoppingList(item:AlexaShoppingListItem): Promise <boolean> {
                   // Parameter validation
                  if (!(item instanceof AlexaShoppingListItem)) {
                      throw new Error("Invalid parameter: Parameter 'item' must be an instance of 'AlexaShoppingListItem'!");
                  }
          
                  await setStateAsync(item.idOfDeleteDatapoint, true, false);
                  return true;
               }
          }
          
          
          
          class BringShoppingListItem {
              public value: string;
              
              constructor(value:string) {
                  this.value = value;
              }
          
              /********************
               * Adds an item to the Bring shopping list.
               * 
               * @param item - The item to save to the Bring shopping list. Must be either an instance of 'AlexaShoppingListItem' or 'BringShoppingListItem'.
               * @return Promise<boolean> - True if the function completed running its code.
               ********************/
              static async addItemToShoppingList(item:AlexaShoppingListItem | BringShoppingListItem): Promise <boolean> {
                  // Parameter validation
                  if (!(item instanceof AlexaShoppingListItem || item instanceof BringShoppingListItem)) {
                      throw new Error("Invalid parameter: Parameter 'item' must be an instance of 'AlexaShoppingListItem' or 'BringShoppingListItem'!");
                  }
          
                  await setStateAsync(objectID_bring_shoppingList_addItem, capitalizeFirstLetter(item.value), false);
                  return true;
              }
          
              /********************
               * Removes an item from the Bring shopping list.
               * 
               * @param item - The item to remove from the Bring shopping list. Must be an instance of 'BringShoppingListItem'.
               * @return Promise<boolean> - True if the function completed running its code.
               ********************/
               static async removeItemFromShoppingList(item:BringShoppingListItem): Promise <boolean> {
                   // Parameter validation
                  if (!(item instanceof BringShoppingListItem)) {
                      throw new Error("Invalid parameter: Parameter 'item' must be an instance of 'BringShoppingListItem'!");
                  }
          
                  await setStateAsync(objectID_bring_shoppingList_removeItem, item.value, false);
                  return true;
               }
          }
          
          
          
          class ShoppingListUtils {
              /********************
               * Checks if a given shopping list array contains a specific shopping list item.
               * Herefore it compares all items in the given shopping list array with the given shopping list item
               * (either an 'AlexaShoppingListItem' or a 'BringShoppingListItem')
               * in a case-insensitive way.
               * 
               * @param shoppingListArray - Array of AlexaShoppingListItem or BringShoppingListItem objects to check within.
               * @param shoppingListItem - The shopping list item to find in the array.
               * @returns Promise<boolean> - True if the item is found, otherwise false.
               ********************/
              static async checkIfShoppingListArrayContainsShoppingListItem(shoppingListArray: Array<AlexaShoppingListItem | BringShoppingListItem>, shoppingListItem: AlexaShoppingListItem | BringShoppingListItem): Promise <boolean> {
                  // Parameter validation
                  if (!Array.isArray(shoppingListArray)) {
                      throw new Error("Invalid parameter: 'shoppingListArray' must be an array.");
                  }
                  if (!(shoppingListItem instanceof AlexaShoppingListItem || shoppingListItem instanceof BringShoppingListItem)) {
                      throw new Error("Invalid parameter: 'shoppingListItem' must be an instance of 'AlexaShoppingListItem' or 'BringShoppingListItem'.");
                  }
          
                  for (const item of shoppingListArray) {
                      if (!(item instanceof AlexaShoppingListItem || item instanceof BringShoppingListItem)) {
                          throw new Error("Invalid parameter: All elements in 'shoppingListArray' must be instances of 'AlexaShoppingListItem' or 'BringShoppingListItem'.");
                      }
                  }
                  
                  // Normalize the value of the shopping list item to lower case for case-insensitive comparison
                  const itemValueToCheck = shoppingListItem.value.toLowerCase();
          
                  // Check if any item in the array matches the shopping list item value
                  for (const item of shoppingListArray) {
                      if (item.value.toLowerCase() === itemValueToCheck) {
                          return true;
                      }
                  }
          
                  return false;
              }
          }
          /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
          *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
          
          
          
          
          
          
          
          
          
          
          /*---------- HELPER FUNCTIONS ---------------------------------------------------------------------------------------------------*/
          function capitalizeFirstLetter(input: string): string {
              if (input.length === 0) {
                  return input; // Return the empty string if input is empty
              }
              return input.charAt(0).toUpperCase() + input.slice(1);
          }
          
          function replaceAllOccurrencesOfASubstringWithinAString(originalString: string, searchValue: string, replaceValue: string): string {
              const regex = new RegExp(searchValue, 'g');
              return originalString.replace(regex, replaceValue);
          }
          
          function splitStringIntoArray(input: string, delimiter: string): string[] {
              return input.split(delimiter);
          }
          
          M 1 Antwort Letzte Antwort
          0
          • E elron

            @MartyBr sagte in Alexa Shopping List mit Bring synchronisieren:

            @elron
            Der Link funktioniert leider nicht (zumindest bei mir). Ich habe das Script von @sil4s genommen.

            Vielen Dank

            Okay, komisch, hier nochmal der gesamte Code inkl. der aktuellen Änderung:

            /***********************************************************************************************************************************************************************************
             * GENERIC INFO:
             *      Created by Daniel Drießen @ DDProductions
             * 
             *      Date of creation:   17.07.2024
             *      Version:            0.1.0.0    
             * 
             * DESCRIPTION:
             *      This script synchronizes the shopping lists of 'Alexa' and 'Bring'.
             *      It monitors changes in both shopping lists and ensures that they stay updated with each other.
             *      The script uses events to detect changes in the shopping lists of both 'Alexa' and 'Bring'.
             *      When changes are detected, it initiates a synchronization to reconcile any discrepancies between the lists.
             *      The synchronization process involves comparing the items on both lists and updating each list to reflect the combined items from both sources.
             * 
             * DEPENDENCIES:
             *      - Alexa2 adapter
             *      - Bring adapter
             *
             * LIMITATIONS:
             *      Due to the nature of the 'Bring' adapter (which does not store an 'added' or 'updated' timestamp for items on the shopping list), a 'real' synchronization is not possible
             *      because there is no way to detect the order of changes to the shopping lists of 'Alexa' and 'Bring'.
             * 
             * TODO & FUTURE GOALS:
             *      - Add better error handling.
             *      - Move functions 'getItemsOnAlexaShoppingList' & 'getItemsOnBringShoppingList' into classes 'AlexaShoppingList' & 'BringShoppingList'.
             *      - Move functions 'addItemToShoppingList', 'removeItemFromShoppingList' into classes 'AlexaShoppingList' & 'BringShoppingList'.
             *      - Enhance the synchronization logic to minimize potential syncing errors and improve reliability given the above limitations.
             *      - Maybe add a sync schedule for periodic syncing.
             * 
             * CHANGELOG:
             *      18.07.2024 - Initial version completed.
             * 
             ***********************************************************************************************************************************************************************************/
            
            export default this;
            
            
            
            
            
            /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
            +                                                                   SCRIPT SETUP                                                                           +
            *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
            /*---------- OBJECT-ID CONSTANTS ------------------------------------------------------------------------------------------------*/
            const objectID_alexa_shoppingList_folder = 'alexa2.0.Lists.SHOP';
            const objectID_alexa_shoppingList_addItem = 'alexa2.0.Lists.SHOP.#New';
            
            const objectID_bring_shoppingList_sentenceString = 'bring.0.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.enumSentence';
            const objectID_bring_shoppingList_addItem = 'bring.0.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.saveItem';
            const objectID_bring_shoppingList_removeItem = 'bring.0.xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.removeItem';
            /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
            *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
            
            
            
            
            
            
            
            
            
            
            /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
            +                                                               START OF SCRIPT LOGIC                                                                      +
            *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
            var initComplete = false;
            var syncingInProgress = false;
            var resyncRequests = [];
            startScriptLogic();
            
            
            async function startScriptLogic() {
                try {
                    await init();
                    initComplete = true;
                } catch (error) {
                    console.error(error.message);
                    console.debug(`SCRIPT WILL TERMINATE NOW!`);
                    await this.stopScriptAsync();
                }
            }
            /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
            *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
            
            
            
            
            
            /*---------- INIT ---------------------------------------------------------------------------------------------------------------*/
            async function init(): Promise <void> {
                console.info(`Script initialization...`);
            
                await synchronizeShoppingLists('init', false);
            
                console.info(`Script initialization completed successfully!`);
            
                await checkForResyncRequests();
            }
            
            
            
            /*---------- TRIGGER ------------------------------------------------------------------------------------------------------------*/
            // Alexa
            on({ id: [].concat([objectID_alexa_shoppingList_folder + '.json']), change: 'ne' }, async (obj) => {
              let value = obj.state.val;
              let oldValue = obj.oldState.val;
            
              console.debug(`'Alexa' shopping list changed!`);
            
              if (initComplete && !syncingInProgress) {
                  await synchronizeShoppingLists('alexa', false);
                  await checkForResyncRequests();
              } else {
                  resyncRequests.push('alexa');
              }
            });
            
            // Bring
            on({ id: [].concat([objectID_bring_shoppingList_sentenceString]), change: 'ne' }, async (obj) => {
              let value = obj.state.val;
              let oldValue = obj.oldState.val;
            
              console.debug(`'Bring' shopping list changed!`);
            
              if (initComplete && !syncingInProgress) {
                  await synchronizeShoppingLists('bring', false);
                  await checkForResyncRequests();
              } else {
                  resyncRequests.push('bring');
              }
            });
            
            
            
            /*---------- SYNC ---------------------------------------------------------------------------------------------------------------*/
            async function synchronizeShoppingLists(syncInitiator:string, isResync:boolean) {
                console.info(`Sync started`);
                console.debug(`Sync initiator: '${capitalizeFirstLetter(syncInitiator)}'`);
                console.debug(`Sync is a resync: '${isResync}'`);
                syncingInProgress = true;
                
                var itemsOnAlexaShoppingList = await getItemsOnAlexaShoppingList();
                var itemsOnBringShoppingList = await getItemsOnBringShoppingList();
                console.debug(`Items on "Alexa" shopping list (${itemsOnAlexaShoppingList.length}): ${itemsOnAlexaShoppingList.map(item => item.value)}`);
                console.debug(`Items on "Bring" shopping list (${itemsOnBringShoppingList.length}): ${itemsOnBringShoppingList.map(item => item.value)}`);
            
                if (syncInitiator.toLowerCase() === 'init') {
                    // If sync initiator is "init"
                    // create a combined item list and add each item missing on each list to each list.
            
                    // Create combined shopping list
                    var itemsOnCombinedShoppingList = [];
                    for (const itemOnAlexaShoppingList of itemsOnAlexaShoppingList) {
                        if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnCombinedShoppingList, itemOnAlexaShoppingList)) {
                            itemsOnCombinedShoppingList.push(itemOnAlexaShoppingList);
                        }
                    }
                    for (const itemOnBringShoppingList of itemsOnBringShoppingList) {
                        if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnCombinedShoppingList, itemOnBringShoppingList)) {
                            itemsOnCombinedShoppingList.push(itemOnBringShoppingList);
                        }
                    }
                    //console.debug(`Items on "Combined" shopping list: ${itemsOnCombinedShoppingList.map(item => item.value)}`);
            
                    // Add each missing item on each list to each list
                    var itemsToAddToAlexaShoppingList = [];
                    var itemsToAddToBringShoppingList = [];
            
                    for (const itemOnCombinedShoppingList of itemsOnCombinedShoppingList) {
                        if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnAlexaShoppingList, itemOnCombinedShoppingList)) {
                            itemsToAddToAlexaShoppingList.push(itemOnCombinedShoppingList);
                        }
                        if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnBringShoppingList, itemOnCombinedShoppingList)) {
                            itemsToAddToBringShoppingList.push(itemOnCombinedShoppingList);
                        }
                    }
                    if (itemsToAddToAlexaShoppingList.length > 0) {
                        console.debug(`Items to add to "Alexa" shopping list (${itemsToAddToAlexaShoppingList.length}): ${itemsToAddToAlexaShoppingList.map(item => item.value)}`);
                    }
                    if (itemsToAddToBringShoppingList.length > 0) {
                        console.debug(`Items to add to "Bring" shopping list (${itemsToAddToBringShoppingList.length}): ${itemsToAddToBringShoppingList.map(item => item.value)}`);
                    }
            
                    for (const itemToAddToAlexaShoppingList of itemsToAddToAlexaShoppingList) {
                        await AlexaShoppingListItem.addItemToShoppingList(itemToAddToAlexaShoppingList);
                    }
                    for (const itemToAddToBringShoppingList of itemsToAddToBringShoppingList) {
                        await BringShoppingListItem.addItemToShoppingList(itemToAddToBringShoppingList);
                    }
                } else if (syncInitiator.toLowerCase() === 'alexa') {
                    // If sync initiator is "alexa"
                    // add each item from the alexa shopping list that is missing on the bring shopping list to the bring shopping list.
                    // Then remove each item from the bring shopping list that is not on the alexa shopping list.
                    
                    // Add each item from the alexa shopping list that is missing on the bring shopping list to the bring shopping list.
                    var itemsToAddToBringShoppingList = [];
            
                    for (const itemOnAlexaShoppingList of itemsOnAlexaShoppingList) {
                        if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnBringShoppingList, itemOnAlexaShoppingList)) {
                            itemsToAddToBringShoppingList.push(itemOnAlexaShoppingList);
                        }
                    }
                    if (itemsToAddToBringShoppingList.length > 0) {
                        console.debug(`Items to add to "Bring" shopping list (${itemsToAddToBringShoppingList.length}): ${itemsToAddToBringShoppingList.map(item => item.value)}`);
                    }
            
                    for (const itemToAddToBringShoppingList of itemsToAddToBringShoppingList) {
                        await BringShoppingListItem.addItemToShoppingList(itemToAddToBringShoppingList);
                    }
            
            
                    // Get an update of the bring shopping list
                    itemsOnBringShoppingList = await getItemsOnBringShoppingList();
            
            
                    // Remove each item from the bring shopping list that is not on the alexa shopping list.
                    var itemsToRemoveFromBringShoppingList = [];
            
                    for (const itemOnBringShoppingList of itemsOnBringShoppingList) {
                        if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnAlexaShoppingList, itemOnBringShoppingList)) {
                            itemsToRemoveFromBringShoppingList.push(itemOnBringShoppingList);
                        }
                    }
                    if (itemsToRemoveFromBringShoppingList.length > 0) {
                        console.debug(`Items to remove from "Bring" shopping list (${itemsToRemoveFromBringShoppingList.length}): ${itemsToRemoveFromBringShoppingList.map(item => item.value)}`);
                    }
            
                    for (const itemToRemoveFromBringShoppingList of itemsToRemoveFromBringShoppingList) {
                        await BringShoppingListItem.removeItemFromShoppingList(itemToRemoveFromBringShoppingList);
                    }
                } else if (syncInitiator.toLowerCase() === 'bring') {
                    // If sync initiator is "bring"
                    // add each item from the bring shopping list that is missing on the alexa shopping list to the alexa shopping list.
                    // Then remove each item from the alexa shopping list that is not on the bring shopping list.
            
                    // Add each item from the bring shopping list that is missing on the alexa shopping list to the alexa shopping list.
                    var itemsToAddToAlexaShoppingList = [];
            
                    for (const itemOnBringShoppingList of itemsOnBringShoppingList) {
                        if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnAlexaShoppingList, itemOnBringShoppingList)) {
                            itemsToAddToAlexaShoppingList.push(itemOnBringShoppingList);
                        }
                    }
                    if (itemsToAddToAlexaShoppingList.length > 0) {
                        console.debug(`Items to add to "Alexa" shopping list (${itemsToAddToAlexaShoppingList.length}): ${itemsToAddToAlexaShoppingList.map(item => item.value)}`);
                    }
            
                    for (const itemToAddToAlexaShoppingList of itemsToAddToAlexaShoppingList) {
                        await AlexaShoppingListItem.addItemToShoppingList(itemToAddToAlexaShoppingList);
                    }
            
            
                    // Get an update of the alexa shopping list
                    itemsOnAlexaShoppingList = await getItemsOnAlexaShoppingList();
            
            
                    // Remove each item from the alexa shopping list that is not on the bring shopping list.
                    var itemsToRemoveFromAlexaShoppingList = [];
            
                    for (const itemOnAlexaShoppingList of itemsOnAlexaShoppingList) {
                        if (!await ShoppingListUtils.checkIfShoppingListArrayContainsShoppingListItem(itemsOnBringShoppingList, itemOnAlexaShoppingList)) {
                            itemsToRemoveFromAlexaShoppingList.push(itemOnAlexaShoppingList);
                        }
                    }
                    if (itemsToRemoveFromAlexaShoppingList.length > 0) {
                        console.debug(`Items to remove from "Alexa" shopping list (${itemsToRemoveFromAlexaShoppingList.length}): ${itemsToRemoveFromAlexaShoppingList.map(item => item.value)}`);
                    }
            
                    for (const itemToRemoveFromAlexaShoppingList of itemsToRemoveFromAlexaShoppingList) {
                        await AlexaShoppingListItem.removeItemFromShoppingList(itemToRemoveFromAlexaShoppingList);
                    }
                }
            
                console.info(`Sync completed!`);
                syncingInProgress = false;
            }
            
            
            
            async function checkForResyncRequests() {
                if (resyncRequests.length > 0) {
                    // Get the first resync request.
                    var firstResyncRequest = resyncRequests[0];
            
                    // Remove the first resync request from the array.
                    resyncRequests.shift();
            
                    // Remove all values from the array that match the firstResyncRequest value
                    resyncRequests = resyncRequests.filter(request => request !== firstResyncRequest);
            
                    // Perform resync
                    await synchronizeShoppingLists(firstResyncRequest, true);
                }
            }
            
            
            
            /*---------- GET SHOPPING LIST ITEMS --------------------------------------------------------------------------------------------*/
            // Alexa
            async function getItemsOnAlexaShoppingList(): Promise <Array<AlexaShoppingListItem>> {
                var alexaShoppingListItems = [];
                var completedAlexaShoppingListItemsToRemove = [];
            
                var jsonShoppingListString = await getStateAsync(objectID_alexa_shoppingList_folder + '.json');
                var jsonShoppingList = JSON.parse(jsonShoppingListString.val);
            
                jsonShoppingList.forEach((item, index) => {
                    var item_id = item.id;
                    var item_listID = item.listId;
                    var item_customerID = item.customerId;
                    var item_shoppingListItem = item.shoppingListItem;
                    var item_value = item.value;
                    var item_completed = item.completed;
                    var item_version = item.version;
                    var item_createdDateTime = item.createdDateTime;
                    var item_updatedDateTime = item.updatedDateTime;
                    var item_idOfFolderObject = objectID_alexa_shoppingList_folder + '.items.' + item.id;
                    var item_idOfDeleteDatapoint = objectID_alexa_shoppingList_folder + '.items.' + item.id + '.#delete';
                    
                    /*
                    console.debug(`item_id: ${item_id}`);
                    console.debug(`item_listID: ${item_listID}`);
                    console.debug(`item_customerID: ${item_customerID}`);
                    console.debug(`item_shoppingListItem: ${item_shoppingListItem}`);
                    console.debug(`item_value: ${item_value}`);
                    console.debug(`item_completed: ${item_completed}`);
                    console.debug(`item_version: ${item_version}`);
                    console.debug(`item_createdDateTime: ${item_createdDateTime}`);
                    console.debug(`item_updatedDateTime: ${item_updatedDateTime}`);
                    console.debug(`item_idOfFolderObject: ${item_idOfFolderObject}`);
                    console.debug(`item_idOfDeleteDatapoint: ${item_idOfDeleteDatapoint}`);
                    */
            
                    try {
                        const newAlexaShoppingListItem = new AlexaShoppingListItem(
                            item_id,
                            item_listID,
                            item_customerID,
                            item_shoppingListItem,
                            item_value,
                            item_completed,
                            item_version,
                            item_createdDateTime,
                            item_updatedDateTime,
                            item_idOfFolderObject,
                            item_idOfDeleteDatapoint);
                            
                            if (!newAlexaShoppingListItem.completed) {
                                alexaShoppingListItems.push(newAlexaShoppingListItem);
                            } else {
                                completedAlexaShoppingListItemsToRemove.push(newAlexaShoppingListItem);
                            }
                        } catch (error) {
                            console.error(`Error while creating Alexa Shopping-List Item Object! -  Original Error: ${error.message}`);
                        }
                });
            
                if (completedAlexaShoppingListItemsToRemove.length > 0) {
                    for (const completedAlexaShoppingListItemToRemove of completedAlexaShoppingListItemsToRemove) {
                        await AlexaShoppingListItem.removeItemFromShoppingList(completedAlexaShoppingListItemToRemove);
                    }
                }
            
                // Sort the array of alexa shopping list items (case insensitive sorting)
                alexaShoppingListItems.sort((a, b) => a.value.toLowerCase().localeCompare(b.value.toLowerCase()));
            
                return alexaShoppingListItems;
            }
            
            // Bring
            async function getItemsOnBringShoppingList(): Promise <Array<BringShoppingListItem>> {
                var bringShoppingListItems = [];
            
                try {
                    var humanReadableShoppingListObject = await getStateAsync(objectID_bring_shoppingList_sentenceString);
                    var humanReadableShoppingList = humanReadableShoppingListObject.val;
                    if (humanReadableShoppingList !== null && humanReadableShoppingList !== '' && humanReadableShoppingList.length > 0) {
                        humanReadableShoppingList = replaceAllOccurrencesOfASubstringWithinAString(humanReadableShoppingList, ' und ', ', ');
                        humanReadableShoppingList = replaceAllOccurrencesOfASubstringWithinAString(humanReadableShoppingList, ', ', ';');
                        
                        const bringShoppingListItemStrings = splitStringIntoArray(humanReadableShoppingList, ';');
                        
                        for (const bringShoppingListItemString of bringShoppingListItemStrings) {
                            try {
                                const newBringShoppingListItem = new BringShoppingListItem(bringShoppingListItemString);
                                bringShoppingListItems.push(newBringShoppingListItem);
                            } catch (error) {
                                console.error(`Error while creating Alexa Shopping-List Item Object! -  Original Error: ${error.message}`);
                                continue;
                            }
                        }
                    }
                } catch (error) {
                    console.error(`Error while getting Bring Shopping-List Items! - Original Error: ${error.message}`);
                }
            
                // Sort the array of alexa shopping list items (case insensitive sorting)
                bringShoppingListItems.sort((a, b) => a.value.toLowerCase().localeCompare(b.value.toLowerCase()));
            
                return bringShoppingListItems;
            }
            
            
            
            
            
            
            
            
            
            
            /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
            +                                                                     CLASSES                                                                              +
            *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
            class AlexaShoppingListItem {
                public id: string;
                public listID: string;
                public customerID: string;
                public shoppingListItem: boolean;
                public value: string;
                public completed: boolean;
                public version: number;
                public createdDateTime: number;
                public updatedDateTime: number;
                public idOfFolderObject: string;
                public idOfDeleteDatapoint: string;
                
                constructor(
                    id:string,
                    listID:string,
                    customerID:string,
                    shoppingListItem:boolean,
                    value:string,
                    completed:boolean,
                    version:number,
                    createdDateTime:number,
                    updatedDateTime:number,
                    idOfFolderObject:string,
                    idOfDeleteDatapoint:string) {
                        this.id = id;
                        this.listID = listID;
                        this.customerID = customerID;
                        this.shoppingListItem = shoppingListItem;
                        this.value = value;
                        this.completed = completed;
                        this.version = version;
                        this.createdDateTime = createdDateTime;
                        this.updatedDateTime = updatedDateTime;
                        this.idOfFolderObject = idOfFolderObject;
                        this.idOfDeleteDatapoint = idOfDeleteDatapoint;
                }
                
                /********************
                 * Adds an item to the Alexa shopping list.
                 * 
                 * @param item - The item to save to the Alexa shopping list. Must be either an instance of 'AlexaShoppingListItem' or 'BringShoppingListItem'.
                 * @return Promise<boolean> - True if the function completed running its code.
                 ********************/
                static async addItemToShoppingList(item:AlexaShoppingListItem | BringShoppingListItem): Promise <boolean> {
                    // Parameter validation
                    if (!(item instanceof AlexaShoppingListItem || item instanceof BringShoppingListItem)) {
                        throw new Error("Invalid parameter: Parameter 'item' must be an instance of 'AlexaShoppingListItem' or 'BringShoppingListItem'!");
                    }
            
                    await setStateAsync(objectID_alexa_shoppingList_addItem, capitalizeFirstLetter(item.value), false);
                    return true;
                }
            
                /********************
                 * Removes an item from the Alexa shopping list.
                 * 
                 * @param item - The item to remove from the Alexa shopping list. Must be an instance of 'AlexaShoppingListItem'.
                 * @return Promise<boolean> - True if the function completed running its code.
                 ********************/
                 static async removeItemFromShoppingList(item:AlexaShoppingListItem): Promise <boolean> {
                     // Parameter validation
                    if (!(item instanceof AlexaShoppingListItem)) {
                        throw new Error("Invalid parameter: Parameter 'item' must be an instance of 'AlexaShoppingListItem'!");
                    }
            
                    await setStateAsync(item.idOfDeleteDatapoint, true, false);
                    return true;
                 }
            }
            
            
            
            class BringShoppingListItem {
                public value: string;
                
                constructor(value:string) {
                    this.value = value;
                }
            
                /********************
                 * Adds an item to the Bring shopping list.
                 * 
                 * @param item - The item to save to the Bring shopping list. Must be either an instance of 'AlexaShoppingListItem' or 'BringShoppingListItem'.
                 * @return Promise<boolean> - True if the function completed running its code.
                 ********************/
                static async addItemToShoppingList(item:AlexaShoppingListItem | BringShoppingListItem): Promise <boolean> {
                    // Parameter validation
                    if (!(item instanceof AlexaShoppingListItem || item instanceof BringShoppingListItem)) {
                        throw new Error("Invalid parameter: Parameter 'item' must be an instance of 'AlexaShoppingListItem' or 'BringShoppingListItem'!");
                    }
            
                    await setStateAsync(objectID_bring_shoppingList_addItem, capitalizeFirstLetter(item.value), false);
                    return true;
                }
            
                /********************
                 * Removes an item from the Bring shopping list.
                 * 
                 * @param item - The item to remove from the Bring shopping list. Must be an instance of 'BringShoppingListItem'.
                 * @return Promise<boolean> - True if the function completed running its code.
                 ********************/
                 static async removeItemFromShoppingList(item:BringShoppingListItem): Promise <boolean> {
                     // Parameter validation
                    if (!(item instanceof BringShoppingListItem)) {
                        throw new Error("Invalid parameter: Parameter 'item' must be an instance of 'BringShoppingListItem'!");
                    }
            
                    await setStateAsync(objectID_bring_shoppingList_removeItem, item.value, false);
                    return true;
                 }
            }
            
            
            
            class ShoppingListUtils {
                /********************
                 * Checks if a given shopping list array contains a specific shopping list item.
                 * Herefore it compares all items in the given shopping list array with the given shopping list item
                 * (either an 'AlexaShoppingListItem' or a 'BringShoppingListItem')
                 * in a case-insensitive way.
                 * 
                 * @param shoppingListArray - Array of AlexaShoppingListItem or BringShoppingListItem objects to check within.
                 * @param shoppingListItem - The shopping list item to find in the array.
                 * @returns Promise<boolean> - True if the item is found, otherwise false.
                 ********************/
                static async checkIfShoppingListArrayContainsShoppingListItem(shoppingListArray: Array<AlexaShoppingListItem | BringShoppingListItem>, shoppingListItem: AlexaShoppingListItem | BringShoppingListItem): Promise <boolean> {
                    // Parameter validation
                    if (!Array.isArray(shoppingListArray)) {
                        throw new Error("Invalid parameter: 'shoppingListArray' must be an array.");
                    }
                    if (!(shoppingListItem instanceof AlexaShoppingListItem || shoppingListItem instanceof BringShoppingListItem)) {
                        throw new Error("Invalid parameter: 'shoppingListItem' must be an instance of 'AlexaShoppingListItem' or 'BringShoppingListItem'.");
                    }
            
                    for (const item of shoppingListArray) {
                        if (!(item instanceof AlexaShoppingListItem || item instanceof BringShoppingListItem)) {
                            throw new Error("Invalid parameter: All elements in 'shoppingListArray' must be instances of 'AlexaShoppingListItem' or 'BringShoppingListItem'.");
                        }
                    }
                    
                    // Normalize the value of the shopping list item to lower case for case-insensitive comparison
                    const itemValueToCheck = shoppingListItem.value.toLowerCase();
            
                    // Check if any item in the array matches the shopping list item value
                    for (const item of shoppingListArray) {
                        if (item.value.toLowerCase() === itemValueToCheck) {
                            return true;
                        }
                    }
            
                    return false;
                }
            }
            /*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++
            *++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++*++++++++++++++++++++++++++++++++++++++++++++++++++++*/
            
            
            
            
            
            
            
            
            
            
            /*---------- HELPER FUNCTIONS ---------------------------------------------------------------------------------------------------*/
            function capitalizeFirstLetter(input: string): string {
                if (input.length === 0) {
                    return input; // Return the empty string if input is empty
                }
                return input.charAt(0).toUpperCase() + input.slice(1);
            }
            
            function replaceAllOccurrencesOfASubstringWithinAString(originalString: string, searchValue: string, replaceValue: string): string {
                const regex = new RegExp(searchValue, 'g');
                return originalString.replace(regex, replaceValue);
            }
            
            function splitStringIntoArray(input: string, delimiter: string): string[] {
                return input.split(delimiter);
            }
            
            M Offline
            M Offline
            MartyBr
            schrieb am zuletzt editiert von
            #173

            @elron
            Viele Dank. Ich habe es importiert und die Datenpunkte angepasst. Test erfolgt morgen.

            Gruß
            Martin


            Intel NUCs mit Proxmox / Iobroker als VM unter Debian
            Raspeberry mit USB Leseköpfen für Smartmeter
            Homematic und Homematic IP

            1 Antwort Letzte Antwort
            0
            • mcBirneM Offline
              mcBirneM Offline
              mcBirne
              schrieb am zuletzt editiert von mcBirne
              #174

              Ich habe die beiden Datenpunkte angepasst. Leider erhalte ich folgende Fehlermeldung:

              javascript.0	16:24:56.439	error	
              compile failed at: script.js.Alexas.Bring_Synchronisieren2:36
              javascript.0	16:24:56.439	error	
              export default this;
              javascript.0	16:24:56.439	error	
              ^^^^^^
              javascript.0	16:24:56.439	error	
              SyntaxError: Unexpected token 'export'
              javascript.0	16:24:56.439	error	
                  at new Script (node:vm:117:7)
              javascript.0	16:24:56.439	error	
                  at JavaScript.createVM (/opt/iobroker/node_modules/iobroker.javascript/src/main.ts:2174:25)
              javascript.0	16:24:56.439	error	
                  at JavaScript.prepareScript (/opt/iobroker/node_modules/iobroker.javascript/src/main.ts:2472:44)
              javascript.0	16:24:56.439	error	
                  at processTicksAndRejections (node:internal/process/task_queues:105:5)
              javascript.0	16:24:56.439	error	
                  at JavaScript.onObjectChange (/opt/iobroker/node_modules/iobroker.javascript/src/main.ts:659:25)
              

              Hat das Problem noch jemand? Oder weiß jemand, was ich machen kann?

              grrfieldG 1 Antwort Letzte Antwort
              0
              • mcBirneM mcBirne

                Ich habe die beiden Datenpunkte angepasst. Leider erhalte ich folgende Fehlermeldung:

                javascript.0	16:24:56.439	error	
                compile failed at: script.js.Alexas.Bring_Synchronisieren2:36
                javascript.0	16:24:56.439	error	
                export default this;
                javascript.0	16:24:56.439	error	
                ^^^^^^
                javascript.0	16:24:56.439	error	
                SyntaxError: Unexpected token 'export'
                javascript.0	16:24:56.439	error	
                    at new Script (node:vm:117:7)
                javascript.0	16:24:56.439	error	
                    at JavaScript.createVM (/opt/iobroker/node_modules/iobroker.javascript/src/main.ts:2174:25)
                javascript.0	16:24:56.439	error	
                    at JavaScript.prepareScript (/opt/iobroker/node_modules/iobroker.javascript/src/main.ts:2472:44)
                javascript.0	16:24:56.439	error	
                    at processTicksAndRejections (node:internal/process/task_queues:105:5)
                javascript.0	16:24:56.439	error	
                    at JavaScript.onObjectChange (/opt/iobroker/node_modules/iobroker.javascript/src/main.ts:659:25)
                

                Hat das Problem noch jemand? Oder weiß jemand, was ich machen kann?

                grrfieldG Offline
                grrfieldG Offline
                grrfield
                schrieb am zuletzt editiert von
                #175

                @mcBirne Es ist zwar schon einige Zeit her, aber hast Du das Skript als TypeScript eingefügt? Die Fehlermeldungen sehen nach JavaScript aus.

                1 Antwort Letzte Antwort
                0
                • HeimwehH Heimweh

                  @martin_olw Ich hab es jetzt doch getestet und noch was geändert:

                  Hier die Beschreibung:

                  Der Skript überwacht den Datenpunkt alexa2.0.Lists.SHOP.json.

                  Sobald Alexa z. B. sagt:

                  „Alexa, setze vier Äpfel auf die Einkaufsliste“
                  wird das als neuer Eintrag in dieser Liste erkannt.

                  Zahlwort-Umwandlung:

                  Aus „vier Äpfel“ wird „4 Äpfel“.

                  Auch zusammengesetzte Zahlen wie „fünfzehn“ oder „dreiundzwanzig“ werden korrekt erkannt.

                  Das erste Wort nach der Zahl wird großgeschrieben: z. B. „4 Äpfel“ statt „4 äpfel“.

                  Todoist-Eintrag:

                  Der bereinigte und umgewandelte Eintrag wird direkt über die REST-API an Todoist gesendet und dort im definierten Projekt als Aufgabe eingetragen.

                  Automatisches Entfernen in Alexa:

                  Nach 60 Sekunden wird der passende Alexa-Listeneintrag automatisch als „abgehakt“ markiert.

                  Es muss nur die ListID und der Token eingesetzt werden.

                  const axios = require('axios');
                  
                  // Todoist API-Konfiguration
                  const todoistProjectId = 'XXXXXXXX'; // Deine Projekt-ID
                  const todoistToken = 'XXXXXXXXXXXXXXXXXXXXXXX'; // Dein Token
                  
                  let previousList = [];
                  
                  // 1. Alexa-Änderungen überwachen
                  on({ id: 'alexa2.0.Lists.SHOP.json', change: 'any' }, function (obj) {
                      try {
                          const currentList = JSON.parse(obj.state.val);
                  
                          if (currentList && currentList.length > 0) {
                              if (previousList.length < currentList.length) {
                                  const newItem = currentList.find(item =>
                                      !previousList.some(prevItem => prevItem.id === item.id)
                                  );
                  
                                  if (newItem) {
                                      const umgewandelt = wordsToNumbersSmart(newItem.value);
                                      const aufgabe = capitalizeFirst(umgewandelt);
                                      addTaskToTodoist(aufgabe);
                  
                                      // Nach 60 Sekunden Alexa-Eintrag auf "completed" setzen
                                      setTimeout(() => {
                                          const alexaList = JSON.parse(getState('alexa2.0.Lists.SHOP.json').val);
                  
                                          const matchingItem = alexaList.find(item => {
                                              const itemText = wordsToNumbersSmart(item.value).trim().toLowerCase();
                                              return itemText === aufgabe.trim().toLowerCase();
                                          });
                  
                                          if (matchingItem) {
                                              const completeState = `alexa2.0.Lists.SHOP.items.${matchingItem.id}.completed`;
                                              setState(completeState, true);
                                          } else {
                                              console.warn(`⚠️ Kein passender Alexa-Eintrag zu "${aufgabe}" gefunden.`);
                                          }
                                      }, 60 * 1000); // 60 Sekunden
                                  }
                              }
                  
                              previousList = currentList;
                          }
                      } catch (e) {
                          console.error('Fehler beim Parsen der Alexa-Liste:', e.message || e);
                      }
                  });
                  
                  // 2. Aufgaben an Todoist senden
                  function addTaskToTodoist(itemValue) {
                      const todoistData = {
                          content: itemValue,
                          project_id: todoistProjectId
                      };
                  
                      axios.post('https://api.todoist.com/rest/v2/tasks', todoistData, {
                          headers: {
                              'Content-Type': 'application/json',
                              'Authorization': `Bearer ${todoistToken}`
                          }
                      })
                      .then(() => {
                          console.log(`✅ "${itemValue}" zu Todoist hinzugefügt.`);
                      })
                      .catch(error => {
                          console.error('Fehler beim Hinzufügen zu Todoist:', error.message || error);
                      });
                  }
                  
                  // 3. Erstes Wort groß
                  function capitalizeFirst(text) {
                      if (!text || typeof text !== 'string') return '';
                      return text.charAt(0).toUpperCase() + text.slice(1);
                  }
                  
                  // 4. Wörter → Zahlen (z. B. „vierzehn“ → 14)
                  function wordsToNumbersSmart(text) {
                      const ones = {
                          'null': 0, 'eins': 1, 'eine': 1, 'einen': 1,
                          'zwei': 2, 'drei': 3, 'vier': 4, 'fünf': 5,
                          'sechs': 6, 'sieben': 7, 'acht': 8, 'neun': 9,
                          'zehn': 10, 'elf': 11, 'zwölf': 12, 'dreizehn': 13,
                          'vierzehn': 14, 'fünfzehn': 15, 'sechzehn': 16,
                          'siebzehn': 17, 'achtzehn': 18, 'neunzehn': 19
                      };
                  
                      const tens = {
                          'zwanzig': 20, 'dreißig': 30, 'vierzig': 40,
                          'fünfzig': 50, 'sechzig': 60, 'siebzig': 70,
                          'achtzig': 80, 'neunzig': 90
                      };
                  
                      const multipliers = {
                          'hundert': 100,
                          'tausend': 1000
                      };
                  
                      const skipWords = ['und', 'oder', 'mit', 'für', 'pro'];
                      const words = text.toLowerCase().split(/\s+/);
                      const finalText = [];
                      let i = 0;
                      let capitalizeNext = 0;
                  
                      while (i < words.length) {
                          const word = words[i];
                  
                          if (ones[word] !== undefined) {
                              if (i + 2 < words.length && words[i + 1] === 'und' && tens[words[i + 2]]) {
                                  const value = ones[word] + tens[words[i + 2]];
                                  finalText.push(value.toString());
                                  capitalizeNext = 2;
                                  i += 3;
                                  continue;
                              }
                  
                              if (i + 1 < words.length && multipliers[words[i + 1]]) {
                                  const value = ones[word] * multipliers[words[i + 1]];
                                  finalText.push(value.toString());
                                  capitalizeNext = 2;
                                  i += 2;
                                  continue;
                              }
                  
                              finalText.push(ones[word].toString());
                              capitalizeNext = 2;
                              i++;
                          } else if (tens[word] !== undefined) {
                              finalText.push(tens[word].toString());
                              capitalizeNext = 2;
                              i++;
                          } else if (!isNaN(word)) {
                              finalText.push(word);
                              capitalizeNext = 2;
                              i++;
                          } else {
                              if (capitalizeNext > 0 && !skipWords.includes(word)) {
                                  finalText.push(word.charAt(0).toUpperCase() + word.slice(1));
                                  capitalizeNext--;
                              } else {
                                  finalText.push(word);
                              }
                              i++;
                          }
                      }
                  
                      return finalText.join(' ');
                  }
                  
                  
                  M Offline
                  M Offline
                  martin_olw
                  schrieb am zuletzt editiert von
                  #176

                  Hallo @Heimweh und vielen Dank noch einmal für das Script und die Anpassungen. Lief bis vor kurzem alles ohne Probleme bei mir. Jetzt aber bekomme ich immer folgende Fehlermeldung:

                  script.js.common.ToDoist.Einkaufsliste_Alexa_Todoist: Fehler beim Hinzufügen zu Todoist:
                  

                  Mehr nicht. Woran kann das liegen?
                  Prinzipiell funktioniert dein Skript aber:

                  Updating item "bananentest" ({"id":"ad0e6a37-8dc2-4085-83d6-b282773292ac","listId":"YW16bjEuYWNjb3VudC5BRTVPSlgyRFk0UUhEVUJUNEJOWUpPUEJVSjNBLVNIT1BQSU5HX0lURU0=","value":"bananentest","customerId":"A2FTPWL63RKNMN","completed":true,"createdDateTime":1770990517898,"updatedDateTime":1770990517898,"version":1,"note":null,"index":0,"#delete":false,"listName":"SHOP"}) of the list SHOP.
                  

                  Funktioniert das bei dir noch alles?
                  Danke für die ernuete Hilfe!
                  VG Martin

                  1 Antwort Letzte Antwort
                  0
                  • N Offline
                    N Offline
                    no6mis
                    schrieb am zuletzt editiert von
                    #177

                    Todoist hat die API angepasst, v2 gibt es nicht mehr.
                    https://developer.todoist.com/api/v1/#tag/Tasks

                    Du musst die API-URL auf https://api.todoist.com/api/v1/tasks ändern und die Projekt-IDs aktualisieren.

                    M 1 Antwort Letzte Antwort
                    0
                    • N no6mis

                      Todoist hat die API angepasst, v2 gibt es nicht mehr.
                      https://developer.todoist.com/api/v1/#tag/Tasks

                      Du musst die API-URL auf https://api.todoist.com/api/v1/tasks ändern und die Projekt-IDs aktualisieren.

                      M Offline
                      M Offline
                      martin_olw
                      schrieb am zuletzt editiert von
                      #178

                      @no6mis Ich danke dir! Genau das hat geholfen!

                      1 Antwort Letzte Antwort
                      0
                      Antworten
                      • In einem neuen Thema antworten
                      Anmelden zum Antworten
                      • Älteste zuerst
                      • Neuste zuerst
                      • Meiste Stimmen


                      Support us

                      ioBroker
                      Community Adapters
                      Donate

                      585

                      Online

                      32.6k

                      Benutzer

                      82.3k

                      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