Skip to content

Commit

Permalink
feat(summary): Refactor slightly
Browse files Browse the repository at this point in the history
  • Loading branch information
thomashermine committed Nov 12, 2023
1 parent 73e7ff3 commit bcf04da
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 14 deletions.
2 changes: 1 addition & 1 deletion src/_helpers/ha.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function addDomainToEntities(states) {
export function summarizeEntities(states) {
return states.map(el => {
const attributes = Object.keys(el.attributes).map(key => { return `${key}: ${el.attributes[key]}`});
return `${el.entity_id}: ${el.state} (${attributes.join(', ')})`;
return `${el.attributes.friendly_name} — (id: ${el.entity_id}): ${el.state} (${attributes.join(', ')})`;
});
}

5 changes: 3 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const {
const DEFAULT_HOME_ASSISTANT_HOST = (NODE_ENV === 'production') ? 'http://supervisor/core' : 'http://homeassistant.local';
const DEFAULT_HOME_ASSISTANT_PORT = (NODE_ENV === 'production') ? 80 : 8123;
const DEFAULT_HOME_ASSISTANT_TOKEN = process.env.SUPERVISOR_TOKEN;
const DEFAULT_UPDATE_INTERVAL = (NODE_ENV === 'production') ? 1000*60*60 : 5000; // Every hour in production, every 5 seconds in dev
const DEFAULT_UPDATE_INTERVAL = (NODE_ENV === 'production') ? 1000*60*60 : 10000; // Every hour in production, every 5 seconds in dev

// =====================================================================================================================
// Config
Expand All @@ -28,4 +28,5 @@ export const {
} = process.env;

export const SUMMARY_DEVICE_ENTITY_PREFIX = 'summary_device_'; // will be followed by the device name
export const SUMMARY_DEVICES_ENTITIES = ['hall_tablet','iphone_thomas','iphone_caroline','macbookpro_thomas','roborock','octoprint','litterbox','toothbrush'];
export const SUMMARY_DEVICES_ENTITIES = ['hall_tablet','iphone_thomas','iphone_caroline','macbookpro_thomas','roborock','octoprint','litterbox','toothbrush','pet_feeder','pet_fountain'];
//export const SUMMARY_DEVICES_ENTITIES = ['roborock'];
57 changes: 55 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import {sleep} from './_helpers/sleep';

import {
SUMMARY_DEVICES_ENTITIES,
SUMMARY_DEVICE_ENTITY_PREFIX,
UPDATE_INTERVAL,
} from './config';
import {init} from './init';
import {updateDeviceSummary} from './summary'
import {updateDeviceSummary} from './summary.device'

async function main() {

Expand All @@ -28,16 +29,68 @@ async function main() {
// Device Summaries
// ===================================================================================================================
log('app','info',`Updating summary for ${SUMMARY_DEVICES_ENTITIES.length} devices...`);
await Promise.allSettled(SUMMARY_DEVICES_ENTITIES.map(device => updateDeviceSummary(openai, ha, states, device)));
await Promise.all(SUMMARY_DEVICES_ENTITIES.map(device => updateDeviceSummary(openai, ha, states, device)));
log('app','info',`Summary updated for ${SUMMARY_DEVICES_ENTITIES.length} devices.`);

// Device Summaries, Summary
// ===================================================================================================================
const deviceSummaries = states.filter(state => state.entity_id.startsWith('sensor.'+SUMMARY_DEVICE_ENTITY_PREFIX));
const deviceSummariesStates = deviceSummaries.map(state => state.attributes.content);
console.log(deviceSummariesStates);



// Sleep till next run
// ===================================================================================================================
// TODO: Implement publish/subscribe pattern to avoid polling
log('app','info',`Sleeping for ${UPDATE_INTERVAL/1000} seconds...`);
await sleep(UPDATE_INTERVAL);

}
//console.log(summary);

//prompt.push(briefing);
//prompt.push(

// const blocks = [
// // All on lights
// {
// iterators: ["light"],
// filters : [{key: 'domain', value: 'iterator'}],
// returnProperty : 'attributes.friendly_name',
// },
// // All on motions, door, windows
// {
// iterators: ["motion","door","window"],
// filters : [{key: 'attributes.device_class', value: 'iterator'}],
// returnProperty : 'attributes.friendly_name',
// },
// // All vacuum and locks, no matter the state
// {
// iterators: ["vacuum","lock","weather"],
// filters : [{key: 'domain', value: 'iterator'}],
// returnProperty : 'attributes.friendly_name',
// },
// // All temperature & humidity sensors
// {
// iterators: ["temperature",'humidity'],
// filters : [{key: 'domain', value: 'sensor'},{key: 'attributes.device_class', value: 'temperature'}],
// returnProperty : 'attributes.friendly_name',
// },
// ];

// for (const block of blocks) {
// for (const iterator of block.iterators) {
// const cleanedFilter = block.filters.map(filter => {
// return {key: filter.key.replace('iterator', iterator), value: filter.value.replace('iterator', iterator)}
// });
// const entities = getEntities(states, cleanedFilter, block.returnProperty, true);
// prompt.push(`${iterator} ${entities.join(',')}`);
// }
// }
//console.log(prompt.join('\n\n'));


}

main();
27 changes: 18 additions & 9 deletions src/summary.ts → src/summary.device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ export function getPromptForDeviceSummary(states, deviceName, paramOptions) {
if(!states || states.length === 0) {
throw new Error('States must be an array');
}
const deviceEntities = states.filter(el => el.entity_id.includes(deviceName));
const deviceEntities = states
.filter(el => el.entity_id.includes(deviceName))
.filter(el => !el.entity_id.includes(SUMMARY_DEVICE_ENTITY_PREFIX)); // Ignoring ourselves
if(deviceEntities.length === 0) {
log('app','warn',`No entity found for device ${deviceName}`);
return null;
}
const summary = summarizeEntities(deviceEntities);
const defaultOptions = {
length: 200,
length: 100,
customPrompt: '',
}
const options = {
Expand All @@ -34,14 +36,19 @@ export function getPromptForDeviceSummary(states, deviceName, paramOptions) {

let prompt = [];
const briefing = `
Following is an overview of the state of a device.
Summarize it in less than ${options.length} characters.
Do not start nor include the name of the device. Do not start by "summary" or "briefing" or "device".
Only keep the most important information, do not give everything you have. ALWAYS keep it short.
Prioritize errors, last state changes, and then only the rest.
Write a single sentence, as if your were do not use bullets point, separator, line breaks.
Ignore device containing "summary" or "briefing" or "device" in their name.
BRIEFING :
Following is an overview of the state of a device.
You must filter out the noise and respond with a single sentence, no bullets points summary of maximum ${options.length} characters.
Keep the summary short no matter the amount of data you are given.
Only keep errors, warnings, statuses, entities called "Dock Status" and other important information.
Dont write "error" or "warning" if you dont see any.
Do not introduce with "Summary" or "Résumé", just the content itself.
Round float value to integer. Transform any duration in seconds to minutes.
Ignore any "unknown" or "unavailable" state.
${options.customPrompt}
CONTEXT :
For relative date calculation, today is ${new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC' })}.
DATA :
`;
prompt.push(briefing);
prompt = [...prompt, ...summary];
Expand All @@ -62,6 +69,7 @@ export function getPromptForDeviceSummary(states, deviceName, paramOptions) {
export async function getDeviceSummary(openai, ha, states, deviceName, paramOptions) {
const prompt = getPromptForDeviceSummary(states, deviceName, paramOptions);
if(!prompt) {
log('app','warn',`No prompt found for device ${deviceName}`);
return null;
}
const summary = getFirstChatResponse(openai, prompt);
Expand All @@ -82,6 +90,7 @@ export async function getDeviceSummary(openai, ha, states, deviceName, paramOpti
export async function updateDeviceSummary(openai, ha, states, deviceName, paramOptions) {
const summary = await getDeviceSummary(openai, ha, states, deviceName, paramOptions);
if(!summary) {
log('app','warn',`No summary found for device ${deviceName}`);
return null;
}
return ha.states.update('sensor', `${SUMMARY_DEVICE_ENTITY_PREFIX}${deviceName}`, {
Expand Down

0 comments on commit bcf04da

Please sign in to comment.