diff --git a/README.md b/README.md index 645c986..03fdb80 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,12 @@ This adapter can be used to automatically interact with google spreadsheets. * [Append data to spreadsheet](features/append.md) * [Delete rows from a spreadsheet](features/delete-rows.md) * [Create sheets](features/create-sheet.md) -* [Delete sheets](features/delete-sheet.md) +* [Delete sheet](features/delete-sheet.md) +* [Delete sheets](features/delete-sheets.md) * [Duplicate sheets](features/duplicate-sheet.md) * [Read cell](features/read-cell.md) * [Write cell](features/write-cell.md) +* [Write cells](features/write-cells.md) ## Usage diff --git a/admin/blockly.js b/admin/blockly.js index 9b8092a..76a6687 100644 --- a/admin/blockly.js +++ b/admin/blockly.js @@ -54,6 +54,8 @@ loadJS('../google-spreadsheet/blocks/append.js'); loadJS('../google-spreadsheet/blocks/deleteRows.js'); loadJS('../google-spreadsheet/blocks/createSheet.js'); loadJS('../google-spreadsheet/blocks/deleteSheet.js'); +loadJS('../google-spreadsheet/blocks/deleteSheets.js'); loadJS('../google-spreadsheet/blocks/duplicateSheet.js'); loadJS('../google-spreadsheet/blocks/readCell.js'); loadJS('../google-spreadsheet/blocks/writeCell.js'); +loadJS('../google-spreadsheet/blocks/writeCells.js'); diff --git a/admin/blocks/append.js b/admin/blocks/append.js index cc099ea..d397406 100644 --- a/admin/blocks/append.js +++ b/admin/blocks/append.js @@ -10,10 +10,7 @@ Blockly.Words['google-spreadsheet_append_add-to-suffix'] = { en: '', de: 'hinzu' Blockly.Sendto.blocks['google-spreadsheet.append'] = '' + - ' ' + - ' ' + - ' ' + - ' ' + + ' ' + ' ' + ' ' + ' text' + @@ -45,13 +42,13 @@ Blockly.Blocks['google-spreadsheet.append'] = { }, }; -Blockly.JavaScript['google-spreadsheet.append'] = function (block) { - var dropdown_instance = block.getFieldValue('INSTANCE'); - var data = Blockly.JavaScript.valueToCode(block, 'DATA', Blockly.JavaScript.ORDER_ATOMIC); +Blockly.JavaScript.forBlock['google-spreadsheet.append'] = function (block) { + const dropdown_instance = block.getFieldValue('INSTANCE'); + let data = Blockly.JavaScript.valueToCode(block, 'DATA', Blockly.JavaScript.ORDER_ATOMIC); if (!data) { data = '{}'; } - var sheetName = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); + const sheetName = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); return `sendTo("google-spreadsheet${dropdown_instance}", "append", {"sheetName":${sheetName}, "data":${data}});\n`; }; diff --git a/admin/blocks/createSheet.js b/admin/blocks/createSheet.js index f3e84c8..2416fe5 100644 --- a/admin/blocks/createSheet.js +++ b/admin/blocks/createSheet.js @@ -11,10 +11,7 @@ Blockly.Words['google-spreadsheet_create-sheet_sheet-name'] = { Blockly.Sendto.blocks['google-spreadsheet.createSheet'] = '' + - ' ' + - ' ' + - ' ' + - ' ' + + ' ' + ' ' + ' ' + ' text' + @@ -44,9 +41,9 @@ Blockly.Blocks['google-spreadsheet.createSheet'] = { }, }; -Blockly.JavaScript['google-spreadsheet.createSheet'] = function (block) { - var dropdown_instance = block.getFieldValue('INSTANCE'); - var data = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); +Blockly.JavaScript.forBlock['google-spreadsheet.createSheet'] = function (block) { + const dropdown_instance = block.getFieldValue('INSTANCE'); + const data = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); return `sendTo("google-spreadsheet${dropdown_instance}", "createSheet", ${data});\n`; }; diff --git a/admin/blocks/deleteRows.js b/admin/blocks/deleteRows.js index efcfeb6..b18629c 100644 --- a/admin/blocks/deleteRows.js +++ b/admin/blocks/deleteRows.js @@ -11,8 +11,7 @@ Blockly.Words['google-spreadsheet_delete-rows_endRow'] = { en: 'to', de: 'bis' } Blockly.Sendto.blocks['google-spreadsheet.deleteRows'] = '' + - ' ' + - ' ' + + ' ' + ' ' + ' ' + ' text' + @@ -62,11 +61,11 @@ Blockly.Blocks['google-spreadsheet.deleteRows'] = { }, }; -Blockly.JavaScript['google-spreadsheet.deleteRows'] = function (block) { - var dropdown_instance = block.getFieldValue('INSTANCE'); - var startRow = Blockly.JavaScript.valueToCode(block, 'START_ROW', Blockly.JavaScript.ORDER_ATOMIC); - var endRow = Blockly.JavaScript.valueToCode(block, 'END_ROW', Blockly.JavaScript.ORDER_ATOMIC); - var sheetName = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); +Blockly.JavaScript.forBlock['google-spreadsheet.deleteRows'] = function (block) { + const dropdown_instance = block.getFieldValue('INSTANCE'); + const startRow = Blockly.JavaScript.valueToCode(block, 'START_ROW', Blockly.JavaScript.ORDER_ATOMIC); + const endRow = Blockly.JavaScript.valueToCode(block, 'END_ROW', Blockly.JavaScript.ORDER_ATOMIC); + const sheetName = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); return `sendTo("google-spreadsheet${dropdown_instance}", "deleteRows", {"sheetName":${sheetName},"start":${ startRow }, "end":${endRow}});\n`; diff --git a/admin/blocks/deleteSheet.js b/admin/blocks/deleteSheet.js index 131e41b..d74b370 100644 --- a/admin/blocks/deleteSheet.js +++ b/admin/blocks/deleteSheet.js @@ -10,8 +10,7 @@ Blockly.Words['google-spreadsheet_delete-sheet_sheetName'] = { Blockly.Sendto.blocks['google-spreadsheet.deleteSheet'] = '' + - ' ' + - ' ' + + ' ' + ' ' + ' ' + ' text' + @@ -21,7 +20,7 @@ Blockly.Sendto.blocks['google-spreadsheet.deleteSheet'] = Blockly.Blocks['google-spreadsheet.deleteSheet'] = { init: function () { - var instances = getInstances(); + const instances = getInstances(); this.appendDummyInput('INSTANCE') .appendField(Blockly.Translate('google-spreadsheet_delete-sheet_delete-on')) @@ -39,9 +38,9 @@ Blockly.Blocks['google-spreadsheet.deleteSheet'] = { }, }; -Blockly.JavaScript['google-spreadsheet.deleteSheet'] = function (block) { - var dropdown_instance = block.getFieldValue('INSTANCE'); - var data = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); +Blockly.JavaScript.forBlock['google-spreadsheet.deleteSheet'] = function (block) { + const dropdown_instance = block.getFieldValue('INSTANCE'); + const data = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); return `sendTo("google-spreadsheet${dropdown_instance}", "deleteSheet", ${data});\n`; }; diff --git a/admin/blocks/deleteSheets.js b/admin/blocks/deleteSheets.js new file mode 100644 index 0000000..bd37bcb --- /dev/null +++ b/admin/blocks/deleteSheets.js @@ -0,0 +1,48 @@ +'use strict'; +/*global Blockly */ +/*global getInstances */ + +Blockly.Words['google-spreadsheet_delete-sheets_delete-on'] = { en: 'delete sheets in', de: 'lösche Blätter in' }; +Blockly.Words['google-spreadsheet_delete-sheets_sheetNames'] = { + en: 'the sheets with names (array)', + de: 'die Blätter mit Namen (Array)', +}; + +Blockly.Sendto.blocks['google-spreadsheet.deleteSheets'] = + '' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + +Blockly.Blocks['google-spreadsheet.deleteSheets'] = { + init: function () { + const instances = getInstances(); + + this.appendDummyInput('INSTANCE') + .appendField(Blockly.Translate('google-spreadsheet_delete-sheets_delete-on')) + .appendField(new Blockly.FieldDropdown(instances), 'INSTANCE'); + + this.appendValueInput('SHEET_NAMES') + .setCheck('Array') + .appendField(Blockly.Translate('google-spreadsheet_delete-sheets_sheetNames')); + + this.setInputsInline(false); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + + this.setColour(Blockly.Sendto.HUE); + this.setTooltip(Blockly.Translate('google-spreadsheet_tooltip')); + this.setHelpUrl(Blockly.Translate('google-spreadsheet_help')); + }, +}; + +Blockly.Blocks['google-spreadsheet.deleteSheets'].mutator = 'google-spreadsheet_deleteSheets_mutator'; + +Blockly.JavaScript.forBlock['google-spreadsheet.deleteSheets'] = function (block) { + const dropdown_instance = block.getFieldValue('INSTANCE'); + const data = Blockly.JavaScript.valueToCode(block, 'SHEET_NAMES', Blockly.JavaScript.ORDER_ATOMIC); + + return `sendTo("google-spreadsheet${dropdown_instance}", "deleteSheets", ${data});\n`; +}; diff --git a/admin/blocks/duplicateSheet.js b/admin/blocks/duplicateSheet.js index d27202a..48ca82e 100644 --- a/admin/blocks/duplicateSheet.js +++ b/admin/blocks/duplicateSheet.js @@ -12,8 +12,7 @@ Blockly.Words['google-spreadsheet_duplicate-sheet_newPosition'] = { en: 'at posi Blockly.Sendto.blocks['google-spreadsheetduplicateSheet'] = '' + - ' ' + - ' ' + + ' ' + ' ' + ' ' + ' text' + @@ -59,11 +58,11 @@ Blockly.Blocks['google-spreadsheet.duplicateSheet'] = { }, }; -Blockly.JavaScript['google-spreadsheet.duplicateSheet'] = function (block) { - var dropdown_instance = block.getFieldValue('INSTANCE'); - var source = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); - var target = Blockly.JavaScript.valueToCode(block, 'NEW_SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); - var index = Blockly.JavaScript.valueToCode(block, 'NEW_POSITION', Blockly.JavaScript.ORDER_ATOMIC); +Blockly.JavaScript.forBlock['google-spreadsheet.duplicateSheet'] = function (block) { + const dropdown_instance = block.getFieldValue('INSTANCE'); + const source = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); + const target = Blockly.JavaScript.valueToCode(block, 'NEW_SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); + let index = Blockly.JavaScript.valueToCode(block, 'NEW_POSITION', Blockly.JavaScript.ORDER_ATOMIC); if (!index) { index = -1; } diff --git a/admin/blocks/readCell.js b/admin/blocks/readCell.js index f39fd55..be0a1bf 100644 --- a/admin/blocks/readCell.js +++ b/admin/blocks/readCell.js @@ -9,10 +9,7 @@ Blockly.Words['google-spreadsheet_read_on-sheetName'] = { en: 'sheet', de: 'Tabe Blockly.Words['google-spreadsheet_read_in-cell'] = { en: 'cell', de: 'Zelle' }; Blockly.Sendto.blocks['google-spreadsheet.read'] = '' + - ' ' + - ' ' + - ' ' + - ' ' + + ' ' + ' ' + ' ' + ' text' + @@ -46,14 +43,14 @@ Blockly.Blocks['google-spreadsheet.read'] = { }, }; -Blockly.JavaScript['google-spreadsheet.read'] = function (block) { - var dropdown_instance = block.getFieldValue('INSTANCE'); - var data = Blockly.JavaScript.valueToCode(block, 'DATA', Blockly.JavaScript.ORDER_ATOMIC); +Blockly.JavaScript.forBlock['google-spreadsheet.read'] = function (block) { + const dropdown_instance = block.getFieldValue('INSTANCE'); + let data = Blockly.JavaScript.valueToCode(block, 'DATA', Blockly.JavaScript.ORDER_ATOMIC); if (!data) { data = '{}'; } - var sheetName = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); - var cell = Blockly.JavaScript.valueToCode(block, 'CELL', Blockly.JavaScript.ORDER_ATOMIC); + const sheetName = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); + const cell = Blockly.JavaScript.valueToCode(block, 'CELL', Blockly.JavaScript.ORDER_ATOMIC); return [ `await new Promise((resolve)=>{sendTo("google-spreadsheet${dropdown_instance}", "readCell", {"sheetName":"${ diff --git a/admin/blocks/writeCell.js b/admin/blocks/writeCell.js index 02b3f4c..1d59c55 100644 --- a/admin/blocks/writeCell.js +++ b/admin/blocks/writeCell.js @@ -1,6 +1,6 @@ 'use strict'; -/*global Blockly:true */ -/*global getInstances:true */ +/*global Blockly */ +/*global getInstances */ /// --- Write Cell -------------------------------------------------- @@ -11,20 +11,17 @@ Blockly.Words['google-spreadsheet_writeCell_data'] = { en: 'the data', de: 'die Blockly.Sendto.blocks['google-spreadsheet.writeCell'] = '' + - ' ' + - ' ' + - ' ' + - ' ' + - ' ' + + ' ' + + ' ' + ' ' + ' text' + ' ' + - ' ' + - ' ' + + ' ' + + ' ' + ' ' + ' A1' + ' ' + - ' ' + + ' ' + ' ' + ' ' + ''; @@ -51,11 +48,11 @@ Blockly.Blocks['google-spreadsheet.writeCell'] = { }, }; -Blockly.JavaScript['google-spreadsheet.writeCell'] = function (block) { - var dropdown_instance = block.getFieldValue('INSTANCE'); - var sheetName = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); - var cell = Blockly.JavaScript.valueToCode(block, 'CELL', Blockly.JavaScript.ORDER_ATOMIC); - var data = Blockly.JavaScript.valueToCode(block, 'DATA', Blockly.JavaScript.ORDER_ATOMIC); +Blockly.JavaScript.forBlock['google-spreadsheet.writeCell'] = function (block) { + const dropdown_instance = block.getFieldValue('INSTANCE'); + const sheetName = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); + const cell = Blockly.JavaScript.valueToCode(block, 'CELL', Blockly.JavaScript.ORDER_ATOMIC); + const data = Blockly.JavaScript.valueToCode(block, 'DATA', Blockly.JavaScript.ORDER_ATOMIC); return ( `sendTo("google-spreadsheet${dropdown_instance}", "writeCell", {"sheetName": ${sheetName}, "cell": ${ diff --git a/admin/blocks/writeCells.js b/admin/blocks/writeCells.js new file mode 100644 index 0000000..836ad95 --- /dev/null +++ b/admin/blocks/writeCells.js @@ -0,0 +1,90 @@ +'use strict'; +/*global Blockly */ +/*global getInstances */ + +/// --- Write Cells -------------------------------------------------- + +Blockly.Words['google-spreadsheet_writeCells_write-to'] = { en: 'write cells to', de: 'Schreibe Zellen in ' }; +Blockly.Words['google-spreadsheet_writeCells_cells'] = { en: 'cells', de: 'Zellen' }; + +Blockly.Sendto.blocks['google-spreadsheet.writeCells'] = + '' + + ' ' + + ' ' + + ''; + +// addCell-Block in die Toolbox aufnehmen, damit er auswählbar ist +Blockly.Sendto.blocks['google-spreadsheet.addCell'] = + '' + + ' ' + + ' ' + + ' text' + + ' ' + + ' ' + + ' ' + + ' ' + + ' A1' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ' ' + + ''; + +Blockly.Blocks['google-spreadsheet.writeCells'] = { + init: function () { + const instances = getInstances(); + + this.appendDummyInput('NAME') + .appendField(Blockly.Translate('google-spreadsheet_writeCells_write-to')) + .appendField(new Blockly.FieldDropdown(instances), 'INSTANCE'); + + this.appendStatementInput('CELLS') + .setCheck('google-spreadsheet.writeCell') + .appendField(Blockly.Translate('google-spreadsheet_writeCells_cells')); + + this.setInputsInline(false); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + this.setColour(Blockly.Sendto.HUE); + }, +}; + +Blockly.JavaScript.forBlock['google-spreadsheet.writeCells'] = function (block) { + const dropdown_instance = block.getFieldValue('INSTANCE'); + const cellsCode = Blockly.JavaScript.statementToCode(block, 'CELLS'); + // cellsCode enthält mehrere Zeilen wie: addCell({sheetName: ..., cell: ..., data: ...}); + // Wir sammeln die Argumente in ein Array + const cells = []; + const cellRegex = /addCell\((\{[^}]+\})\);/g; + let match; + while ((match = cellRegex.exec(cellsCode)) !== null) { + try { + cells.push(eval(`(${match[1]})`)); + } catch (e) { + console.error('Error parsing cell data:', e); + } + } + return `sendTo("google-spreadsheet${dropdown_instance}", "writeCells", {cells: ${JSON.stringify(cells)}});\n`; +}; + +// Hilfsblock für einzelne Zelleingabe (nur für writeCells) +Blockly.Blocks['google-spreadsheet.addCell'] = { + init: function () { + this.appendValueInput('SHEET_NAME').appendField('Sheet'); + this.appendValueInput('CELL').appendField('Cell'); + this.appendValueInput('DATA').appendField('Data'); + this.setPreviousStatement(true, 'google-spreadsheet.writeCell'); + this.setNextStatement(true, 'google-spreadsheet.writeCell'); + this.setColour(Blockly.Sendto.HUE); + }, +}; + +Blockly.JavaScript.forBlock['google-spreadsheet.addCell'] = function (block) { + const sheetName = Blockly.JavaScript.valueToCode(block, 'SHEET_NAME', Blockly.JavaScript.ORDER_ATOMIC); + const cell = Blockly.JavaScript.valueToCode(block, 'CELL', Blockly.JavaScript.ORDER_ATOMIC); + const data = Blockly.JavaScript.valueToCode(block, 'DATA', Blockly.JavaScript.ORDER_ATOMIC); + return `addCell({sheetName: ${sheetName}, cell: ${cell}, data: ${data}});\n`; +}; diff --git a/build/lib/google.js b/build/lib/google.js index b6a5d82..bc6db71 100644 --- a/build/lib/google.js +++ b/build/lib/google.js @@ -224,6 +224,45 @@ class SpreadsheetUtils { this.log.error(`Error while sending data to Google Spreadsheet:${error}`); }); } + /** + * Delete multiple sheets from the Google Spreadsheet + * + * @param titles The titles of the sheets to delete + */ + deleteSheets(titles) { + const sheets = this.init(); + sheets.spreadsheets.get({ spreadsheetId: this.config.spreadsheetId }).then((spreadsheet) => { + if (spreadsheet && spreadsheet.data.sheets) { + const requests = []; + for (const title of titles) { + const sheet = spreadsheet.data.sheets.find( + (sheet2) => sheet2.properties && sheet2.properties.title == title + ); + if (sheet && sheet.properties) { + requests.push({ + deleteSheet: { + sheetId: sheet.properties.sheetId + } + }); + } + } + if (requests.length > 0) { + sheets.spreadsheets.batchUpdate({ + spreadsheetId: this.config.spreadsheetId, + requestBody: { + requests + } + }).then(() => { + this.log.debug("Sheets successfully deleted from google spreadsheet"); + }).catch((error) => { + this.log.error(`Error while deleting sheets from Google Spreadsheet:${error}`); + }); + } + } + }).catch((error) => { + this.log.error(`Error while deleting sheets from Google Spreadsheet:${error}`); + }); + } /** * Send data to a Google Spreadsheet * @@ -255,25 +294,45 @@ class SpreadsheetUtils { * @param data Data to write */ writeCell(sheetName, cell, data) { + this.writeCells([{ sheetName, cell, data }]); + } + /** + * Write multiple cells in a Google Spreadsheet + * + * @param cells Array of objects: { sheetName, cell, data } + */ + writeCells(cells) { const sheets = this.init(); - if (cell.startsWith("'") && cell.endsWith("'")) { - cell = cell.substring(1, cell.length - 1); + const grouped = {}; + for (const cellObj of cells) { + if (!grouped[cellObj.sheetName]) { + grouped[cellObj.sheetName] = []; + } + grouped[cellObj.sheetName].push({ cell: cellObj.cell, data: cellObj.data }); + } + const data = []; + for (const sheetName in grouped) { + for (const entry of grouped[sheetName]) { + let cell = entry.cell; + if (cell.startsWith("'") && cell.endsWith("'")) { + cell = cell.substring(1, cell.length - 1); + } + data.push({ + range: `${sheetName}!${cell}`, + values: this.prepareValues(entry.data) + }); + } } sheets.spreadsheets.values.batchUpdate({ spreadsheetId: this.config.spreadsheetId, requestBody: { valueInputOption: "USER_ENTERED", - data: [ - { - range: `${sheetName}!${cell}`, - values: this.prepareValues(data) - } - ] + data } }).then(() => { - this.log.debug("Data successfully sent to google spreadsheet"); + this.log.debug("Cells successfully written to google spreadsheet"); }).catch((error) => { - this.log.error(`Error while sending data to Google Spreadsheet:${error}`); + this.log.error(`Error while writing cells to Google Spreadsheet:${error}`); }); } /** diff --git a/build/lib/google.js.map b/build/lib/google.js.map index f8dfea7..ee88de6 100644 --- a/build/lib/google.js.map +++ b/build/lib/google.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../src/lib/google.ts"], - "sourcesContent": ["import { google } from 'googleapis';\nimport type { sheets_v4 } from 'googleapis/build/src/apis/sheets/v4';\nimport { JWT } from 'google-auth-library';\n/**\n * This class provides utility functions to interact with Google Sheets\n *\n * @param config The adapter configuration\n * @param log The logger\n */\nexport class SpreadsheetUtils {\n /**\n * Constructor\n *\n * @param config The adapter configuration\n * @param log The logger\n */\n public constructor(\n private config: ioBroker.AdapterConfig,\n private log: ioBroker.Log,\n ) {}\n\n /**\n * Delete rows from a Google Spreadsheet\n *\n * @param sheetName Name of the sheet\n * @param start First row to delete\n * @param end Last row to delete\n */\n public deleteRows(sheetName: string, start: number, end: number): void {\n const sheets = this.init();\n\n sheets.spreadsheets\n .get({ spreadsheetId: this.config.spreadsheetId })\n .then(spreadsheet => {\n if (spreadsheet && spreadsheet.data.sheets) {\n const sheet = spreadsheet.data.sheets.find(\n sheet => sheet.properties && sheet.properties.title == sheetName,\n );\n if (sheet && sheet.properties) {\n const sheetId = sheet.properties.sheetId;\n sheets.spreadsheets\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n requests: [\n {\n deleteDimension: {\n range: {\n dimension: 'ROWS',\n endIndex: end,\n sheetId: sheetId,\n startIndex: start - 1,\n },\n },\n },\n ],\n },\n })\n .then(() => {\n this.log.debug('Rows successfully deleted from google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while deleting rows from Google Spreadsheet:${error}`);\n });\n }\n }\n })\n .catch(error => {\n this.log.error(`Error while deleting rows from Google Spreadsheet:${error}`);\n });\n }\n\n private init(): sheets_v4.Sheets {\n const auth = new google.auth.GoogleAuth({\n credentials: {\n client_email: this.config.serviceAccountEmail,\n private_key: this.formatPrivateKey(this.config.privateKey),\n },\n scopes: ['https://www.googleapis.com/auth/spreadsheets'],\n });\n return google.sheets({ version: 'v4', auth });\n }\n\n /**\n * Create a new sheet in the Google Spreadsheet\n *\n * @param title The title of the new sheet\n */\n public createSheet(title: string): void {\n const sheets = this.init();\n\n sheets.spreadsheets\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n requests: [\n {\n addSheet: {\n properties: {\n title: title,\n },\n },\n },\n ],\n },\n })\n .then(() => {\n this.log.debug('Sheet created successfully');\n })\n .catch(error => {\n this.log.error(`Error while creating sheet:${error}`);\n });\n }\n\n /**\n * Duplicate a sheet in the Google Spreadsheet\n *\n * @param source Name of the source sheet\n * @param target Name of the target sheet\n * @param index Position of the new sheet\n */\n public duplicateSheet(source: string, target: string, index: number): void {\n const sheets = this.init();\n\n sheets.spreadsheets\n .get({ spreadsheetId: this.config.spreadsheetId })\n .then(spreadsheet => {\n if (spreadsheet && spreadsheet.data.sheets) {\n const sheet = spreadsheet.data.sheets.find(\n sheet => sheet.properties && sheet.properties.title == source,\n );\n if (sheet && sheet.properties) {\n let insertIndex = index;\n if (insertIndex == -1 || insertIndex == undefined) {\n insertIndex = spreadsheet.data.sheets.length;\n }\n sheets.spreadsheets\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n requests: [\n {\n duplicateSheet: {\n sourceSheetId: sheet.properties.sheetId,\n newSheetName: target,\n insertSheetIndex: insertIndex,\n },\n },\n ],\n },\n })\n .then(() => {\n this.log.debug('Data successfully sent to google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n } else {\n this.log.warn(`Cannot find sheet: ${source}`);\n }\n }\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Upload a file to the Google Drive\n *\n * @param target Name of the target file\n * @param parentFolder Name of the parent folder\n * @param filecontent Data of the file\n */\n public upload(target: string, parentFolder: string, filecontent: any): void {\n const auth = new google.auth.GoogleAuth({\n credentials: {\n client_email: this.config.serviceAccountEmail,\n private_key: this.formatPrivateKey(this.config.privateKey),\n },\n scopes: ['https://www.googleapis.com/auth/spreadsheets'],\n });\n const driveapi = google.drive({ version: 'v3', auth });\n\n driveapi.files\n .create({\n requestBody: {\n parents: [parentFolder],\n name: target,\n },\n media: {\n mimeType: 'application/octet-stream',\n body: filecontent,\n },\n fields: 'id',\n })\n .then(() => {\n this.log.debug('Data successfully uploaded to google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while uploading data to Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Delete a sheet from the Google Spreadsheet\n *\n * @param title The title of the sheet to delete\n */\n public deleteSheet(title: string): void {\n const sheets = this.init();\n\n sheets.spreadsheets\n .get({ spreadsheetId: this.config.spreadsheetId })\n .then(spreadsheet => {\n if (spreadsheet && spreadsheet.data.sheets) {\n const sheet = spreadsheet.data.sheets.find(\n sheet => sheet.properties && sheet.properties.title == title,\n );\n if (sheet && sheet.properties) {\n sheets.spreadsheets\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n requests: [\n {\n deleteSheet: {\n sheetId: sheet.properties.sheetId,\n },\n },\n ],\n },\n })\n .then(() => {\n this.log.debug('Data successfully sent to google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n }\n }\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Send data to a Google Spreadsheet\n *\n * @param sheetName Name of the sheet\n * @param data Data to send\n */\n public append(sheetName: string, data: any): void {\n const sheets = this.init();\n\n sheets.spreadsheets.values\n .append({\n // The [A1 notation](/sheets/api/guides/concepts#cell) of a range to search for a logical table of data. Values are appended after the last row of the table.\n range: sheetName,\n spreadsheetId: this.config.spreadsheetId,\n valueInputOption: 'USER_ENTERED',\n // Request body metadata\n requestBody: {\n values: this.prepareValues(data),\n },\n })\n .then(() => {\n this.log.debug('Data successfully sent to google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Write data to a cell in a Google Spreadsheet\n *\n * @param sheetName Name of the sheet\n * @param cell Cell to write to\n * @param data Data to write\n */\n public writeCell(sheetName: string, cell: string, data: any): void {\n const sheets = this.init();\n if (cell.startsWith(\"'\") && cell.endsWith(\"'\")) {\n cell = cell.substring(1, cell.length - 1);\n }\n\n sheets.spreadsheets.values\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n valueInputOption: 'USER_ENTERED',\n data: [\n {\n range: `${sheetName}!${cell}`,\n values: this.prepareValues(data),\n },\n ],\n },\n })\n .then(() => {\n this.log.debug('Data successfully sent to google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Read data from a cell in a Google Spreadsheet\n *\n * @param sheetName Name of the sheet\n * @param cell Cell to read from\n * @returns The data from the cell\n */\n public async readCell(sheetName: string, cell: string): Promise {\n const sheets = this.init();\n return new Promise((resolve, reject) => {\n if (cell.startsWith(\"'\") && cell.endsWith(\"'\")) {\n cell = cell.substring(1, cell.length - 1);\n }\n sheets.spreadsheets.values\n .get({\n range: `${sheetName}!${cell}`,\n spreadsheetId: this.config.spreadsheetId,\n })\n .then(response => {\n this.log.debug('Data successfully retrieved from google spreadsheet');\n if (response.data.values && response.data.values.length > 0) {\n resolve(response.data.values[0][0]);\n } else {\n reject(new Error('No data found'));\n }\n })\n .catch(error => {\n this.log.error(`Error while retrieving data from Google Spreadsheet:${error}`);\n reject(new Error(`Error while retrieving data from Google Spreadsheet: ${error.message}`));\n });\n });\n }\n private prepareValues(message: any): any {\n if (Array.isArray(message)) {\n return [message];\n }\n return [[message]];\n }\n private formatPrivateKey(privateKey: string): string | undefined {\n //replace all \\n with line breaks\n if (privateKey) {\n return privateKey.replace(/\\\\n/g, '\\n');\n }\n return undefined;\n }\n}\n"], - "mappings": "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", + "sourcesContent": ["import { google } from 'googleapis';\nimport type { sheets_v4 } from 'googleapis/build/src/apis/sheets/v4';\n/**\n * This class provides utility functions to interact with Google Sheets\n *\n * @param config The adapter configuration\n * @param log The logger\n */\nexport class SpreadsheetUtils {\n /**\n * Constructor\n *\n * @param config The adapter configuration\n * @param log The logger\n */\n public constructor(\n private config: ioBroker.AdapterConfig,\n private log: ioBroker.Log,\n ) {}\n\n /**\n * Delete rows from a Google Spreadsheet\n *\n * @param sheetName Name of the sheet\n * @param start First row to delete\n * @param end Last row to delete\n */\n public deleteRows(sheetName: string, start: number, end: number): void {\n const sheets = this.init();\n\n sheets.spreadsheets\n .get({ spreadsheetId: this.config.spreadsheetId })\n .then(spreadsheet => {\n if (spreadsheet && spreadsheet.data.sheets) {\n const sheet = spreadsheet.data.sheets.find(\n sheet => sheet.properties && sheet.properties.title == sheetName,\n );\n if (sheet && sheet.properties) {\n const sheetId = sheet.properties.sheetId;\n sheets.spreadsheets\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n requests: [\n {\n deleteDimension: {\n range: {\n dimension: 'ROWS',\n endIndex: end,\n sheetId: sheetId,\n startIndex: start - 1,\n },\n },\n },\n ],\n },\n })\n .then(() => {\n this.log.debug('Rows successfully deleted from google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while deleting rows from Google Spreadsheet:${error}`);\n });\n }\n }\n })\n .catch(error => {\n this.log.error(`Error while deleting rows from Google Spreadsheet:${error}`);\n });\n }\n\n private init(): sheets_v4.Sheets {\n const auth = new google.auth.GoogleAuth({\n credentials: {\n client_email: this.config.serviceAccountEmail,\n private_key: this.formatPrivateKey(this.config.privateKey),\n },\n scopes: ['https://www.googleapis.com/auth/spreadsheets'],\n });\n return google.sheets({ version: 'v4', auth });\n }\n\n /**\n * Create a new sheet in the Google Spreadsheet\n *\n * @param title The title of the new sheet\n */\n public createSheet(title: string): void {\n const sheets = this.init();\n\n sheets.spreadsheets\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n requests: [\n {\n addSheet: {\n properties: {\n title: title,\n },\n },\n },\n ],\n },\n })\n .then(() => {\n this.log.debug('Sheet created successfully');\n })\n .catch(error => {\n this.log.error(`Error while creating sheet:${error}`);\n });\n }\n\n /**\n * Duplicate a sheet in the Google Spreadsheet\n *\n * @param source Name of the source sheet\n * @param target Name of the target sheet\n * @param index Position of the new sheet\n */\n public duplicateSheet(source: string, target: string, index: number): void {\n const sheets = this.init();\n\n sheets.spreadsheets\n .get({ spreadsheetId: this.config.spreadsheetId })\n .then(spreadsheet => {\n if (spreadsheet && spreadsheet.data.sheets) {\n const sheet = spreadsheet.data.sheets.find(\n sheet => sheet.properties && sheet.properties.title == source,\n );\n if (sheet && sheet.properties) {\n let insertIndex = index;\n if (insertIndex == -1 || insertIndex == undefined) {\n insertIndex = spreadsheet.data.sheets.length;\n }\n sheets.spreadsheets\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n requests: [\n {\n duplicateSheet: {\n sourceSheetId: sheet.properties.sheetId,\n newSheetName: target,\n insertSheetIndex: insertIndex,\n },\n },\n ],\n },\n })\n .then(() => {\n this.log.debug('Data successfully sent to google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n } else {\n this.log.warn(`Cannot find sheet: ${source}`);\n }\n }\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Upload a file to the Google Drive\n *\n * @param target Name of the target file\n * @param parentFolder Name of the parent folder\n * @param filecontent Data of the file\n */\n public upload(target: string, parentFolder: string, filecontent: any): void {\n const auth = new google.auth.GoogleAuth({\n credentials: {\n client_email: this.config.serviceAccountEmail,\n private_key: this.formatPrivateKey(this.config.privateKey),\n },\n scopes: ['https://www.googleapis.com/auth/spreadsheets'],\n });\n const driveapi = google.drive({ version: 'v3', auth });\n\n driveapi.files\n .create({\n requestBody: {\n parents: [parentFolder],\n name: target,\n },\n media: {\n mimeType: 'application/octet-stream',\n body: filecontent,\n },\n fields: 'id',\n })\n .then(() => {\n this.log.debug('Data successfully uploaded to google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while uploading data to Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Delete a sheet from the Google Spreadsheet\n *\n * @param title The title of the sheet to delete\n */\n public deleteSheet(title: string): void {\n const sheets = this.init();\n\n sheets.spreadsheets\n .get({ spreadsheetId: this.config.spreadsheetId })\n .then(spreadsheet => {\n if (spreadsheet && spreadsheet.data.sheets) {\n const sheet = spreadsheet.data.sheets.find(\n sheet => sheet.properties && sheet.properties.title == title,\n );\n if (sheet && sheet.properties) {\n sheets.spreadsheets\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n requests: [\n {\n deleteSheet: {\n sheetId: sheet.properties.sheetId,\n },\n },\n ],\n },\n })\n .then(() => {\n this.log.debug('Data successfully sent to google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n }\n }\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Delete multiple sheets from the Google Spreadsheet\n *\n * @param titles The titles of the sheets to delete\n */\n public deleteSheets(titles: string[]): void {\n const sheets = this.init();\n\n sheets.spreadsheets\n .get({ spreadsheetId: this.config.spreadsheetId })\n .then(spreadsheet => {\n if (spreadsheet && spreadsheet.data.sheets) {\n const requests: sheets_v4.Schema$Request[] = [];\n for (const title of titles) {\n const sheet = spreadsheet.data.sheets.find(\n sheet => sheet.properties && sheet.properties.title == title,\n );\n if (sheet && sheet.properties) {\n requests.push({\n deleteSheet: {\n sheetId: sheet.properties.sheetId,\n },\n });\n }\n }\n if (requests.length > 0) {\n sheets.spreadsheets\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n requests: requests,\n },\n })\n .then(() => {\n this.log.debug('Sheets successfully deleted from google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while deleting sheets from Google Spreadsheet:${error}`);\n });\n }\n }\n })\n .catch(error => {\n this.log.error(`Error while deleting sheets from Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Send data to a Google Spreadsheet\n *\n * @param sheetName Name of the sheet\n * @param data Data to send\n */\n public append(sheetName: string, data: any): void {\n const sheets = this.init();\n\n sheets.spreadsheets.values\n .append({\n // The [A1 notation](/sheets/api/guides/concepts#cell) of a range to search for a logical table of data. Values are appended after the last row of the table.\n range: sheetName,\n spreadsheetId: this.config.spreadsheetId,\n valueInputOption: 'USER_ENTERED',\n // Request body metadata\n requestBody: {\n values: this.prepareValues(data),\n },\n })\n .then(() => {\n this.log.debug('Data successfully sent to google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while sending data to Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Write data to a cell in a Google Spreadsheet\n *\n * @param sheetName Name of the sheet\n * @param cell Cell to write to\n * @param data Data to write\n */\n public writeCell(sheetName: string, cell: string, data: any): void {\n this.writeCells([{ sheetName, cell, data }]);\n }\n\n /**\n * Write multiple cells in a Google Spreadsheet\n *\n * @param cells Array of objects: { sheetName, cell, data }\n */\n public writeCells(cells: Array<{ sheetName: string; cell: string; data: any }>): void {\n const sheets = this.init();\n // Gruppiere nach sheetName, da batchUpdate mehrere Bereiche pro Sheet erlaubt\n const grouped: { [sheet: string]: Array<{ cell: string; data: any }> } = {};\n for (const cellObj of cells) {\n if (!grouped[cellObj.sheetName]) {\n grouped[cellObj.sheetName] = [];\n }\n grouped[cellObj.sheetName].push({ cell: cellObj.cell, data: cellObj.data });\n }\n const data: Array<{ range: string; values: any[][] }> = [];\n for (const sheetName in grouped) {\n for (const entry of grouped[sheetName]) {\n let cell = entry.cell;\n if (cell.startsWith(\"'\") && cell.endsWith(\"'\")) {\n cell = cell.substring(1, cell.length - 1);\n }\n data.push({\n range: `${sheetName}!${cell}`,\n values: this.prepareValues(entry.data),\n });\n }\n }\n sheets.spreadsheets.values\n .batchUpdate({\n spreadsheetId: this.config.spreadsheetId,\n requestBody: {\n valueInputOption: 'USER_ENTERED',\n data,\n },\n })\n .then(() => {\n this.log.debug('Cells successfully written to google spreadsheet');\n })\n .catch(error => {\n this.log.error(`Error while writing cells to Google Spreadsheet:${error}`);\n });\n }\n\n /**\n * Read data from a cell in a Google Spreadsheet\n *\n * @param sheetName Name of the sheet\n * @param cell Cell to read from\n * @returns The data from the cell\n */\n public async readCell(sheetName: string, cell: string): Promise {\n const sheets = this.init();\n return new Promise((resolve, reject) => {\n if (cell.startsWith(\"'\") && cell.endsWith(\"'\")) {\n cell = cell.substring(1, cell.length - 1);\n }\n sheets.spreadsheets.values\n .get({\n range: `${sheetName}!${cell}`,\n spreadsheetId: this.config.spreadsheetId,\n })\n .then(response => {\n this.log.debug('Data successfully retrieved from google spreadsheet');\n if (response.data.values && response.data.values.length > 0) {\n resolve(response.data.values[0][0]);\n } else {\n reject(new Error('No data found'));\n }\n })\n .catch(error => {\n this.log.error(`Error while retrieving data from Google Spreadsheet:${error}`);\n reject(new Error(`Error while retrieving data from Google Spreadsheet: ${error.message}`));\n });\n });\n }\n private prepareValues(message: any): any {\n if (Array.isArray(message)) {\n return [message];\n }\n return [[message]];\n }\n private formatPrivateKey(privateKey: string): string | undefined {\n //replace all \\n with line breaks\n if (privateKey) {\n return privateKey.replace(/\\\\n/g, '\\n');\n }\n return undefined;\n }\n}\n"], + "mappings": "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", "names": ["sheet"] } diff --git a/build/main.js b/build/main.js index c7280ab..8bc8e68 100644 --- a/build/main.js +++ b/build/main.js @@ -115,6 +115,14 @@ class GoogleSpreadsheet extends utils.Adapter { } break; } + case "deleteSheets": { + this.log.debug("delete sheet"); + this.deleteSheets(obj); + if (obj.callback) { + this.sendTo(obj.from, obj.command, "Message received", obj.callback); + } + break; + } case "duplicateSheet": { this.log.debug("duplicate sheet"); this.duplicateSheet(obj); @@ -139,6 +147,14 @@ class GoogleSpreadsheet extends utils.Adapter { } break; } + case "writeCells": { + this.log.debug("write data to multiple cells"); + this.writeCells(obj); + if (obj.callback) { + this.sendTo(obj.from, obj.command, "Message received", obj.callback); + } + break; + } case "readCell": { this.log.debug("read single cell"); this.readCell(obj).then((result) => { @@ -181,6 +197,21 @@ class GoogleSpreadsheet extends utils.Adapter { } this.spreadsheet.writeCell(messageData.sheetName, messageData.cell, messageData.data); } + writeCells(message) { + const messageData = message.message; + if (this.missingParameters(["cells"], messageData)) { + return; + } + const cells = messageData.cells; + const cellPattern = new RegExp("[A-Z]+[0-9]+()"); + for (const cellObj of cells) { + if (!cellPattern.test(cellObj.cell)) { + this.log.error(`Invalid cell pattern ${cellObj.cell}. Expected: A1`); + return; + } + } + this.spreadsheet.writeCells(cells); + } async readCell(message) { return new Promise((resolve, reject) => { const messageData = message.message; @@ -208,6 +239,9 @@ class GoogleSpreadsheet extends utils.Adapter { deleteSheet(message) { this.spreadsheet.deleteSheet(message.message); } + deleteSheets(message) { + this.spreadsheet.deleteSheets(message.message); + } duplicateSheet(message) { const messageData = message.message; if (this.missingParameters(["source", "target", "index"], messageData)) { diff --git a/build/main.js.map b/build/main.js.map index b43289f..b747a11 100644 --- a/build/main.js.map +++ b/build/main.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../src/main.ts"], - "sourcesContent": ["/*\n * Created with @iobroker/create-adapter v2.4.0\n */\n\nimport * as utils from '@iobroker/adapter-core';\n\nimport fs from 'fs';\nimport { SpreadsheetUtils } from './lib/google';\n\nclass GoogleSpreadsheet extends utils.Adapter {\n private spreadsheet: SpreadsheetUtils;\n\n public constructor(options: Partial = {}) {\n super({\n ...options,\n name: 'google-spreadsheet',\n });\n this.on('ready', this.onReady.bind(this));\n this.on('message', this.onMessage.bind(this));\n this.on('unload', this.onUnload.bind(this));\n this.spreadsheet = new SpreadsheetUtils(this.config, this.log);\n }\n\n /**\n * Is called when databases are connected and adapter received configuration.\n */\n private async onReady(): Promise {\n // Initialize your adapter here\n\n // The adapters config (in the instance object everything under the attribute \"native\") is accessible via\n // this.config:\n this.log.debug(`config spreadsheetId: ${this.config.spreadsheetId}`);\n if (this.config.privateKey && this.config.serviceAccountEmail && this.config.spreadsheetId) {\n await this.setState('info.connection', true, true);\n this.log.info('Google-spreadsheet adapter configured');\n } else {\n await this.setState('info.connection', false, true);\n this.log.warn('Google-spreadsheet adapter not configured');\n }\n await this.encryptPrivateKeyIfNeeded();\n\n this.spreadsheet = new SpreadsheetUtils(this.config, this.log);\n }\n\n private async encryptPrivateKeyIfNeeded(): Promise {\n if (this.config.privateKey && this.config.privateKey.length > 0) {\n await this.getForeignObjectAsync(`system.adapter.${this.name}.${this.instance}`).then(data => {\n if (data && data.native && data.native.privateKey && !data.native.privateKey.startsWith('$/aes')) {\n this.config.privateKey = data.native.privateKey;\n data.native.privateKey = this.encrypt(data.native.privateKey);\n this.extendForeignObject(`system.adapter.${this.name}.${this.instance}`, data);\n this.log.info('privateKey is stored now encrypted');\n }\n });\n }\n }\n\n /**\n * Is called when adapter shuts down - callback has to be called under any circumstances!\n *\n * @param callback Callback so the adapter can shut down\n */\n private onUnload(callback: () => void): void {\n try {\n callback();\n } catch (e) {\n this.log.error(`Error during unload: ${JSON.stringify(e)}`);\n callback();\n }\n }\n\n // If you need to accept messages in your adapter, uncomment the following block and the corresponding line in the constructor.\n // /**\n // * Some message was sent to this instance over message box. Used by email, pushover, text2speech, ...\n // * Using this method requires \"common.messagebox\" property to be set to true in io-package.json\n // */\n private onMessage(obj: ioBroker.Message): void {\n if (typeof obj === 'object' && obj.message) {\n switch (obj.command) {\n case 'append': {\n this.log.debug('append to spreadsheet');\n this.append(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'deleteRows': {\n this.log.debug('delete rows from spreadsheet');\n this.deleteRows(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'createSheet': {\n this.log.debug('create sheet');\n this.createSheet(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'deleteSheet': {\n this.log.debug('delete sheet');\n this.deleteSheet(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'duplicateSheet': {\n this.log.debug('duplicate sheet');\n this.duplicateSheet(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'upload': {\n this.log.debug('upload file');\n this.upload(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'writeCell': {\n this.log.debug('write data to single cell');\n this.writeCell(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'readCell': {\n this.log.debug('read single cell');\n this.readCell(obj)\n .then((result: any) => {\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, result, obj.callback);\n }\n })\n .catch(error => this.log.error(`Cannot read cell: ${error}`));\n\n break;\n }\n default: {\n this.log.warn(`unknown command: ${obj.command}`);\n break;\n }\n }\n }\n }\n\n private upload(message: Record): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['target', 'parentFolder'], messageData)) {\n return;\n }\n this.spreadsheet.upload(messageData.target, messageData.parentFolder, fs.createReadStream(messageData.source));\n }\n private append(message: Record): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['sheetName', 'data'], messageData)) {\n return;\n }\n this.spreadsheet.append(messageData.sheetName, messageData.data);\n }\n private writeCell(message: Record): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['sheetName', 'cell', 'data'], messageData)) {\n return;\n }\n //Check that cell is a valid pattern\n const cellPattern = new RegExp('[A-Z]+[0-9]+()');\n if (!cellPattern.test(messageData.cell)) {\n this.log.error(`Invalid cell pattern ${messageData.cell}. Expected: A1`);\n return;\n }\n this.spreadsheet.writeCell(messageData.sheetName, messageData.cell, messageData.data);\n }\n private async readCell(message: Record): Promise {\n return new Promise((resolve, reject) => {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['sheetName', 'cell'], messageData)) {\n return;\n }\n //Check that cell is a valid pattern\n const cellPattern = new RegExp('[A-Z]+[0-9]+()');\n if (!cellPattern.test(messageData.cell)) {\n this.log.error(`Invalid cell pattern ${messageData.cell}. Expected: A1`);\n return;\n }\n this.spreadsheet\n .readCell(messageData.sheetName, messageData.cell)\n .then(result => resolve(result))\n .catch(error => reject(new Error(error)));\n });\n }\n public deleteRows(message: ioBroker.Message): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['sheetName', 'start', 'end'], messageData)) {\n return;\n }\n this.spreadsheet.deleteRows(messageData.sheetName, messageData.start, messageData.end);\n }\n public createSheet(message: ioBroker.Message): void {\n this.spreadsheet.createSheet(message.message as string);\n }\n public deleteSheet(message: ioBroker.Message): void {\n this.spreadsheet.deleteSheet(message.message as string);\n }\n public duplicateSheet(message: ioBroker.Message): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['source', 'target', 'index'], messageData)) {\n return;\n }\n this.spreadsheet.duplicateSheet(messageData.source, messageData.target, messageData.index);\n }\n\n private missingParameters(neededParameters: string[], messageData: Record): boolean {\n let result = false;\n for (const parameter of neededParameters) {\n if (Object.keys(messageData).indexOf(parameter) == -1) {\n result = true;\n this.log.error(`The parameter '${parameter}' is required but was not passed!`);\n }\n }\n\n return result;\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial | undefined) => new GoogleSpreadsheet(options);\n} else {\n // otherwise start the instance directly\n (() => new GoogleSpreadsheet())();\n}\n"], - "mappings": "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", + "sourcesContent": ["/*\n * Created with @iobroker/create-adapter v2.4.0\n */\n\nimport * as utils from '@iobroker/adapter-core';\n\nimport fs from 'fs';\nimport { SpreadsheetUtils } from './lib/google';\n\nclass GoogleSpreadsheet extends utils.Adapter {\n private spreadsheet: SpreadsheetUtils;\n\n public constructor(options: Partial = {}) {\n super({\n ...options,\n name: 'google-spreadsheet',\n });\n this.on('ready', this.onReady.bind(this));\n this.on('message', this.onMessage.bind(this));\n this.on('unload', this.onUnload.bind(this));\n this.spreadsheet = new SpreadsheetUtils(this.config, this.log);\n }\n\n /**\n * Is called when databases are connected and adapter received configuration.\n */\n private async onReady(): Promise {\n // Initialize your adapter here\n\n // The adapters config (in the instance object everything under the attribute \"native\") is accessible via\n // this.config:\n this.log.debug(`config spreadsheetId: ${this.config.spreadsheetId}`);\n if (this.config.privateKey && this.config.serviceAccountEmail && this.config.spreadsheetId) {\n await this.setState('info.connection', true, true);\n this.log.info('Google-spreadsheet adapter configured');\n } else {\n await this.setState('info.connection', false, true);\n this.log.warn('Google-spreadsheet adapter not configured');\n }\n await this.encryptPrivateKeyIfNeeded();\n\n this.spreadsheet = new SpreadsheetUtils(this.config, this.log);\n }\n\n private async encryptPrivateKeyIfNeeded(): Promise {\n if (this.config.privateKey && this.config.privateKey.length > 0) {\n await this.getForeignObjectAsync(`system.adapter.${this.name}.${this.instance}`).then(data => {\n if (data && data.native && data.native.privateKey && !data.native.privateKey.startsWith('$/aes')) {\n this.config.privateKey = data.native.privateKey;\n data.native.privateKey = this.encrypt(data.native.privateKey);\n this.extendForeignObject(`system.adapter.${this.name}.${this.instance}`, data);\n this.log.info('privateKey is stored now encrypted');\n }\n });\n }\n }\n\n /**\n * Is called when adapter shuts down - callback has to be called under any circumstances!\n *\n * @param callback Callback so the adapter can shut down\n */\n private onUnload(callback: () => void): void {\n try {\n callback();\n } catch (e) {\n this.log.error(`Error during unload: ${JSON.stringify(e)}`);\n callback();\n }\n }\n\n // If you need to accept messages in your adapter, uncomment the following block and the corresponding line in the constructor.\n // /**\n // * Some message was sent to this instance over message box. Used by email, pushover, text2speech, ...\n // * Using this method requires \"common.messagebox\" property to be set to true in io-package.json\n // */\n private onMessage(obj: ioBroker.Message): void {\n if (typeof obj === 'object' && obj.message) {\n switch (obj.command) {\n case 'append': {\n this.log.debug('append to spreadsheet');\n this.append(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'deleteRows': {\n this.log.debug('delete rows from spreadsheet');\n this.deleteRows(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'createSheet': {\n this.log.debug('create sheet');\n this.createSheet(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'deleteSheet': {\n this.log.debug('delete sheet');\n this.deleteSheet(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'deleteSheets': {\n this.log.debug('delete sheet');\n this.deleteSheets(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'duplicateSheet': {\n this.log.debug('duplicate sheet');\n this.duplicateSheet(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'upload': {\n this.log.debug('upload file');\n this.upload(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'writeCell': {\n this.log.debug('write data to single cell');\n this.writeCell(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'writeCells': {\n this.log.debug('write data to multiple cells');\n this.writeCells(obj);\n\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, 'Message received', obj.callback);\n }\n break;\n }\n case 'readCell': {\n this.log.debug('read single cell');\n this.readCell(obj)\n .then((result: any) => {\n if (obj.callback) {\n this.sendTo(obj.from, obj.command, result, obj.callback);\n }\n })\n .catch(error => this.log.error(`Cannot read cell: ${error}`));\n\n break;\n }\n default: {\n this.log.warn(`unknown command: ${obj.command}`);\n break;\n }\n }\n }\n }\n\n private upload(message: Record): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['target', 'parentFolder'], messageData)) {\n return;\n }\n this.spreadsheet.upload(messageData.target, messageData.parentFolder, fs.createReadStream(messageData.source));\n }\n private append(message: Record): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['sheetName', 'data'], messageData)) {\n return;\n }\n this.spreadsheet.append(messageData.sheetName, messageData.data);\n }\n private writeCell(message: Record): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['sheetName', 'cell', 'data'], messageData)) {\n return;\n }\n //Check that cell is a valid pattern\n const cellPattern = new RegExp('[A-Z]+[0-9]+()');\n if (!cellPattern.test(messageData.cell)) {\n this.log.error(`Invalid cell pattern ${messageData.cell}. Expected: A1`);\n return;\n }\n this.spreadsheet.writeCell(messageData.sheetName, messageData.cell, messageData.data);\n }\n private writeCells(message: Record): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['cells'], messageData)) {\n return;\n }\n const cells: Array<{ sheetName: string; cell: string; data: any }> = messageData.cells as Array<{\n sheetName: string;\n cell: string;\n data: any;\n }>;\n //Check that cell is a valid pattern\n const cellPattern = new RegExp('[A-Z]+[0-9]+()');\n for (const cellObj of cells) {\n if (!cellPattern.test(cellObj.cell)) {\n this.log.error(`Invalid cell pattern ${cellObj.cell}. Expected: A1`);\n return;\n }\n }\n this.spreadsheet.writeCells(cells);\n }\n private async readCell(message: Record): Promise {\n return new Promise((resolve, reject) => {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['sheetName', 'cell'], messageData)) {\n return;\n }\n //Check that cell is a valid pattern\n const cellPattern = new RegExp('[A-Z]+[0-9]+()');\n if (!cellPattern.test(messageData.cell)) {\n this.log.error(`Invalid cell pattern ${messageData.cell}. Expected: A1`);\n return;\n }\n this.spreadsheet\n .readCell(messageData.sheetName, messageData.cell)\n .then(result => resolve(result))\n .catch(error => reject(new Error(error)));\n });\n }\n public deleteRows(message: ioBroker.Message): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['sheetName', 'start', 'end'], messageData)) {\n return;\n }\n this.spreadsheet.deleteRows(messageData.sheetName, messageData.start, messageData.end);\n }\n public createSheet(message: ioBroker.Message): void {\n this.spreadsheet.createSheet(message.message as string);\n }\n public deleteSheet(message: ioBroker.Message): void {\n this.spreadsheet.deleteSheet(message.message as string);\n }\n public deleteSheets(message: ioBroker.Message): void {\n this.spreadsheet.deleteSheets(message.message as string[]);\n }\n public duplicateSheet(message: ioBroker.Message): void {\n const messageData: Record = message.message as Record;\n if (this.missingParameters(['source', 'target', 'index'], messageData)) {\n return;\n }\n this.spreadsheet.duplicateSheet(messageData.source, messageData.target, messageData.index);\n }\n\n private missingParameters(neededParameters: string[], messageData: Record): boolean {\n let result = false;\n for (const parameter of neededParameters) {\n if (Object.keys(messageData).indexOf(parameter) == -1) {\n result = true;\n this.log.error(`The parameter '${parameter}' is required but was not passed!`);\n }\n }\n\n return result;\n }\n}\n\nif (require.main !== module) {\n // Export the constructor in compact mode\n module.exports = (options: Partial | undefined) => new GoogleSpreadsheet(options);\n} else {\n // otherwise start the instance directly\n (() => new GoogleSpreadsheet())();\n}\n"], + "mappings": "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", "names": ["fs"] } diff --git a/features/delete-sheets.md b/features/delete-sheets.md new file mode 100644 index 0000000..aa3a335 --- /dev/null +++ b/features/delete-sheets.md @@ -0,0 +1,31 @@ +# Delete multiple sheets + +With the `deleteSheets` block you can delete several sheets from your Google Spreadsheet at once. + +## Blockly + +![Blockly](../img/blockly-delete-sheets.png) + +Use the block **delete sheets in [instance] the sheets with names (array)** and provide an array of sheet names, e.g. `["Sheet1", "Sheet2"]`. + +## JavaScript + +You can also call the function directly: + +```javascript +sendTo("google-spreadsheet.0", "deleteSheets", ["Sheet1", "Sheet2"]); +``` + +## Parameters + +- **instance**: The instance of your google-spreadsheet adapter. +- **sheet names (array)**: An array of strings with the names of the sheets to delete. + +## Example + +```javascript +sendTo("google-spreadsheet.0", "deleteSheets", ["Log", "Backup"]); +``` + +This will delete the sheets named "Log" and "Backup" from your spreadsheet. + diff --git a/features/write b/features/write deleted file mode 100644 index e69de29..0000000 diff --git a/features/write-cells.md b/features/write-cells.md new file mode 100644 index 0000000..915edd0 --- /dev/null +++ b/features/write-cells.md @@ -0,0 +1,52 @@ +# Write multiple cells + +With the `writeCells` block you can write values to multiple cells in your Google Spreadsheet at once. + +## Blockly + +![Blockly](../img/blockly-write-cells.png) + +Use the block **write cells to [instance] cells** and add one or more cell blocks. +Each cell block lets you specify: +- **Sheet**: The name of the sheet +- **Cell**: The cell address (e.g. `A1`) +- **Data**: The value to write + +Example Blockly setup: +- write cells to `google-spreadsheet.0` + - Sheet: `Log`, Cell: `A1`, Data: `Hello` + - Sheet: `Log`, Cell: `B1`, Data: `World` + +## JavaScript + +You can also call the function directly: + +```javascript +sendTo("google-spreadsheet.0", "writeCells", { + cells: [ + { sheetName: "Log", cell: "A1", data: "Hello" }, + { sheetName: "Log", cell: "B1", data: "World" } + ] +}); +``` + +## Parameters + +- **instance**: The instance of your google-spreadsheet adapter. +- **cells**: Array of objects with the following properties: + - **sheetName**: Name of the sheet + - **cell**: Cell address (e.g. `A1`) + - **data**: Value to write + +## Example + +```javascript +sendTo("google-spreadsheet.0", "writeCells", { + cells: [ + { sheetName: "Log", cell: "A1", data: "Temperature" }, + { sheetName: "Log", cell: "B1", data: 22.5 } + ] +}); +``` + +This will write "Temperature" to cell A1 and 22.5 to cell B1 in the sheet "Log". diff --git a/img/blockly-delete-sheets.png b/img/blockly-delete-sheets.png new file mode 100644 index 0000000..4ed12a9 Binary files /dev/null and b/img/blockly-delete-sheets.png differ diff --git a/img/blockly-write-cells.png b/img/blockly-write-cells.png new file mode 100644 index 0000000..5513842 Binary files /dev/null and b/img/blockly-write-cells.png differ diff --git a/src/lib/google.ts b/src/lib/google.ts index d46d643..b7c82d0 100644 --- a/src/lib/google.ts +++ b/src/lib/google.ts @@ -244,6 +244,53 @@ export class SpreadsheetUtils { }); } + /** + * Delete multiple sheets from the Google Spreadsheet + * + * @param titles The titles of the sheets to delete + */ + public deleteSheets(titles: string[]): void { + const sheets = this.init(); + + sheets.spreadsheets + .get({ spreadsheetId: this.config.spreadsheetId }) + .then(spreadsheet => { + if (spreadsheet && spreadsheet.data.sheets) { + const requests: sheets_v4.Schema$Request[] = []; + for (const title of titles) { + const sheet = spreadsheet.data.sheets.find( + sheet => sheet.properties && sheet.properties.title == title, + ); + if (sheet && sheet.properties) { + requests.push({ + deleteSheet: { + sheetId: sheet.properties.sheetId, + }, + }); + } + } + if (requests.length > 0) { + sheets.spreadsheets + .batchUpdate({ + spreadsheetId: this.config.spreadsheetId, + requestBody: { + requests: requests, + }, + }) + .then(() => { + this.log.debug('Sheets successfully deleted from google spreadsheet'); + }) + .catch(error => { + this.log.error(`Error while deleting sheets from Google Spreadsheet:${error}`); + }); + } + } + }) + .catch(error => { + this.log.error(`Error while deleting sheets from Google Spreadsheet:${error}`); + }); + } + /** * Send data to a Google Spreadsheet * @@ -280,29 +327,50 @@ export class SpreadsheetUtils { * @param data Data to write */ public writeCell(sheetName: string, cell: string, data: any): void { + this.writeCells([{ sheetName, cell, data }]); + } + + /** + * Write multiple cells in a Google Spreadsheet + * + * @param cells Array of objects: { sheetName, cell, data } + */ + public writeCells(cells: Array<{ sheetName: string; cell: string; data: any }>): void { const sheets = this.init(); - if (cell.startsWith("'") && cell.endsWith("'")) { - cell = cell.substring(1, cell.length - 1); + // Gruppiere nach sheetName, da batchUpdate mehrere Bereiche pro Sheet erlaubt + const grouped: { [sheet: string]: Array<{ cell: string; data: any }> } = {}; + for (const cellObj of cells) { + if (!grouped[cellObj.sheetName]) { + grouped[cellObj.sheetName] = []; + } + grouped[cellObj.sheetName].push({ cell: cellObj.cell, data: cellObj.data }); + } + const data: Array<{ range: string; values: any[][] }> = []; + for (const sheetName in grouped) { + for (const entry of grouped[sheetName]) { + let cell = entry.cell; + if (cell.startsWith("'") && cell.endsWith("'")) { + cell = cell.substring(1, cell.length - 1); + } + data.push({ + range: `${sheetName}!${cell}`, + values: this.prepareValues(entry.data), + }); + } } - sheets.spreadsheets.values .batchUpdate({ spreadsheetId: this.config.spreadsheetId, requestBody: { valueInputOption: 'USER_ENTERED', - data: [ - { - range: `${sheetName}!${cell}`, - values: this.prepareValues(data), - }, - ], + data, }, }) .then(() => { - this.log.debug('Data successfully sent to google spreadsheet'); + this.log.debug('Cells successfully written to google spreadsheet'); }) .catch(error => { - this.log.error(`Error while sending data to Google Spreadsheet:${error}`); + this.log.error(`Error while writing cells to Google Spreadsheet:${error}`); }); } diff --git a/src/main.ts b/src/main.ts index fbcbc81..bc12273 100644 --- a/src/main.ts +++ b/src/main.ts @@ -113,6 +113,15 @@ class GoogleSpreadsheet extends utils.Adapter { } break; } + case 'deleteSheets': { + this.log.debug('delete sheet'); + this.deleteSheets(obj); + + if (obj.callback) { + this.sendTo(obj.from, obj.command, 'Message received', obj.callback); + } + break; + } case 'duplicateSheet': { this.log.debug('duplicate sheet'); this.duplicateSheet(obj); @@ -140,6 +149,15 @@ class GoogleSpreadsheet extends utils.Adapter { } break; } + case 'writeCells': { + this.log.debug('write data to multiple cells'); + this.writeCells(obj); + + if (obj.callback) { + this.sendTo(obj.from, obj.command, 'Message received', obj.callback); + } + break; + } case 'readCell': { this.log.debug('read single cell'); this.readCell(obj) @@ -187,6 +205,26 @@ class GoogleSpreadsheet extends utils.Adapter { } this.spreadsheet.writeCell(messageData.sheetName, messageData.cell, messageData.data); } + private writeCells(message: Record): void { + const messageData: Record = message.message as Record; + if (this.missingParameters(['cells'], messageData)) { + return; + } + const cells: Array<{ sheetName: string; cell: string; data: any }> = messageData.cells as Array<{ + sheetName: string; + cell: string; + data: any; + }>; + //Check that cell is a valid pattern + const cellPattern = new RegExp('[A-Z]+[0-9]+()'); + for (const cellObj of cells) { + if (!cellPattern.test(cellObj.cell)) { + this.log.error(`Invalid cell pattern ${cellObj.cell}. Expected: A1`); + return; + } + } + this.spreadsheet.writeCells(cells); + } private async readCell(message: Record): Promise { return new Promise((resolve, reject) => { const messageData: Record = message.message as Record; @@ -218,6 +256,9 @@ class GoogleSpreadsheet extends utils.Adapter { public deleteSheet(message: ioBroker.Message): void { this.spreadsheet.deleteSheet(message.message as string); } + public deleteSheets(message: ioBroker.Message): void { + this.spreadsheet.deleteSheets(message.message as string[]); + } public duplicateSheet(message: ioBroker.Message): void { const messageData: Record = message.message as Record; if (this.missingParameters(['source', 'target', 'index'], messageData)) {