Files
mysa-js-sdk/example/main.ts
Pascal Bourque d861a50136 fix!: Device and state properties are now optional (#170)
Updated DeviceBase, BrandInfo, and DeviceState interfaces to make most properties optional, improving flexibility for partial objects and better handling of missing data.
2025-11-01 09:33:31 -04:00

112 lines
3.6 KiB
TypeScript

import { MysaApiClient, MysaSession } from '@/api';
import 'dotenv/config';
import { readFile, rm, writeFile } from 'fs/promises';
import { pino } from 'pino';
const rootLogger = pino({
name: 'example',
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
transport: {
target: 'pino-pretty',
options: {
colorize: true,
singleLine: true,
ignore: 'hostname,module',
messageFormat: '\x1b[33m[{module}]\x1b[39m {msg}'
}
}
}).child({ module: 'example' });
/** Main entry point of the example application. */
async function main() {
let session: MysaSession | undefined;
try {
rootLogger.info('Loading session...');
const sessionJson = await readFile('session.json', 'utf8');
session = JSON.parse(sessionJson);
} catch {
rootLogger.info('No valid session file found.');
}
const client = new MysaApiClient(session, { logger: rootLogger.child({ module: 'mysa-js-sdk' }) });
client.emitter.on('sessionChanged', async (newSession) => {
if (newSession) {
rootLogger.info('Saving new session...');
await writeFile('session.json', JSON.stringify(newSession));
} else {
try {
rootLogger.info('Removing session file...');
await rm('session.json');
} catch {
// Ignore error if file does not exist
}
}
});
if (!client.isAuthenticated) {
rootLogger.info('Logging in...');
const username = process.env.MYSA_USERNAME;
const password = process.env.MYSA_PASSWORD;
if (!username || !password) {
throw new Error('Missing MYSA_USERNAME or MYSA_PASSWORD environment variables.');
}
await client.login(username, password);
}
const devices = await client.getDevices();
if (process.env.MYSA_OUTPUT_RAW_DATA === 'true') {
client.emitter.on('rawRealtimeMessageReceived', (data) => {
rootLogger.info(data, 'Raw message received');
});
} else {
client.emitter.on('statusChanged', (status) => {
try {
const device = devices.DevicesObj[status.deviceId];
const watts =
status.current !== undefined && device.Voltage !== undefined ? status.current * device.Voltage : undefined;
rootLogger.info(
`[${status.deviceId}] '${device.Name ?? 'Unknown'}' status changed: ${status.temperature}°C, ${status.humidity}%, ${watts ?? 'na'}W`
);
} catch (error) {
rootLogger.error(error, `Error processing status update for device '${status.deviceId}'`);
}
});
client.emitter.on('setPointChanged', (change) => {
try {
const device = devices.DevicesObj[change.deviceId];
rootLogger.info(
`'${device.Name ?? 'Unknown'}' setpoint changed from ${change.previousSetPoint} to ${change.newSetPoint}`
);
} catch (error) {
rootLogger.error(error, `Error processing setpoint update for device '${change.deviceId}'`);
}
});
client.emitter.on('stateChanged', (change) => {
try {
const device = devices.DevicesObj[change.deviceId];
rootLogger.info(change, `'${device.Name ?? 'Unknown'}' state changed.`);
} catch (error) {
rootLogger.error(error, `Error processing state update for device '${change.deviceId}'`);
}
});
}
await Promise.all(
Object.entries(devices.DevicesObj).map(async ([deviceId, device]) => {
const serial = await client.getDeviceSerialNumber(deviceId);
rootLogger.info(`Serial number for device '${deviceId}' (${device.Name ?? 'Unknown'}): ${serial}`);
await client.startRealtimeUpdates(deviceId);
})
);
}
main().catch((error) => {
rootLogger.error(error, 'Error in main');
});