UNPKG

signalk-tides

Version:

Tidal predictions for the vessel's position from various online sources.

154 lines (153 loc) 6.38 kB
"use strict"; /* * Copyright 2017 Scott Bender <scott@scottbender.net> and Joachim Bakke * Copyright 2025 Brandon Keepers <brandon@opensoul.org> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; const noaa_js_1 = __importDefault(require("./sources/noaa.js")); const stormglass_js_1 = __importDefault(require("./sources/stormglass.js")); const worldtides_js_1 = __importDefault(require("./sources/worldtides.js")); module.exports = function (app) { // Interval to update tide data const defaultPeriod = 60; // 1 hour let unsubscribes = []; const sources = [ (0, noaa_js_1.default)(app), (0, stormglass_js_1.default)(app), (0, worldtides_js_1.default)(app), ]; const plugin = { id: "tides", name: "Tides", // @ts-expect-error: TODO[TS]: fix Plugin type upstream description: "Tidal predictions for the vessel's position from various online sources.", schema: () => ({ title: "Tides API", type: "object", properties: { source: { title: "Data source", type: "string", "anyOf": sources.map(({ id, title }) => ({ const: id, title })), default: sources[0].id, }, // Update plugin schema with sources ...sources.reduce((properties, source) => Object.assign(properties, source.properties ?? {}), {}), period: { title: "Update frequency", type: "number", description: "How often to update tide data (minutes)", default: 60, minimum: 1, }, } }), stop() { unsubscribes.forEach((f) => f()); unsubscribes = []; } }; plugin.start = async function (props) { app.debug("Starting tides-api: " + JSON.stringify(props)); // Use the selected source, or the first one if not specified const source = sources.find(source => source.id === props.source) || sources[0]; // Load the selected source const provider = await source.start(props); // Register the source as a resource provider app.registerResourceProvider({ type: "tides", methods: { async listResources(query) { return provider(query); }, getResource() { throw new Error("Not implemented"); }, setResource() { throw new Error("Not implemented"); }, deleteResource() { throw new Error("Not implemented"); } } }); app.subscriptionmanager.subscribe({ context: "vessels." + app.selfId, subscribe: [ { path: "navigation.position", period: (props.period ?? defaultPeriod) * 60 * 1000, policy: "fixed", }, ], }, unsubscribes, (subscriptionError) => { app.error("Error:" + subscriptionError); }, (delta) => { // @ts-expect-error: TODO[TS]: fix Delta type upstream delta.updates.forEach(({ values }) => { // @ts-expect-error: TODO[TS]: fix Delta type upstream values.forEach(({ path }) => { if (path === "navigation.position") { performUpdate(); } }); }); }); async function performUpdate() { try { const { extremes } = await provider(); // Use server date, or current date if not available const now = new Date(app.getSelfPath("navigation.datetime.value") ?? Date.now()); const nextTide = {}; extremes.forEach(({ type, value, time }) => { // Get the first tide of this type after now if (!nextTide[type] && new Date(time) > now) { nextTide[type] = { time, value }; } }); const delta = { context: "vessels." + app.selfId, updates: [ { timestamp: now.toISOString(), values: Object.entries(nextTide).flatMap(([type, { time, value }]) => { return [ { path: `environment.tide.height${type}`, value: value }, { path: `environment.tide.time${type}`, value: time }, ]; }), }, ], }; app.debug("Sending delta: " + JSON.stringify(delta)); app.handleMessage(plugin.id, delta); app.setPluginStatus("Updated tide data"); } catch (e) { // eslint-disable-next-line @typescript-eslint/no-explicit-any app.setPluginError(e.message); // @ts-expect-error: TODO[TS] this accepts more than just a string: https://github.com/bkeepers/signalk-server/blob/d6845ee1f915e6b729d66d2b08b15dc2e0da8e51/src/interfaces/plugins.ts#L517-L519 app.error(e); } } // Perform initial update on startup performUpdate(); }; return plugin; };