Last active
February 2, 2024 13:45
-
-
Save Garfonso/16a7978e50b5d18b98bf408c2ff82060 to your computer and use it in GitHub Desktop.
ioBroker: Sync Lovelace and Alexa Shopping Lists
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const shoppingListId = 'lovelace.0.control.shopping_list'; | |
const alexa2BaseId = 'alexa2.0.Lists.SHOPPING_LIST'; //important. Needed for delete & update also. | |
const alexaAddToList = alexa2BaseId + '.#New'; | |
const alexaListId = alexa2BaseId + '.json'; | |
//switch off to silence: | |
const printDebug = true; | |
function debug(msg) { | |
if (printDebug) { | |
log(msg) | |
} | |
} | |
const TodoItemStatus = { | |
NeedsAction: 'needs_action', | |
Completed: 'completed', | |
}; | |
/** | |
* @typedef lovelaceItem | |
* @type {object} | |
* @property {string} summary | |
* @property {string} status | |
* @property {string} uid | |
* @property {boolean} [found] - keep track if found or not. | |
*/ | |
/** | |
* @typedef alexaItem | |
* @type {object} | |
* @property {string} value | |
* @property {string} id | |
* @property {boolean} completed | |
* @property {number} updatedDateTime | |
* @proerpty {boolean} [found] - keep track if found or not. | |
*/ | |
/** | |
* Compare alexaItem complete and lovelaceItem status -> returns true if same status. | |
* @param {Array<alexaItem>} alexaList | |
* @param {Array<lovelaceItem>} list | |
* @returns {boolean} true if same status. | |
*/ | |
function compareCompleted(alexaItem, lovelaceItem) { | |
if (alexaItem.completed && lovelaceItem.status !== TodoItemStatus.Completed) { | |
return false; | |
} | |
if (!alexaItem.completed && lovelaceItem.status !== TodoItemStatus.NeedsAction) { | |
return false; | |
} | |
return true; | |
} | |
/** | |
* sync lists | |
* @param {Array<alexaItem>} alexaList | |
* @param {Array<lovelaceItem>} list | |
* @param {number} timestampLovelace | |
* @returns {Array<lovelaceItem>} new lovelace List | |
*/ | |
function syncLists(alexaList, list, timestampLovelace) { | |
const newLovelaceList = []; | |
for (const alexaItem of alexaList) { | |
for (const lovelaceItem of list) { | |
if (lovelaceItem.uid === alexaItem.id) { | |
alexaItem.found = true; | |
lovelaceItem.found = true; | |
//found item. Update completed state from 'newer' list: | |
if (alexaItem.updatedDateTime > timestampLovelace) { | |
if (alexaItem.value !== lovelaceItem.summary || !compareCompleted(alexaItem, lovelaceItem)) { | |
debug('Updating ' + lovelaceItem.name + ' from alexa'); | |
} | |
//update lovelace: | |
newLovelaceList.push({ | |
summary: alexaItem.value, | |
status: alexaItem.completed ? TodoItemStatus.Completed : TodoItemStatus.NeedsAction, | |
uid: alexaItem.id | |
}); | |
} else { | |
//keep lovelace: | |
newLovelaceList.push({ | |
summary: lovelaceItem.summary, | |
status: lovelaceItem.status, | |
uid: alexaItem.id | |
}); | |
//update alexa: | |
if (!compareCompleted(alexaItem, lovelaceItem)) { | |
debug('Update ' + alexaItem.value + ' to ' + (lovelaceItem.status === TodoItemStatus.Completed ? 'done' : 'undone') + ' from lovelace.'); | |
setState(`${alexa2BaseId}.items.${alexaItem.id}.completed`, lovelaceItem.status === TodoItemStatus.Completed); | |
} | |
if (alexaItem.value !== lovelaceItem.summary) { | |
debug('Update ' + alexaItem.value + ' to ' + lovelaceItem.summary + ' from lovelace'); | |
setState(`${alexa2BaseId}.items.${alexaItem.id}.value`, lovelaceItem.summary); | |
} | |
} | |
} | |
} | |
if (!alexaItem.found) { | |
//alexa item not found: | |
if (alexaItem.completed) { | |
//if completed and not in lovelace -> remove. | |
debug('Delete ' + alexaItem.value + ' because done and not in lovelace list.'); | |
setState(`${alexa2BaseId}.items.${alexaItem.id}.#delete`, true); | |
} else { | |
debug('Add ' + alexaItem.value + ' to lovelace list'); | |
newLovelaceList.push({ | |
summary: alexaItem.value, | |
status: alexaItem.completed ? TodoItemStatus.Completed : TodoItemStatus.NeedsAction, | |
uid: alexaItem.id | |
}); | |
} | |
} | |
} | |
for (const lovelaceItem of list) { | |
if (!lovelaceItem.found) { | |
if (lovelaceItem.status === TodoItemStatus.Completed) { | |
debug('Delete ' + lovelaceItem.summary + ' because done and not on alexa list'); | |
} else { | |
//this will add a new entry to alexa list. | |
//of course ids won't match. So we will not keep the item in lovelace at this place | |
//but it will be re-added once it traveled through the alexa ecosystem. | |
debug('Adding ' + lovelaceItem.summary + ' to alexa list.'); | |
setState(alexaAddToList, lovelaceItem.summary); | |
} | |
} | |
} | |
return newLovelaceList; | |
} | |
function doSync() { | |
const alexaList = JSON.parse(getState(alexaListId).val); | |
const state = getState(shoppingListId); | |
const lovelaceList = JSON.parse(state.val); | |
const newList = syncLists(alexaList, lovelaceList, state.ts); | |
setState(shoppingListId, JSON.stringify(newList)); | |
} | |
on({id: shoppingListId, ack: true}, e => { | |
debug('Got shopping list update: ' + e.state.val); | |
doSync(); | |
}); | |
on({id: alexaListId, ack: true}, e => { | |
debug('Got alexa shopping list update.'); | |
doSync(); | |
}); | |
doSync(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Updated to new ioBroker.lovelace version (4.x)