Skip to content

Commit 955928e

Browse files
committed
refactor: extract MQTT_RETAINED_STATE_OPTIONS + named regex constants
Two small DRY wins flagged by /simplify but skipped earlier: 1. The literal { retain: true, qos: 0 } appeared at 16 callsites across haDiscovery.js, haBridgeDiagnostics.js, staleDeviceDetector.js, mqttCommandRouter.js, and cgateWebBridge.js. Replace with a frozen MQTT_RETAINED_STATE_OPTIONS constant in src/constants.js. The frozen object prevents accidental mutation of a now-shared reference. 2. Two regexes in commandResponseProcessor.js — the C-Gate timestamp prefix and the network-path extractor — were inline. Move them to named module-scope constants (CGATE_TIMESTAMP_PREFIX, CGATE_NETWORK_PATH) so the intent is visible at the callsite and the patterns can be referenced by future code without copy-paste. Also drops a now-redundant local `opts` alias in staleDeviceDetector that just aliased the literal. No behaviour change. Full suite 1199/1199 passing, lint clean.
1 parent f4b72ef commit 955928e

7 files changed

Lines changed: 36 additions & 23 deletions

src/cgateWebBridge.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const HaBridgeDiagnostics = require('./haBridgeDiagnostics');
1515
const StaleDeviceDetector = require('./staleDeviceDetector');
1616
const { createLogger } = require('./logger');
1717
const { LineProcessor } = require('./lineProcessor');
18+
const { MQTT_RETAINED_STATE_OPTIONS } = require('./constants');
1819

1920
/**
2021
* Main bridge class that connects C-Gate (Clipsal C-Bus automation system) to MQTT.
@@ -140,7 +141,7 @@ class CgateWebBridge {
140141
};
141142

142143
// MQTT options
143-
this._mqttOptions = this.settings.retainreads ? { retain: true, qos: 0 } : { qos: 0 };
144+
this._mqttOptions = this.settings.retainreads ? MQTT_RETAINED_STATE_OPTIONS : { qos: 0 };
144145

145146
// Label loader for custom device names (before EventPublisher so it can use type overrides)
146147
this.labelLoader = new LabelLoader(this.settings.cbus_label_file || null);

src/commandResponseProcessor.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ const {
88
CGATE_RESPONSE_SYSTEM_EVENT
99
} = require('./constants');
1010

11+
// Strips C-Gate's leading async-event timestamp ("20260504-193110.569 ").
12+
const CGATE_TIMESTAMP_PREFIX = /^\d{8}-\d{6}\.\d+\s+/;
13+
// Extracts the numeric network id from a C-Gate object path "//PROJECT/254 ...".
14+
const CGATE_NETWORK_PATH = /\/\/[^/]+\/(\d+)\b/;
15+
1116
/**
1217
* Handles processing of C-Gate command responses.
1318
*
@@ -90,7 +95,7 @@ class CommandResponseProcessor {
9095
// Asynchronous notifications enabled by EVENT ON arrive on the command
9196
// port with this prefix; without stripping it the hyphen-first split
9297
// below would land in the date instead of the response code.
93-
const stripped = (line || '').replace(/^\d{8}-\d{6}\.\d+\s+/, '');
98+
const stripped = (line || '').replace(CGATE_TIMESTAMP_PREFIX, '');
9499

95100
// C-Gate response codes are exactly 3 digits at the start of the line,
96101
// followed by either '-' (e.g. "200-OK") or ' ' (e.g. "742 //PROJECT
@@ -200,7 +205,7 @@ class CommandResponseProcessor {
200205
this.logger.debug(`C-Gate system event 742 (no action): ${data}`);
201206
return;
202207
}
203-
const match = data.match(/\/\/[^/]+\/(\d+)\b/);
208+
const match = data.match(CGATE_NETWORK_PATH);
204209
if (!match) {
205210
this.logger.debug(`C-Gate system event 742 (Network created, but no network id parsed): ${data}`);
206211
return;

src/constants.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ const MQTT_TOPIC_SUFFIX_HVAC_FAN_MODE = 'fan_mode'; // Fan speed/m
4444
const MQTT_TOPIC_STATUS = 'hello/cgateweb';
4545
const MQTT_TOPIC_MANUAL_TRIGGER = `${MQTT_TOPIC_PREFIX_WRITE}/bridge/announce`;
4646

47+
// Default publish options for retained state topics (HA Discovery configs,
48+
// device states, diagnostics). Frozen so callers can't accidentally mutate
49+
// the shared object.
50+
const MQTT_RETAINED_STATE_OPTIONS = Object.freeze({ retain: true, qos: 0 });
51+
4752
// MQTT Payloads
4853
const MQTT_PAYLOAD_STATUS_ONLINE = 'Online';
4954
const MQTT_PAYLOAD_STATUS_OFFLINE = 'Offline';
@@ -168,6 +173,7 @@ module.exports = {
168173
MQTT_TOPIC_SUFFIX_HVAC_FAN_MODE,
169174
MQTT_TOPIC_STATUS,
170175
MQTT_TOPIC_MANUAL_TRIGGER,
176+
MQTT_RETAINED_STATE_OPTIONS,
171177
MQTT_PAYLOAD_STATUS_ONLINE,
172178
MQTT_PAYLOAD_STATUS_OFFLINE,
173179
MQTT_STATE_ON,

src/haBridgeDiagnostics.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const fs = require('fs');
22
const { createLogger } = require('./logger');
3-
const { MQTT_TOPIC_STATUS, entityIdFields, HA_COMPONENT_SENSOR, HA_COMPONENT_BINARY_SENSOR, HA_DEVICE_VIA } = require('./constants');
3+
const { MQTT_TOPIC_STATUS, MQTT_RETAINED_STATE_OPTIONS, entityIdFields, HA_COMPONENT_SENSOR, HA_COMPONENT_BINARY_SENSOR, HA_DEVICE_VIA } = require('./constants');
44

55
const CGATE_VERSION_FILE = '/data/cgate/.version';
66

@@ -90,7 +90,7 @@ class HaBridgeDiagnostics {
9090
model: 'Bridge Diagnostics'
9191
}
9292
};
93-
this._publish(topic, JSON.stringify(payload), { retain: true, qos: 0 });
93+
this._publish(topic, JSON.stringify(payload), MQTT_RETAINED_STATE_OPTIONS);
9494
}
9595
}
9696

@@ -124,7 +124,7 @@ class HaBridgeDiagnostics {
124124

125125
for (const [key, value] of Object.entries(values)) {
126126
const topic = `cbus/read/bridge/diagnostics/${key}/state`;
127-
this._publish(topic, String(value), { retain: true, qos: 0 });
127+
this._publish(topic, String(value), MQTT_RETAINED_STATE_OPTIONS);
128128
}
129129

130130
// Publish consolidated JSON stats for monitoring dashboards
@@ -156,7 +156,7 @@ class HaBridgeDiagnostics {
156156
} : null,
157157
cgate_version: cgateVersion
158158
};
159-
this._publish('cbus/read/bridge/stats', JSON.stringify(stats), { retain: true, qos: 0 });
159+
this._publish('cbus/read/bridge/stats', JSON.stringify(stats), MQTT_RETAINED_STATE_OPTIONS);
160160
}
161161
}
162162

src/haDiscovery.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const {
2727
MQTT_COMMAND_STOP,
2828
MQTT_TOPIC_SUFFIX_DISCOVERY_STATUS,
2929
MQTT_TOPIC_STATUS,
30+
MQTT_RETAINED_STATE_OPTIONS,
3031
HA_COMPONENT_LIGHT,
3132
HA_COMPONENT_BUTTON,
3233
HA_COMPONENT_CLIMATE,
@@ -346,7 +347,7 @@ class HaDiscovery {
346347
}
347348
};
348349

349-
this._publish(configTopic, JSON.stringify(payload), { retain: true, qos: 0 });
350+
this._publish(configTopic, JSON.stringify(payload), MQTT_RETAINED_STATE_OPTIONS);
350351
entry.configPublished = true;
351352
}
352353

@@ -373,7 +374,7 @@ class HaDiscovery {
373374
entry.status = status;
374375

375376
const stateTopic = `${MQTT_TOPIC_PREFIX_READ}/${networkKey}///${MQTT_TOPIC_SUFFIX_DISCOVERY_STATUS}`;
376-
this._publish(stateTopic, status, { retain: true, qos: 0 });
377+
this._publish(stateTopic, status, MQTT_RETAINED_STATE_OPTIONS);
377378
this.logger.debug(`Discovery status for network ${networkKey}: ${previous || 'init'} -> ${status}`);
378379
}
379380

@@ -458,7 +459,7 @@ class HaDiscovery {
458459
this._publish(
459460
`${MQTT_TOPIC_PREFIX_READ}/${networkForTree}///tree`,
460461
JSON.stringify(result),
461-
{ retain: true, qos: 0 }
462+
MQTT_RETAINED_STATE_OPTIONS
462463
);
463464

464465
// Generate HA Discovery messages
@@ -541,7 +542,7 @@ class HaDiscovery {
541542
for (const topic of this._publishedTopics) {
542543
if (topic.includes(`/${networkUniqueIdPrefix}`) && !this._currentRunTopics.has(topic)) {
543544
this.logger.debug(`Clearing stale discovery topic: ${topic}`);
544-
this._publish(topic, '', { retain: true, qos: 0 });
545+
this._publish(topic, '', MQTT_RETAINED_STATE_OPTIONS);
545546
}
546547
}
547548

@@ -621,7 +622,7 @@ class HaDiscovery {
621622
// (e.g. first run saw it as a light; this run sees the type override).
622623
const uniqueId = `cgateweb_${networkId}_${appId}_${groupId}`;
623624
const staleTopic = `${this.settings.ha_discovery_prefix}/${HA_COMPONENT_LIGHT}/${uniqueId}/${HA_DISCOVERY_SUFFIX}`;
624-
this._publish(staleTopic, '', { retain: true, qos: 0 });
625+
this._publish(staleTopic, '', MQTT_RETAINED_STATE_OPTIONS);
625626
// Ensure the stale light topic is not retained in _publishedTopics
626627
this._publishedTopics.delete(staleTopic);
627628
return;
@@ -671,7 +672,7 @@ class HaDiscovery {
671672
}
672673
};
673674

674-
this._publish(discoveryTopic, JSON.stringify(payload), { retain: true, qos: 0 });
675+
this._publish(discoveryTopic, JSON.stringify(payload), MQTT_RETAINED_STATE_OPTIONS);
675676
if (this._currentRunTopics) this._currentRunTopics.add(discoveryTopic);
676677
this.discoveryCount++;
677678
});
@@ -796,7 +797,7 @@ class HaDiscovery {
796797
}
797798
};
798799

799-
this._publish(discoveryTopic, JSON.stringify(payload), { retain: true, qos: 0 });
800+
this._publish(discoveryTopic, JSON.stringify(payload), MQTT_RETAINED_STATE_OPTIONS);
800801
this.discoveryCount++;
801802
}
802803

@@ -865,7 +866,7 @@ class HaDiscovery {
865866
}
866867
};
867868

868-
this._publish(discoveryTopic, JSON.stringify(payload), { retain: true, qos: 0 });
869+
this._publish(discoveryTopic, JSON.stringify(payload), MQTT_RETAINED_STATE_OPTIONS);
869870
if (this._currentRunTopics) this._currentRunTopics.add(discoveryTopic);
870871
this.discoveryCount++;
871872

@@ -909,7 +910,7 @@ class HaDiscovery {
909910
}
910911
};
911912

912-
this._publish(discoveryTopic, JSON.stringify(payload), { retain: true, qos: 0 });
913+
this._publish(discoveryTopic, JSON.stringify(payload), MQTT_RETAINED_STATE_OPTIONS);
913914
this.discoveryCount++;
914915
}
915916

@@ -942,7 +943,7 @@ class HaDiscovery {
942943
}
943944
};
944945

945-
this._publish(discoveryTopic, JSON.stringify(payload), { retain: true, qos: 0 });
946+
this._publish(discoveryTopic, JSON.stringify(payload), MQTT_RETAINED_STATE_OPTIONS);
946947
this.discoveryCount++;
947948
}
948949

src/mqttCommandRouter.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { createLogger } = require('./logger');
55
const {
66
MQTT_TOPIC_MANUAL_TRIGGER,
77
MQTT_TOPIC_PREFIX_READ,
8+
MQTT_RETAINED_STATE_OPTIONS,
89
MQTT_TOPIC_SUFFIX_LEVEL,
910
MQTT_TOPIC_SUFFIX_POSITION,
1011
MQTT_CMD_TYPE_GETALL,
@@ -462,7 +463,7 @@ class MqttCommandRouter extends EventEmitter {
462463
? durationMs
463464
: (this.settings.cover_ramp_duration_ms || 5000);
464465

465-
const mqttOptions = this.settings.retainreads ? { retain: true, qos: 0 } : { qos: 0 };
466+
const mqttOptions = this.settings.retainreads ? MQTT_RETAINED_STATE_OPTIONS : { qos: 0 };
466467
const topicBase = `${MQTT_TOPIC_PREFIX_READ}/${network}/${application}/${group}`;
467468

468469
this._coverRampTracker.startRamp(key, startLevel, targetLevel, duration, (level) => {

src/staleDeviceDetector.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
const { createLogger } = require('./logger');
2-
const { MQTT_TOPIC_STATUS, entityIdFields, HA_COMPONENT_SENSOR, HA_DEVICE_VIA } = require('./constants');
2+
const { MQTT_TOPIC_STATUS, MQTT_RETAINED_STATE_OPTIONS, entityIdFields, HA_COMPONENT_SENSOR, HA_DEVICE_VIA } = require('./constants');
33

44
/**
55
* Periodically checks for C-Bus devices that have not reported a state change
@@ -121,16 +121,15 @@ class StaleDeviceDetector {
121121
*/
122122
_publishStaleCount(count, staleDevices) {
123123
const thresholdHours = Math.max(1, Number(this.settings.stale_device_threshold_hours) || 24);
124-
const opts = { retain: true, qos: 0 };
125124

126-
this.mqttClient.publish('cbus/bridge/stale_devices', String(count), opts);
125+
this.mqttClient.publish('cbus/bridge/stale_devices', String(count), MQTT_RETAINED_STATE_OPTIONS);
127126

128127
const attributes = {
129128
stale_devices: staleDevices,
130129
threshold_hours: thresholdHours,
131130
checked_at: new Date().toISOString()
132131
};
133-
this.mqttClient.publish('cbus/bridge/stale_devices_detail', JSON.stringify(attributes), opts);
132+
this.mqttClient.publish('cbus/bridge/stale_devices_detail', JSON.stringify(attributes), MQTT_RETAINED_STATE_OPTIONS);
134133
}
135134

136135
/**
@@ -159,7 +158,7 @@ class StaleDeviceDetector {
159158
model: 'Bridge Diagnostics'
160159
}
161160
};
162-
this.mqttClient.publish(topic, JSON.stringify(payload), { retain: true, qos: 0 });
161+
this.mqttClient.publish(topic, JSON.stringify(payload), MQTT_RETAINED_STATE_OPTIONS);
163162
}
164163
}
165164

0 commit comments

Comments
 (0)