From ba5f120840a4822d81d50800ab6286fcf8bcc1fb Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Tue, 27 Jun 2023 14:17:02 +0100 Subject: [PATCH 01/17] package and manifest update --- companion/manifest.json | 31 +++++++++++++++++++++++++++++++ package.json | 29 ++++++++++------------------- 2 files changed, 41 insertions(+), 19 deletions(-) create mode 100644 companion/manifest.json diff --git a/companion/manifest.json b/companion/manifest.json new file mode 100644 index 0000000..1a6be98 --- /dev/null +++ b/companion/manifest.json @@ -0,0 +1,31 @@ +{ + "id": "toggl-track", + "name": "toggl-track", + "shortname": "toggl", + "description": "Companion module for toggltrack timers", + "version": "2.0.0", + "license": "MIT", + "repository": "git+https://github.com/bitfocus/companion-module-toggl-track.git", + "bugs": "https://github.com/bitfocus/companion-module-toggl-track/issues", + "maintainers": [ + { + "name": "Peter Daniel" + } + ], + "legacyIds": [ + "toggl-track" + ], + "runtime": { + "type": "node18", + "api": "nodejs-ipc", + "apiVersion": "0.0.0", + "entrypoint": "../index.js" + }, + "manufacturer": "toggl", + "products": [ + "track" + ], + "keywords": [ + "Logging", "Timer", "Task Tracking", "Time Tracking", "Project Management" + ] +} diff --git a/package.json b/package.json index d0d4837..9f58021 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,20 @@ { "name": "toggl-track", - "version": "1.0.3", - "api_version": "1.0.0", - "keywords": [ - "Timer", - "Logging" - ], - "manufacturer": "Toggl", - "product": [ - "Track" - ], - "shortname": "toggl", - "description": "Companion module for TogglTrack", + "version": "2.0.0", "main": "index.js", + "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "author": "Peter Daniel", - "license": "MIT", - "homepage": "https://github.com/bitfocus/companion-module-toggl-track#readme", - "bugs": { - "url": "https://github.com/bitfocus/companion-module-toggl-track/issues" - }, "repository": { "type": "git", "url": "git+https://github.com/bitfocus/companion-module-toggl-track.git" + }, + "license": "MIT", + "dependencies": { + "@companion-module/base": "~1.2" + }, + "devDependencies": { + "@companion-module/tools": "~1.1" } -} +} \ No newline at end of file From 792d2787af33b52a4406ee7a69742f6c7e7c0bbb Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Tue, 27 Jun 2023 14:17:59 +0100 Subject: [PATCH 02/17] v2 help --- companion/HELP.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 companion/HELP.md diff --git a/companion/HELP.md b/companion/HELP.md new file mode 100644 index 0000000..ad88884 --- /dev/null +++ b/companion/HELP.md @@ -0,0 +1,55 @@ +# Companion Module Toggl Track + +This module allows you to start and stop [Toggl Track](https://track.toggl.com/) timers. + +## Configuration + +Before using this module you must create a Toggl Track account. Then go to your Toggl profile settings page and copy the API token. Look for this section on your toggl profile. + +![api token](api_token.png) + +Paste this token into the config page of this module and click save. If all is well the module status will turn green. + +## Actions + +**Start New Timer** + +Start a new timer running with the description set in the action and store the ID. If a list of projects has been retrieved on startup you can choose a project. By default a new timer can't be started if one is already running. This behaviour can be changed by ticking the 'Always Start' option in the module configuration. + +**Get Current Timer** + +Companion only knows the ID of timers it has started, if a timer is started from another application or the toggle website then this action will get the ID so Companion knows about it. + +**Stop Current Timer** + +Attempt to stop the current timer. This will fail if Companion doesn't know the ID of the currently running timer. + +**Refresh Project List** + +Retrives the current list of projects from the toggl server. This action runs automatically when Companion starts and when the module is enabled. + +## Presets + +Presets are available for **Start Timer** and **Stop Timer**. + +## History + +### Version 1.0.0 +First release + +### Version 1.0.1 +Fix broken link + +### Version 1.0.2 +Allow a project to be specified when starting a new timer button + +Add an action to refresh the project list + +Add 'Always start' configuration option + +### Version 1.0.3 +Add variables for timerId and timerDescription + +### Version 2.0 +Updated for Companion version 3 + From f3c382e68bdb282ef72dc75efb1f46c4a43109a0 Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Tue, 27 Jun 2023 14:18:37 +0100 Subject: [PATCH 03/17] remove --- HELP.md | 51 --------------------------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 HELP.md diff --git a/HELP.md b/HELP.md deleted file mode 100644 index bb9a383..0000000 --- a/HELP.md +++ /dev/null @@ -1,51 +0,0 @@ -# Companion Module Toggl Track - -This module allows you to start and stop [Toggl Track](https://track.toggl.com/) timers. - -## Configuration - -Before using this module you must create a Toggl Track account. Then go to your Toggl profile settings page and copy the API token. Look for this section on your toggl profile. - -![api token](api_token.png) - -Paste this token into the config page of this module and click save. If all is well the module status will turn green. - -## Actions - -**Start New Timer** - -Start a new timer running with the description set in the action and store the ID. If a list of projects has been retrieved on startup you can choose a project. By default a new timer can't be started if one is already running. This behaviour can be changed by ticking the 'Always Start' option in the module configuration. - -**Get Current Timer** - -Companion only knows the ID of timers it has started, if a timer is started from another application or the toggle website then this action will get the ID so Companion knows about it. - -**Stop Current Timer** - -Attempt to stop the current timer. This will fail if Companion doesn't know the ID of the currently running timer. - -**Refresh Project List** - -Retrives the current list of projects from the toggl server. This action runs automatically when Companion starts and when the module is enabled. - -## Presets - -Presets are available for **Start Timer** and **Stop Timer**. - -## History - -### Version 1.0.0 -First release - -### Version 1.0.1 -Fix broken link - -### Version 1.0.2 -Allow a project to be specified when starting a new timer button - -Add an action to refresh the project list - -Add 'Always start' configuration option - -### Version 1.0.3 -Add variables for timerId and timerDescription From 816434929d99dac4bb0740f1bac25efd2add5aef Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Tue, 27 Jun 2023 14:20:27 +0100 Subject: [PATCH 04/17] fix spelling, move image --- companion/HELP.md | 2 +- api_token.png => companion/api_token.png | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename api_token.png => companion/api_token.png (100%) diff --git a/companion/HELP.md b/companion/HELP.md index ad88884..6c2ab4f 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -26,7 +26,7 @@ Attempt to stop the current timer. This will fail if Companion doesn't know the **Refresh Project List** -Retrives the current list of projects from the toggl server. This action runs automatically when Companion starts and when the module is enabled. +Retrieves the current list of projects from the toggl server. This action runs automatically when Companion starts and when the module is enabled. ## Presets diff --git a/api_token.png b/companion/api_token.png similarity index 100% rename from api_token.png rename to companion/api_token.png From 3620c9750c08d8ae261d3ebc4aef8cfaa6e8b27f Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Tue, 27 Jun 2023 21:58:02 +0100 Subject: [PATCH 05/17] help --- companion/HELP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/companion/HELP.md b/companion/HELP.md index 6c2ab4f..f357d74 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -50,6 +50,6 @@ Add 'Always start' configuration option ### Version 1.0.3 Add variables for timerId and timerDescription -### Version 2.0 +### Version 2.0.0 Updated for Companion version 3 From 0145891bfaad0b512e79a21d4dc6b4442e4412a4 Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Tue, 27 Jun 2023 21:58:35 +0100 Subject: [PATCH 06/17] ignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a9ad571..8cf7a42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ node_modules/ package-lock.json -.nova \ No newline at end of file +.nova +archive +.DS_Store From e676a0986b570b19d0912778f85a7f2ef4425cf5 Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Wed, 28 Jun 2023 12:08:46 +0100 Subject: [PATCH 07/17] add new variables file --- variables.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 variables.js diff --git a/variables.js b/variables.js new file mode 100644 index 0000000..00d9c41 --- /dev/null +++ b/variables.js @@ -0,0 +1,21 @@ +export function updateVariables() { + let variables = [] + + variables.push( + { + name: 'Current Timer Id', + variableId: 'timerId', + }, + { + name: 'Current Timer Duration', + variableId: 'timerDuration', + }, + { + name: 'Current Timer Description', + variableId: 'timerDescription', + } + ) + + this.setVariableDefinitions(variables) + +} \ No newline at end of file From 46f9d8bfc831040207bb71e3d11b84d55153d52c Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Wed, 28 Jun 2023 12:08:58 +0100 Subject: [PATCH 08/17] add new upgrade file --- upgrades.js | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 upgrades.js diff --git a/upgrades.js b/upgrades.js new file mode 100644 index 0000000..64e4f66 --- /dev/null +++ b/upgrades.js @@ -0,0 +1,7 @@ +export function upgradeScripts() { + return { + updatedConfig: null, + updatedActions: [], + updatedFeedbacks: [], + } +} \ No newline at end of file From 982e3f5ee6320e9cd9be3b0d75f07b9ad8ea8439 Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Wed, 28 Jun 2023 12:09:44 +0100 Subject: [PATCH 09/17] add new presets file --- presets.js | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 presets.js diff --git a/presets.js b/presets.js new file mode 100644 index 0000000..7c16030 --- /dev/null +++ b/presets.js @@ -0,0 +1,56 @@ +import { combineRgb } from '@companion-module/base' + +export function updatePresets() { + let presets = {} + + presets['Start'] = { + type: 'button', + category: 'Timer', + name: 'Start', + style: { + text: 'Start Timer', + size: '18', + color: combineRgb(255, 255, 255), + bgcolor: combineRgb(0, 0, 0), + }, + steps: [{ + down: [{ + actionId: 'startNewTimer', + options: { + description: '', + project: '0', + }, + }, + ], + up: [], + }, + ], + feedbacks: [], + } + + presets['Stop'] = { + type: 'button', + category: 'Timer', + name: 'Stop', + style: { + text: 'Stop Timer', + size: '18', + color: combineRgb(255, 255, 255), + bgcolor: combineRgb(0, 0, 0), + }, + steps: [{ + down: [ + { + actionId: 'stopCurrentTimer', + options: { + }, + }, + ], + up: [], + }, + ], + feedbacks: [], + } + + this.setPresetDefinitions(presets) +} \ No newline at end of file From 910bbade9b2a2f4eefaa631a659f86591ab98455 Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Wed, 28 Jun 2023 18:43:36 +0100 Subject: [PATCH 10/17] new variables --- variables.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/variables.js b/variables.js index 00d9c41..6de927d 100644 --- a/variables.js +++ b/variables.js @@ -2,6 +2,10 @@ export function updateVariables() { let variables = [] variables.push( + { + name: 'Workspace', + variableId: 'workspace', + }, { name: 'Current Timer Id', variableId: 'timerId', @@ -10,6 +14,10 @@ export function updateVariables() { name: 'Current Timer Duration', variableId: 'timerDuration', }, + { + name: 'Last Timer Duration', + variableId: 'lastTimerDuration', + }, { name: 'Current Timer Description', variableId: 'timerDescription', From 8092a78b29fa00b4336138c6ab2008c9460cb0d5 Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Wed, 28 Jun 2023 18:43:49 +0100 Subject: [PATCH 11/17] add got --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9f58021..e2e9afb 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ }, "license": "MIT", "dependencies": { - "@companion-module/base": "~1.2" + "@companion-module/base": "~1.2", + "got": "~13.0.0" }, "devDependencies": { "@companion-module/tools": "~1.1" From 3acebd984bbdbd7796a8d1d13dcbd22f5501a94b Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Wed, 28 Jun 2023 18:44:06 +0100 Subject: [PATCH 12/17] add api v9 change --- companion/HELP.md | 1 + 1 file changed, 1 insertion(+) diff --git a/companion/HELP.md b/companion/HELP.md index f357d74..09c22f8 100644 --- a/companion/HELP.md +++ b/companion/HELP.md @@ -52,4 +52,5 @@ Add variables for timerId and timerDescription ### Version 2.0.0 Updated for Companion version 3 +Updated for toggl API version 9 From 665d3dba2f63a769911e941fe5870795e0b80bca Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Wed, 28 Jun 2023 18:44:19 +0100 Subject: [PATCH 13/17] update to match old names --- companion/manifest.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/companion/manifest.json b/companion/manifest.json index 1a6be98..f91d46b 100644 --- a/companion/manifest.json +++ b/companion/manifest.json @@ -21,9 +21,9 @@ "apiVersion": "0.0.0", "entrypoint": "../index.js" }, - "manufacturer": "toggl", + "manufacturer": "Toggl", "products": [ - "track" + "Track" ], "keywords": [ "Logging", "Timer", "Task Tracking", "Time Tracking", "Project Management" From 0e33e46b70455202b6abec8da1724f6e392921ee Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Thu, 29 Jun 2023 12:17:21 +0100 Subject: [PATCH 14/17] update module/base --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e2e9afb..c4776ab 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "license": "MIT", "dependencies": { - "@companion-module/base": "~1.2", + "@companion-module/base": "~1.4", "got": "~13.0.0" }, "devDependencies": { From 3f50c7e71afc0b9ec7ec13b9eeba2aef8a6869ef Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Thu, 29 Jun 2023 12:17:31 +0100 Subject: [PATCH 15/17] beta v2 --- actions.js | 51 ++++ index.js | 800 +++++++++++++++++++++++++++-------------------------- 2 files changed, 454 insertions(+), 397 deletions(-) create mode 100644 actions.js diff --git a/actions.js b/actions.js new file mode 100644 index 0000000..7dc7e93 --- /dev/null +++ b/actions.js @@ -0,0 +1,51 @@ +export function updateActions() { + let actions = {} + + actions['startNewTimer'] = { + name: 'Start New Timer', + options: [ + { + type: 'textinput', + label: 'Description', + id: 'description', + default: '', + }, + { + type: 'dropdown', + label: 'Project', + id: 'project', + default: '0', + choices: this.projects, + }, + ], + callback: ({ options }) => { + this.startTimer(options.project, options.description) + }, + } + + actions['getCurrentTimer'] = { + name: 'Get Current Timer', + options: [], + callback: (action) => { + this.getCurrentTimer() + }, + } + + actions['stopCurrentTimer'] = { + name: 'Stop Current Timer', + options: [], + callback: (action) => { + this.stopTimer() + }, + } + + actions['refreshProjects'] = { + name: 'Refresh Project List', + options: [], + callback: (action) => { + this.getWorkspace() + }, + } + + this.setActionDefinitions(actions) +} diff --git a/index.js b/index.js index 671458c..7938868 100644 --- a/index.js +++ b/index.js @@ -1,443 +1,449 @@ -var instance_skel = require('../../instance_skel') +// toggltrack module +// Peter Daniel -var debug -var log +import { InstanceBase, Regex, runEntrypoint, InstanceStatus } from '@companion-module/base' +import { updateActions } from './actions.js' +import { updatePresets } from './presets.js' +import { updateVariables } from './variables.js' +import { upgradeScripts } from './upgrades.js' +import got from 'got' -function instance(system, id, config) { - var self = this +class toggltrack extends InstanceBase { + constructor(internal) { + super(internal) - // super-constructor - instance_skel.apply(this, arguments) + this.updateActions = updateActions.bind(this) + this.updatePresets = updatePresets.bind(this) + this.updateVariables = updateVariables.bind(this) + + } + + getConfigFields() { + console.log('config fields') + return [ + { + type: 'textinput', + id: 'apiToken', + label: 'Personal API Token from your Toggl user profile (required)', + width: 12, + default: '', + }, + { + type: 'checkbox', + id: 'alwaysStart', + label: 'Always start a new timer even if there is one already running', + width: 12, + default: false, + }, + ] + } + + async destroy() { + console.log('destroy', this.id) + } + + async init(config) { + console.log('--- init toggltrack ---') + this.prefixUrl = 'https://api.track.toggl.com/api/v9/' + + this.config = config + + this.gotOptions = { + responseType: 'json', + throwHttpErrors: false + } + + this.gotOptions.prefixUrl = this.prefixUrl - return self -} + this.workspace = null + this.workspaceName = null + this.projects = [{ id: '0', label: 'None' }] -instance.prototype.updateConfig = function (config) { - var self = this - self.config = config + this.updateVariables() + this.updatePresets() - self.auth() - self.getWorkspace() - self.actions() -} + this.gotOptions.headers = this.auth() + this.getWorkspace() + + this.getCurrentTimer().then((timerId) => { + console.log('Init current timer id: ' + timerId) + }) -instance.prototype.init = function () { - var self = this + this.setVariableValues({ + timerId: null, + timerDuration: null, + timerDescription: null, + lastTimerDuration: null, + workspace: null, + }) - debug = self.debug - log = self.log + this.updateActions() - self.workspace = null - self.workspaceName = null - self.projects = [{ id: '0', label: 'None' }] - - self.init_presets() - self.update_variables() - self.auth() - self.getWorkspace() - self.getCurrentTimer().then((timerId) => { - self.setVariable('timerId', timerId.id) - self.setVariable('timerDescription', timerId.description) - }) - self.actions() -} - -instance.prototype.auth = function () { - var self = this - self.header = [] - - if (self.config.apiToken !== null && self.config.apiToken.length > 0) { - auth = Buffer.from(self.config.apiToken + ':' + 'api_token').toString('base64') - self.header['Content-Type'] = 'application/json' - self.header['Authorization'] = 'Basic ' + auth - } else { - self.log('warn', 'Please enter your toggl API token') } - // console.log(self.header) -} + async configUpdated(config) { + console.log('config updated') + this.config = config -instance.prototype.config_fields = function () { - var self = this - return [ - { - type: 'text', - id: 'info', - width: 12, - label: 'Information', - value: 'This module is for the toggl track service', - }, - { - type: 'textinput', - id: 'apiToken', - label: 'Personal API Token from your Toggl user profile (required)', - width: 12, - default: '', - }, - { - type: 'checkbox', - id: 'alwaysStart', - label: 'Enable', - width: 1, - default: false, - }, - { - type: 'text', - id: 'alwaysStartTxt', - label: 'Always start a new timer even if there is one already running', - width: 11, - }, - { - type: 'text', - id: 'break', - label: '', - width: 12, - }, - ] -} + this.auth() + this.getWorkspace() + this.updateActions() + this.updateVariables() + } -instance.prototype.destroy = function () { - var self = this - debug('destroy', self.id) -} - -instance.prototype.update_variables = function (system) { - var self = this - var variables = [] - - variables.push( - { - label: 'Current Timer Id', - name: 'timerId', - }, - { - label: 'Current Timer Description', - name: 'timerDescription', + auth() { + if (this.config.apiToken !== null && this.config.apiToken.length > 0) { + let auth = Buffer.from(this.config.apiToken + ':' + 'api_token').toString('base64') + let headers = {} + headers['Content-Type'] = 'application/json' + headers['authorization'] = 'Basic ' + auth + console.log(headers) + return headers + } else { + this.log('warn', 'Please enter your toggl API token') + return null } - ) + // console.log(this.gotOptions) + } + + async getCurrentTimer() { + console.log('function: getCurrentTimer') + + if (this.gotOptions.headers.authorization == undefined) { + this.log('warn', 'Not authorized') + return + } + let cmd = 'me/time_entries/current' - self.setVariableDefinitions(variables) - self.setVariable('timerId', null) - self.setVariable('timerDescription', null) -} - -instance.prototype.init_presets = function () { - var self = this - var presets = [] - - presets.push({ - category: 'Timer', - label: 'Start', - bank: { - style: 'text', - text: 'Start Timer', - size: '18', - color: 16777215, - bgcolor: 0, - }, - actions: [ - { - action: 'startNewTimer', - options: { - description: '', - project: '0', - }, - }, - ], - }) - - presets.push({ - category: 'Timer', - label: 'Stop', - bank: { - style: 'text', - text: 'Stop Timer', - size: '18', - color: 16777215, - bgcolor: 0, - }, - actions: [ - { - action: 'stopCurrentTimer', - }, - ], - }) - - self.setPresetDefinitions(presets) -} - -instance.prototype.actions = function (system) { - var self = this - - self.setActions({ - startNewTimer: { - label: 'Start New Timer', - options: [ - { - type: 'textinput', - label: 'Description', - id: 'description', - default: '', - }, - { - type: 'dropdown', - label: 'Project', - id: 'project', - default: '0', - choices: self.projects, - }, - ], - }, - getCurrentTimer: { - label: 'Get Current Timer', - }, - stopCurrentTimer: { - label: 'Stop Current Timer', - }, - refreshProjects: { - label: 'Refresh Project List', - }, - }) -} - -instance.prototype.action = function (action) { - var self = this - const opt = action.options - - switch (action.action) { - case 'startNewTimer': { - self.getCurrentTimer().then((timerId) => { - if (timerId === undefined || timerId === null || self.config.alwaysStart === true) { - // no timer currently running or we want to restart it - var cmd = 'https://api.track.toggl.com/api/v8/time_entries/start' - if (opt.project == '0') { - var body = '{"time_entry":{"description":"' + opt.description + '","created_with":"companion"}}' + return new Promise((resolve, reject) => { + this.sendGetCommand(cmd).then( + (result) => { + if (typeof result === 'object' && result !== null) { + if ('id' in result) { + this.setVariableValues({ + timerId: result.id, + timerDescription: result.description, + timerDuration: result.duration, + }) + this.log('info', 'Current timer id: ' + result.id) + resolve(result.id) + } else { + this.log('info', 'No current timer (no id in data)') + this.setVariableValues({ + timerId: null, + timerDescription: null, + timerDuration: null + }) + resolve(null) + } } else { - var body = - '{"time_entry":{"description":"' + - opt.description + - '","created_with":"companion","pid":"' + - opt.project + - '"}}' + this.log('info', 'No current timer (no object)') + this.setVariableValues({ + timerId: null, + timerDescription: null, + timerDuration: null + }) + resolve(null) } - self.sendCommand('rest', cmd, body).then((result) => { - if (typeof result === 'object' && result.data !== null && result.data !== undefined) { - self.log('debug', 'New timer started ' + result.data.id) - self.setVariable('timerId', result.data.id) - self.setVariable('timerDescription', result.data.description) - } else { - self.log('warn', 'Error starting timer') - } - }) - } else { - self.log('debug', 'A timer is already running ' + timerId.id) } - }) - break - } - case 'stopCurrentTimer': { - self.getCurrentTimer().then((timerId) => { - self.log('debug', 'Current timer id ' + timerId.id) - if (timerId.id !== null && timerId.id !== undefined) { - var cmd = 'https://api.track.toggl.com/api/v8/time_entries/' + timerId.id + '/stop' - self.sendCommand('rest_put', cmd).then((result) => { - if (typeof result === 'object' && result.data !== null && result.data !== undefined) { - self.log('debug', 'Stopped ' + result.data.id + ', duration ' + result.data.duration) - self.setVariable('timerId', null) - self.setVariable('timerDescription', null) - } else { - self.log('warn', 'Error stopping timer') - } - }) - } else { - self.log('warn', 'No running timer to stop or running timer id unknown') - } - }) - break - } - case 'getCurrentTimer': { - self.getCurrentTimer().then((timerId) => { - self.log('debug', 'Current timer id ' + timerId.id + ' ' + timerId.description) - self.setVariable('timerId', timerId.id) - self.setVariable('timerDescription', timerId.description) - }) - break - } - case 'refreshProjects': { - self.getWorkspace() - break - } - default: - break + ) + }) } -} -instance.prototype.getWorkspace = function () { - var self = this - var cmd = 'https://api.track.toggl.com/api/v8/workspaces' - // console.log('getWorkspace') - - // reset - self.workspace = null - - // get workspace ID - self.sendCommand('rest_get', cmd).then( - (result) => { - // console.log('result ' + JSON.stringify(result, null, 4)) - if (typeof result === 'object' && result !== null) { - console.log('Found ' + result.length + ' workspace') - // only interested in first workspace - if ('id' in result[0]) { - self.workspace = result[0].id - self.workspaceName = result[0].name - console.log('Workspace ' + self.workspace + ' ' + self.workspaceName) - self.log('debug', 'Workspace ' + self.workspace + ':' + self.workspaceName) - self.getProjects() - } - } else { - console.log('result ' + JSON.stringify(result, null, 4)) - self.log('debug', 'No workspace') - } - }, - (error) => { - console.log('error ' + error) - self.log('debug', 'Error getting workspace') + getWorkspace() { + let cmd = 'workspaces' + console.log('function: getWorkspace') + + if (this.gotOptions.headers.authorization == undefined) { + this.log('warn', 'Not authorized') + return } - ) -} - -instance.prototype.getProjects = function () { - var self = this - - if (self.workspace !== null) { - var cmd = 'https://api.track.toggl.com/api/v8/workspaces/' + self.workspace + '/projects' - self.sendCommand('rest_get', cmd).then( + + // reset + this.workspace = null + this.setVariableValues({ + workspace: null + }) + + // get workspace ID + this.sendGetCommand(cmd).then( (result) => { // console.log('result ' + JSON.stringify(result, null, 4)) if (typeof result === 'object' && result !== null) { - // reset - self.projects = [] - - for (p = 0; p < result.length; p++) { - if ('id' in result[p]) { - self.projects.push({ - id: result[p].id.toString(), - label: result[p].name, - }) - self.log('debug', 'Project ' + result[p].id + ':' + result[p].name) - } + console.log('Found ' + result.length + ' workspace') + // only interested in first workspace + if ('id' in result[0]) { + this.workspace = result[0].id + this.workspaceName = result[0].name + this.log('info', 'Workspace: ' + this.workspace + ' - ' + this.workspaceName) + this.setVariableValues({ + workspace: this.workspaceName + }) + this.getProjects() } - - self.projects.sort((a, b) => { - fa = a.label.toLowerCase() - fb = b.label.toLowerCase() - - if (fa < fb) { - return -1 - } - if (fa > fb) { - return 1 - } - return 0 - }) - - self.projects.unshift({ id: '0', label: 'None' }) - console.log('Projects:') - console.log(self.projects) - self.actions() } else { - console.log(result) - self.log('debug', 'No projects') + console.log('result ' + JSON.stringify(result, null, 4)) + this.log('debug', 'No workspace') } - }, - (error) => { - console.log('error ' + error) - self.log('debug', 'Error getting projects') } ) } -} -instance.prototype.getCurrentTimer = function () { - var self = this - var cmd = 'https://api.track.toggl.com/api/v8/time_entries/current' + getProjects() { + console.log('function: getProjects') - return new Promise((resolve, reject) => { - self.sendCommand('rest_get', cmd).then( - (result) => { - if (typeof result === 'object' && result.data !== null && result.data !== undefined) { - if ('id' in result.data) { - resolve(result.data) + if (this.workspace !== null) { + let cmd = 'workspaces/' + this.workspace + '/projects' + this.sendGetCommand(cmd).then( + (result) => { + // console.log('result ' + JSON.stringify(result, null, 4)) + if (typeof result === 'object' && result !== null) { + // reset + this.projects = [] + + for (let p = 0; p < result.length; p++) { + if ('id' in result[p]) { + if (result[p].active === true) { + // don't add archived projects + this.projects.push({ + id: result[p].id.toString(), + label: result[p].name, + }) + } + // this.log('debug', 'Project ' + result[p].id + ':' + result[p].name) + } + } + + this.projects.sort((a, b) => { + let fa = a.label.toLowerCase() + let fb = b.label.toLowerCase() + + if (fa < fb) { + return -1 + } + if (fa > fb) { + return 1 + } + return 0 + }) + + this.projects.unshift({ id: '0', label: 'None' }) + console.log('Projects:') + console.log(this.projects) + this.updateActions() } else { - self.log('debug', 'Error getting current timer (no id in data)') - self.setVariable('timerId', null) - self.setVariable('timerDescription', null) - resolve(null) + console.log(result) + this.log('debug', 'No projects') } + }) + } + } + + // getTimerDuration(id) { + // let cmd = 'time_entries/' + id + // + // return new Promise((resolve, reject) => { + // self.sendCommand('rest_get', cmd).then( + // (result) => { + // if (typeof result === 'object' && result.data !== null && result.data !== undefined) { + // if ('duration' in result.data) { + // self.setVariable('timerDuration', result.data.duration) + // resolve(result.data.duration) + // } else { + // self.log('debug', 'Error getting current timer duration (no id in data)') + // self.setVariable('timerDuration', null) + // resolve(null) + // } + // } else { + // self.log('debug', 'Error getting current timer duration (no object)') + // self.setVariable('timerDuration', null) + // resolve(null) + // } + // }, + // (error) => { + // console.log('error ' + error) + // self.log('debug', 'Error getting current timer duration') + // } + // ) + // }) + // } + + async startTimer(project, description) { + let body + let cmd + let timerId + const startTime = new Date() + this.getCurrentTimer().then((timerId) => { + console.log('timerId: ' + timerId) + if (timerId === null || this.config.alwaysStart === true) { + // no timer currently running or we want to restart it + cmd = 'workspaces/' + this.workspace + '/time_entries' + if (project == '0') { + body = '{"wid":' + this.workspace + ',"description":"' + description + + '","created_with":"companion",' + '"start":"' + startTime.toISOString() + '","duration":-1}' } else { - self.log('debug', 'Error getting current timer (no object)') - self.setVariable('timerId', null) - self.setVariable('timerDescription', null) - resolve(null) + body = + '{"wid":' + this.workspace + ',"description":"' + description + + '","created_with":"companion","project_id":' + project + + ',"start":"' + startTime.toISOString() + '","duration":-1}' } - }, - (error) => { - console.log('error ' + error) - self.log('debug', 'Error getting current timer') + console.log(body) + this.sendPostCommand(cmd, body).then((result) => { + if (typeof result === 'object' && result !== null) { + this.log('info', 'New timer started ' + result.id + " " + result.description) + this.setVariableValues({ + timerId: result.id, + timerDescription: result.description, + timerDuration: result.duration, + }) + } else { + this.log('warn', 'Error starting timer') + } + }) + } else { + this.log('info', 'A timer is already running ' + timerId + ' not starting a new one!') } - ) - }) -} + }) + } + + async stopTimer() { + console.log('function: stopTimer') -instance.prototype.sendCommand = function (mode, command, body = '') { - var self = this - console.log(mode + ' : ' + command) + this.getCurrentTimer().then((timerId) => { + this.log('info', 'Trying to stop current timer id: ' + timerId) + // console.log(typeof timerId) + if (typeof timerId === 'number' && timerId > 0) { + let cmd = 'workspaces/' + this.workspace + '/time_entries/' + timerId + '/stop' + this.sendPatchCommand(cmd).then((result) => { + if (typeof result === 'object' && result !== null && result !== undefined) { + this.log('info', 'Stopped ' + result.id + ', duration ' + result.duration) + this.setVariableValues({ + timerId: null, + timerDescription: null, + timerDuration: null, + lastTimerDuration: result.duration, + }) + } else { + this.log('warn', 'Error stopping timer') + } + }) + } else { + this.log('warn', 'No running timer to stop or running timer id unknown') + } + }) + } - switch (mode) { - case 'rest_get': { - return new Promise((resolve, reject) => { - self.system.emit( - mode, - command, - (err, { data, error, response }) => { - if (err) { - self.status(self.STATUS_ERROR) - console.log(error) - reject(error) - return - } - self.status(self.STATUS_OK) - resolve(data) - }, - self.header + async sendGetCommand(GetURL) { + console.log('get: ' + GetURL) + let response + + try { + response = await got.get(GetURL, this.gotOptions) + if (response.statusCode == 200) { + this.updateStatus(InstanceStatus.Ok) + return response.body + } else { + this.updateStatus( + InstanceStatus.UnknownError, + `Unexpected HTTP status code: ${response.statusCode} - ${response.body.error}` ) - }) - break + this.log('warn', `Unexpected HTTP status code: ${response.statusCode} - ${response.body.error}`) + return null + } + } catch (error) { + console.log(error.message) + this.processError(error) + return null } - case 'rest': - case 'rest_put': { - return new Promise((resolve, reject) => { - self.system.emit( - mode, - command, - body, - (err, { data, error, response }) => { - if (err) { - self.status(self.STATUS_ERROR) - console.log(error) - reject(error) - return - } - self.status(self.STATUS_OK) - resolve(data) - }, - self.header + } + + async sendPutCommand(PutURL) { + console.log('put: ' + PutURL) + let response + + try { + response = await got.put(PutURL, this.gotOptions) + console.log('status: ' + response.statusCode) + if (response.statusCode == 200) { + console.log(response.body) + return response.body + } else { + this.updateStatus( + InstanceStatus.UnknownError, + `Unexpected HTTP status code: ${response.statusCode} - ${response.body.error}` ) - }) - break + this.log('warn', `Unexpected HTTP status code: ${response.statusCode} - ${response.body.error}`) + return null + } + } catch (error) { + console.log(error.message) + this.processError(error) + return null } } + + async sendPatchCommand(PatchURL) { + console.log('patch: ' + PatchURL) + let response + + try { + response = await got.patch(PatchURL, this.gotOptions) + // console.log('status: ' + response.statusCode) + if (response.statusCode == 200) { + // console.log(response.body) + return response.body + } else { + this.updateStatus( + InstanceStatus.UnknownError, + `Unexpected HTTP status code: ${response.statusCode} - ${response.body.error}` + ) + this.log('warn', `Unexpected HTTP status code: ${response.statusCode} - ${response.body.error}`) + return null + } + } catch (error) { + console.log(error.message) + this.processError(error) + return null + } + } + + async sendPostCommand(cmd, body) { + + console.log(body) + let response + let postdata = {} + postdata.prefixUrl = this.prefixUrl + postdata.responseType = 'json', + postdata.throwHttpErrors = false + postdata.headers = this.auth() + postdata.json = JSON.parse(body) + + // console.log(postdata) + + try { + response = await got.post(cmd, postdata) + // console.log(response.request.requestUrl) + // console.log(response.statusCode) + if (response.statusCode == 200) { + return response.body + } else { + this.updateStatus( + InstanceStatus.UnknownError, + `Unexpected HTTP status code: ${response.statusCode} - ${response.body.error}` + ) + this.log('warn', `Unexpected HTTP status code: ${response.statusCode} - ${response.body.error}`) + return null + } + } catch (error) { + console.log(error.message) + this.processError(error) + return null + } + } + + processError(error) { + console.log('gotError: ' + error.code) + } } -instance_skel.extendedBy(instance) -exports = module.exports = instance +runEntrypoint(toggltrack, upgradeScripts) From b9e7695675e3524429089cbfa9acca83754da465 Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Thu, 29 Jun 2023 12:58:49 +0100 Subject: [PATCH 16/17] fix startup new instance --- index.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index 7938868..710a157 100644 --- a/index.js +++ b/index.js @@ -19,7 +19,7 @@ class toggltrack extends InstanceBase { } getConfigFields() { - console.log('config fields') + // console.log('config fields') return [ { type: 'textinput', @@ -61,14 +61,7 @@ class toggltrack extends InstanceBase { this.updateVariables() this.updatePresets() - - this.gotOptions.headers = this.auth() - this.getWorkspace() - this.getCurrentTimer().then((timerId) => { - console.log('Init current timer id: ' + timerId) - }) - this.setVariableValues({ timerId: null, timerDuration: null, @@ -77,6 +70,14 @@ class toggltrack extends InstanceBase { workspace: null, }) + this.gotOptions.headers = this.auth() + + if (this.gotOptions.headers != null) { + this.getWorkspace().then( + this.getCurrentTimer() + ) + } + this.updateActions() } @@ -85,7 +86,7 @@ class toggltrack extends InstanceBase { console.log('config updated') this.config = config - this.auth() + this.gotOptions.headers = this.auth() this.getWorkspace() this.updateActions() this.updateVariables() @@ -97,7 +98,6 @@ class toggltrack extends InstanceBase { let headers = {} headers['Content-Type'] = 'application/json' headers['authorization'] = 'Basic ' + auth - console.log(headers) return headers } else { this.log('warn', 'Please enter your toggl API token') @@ -109,7 +109,7 @@ class toggltrack extends InstanceBase { async getCurrentTimer() { console.log('function: getCurrentTimer') - if (this.gotOptions.headers.authorization == undefined) { + if (this.gotOptions.headers == null) { this.log('warn', 'Not authorized') return } @@ -150,11 +150,11 @@ class toggltrack extends InstanceBase { }) } - getWorkspace() { + async getWorkspace() { let cmd = 'workspaces' console.log('function: getWorkspace') - if (this.gotOptions.headers.authorization == undefined) { + if (this.gotOptions.headers == null) { this.log('warn', 'Not authorized') return } @@ -287,7 +287,7 @@ class toggltrack extends InstanceBase { '","created_with":"companion","project_id":' + project + ',"start":"' + startTime.toISOString() + '","duration":-1}' } - console.log(body) + // console.log(body) this.sendPostCommand(cmd, body).then((result) => { if (typeof result === 'object' && result !== null) { this.log('info', 'New timer started ' + result.id + " " + result.description) From 535b64e0e38c8ba6b83311a307df743eb6cc61f7 Mon Sep 17 00:00:00 2001 From: Peter Daniel Date: Thu, 29 Jun 2023 13:03:39 +0100 Subject: [PATCH 17/17] add getcurrenttimer on config update --- index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 710a157..94bc061 100644 --- a/index.js +++ b/index.js @@ -87,7 +87,13 @@ class toggltrack extends InstanceBase { this.config = config this.gotOptions.headers = this.auth() - this.getWorkspace() + + if (this.gotOptions.headers != null) { + this.getWorkspace().then( + this.getCurrentTimer() + ) + } + this.updateActions() this.updateVariables() }