matterbridge 2.1.6-dev.1 → 2.1.6-dev.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -33,18 +33,19 @@ matterbridge-zigbee2mqtt v. 2.4.4
33
33
  matterbridge-somfy-tahoma v. 1.2.3
34
34
  matterbridge-hass v. 0.0.8
35
35
 
36
- ## [2.1.6] - 2025-02-12
36
+ ## [2.1.6] - 2025-02-13
37
37
 
38
38
  ### Added
39
39
 
40
40
  - [docker]: Added health check directly inside the docker image. No need to change configuration.
41
- - [platform]: Added saving to storage the selects for faster loading.
41
+ - [platform]: Saving in the storage the selects for faster loading of plugins.
42
42
  - [icon]: Added matterbridge svg icon (thanks: https://github.com/robvanoostenrijk https://github.com/stuntguy3000).
43
43
  - [frontend]: Frontend v.2.4.2.
44
44
 
45
45
  ### Changed
46
46
 
47
47
  - [package]: Update matter.js to 0.12.4-alpha.0-20250212-b2729c9eb
48
+ - [package]: Update matter.js to 0.12.4-alpha.0-20250213-1187f81eb
48
49
 
49
50
  <a href="https://www.buymeacoffee.com/luligugithub">
50
51
  <img src="./yellow-button.png" alt="Buy me a coffee" width="120">
package/dist/cli.js CHANGED
@@ -1,10 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
  import { Matterbridge } from './matterbridge.js';
3
3
  let instance;
4
+ let session;
5
+ let memoryCheckInterval;
6
+ let prevCpus;
7
+ let lastCpuUsage = 0;
4
8
  const cli = '\u001B[32m';
5
9
  const er = '\u001B[38;5;9m';
6
10
  const rs = '\u001B[40;0m';
11
+ const db = '\u001B[38;5;245m';
12
+ const CYAN = '\u001B[36m';
13
+ const YELLOW = '\u001B[33m';
14
+ const BRIGHT = '\u001B[1m';
7
15
  async function main() {
16
+ if (process.argv.includes('-memorycheck'))
17
+ await startMemoryCheck();
18
+ if (process.argv.includes('-inspector'))
19
+ await startInspector();
8
20
  if (process.argv.includes('-debug'))
9
21
  console.log(cli + `CLI: ${process.argv.includes('-edge') ? 'MatterbridgeEdge' : 'Matterbridge'}.loadInstance() called` + rs);
10
22
  instance = await Matterbridge.loadInstance(true);
@@ -12,6 +24,90 @@ async function main() {
12
24
  if (process.argv.includes('-debug'))
13
25
  console.log(cli + `CLI: ${process.argv.includes('-edge') ? 'MatterbridgeEdge' : 'Matterbridge'}.loadInstance() exited` + rs);
14
26
  }
27
+ async function startMemoryCheck() {
28
+ const os = await import('node:os');
29
+ console.log(cli + `CLI: Memory check started` + rs);
30
+ prevCpus = os.cpus();
31
+ const formatMemoryUsage = (bytes) => {
32
+ if (bytes >= 1024 ** 3) {
33
+ return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
34
+ }
35
+ else if (bytes >= 1024 ** 2) {
36
+ return `${(bytes / 1024 ** 2).toFixed(2)} MB`;
37
+ }
38
+ else {
39
+ return `${(bytes / 1024).toFixed(2)} KB`;
40
+ }
41
+ };
42
+ const interval = () => {
43
+ const currCpus = os.cpus();
44
+ let cpuUsageLog;
45
+ if (currCpus.length !== prevCpus.length) {
46
+ prevCpus = currCpus;
47
+ cpuUsageLog = lastCpuUsage.toFixed(2);
48
+ }
49
+ let totalIdle = 0, totalTick = 0;
50
+ prevCpus.forEach((prevCpu, i) => {
51
+ const currCpu = currCpus[i];
52
+ const idleDiff = currCpu.times.idle - prevCpu.times.idle;
53
+ const totalDiff = Object.keys(currCpu.times).reduce((acc, key) => acc + (currCpu.times[key] - prevCpu.times[key]), 0);
54
+ totalIdle += idleDiff;
55
+ totalTick += totalDiff;
56
+ });
57
+ const cpuUsage = 100 - (totalIdle / totalTick) * 100;
58
+ if (totalTick === 0 || isNaN(cpuUsage) || !isFinite(cpuUsage) || cpuUsage <= 0) {
59
+ cpuUsageLog = lastCpuUsage.toFixed(2);
60
+ }
61
+ prevCpus = currCpus;
62
+ lastCpuUsage = cpuUsage;
63
+ cpuUsageLog = cpuUsage.toFixed(2);
64
+ const memoryUsageRaw = process.memoryUsage();
65
+ const memoryUsage = {
66
+ rss: formatMemoryUsage(memoryUsageRaw.rss),
67
+ heapTotal: formatMemoryUsage(memoryUsageRaw.heapTotal),
68
+ heapUsed: formatMemoryUsage(memoryUsageRaw.heapUsed),
69
+ external: formatMemoryUsage(memoryUsageRaw.external),
70
+ arrayBuffers: formatMemoryUsage(memoryUsageRaw.arrayBuffers),
71
+ };
72
+ console.log(`${YELLOW}${BRIGHT}Cpu usage:${db} ${CYAN}${cpuUsageLog.padStart(6, ' ')} %${db} ${YELLOW}${BRIGHT}Memory usage:${db} rss ${CYAN}${memoryUsage.rss}${db} heapTotal ${CYAN}${memoryUsage.heapTotal}${db} heapUsed ${CYAN}${memoryUsage.heapUsed}${db} external ${memoryUsage.external} arrayBuffers ${memoryUsage.arrayBuffers}` +
73
+ rs);
74
+ };
75
+ interval();
76
+ memoryCheckInterval = setInterval(interval, 1000);
77
+ }
78
+ async function stopMemoryCheck() {
79
+ console.log(cli + `CLI: Stopping memory check in 5 minute` + rs);
80
+ instance = undefined;
81
+ setTimeout(() => {
82
+ console.log(cli + `CLI: Memory check stopped` + rs);
83
+ clearInterval(memoryCheckInterval);
84
+ process.exit(0);
85
+ }, 5 * 60 * 1000);
86
+ }
87
+ async function startInspector() {
88
+ const { Session } = await import('node:inspector');
89
+ session = new Session();
90
+ session.connect();
91
+ session.post('HeapProfiler.startSampling', {}, (err) => {
92
+ if (err)
93
+ console.error(err);
94
+ else
95
+ console.log(cli + `CLI: Heap sampling started` + rs);
96
+ });
97
+ }
98
+ async function stopInspector() {
99
+ const { writeFileSync } = await import('node:fs');
100
+ session.post('HeapProfiler.stopSampling', (err, result) => {
101
+ if (err) {
102
+ console.error(err);
103
+ }
104
+ else {
105
+ const profile = JSON.stringify(result.profile, null, 2);
106
+ writeFileSync('heap-sampling-profile.heapsampling.json', profile);
107
+ console.log(cli + `CLI: Heap sampling profile saved to heap-sampling-profile.heapsnapshot` + rs);
108
+ }
109
+ });
110
+ }
15
111
  function registerHandlers() {
16
112
  if (instance)
17
113
  instance.on('shutdown', async () => shutdown());
@@ -23,7 +119,14 @@ function registerHandlers() {
23
119
  async function shutdown() {
24
120
  if (process.argv.includes('-debug'))
25
121
  console.log(cli + 'CLI: received shutdown event, exiting...' + rs);
26
- process.exit(0);
122
+ if (process.argv.includes('-inspector'))
123
+ await stopInspector();
124
+ if (process.argv.includes('-memorycheck')) {
125
+ await stopMemoryCheck();
126
+ }
127
+ else {
128
+ process.exit(0);
129
+ }
27
130
  }
28
131
  async function restart() {
29
132
  if (process.argv.includes('-debug'))
package/dist/frontend.js CHANGED
@@ -752,17 +752,6 @@ export class Frontend {
752
752
  this.log.debug(`Frontend initialized on port ${YELLOW}${this.port}${db} static ${UNDERLINE}${path.join(this.matterbridge.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
753
753
  }
754
754
  async stop() {
755
- if (hasParameter('memorycheck')) {
756
- this.wssSendSnackbarMessage('Memory check started', getIntParameter('memorycheck') ?? 5 * 60 * 1000);
757
- await new Promise((resolve) => {
758
- this.log.debug(`***Memory check started for ${getIntParameter('memorycheck') ?? 5 * 60 * 1000} ms`);
759
- setTimeout(() => {
760
- this.wssSendSnackbarMessage('Memory check stopped', 10);
761
- this.log.debug(`***Memory check stopped after ${getIntParameter('memorycheck') ?? 5 * 60 * 1000} ms`);
762
- resolve();
763
- }, getIntParameter('memorycheck') ?? 5 * 60 * 1000);
764
- });
765
- }
766
755
  if (this.httpServer) {
767
756
  this.httpServer.close();
768
757
  this.httpServer.removeAllListeners();
@@ -877,12 +866,10 @@ export class Frontend {
877
866
  this.wssSendMemoryUpdate(this.matterbridge.systemInformation.freeMemory, this.matterbridge.systemInformation.totalMemory, this.matterbridge.systemInformation.systemUptime, this.matterbridge.systemInformation.rss, this.matterbridge.systemInformation.heapUsed, this.matterbridge.systemInformation.heapTotal);
878
867
  };
879
868
  interval();
880
- this.memoryInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 1000);
881
- this.memoryInterval.unref();
869
+ this.memoryInterval = setInterval(interval, getIntParameter('memoryinterval') ?? 1000).unref();
882
870
  this.memoryTimeout = setTimeout(() => {
883
871
  this.stopCpuMemoryDump();
884
- }, getIntParameter('memorytimeout') ?? 600000);
885
- this.memoryTimeout.unref();
872
+ }, getIntParameter('memorytimeout') ?? 600000).unref();
886
873
  }
887
874
  stopCpuMemoryDump() {
888
875
  clearInterval(this.memoryInterval);
@@ -1,4 +1,4 @@
1
1
  export * from '@matter/main';
2
2
  export { SemanticNamespace, ClosureTag, CompassDirectionTag, CompassLocationTag, DirectionTag, ElectricalMeasurementTag, LaundryTag, LevelTag, LocationTag, NumberTag, PositionTag, PowerSourceTag, RefrigeratorTag, RoomAirConditionerTag, SwitchesTag, } from '@matter/main';
3
3
  export { AttributeElement, ClusterElement, ClusterModel, CommandElement, EventElement, FieldElement } from '@matter/main/model';
4
- export { logEndpoint, MdnsService } from '@matter/main/protocol';
4
+ export { logEndpoint, MdnsService, Val } from '@matter/main/protocol';
@@ -1443,6 +1443,7 @@ export class Matterbridge extends EventEmitter {
1443
1443
  sanitizeFabrics(serverNode.state.commissioning.fabrics, true);
1444
1444
  }
1445
1445
  this.frontend.wssSendRefreshRequired();
1446
+ this.frontend.wssSendSnackbarMessage(`${storeId} is online`);
1446
1447
  });
1447
1448
  serverNode.lifecycle.offline.on(() => {
1448
1449
  this.log.notice(`Server node for ${storeId} is offline`);
@@ -14,6 +14,10 @@ export class MatterbridgePlatform {
14
14
  context;
15
15
  selectDevice = new Map();
16
16
  selectEntity = new Map();
17
+ contextReady;
18
+ selectDeviceContextReady;
19
+ selectEntityContextReady;
20
+ ready;
17
21
  registeredEndpoints = new Map();
18
22
  registeredEndpointsByName = new Map();
19
23
  constructor(matterbridge, log, config) {
@@ -31,26 +35,28 @@ export class MatterbridgePlatform {
31
35
  forgiveParseErrors: true,
32
36
  });
33
37
  this.log.debug(`Creating context for plugin ${this.config.name}`);
34
- this.storage.createStorage('context').then((context) => {
38
+ this.contextReady = this.storage.createStorage('context').then((context) => {
35
39
  this.context = context;
36
40
  this.log.debug(`Created context for plugin ${this.config.name}`);
41
+ return context;
37
42
  });
38
43
  this.log.debug(`Loading selectDevice for plugin ${this.config.name}`);
39
- this.storage.createStorage('selectDevice').then((context) => {
40
- context.get('selectDevice', []).then((selectDevice) => {
41
- for (const device of selectDevice)
42
- this.selectDevice.set(device.serial, device);
43
- });
44
+ this.selectDeviceContextReady = this.storage.createStorage('selectDevice').then(async (context) => {
45
+ const selectDevice = await context.get('selectDevice', []);
46
+ for (const device of selectDevice)
47
+ this.selectDevice.set(device.serial, device);
44
48
  this.log.debug(`Loaded ${this.selectDevice.size} selectDevice for plugin ${this.config.name}`);
45
49
  });
46
50
  this.log.debug(`Loading selectEntity for plugin ${this.config.name}`);
47
- this.storage.createStorage('selectEntity').then((context) => {
48
- context.get('selectEntity', []).then((selectEntity) => {
49
- for (const entity of selectEntity)
50
- this.selectEntity.set(entity.name, entity);
51
- });
51
+ this.selectEntityContextReady = this.storage.createStorage('selectEntity').then(async (context) => {
52
+ const selectEntity = await context.get('selectEntity', []);
53
+ for (const entity of selectEntity)
54
+ this.selectEntity.set(entity.name, entity);
52
55
  this.log.debug(`Loaded ${this.selectEntity.size} selectEntity for plugin ${this.config.name}`);
53
56
  });
57
+ this.ready = Promise.all([this.contextReady, this.selectDeviceContextReady, this.selectEntityContextReady]).then(() => {
58
+ this.log.debug(`MatterbridgePlatform for plugin ${this.config.name} is fully initialized`);
59
+ });
54
60
  }
55
61
  async onStart(reason) {
56
62
  this.log.error('Plugins must override onStart.', reason);
@@ -77,7 +83,9 @@ export class MatterbridgePlatform {
77
83
  this.selectEntity.clear();
78
84
  this.registeredEndpoints.clear();
79
85
  this.registeredEndpointsByName.clear();
80
- this.log.debug('Saving context...');
86
+ this.contextReady = undefined;
87
+ this.selectDeviceContextReady = undefined;
88
+ this.selectEntityContextReady = undefined;
81
89
  await this.context?.close();
82
90
  this.context = undefined;
83
91
  await this.storage?.close();
@@ -214,16 +222,15 @@ export class MatterbridgePlatform {
214
222
  endpointMap.set(device.uniqueId, device.maybeNumber);
215
223
  }
216
224
  for (const child of device.getChildEndpoints()) {
217
- const childId = child.id;
218
- if (!childId || !child.maybeNumber)
225
+ if (!child.maybeId || !child.maybeNumber)
219
226
  continue;
220
- if (endpointMap.has(device.uniqueId + separator + childId) && endpointMap.get(device.uniqueId + separator + childId) !== child.maybeNumber) {
221
- this.log.warn(`Child endpoint number for device ${CYAN}${device.deviceName}${wr}.${CYAN}${childId}${wr} changed from ${CYAN}${endpointMap.get(device.uniqueId + separator + childId)}${wr} to ${CYAN}${child.maybeNumber}${wr}`);
222
- endpointMap.set(device.uniqueId + separator + childId, child.maybeNumber);
227
+ if (endpointMap.has(device.uniqueId + separator + child.id) && endpointMap.get(device.uniqueId + separator + child.id) !== child.maybeNumber) {
228
+ this.log.warn(`Child endpoint number for device ${CYAN}${device.deviceName}${wr}.${CYAN}${child.id}${wr} changed from ${CYAN}${endpointMap.get(device.uniqueId + separator + child.id)}${wr} to ${CYAN}${child.maybeNumber}${wr}`);
229
+ endpointMap.set(device.uniqueId + separator + child.id, child.maybeNumber);
223
230
  }
224
- if (!endpointMap.has(device.uniqueId + separator + childId)) {
225
- this.log.debug(`Setting child endpoint number for device ${CYAN}${device.uniqueId}${db}.${CYAN}${childId}${db} to ${CYAN}${child.maybeNumber}${db}`);
226
- endpointMap.set(device.uniqueId + separator + childId, child.maybeNumber);
231
+ if (!endpointMap.has(device.uniqueId + separator + child.id)) {
232
+ this.log.debug(`Setting child endpoint number for device ${CYAN}${device.uniqueId}${db}.${CYAN}${child.id}${db} to ${CYAN}${child.maybeNumber}${db}`);
233
+ endpointMap.set(device.uniqueId + separator + child.id, child.maybeNumber);
227
234
  }
228
235
  }
229
236
  }
@@ -463,7 +463,7 @@ export class PluginManager {
463
463
  }
464
464
  }
465
465
  catch (err) {
466
- this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
466
+ this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message : err}`);
467
467
  plugin.error = true;
468
468
  }
469
469
  return undefined;
@@ -493,7 +493,7 @@ export class PluginManager {
493
493
  }
494
494
  catch (err) {
495
495
  plugin.error = true;
496
- this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
496
+ this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message : err}`);
497
497
  }
498
498
  return undefined;
499
499
  }
@@ -543,7 +543,7 @@ export class PluginManager {
543
543
  this.log.debug(`Plugin ${plg}${plugin.name}${db} not configured`);
544
544
  }
545
545
  if (!plugin.platform) {
546
- this.log.debug(`*Plugin ${plg}${plugin.name}${db} no platform found`);
546
+ this.log.debug(`Plugin ${plg}${plugin.name}${db} no platform found`);
547
547
  return undefined;
548
548
  }
549
549
  this.log.info(`Shutting down plugin ${plg}${plugin.name}${nf}: ${reason}...`);
@@ -565,7 +565,7 @@ export class PluginManager {
565
565
  return plugin;
566
566
  }
567
567
  catch (err) {
568
- this.log.error(`Failed to shut down plugin ${plg}${plugin.name}${er}: ${err}`);
568
+ this.log.error(`Failed to shut down plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message : err}`);
569
569
  }
570
570
  return undefined;
571
571
  }
@@ -585,35 +585,31 @@ export class PluginManager {
585
585
  return config;
586
586
  }
587
587
  catch (err) {
588
- if (err) {
589
- const nodeErr = err;
590
- if (nodeErr.code === 'ENOENT') {
591
- let config;
592
- if (plugin.name === 'matterbridge-zigbee2mqtt')
593
- config = zigbee2mqtt_config;
594
- else if (plugin.name === 'matterbridge-somfy-tahoma')
595
- config = somfytahoma_config;
596
- else if (plugin.name === 'matterbridge-shelly')
597
- config = shelly_config;
598
- else
599
- config = { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
600
- try {
601
- await fs.writeFile(configFile, JSON.stringify(config, null, 2), 'utf8');
602
- this.log.debug(`Created config file ${configFile} for plugin ${plg}${plugin.name}${db}.`);
603
- return config;
604
- }
605
- catch (err) {
606
- this.log.error(`Error creating config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err}`);
607
- return config;
608
- }
588
+ const nodeErr = err;
589
+ if (nodeErr.code === 'ENOENT') {
590
+ let config;
591
+ if (plugin.name === 'matterbridge-zigbee2mqtt')
592
+ config = zigbee2mqtt_config;
593
+ else if (plugin.name === 'matterbridge-somfy-tahoma')
594
+ config = somfytahoma_config;
595
+ else if (plugin.name === 'matterbridge-shelly')
596
+ config = shelly_config;
597
+ else
598
+ config = { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
599
+ try {
600
+ await fs.writeFile(configFile, JSON.stringify(config, null, 2), 'utf8');
601
+ this.log.debug(`Created config file ${configFile} for plugin ${plg}${plugin.name}${db}.`);
602
+ return config;
609
603
  }
610
- else {
611
- this.log.error(`Error accessing config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err}`);
612
- return { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
604
+ catch (err) {
605
+ this.log.error(`Error creating config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message : err}`);
606
+ return config;
613
607
  }
614
608
  }
615
- this.log.error(`Error loading config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err}`);
616
- return { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
609
+ else {
610
+ this.log.error(`Error accessing config file ${configFile} for plugin ${plg}${plugin.name}${er}: ${err instanceof Error ? err.message : err}`);
611
+ return { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
612
+ }
617
613
  }
618
614
  }
619
615
  async saveConfigFromPlugin(plugin) {
@@ -0,0 +1,50 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 296.2 296.2">
2
+ <defs>
3
+ <linearGradient id="lg1" x1="16.6" y1="16.6" x2="279.6" y2="279.6" gradientUnits="userSpaceOnUse">
4
+ <stop offset="0" stop-color="#00b48d" />
5
+ <stop offset=".1" stop-color="#3faa77" />
6
+ <stop offset=".3" stop-color="#234148" />
7
+ <stop offset=".7" stop-color="#203b44" />
8
+ <stop offset=".9" stop-color="#ad2e6e" />
9
+ <stop offset="1" stop-color="#c81b74" />
10
+ </linearGradient>
11
+ <linearGradient id="lg2" x1="31.1" y1="31.1" x2="265.1" y2="265.1" gradientUnits="userSpaceOnUse">
12
+ <stop offset="0" stop-color="#00b48d" />
13
+ <stop offset=".2" stop-color="#285251" />
14
+ <stop offset=".4" stop-color="#234148" />
15
+ <stop offset=".8" stop-color="#203b44" />
16
+ <stop offset=".9" stop-color="#a8316c" />
17
+ <stop offset="1" stop-color="#c81b74" />
18
+ </linearGradient>
19
+ <linearGradient id="lg3" x1="116.2" y1="143.9" x2="139.8" y2="143.9"
20
+ gradientUnits="userSpaceOnUse">
21
+ <stop offset="0" stop-color="#8bc751" />
22
+ <stop offset="1" stop-color="#0db14b" />
23
+ </linearGradient>
24
+ <linearGradient id="lg4" x1="136.1" y1="100.8" x2="159.6" y2="100.8"
25
+ xlink:href="#lg3" />
26
+ <linearGradient id="lg5" x1="155.3" y1="143.9" x2="178.9" y2="143.9"
27
+ xlink:href="#lg3" />
28
+ <linearGradient id="lg6" x1="46.8" y1="25.7" x2="89.6" y2="74.8" gradientUnits="userSpaceOnUse">
29
+ <stop offset="0" stop-color="#b1d34a" />
30
+ <stop offset="1" stop-color="#50b848" />
31
+ </linearGradient>
32
+ </defs>
33
+ <rect width="296.2" height="296.2" rx="56.7" ry="56.7" style="fill:url(#lg1)" />
34
+ <rect x="16.3" y="16.3" width="263.6" height="263.6" rx="50.5" ry="50.5" style="fill:url(#lg2)" />
35
+ <circle cx="128" cy="143.9" r="11.8" style="fill:url(#lg3)" />
36
+ <circle cx="147.8" cy="100.8" r="11.8" style="fill:url(#lg4)" />
37
+ <path
38
+ d="m244.6 114.5.4-.5L160 33a17 17 0 0 0-24.7-.5l-86.4 83.3a15 15 0 0 0 9.2 26.9h19.3v-4.7l-13.7-12.7v-.1l83.7-80.8 84.2 81-13.9 12.8v4.5h19.5a15 15 0 0 0 7.4-28.1Z"
39
+ style="fill:url(#lg3)" />
40
+ <circle cx="167.1" cy="143.9" r="11.8" style="fill:url(#lg5)" />
41
+ <path fill="#fff" d="M219 89.3V35.5a10.5 10.5 0 1 0-21 0v33.7l21 20Z" />
42
+ <path
43
+ d="M91.4 73.3H83a37 37 0 0 0-14.5-28.4L65 50.2c.1 0 12.6 9 11.7 25.4-5.3-.4-11.2-1.9-16.3-5.3-11.8-7.8-16-23.7-11.9-46 8.7 1.5 34 7 43 22.8 4.1 7.3 4.1 16.1 0 26.2Z"
44
+ style="fill:url(#lg6)" />
45
+ <path
46
+ d="M65.9 80a49.6 49.6 0 0 0 17.8 2.2l16.6-16c1.6-8.3.5-15.7-3.3-22.4C84.6 22 47.8 17.5 46.2 17.4l-3-.4-.6 3c-3.8 18.4-5.9 50.6 23.2 60ZM48.4 24.4c8.7 1.5 34 7 43 22.8 4.1 7.3 4.1 16.1 0 26.2H83a37 37 0 0 0-14.5-28.4l-3.7 5.3c.1 0 12.6 9 11.7 25.4-5.3-.4-11.2-1.9-16.3-5.3-11.9-7.8-16-23.7-11.9-46Z"
47
+ fill="#1e5857" />
48
+ <path fill="#fff"
49
+ d="M250.5 90.5a17.4 17.4 0 1 1 0-34.8 17.4 17.4 0 0 1 0 34.8Zm0-22.7a5.4 5.4 0 0 0 0 10.7 5.3 5.3 0 0 0 0-10.7ZM258.8 148.2a15.9 15.9 0 0 0-9.6 28.5c-.8 4.2-5.4 4.6-5.4 4.6h-26v-43l13.6-13-1.8-2-82.2-79-81.2 78.3-2.5 2.6 13.7 13v42.9H53a21.5 21.5 0 1 0 11.7 15h12.6v18.8c0 7.8 6.4 14.1 14.1 14.1h29.3v14.8H64a10.6 10.6 0 0 0-17.7 8 10.6 10.6 0 0 0 17.6 8h157.6a16.3 16.3 0 1 0 0-16h-84.8V229h66.8c7.8 0 14.2-6.3 14.2-14.1v-19.2h27.6c14.3 0 17.8-12.8 18.5-16.6a15.9 15.9 0 0 0-5-30.9ZM43.7 210.8a10.3 10.3 0 1 1 0-20.6 10.3 10.3 0 0 1 0 20.6Zm192 36a5 5 0 1 1 0 10 5 5 0 0 1 0-10Zm-77-34.8h-22v-34h22v34Zm8.4-79.8c2.7 0 5.2 1 7.2 2.5v-10.4L188 137s2.6 1.3 4.6 1.3h6.7v68c0 3.2-2.6 5.7-5.7 5.7h-19v-34h1.4a7.5 7.5 0 0 0 0-15H120a7.5 7.5 0 0 0 0 15h.7v34h-19.3a5.7 5.7 0 0 1-5.7-5.6v-68.1h6.7c2 0 4.6-1.3 4.6-1.3l13.7-12.7v10.4a11.7 11.7 0 0 1 16 1.6v-13a14.9 14.9 0 0 0-25-10.8s-.1.2-.1.2l-.5.5-6.9 7H92.5l55-53.2 55.1 53.2h-11.8l-7-7c0-.2-.2-.3-.4-.5l-.2-.2a14.8 14.8 0 0 0-25 10.9v12.9c2.2-2.5 5.3-4.1 8.9-4.1Zm91.7 36.7a4.9 4.9 0 1 1 0-9.7 4.9 4.9 0 0 1 0 9.7Z" />
50
+ </svg>