@signalk/freeboard-sk 2.2.0 → 2.2.2

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
@@ -1,5 +1,17 @@
1
1
  # CHANGELOG: Freeboard
2
2
 
3
+ ### v2.2.2
4
+
5
+ - **Fixed**: Update Anchor Watch to use `signalk-anchor-alarm` REST API to resolve incorrect state being displayed.
6
+ - **Fixed**: Switching to fixed location did not update map until a delta update had been parsed.
7
+
8
+ ### v2.2.1
9
+
10
+ - **Fixed**: Issue where waypoint was not centered on the screen when the center waypoint button was clicked from the entry in the waypoint list.
11
+
12
+ - **Fixed**: Issue where invalid fixed location postion was being written to settings.
13
+
14
+
3
15
  ### v2.2.0
4
16
 
5
17
  - **Added**: Ability to center a vessel in the Vessels List.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalk/freeboard-sk",
3
- "version": "2.2.0",
3
+ "version": "2.2.2",
4
4
  "description": "Openlayers chart plotter implementation for Signal K",
5
5
  "keywords": [
6
6
  "signalk-webapp",
@@ -20,9 +20,7 @@
20
20
  "build": "ng build",
21
21
  "test": "ng test",
22
22
  "format": "prettier --ignore-path .gitignore --write \"src/**/*.+(ts|html)\"",
23
- "format:projects": "prettier --ignore-path .gitignore --write \"projects/**/*.+(ts|html)\"",
24
23
  "format:helper": "prettier --ignore-path .gitignore --write \"helper/**/*.+(ts|html)\"",
25
- "e2e": "ng e2e",
26
24
  "build:helper": "tsc -p tsconfig-helper.json",
27
25
  "build:web": "ng build -c production --output-hashing all",
28
26
  "build:all": "npm run build:helper && npm run build:web",
@@ -44,19 +42,19 @@
44
42
  "tslib": "^2.0.0"
45
43
  },
46
44
  "devDependencies": {
47
- "@angular-devkit/build-angular": "^14.0.1",
48
- "@angular/animations": "^14.0.1",
49
- "@angular/cdk": "^14.0.1",
50
- "@angular/cli": "^14.0.1",
51
- "@angular/common": "^14.0.1",
52
- "@angular/compiler": "^14.0.1",
53
- "@angular/compiler-cli": "^14.0.1",
54
- "@angular/core": "^14.0.1",
55
- "@angular/forms": "^14.0.1",
56
- "@angular/language-service": "^14.0.1",
57
- "@angular/material": "^14.0.1",
58
- "@angular/platform-browser": "^14.0.1",
59
- "@angular/platform-browser-dynamic": "^14.0.1",
45
+ "@angular-devkit/build-angular": "^16.2.0",
46
+ "@angular/animations": "^16.2.0",
47
+ "@angular/cdk": "^16.2.0",
48
+ "@angular/cli": "^16.2.0",
49
+ "@angular/common": "^16.2.0",
50
+ "@angular/compiler": "^16.2.0",
51
+ "@angular/compiler-cli": "^16.2.0",
52
+ "@angular/core": "^16.2.0",
53
+ "@angular/forms": "^16.2.0",
54
+ "@angular/language-service": "^16.2.0",
55
+ "@angular/material": "^16.2.0",
56
+ "@angular/platform-browser": "^16.2.0",
57
+ "@angular/platform-browser-dynamic": "^16.2.0",
60
58
  "@kolkov/angular-editor": "^2.1.0",
61
59
  "@signalk/server-api": "^2.0.0",
62
60
  "@types/arcgis-rest-api": "^10.4.5",
@@ -78,7 +76,7 @@
78
76
  "karma-coverage-istanbul-reporter": "~3.0.2",
79
77
  "karma-jasmine": "~4.0.0",
80
78
  "karma-jasmine-html-reporter": "^1.5.0",
81
- "ng-packagr": "^14.0.2",
79
+ "ng-packagr": "^16.2.0",
82
80
  "ngeohash": "^0.6.3",
83
81
  "ol": "^7.4.0",
84
82
  "pmtiles": "^2.7.0",
@@ -91,7 +89,7 @@
91
89
  "signalk-worker-angular": "^1.1.4",
92
90
  "simplify-ts": "^1.0.2",
93
91
  "ts-node": "~7.0.0",
94
- "typescript": "~4.7.3",
95
- "zone.js": "~0.11.4"
92
+ "typescript": "~4.9.5",
93
+ "zone.js": "~0.13.1"
96
94
  }
97
- }
95
+ }
@@ -77,7 +77,6 @@ const initAlarmEndpoints = () => {
77
77
  });
78
78
  };
79
79
  const handlePutAlarmState = (context, path, value) => {
80
- var _a, _b;
81
80
  server.debug(context);
82
81
  server.debug(path);
83
82
  server.debug(JSON.stringify(value));
@@ -95,7 +94,7 @@ const handlePutAlarmState = (context, path, value) => {
95
94
  server.debug(JSON.stringify(alarmType));
96
95
  let noti;
97
96
  if (value) {
98
- noti = new types_1.Notification(alarmType, buildAlarmMessage(value.message, alarmType), (_a = value.state) !== null && _a !== void 0 ? _a : null, (_b = value.method) !== null && _b !== void 0 ? _b : null);
97
+ noti = new types_1.Notification(alarmType, buildAlarmMessage(value.message, alarmType), value.state ?? null, value.method ?? null);
99
98
  }
100
99
  else {
101
100
  noti = {
@@ -123,7 +122,7 @@ const buildAlarmMessage = (message, alarmType) => {
123
122
  if (['mob', 'sinking'].includes(alarmType)) {
124
123
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
124
  const pos = server.getSelfPath('navigation.position');
126
- msgAttrib = pos ? JSON.stringify(pos === null || pos === void 0 ? void 0 : pos.value) : '';
125
+ msgAttrib = pos ? JSON.stringify(pos?.value) : '';
127
126
  }
128
127
  return `${message}\n\r${msgAttrib}`;
129
128
  };
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initAnchorApi = void 0;
4
+ const fetch_1 = require("../lib/fetch");
5
+ let server;
6
+ let hostPath;
7
+ const apiBasePath = '/signalk/v2/api/vessels/self/navigation/anchor';
8
+ const anchorPlugin = {
9
+ has: false,
10
+ enabled: false,
11
+ version: ''
12
+ };
13
+ let pluginPath;
14
+ const msgPluginNotFound = 'signalk-anchor-alarm is not installed!';
15
+ const initAnchorApi = async (app) => {
16
+ server = app;
17
+ server.debug(`** initAnchorApi() **`);
18
+ // detect signalk-anchor-alarm plugin
19
+ let port = 3000;
20
+ if (typeof server.config?.getExternalPort === 'function') {
21
+ server.debug('*** getExternalPort()', server.config.getExternalPort());
22
+ port = server.config.getExternalPort();
23
+ }
24
+ hostPath = `${server.config.ssl ? 'https' : 'http'}://localhost:${port}`;
25
+ const url = `${hostPath}/plugins`;
26
+ const r = await (0, fetch_1.fetch)(url);
27
+ r.forEach((plugin) => {
28
+ if (plugin.id === 'anchoralarm') {
29
+ pluginPath = `/plugins/${plugin.id}`;
30
+ anchorPlugin.has = true;
31
+ anchorPlugin.version = plugin.version;
32
+ anchorPlugin.enabled = plugin.data.enabled;
33
+ }
34
+ });
35
+ server.debug('*** Anchor Alarm Plugin detected:', anchorPlugin.has);
36
+ server.debug('*** Anchor Alarm Plugin enabled:', anchorPlugin.enabled);
37
+ server.debug('*** Anchor Alarm Plugin API Path', `${hostPath}${pluginPath}`);
38
+ if (anchorPlugin.has) {
39
+ initApiEndpoints();
40
+ }
41
+ };
42
+ exports.initAnchorApi = initAnchorApi;
43
+ const initApiEndpoints = () => {
44
+ server.debug(`** Registering Anchor API endpoint(s) **`);
45
+ server.post(`${apiBasePath}/drop`, async (req, res) => {
46
+ server.debug(`** POST ${apiBasePath}/drop`);
47
+ if (!anchorPlugin.has) {
48
+ res.status(400).json({
49
+ state: 'COMPLETED',
50
+ statusCode: 400,
51
+ message: msgPluginNotFound
52
+ });
53
+ return;
54
+ }
55
+ try {
56
+ const r = await (0, fetch_1.post)(`${hostPath}${pluginPath}/dropAnchor`, '{}');
57
+ res.status(r.statusCode).json(r);
58
+ }
59
+ catch (e) {
60
+ res.status(e.statusCode).json(e);
61
+ }
62
+ });
63
+ server.post(`${apiBasePath}/raise`, async (req, res) => {
64
+ server.debug(`** POST ${apiBasePath}/raise`);
65
+ if (!anchorPlugin.has) {
66
+ res.status(400).json({
67
+ state: 'COMPLETED',
68
+ statusCode: 400,
69
+ message: msgPluginNotFound
70
+ });
71
+ return;
72
+ }
73
+ try {
74
+ const r = await (0, fetch_1.post)(`${hostPath}${pluginPath}/raiseAnchor`, '{}');
75
+ res.status(r.statusCode).json(r);
76
+ }
77
+ catch (e) {
78
+ res.status(e.statusCode).json(e);
79
+ }
80
+ });
81
+ server.post(`${apiBasePath}/radius`, async (req, res) => {
82
+ server.debug(`** POST ${apiBasePath}/radius`);
83
+ if (!anchorPlugin.has) {
84
+ res.status(400).json({
85
+ state: 'COMPLETED',
86
+ statusCode: 400,
87
+ message: msgPluginNotFound
88
+ });
89
+ return;
90
+ }
91
+ try {
92
+ const val = req.body.value && typeof req.body.value === 'number'
93
+ ? { radius: req.body.value }
94
+ : {};
95
+ const r = await (0, fetch_1.post)(`${hostPath}${pluginPath}/setRadius`, JSON.stringify(val));
96
+ res.status(r.statusCode).json(r);
97
+ }
98
+ catch (e) {
99
+ res.status(e.statusCode).json(e);
100
+ }
101
+ });
102
+ };
package/plugin/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const alarms_1 = require("./alarms/alarms");
4
+ const anchor_api_1 = require("./anchor/anchor-api");
4
5
  const weather_1 = require("./weather");
5
6
  const pypilot_1 = require("./pypilot");
6
7
  const openapi = require("./openApi.json");
@@ -122,32 +123,31 @@ module.exports = (server) => {
122
123
  };
123
124
  // ************************************
124
125
  const doStartup = (options) => {
125
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
126
126
  try {
127
127
  server.debug(`${plugin.name} starting.......`);
128
128
  if (typeof options !== 'undefined') {
129
129
  settings = options;
130
130
  }
131
- settings.weather = (_a = options.weather) !== null && _a !== void 0 ? _a : {
131
+ settings.weather = options.weather ?? {
132
132
  enable: false,
133
133
  apiKey: '',
134
134
  service: 'openweather'
135
135
  };
136
- settings.weather.enable = (_b = options.weather.enable) !== null && _b !== void 0 ? _b : false;
137
- settings.weather.apiKey = (_c = options.weather.apiKey) !== null && _c !== void 0 ? _c : '';
138
- settings.weather.service = (_d = options.weather.service) !== null && _d !== void 0 ? _d : 'openweather';
139
- settings.alarms = (_e = options.alarms) !== null && _e !== void 0 ? _e : {
136
+ settings.weather.enable = options.weather.enable ?? false;
137
+ settings.weather.apiKey = options.weather.apiKey ?? '';
138
+ settings.weather.service = options.weather.service ?? 'openweather';
139
+ settings.alarms = options.alarms ?? {
140
140
  enable: true
141
141
  };
142
- settings.alarms.enable = (_f = options.alarms.enable) !== null && _f !== void 0 ? _f : true;
143
- settings.pypilot = (_g = options.pypilot) !== null && _g !== void 0 ? _g : {
142
+ settings.alarms.enable = options.alarms.enable ?? true;
143
+ settings.pypilot = options.pypilot ?? {
144
144
  enable: false,
145
145
  host: 'localhost',
146
146
  port: 8000
147
147
  };
148
- settings.pypilot.enable = (_h = options.pypilot.enable) !== null && _h !== void 0 ? _h : false;
149
- settings.pypilot.host = (_j = options.pypilot.host) !== null && _j !== void 0 ? _j : 'localhost';
150
- settings.pypilot.port = (_k = options.pypilot.port) !== null && _k !== void 0 ? _k : 8000;
148
+ settings.pypilot.enable = options.pypilot.enable ?? false;
149
+ settings.pypilot.host = options.pypilot.host ?? 'localhost';
150
+ settings.pypilot.port = options.pypilot.port ?? 8000;
151
151
  server.debug(`Applied config: ${JSON.stringify(settings)}`);
152
152
  if (settings.alarms.enable) {
153
153
  (0, alarms_1.initAlarms)(server, plugin.id);
@@ -163,6 +163,8 @@ module.exports = (server) => {
163
163
  if (settings.pypilot.enable) {
164
164
  (0, pypilot_1.initPyPilot)(server, plugin.id, settings.pypilot);
165
165
  }
166
+ // Anchor API facade
167
+ (0, anchor_api_1.initAnchorApi)(server);
166
168
  server.setPluginStatus(msg);
167
169
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
168
170
  }
@@ -1,13 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fetch = void 0;
3
+ exports.post = exports.fetch = void 0;
4
4
  const https = require("https");
5
5
  const url = require("url");
6
+ const http = require("http");
7
+ // HTTP GET
6
8
  const fetch = (href) => {
7
9
  const opt = url.parse(href);
8
10
  opt.headers = { 'User-Agent': 'Mozilla/5.0' };
11
+ const req = href.indexOf('https') !== -1 ? https : http;
9
12
  return new Promise((resolve, reject) => {
10
- https
13
+ req
11
14
  .get(opt, (res) => {
12
15
  let data = '';
13
16
  res.on('data', (chunk) => {
@@ -29,3 +32,46 @@ const fetch = (href) => {
29
32
  });
30
33
  };
31
34
  exports.fetch = fetch;
35
+ // HTTP POST
36
+ const post = (href, data) => {
37
+ const opt = url.parse(href);
38
+ (opt.method = 'POST'),
39
+ (opt.headers = {
40
+ 'Content-Type': 'application/json'
41
+ });
42
+ const req = href.indexOf('https') !== -1 ? https : http;
43
+ return new Promise((resolve, reject) => {
44
+ const postReq = req
45
+ .request(opt, (res) => {
46
+ let resText = '';
47
+ res.on('data', (chunk) => {
48
+ resText += chunk;
49
+ });
50
+ res.on('end', () => {
51
+ if (Math.floor(res.statusCode / 100) === 2) {
52
+ resolve({
53
+ statusCode: res.statusCode,
54
+ state: 'COMPLETED'
55
+ });
56
+ }
57
+ else {
58
+ reject({
59
+ statusCode: res.statusCode,
60
+ state: 'FAILED',
61
+ message: resText
62
+ });
63
+ }
64
+ });
65
+ })
66
+ .on('error', (error) => {
67
+ reject({
68
+ statusCode: 400,
69
+ state: 'FAILED',
70
+ message: error.message
71
+ });
72
+ });
73
+ postReq.write(data);
74
+ postReq.end();
75
+ });
76
+ };
77
+ exports.post = post;
@@ -2,7 +2,6 @@
2
2
  // NOAA
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.NOAA = void 0;
5
- const tslib_1 = require("tslib");
6
5
  const fetch_1 = require("./fetch");
7
6
  var CARDINAL_POINTS;
8
7
  (function (CARDINAL_POINTS) {
@@ -24,55 +23,8 @@ var CARDINAL_POINTS;
24
23
  CARDINAL_POINTS[CARDINAL_POINTS["NNW"] = 337.5] = "NNW";
25
24
  })(CARDINAL_POINTS || (CARDINAL_POINTS = {}));
26
25
  class NOAA {
26
+ settings;
27
27
  constructor(config) {
28
- this.fetchData = (position) => tslib_1.__awaiter(this, void 0, void 0, function* () {
29
- var _a, _b, _c, _d, _e, _f;
30
- const url = this.getUrl(position);
31
- try {
32
- //console.log(`url`, url)
33
- const response = yield (0, fetch_1.fetch)(url);
34
- let forecasts = [];
35
- let observations = [];
36
- // observations
37
- if ((_a = response === null || response === void 0 ? void 0 : response.properties) === null || _a === void 0 ? void 0 : _a.observationStations) {
38
- const stations = yield (0, fetch_1.fetch)(response.properties.observationStations);
39
- observations = yield (0, fetch_1.fetch)(`${stations.features[0].id}/observations/latest`);
40
- //console.log(`observations`, observations)
41
- }
42
- // forecasts
43
- if ((_b = response === null || response === void 0 ? void 0 : response.properties) === null || _b === void 0 ? void 0 : _b.forecastHourly) {
44
- forecasts = yield (0, fetch_1.fetch)(response.properties.forecastHourly);
45
- //console.log(`forecasts`, forecasts)
46
- }
47
- // warnings
48
- const warnings = yield (0, fetch_1.fetch)(`https://api.weather.gov/alerts/active?point=${position.latitude.toFixed(4)},${position.longitude.toFixed(4)}`);
49
- //console.log(`warnings`, warnings)
50
- return this.parseResponse({
51
- position: position,
52
- forecasts: (_d = (_c = forecasts === null || forecasts === void 0 ? void 0 : forecasts.properties) === null || _c === void 0 ? void 0 : _c.periods) !== null && _d !== void 0 ? _d : [],
53
- observations: (_e = observations === null || observations === void 0 ? void 0 : observations.properties) !== null && _e !== void 0 ? _e : null,
54
- warnings: (_f = warnings === null || warnings === void 0 ? void 0 : warnings.features) !== null && _f !== void 0 ? _f : []
55
- });
56
- }
57
- catch (error) {
58
- throw error;
59
- }
60
- });
61
- this.parseResponse = (wData) => {
62
- return {
63
- self: {
64
- id: 'self',
65
- name: 'Weather data relative to supplied position.',
66
- position: {
67
- latitude: wData.latitude,
68
- longitude: wData.longitude
69
- },
70
- observations: this.parseNoaaObservations(wData.observations),
71
- forecasts: this.parseNoaaForecasts(wData.forecasts),
72
- warnings: this.parseNoaaWarnings(wData.warnings)
73
- }
74
- };
75
- };
76
28
  this.settings = config;
77
29
  }
78
30
  getUrl(position) {
@@ -84,14 +36,60 @@ class NOAA {
84
36
  return `${api}/${position.latitude.toFixed(4)},${position.longitude.toFixed(4)}`;
85
37
  }
86
38
  }
39
+ fetchData = async (position) => {
40
+ const url = this.getUrl(position);
41
+ try {
42
+ //console.log(`url`, url)
43
+ const response = await (0, fetch_1.fetch)(url);
44
+ let forecasts = [];
45
+ let observations = [];
46
+ // observations
47
+ if (response?.properties?.observationStations) {
48
+ const stations = await (0, fetch_1.fetch)(response.properties.observationStations);
49
+ observations = await (0, fetch_1.fetch)(`${stations.features[0].id}/observations/latest`);
50
+ //console.log(`observations`, observations)
51
+ }
52
+ // forecasts
53
+ if (response?.properties?.forecastHourly) {
54
+ forecasts = await (0, fetch_1.fetch)(response.properties.forecastHourly);
55
+ //console.log(`forecasts`, forecasts)
56
+ }
57
+ // warnings
58
+ const warnings = await (0, fetch_1.fetch)(`https://api.weather.gov/alerts/active?point=${position.latitude.toFixed(4)},${position.longitude.toFixed(4)}`);
59
+ //console.log(`warnings`, warnings)
60
+ return this.parseResponse({
61
+ position: position,
62
+ forecasts: forecasts?.properties?.periods ?? [],
63
+ observations: observations?.properties ?? null,
64
+ warnings: warnings?.features ?? []
65
+ });
66
+ }
67
+ catch (error) {
68
+ throw error;
69
+ }
70
+ };
71
+ parseResponse = (wData) => {
72
+ return {
73
+ self: {
74
+ id: 'self',
75
+ name: 'Weather data relative to supplied position.',
76
+ position: {
77
+ latitude: wData.latitude,
78
+ longitude: wData.longitude
79
+ },
80
+ observations: this.parseNoaaObservations(wData.observations),
81
+ forecasts: this.parseNoaaForecasts(wData.forecasts),
82
+ warnings: this.parseNoaaWarnings(wData.warnings)
83
+ }
84
+ };
85
+ };
87
86
  parseNoaaObservations(wData) {
88
- var _a, _b;
89
87
  const data = [];
90
88
  const obs = {};
91
89
  let v;
92
90
  if (wData) {
93
- obs.timestamp = (_a = wData.timestamp) !== null && _a !== void 0 ? _a : null;
94
- obs.description = (_b = wData.textDescription) !== null && _b !== void 0 ? _b : null;
91
+ obs.timestamp = wData.timestamp ?? null;
92
+ obs.description = wData.textDescription ?? null;
95
93
  v = wData.visibility.value
96
94
  ? wData.visibility.unitCode === 'wmoUnit:m'
97
95
  ? wData.visibility.value
@@ -176,10 +174,9 @@ class NOAA {
176
174
  const data = [];
177
175
  if (forecasts && Array.isArray(forecasts)) {
178
176
  forecasts.forEach((f) => {
179
- var _a, _b, _c;
180
177
  const forecast = {};
181
- forecast.timestamp = (_a = f.startTime) !== null && _a !== void 0 ? _a : null;
182
- forecast.description = (_b = f.shortForecast) !== null && _b !== void 0 ? _b : null;
178
+ forecast.timestamp = f.startTime ?? null;
179
+ forecast.description = f.shortForecast ?? null;
183
180
  forecast.temperature = {};
184
181
  forecast.temperature.air = {
185
182
  value: typeof f.temperature !== 'undefined'
@@ -189,7 +186,7 @@ class NOAA {
189
186
  };
190
187
  forecast.wind = {};
191
188
  forecast.wind.speed = {
192
- value: (_c = parseInt(f.windSpeed.split(' ')[0]) / 2.237) !== null && _c !== void 0 ? _c : null,
189
+ value: parseInt(f.windSpeed.split(' ')[0]) / 2.237 ?? null,
193
190
  units: 'm/s'
194
191
  };
195
192
  const wd = f.windDirection
@@ -208,13 +205,12 @@ class NOAA {
208
205
  const data = [];
209
206
  if (alerts && Array.isArray(alerts)) {
210
207
  alerts.forEach((alert) => {
211
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
212
208
  const warn = {
213
- startTime: (_b = (_a = alert === null || alert === void 0 ? void 0 : alert.properties) === null || _a === void 0 ? void 0 : _a.effective) !== null && _b !== void 0 ? _b : null,
214
- endTime: (_d = (_c = alert === null || alert === void 0 ? void 0 : alert.properties) === null || _c === void 0 ? void 0 : _c.ends) !== null && _d !== void 0 ? _d : null,
215
- details: (_f = (_e = alert === null || alert === void 0 ? void 0 : alert.properties) === null || _e === void 0 ? void 0 : _e.description) !== null && _f !== void 0 ? _f : null,
216
- source: (_h = (_g = alert === null || alert === void 0 ? void 0 : alert.properties) === null || _g === void 0 ? void 0 : _g.senderName) !== null && _h !== void 0 ? _h : null,
217
- type: (_k = (_j = alert === null || alert === void 0 ? void 0 : alert.properties) === null || _j === void 0 ? void 0 : _j.messageType) !== null && _k !== void 0 ? _k : null
209
+ startTime: alert?.properties?.effective ?? null,
210
+ endTime: alert?.properties?.ends ?? null,
211
+ details: alert?.properties?.description ?? null,
212
+ source: alert?.properties?.senderName ?? null,
213
+ type: alert?.properties?.messageType ?? null
218
214
  };
219
215
  data.push(warn);
220
216
  });