Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Changelog
## [Next]


- Fix loop when all configured devices have invalid/unknown device types.

## [1.5.2] - 2025-10-03

Expand Down
50 changes: 50 additions & 0 deletions src/deviceManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,54 @@ describe('DeviceManager', () => {
expect(topics?.controlSubscriptionTopic).toBe('customPrefix/HMA-1/control/test123');
expect(topics?.availabilityTopic).toBe('customPrefix/HMA-1/availability/test123');
});

it('should handle invalid device types gracefully', () => {
const invalidConfig: MqttConfig = {
brokerUrl: 'mqtt://localhost',
clientId: 'test-client',
topicPrefix: DEFAULT_TOPIC_PREFIX,
devices: [
{
deviceType: 'INVALID-TYPE',
deviceId: 'test123',
},
],
};

// DeviceManager constructor should throw an error for invalid devices
expect(() => new DeviceManager(invalidConfig, mockOnUpdateState)).toThrow(
'No valid devices configured. All configured devices have unknown device types.',
);
});

it('should handle mix of valid and invalid device types', () => {
const mixedConfig: MqttConfig = {
brokerUrl: 'mqtt://localhost',
clientId: 'test-client',
topicPrefix: DEFAULT_TOPIC_PREFIX,
devices: [
{
deviceType: 'INVALID-TYPE',
deviceId: 'invalid123',
},
{
deviceType: 'HMA-1',
deviceId: 'valid123',
},
],
};

const dm = new DeviceManager(mixedConfig, mockOnUpdateState);

// Invalid device should not have topics
const invalidTopics = dm.getDeviceTopics(mixedConfig.devices[0]);
expect(invalidTopics).toBeUndefined();

// Valid device should have topics
const validTopics = dm.getDeviceTopics(mixedConfig.devices[1]);
expect(validTopics).toBeDefined();

// getPollingInterval should work since there's at least one valid device
expect(() => dm.getPollingInterval()).not.toThrow();
});
});
16 changes: 16 additions & 0 deletions src/deviceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,16 @@ export class DeviceManager {
deviceState: DeviceStateData,
) => void,
) {
let validDeviceCount = 0;

this.config.devices.forEach(device => {
const deviceDefinition = getDeviceDefinition(device.deviceType);
if (!deviceDefinition) {
logger.warn(`Skipping unknown device type: ${device.deviceType}`);
return;
}
validDeviceCount++;

const deviceKey = this.getDeviceKey(device);
logger.info(`Initializing topics for device: ${deviceKey}`);
let deviceId = device.deviceId;
Expand All @@ -70,6 +74,12 @@ export class DeviceManager {

logger.debug(`Topics for ${deviceKey}:`, this.deviceTopics[deviceKey]);
});

if (validDeviceCount === 0) {
throw new Error(
'No valid devices configured. All configured devices have unknown device types.',
);
}
}

private getDeviceKey(device: Device): DeviceKey {
Expand Down Expand Up @@ -258,6 +268,12 @@ export class DeviceManager {
?.filter(n => n != null) ?? []
);
});

// Check if there are any valid polling intervals
if (allPollingIntervals.length === 0) {
throw new Error('No valid devices configured');
}

function gcd2(a: number, b: number): number {
if (b === 0) {
return a;
Expand Down
10 changes: 9 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,15 @@ async function main() {

logger.debug('Application initialized successfully');
} catch (error) {
logger.error('Failed to initialize application:', error);
logger.error('Failed to initialize application');
if (error instanceof Error) {
logger.error(`Error: ${error.message}`);
if (logger.levelVal <= logger.levels.values.debug) {
logger.debug('Stack trace:', error.stack);
}
} else {
logger.error('Error:', error);
}
process.exit(1);
}
}
Expand Down
10 changes: 9 additions & 1 deletion src/mqttClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,15 @@ export class MqttClient {
* Set up periodic polling
*/
private setupPeriodicPolling(): void {
const pollingInterval = this.deviceManager.getPollingInterval();
let pollingInterval: number;
try {
pollingInterval = this.deviceManager.getPollingInterval();
} catch (error) {
logger.error('Failed to get polling interval:', error);
logger.error('This usually means no valid devices are configured');
return;
}

logger.debug(`Setting up periodic polling every ${pollingInterval / 1000} seconds`);

// Initial poll - request data immediately for all devices
Expand Down
Loading