Merge pull request #5 from bitfocus/companion-v3-compatibility

Companion v3 compatibility
This commit is contained in:
Peter
2023-06-29 13:12:12 +01:00
committed by GitHub
10 changed files with 604 additions and 419 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
node_modules/
package-lock.json
.nova
archive
.DS_Store

51
actions.js Normal file
View File

@@ -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)
}

View File

@@ -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
@@ -49,3 +49,8 @@ Add 'Always start' configuration option
### Version 1.0.3
Add variables for timerId and timerDescription
### Version 2.0.0
Updated for Companion version 3
Updated for toggl API version 9

View File

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

31
companion/manifest.json Normal file
View File

@@ -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"
]
}

790
index.js
View File

@@ -1,443 +1,455 @@
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)
return self
}
instance.prototype.updateConfig = function (config) {
var self = this
self.config = config
self.auth()
self.getWorkspace()
self.actions()
}
instance.prototype.init = function () {
var self = this
debug = self.debug
log = self.log
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)
}
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,
},
]
}
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,
},
]
}
async destroy() {
console.log('destroy', this.id)
}
instance.prototype.destroy = function () {
var self = this
debug('destroy', self.id)
}
async init(config) {
console.log('--- init toggltrack ---')
this.prefixUrl = 'https://api.track.toggl.com/api/v9/'
instance.prototype.update_variables = function (system) {
var self = this
var variables = []
this.config = config
variables.push(
{
label: 'Current Timer Id',
name: 'timerId',
},
{
label: 'Current Timer Description',
name: 'timerDescription',
this.gotOptions = {
responseType: 'json',
throwHttpErrors: false
}
)
self.setVariableDefinitions(variables)
self.setVariable('timerId', null)
self.setVariable('timerDescription', null)
}
this.gotOptions.prefixUrl = this.prefixUrl
instance.prototype.init_presets = function () {
var self = this
var presets = []
this.workspace = null
this.workspaceName = null
this.projects = [{ id: '0', label: 'None' }]
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',
},
},
],
})
this.updateVariables()
this.updatePresets()
presets.push({
category: 'Timer',
label: 'Stop',
bank: {
style: 'text',
text: 'Stop Timer',
size: '18',
color: 16777215,
bgcolor: 0,
},
actions: [
{
action: 'stopCurrentTimer',
},
],
})
this.setVariableValues({
timerId: null,
timerDuration: null,
timerDescription: null,
lastTimerDuration: null,
workspace: null,
})
self.setPresetDefinitions(presets)
}
this.gotOptions.headers = this.auth()
instance.prototype.actions = function (system) {
var self = this
if (this.gotOptions.headers != null) {
this.getWorkspace().then(
this.getCurrentTimer()
)
}
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',
},
})
}
this.updateActions()
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"}}'
async configUpdated(config) {
console.log('config updated')
this.config = config
this.gotOptions.headers = this.auth()
if (this.gotOptions.headers != null) {
this.getWorkspace().then(
this.getCurrentTimer()
)
}
this.updateActions()
this.updateVariables()
}
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
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 == null) {
this.log('warn', 'Not authorized')
return
}
let cmd = 'me/time_entries/current'
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')
async getWorkspace() {
let cmd = 'workspaces'
console.log('function: 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')
if (this.gotOptions.headers == null) {
this.log('warn', 'Not authorized')
return
}
)
}
instance.prototype.getProjects = function () {
var self = this
// reset
this.workspace = null
this.setVariableValues({
workspace: null
})
if (self.workspace !== null) {
var cmd = 'https://api.track.toggl.com/api/v8/workspaces/' + self.workspace + '/projects'
self.sendCommand('rest_get', cmd).then(
// 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!')
}
)
})
}
})
}
instance.prototype.sendCommand = function (mode, command, body = '') {
var self = this
console.log(mode + ' : ' + command)
async stopTimer() {
console.log('function: stopTimer')
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
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')
}
})
}
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)

View File

@@ -1,29 +1,21 @@
{
"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.4",
"got": "~13.0.0"
},
"devDependencies": {
"@companion-module/tools": "~1.1"
}
}

56
presets.js Normal file
View File

@@ -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)
}

7
upgrades.js Normal file
View File

@@ -0,0 +1,7 @@
export function upgradeScripts() {
return {
updatedConfig: null,
updatedActions: [],
updatedFeedbacks: [],
}
}

29
variables.js Normal file
View File

@@ -0,0 +1,29 @@
export function updateVariables() {
let variables = []
variables.push(
{
name: 'Workspace',
variableId: 'workspace',
},
{
name: 'Current Timer Id',
variableId: 'timerId',
},
{
name: 'Current Timer Duration',
variableId: 'timerDuration',
},
{
name: 'Last Timer Duration',
variableId: 'lastTimerDuration',
},
{
name: 'Current Timer Description',
variableId: 'timerDescription',
}
)
this.setVariableDefinitions(variables)
}