├── .babelrc ├── .editorconfig ├── .github └── workflows │ └── npnm_test.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── CONTRIBUTING.md ├── LICENSE ├── NMEA_SignalK_recognition.pdf ├── README.md ├── REFERENCE.md ├── bin └── validate.js ├── mdbook ├── .gitignore ├── book.toml ├── docker-compose.yml └── src │ ├── README.md │ ├── SUMMARY.md │ ├── access_requests.md │ ├── apps_api.md │ ├── changelog.md │ ├── connection.md │ ├── consumer_discovery_process.dia │ ├── consumer_discovery_process.png │ ├── data_model.md │ ├── data_model_metadata.md │ ├── data_model_multiple_values.md │ ├── gateway_and_server.dia │ ├── gateway_and_server.png │ ├── get.md │ ├── how_to_help.md │ ├── keys.md │ ├── notifications.md │ ├── put.md │ ├── request_response.md │ ├── rest_api.md │ ├── security.md │ ├── server_only.dia │ ├── server_only.png │ ├── sources.md │ ├── start_developing.md │ ├── start_using.md │ ├── streaming_api.md │ ├── subscription_protocol.md │ ├── urls_ports.md │ └── versioning.md ├── package.json ├── samples ├── delta │ ├── 0183-RMC-export-delta.json │ ├── 0183-RMC-export-min-delta.json │ ├── MOB-alarm-delta.json │ ├── docs-data_model.json │ ├── docs-data_model_meta_deltas.json │ ├── docs-data_model_multiple_values.json │ ├── docs-notifications.json │ └── docs-subscription_protocol.json ├── discovery │ ├── discovery.json │ └── docs-rest_api.json ├── full │ ├── 0183-RMC-export-min.json │ ├── 0183-RMC-export.json │ ├── 0183-RMC-full.json │ ├── docs-data_model.json │ ├── docs-data_model_metadata.json │ ├── docs-data_model_multiple_values.json │ ├── docs-notifications.json │ ├── electrical-ac-full.json │ ├── mob-alarm.json │ ├── signalk-depth-alarm.json │ ├── signalk-depth-meta-attr.json │ └── vessel-time.json ├── hello │ ├── docs-hello-minimal.json │ ├── docs-hello-playback.json │ └── docs-hello.json ├── put │ ├── put-with-context.json │ └── put.json ├── subscribe │ ├── docs-subscription_protocol1.json │ ├── docs-subscription_protocol2.json │ ├── docs-subscription_protocol3.json │ ├── docs-subscription_protocol4.json │ ├── signalk-subscribe-mqtt.json │ └── signalk-subscribe.json └── unsubscribe │ ├── docs-subscription_protocol.json │ ├── signalk-unsubscribe-mqtt.json │ └── signalk-unsubscribe.json ├── schemas ├── aircraft.json ├── aton.json ├── definitions.json ├── delta.json ├── discovery.json ├── external │ └── geojson │ │ ├── README.txt │ │ ├── bbox.json │ │ ├── crs.json │ │ ├── geojson.json │ │ └── geometry.json ├── groups │ ├── communication.json │ ├── design.json │ ├── electrical.json │ ├── environment.json │ ├── navigation.json │ ├── notifications.json │ ├── performance.json │ ├── propulsion.json │ ├── resources.json │ ├── sails.json │ ├── sensors.json │ ├── sources.json │ ├── steering.json │ └── tanks.json ├── hello.json ├── messages │ ├── auth.json │ ├── get.json │ ├── put.json │ ├── subscribe.json │ └── unsubscribe.json ├── sar.json ├── signalk.json └── vessel.json ├── scripts └── processSchemaFiles.js ├── src ├── delta.js ├── fullsignalk.js └── index.js ├── test ├── data │ ├── auth-invalid │ │ ├── login-request-no-pass.json │ │ ├── login-request-no-requestid.json │ │ ├── login-request-no-username.json │ │ ├── login-response-no-token.json │ │ └── validate-logout-request.json │ ├── auth-valid │ │ ├── login-request.json │ │ ├── login-response.json │ │ ├── logout-request.json │ │ ├── validate-request.json │ │ └── validate-response.json │ ├── delta-invalid │ │ ├── delta-empty_object.json │ │ ├── delta-extra_property.json │ │ ├── delta-update-with-get.json │ │ ├── delta-update-with-put-no-requestId.json │ │ ├── meta-missing-value.json │ │ ├── sources-bad_1.json │ │ ├── sources-bad_2.json │ │ ├── sources-bad_3.json │ │ ├── sources-bad_4.json │ │ ├── value-path_missing.json │ │ ├── value-value_and_path_missing.json │ │ └── value-value_missing.json │ ├── delta-valid │ │ ├── delta-object_with_only_an_empty_updates_validates.json │ │ ├── delta-simple.json │ │ ├── delta-without_context_source_and_timestamp_is_valid.json │ │ ├── identity-blank_ie_self.json │ │ ├── identity-mmsi.json │ │ ├── identity-url.json │ │ ├── identity-uuid.json │ │ ├── meta-and-values.json │ │ ├── meta-delta.json │ │ ├── value-can_be_boolean.json │ │ ├── value-can_be_null.json │ │ ├── value-can_be_number.json │ │ └── value-can_be_object.json │ ├── discovery-invalid │ │ └── endpoints-version_missing.json │ ├── discovery-valid │ │ └── endpoints-sample.json │ ├── full-invalid │ │ ├── aircraft-mmsi_bad.json │ │ ├── ais-aisShipType_bad.json │ │ ├── ais-source0.json │ │ ├── ais-source28.json │ │ ├── alarms-must_not_have_additional_keys_in_notifications.json │ │ ├── alarms-must_not_have_dot_notation_in_branch_names.json │ │ ├── alarms-must_not_have_incorrectly_spelled_keys.json │ │ ├── alarms-must_not_have_spaces_in_branch_names.json │ │ ├── alarms-must_not_have_sub_trees_beneath_well_defined_notifications.json │ │ ├── alarms-must_not_use_well_defined_notifications_at_deep_levels.json │ │ ├── alarms-must_use_alarmMethod_array_with_enum.json │ │ ├── alarms-must_use_alarmStateEnum.json │ │ ├── alarms-must_use_array_of_states_not_string.json │ │ ├── aton-atonType_bad.json │ │ ├── aton-has_sails.json │ │ ├── aton-mmsi_bad.json │ │ ├── datetime-timezoneRegion_but_no_timezone.json │ │ ├── datetime-timezone_region_invalid.json │ │ ├── gnss-methodQuality_bad.json │ │ ├── gnss-type_bad.json │ │ ├── invalid-timestamp-ending-with-Z.json │ │ ├── invalid-timestamp-subtle.json │ │ ├── maneuver-bad.json │ │ ├── mothership-bad.json │ │ ├── nav-destination_bad.json │ │ ├── sar-mmsi_bad.json │ │ ├── sar-sails.json │ │ ├── sources-both_n2k_and_ais.json │ │ ├── sources-valid_sources_with_no_0183_n2k_or_ais_and_other_items.json │ │ ├── trip-key_bad.json │ │ ├── trip-value_is_a_string.json │ │ └── vessel-mmsi_bad.json │ ├── full-valid │ │ ├── aircraft-basic_nav.json │ │ ├── aircraft-home_base.json │ │ ├── ais-aisShipType.json │ │ ├── ais-full_cpa_tcpa.json │ │ ├── ais-source.json │ │ ├── ais.json │ │ ├── alarms-branches_with_names_similar_to_mob_etc.json │ │ ├── alarms-deep_keys.json │ │ ├── alarms-multiple_at_multiple_levels.json │ │ ├── alarms-shallow_keys.json │ │ ├── alarms-simple.json │ │ ├── aton-basic_position.json │ │ ├── aton-dimensions.json │ │ ├── course-full_tree.json │ │ ├── datetime-in_full_tree.json │ │ ├── datetime-timezone_in_environment.json │ │ ├── electrical-full_tree.json │ │ ├── environment-mode.json │ │ ├── gnss-sample.json │ │ ├── identities-various_formats.json │ │ ├── lux-N2K.json │ │ ├── lux-i2c.json │ │ ├── maneuver-sample.json │ │ ├── miscellaneous-sample.json │ │ ├── mothership-sample.json │ │ ├── nav-destination.json │ │ ├── racing-sample.json │ │ ├── registrations-sample.json │ │ ├── resources-with_position_ref.json │ │ ├── sar-notifications.json │ │ ├── sar-position.json │ │ ├── sources-multiple_sources_for_same_path.json │ │ ├── sources-sample.json │ │ ├── temperatures-sample.json │ │ ├── trip-sample.json │ │ └── vessel-basic_nav.json │ ├── get-invalid │ │ ├── delta-get-no-context.json │ │ └── delta-get-no-requestId.json │ ├── get-valid │ │ ├── delta-get-array.json │ │ └── delta-get-no-array.json │ ├── hello-invalid │ │ └── version_bad.json │ ├── hello-valid │ │ ├── master.json │ │ └── slave_minimal.json │ ├── put-invalid │ │ ├── delta-put-array-no-requestId.json │ │ └── delta-update-with-put.json │ ├── put-valid │ │ ├── delta-put-array.json │ │ ├── delta-put-no-array.json │ │ ├── delta-put-no-context.json │ │ └── put.json │ ├── subscribe-invalid │ │ └── subscribe-maxPeriod_invalid_property.json │ ├── subscribe-valid │ │ └── subscribe-sample.json │ ├── unsubscribe-invalid │ │ └── unsubscribe-context_missing.json │ ├── unsubscribe-valid │ │ └── unsubscribe-from_all.json │ ├── vessel-invalid │ │ ├── environment-inside_humidity_rather_than_relativeHumidity.json │ │ ├── environment-inside_invalid_property.json │ │ ├── environment-with_inside_heatIndex.json │ │ ├── meta-missing-description.json │ │ ├── meta-with-invalid-enum.json │ │ ├── meta-with_displayScale_and_additional_properties.json │ │ ├── meta-with_displayScale_and_invalid_power.json │ │ ├── meta-with_displayScale_lower_only.json │ │ ├── meta-with_displayScale_non_numeric_lower.json │ │ ├── meta-with_displayScale_non_numeric_upper.json │ │ ├── meta-with_displayScale_upper_only.json │ │ ├── sails-sample.json │ │ ├── vessel-no_id.json │ │ └── vessel-uuid_not_canonical.json │ └── vessel-valid │ │ ├── environment-sample.json │ │ ├── environment-with_inside_heatIndexTemperature.json │ │ ├── meta-missing-units.json │ │ ├── meta-with-enum.json │ │ ├── meta-with_displayScale.json │ │ ├── meta-with_displayScale_type.json │ │ ├── meta-with_displayScale_type_power.json │ │ ├── polar-data.json │ │ ├── propulsion-sample.json │ │ ├── sails-sample.json │ │ ├── steering-sample.json │ │ ├── tanks-sample.json │ │ ├── vessel-just_mmsi_identifier.json │ │ ├── vessel-just_uuid_identifier.json │ │ └── vessel-uuid_and_mmsi_identifiers.json ├── fullsignalk.js ├── jsonSamplesAndTestFiles.js ├── schema-api.js ├── schemaReferences.js └── sources.js └── v2-food-for-thought.md /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | max_line_length = 120 12 | -------------------------------------------------------------------------------- /.github/workflows/npnm_test.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Npm test 3 | 4 | on: [push] 5 | 6 | jobs: 7 | build: 8 | 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | matrix: 13 | node-version: [12.x, 16.x] 14 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Use Node.js ${{ matrix.node-version }} 19 | uses: actions/setup-node@v2 20 | with: 21 | node-version: ${{ matrix.node-version }} 22 | - run: npm install 23 | - run: npm test 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | _site 22 | 23 | # Dependency directory 24 | # Deployed apps should consider commenting this line out: 25 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 26 | node_modules 27 | 28 | # FS stuff 29 | .DS_Store 30 | thumbs.db 31 | 32 | # IDE Stuff 33 | *.iml 34 | *.swp 35 | *~ 36 | .vscode 37 | 38 | #Generated by publishing docs 39 | mdbook/src/otherBranches.md 40 | mdbook/src/vesselsBranch.md 41 | 42 | #Generated 43 | src/keyswithmetadata.json 44 | mdbook/[0-9]* 45 | dist 46 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # source files (transpiled under dist) 11 | src 12 | 13 | # samples 14 | samples 15 | 16 | # test 17 | test 18 | 19 | #scripts 20 | scripts 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | _site 25 | 26 | # Dependency directory 27 | # Deployed apps should consider commenting this line out: 28 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 29 | node_modules 30 | 31 | # FS stuff 32 | .DS_Store 33 | thumbs.db 34 | 35 | # IDE Stuff 36 | *.iml 37 | *.swp 38 | *~ 39 | .vscode 40 | 41 | #Gitbook source files, original and generated 42 | mdbook -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Some Guidelines to Contributing to the Signal K Specification 2 | 3 | ## RFC Format 4 | 5 | Any substantial changes should take the form of RFC. See the 6 | [very first RFC](https://github.com/SignalK/specification/issues/264) 7 | for an example. 8 | 9 | ## Include test data and tests 10 | 11 | See the [tests](https://github.com/SignalK/specification/tree/master/test) and 12 | [test sample data](https://github.com/SignalK/specification/tree/master/test/data). 13 | 14 | Any additions should include some test data that use the proposed changes with 15 | real world data. 16 | 17 | Any schema changes should be checked against the existing set of tests: 18 | - install Node (and npm with it) 19 | - install dependencies with `npm install` 20 | - run the tests with `npm test` 21 | 22 | ## Pull Request and Commit messages 23 | 24 | The Signal K change log is generated automatically from Pull Requests and Commits. In order to ensure consistency in the logs the following guide should be followed when submitting Pull Requests (and commits). 25 | 26 | #### Subject Line 27 | 28 | The subject line should be in the format `: ` 29 | 30 | `` should be one of: 31 | 32 | - feat (feature) 33 | - fix (bug fix) 34 | - docs (documentation) 35 | - style (formatting, missing semi colons, ...) 36 | - refactor 37 | - test (when adding missing tests) 38 | - chore (maintain) 39 | 40 | `` 41 | 42 | - use imperative, present tense: "change" not "changed" or "changes" 43 | - don't capitalise the first letter 44 | - no full stop (dot) at the end 45 | 46 | ##### Examples of good Subject Lines: 47 | 48 | `doc: clarify meta.units behaviour` 49 | 50 | `chore: update keyswithmetadat.json` 51 | 52 | `style: whitespace` 53 | 54 | `fix: allow nextPoint to be an intermediate leaf` 55 | 56 | `feature: push design object fields under value/values` 57 | 58 | #### Message body 59 | 60 | Again use imperative, present tense and include motivation for the change and differences to previous behaviour. 61 | 62 | #### Message Footer 63 | 64 | At the end of the message reference any issues. If the PR should close issue(s) (assuming it is committed), use closes/fixes or resolves and the issue number. eg. "closes #18", "fixes #21 and resolves #23". 65 | 66 | ### Flattening commits 67 | 68 | During development a number of commits may be made which should logically be flattened to a single commit before creating a Pull Request. This aids the understanding and readability of the work done. Multiple commits should be left in place where they cover logically different elements within the Pull Request. 69 | -------------------------------------------------------------------------------- /NMEA_SignalK_recognition.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SignalK/specification/1978f556d8790be2b3bfdc9fffcf82a9a913e3c2/NMEA_SignalK_recognition.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Signal K Specification 2 | ====================== 3 | See the [latest published version](http://signalk.org/specification/). 4 | 5 | This repository contains the (working) specification for Signal K, defined in 6 | JSON Schema files, tests for the schema files and assorted JavaScript utilities 7 | for working with Signal K delta and full JSON data as well as validation 8 | utilities. 9 | 10 | [![Build Status](https://travis-ci.org/SignalK/specification.svg?branch=master)](https://travis-ci.org/SignalK/specification) [![Slack Chat](https://img.shields.io/badge/Chat-Slack-ff69b4.svg "Join us and help develop Signal K. Anyone is welcome!")](http://slack-invite.signalk.org/) 11 | 12 | Signal K 13 | -------- 14 | 15 | The Free and Open Source universal marine data exchange 16 | 17 | Signal K is about publishing a common modern and open data format for marine 18 | use. A format for the modern boat, compatible with NMEA, friendly to WiFi, 19 | cellphones, tablets, and the Internet. A format available to everyone, where 20 | anyone can contribute. 21 | 22 | Find out more at [signalk.org](http://signalk.org). Then join the mailinglist 23 | by sending an email to signalk+subscribe@googlegroups.com or follow the 24 | discussion via the Signal K Google Groups forum. 25 | 26 | Version 2 27 | -------- 28 | We have started [gathering thoughts as well as actual work on version 2](https://github.com/SignalK/specification/blob/master/v2-food-for-thought.md). 29 | 30 | Usage 31 | ----- 32 | 33 | The `master` branch contains the latest version of the Schema. When making 34 | changes, please clone this repo to your local machine and set up a new branch 35 | (`git checkout -b branch_name`). Send in a pull request for every change, put 36 | it up for discussion in the mailing list and then (when a consensus has been 37 | reached) merge it into `master`. 38 | 39 | The `gh-pages` branch contains the currently published version of the schema 40 | and specification. Documentation is generated with Gitbook and published at 41 | http://signalk.org/specification/master/. Documentation is published on the web 42 | with a single npm command: 43 | 44 | ``` 45 | $ npm run docs:publish 46 | ``` 47 | 48 | See below for details. 49 | 50 | Gitbook Documentation 51 | --------------------- 52 | 53 | The documentation .md sources are at 54 | https://github.com/SignalK/specification/tree/master/gitbook-docs. 55 | 56 | Requires separate installation of `ebook-convert`, see 57 | https://toolchain.gitbook.com/ebook.html. 58 | 59 | - `npm run docs:serve` for local preview 60 | - `npm run docs:all` to generate locally 61 | - `npm run docs:publish` to publish in gh-pages. 62 | 63 | The changelog in the documentation is generated based on Github Pull Requests. For things to show up in the changelog you MUST USE PRs! Rewording is possible by rewriting PR titles. 64 | 65 | Validation 66 | ---------- 67 | 68 | Validation against Signal K schema can be done 69 | - with a command line validator accepting JSON from stdin 70 | - by explicitly calling validate packaged as an npm module 71 | - by using a Chai assertion, available in the npm module 72 | 73 | ``` 74 | cat test/data/full-invalid/vessel-mmsi_bad.json | bin/validate.js 75 | { 76 | "errors": [ 77 | { 78 | "message": "String does not match pattern: ^[2-7][0-9]{8}$", 79 | "params" .....etc 80 | ``` 81 | 82 | ```javascript 83 | var validate = require('signalk-schema').validate; 84 | var result = validate(msg); 85 | result.errors.forEach(function(error) { 86 | console.error(error.message + ':' + error.dataPath); 87 | }); 88 | ``` 89 | 90 | ```javascript 91 | chai.use(require('signalk-schema').chaiModule); 92 | tree.should.be.validSignalK; 93 | ``` 94 | -------------------------------------------------------------------------------- /REFERENCE.md: -------------------------------------------------------------------------------- 1 | Basic principles 2 | ---------------- 3 | 4 | Signal K is a data model, and protocol for data sharing. The internal implementation of Signal K in a device is the developers own choice, and need not follow the Signal K model structure, but the external messages must to ensure interoperability. 5 | 6 | Adding to the Signal K schema 7 | ----------------------------- 8 | 9 | * keys are globally unique. 10 | * a key always has the same statically defined units. These are the appropriate SI units. 11 | * key names are camelCase, with lowercase first letter, java style. 12 | * key names follow a 'more generic>>more specific' model. Hence a key name will be 'bearingActualTrue', rather than trueActualBearing. This allows bearing* or bearingActual*, and better sorting and IDE integration. 13 | * if there are many bearing* variants, bearing* should become an object tree, eg bearing.actualTrue, for a cleaner model 14 | * by default keys are optional. Very few keys are required, those are mainly in the topmost structures. 15 | * adding your own custom keys is acceptable, but unless you publish them here, only you will be able to use them. 16 | * favour the use of objects over arrays. 17 | 18 | Using the Signal K model 19 | ------------------------ 20 | 21 | * all data is UTF-8 22 | * the model is designed so that any incoming message can simply be merged into the currently held model since all keys are unique. 23 | * each message must start from the root element, but need only contain the keys of interest. eg a given message will be a filtered partial copy of the whole model. It may contain one key, or a whole branch of keys. 24 | * if a device receives an unknown key it can be ignored by that device. 25 | * each device may hold a full(eg server) or partial(eg autopilot) copy of the model, or may simply transmit its specific keys. eg(depth). 26 | * any changes made locally (to a devices local model) need to be transmitted to other interested devices, so that their copies of the model remain synchronised. 27 | 28 | Security 29 | -------- 30 | 31 | Signal K does not define or apply security. Security is the responsibility of the implementation. Developers should be aware that a Signal K implementation may be accessible from the internet, and consider the effect sending misc messages into the system from an external source may have. At a minimum you should: 32 | 33 | * not update your localBoat's critical navigational model with arbitrary data from outside of your vessel, eg on the internet. Trust your own instruments first! 34 | * you should filter the information you allow onto the internet. 35 | * you should consider if an external source is trustworthy before relying on their data (eg a waypoint list) 36 | 37 | 38 | -------------------------------------------------------------------------------- /bin/validate.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var JSONStream = require('JSONStream'); 4 | var Transform = require('stream').Transform; 5 | 6 | var signalkSchema = require('../'); 7 | 8 | process.stdin.resume(); 9 | process.stdin.setEncoding('utf8'); 10 | 11 | 12 | function Validator(options) { 13 | Transform.call(this, { 14 | objectMode: true 15 | }); 16 | } 17 | 18 | require('util').inherits(Validator, Transform); 19 | 20 | Validator.prototype._transform = function(chunk, encoding, done) { 21 | var validationResult = signalkSchema.validateFull(chunk); 22 | if (!validationResult.valid) { 23 | console.error(JSON.stringify(validationResult, null, 2)); 24 | } 25 | done(); 26 | } 27 | 28 | process.stdin.pipe(JSONStream.parse()).pipe(new Validator()); 29 | -------------------------------------------------------------------------------- /mdbook/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | otherBranches.md 3 | vesselsBranch.md 4 | -------------------------------------------------------------------------------- /mdbook/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = [] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | -------------------------------------------------------------------------------- /mdbook/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | mdbook: 5 | container_name: mdbook 6 | image: peaceiris/mdbook:v0.4.13 7 | stdin_open: true 8 | tty: true 9 | ports: 10 | - 3000:3000 11 | - 3001:3001 12 | volumes: 13 | - ${PWD}/mdbook:/book 14 | command: 15 | - serve 16 | - --hostname 17 | - '0.0.0.0' -------------------------------------------------------------------------------- /mdbook/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Signal K Specification 2 | 3 | * [Signal K Specification](README.md) 4 | * [Getting Started]() 5 | * [Using SK](start_using.md) 6 | * [Developing with SK](start_developing.md) 7 | * [Data Model]() 8 | * [Full and Delta Models](data_model.md) 9 | * [Multiple Values](data_model_multiple_values.md) 10 | * [Metadata](data_model_metadata.md) 11 | * [Sources](sources.md) 12 | * [API]() 13 | * [URLs and Ports](urls_ports.md) 14 | * [REST API](rest_api.md) 15 | * [Streaming API](streaming_api.md) 16 | * [Subscription Protocol](subscription_protocol.md) 17 | * [Discovery & Connection Establishment](connection.md) 18 | * [Notifications](notifications.md) 19 | * [Security](security.md) 20 | * [Request/Response](request_response.md) 21 | * [PUT Requests](put.md) 22 | * [How Can I Help?](how_to_help.md) 23 | * [Appendix A: Keys Reference (Vessel)](vesselsBranch.md) 24 | * [Appendix B: Keys Reference (Others)](otherBranches.md) 25 | * [Appendix C: Changelog](changelog.md) 26 | * [Appendix D: Versioning](versioning.md) 27 | * [Appendix E: Access Requests](access_requests.md) 28 | -------------------------------------------------------------------------------- /mdbook/src/access_requests.md: -------------------------------------------------------------------------------- 1 | # Access Requests 2 | 3 | When a device needs to gain access to a secured Signal K server, it can use __Access Requests__ to request and be 4 | granted access to the server. 5 | 6 | See [Request/Response](request_response.md) for more information on request/response semantics. 7 | 8 | A device could be a display or for example an engine sensor or a temperature sensor. 9 | 10 | ## Definitions 11 | 12 | * `clientId` is a string that uniquely identifies a device. It must be a [v4 UUID](https://tools.ietf.org/html/rfc4122.html#section-4.4) The client should use the same 13 | value for all its requests. 14 | * `requestId` is a string generated by the server in response to an access request and used by the client to correlate 15 | the request with the results. The client should not rely on the format or contents of this string. 16 | 17 | ## Device Requests 18 | 19 | The device will send a REST request to the server: 20 | 21 | ```sh 22 | $ curl -k \ 23 | --header "Content-Type: application/json" \ 24 | --request POST \ 25 | --data '{"clientId":"1234-45653-343453","description":"My Awesome Humidity Sensor"}' \ 26 | https://localhost:3443/signalk/v1/access/requests 27 | ``` 28 | 29 | If the server does not support access requests it must return HTTP status code 501 Not Implemented. 30 | 31 | For a successfully received access request the server must return an HTTP status code 202 and a JSON response with an 32 | href to check the status and get the response. 33 | 34 | ```json 35 | { 36 | "state": "PENDING", 37 | "href": "/signalk/v1/access/requests/358b5f32-76bf-4b33-8b23-10a330827185" 38 | } 39 | ``` 40 | 41 | The server should then provide a process for an administrator to review and approve or deny the request. 42 | 43 | In the meantime, a device should poll the server using the `requestId` in the response above to see if it has been 44 | granted access and get the token. 45 | 46 | ### Response to a Pending Request 47 | 48 | ```sh 49 | $ curl -k https://localhost:3443/signalk/v1/access/requests/358b5f32-76bf-4b33-8b23-10a330827185 50 | { 51 | "state": "PENDING" 52 | } 53 | ``` 54 | 55 | ### Response to a Denied Request 56 | 57 | ```sh 58 | $ curl -k https://localhost:3443/signalk/v1/access/requests/358b5f32-76bf-4b33-8b23-10a330827185 59 | { 60 | "state": "COMPLETED", 61 | "statusCode": 200, 62 | "accessRequest": { 63 | "permission": "DENIED" 64 | } 65 | } 66 | ``` 67 | 68 | When a device gets a denied response, it should refrain from sending further access requests until the device is reset, rebooted or the user takes some action. 69 | 70 | ### Response to an Approved Request 71 | 72 | _Note:_ The `expirationTime` property is optional. 73 | 74 | ```sh 75 | $ curl -k https://localhost:3443/signalk/v1/access/requests/358b5f32-76bf-4b33-8b23-10a330827185 76 | { 77 | "state": "COMPLETED", 78 | "statusCode": 200, 79 | "accessRequest": { 80 | "permission": "APPROVED", 81 | "token": "eyJhbGciOiJIUzI1NiIs...BAP8bt3tNBT1WiIttm3qM", 82 | "expirationTime": "2018-09-20T16:51:31.350Z" 83 | } 84 | } 85 | ``` 86 | 87 | ### Response Indicating an Error With the Request 88 | 89 | ```sh 90 | $ curl -k https://localhost:3443/signalk/v1/access/requests/358b5f32-76bf-4b33-8b23-10a330827185 91 | { 92 | "state": "COMPLETED", 93 | "statusCode": 400, 94 | "message": "A device with clientId '1234-45653-343453' has already requested access" 95 | } 96 | ``` 97 | 98 | ### After Access Approval 99 | 100 | On approval, the device would save the token in a secure way and use it when sending or requesting data. 101 | 102 | At some point in the future the provided token could expire, access to the server could be revoked or the server could be replaced. In all cases the server will respond to requests with a 403 status code. The device should then submit a new request for access and follow the process defined above. 103 | 104 | -------------------------------------------------------------------------------- /mdbook/src/consumer_discovery_process.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SignalK/specification/1978f556d8790be2b3bfdc9fffcf82a9a913e3c2/mdbook/src/consumer_discovery_process.dia -------------------------------------------------------------------------------- /mdbook/src/consumer_discovery_process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SignalK/specification/1978f556d8790be2b3bfdc9fffcf82a9a913e3c2/mdbook/src/consumer_discovery_process.png -------------------------------------------------------------------------------- /mdbook/src/gateway_and_server.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SignalK/specification/1978f556d8790be2b3bfdc9fffcf82a9a913e3c2/mdbook/src/gateway_and_server.dia -------------------------------------------------------------------------------- /mdbook/src/gateway_and_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SignalK/specification/1978f556d8790be2b3bfdc9fffcf82a9a913e3c2/mdbook/src/gateway_and_server.png -------------------------------------------------------------------------------- /mdbook/src/get.md: -------------------------------------------------------------------------------- 1 | # GET Requests 2 | 3 | GET requests are sent to a server to request a value. For example, a client would use GET to find the state of the 4 | anchor light, the heading of the autopilot, or position of the anchor. 5 | 6 | See [Request/Response](request_response.md) for more details on request/response in Signal K. 7 | 8 | ## Making a Request to Get a Value 9 | 10 | To get a value, a GET request should be sent via HTTP or using a Signal K __get__ message. 11 | 12 | ### Via HTTP 13 | ``` 14 | GET http://localhost:3000/signalk/v1/api/vessels/self/steering/autopilot/target/headingTrue 15 | 16 | ``` 17 | 18 | ### Via a Delta 19 | 20 | [>]: # (mdpInsert ```json fsnip ../../test/data/get-valid/delta-get-array.json) 21 | ```json 22 | { 23 | "context": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5", 24 | "requestId": "6b0e776f-811a-4b35-980e-b93405371bc5", 25 | "get": [{ 26 | "path": "a.b.c" 27 | }] 28 | } 29 | ``` 30 | [<]: # 31 | 32 | The `context` key is optional, and defaults to `vessels.self`, which is the usual case. You can include it to be able to set values on other vessels. 33 | 34 | #### NOTE #### 35 | The above GET request (v1) uses an array to allow multiple keys in a single GET. This is deprecated and strongly discouraged as it cannot work via http. 36 | An alternative format has been added to the v1 specification where the GET request is: 37 | 38 | [>]: # (mdpInsert ```json fsnip ../../test/data/get-valid/delta-get-no-array.json) 39 | ```json 40 | { 41 | "context": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5", 42 | "requestId": "6b0e776f-811a-4b35-980e-b93405371bc5", 43 | "get": { 44 | "path": "a.b.c" 45 | } 46 | } 47 | ``` 48 | [<]: # 49 | 50 | In the v2 API the array format will be removed. Implementors are recommended to support both in the interim. 51 | 52 | ## Return states 53 | 54 | A GET request in the array format is only successful if _ALL_ if the items are successful. It is up to the client to ascertain which were in error, and why. 55 | -------------------------------------------------------------------------------- /mdbook/src/how_to_help.md: -------------------------------------------------------------------------------- 1 | This is a quick start for any-one that would like to contribute. Its roughly from technically unskilled to skilled, top to bottom. Dont be afraid to ask for help. Each task will probably start with a new thread for more details on the Google groups (https://groups.google.com/forum/#!forum/signalk). Be patient, civil, and persistent :-) 2 | 3 | Completely unskilled at boat electronics: 4 | * Join https://groups.google.com/forum/#!forum/signalk - as the user base grows, so does awareness. 5 | * Tell others, spread the word 6 | * Fly a Signal K flag from your boat 7 | * If you have special skills (eg motors, batteries, navigation, etc) help us extend the Signal K protocol by identifying what we need to cover. 8 | * Ask manufacturers about Signal K support 9 | * Ask questions about what you dont understand, and collate the answers for us to put on the website. 10 | 11 | Can do own installs, handyman, but not IT skilled. 12 | * Try an install of Raspberry Pi and WIFI, document exactly how you did it, so others can follow. 13 | 14 | Website or documentation skills 15 | * Help us maintain the website, and improve the documents 16 | 17 | Good computer skills, but not programming 18 | * Download and try the java server (https://github.com/SignalK/signalk-server-java) and node server (https://github.com/SignalK/signalk-server-node) and the various apps and clients. Help test and identify issues, help improve documents so others can follow easier. 19 | * Help with User manuals! 20 | 21 | Systems engineer 22 | * Help other users, help with scripts, develop and maintain install processes, managing our web sites, etc. 23 | * Examples: 24 | * Create Debian packages of the Signal K software for easy installation to Raspbian 25 | 26 | Software developer 27 | * Download and test/fix our stuff, add improvements, join the team and help code, develop support in your own software. 28 | 29 | Microprocessors 30 | * Improve our Arduino stuff, add your own, incorporate Signal K into your products. -------------------------------------------------------------------------------- /mdbook/src/put.md: -------------------------------------------------------------------------------- 1 | # PUT Requests 2 | 3 | PUT requests are sent to a server to request an action to be taken. For example, a client would use PUT to switch the anchor light on or off, change the heading of the autopilot, or change the current input on a stereo. 4 | 5 | Note that this is very different than updating the current state of something. Use a delta update message to report the current / updated value of something, for example to report on the current wind speed from a sensor. See [Delta Format](data_model.md) 6 | 7 | See [Request/Response](request_response.md) for more details on request/response in Signal K. 8 | 9 | ## Making a Request To Take an Action 10 | 11 | To change a value, a PUT request should be sent via HTTP or using a Signal K __put__ delta. 12 | 13 | The `source` field is optional. If a request is sent without the source and there is more than one source for the 14 | value, the server should respond with a 400 (Bad Request) HTTP status code. 15 | 16 | ### Via HTTP 17 | ``` 18 | PUT http://localhost:3000/signalk/v1/api/vessels/self/steering/autopilot/target/headingTrue 19 | { 20 | "value": 1.52, 21 | "source": "actisense.204", 22 | } 23 | ``` 24 | 25 | ### Via a Delta 26 | 27 | [>]: # (mdpInsert ```json fsnip ../../test/data/put-valid/delta-put-array.json) 28 | ```json 29 | { 30 | "context": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5", 31 | "requestId": "6b0e776f-811a-4b35-980e-b93405371bc5", 32 | "put": [{ 33 | "path": "a.b.c", 34 | "value": 1234 35 | }] 36 | } 37 | ``` 38 | [<]: # 39 | 40 | The `context` key is optional, and defaults to `vessels.self`, which is the usual case. You can include it to be able to set values on other vessels. 41 | 42 | #### NOTE #### 43 | The above PUT request (v1) uses an array to allow multiple keys in a single PUT. This is deprecated and strongly discouraged as it causes complex problems 44 | with the request/response semantics in cases of partial failures. An alternative format has been added to the v1 specification where the PUT request is: 45 | 46 | [>]: # (mdpInsert ```json fsnip ../../test/data/put-valid/delta-put-no-array.json) 47 | ```json 48 | { 49 | "context": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5", 50 | "requestId": "6b0e776f-811a-4b35-980e-b93405371bc5", 51 | "put": { 52 | "path": "a.b.c", 53 | "value": 1234 54 | } 55 | } 56 | ``` 57 | [<]: # 58 | 59 | In the v2 API the array format will be removed. Implementors are recommended to support both in the interim. 60 | 61 | ## Return states 62 | 63 | A PUT request in the array format is only successful if _ALL_ if the items are successful. It is up to the client to ascertain which were in error, and why. 64 | -------------------------------------------------------------------------------- /mdbook/src/server_only.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SignalK/specification/1978f556d8790be2b3bfdc9fffcf82a9a913e3c2/mdbook/src/server_only.dia -------------------------------------------------------------------------------- /mdbook/src/server_only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SignalK/specification/1978f556d8790be2b3bfdc9fffcf82a9a913e3c2/mdbook/src/server_only.png -------------------------------------------------------------------------------- /mdbook/src/start_developing.md: -------------------------------------------------------------------------------- 1 | # Getting Started in Developing with Signal K 2 | 3 | * [Example HTML5 Applications](https://github.com/SignalK/signalk-server-node/tree/master/public/examples) 4 | * [Signal K JavaScript Client](https://github.com/SignalK/signalk-js-client) 5 | * [iKommunicate Developer’s Guide](https://github.com/digitalyacht/ikommunicate/wiki/iKommunicate-Developer%27s-Guide-%28SDK%29) 6 | -------------------------------------------------------------------------------- /mdbook/src/start_using.md: -------------------------------------------------------------------------------- 1 | # Getting Started Using Signal K 2 | 3 | You can start using Signal K by 4 | 5 | * connecting to the [demo server](http://demo.signalk.org/) on the Internet with any web browser 6 | * installing [SK server](https://github.com/signalk/signalk-server-node) on any computer 7 | * getting some hardware for your boat, such as a [Raspberry Pi](https://en.wikipedia.org/wiki/Raspberry_Pi), suitable 8 | USB adapters for your boat’s network ([NMEA 0183](http://digitalyacht.co.uk/product/usb-nmea-adaptor/), 9 | [NMEA 2000](https://github.com/SignalK/signalk-server/wiki/FAQ:-Frequently-Asked-Questions#how-do-i-integrate-with-nmea2000-can-bus) or roll your own with 10 | [I2C](https://en.wikipedia.org/wiki/I%C2%B2C) sensors) and installing the server 11 | * For sensors over WiFi take a look at [SensESP](https://github.com/SignalK/SensESP) 12 | * installing [OpenPlotter](https://openplotter.readthedocs.io/en/3.x.x/), which includes a Signal K server 13 | 14 | Once you have a server running (or you start by using the demo server) you can install some Signal K supporting mobile 15 | apps such as [WilhelmSK](https://www.wilhelmsk.com/) (iOS). 16 | -------------------------------------------------------------------------------- /mdbook/src/urls_ports.md: -------------------------------------------------------------------------------- 1 | # URLs and Ports 2 | 3 | While Signal K is a transport-agnostic protocol, there are certain conventions that have been established for use on 4 | the Web and by clients and servers using HTTP and WebSockets. 5 | 6 | ## Ports 7 | 8 | The Signal K HTTP and WebSocket services SHOULD be found on the usual HTTP/S ports (80 or 443). The services SHOULD be 9 | found on the same port, but may be configured for independent ports and MAY be configured for ports other than HTTP/S. 10 | 11 | A Signal K server MAY offer Signal K over TCP or UDP, these services SHOULD be on port 8375[1](#fn_1). 13 | 14 | If an alternate port is needed it SHOULD be an arbitrary high port in the range 49152–65535[2](#fn_2). 16 | 17 | ## URL Prefix 18 | 19 | The Signal K applications start from the `/signalk` root. This provides some protection against name collisions with 20 | other applications on the same server. Therefore the Signal K entry point will always be found by loading 21 | `http(s)://«host»:«port»/signalk`. 22 | 23 | ------ 24 | [1] This has not been registered with IANA yet. It is the ASCII decimal code for SK. 25 |
26 | [2] This is the private use section of IP ports specified as reserved by IANA. 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@signalk/signalk-schema", 3 | "version": "1.7.2", 4 | "description": "SignalK specification schema as an npm module with tests", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "test": "babel src --out-dir dist --copy-files && mocha", 8 | "prepublish": "npm run docs:keys && npm run js:dist", 9 | "js:watch": "babel --watch src --out-dir dist --copy-files", 10 | "js:dist": "babel src --out-dir dist --copy-files", 11 | "schema:version": "cross-var replace-in-file /https:\\\\/\\\\/signalk.org\\\\/specification\\\\/[\\\\w\\\\.]+\\\\/schemas\\\\//g https://signalk.org/specification/$npm_package_version/schemas/ './**/*.j*' --ignore=./package.json,./node_modules/** --isRegex --verbose", 12 | "schema:publish": "git checkout gh-pages && git checkout master -- schemas && cross-var mkdir -p $npm_package_version && cross-var git add $npm_package_version && cross-var git mv schemas/ $npm_package_version/ && git commit -m \"Schemas from master\" && git push", 13 | "docs:prep": "cross-var replace-in-file /_version_/g $npm_package_version ./mdbook/src/* --isRegex --verbose && mdprepare mdbook/src/*.md", 14 | "docs:keys": "node scripts/processSchemaFiles.js", 15 | "docs:html": "cross-var docker-compose -f mdbook/docker-compose.yml run mdbook build -d $npm_package_version/doc", 16 | "docs:serve": "docker-compose -f mdbook/docker-compose.yml up", 17 | "docs:all": "npm run docs:prep && npm run docs:keys && npm run docs:html", 18 | "docs:publish-local-gh-pages": "npm run docs:all && mv mdbook/$npm_package_version . && cross-var git add $npm_package_version && git stash && git checkout gh-pages && git pull && cross-var git checkout stash -- $npm_package_version && git add master && cross-var git commit --allow-empty -m $npm_package_version" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/SignalK/specification.git" 23 | }, 24 | "keywords": [ 25 | "signalk", 26 | "nmea", 27 | "marine", 28 | "navigation", 29 | "n2k", 30 | "nmea200" 31 | ], 32 | "author": "SignalK group", 33 | "license": "CC-BY-SA-2.0", 34 | "bugs": { 35 | "url": "https://github.com/SignalK/specification/issues" 36 | }, 37 | "bin": { 38 | "signalk-validate-full": "bin/validate.js" 39 | }, 40 | "homepage": "https://github.com/SignalK/specification", 41 | "dependencies": { 42 | "JSONStream": "^0.7.4", 43 | "debug": "^4.3.1", 44 | "@apidevtools/json-schema-ref-parser": "^9.1.0", 45 | "lodash": "^4.17.21", 46 | "tv4": "^1.2.7", 47 | "tv4-formats": "^3.0.3" 48 | }, 49 | "devDependencies": { 50 | "babel-cli": "^6.24.1", 51 | "babel-preset-es2015": "^6.24.1", 52 | "chai": "^1.9.2", 53 | "cross-var": "1.1.0", 54 | "fsnip": "^0.9.5", 55 | "github-changes": "^1.0.4", 56 | "infuse.js": "^2.0.2", 57 | "markdown-it": "^8.0.0", 58 | "mdprepare": "^0.9.3", 59 | "mocha": "^2.1.0", 60 | "mz": "^2.4.0", 61 | "replace-in-file": "^3.1.0", 62 | "rimraf": "^2.5.4" 63 | }, 64 | "funding": "https://opencollective.com/signalk" 65 | } 66 | -------------------------------------------------------------------------------- /samples/delta/0183-RMC-export-delta.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.366982330.navigation", 3 | "updates": [ 4 | { 5 | "values": [ 6 | { 7 | "path": "position.timestamp", 8 | "value": "2015-03-07T12:37:10.523+13:00" 9 | }, 10 | { 11 | "path": "position.longitude", 12 | "value": 173.1693 13 | }, 14 | { 15 | "path": "position.latitude", 16 | "value": -41.156426 17 | }, 18 | { 19 | "path": "position.source", 20 | "value": "sources.gps_0183_RMC" 21 | }, 22 | { 23 | "path": "position.altitude", 24 | "value": 0 25 | }, 26 | { 27 | "path": "courseOverGroundTrue", 28 | "value": 245.69 29 | } 30 | ], 31 | "$source": "sources.gps_0183_RMC" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /samples/delta/0183-RMC-export-min-delta.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.366982330.navigation", 3 | "updates": [ 4 | { 5 | "values": [ 6 | { 7 | "path": "position.longitude", 8 | "value": 173.1693 9 | }, 10 | { 11 | "path": "position.latitude", 12 | "value": -41.156426 13 | }, 14 | { 15 | "path": "position.altitude", 16 | "value": 0 17 | }, 18 | { 19 | "path": "courseOverGroundTrue", 20 | "value": 245.69 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /samples/delta/MOB-alarm-delta.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "updates": [ 4 | { 5 | "source": { 6 | "label": "ttyUSB0", 7 | "type": "NMEA0183", 8 | "talker": "GP", 9 | "sentence": "MOB" 10 | }, 11 | "timestamp": "2014-08-15T16:00:05.538Z", 12 | "values": [ 13 | { 14 | "path": "notifications.mob", 15 | "value": { 16 | "message": "MOB", 17 | "state": "emergency", 18 | "method": ["visual", "sound"] 19 | } 20 | } 21 | ] 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /samples/delta/docs-data_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:234567890", 3 | "updates": [ 4 | { 5 | "source": { 6 | "label": "N2000-01", 7 | "type": "NMEA2000", 8 | "src": "017", 9 | "pgn": 127488 10 | }, 11 | "timestamp": "2010-01-07T07:18:44Z", 12 | "values": [ 13 | { 14 | "path": "propulsion.0.revolutions", 15 | "value": 16.341667 16 | }, 17 | { 18 | "path": "propulsion.0.boostPressure", 19 | "value": 45500.0 20 | } 21 | ] 22 | }, 23 | { 24 | "source": { 25 | "label": "N2000-01", 26 | "type": "NMEA2000", 27 | "src": "115", 28 | "pgn": 128267 29 | }, 30 | "timestamp": "2014-08-15T16:00:00.081Z", 31 | "values": [ 32 | { 33 | "path": "navigation.courseOverGroundTrue", 34 | "value": 2.971 35 | }, 36 | { 37 | "path": "navigation.speedOverGround", 38 | "value": 3.85 39 | } 40 | ] 41 | }, 42 | { 43 | "source": { 44 | "label": "N2000-01", 45 | "type": "NMEA2000", 46 | "src": "115", 47 | "pgn": 128267 48 | }, 49 | "timestamp": "2014-08-15T19:02:31.507Z", 50 | "values": [ 51 | { 52 | "path": "", 53 | "value": { 54 | "name": "WRANGO" 55 | } 56 | } 57 | ] 58 | } 59 | ] 60 | } -------------------------------------------------------------------------------- /samples/delta/docs-data_model_meta_deltas.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:234567890", 3 | "updates": [ 4 | { 5 | "timestamp": "2014-08-15T19:02:31.507Z", 6 | "meta": [ 7 | { 8 | "path": "environment.wind.speedApparent", 9 | "value": { 10 | "units": "m/s", 11 | "description": "Apparent wind speed", 12 | "displayName": "Apparent Wind Speed", 13 | "shortName": "AWS", 14 | "zones": [{ 15 | "upper": 15.4333, 16 | "state": "warn", 17 | "message": "high wind speed" 18 | } 19 | ] 20 | } 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /samples/delta/docs-data_model_multiple_values.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "updates": [ 4 | { 5 | "source": { 6 | "label": "GPS-1", 7 | "type": "NMEA0183", 8 | "talker": "GP", 9 | "sentence": "RMC" 10 | }, 11 | "timestamp": "2017-04-03T06:14:04.451Z", 12 | "values": [ 13 | { 14 | "path": "navigation.courseOverGroundTrue", 15 | "value": 3.615624078431440 16 | } 17 | ] 18 | }, 19 | { 20 | "source": { 21 | "label": "actisense", 22 | "type": "NMEA2000", 23 | "src": "115", 24 | "pgn": 128267 25 | }, 26 | "timestamp": "2017-04-03T06:14:04.451Z", 27 | "values": [ 28 | { 29 | "path": "navigation.courseOverGroundTrue", 30 | "value": 3.615624078431453 31 | } 32 | ] 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /samples/delta/docs-notifications.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "updates": [ 4 | { 5 | "source": { 6 | "label": "ttyUSB0", 7 | "type": "NMEA0183", 8 | "talker": "GP", 9 | "sentence": "MOB" 10 | }, 11 | "timestamp": "2017-08-15T16:00:05.200Z", 12 | "values": [ 13 | { 14 | "path": "notifications.mob", 15 | "value": { 16 | "message": "MOB", 17 | "state": "emergency", 18 | "method": [ 19 | "visual", 20 | "sound" 21 | ] 22 | } 23 | } 24 | ] 25 | }, 26 | { 27 | "source": { 28 | "label": "ttyUSB0", 29 | "type": "NMEA0183", 30 | "talker": "GP", 31 | "sentence": "MOB" 32 | }, 33 | "timestamp": "2017-08-15T16:00:05.538Z", 34 | "values": [ 35 | { 36 | "path": "notifications.mob", 37 | "value": null 38 | } 39 | ] 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /samples/delta/docs-subscription_protocol.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:234567890", 3 | "updates": [ 4 | { 5 | "source": { 6 | "label": "N2000-01", 7 | "type": "NMEA2000", 8 | "src": "115", 9 | "pgn": 128275 10 | }, 11 | "values": [ 12 | { 13 | "path": "navigation.trip.log", 14 | "value": 43374 15 | }, 16 | { 17 | "path": "navigation.log", 18 | "value": 17404540 19 | } 20 | ] 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /samples/discovery/discovery.json: -------------------------------------------------------------------------------- 1 | { 2 | "endpoints": { 3 | "v1": { 4 | "version": "1.0.0", 5 | "signalk-http": "http://localhost:3000/signalk/v1/api/", 6 | "signalk-ws": "ws://localhost:3000/signalk/v1/stream" 7 | }, 8 | "v3": { 9 | "version": "3.0.0-Alpha", 10 | "signalk-http": "http://localhost/signalk/v3/api/", 11 | "signalk-ws": "ws://localhost/signalk/v3/stream", 12 | "signalk-tcp": "tcp://localhost:8367" 13 | } 14 | }, 15 | "server": { 16 | "id": "signalk-server-node", 17 | "version": "0.1.33" 18 | } 19 | } -------------------------------------------------------------------------------- /samples/discovery/docs-rest_api.json: -------------------------------------------------------------------------------- 1 | { 2 | "endpoints": { 3 | "v1": { 4 | "version": "1.0.0-alpha1", 5 | "signalk-http": "http://localhost:3000/signalk/v1/api/", 6 | "signalk-ws": "ws://localhost:3000/signalk/v1/stream" 7 | }, 8 | "v3": { 9 | "version": "3.0.0", 10 | "signalk-http": "http://localhost/signalk/v3/api/", 11 | "signalk-ws": "ws://localhost/signalk/v3/stream", 12 | "signalk-tcp": "tcp://localhost:8367" 13 | } 14 | }, 15 | "server": { 16 | "id": "signalk-server-node", 17 | "version": "0.1.33" 18 | } 19 | } -------------------------------------------------------------------------------- /samples/full/0183-RMC-export-min.json: -------------------------------------------------------------------------------- 1 | { "self":"urn:mrn:imo:mmsi:366982330", 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:366982330": { 4 | "mmsi": "230099999", 5 | "navigation": { 6 | "position": { 7 | "value": { 8 | "longitude": 173.1693, 9 | "latitude": -41.156426, 10 | "altitude": 0 11 | }, 12 | "timestamp": "2015-01-25T12:01:01Z", 13 | "$source": "a.suitable.path" 14 | }, 15 | "courseOverGroundTrue": { 16 | "value": 245.69, 17 | "timestamp": "2015-01-25T12:01:01Z", 18 | "$source": "a.suitable.path" 19 | } 20 | } 21 | } 22 | }, 23 | "version": "1.0.0" 24 | } -------------------------------------------------------------------------------- /samples/full/0183-RMC-export.json: -------------------------------------------------------------------------------- 1 | { "self":"urn:mrn:imo:mmsi:366982330", 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:366982330": { 4 | "mmsi": "366982330", 5 | "navigation": { 6 | "position": { 7 | "timestamp": "2015-03-06T16:57:53.643Z", 8 | "value": { 9 | "longitude": 173.1693, 10 | "latitude": -41.156426 11 | }, 12 | "$source": "sources.gps_0183_RMC" 13 | }, 14 | "courseOverGroundTrue": { 15 | "timestamp": "2015-03-06T16:57:53.643Z", 16 | "$source": "sources.gps_0183_RMC", 17 | "value": 245.69 18 | } 19 | } 20 | } 21 | }, 22 | "version": "1.0.0" 23 | } -------------------------------------------------------------------------------- /samples/full/0183-RMC-full.json: -------------------------------------------------------------------------------- 1 | { "self":"urn:mrn:imo:mmsi:366982330", 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:366982330": { 4 | "mmsi": "366982330", 5 | "navigation": { 6 | "position": { 7 | "timestamp": "2015-03-06T16:57:53.643Z", 8 | "value": { 9 | "longitude": 173.1693, 10 | "latitude": -41.156426, 11 | "altitude": 0 12 | }, 13 | "$source": "sources.gps_0183_RMC", 14 | "_attr": { 15 | "_mode": 644, 16 | "_owner": "self", 17 | "_group": "self" 18 | } 19 | }, 20 | "courseOverGroundTrue": { 21 | "meta": { 22 | "description": "Course over ground (true)", 23 | "units": "rad", 24 | "zones": [ 25 | { 26 | "lower": 260, 27 | "upper": 360, 28 | "state": "alarm" 29 | }, 30 | { 31 | "lower": 220, 32 | "upper": 230, 33 | "state": "warn" 34 | }, 35 | { 36 | "lower": 0, 37 | "upper": 220, 38 | "state": "alarm" 39 | }, 40 | { 41 | "lower": 220, 42 | "upper": 260, 43 | "state": "normal" 44 | } 45 | ], 46 | "shortName": "COG", 47 | "alarmMethod": [ 48 | "sound" 49 | ], 50 | "warnMethod": [ 51 | "visual" 52 | ], 53 | "displayName": "COG (True)" 54 | }, 55 | "timestamp": "2015-03-06T16:57:53.643Z", 56 | "$source": "sources.gps_0183_RMC", 57 | "_attr": { 58 | "_mode": 644, 59 | "_owner": "self", 60 | "_group": "self" 61 | }, 62 | "value": 245.69 63 | } 64 | } 65 | } 66 | }, 67 | "sources": { 68 | "gps_0183_RMC": { 69 | "timestamp": "2015-03-06T16:57:53.643Z", 70 | "src": "$GPRMC,033025.000,A,4115.6426,S,17316.9300,E,0.05,245.69,090113,,*15", 71 | "bus": "/dev/ttyUSB1" 72 | }, 73 | "_attr": { 74 | "_mode": 644, 75 | "_owner": "self", 76 | "_group": "self" 77 | } 78 | }, 79 | "version": "0.1.0" 80 | } -------------------------------------------------------------------------------- /samples/full/docs-data_model.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "self": "urn:mrn:signalk:uuid:705f5f1a-efaf-44aa-9cb8-a0fd6305567c", 4 | "vessels": { 5 | "urn:mrn:signalk:uuid:705f5f1a-efaf-44aa-9cb8-a0fd6305567c": { 6 | "navigation": { 7 | "speedOverGround": { 8 | "value": 4.32693662, 9 | "$source": "ttyUSB0.GP", 10 | "sentence": "RMC", 11 | "timestamp": "2017-05-16T05:15:50.007Z" 12 | }, 13 | "position": { 14 | "value": { 15 | "altitude": 0.0, 16 | "latitude": 37.81479, 17 | "longitude": -122.44880152 18 | }, 19 | "$source": "ttyUSB0.GP", 20 | "sentence": "RMC", 21 | "timestamp": "2017-05-16T05:15:50.007Z" 22 | }, 23 | "headingMagnetic": { 24 | "value": 5.55014702, 25 | "$source": "ttyUSB0.II", 26 | "sentence": "HDM", 27 | "timestamp": "2017-05-16T05:15:54.006Z" 28 | } 29 | }, 30 | "name": "Motu", 31 | "uuid": "urn:mrn:signalk:uuid:705f5f1a-efaf-44aa-9cb8-a0fd6305567c" 32 | } 33 | }, 34 | "sources": { 35 | "ttyUSB0": { 36 | "label": "ttyUSB0", 37 | "type": "NMEA0183", 38 | "GP": { 39 | "talker": "GP", 40 | "sentences": { 41 | "RMC": "2017-04-03T06:14:04.451Z" 42 | } 43 | }, 44 | "II": { 45 | "talker": "II", 46 | "sentences": { 47 | "HDM": "2017-05-16T05:15:54.006Z" 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /samples/full/docs-data_model_metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "vessels": { 4 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 5 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 6 | "propulsion": { 7 | "instance0": { 8 | "label": "Port Engine", 9 | "revolutions": { 10 | "value": 1280, 11 | "timestamp": "2014-08-15T19:00:15.402Z", 12 | "$source": "foo.bar", 13 | "meta": { 14 | "displayName": "Port Tachometer", 15 | "longName": "Engine 2 Tachometer", 16 | "shortName": "Tacho", 17 | "description": "Engine revolutions (x60 for RPM)", 18 | "units": "Hz", 19 | "timeout": 1, 20 | "displayScale": { 21 | "lower": 0, 22 | "upper": 75, 23 | "type": "linear" 24 | }, 25 | "alertMethod": [ 26 | "visual" 27 | ], 28 | "warnMethod": [ 29 | "visual" 30 | ], 31 | "alarmMethod": [ 32 | "sound", 33 | "visual" 34 | ], 35 | "emergencyMethod": [ 36 | "sound", 37 | "visual" 38 | ], 39 | "zones": [ 40 | { 41 | "upper": 4, 42 | "state": "alarm", 43 | "message": "Stopped or very slow" 44 | }, 45 | { 46 | "lower": 4, 47 | "upper": 60, 48 | "state": "normal" 49 | }, 50 | { 51 | "lower": 60, 52 | "upper": 65, 53 | "state": "warn", 54 | "message": "Approaching maximum" 55 | }, 56 | { 57 | "lower": 65, 58 | "state": "alarm", 59 | "message": "Exceeding maximum" 60 | } 61 | ] 62 | } 63 | } 64 | } 65 | } 66 | } 67 | }, 68 | "version": "1.0.0" 69 | } -------------------------------------------------------------------------------- /samples/full/docs-data_model_multiple_values.json: -------------------------------------------------------------------------------- 1 | { 2 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "version": "0.9.0", 4 | "vessels": { 5 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 6 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 7 | "navigation": { 8 | "courseOverGroundTrue": { 9 | "value": 3.615624078431440, 10 | "$source": "ttyUSB0.GP", 11 | "timestamp": "2017-04-03T06:14:04.451Z", 12 | "values":{ 13 | "ttyUSB0.GP.RMC":{ 14 | "value": 3.615624078431440, 15 | "timestamp": "2017-04-03T06:14:04.451Z" 16 | }, 17 | "n2k.ikommunicate.128267":{ 18 | "value": 3.615624078431453, 19 | "timestamp": "2017-04-03T06:14:04.451Z" 20 | } 21 | } 22 | } 23 | } 24 | } 25 | }, 26 | "sources":{ 27 | "ttyUSB0": { 28 | "GP": { 29 | "sentences": { 30 | "RMC": "2017-04-03T06:14:04.451Z" 31 | } 32 | } 33 | }, 34 | "ikommunicate": { 35 | "2": { 36 | "n2k": { 37 | "src": "2", 38 | "pgns": { 39 | "128267": "2017-04-03T06:14:05.221Z" 40 | } 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /samples/full/docs-notifications.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 4 | "vessels": { 5 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 6 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 7 | "notifications": { 8 | "mob": { 9 | "value": { 10 | "method": [ 11 | "visual", 12 | "sound" 13 | ], 14 | "state": "emergency", 15 | "message": "Man Overboard!" 16 | }, 17 | "timestamp": "2017-04-10T08:33:53Z", 18 | "$source": "nmea1.II" 19 | }, 20 | "navigation": { 21 | "gnss": { 22 | "value": { 23 | "method": [ 24 | "visual" 25 | ], 26 | "state": "alert", 27 | "message": "GPS signal lost!" 28 | }, 29 | "$source": "nmea1.II", 30 | "timestamp": "2017-04-10T08:33:53Z" 31 | }, 32 | "anchor": { 33 | "currentRadius": { 34 | "value": { 35 | "method": [ 36 | "sound" 37 | ], 38 | "state": "alarm", 39 | "message": "Dragging anchor!" 40 | }, 41 | "timestamp": "2017-04-10T08:33:53Z", 42 | "$source": "nmea1.II" 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /samples/full/mob-alarm.json: -------------------------------------------------------------------------------- 1 | { "self": "urn:mrn:imo:mmsi:366982330", 2 | "version": "1.0.0", 3 | "vessels": { 4 | "urn:mrn:imo:mmsi:366982330": { 5 | "mmsi": "366982330", 6 | "notifications": { 7 | "mob":{ 8 | "value": { 9 | "message": "MOB", 10 | "state": "emergency", 11 | "method": ["visual", "sound"] 12 | }, 13 | "timestamp": "2014-04-10T08:33:53Z", 14 | "$source": "sources.gps_0183_MOB" 15 | } 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /samples/full/signalk-depth-alarm.json: -------------------------------------------------------------------------------- 1 | { "self": "urn:mrn:signalk:uuid:b7590868-1d62-47d9-989c-32321b349fb9", 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:b7590868-1d62-47d9-989c-32321b349fb9": { 4 | "notifications": { 5 | "environment": { 6 | "depth": { 7 | "belowKeel": { 8 | "timestamp": "2014-04-10T08:33:53Z", 9 | "$source": "nmea1.II", 10 | "value": { 11 | "method": ["sound"], 12 | "state": "alarm", 13 | "message": "Running aground!" 14 | } 15 | } 16 | } 17 | } 18 | }, 19 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 20 | } 21 | }, 22 | "version": "1.0.0" 23 | } -------------------------------------------------------------------------------- /samples/full/signalk-depth-meta-attr.json: -------------------------------------------------------------------------------- 1 | { "self": "urn:mrn:signalk:uuid:b7590868-1d62-47d9-989c-32321b349fb9", 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:b7590868-1d62-47d9-989c-32321b349fb9": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "environment": { 6 | "depth": { 7 | "belowKeel": { 8 | "value": 3.4, 9 | "timestamp": "2015-03-06T16:57:53.643Z", 10 | "$source": "nmea1.II", 11 | "_attr": { 12 | "_mode": 644, 13 | "_owner": "self", 14 | "_group": "self" 15 | }, 16 | "meta": { 17 | "description": "Depth below keel", 18 | "displayName": "Depth Below Keel", 19 | "longName": "Depth Below Keel", 20 | "shortName": "DBK", 21 | "units": "Meters (m)", 22 | "warnMethod": ["visual"], 23 | "alarmMethod": ["sound"], 24 | "zones": [ 25 | {"lower":0.0,"upper":1.5,"state":"alarm", "message":"Running aground!"}, 26 | {"lower":1.5,"upper":3.0,"state":"warn", "message":"Shallow water!"}, 27 | {"lower":3.0, "state":"normal"} 28 | ] 29 | } 30 | } 31 | } 32 | } 33 | } 34 | }, 35 | "version": "1.0.0" 36 | } 37 | -------------------------------------------------------------------------------- /samples/full/vessel-time.json: -------------------------------------------------------------------------------- 1 | { "self": "urn:mrn:signalk:uuid:b7590868-1d62-47d9-989c-32321b349fb9", 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:b7590868-1d62-47d9-989c-32321b349fb9": { 4 | "uuid": "urn:mrn:signalk:uuid:b7590868-1d62-47d9-989c-32321b349fb9", 5 | "environment": { 6 | "time": { 7 | "millis": 1449648657735, 8 | "timestamp": "2014-04-10T08:33:53Z", 9 | "source": { 10 | "label": "Realtime clock", 11 | "type": "system" 12 | }, 13 | "timezoneOffset": -1300 14 | } 15 | } 16 | } 17 | }, 18 | "version": "1.0.0" 19 | } 20 | -------------------------------------------------------------------------------- /samples/hello/docs-hello-minimal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.2", 3 | "roles": [ 4 | "slave" 5 | ] 6 | } -------------------------------------------------------------------------------- /samples/hello/docs-hello-playback.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foobar marine server", 3 | "version": "1.1.4", 4 | "startTime": "2018-08-24T15:19:09Z", 5 | "playbackRate": 1, 6 | "self": "vessels.urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 7 | "roles": [ 8 | "master", 9 | "main" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /samples/hello/docs-hello.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foobar marine server", 3 | "version": "1.0.4", 4 | "timestamp": "2018-06-21T15:09:16.704Z", 5 | "self": "vessels.urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 6 | "roles": [ 7 | "master", 8 | "main" 9 | ] 10 | } -------------------------------------------------------------------------------- /samples/put/put-with-context.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5", 3 | "requestId": "c0d79334-4e25-4245-8892-54e8ccc8021d", 4 | "put": { 5 | "path": "electrical.switches.anchorLight.state", 6 | "value": 1 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /samples/put/put.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "put": { 4 | "path": "electrical.switches.anchorLight.state", 5 | "value": 1 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /samples/subscribe/docs-subscription_protocol1.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.self", 3 | "subscribe": [ 4 | { 5 | "path": "navigation.speedThroughWater", 6 | "period": 1000, 7 | "format": "delta", 8 | "policy": "ideal", 9 | "minPeriod": 200 10 | }, 11 | { 12 | "path": "navigation.logTrip", 13 | "period": 10000 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /samples/subscribe/docs-subscription_protocol2.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.self", 3 | "subscribe": [ 4 | { 5 | "path": "environment.depth.belowTransducer" 6 | }, 7 | { 8 | "path": "navigation.speedThroughWater" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /samples/subscribe/docs-subscription_protocol3.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.*", 3 | "subscribe": [ 4 | { 5 | "path": "navigation.position", 6 | "period": 120000, 7 | "policy": "fixed" 8 | }, 9 | { 10 | "path": "navigation.courseOverGround", 11 | "period": 120000, 12 | "policy": "fixed" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /samples/subscribe/docs-subscription_protocol4.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.230029970", 3 | "subscribe": [ 4 | { 5 | "path": "navigation.position", 6 | "minPeriod": 60000, 7 | "policy": "instant" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /samples/subscribe/signalk-subscribe-mqtt.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.self", 3 | "websocket.connectionkey":"d2f691ac-a5ed-4cb7-b361-9072a24ce6bc", 4 | "reply-to":"signalk.3202a939-1681-4a74-ad4b-3a90212e4f33.vessels.self.navigation", 5 | "subscribe": [ 6 | { 7 | "path": "navigation.speedThroughWater", 8 | "period": 1000, 9 | "format": "delta", 10 | "policy": "ideal", 11 | "minPeriod": 200 12 | }, 13 | { 14 | "path": "navigation.logTrip", 15 | "period": 10000 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /samples/subscribe/signalk-subscribe.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.self", 3 | "subscribe": [{ 4 | "path": "navigation.speedThroughWater", 5 | "period": 1000, 6 | "format": "delta", 7 | "policy": "ideal", 8 | "minPeriod": 200 9 | }, { 10 | "path": "navigation.logTrip", 11 | "period": 10000 12 | }] 13 | } -------------------------------------------------------------------------------- /samples/unsubscribe/docs-subscription_protocol.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "*", 3 | "unsubscribe": [ 4 | { 5 | "path": "*" 6 | } 7 | ] 8 | } -------------------------------------------------------------------------------- /samples/unsubscribe/signalk-unsubscribe-mqtt.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.self", 3 | "websocket.connectionkey":"d2f691ac-a5ed-4cb7-b361-9072a24ce6bc", 4 | "unsubscribe": [ 5 | { 6 | "path": "navigation.speedThroughWater", 7 | "period": 1000, 8 | "format": "delta", 9 | "policy": "ideal", 10 | "minPeriod": 200 11 | }, 12 | { 13 | "path": "navigation.logTrip", 14 | "period": 10000 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /samples/unsubscribe/signalk-unsubscribe.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.self", 3 | "unsubscribe": [{ 4 | "path": "navigation.speedThroughWater", 5 | "period": 1000, 6 | "format": "delta", 7 | "policy": "ideal", 8 | "minPeriod": 200 9 | }, { 10 | "path": "navigation.logTrip", 11 | "period": 10000 12 | }] 13 | } -------------------------------------------------------------------------------- /schemas/delta.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "id": "https://signalk.org/specification/1.5.1/schemas/delta.json#", 5 | "title": "SignalK Delta message schema", 6 | "description": "Schema for defining updates and subscriptions to parts of a SignalK data model, for example for communicating updates of data", 7 | "required": [ 8 | "updates" 9 | ], 10 | "properties": { 11 | "context": { 12 | "type": "string", 13 | "description": "The context path of the updates, eg. the top level path plus object identifier.", 14 | "example": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5" 15 | }, 16 | "updates": { 17 | "type": "array", 18 | "description": "The updates", 19 | "items": { 20 | "type": "object", 21 | "oneOf": [ 22 | { 23 | "required": ["values"], 24 | "properties": {"$source":{}, "source": {}, "timestamp": {}, "values": {}}, 25 | "additionalProperties": false 26 | }, 27 | { 28 | "required": ["meta"], 29 | "properties": {"$source":{}, "source": {}, "timestamp": {}, "meta": {}}, 30 | "additionalProperties": false 31 | }, 32 | { 33 | "required": ["values", "meta"], 34 | "properties": {"$source":{}, "source": {}, "timestamp": {}, "values": {}, "meta": {}}, 35 | "additionalProperties": false 36 | } 37 | ], 38 | "not": { 39 | "allOf": [{ 40 | "required": ["source"] 41 | }, { 42 | "required": ["$source"] 43 | }] 44 | }, 45 | 46 | "properties": { 47 | "$source": { 48 | "$ref": "./definitions.json#/definitions/sourceRef" 49 | }, 50 | "source": { 51 | "$ref": "./definitions.json#/definitions/source" 52 | }, 53 | "timestamp": { 54 | "$ref": "./definitions.json#/definitions/timestamp" 55 | }, 56 | "values": { 57 | "type": "array", 58 | "items": { 59 | "type": "object", 60 | "required": [ 61 | "path", 62 | "value" 63 | ], 64 | "properties": { 65 | "path": { 66 | "type": "string", 67 | "description": "The local path to the data value", 68 | "example": "navigation.courseOverGroundMagnetic" 69 | }, 70 | "value": { 71 | "type": [ 72 | "string", 73 | "number", 74 | "object", 75 | "boolean", 76 | "null" 77 | ], 78 | "additionalProperties": true 79 | } 80 | } 81 | } 82 | }, 83 | "meta": { 84 | "type": "array", 85 | "items": { 86 | "type": "object", 87 | "required": [ 88 | "path", 89 | "value" 90 | ], 91 | "properties": { 92 | "path": { 93 | "type": "string", 94 | "description": "The local path to the data value", 95 | "example": "navigation.courseOverGroundMagnetic" 96 | }, 97 | "value": { 98 | "$ref": "./definitions.json#/definitions/meta" 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /schemas/discovery.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "id": "https://signalk.org/specification/1.5.1/schemas/discovery.json#", 5 | "title": "SignalK Discovery", 6 | "description": "Schema for SignalK discovery resources used to locate server endpoints.", 7 | "properties": { 8 | "endpoints": { 9 | "type": "object", 10 | "description": "The set of endpoints known to this server", 11 | "patternProperties": { 12 | ".*": { 13 | "description": "The name of a group of endpoints at the same version level", 14 | "type": "object", 15 | "required": [ 16 | "version" 17 | ], 18 | "properties": { 19 | "version": { 20 | "description": "The version this group supports", 21 | "$ref": "definitions.json#/definitions/version" 22 | }, 23 | "signalk-http": { 24 | "description": "The URL of a HTTP(s) REST API endpoint e.g. http://localhost/signalk/v1/api/", 25 | "$ref": "definitions.json#/definitions/url" 26 | }, 27 | "signalk-ws": { 28 | "description": "The URL of a WebSocket streaming endpoint e.g. http://localhost/signalk/v1/stream", 29 | "$ref": "definitions.json#/definitions/url" 30 | }, 31 | "signalk-tcp": { 32 | "description": "The URL of a tcp socket streaming endpoint e.g. tcp://localhost:55555", 33 | "$ref": "definitions.json#/definitions/url" 34 | } 35 | } 36 | } 37 | } 38 | }, 39 | "server": { 40 | "type": "object", 41 | "description": "Information about this server", 42 | "required": [ 43 | "id", 44 | "version" 45 | ], 46 | "properties": { 47 | "id": { 48 | "description": "The id of this server (signalk-server-node, iKommunicate, etc.)", 49 | "type": "string" 50 | }, 51 | "version": { 52 | "description": "The version of this server (not limited to signalk versioning rules).", 53 | "type": "string" 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /schemas/external/geojson/README.txt: -------------------------------------------------------------------------------- 1 | Contents copied from https://github.com/fge/sample-json-schemas 2 | Licence from there : 3 | "The licensing conditions are the same as they are for JSON Schema itself: content in this repository may be licensed under either of the AFL or BSD license." 4 | 5 | Copied here for easier reference in the spec. 6 | -------------------------------------------------------------------------------- /schemas/external/geojson/bbox.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "id": "http://json-schema.org/geojson/bbox.json#", 4 | "description": "A bounding box as defined by GeoJSON", 5 | "FIXME": "unenforceable constraint: even number of elements in array", 6 | "type": "array", 7 | "items": { "type": "number" } 8 | } -------------------------------------------------------------------------------- /schemas/external/geojson/crs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "title": "crs", 4 | "description": "a Coordinate Reference System object", 5 | "type": [ "object", "null" ], 6 | "required": [ "type", "properties" ], 7 | "properties": { 8 | "type": { "type": "string" }, 9 | "properties": { "type": "object" } 10 | }, 11 | "additionalProperties": false, 12 | "oneOf": [ 13 | { "$ref": "#/definitions/namedCrs" }, 14 | { "$ref": "#/definitions/linkedCrs" } 15 | ], 16 | "definitions": { 17 | "namedCrs": { 18 | "properties": { 19 | "type": { "enum": [ "name" ] }, 20 | "properties": { 21 | "required": [ "name" ], 22 | "additionalProperties": false, 23 | "properties": { 24 | "name": { 25 | "type": "string", 26 | "FIXME": "semantic validation necessary" 27 | } 28 | } 29 | } 30 | } 31 | }, 32 | "linkedObject": { 33 | "type": "object", 34 | "required": [ "href" ], 35 | "properties": { 36 | "href": { 37 | "type": "string", 38 | "format": "uri", 39 | "FIXME": "spec says \"dereferenceable\", cannot enforce that" 40 | }, 41 | "type": { 42 | "type": "string", 43 | "description": "Suggested values: proj4, ogjwkt, esriwkt" 44 | } 45 | } 46 | }, 47 | "linkedCrs": { 48 | "properties": { 49 | "type": { "enum": [ "link" ] }, 50 | "properties": { "$ref": "#/definitions/linkedObject" } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /schemas/external/geojson/geojson.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "id": "http://json-schema.org/geojson/geojson.json#", 4 | "title": "Geo JSON object", 5 | "description": "Schema for a Geo JSON object", 6 | "type": "object", 7 | "required": [ "type" ], 8 | "properties": { 9 | "crs": { "$ref": "http://json-schema.org/geojson/crs.json#" }, 10 | "bbox": { "$ref": "http://json-schema.org/geojson/bbox.json#" } 11 | }, 12 | "oneOf": [ 13 | { "$ref": "http://json-schema.org/geojson/geometry.json#" }, 14 | { "$ref": "#/definitions/geometryCollection" }, 15 | { "$ref": "#/definitions/feature" }, 16 | { "$ref": "#/definitions/featureCollection" } 17 | ], 18 | "definitions": { 19 | "geometryCollection": { 20 | "title": "GeometryCollection", 21 | "description": "A collection of geometry objects", 22 | "required": [ "geometries" ], 23 | "properties": { 24 | "type": { "enum": [ "GeometryCollection" ] }, 25 | "geometries": { 26 | "type": "array", 27 | "items": { "$ref": "http://json-schema.org/geojson/geometry.json#" } 28 | } 29 | } 30 | }, 31 | "feature": { 32 | "title": "Feature", 33 | "description": "A Geo JSON feature object", 34 | "required": [ "geometry", "properties" ], 35 | "properties": { 36 | "type": { "enum": [ "Feature" ] }, 37 | "geometry": { 38 | "oneOf": [ 39 | { "type": "null" }, 40 | { "$ref": "http://json-schema.org/geojson/geometry.json#" } 41 | ] 42 | }, 43 | "properties": { "type": [ "object", "null" ] }, 44 | "id": { "FIXME": "may be there, type not known (string? number?)" } 45 | } 46 | }, 47 | "featureCollection": { 48 | "title": "FeatureCollection", 49 | "description": "A Geo JSON feature collection", 50 | "required": [ "features" ], 51 | "properties": { 52 | "type": { "enum": [ "FeatureCollection" ] }, 53 | "features": { 54 | "type": "array", 55 | "items": { "$ref": "#/definitions/feature" } 56 | } 57 | } 58 | } 59 | } 60 | } 61 | 62 | -------------------------------------------------------------------------------- /schemas/external/geojson/geometry.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "id": "http://json-schema.org/geojson/geometry.json#", 4 | "title": "geometry", 5 | "description": "One geometry as defined by GeoJSON", 6 | "type": "object", 7 | "required": [ "type", "coordinates" ], 8 | "oneOf": [ 9 | { 10 | "title": "Point", 11 | "properties": { 12 | "type": { "enum": [ "Point" ] }, 13 | "coordinates": { "$ref": "#/definitions/position" } 14 | } 15 | }, 16 | { 17 | "title": "MultiPoint", 18 | "properties": { 19 | "type": { "enum": [ "MultiPoint" ] }, 20 | "coordinates": { "$ref": "#/definitions/positionArray" } 21 | } 22 | }, 23 | { 24 | "title": "LineString", 25 | "properties": { 26 | "type": { "enum": [ "LineString" ] }, 27 | "coordinates": { "$ref": "#/definitions/lineString" } 28 | } 29 | }, 30 | { 31 | "title": "MultiLineString", 32 | "properties": { 33 | "type": { "enum": [ "MultiLineString" ] }, 34 | "coordinates": { 35 | "type": "array", 36 | "items": { "$ref": "#/definitions/lineString" } 37 | } 38 | } 39 | }, 40 | { 41 | "title": "Polygon", 42 | "properties": { 43 | "type": { "enum": [ "Polygon" ] }, 44 | "coordinates": { "$ref": "#/definitions/polygon" } 45 | } 46 | }, 47 | { 48 | "title": "MultiPolygon", 49 | "properties": { 50 | "type": { "enum": [ "MultiPolygon" ] }, 51 | "coordinates": { 52 | "type": "array", 53 | "items": { "$ref": "#/definitions/polygon" } 54 | } 55 | } 56 | } 57 | ], 58 | "definitions": { 59 | "position": { 60 | "description": "A single position", 61 | "type": "array", 62 | "minItems": 2, 63 | "items": [ { "type": "number" }, { "type": "number" } ], 64 | "additionalItems": false 65 | }, 66 | "positionArray": { 67 | "description": "An array of positions", 68 | "type": "array", 69 | "items": { "$ref": "#/definitions/position" } 70 | }, 71 | "lineString": { 72 | "description": "An array of two or more positions", 73 | "allOf": [ 74 | { "$ref": "#/definitions/positionArray" }, 75 | { "minItems": 2 } 76 | ] 77 | }, 78 | "linearRing": { 79 | "description": "An array of four positions where the first equals the last", 80 | "allOf": [ 81 | { "$ref": "#/definitions/positionArray" }, 82 | { "minItems": 4 } 83 | ] 84 | }, 85 | "polygon": { 86 | "description": "An array of linear rings", 87 | "type": "array", 88 | "items": { "$ref": "#/definitions/linearRing" } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /schemas/groups/communication.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "id": "https://signalk.org/specification/1.5.1/schemas/groups/communication.json#", 5 | "description": "Schema describing the communication child-object of a Vessel.", 6 | "title": "communication", 7 | "properties": { 8 | "callsignVhf": { 9 | "type": "string", 10 | "description": "Callsign for VHF communication", 11 | "example": "ZL1234" 12 | }, 13 | 14 | "callsignHf": { 15 | "type": "string", 16 | "description": "Callsign for HF communication", 17 | "example": "ZL3RTH" 18 | }, 19 | 20 | "phoneNumber": { 21 | "type": "string", 22 | "description": "Phone number of skipper", 23 | "example": "+64xxxxxx" 24 | }, 25 | 26 | "emailHf": { 27 | "type": "string", 28 | "description": "Email address to be used for HF email (Winmail, Airmail, Sailmail)", 29 | "example": "motu@xxx.co.nz" 30 | }, 31 | 32 | "email": { 33 | "type": "string", 34 | "description": "Regular email for the skipper", 35 | "example": "robert@xxx.co.nz" 36 | }, 37 | 38 | "satPhoneNumber": { 39 | "type": "string", 40 | "description": "Satellite phone number for vessel.", 41 | "example": "+64xxxxxx" 42 | }, 43 | 44 | "skipperName": { 45 | "type": "string", 46 | "description": "Full name of the skipper of the vessel.", 47 | "example": "Fabian Tollenaar" 48 | }, 49 | 50 | "crewNames": { 51 | "type": "array", 52 | "description": "Array with the names of the crew", 53 | "items": [ 54 | { 55 | "type": "string", 56 | "description": "Name of a crew member of the vessel.", 57 | "example": "Catherine" 58 | } 59 | ] 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /schemas/groups/notifications.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "id": "https://signalk.org/specification/1.5.1/schemas/groups/notifications.json#", 4 | "title": "notifications", 5 | "definitions": { 6 | "notification": { 7 | "type": "object", 8 | "allOf": [ 9 | { 10 | "$ref": "../definitions.json#/definitions/commonValueFields" 11 | }, 12 | { 13 | "properties": { 14 | "value": { 15 | "type": "object", 16 | "required": [ 17 | "method", 18 | "state", 19 | "message" 20 | ], 21 | "properties": { 22 | "method": { 23 | "description": "Method to use to raise notifications", 24 | "type": "array", 25 | "items": { 26 | "$ref": "../definitions.json#/definitions/alarmMethodEnum" 27 | } 28 | }, 29 | "state": { 30 | "description": "Current notification state", 31 | "$ref": "../definitions.json#/definitions/alarmState" 32 | }, 33 | "message": { 34 | "description": "Message to display or speak", 35 | "type": "string" 36 | } 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | }, 43 | "notificationBranch": { 44 | "type": "object", 45 | "patternProperties": { 46 | "(^((?!^mob$|^fire$|^sinking$|^flooding$|^collision$|^grounding$|^listing$|^adrift$|^piracy$|^abandon$)[A-Za-z0-9-])+$)": { 47 | "description": "This regex pattern is used for validation of the path of the alarm", 48 | "oneOf": [ 49 | { 50 | "$ref": "#/definitions/notificationBranch", 51 | "example": "engine" 52 | }, 53 | { 54 | "$ref": "#/definitions/notification" 55 | } 56 | ] 57 | } 58 | }, 59 | "additionalProperties": false 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /schemas/groups/sensors.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "id": "https://signalk.org/specification/1.5.1/schemas/groups/sensors.json#", 5 | "description": "An object describing an individual sensor. It should be an object in vessel, named using a unique name or UUID", 6 | "title": "sensor", 7 | "properties": { 8 | "name": { 9 | "type": "string", 10 | "description": "The common name of the sensor" 11 | }, 12 | 13 | "sensorType": { 14 | "type": "string", 15 | "description": "The datamodel definition of the sensor data. FIXME - need to create a definitions lib of sensor datamodel types" 16 | }, 17 | 18 | "sensorData": { 19 | "type": "string", 20 | "description": "The data of the sensor data. FIXME - need to ref the definitions of sensor types" 21 | }, 22 | 23 | "fromBow": { 24 | "$ref": "../definitions.json#/definitions/numberValue", 25 | "description": "The distance from the bow to the sensor location" 26 | }, 27 | 28 | "fromCenter": { 29 | "$ref": "../definitions.json#/definitions/numberValue", 30 | "description": "The distance from the centerline to the sensor location, -ve to starboard, +ve to port" 31 | }, 32 | 33 | "class": { 34 | "$ref": "../definitions.json#/definitions/stringValue", 35 | "pattern_": "^[AB]\\Z", 36 | "description": "AIS transponder class in sensors.ais.class, A or B" 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /schemas/hello.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "id": "https://signalk.org/specification/1.5.1/schemas/hello.json#", 5 | "title": "SignalK Websockets Hello message schema", 6 | "description": "Schema for defining the hello message passed from the server to a client following succesful websocket connection", 7 | "required": [ 8 | "version", 9 | "roles" 10 | ], 11 | "properties": { 12 | "version": { 13 | "description": "Version of the schema and APIs that this data is using in canonical format i.e. 1.5.0.", 14 | "$ref": "definitions.json#/definitions/version" 15 | }, 16 | "name": { 17 | "type": "string", 18 | "description": "The name of the Signal K server software", 19 | "example": "iKommunicate" 20 | }, 21 | "timestamp": { 22 | "$ref": "./definitions.json#/definitions/timestamp" 23 | }, 24 | "startTime": { 25 | "$ref": "./definitions.json#/definitions/timestamp", 26 | "description": "Starttime for history playback connections" 27 | }, 28 | "playbackRate": { 29 | "type": "number", 30 | "description": "Playback rate for history playback connections: 1 is real time, 2 is two times and 0.5 half the real time rate" 31 | }, 32 | "self": { 33 | "type": "string", 34 | "description": "This holds the context (prefix + UUID, MMSI or URL in dot notation) of the server's self object.", 35 | "example": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5", 36 | "oneOf": [ 37 | { 38 | "pattern": "^vessels.(urn:mrn:(imo:mmsi:[2-7][0-9]{8}$|signalk:uuid:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$))|(http(s?):.*|mailto:.*|tel:(\\+?)[0-9]{4,})$" 39 | }, 40 | { 41 | "pattern": "^aircraft.(urn:mrn:(imo:mmsi:1[0-9]{8}$|signalk:uuid:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$))|(http(s?):.*|mailto:.*|tel:(\\+?)[0-9]{4,})$" 42 | }, 43 | { 44 | "pattern": "^aton.(urn:mrn:(imo:mmsi:99[0-9]{7}$|signalk:uuid:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$))|(http(s?):.*|mailto:.*|tel:(\\+?)[0-9]{4,})$" 45 | }, 46 | { 47 | "pattern": "^sar.(urn:mrn:(imo:mmsi:97[0-9]{7}$|signalk:uuid:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}$))|(http(s?):.*|mailto:.*|tel:(\\+?)[0-9]{4,})$" 48 | } 49 | ] 50 | }, 51 | "roles": { 52 | "type": "array", 53 | "description": "The designated roles of the server", 54 | "oneOf": [ 55 | { 56 | "minItems": 2, 57 | "maxItems": 2, 58 | "items": [{"enum": ["master"]}, {"enum": ["main", "aux"]}] 59 | }, 60 | { 61 | "minItems": 1, 62 | "maxItems": 1, 63 | "items": [{"enum": ["slave"]}] 64 | } 65 | ] 66 | } 67 | }, 68 | "additionalProperties": false 69 | } -------------------------------------------------------------------------------- /schemas/messages/auth.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "id": "https://signalk.org/specification/1.5.1/schemas/messages/auth.json#", 5 | "title": "SignalK Auth message schema", 6 | "description": "Schema for authentication messages", 7 | "required": ["requestId"], 8 | "oneOf": [{ 9 | "required": ["login"] 10 | }, { 11 | "required": ["logout"] 12 | },{ 13 | "required": ["validate"] 14 | }], 15 | "properties": { 16 | "requestId": { 17 | "type": "string", 18 | "pattern": "[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}" 19 | }, 20 | "state": { 21 | "description": "Used in response, returns request state", 22 | "enum": [ 23 | "COMPLETED", 24 | "PENDING", 25 | "FAILED" 26 | ] 27 | 28 | }, 29 | "result": { 30 | "type": "integer", 31 | "description": "Used in response, returns result code, see HTTP codes for possible values and meanings" 32 | }, 33 | "login": { 34 | "type": "object", 35 | "title": "Login object.", 36 | "description": "Used to login with username/password, returns token on success", 37 | "oneOf": [{ 38 | "required": ["username","password"] 39 | }, { 40 | "required": ["token"] 41 | }], 42 | "properties": { 43 | "username": { 44 | "type": "string" 45 | }, 46 | "password": { 47 | "type": "string" 48 | }, 49 | "token": { 50 | "type": "string" 51 | }, 52 | "timeToLive": { 53 | "type": "integer", 54 | "description": "Expiry time of the token in seconds" 55 | } 56 | } 57 | }, 58 | "logout": { 59 | "type": "object", 60 | "title": "Logout object.", 61 | "description": "Used to logout or invalidate a token", 62 | "required": ["token"], 63 | "properties": { 64 | "token": { 65 | "type": "string" 66 | } 67 | } 68 | }, 69 | "validate": { 70 | "type": "object", 71 | "title": "Validate object.", 72 | "description": "Used to validate a token and refresh its expiry, returns a valid token on success", 73 | "required": ["token"], 74 | "properties": { 75 | "token": { 76 | "type": "string" 77 | }, 78 | "timeToLive": { 79 | "type": "integer", 80 | "description": "Expiry time of the token in seconds" 81 | } 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /schemas/messages/get.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "id": "https://signalk.org/specification/1.5.1/schemas/messages/get.json#", 5 | "title": "SignalK GET message schema", 6 | "description": "Schema for getting values of parts of a SignalK data model, for example the state of the anchor watch", 7 | "required": [ 8 | "requestId","context","get" 9 | ], 10 | "properties": { 11 | "requestId": { 12 | "type": "string", 13 | "pattern": "[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}" 14 | }, 15 | "context": { 16 | "type": "string", 17 | "description": "The context path of the get, eg. the top level path plus object identifier.", 18 | "example": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5" 19 | }, 20 | "get": { 21 | "oneOf":[{ 22 | "type": "array", 23 | "description": "DEPRECATED: Prefer GET format with no array. A transport agnostic equivalent to a REST GET request. GET requests use 'request/response' semantics.", 24 | "items": { 25 | "type": "object", 26 | "properties": { 27 | "path": { 28 | "type": "string", 29 | "description": "The path to get." 30 | } 31 | }, 32 | "required": [ 33 | "path" 34 | ] 35 | } 36 | }, 37 | { 38 | "type": "object", 39 | "properties": { 40 | "path": { 41 | "type": "string", 42 | "description": "The path to set. A transport agnostic equivalent to a REST GET request. GET requests use 'request/response' semantics." 43 | } 44 | }, 45 | "required": [ 46 | "path" 47 | ] 48 | } 49 | ] 50 | } 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /schemas/messages/put.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-04/schema#", 4 | "id": "https://signalk.org/specification/1.5.1/schemas/messages/put.json#", 5 | "title": "SignalK PUT message schema", 6 | "description": "Schema for request/response updates to parts of a SignalK data model, for example turning on anchor watch", 7 | "required": [ 8 | "requestId","put" 9 | ], 10 | "properties": { 11 | "requestId": { 12 | "type": "string", 13 | "pattern": "[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}" 14 | }, 15 | "context": { 16 | "type": "string", 17 | "description": "The context path of the put, eg. the top level path plus object identifier. Optional, defaults to 'vessels.self'", 18 | "example": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5" 19 | }, 20 | "put": { 21 | "oneOf":[{ 22 | "type": "array", 23 | "description": "DEPRECATED: Prefer PUT format with no array. A transport agnostic equivalent to a REST PUT request. PUT requests use 'request/response' semantics, if no acknowlegement is required use the UPDATES message.", 24 | "items": { 25 | "type": "object", 26 | "properties": { 27 | "path": { 28 | "type": "string", 29 | "description": "The path to set." 30 | }, 31 | "value": { 32 | "type": [ 33 | "string", 34 | "number", 35 | "object", 36 | "boolean", 37 | "null" 38 | ], 39 | "description": "The value to set at the path" 40 | } 41 | }, 42 | "required": [ 43 | "path", 44 | "value" 45 | ] 46 | } 47 | }, 48 | { 49 | "type": "object", 50 | "properties": { 51 | "path": { 52 | "type": "string", 53 | "description": "The path to set. A transport agnostic equivalent to a REST PUT request. PUT requests use 'request/response' semantics, if no acknowlegement is required use the UPDATES message." 54 | }, 55 | "value": { 56 | "type": [ 57 | "string", 58 | "number", 59 | "object", 60 | "boolean", 61 | "null" 62 | ], 63 | "description": "The value to set at the path" 64 | } 65 | }, 66 | "required": [ 67 | "path", 68 | "value" 69 | ] 70 | } 71 | ] 72 | } 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /schemas/messages/subscribe.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "id": "https://signalk.org/specification/1.5.1/schemas/messages/subscribe.json#", 4 | "title": "SignalK SUBSCRIBE message schema", 5 | "type": "object", 6 | "description": "A message to allow a client to subscribe for data updates from a signalk server", 7 | "properties": { 8 | "context": { 9 | "id": "context", 10 | "type": "string", 11 | "title": "Context Path.", 12 | "description": "The root path for all subsequent paths, usually a vessel's path.", 13 | "name": "context", 14 | "example": "vessels.230099999" 15 | }, 16 | 17 | "websocket.connectionkey": { 18 | "id": "websocket.connectionkey", 19 | "type": "string", 20 | "title": "Websocket.connectionkey.", 21 | "description": "An optional session key that is used in STOMP and MQTT messages where there are no session facilities", 22 | "name": "websocket.connectionkey", 23 | "example": "d2f691ac-a5ed-4cb7-b361-9072a24ce6bc" 24 | }, 25 | 26 | "reply-to": { 27 | "id": "reply-to", 28 | "type": "string", 29 | "title": "Reply-to.", 30 | "description": "A reply queue that is used in STOMP and MQTT messages where there are no session facilities.", 31 | "name": "reply-to", 32 | "example": "signalk.3202a939-1681-4a74-ad4b-3a90212e4f33.vessels.motu.navigation" 33 | }, 34 | 35 | "subscribe": { 36 | "id": "subscribe", 37 | "type": "array", 38 | "title": "Subscribe.", 39 | "description": "An array of paths to subscribe to, with optional criteria", 40 | "name": "subscribe", 41 | "items": [ 42 | { 43 | "type": "object", 44 | "title": "Path object.", 45 | "description": "A path object with optional criteria to control output", 46 | "properties": { 47 | "path": { 48 | "id": "path", 49 | "type": "string", 50 | "title": "Path.", 51 | "description": "The path to subscribe to.", 52 | "name": "path", 53 | "example": "navigation.speedThroughWater" 54 | }, 55 | 56 | "period": { 57 | "id": "period", 58 | "type": "integer", 59 | "title": "Period.", 60 | "description": "The subscription will be sent every period millisecs.", 61 | "name": "period", 62 | "default": 1000 63 | }, 64 | 65 | "format": { 66 | "id": "format", 67 | "type": "string", 68 | "title": "Format.", 69 | "description": "The signal K format to use (full/delta) for the message.", 70 | "name": "format", 71 | "default": "delta" 72 | }, 73 | 74 | "policy": { 75 | "id": "policy", 76 | "type": "string", 77 | "title": "Policy schema.", 78 | "description": "The policy for sending messages (instant/ideal/fixed).", 79 | "name": "policy", 80 | "default": "ideal" 81 | }, 82 | 83 | "minPeriod": { 84 | "id": "minPeriod", 85 | "type": "integer", 86 | "title": "MinPeriod.", 87 | "description": "If policy=immediate or ideal, consequetive messages will be buffered until minPeriod has expired so the reciever is not swamped.", 88 | "name": "minPeriod", 89 | "default": 200 90 | } 91 | } 92 | } 93 | ] 94 | } 95 | }, 96 | 97 | "required": [ 98 | "context", 99 | "subscribe" 100 | ] 101 | } 102 | -------------------------------------------------------------------------------- /schemas/messages/unsubscribe.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "id": "https://signalk.org/specification/1.5.1/schemas/messages/unsubscribe.json#", 4 | "title": "SignalK UNSUBSCRIBE message schema", 5 | "type": "object", 6 | "description": "A message to allow a client to unsubscribe from data updates from a signalk server", 7 | "properties": { 8 | "context": { 9 | "id": "context", 10 | "type": "string", 11 | "title": "Context Path.", 12 | "description": "The root path for all subsequent paths, usually a vessel's path.", 13 | "name": "context", 14 | "example": "vessels.230099999" 15 | }, 16 | 17 | "websocket.connectionkey": { 18 | "id": "websocket.connectionkey", 19 | "type": "string", 20 | "title": "Websocket.connectionkey.", 21 | "description": "An optional session key that is used in STOMP and MQTT messages where there are no session facilities", 22 | "name": "websocket.connectionkey", 23 | "example": "d2f691ac-a5ed-4cb7-b361-9072a24ce6bc" 24 | }, 25 | 26 | "reply-to": { 27 | "id": "reply-to", 28 | "type": "string", 29 | "title": "Reply-to.", 30 | "description": "A reply queue that is used in STOMP and MQTT messages where there are no session facilities.", 31 | "name": "reply-to", 32 | "example": "signalk.3202a939-1681-4a74-ad4b-3a90212e4f33.vessels.motu.navigation" 33 | }, 34 | 35 | "unsubscribe": { 36 | "id": "unsubscribe", 37 | "type": "array", 38 | "title": "Unsubscribe.", 39 | "description": "An array of paths to unsubscribe from.", 40 | "name": "unsubscribe", 41 | "items": [ 42 | { 43 | "type": "object", 44 | "title": "Path object.", 45 | "description": "A path object with optional criteria to control output", 46 | "properties": { 47 | "path": { 48 | "id": "path", 49 | "type": "string", 50 | "title": "Path.", 51 | "description": "The path to subscribe to.", 52 | "name": "path", 53 | "example": "navigation.speedThroughWater" 54 | }, 55 | 56 | "period": { 57 | "id": "period", 58 | "type": "integer", 59 | "title": "Period.", 60 | "description": "The subscription will be sent every period millisecs.", 61 | "name": "period", 62 | "default": 1000 63 | }, 64 | 65 | "format": { 66 | "id": "format", 67 | "type": "string", 68 | "title": "Format.", 69 | "description": "The signal K format to use (full/delta) for the message.", 70 | "name": "format", 71 | "default": "delta" 72 | }, 73 | 74 | "policy": { 75 | "id": "policy", 76 | "type": "string", 77 | "title": "Policy schema.", 78 | "description": "The policy for sending messages (instant/ideal/fixed).", 79 | "name": "policy", 80 | "default": "ideal" 81 | }, 82 | 83 | "minPeriod": { 84 | "id": "minPeriod", 85 | "type": "integer", 86 | "title": "MinPeriod.", 87 | "description": "If policy=immediate or ideal, consequetive messages will be buffered until minPeriod has expired so the reciever is not swamped.", 88 | "name": "minPeriod", 89 | "default": 200 90 | } 91 | } 92 | } 93 | ] 94 | } 95 | }, 96 | 97 | "required": [ 98 | "context", 99 | "unsubscribe" 100 | ] 101 | } 102 | -------------------------------------------------------------------------------- /src/delta.js: -------------------------------------------------------------------------------- 1 | var _ = require("lodash") 2 | 3 | /* 4 | * Credit for these function goes to @tkurki 5 | */ 6 | 7 | var addToTree = function (pathValue, source, tree) { 8 | var result = {}; 9 | var temp = tree; 10 | var parts = msg.path.split('.'); 11 | for (var i = 0; i < parts.length - 1; i++) { 12 | temp[parts[i]] = {}; 13 | temp = temp[parts[i]]; 14 | } 15 | temp[parts[parts.length - 1]] = msg; 16 | return result; 17 | } 18 | 19 | 20 | function addAsNested(pathValue, source, timestamp, result) { 21 | var temp = result; 22 | var parts = pathValue.path.split('.'); 23 | for (var i = 0; i < parts.length - 1; i++) { 24 | if (typeof temp[parts[i]] === 'undefined') { 25 | temp[parts[i]] = {}; 26 | } 27 | temp = temp[parts[i]]; 28 | }; 29 | 30 | //mapping produced an object like {latitude:...,longitude:...} 31 | if (typeof pathValue.value === 'object') { 32 | temp[parts[parts.length - 1]] = pathValue.value; 33 | temp[parts[parts.length - 1]].source = source; 34 | temp[parts[parts.length - 1]].timestamp = timestamp + ''; 35 | } else { 36 | temp[parts[parts.length - 1]] = { 37 | value: pathValue.value, 38 | source: _.clone(source), 39 | timestamp: timestamp + '' 40 | }; 41 | delete temp[parts[parts.length - 1]].source.timestamp; 42 | } 43 | } 44 | 45 | function valuesToSubTree(delta) { 46 | var valueTree = {}; 47 | var timestamp = delta.updates[0].source.timestamp; 48 | delta.updates[0].values.forEach(function(pathValue) { 49 | addAsNested(pathValue, delta.updates[0].source, timestamp, valueTree); 50 | }); 51 | return valueTree; 52 | } 53 | 54 | function deltaToNested(delta) { 55 | var result = {}; 56 | var contextPointer = result; 57 | var pathPropertyNames = delta.context.split('.'); 58 | for (var i = 0; i < pathPropertyNames.length - 1; i++) { 59 | contextPointer[pathPropertyNames[i]] = {}; 60 | contextPointer = contextPointer[pathPropertyNames[i]]; 61 | }; 62 | contextPointer[pathPropertyNames[pathPropertyNames.length -1]] = valuesToSubTree(delta); 63 | return result; 64 | } 65 | 66 | module.exports.deltaToNested = deltaToNested; 67 | -------------------------------------------------------------------------------- /test/data/auth-invalid/login-request-no-pass.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "3202a939-1681-4a74-ad4b-3a90212e4f33", 3 | "login": { 4 | "username": "john_doe" 5 | } 6 | } -------------------------------------------------------------------------------- /test/data/auth-invalid/login-request-no-requestid.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "login": { 4 | "username": "john_doe", 5 | "password": "password" 6 | } 7 | } -------------------------------------------------------------------------------- /test/data/auth-invalid/login-request-no-username.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "3202a939-1681-4a74-ad4b-3a90212e4f33", 3 | "login": { 4 | "password": "password" 5 | } 6 | } -------------------------------------------------------------------------------- /test/data/auth-invalid/login-response-no-token.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "3202a939-1681-4a74-ad4b-3a90212e4f33", 3 | "state": "COMPLETED", 4 | "result": 403 5 | } -------------------------------------------------------------------------------- /test/data/auth-invalid/validate-logout-request.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "3202a939-1681-4a74-ad4b-3a90212e4f33", 3 | "validate": { 4 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2UiOiIxMjM0LTQ1NjUz" 5 | }, 6 | "logout": { 7 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2UiOiIxMjM0LTQ1NjUz" 8 | } 9 | } -------------------------------------------------------------------------------- /test/data/auth-valid/login-request.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "3202a939-1681-4a74-ad4b-3a90212e4f33", 3 | "login": { 4 | "username": "john_doe", 5 | "password": "password" 6 | } 7 | } -------------------------------------------------------------------------------- /test/data/auth-valid/login-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "3202a939-1681-4a74-ad4b-3a90212e4f33", 3 | "state": "COMPLETED", 4 | "result": 200, 5 | "login": { 6 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2UiOiIxMjM0LTQ1NjUz", 7 | "timeToLive": 3600 8 | } 9 | } -------------------------------------------------------------------------------- /test/data/auth-valid/logout-request.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "3202a939-1681-4a74-ad4b-3a90212e4f33", 3 | "logout": { 4 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2UiOiIxMjM0LTQ1NjUz" 5 | } 6 | } -------------------------------------------------------------------------------- /test/data/auth-valid/validate-request.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "3202a939-1681-4a74-ad4b-3a90212e4f33", 3 | "validate": { 4 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2UiOiIxMjM0LTQ1NjUz" 5 | } 6 | } -------------------------------------------------------------------------------- /test/data/auth-valid/validate-response.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "3202a939-1681-4a74-ad4b-3a90212e4f33", 3 | "state": "COMPLETED", 4 | "result": 200, 5 | "validate": { 6 | "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2UiOiIxMjM0LTQ1NjUz", 7 | "timeToLive": 3600 8 | } 9 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/delta-empty_object.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/data/delta-invalid/delta-extra_property.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "bar", 3 | "updates": [{ 4 | "source": { 5 | "label": "", 6 | "type": "NMEA0183", 7 | "sentence": "GLL", 8 | "talker": "II" 9 | }, 10 | "timestamp": "2013-10-08T15:47:28.263Z", 11 | "values": [{ 12 | "path": "a.b.c", 13 | "value": 1234 14 | }] 15 | }], 16 | "foo": "bar" 17 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/delta-update-with-get.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "bar", 3 | "updates": [{ 4 | "source": { 5 | "label": "", 6 | "type": "NMEA0183", 7 | "sentence": "GLL", 8 | "talker": "II" 9 | }, 10 | "timestamp": "2013-10-08T15:47:28.263Z", 11 | "values": [{ 12 | "path": "a.b.c", 13 | "value": 1234 14 | }] 15 | }], 16 | "get": [{ 17 | "path": "a.b.c", 18 | "value": 1234 19 | }] 20 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/delta-update-with-put-no-requestId.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "bar", 3 | "updates": [{ 4 | "source": { 5 | "label": "", 6 | "type": "NMEA0183", 7 | "sentence": "GLL", 8 | "talker": "II" 9 | }, 10 | "timestamp": "2013-10-08T15:47:28.263Z", 11 | "values": [{ 12 | "path": "a.b.c", 13 | "value": 1234 14 | }] 15 | }], 16 | "put": [{ 17 | "path": "a.b.c", 18 | "value": 1234 19 | }] 20 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/meta-missing-value.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:234567890", 3 | "updates": [ 4 | { 5 | "meta": [ 6 | { 7 | "path": "propulsion.0.revolutions" 8 | } 9 | ] 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/sources-bad_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:200000000", 3 | "updates": [{ 4 | "source": { 5 | "sentence": "HDT", 6 | "label": "0183-1", 7 | "talker": "II" 8 | }, 9 | "$source": "i2c-0.0x48.amps", 10 | "timestamp": "2016-08-03T07:55:57.000Z", 11 | "values": [{ 12 | "path": "navigation.headingTrue", 13 | "value": 0.2231 14 | }] 15 | }] 16 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/sources-bad_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:200000000", 3 | "updates": [{ 4 | "source": "test", 5 | "timestamp": "2016-08-03T07:55:57.000Z", 6 | "values": [{ 7 | "path": "navigation.headingTrue", 8 | "value": 0.2231 9 | }] 10 | }] 11 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/sources-bad_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:200000000", 3 | "updates": [{ 4 | "$source": "path with space", 5 | "timestamp": "2016-08-03T07:55:57.000Z", 6 | "values": [{ 7 | "path": "navigation.headingTrue", 8 | "value": 0.2231 9 | }] 10 | }] 11 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/sources-bad_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:200000000", 3 | "updates": [{ 4 | "$source": { 5 | "sentence": "HDT", 6 | "label": "0183-1", 7 | "talker": "II" 8 | }, 9 | "timestamp": "2016-08-03T07:55:57.000Z", 10 | "values": [{ 11 | "path": "navigation.headingTrue", 12 | "value": 0.2231 13 | }] 14 | }] 15 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/value-path_missing.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": [{ 3 | "values": [{ 4 | "value": 1234 5 | }] 6 | }] 7 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/value-value_and_path_missing.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": [{ 3 | "values": [{ 4 | }] 5 | }] 6 | } -------------------------------------------------------------------------------- /test/data/delta-invalid/value-value_missing.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": [{ 3 | "values": [{ 4 | "path": "a.b.c" 5 | }] 6 | }] 7 | } -------------------------------------------------------------------------------- /test/data/delta-valid/delta-object_with_only_an_empty_updates_validates.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": [] 3 | } -------------------------------------------------------------------------------- /test/data/delta-valid/delta-simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "bar", 3 | "updates": [{ 4 | "source": { 5 | "label": "", 6 | "type": "NMEA0183", 7 | "sentence": "GLL", 8 | "talker": "II" 9 | }, 10 | "timestamp": "2013-10-08T15:47:28.263Z", 11 | "values": [{ 12 | "path": "a.b.c", 13 | "value": 1234 14 | }] 15 | }] 16 | } -------------------------------------------------------------------------------- /test/data/delta-valid/delta-without_context_source_and_timestamp_is_valid.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": [{ 3 | "values": [{ 4 | "path": "a.b.c", 5 | "value": 1234 6 | }] 7 | }] 8 | } -------------------------------------------------------------------------------- /test/data/delta-valid/identity-blank_ie_self.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": [ ] 3 | } 4 | -------------------------------------------------------------------------------- /test/data/delta-valid/identity-mmsi.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:230099999", 3 | "updates": [ ] 4 | } 5 | -------------------------------------------------------------------------------- /test/data/delta-valid/identity-url.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.https://vessels.signalk.org/someboatidentity", 3 | "updates": [ ] 4 | } 5 | -------------------------------------------------------------------------------- /test/data/delta-valid/identity-uuid.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "updates": [ ] 4 | } 5 | -------------------------------------------------------------------------------- /test/data/delta-valid/meta-and-values.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:234567890", 3 | "updates": [ 4 | { 5 | "source": { 6 | "label": "N2000-01", 7 | "type": "NMEA2000", 8 | "src": "017", 9 | "pgn": 127488 10 | }, 11 | "timestamp": "2013-10-08T15:47:28.263Z", 12 | "meta": [ 13 | { 14 | "path": "propulsion.0.revolutions", 15 | "value": { 16 | "description": "Engine revolutions (x60 for RPM)", 17 | "units": "Hz" 18 | } 19 | } 20 | ], 21 | "values": [ 22 | { 23 | "path": "propulsion.0.revolutions", 24 | "value": 11 25 | } 26 | ] 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /test/data/delta-valid/meta-delta.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:imo:mmsi:234567890", 3 | "updates": [ 4 | { 5 | "meta": [ 6 | { 7 | "path": "environment.depth.belowTransducer", 8 | "value": { 9 | "description": "Depth below Transducer", 10 | "units": "m", 11 | "timeout": 2.5, 12 | "zones": [ 13 | { 14 | "upper": 5.0, 15 | "state": "warn", 16 | "message": "Shallow water" 17 | } 18 | ], 19 | "warnMethod": ["sound"] 20 | } 21 | } 22 | ] 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /test/data/delta-valid/value-can_be_boolean.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": [{ 3 | "source": { 4 | "label": "", 5 | "type": "NMEA2000", 6 | "pgn": 129025, 7 | "src": "160" 8 | }, 9 | "values": [{ 10 | "path": "navigation.position", 11 | "value": true 12 | }] 13 | }], 14 | "context": "vessels.123456789" 15 | } -------------------------------------------------------------------------------- /test/data/delta-valid/value-can_be_null.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": [{ 3 | "source": { 4 | "label": "", 5 | "type": "NMEA2000", 6 | "pgn": 129025, 7 | "src": "160" 8 | }, 9 | "values": [{ 10 | "path": "navigation.position", 11 | "value": null 12 | }] 13 | }], 14 | "context": "vessels.123456789" 15 | } -------------------------------------------------------------------------------- /test/data/delta-valid/value-can_be_number.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": [{ 3 | "source": { 4 | "label": "", 5 | "type": "NMEA2000", 6 | "pgn": 129025, 7 | "src": "160" 8 | }, 9 | "values": [{ 10 | "path": "navigation.position", 11 | "value": 1.0 12 | }] 13 | }], 14 | "context": "vessels.123456789" 15 | } -------------------------------------------------------------------------------- /test/data/delta-valid/value-can_be_object.json: -------------------------------------------------------------------------------- 1 | { 2 | "updates": [{ 3 | "source": { 4 | "label": "", 5 | "type": "NMEA2000", 6 | "pgn": 129025, 7 | "src": "160" 8 | }, 9 | "values": [{ 10 | "path": "navigation.position", 11 | "value": { 12 | "longitude": 24.9025173, 13 | "latitude": 60.039317, 14 | "source": { 15 | "label": "", 16 | "type": "NMEA2000", 17 | "pgn": "129025", 18 | "src": "160" 19 | }, 20 | "timestamp": "2014-08-15-16:00:05.770" 21 | } 22 | }] 23 | }], 24 | "context": "vessels.123456789" 25 | } -------------------------------------------------------------------------------- /test/data/discovery-invalid/endpoints-version_missing.json: -------------------------------------------------------------------------------- 1 | { 2 | "endpoints": { 3 | "v1": { 4 | "signalk-http": "http://192.168.1.2/signalk/v1/api/", 5 | "signalk-ws": "ws://192.168.1.2:34567/signalk/v1/stream", 6 | "signalk-tcp": "tcp://localhost:55555" 7 | }, 8 | "v3": { 9 | "version": "3.0.0", 10 | "signalk-http": "http://192.168.1.2/signalk/v3/api/", 11 | "signalk-ws": "ws://192.168.1.2/signalk/v3/stream" 12 | } 13 | }, 14 | "server": { 15 | "id": "test-server", 16 | "version": "1.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/data/discovery-valid/endpoints-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "endpoints": { 3 | "v1": { 4 | "version": "1.1.2", 5 | "signalk-http": "http://192.168.1.2/signalk/v1/api/", 6 | "signalk-ws": "ws://192.168.1.2:34567/signalk/v1/stream", 7 | "signalk-tcp": "tcp://localhost:55555" 8 | }, 9 | "v3": { 10 | "version": "3.0.0", 11 | "signalk-http": "http://192.168.1.2/signalk/v3/api/", 12 | "signalk-ws": "ws://192.168.1.2/signalk/v3/stream" 13 | } 14 | }, 15 | "server": { 16 | "id": "test-server", 17 | "version": "1.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/data/full-invalid/aircraft-mmsi_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "aircraft": { 3 | "urn:mrn:imo:mmsi:116982330": { 4 | "mmsi": "230099999", 5 | "navigation": { 6 | "position": { 7 | "timestamp": "2015-03-06T16:57:53.643Z", 8 | "value": { 9 | "longitude": 173.1693, 10 | "latitude": -41.156426, 11 | "altitude": 0 12 | }, 13 | "$source": "sources.gps_0183_RMC" 14 | }, 15 | "courseOverGroundTrue": { 16 | "timestamp": "2015-03-06T16:57:53.643Z", 17 | "$source": "sources.gps_0183_RMC", 18 | "value": 245.69 19 | } 20 | } 21 | } 22 | }, 23 | "version": "1.0.0", 24 | "self": "urn:mrn:imo:mmsi:116982330" 25 | } -------------------------------------------------------------------------------- /test/data/full-invalid/ais-aisShipType_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "design": { 6 | "aisShipType": { 7 | "value": { 8 | "id": 37, 9 | "name": "Sailing" 10 | } 11 | } 12 | } 13 | } 14 | }, 15 | "version": "1.0.0", 16 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 17 | } 18 | -------------------------------------------------------------------------------- /test/data/full-invalid/ais-source0.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 5 | } 6 | }, 7 | "sources": { 8 | "vhf": { 9 | "ais-b":{ 10 | "ais":{ 11 | "aisType": 0 12 | } 13 | }, 14 | "label": "AIS-0.0", 15 | "type": "AIS" 16 | } 17 | }, 18 | "version": "1.0.0", 19 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 20 | } -------------------------------------------------------------------------------- /test/data/full-invalid/ais-source28.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 5 | } 6 | }, 7 | "sources": { 8 | "vhf": { 9 | "ais-b":{ 10 | "ais":{ 11 | "aisType": 28.3 12 | } 13 | }, 14 | "label": "AIS-28.0", 15 | "type": "AIS" 16 | } 17 | }, 18 | "version": "1.0.0", 19 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 20 | } 21 | -------------------------------------------------------------------------------- /test/data/full-invalid/alarms-must_not_have_additional_keys_in_notifications.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "notifications": { 6 | "navigation": { 7 | "position": { 8 | "method": ["sound"], 9 | "state": "warn", 10 | "message": "Position is more than 5 minutes old", 11 | "timestamp": "2017-01-23T14:24:21Z", 12 | "reason": "Insufficient satellites in view" 13 | } 14 | } 15 | } 16 | } 17 | }, 18 | "version": "1.0.0", 19 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 20 | } -------------------------------------------------------------------------------- /test/data/full-invalid/alarms-must_not_have_dot_notation_in_branch_names.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "notifications": { 6 | "navigation.position": { 7 | "method": ["sound"], 8 | "state": "warn", 9 | "message": "Position is more than 5 minutes old", 10 | "timestamp": "2017-01-23T14:24:21Z" 11 | } 12 | } 13 | } 14 | }, 15 | "version": "1.0.0", 16 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 17 | } -------------------------------------------------------------------------------- /test/data/full-invalid/alarms-must_not_have_incorrectly_spelled_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "notifications": { 6 | "navigation": { 7 | "position": { 8 | "method": ["sound"], 9 | "state": "warn", 10 | "mesage": "Position is more than 5 minutes old", 11 | "timestamp": "2017-01-23T14:24:21Z" 12 | } 13 | } 14 | } 15 | } 16 | }, 17 | "version": "1.0.0", 18 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 19 | } -------------------------------------------------------------------------------- /test/data/full-invalid/alarms-must_not_have_spaces_in_branch_names.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "notifications": { 6 | "navigation": { 7 | "position error": { 8 | "method": ["sound"], 9 | "state": "warn", 10 | "message": "Position is more than 5 minutes old", 11 | "timestamp": "2017-01-23T14:24:21Z" 12 | } 13 | } 14 | } 15 | } 16 | }, 17 | "version": "1.0.0", 18 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 19 | } -------------------------------------------------------------------------------- /test/data/full-invalid/alarms-must_not_have_sub_trees_beneath_well_defined_notifications.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "notifications": { 6 | "mob": { 7 | "navigation": { 8 | "method": ["sound", "visual"], 9 | "state": "emergency", 10 | "message": "Man overboard", 11 | "timestamp": "2017-01-23T14:24:17Z" 12 | } 13 | } 14 | } 15 | } 16 | }, 17 | "version": "1.0.0", 18 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 19 | } -------------------------------------------------------------------------------- /test/data/full-invalid/alarms-must_not_use_well_defined_notifications_at_deep_levels.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "notifications": { 6 | "navigation": { 7 | "position": { 8 | "adrift": { 9 | "method": ["sound"], 10 | "state": "warn", 11 | "message": "We are adrift" 12 | } 13 | } 14 | } 15 | } 16 | } 17 | }, 18 | "version": "1.0.0", 19 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 20 | } -------------------------------------------------------------------------------- /test/data/full-invalid/alarms-must_use_alarmMethod_array_with_enum.json: -------------------------------------------------------------------------------- 1 | { 2 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "vessels": { 4 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 5 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 6 | "notifications": { 7 | "mob":{ 8 | "message": "MOB", 9 | "timestamp": "2014-04-10T08:33:53Z", 10 | "$source": "a.suitable.path", 11 | "state": "warn", 12 | "method" : ["squeek"] 13 | } 14 | } 15 | } 16 | }, 17 | "version": "1.0.0" 18 | } 19 | -------------------------------------------------------------------------------- /test/data/full-invalid/alarms-must_use_alarmStateEnum.json: -------------------------------------------------------------------------------- 1 | { 2 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "vessels": { 4 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 5 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 6 | "notifications": { 7 | "mob":{ 8 | "message": "MOB", 9 | "timestamp": "2014-04-10T08:33:53Z", 10 | "$source": "a.suitable.path", 11 | "state": "worried", 12 | "method" : ["sound"] 13 | } 14 | } 15 | } 16 | }, 17 | "version": "1.0.0" 18 | } 19 | -------------------------------------------------------------------------------- /test/data/full-invalid/alarms-must_use_array_of_states_not_string.json: -------------------------------------------------------------------------------- 1 | { 2 | "self":"urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "vessels": { 4 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 5 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 6 | "notifications": { 7 | "mob":{ 8 | "message": "MOB", 9 | "timestamp": "2014-04-10T08:33:53Z", 10 | "$source": "a.suitable.path", 11 | "state": "emergency", 12 | "method" : "sound" 13 | } 14 | } 15 | } 16 | }, 17 | "version": "1.0.0" 18 | } 19 | -------------------------------------------------------------------------------- /test/data/full-invalid/aton-atonType_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "aton": { 3 | "urn:mrn:imo:mmsi:991982330": { 4 | "mmsi": "991982330", 5 | "name": "A random aton", 6 | "atonType": { 7 | "timestamp": "2017-01-25T00:23:05Z", 8 | "$source": "a.suitable.path", 9 | "value": { 10 | "id": 1, 11 | "name": "Light, With Sectors" 12 | } 13 | }, 14 | "navigation": { 15 | "position": { 16 | "timestamp": "2015-03-06T16:57:53.643Z", 17 | "longitude": 173.1693, 18 | "latitude": -41.156426, 19 | "$source": "sources.gps_0183_RMC", 20 | "altitude": 0 21 | } 22 | } 23 | } 24 | }, 25 | "version": "1.0.0", 26 | "self": "" 27 | } 28 | -------------------------------------------------------------------------------- /test/data/full-invalid/aton-has_sails.json: -------------------------------------------------------------------------------- 1 | { 2 | "aton": { 3 | "urn:mrn:imo:mmsi:996982330": { 4 | "mmsi": "996982330", 5 | "sails": { 6 | "area": { 7 | "total": { 8 | "value": 82, 9 | "timestamp": "2014-08-15T19:00:15.404Z", 10 | "$source": "foo.bar" 11 | }, 12 | "active": { 13 | "value": 78.4, 14 | "timestamp": "2014-08-15T19:00:15.404Z", 15 | "$source": "foo.bar" 16 | } 17 | }, 18 | "inventory": { 19 | "racingMain": { 20 | "name": "Racing main 2016", 21 | "type": "main", 22 | "material": "mylar", 23 | "brand": "WB Sails", 24 | "area": 34, 25 | "active": false 26 | }, 27 | "cruisingMain": { 28 | "name": "Cruising main", 29 | "type": "main", 30 | "material": "dacron", 31 | "brand": "WB Sails", 32 | "area": 33.8, 33 | "active": true 34 | }, 35 | "genoa": { 36 | "name": "Cruising genoa", 37 | "type": "genoa", 38 | "material": "dacron", 39 | "brand": "North Sails", 40 | "area": 44.6, 41 | "active": true 42 | } 43 | } 44 | } 45 | } 46 | }, 47 | "version": "1.0.0", 48 | "self": "" 49 | } -------------------------------------------------------------------------------- /test/data/full-invalid/aton-mmsi_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "aton": { 3 | "urn:mrn:imo:mmsi:991982330": { 4 | "mmsi": "911982330", 5 | "name": "A random aton", 6 | "atonType": { 7 | "timestamp": "2017-01-25T00:23:05Z", 8 | "$source": "a.suitable.path", 9 | "value": { 10 | "id": 6, 11 | "name": "Light, With Sectors" 12 | } 13 | }, 14 | "navigation": { 15 | "position": { 16 | "timestamp": "2015-03-06T16:57:53.643Z", 17 | "value": { 18 | "longitude": 173.1693, 19 | "latitude": -41.156426, 20 | "altitude": 0 21 | }, 22 | "$source": "sources.gps_0183_RMC" 23 | } 24 | } 25 | } 26 | }, 27 | "version": "1.0.0", 28 | "self": "" 29 | } -------------------------------------------------------------------------------- /test/data/full-invalid/datetime-timezoneRegion_but_no_timezone.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "environment":{ 6 | "time": { 7 | "timezoneRegion": "Europe/Helsinki", 8 | "timestamp": "2014-08-15T19:00:15.123456789Z" 9 | } 10 | } 11 | } 12 | }, 13 | "version": "1.0.0", 14 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 15 | } 16 | -------------------------------------------------------------------------------- /test/data/full-invalid/datetime-timezone_region_invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "environment":{ 6 | "time": { 7 | "timezoneOffset": 300, 8 | "timezoneRegion": "Europ_e/Helsinki", 9 | "timestamp": "2014-08-15T19:00:15.123456789Z" 10 | } 11 | } 12 | } 13 | }, 14 | "version": "1.0.0", 15 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 16 | } 17 | -------------------------------------------------------------------------------- /test/data/full-invalid/gnss-methodQuality_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "navigation": { 6 | "gnss": { 7 | "type": { 8 | "timestamp": "2017-01-25T00:23:05Z", 9 | "$source": "a.suitable.path", 10 | "value":"Integrated navigation system" 11 | }, 12 | "methodQuality":{ 13 | "timestamp": "2017-01-25T00:23:05Z", 14 | "$source": "a.suitable.path", 15 | "value":"Estimated (DR) nnnn" 16 | }, 17 | "integrity": { 18 | "timestamp": "2017-01-25T00:23:05Z", 19 | "$source": "a.suitable.path", 20 | "value":"Safe" 21 | }, 22 | "satellites": { 23 | "timestamp": "2017-01-25T00:23:05Z", 24 | "$source": "a.suitable.path", 25 | "value":12 26 | }, 27 | "antennaAltitude": { 28 | "timestamp": "2017-01-25T00:23:05Z", 29 | "$source": "a.suitable.path", 30 | "value":0.9 31 | }, 32 | "horizontalDilution": { 33 | "timestamp": "2017-01-25T00:23:05Z", 34 | "$source": "a.suitable.path", 35 | "value":10.234 36 | }, 37 | "positionDilution": { 38 | "timestamp": "2017-01-25T00:23:05Z", 39 | "$source": "a.suitable.path", 40 | "value":20.234 41 | }, 42 | "geoidalSeparation": { 43 | "timestamp": "2017-01-25T00:23:05Z", 44 | "$source": "a.suitable.path", 45 | "value":10 46 | }, 47 | "differentialAge": { 48 | "timestamp": "2017-01-25T00:23:05Z", 49 | "$source": "a.suitable.path", 50 | "value": 30 51 | }, 52 | "differentialReference":{ 53 | "timestamp": "2017-01-25T00:23:05Z", 54 | "$source": "a.suitable.path", 55 | "value": 1 56 | } 57 | } 58 | } 59 | 60 | } 61 | }, 62 | "version": "1.0.0", 63 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 64 | 65 | } 66 | -------------------------------------------------------------------------------- /test/data/full-invalid/gnss-type_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "navigation": { 6 | "gnss": { 7 | "type": { 8 | "timestamp": "2017-01-25T00:23:05Z", 9 | "$source": "a.suitable.path", 10 | "value":"Integrated navigationnnnn" 11 | }, 12 | "methodQuality":{ 13 | "timestamp": "2017-01-25T00:23:05Z", 14 | "$source": "a.suitable.path", 15 | "value":"Estimated (DR) mode" 16 | }, 17 | "integrity": { 18 | "timestamp": "2017-01-25T00:23:05Z", 19 | "$source": "a.suitable.path", 20 | "value":"Safe" 21 | }, 22 | "satellites": { 23 | "timestamp": "2017-01-25T00:23:05Z", 24 | "$source": "a.suitable.path", 25 | "value":12 26 | }, 27 | "antennaAltitude": { 28 | "timestamp": "2017-01-25T00:23:05Z", 29 | "$source": "a.suitable.path", 30 | "value":0.9 31 | }, 32 | "horizontalDilution": { 33 | "timestamp": "2017-01-25T00:23:05Z", 34 | "$source": "a.suitable.path", 35 | "value":10.234 36 | }, 37 | "positionDilution": { 38 | "timestamp": "2017-01-25T00:23:05Z", 39 | "$source": "a.suitable.path", 40 | "value":20.234 41 | }, 42 | "geoidalSeparation": { 43 | "timestamp": "2017-01-25T00:23:05Z", 44 | "$source": "a.suitable.path", 45 | "value":10 46 | }, 47 | "differentialAge": { 48 | "timestamp": "2017-01-25T00:23:05Z", 49 | "$source": "a.suitable.path", 50 | "value": 30 51 | }, 52 | "differentialReference":{ 53 | "timestamp": "2017-01-25T00:23:05Z", 54 | "$source": "a.suitable.path", 55 | "value": 1 56 | } 57 | } 58 | } 59 | 60 | } 61 | }, 62 | "version": "1.0.0", 63 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 64 | 65 | } 66 | -------------------------------------------------------------------------------- /test/data/full-invalid/invalid-timestamp-ending-with-Z.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:311982330": { 4 | "mmsi": "311982330", 5 | 6 | "navigation": { 7 | "courseOverGroundMagnetic": { 8 | "$source": "sources.gps_0183_RMC", 9 | "timestamp": "very poor timestamp with a Z", 10 | "value": 0.1 11 | } 12 | } 13 | } 14 | }, 15 | "version": "1.0.0", 16 | "self": "" 17 | } -------------------------------------------------------------------------------- /test/data/full-invalid/invalid-timestamp-subtle.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:311982330": { 4 | "mmsi": "311982330", 5 | 6 | "navigation": { 7 | "courseOverGroundTrue": { 8 | "$source": "sources.gps_0183_RMC", 9 | "timestamp": "2015-03-06 16:57", 10 | "value": 0 11 | } 12 | } 13 | } 14 | }, 15 | "version": "1.0.0", 16 | "self": "" 17 | } -------------------------------------------------------------------------------- /test/data/full-invalid/maneuver-bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "navigation": { 6 | "maneuver": { 7 | "value":"No bloody maneuver", 8 | "$source": "a.suitable.path", 9 | "timestamp": "2014-08-15T19:00:15.402Z" 10 | } 11 | } 12 | } 13 | }, 14 | "version": "1.0.0", 15 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 16 | 17 | } 18 | -------------------------------------------------------------------------------- /test/data/full-invalid/mothership-bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "mothershipMmsi": "230099998456" 6 | 7 | } 8 | }, 9 | "version": "1.0.0", 10 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 11 | 12 | } 13 | -------------------------------------------------------------------------------- /test/data/full-invalid/nav-destination_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | 6 | "navigation": { 7 | "destination":{ 8 | "commonName": { 9 | "value": 0, 10 | "$source": "a.suitable.path", 11 | "timestamp": "2014-08-15T19:00:15.402Z" 12 | } 13 | } 14 | } 15 | } 16 | }, 17 | "version": "1.0.0", 18 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 19 | } 20 | -------------------------------------------------------------------------------- /test/data/full-invalid/sar-mmsi_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "sar": { 3 | "urn:mrn:imo:mmsi:971982330": { 4 | "mmsi": "981982330", 5 | "navigation": { 6 | "position": { 7 | "timestamp": "2015-03-06T16:57:53.643Z", 8 | "value": { 9 | "longitude": 173.1693, 10 | "latitude": -41.156426, 11 | "altitude": 0 12 | }, 13 | "$source": "sources.gps_0183_RMC" 14 | } 15 | } 16 | } 17 | }, 18 | "version": "1.0.0", 19 | "self": "urn:mrn:imo:mmsi:230099999" 20 | } -------------------------------------------------------------------------------- /test/data/full-invalid/sar-sails.json: -------------------------------------------------------------------------------- 1 | { 2 | "sar": { 3 | "urn:mrn:imo:mmsi:976982330": { 4 | "mmsi": "976982330", 5 | "sails": { 6 | "area": { 7 | "total": { 8 | "value": 82, 9 | "timestamp": "2014-08-15T19:00:15.404Z", 10 | "$source": "foo.bar" 11 | }, 12 | "active": { 13 | "value": 78.4, 14 | "timestamp": "2014-08-15T19:00:15.404Z", 15 | "$source": "foo.bar" 16 | } 17 | }, 18 | "inventory": { 19 | "racingMain": { 20 | "name": "Racing main 2016", 21 | "type": "main", 22 | "material": "mylar", 23 | "brand": "WB Sails", 24 | "area": 34, 25 | "active": false 26 | }, 27 | "cruisingMain": { 28 | "name": "Cruising main", 29 | "type": "main", 30 | "material": "dacron", 31 | "brand": "WB Sails", 32 | "area": 33.8, 33 | "active": true 34 | }, 35 | "genoa": { 36 | "name": "Cruising genoa", 37 | "type": "genoa", 38 | "material": "dacron", 39 | "brand": "North Sails", 40 | "area": 44.6, 41 | "active": true 42 | } 43 | } 44 | } 45 | } 46 | }, 47 | "version": "1.0.0", 48 | "self": "urn:mrn:imo:mmsi:230099999" 49 | } -------------------------------------------------------------------------------- /test/data/full-invalid/sources-both_n2k_and_ais.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 5 | } 6 | }, 7 | "sources": { 8 | "N2K-1": { 9 | "label": "N2K-1", 10 | "type": "NMEA2000", 11 | "36": { 12 | "n2k": { 13 | "src": "36", 14 | "pgns": { 15 | "130312": "2017-01-25T00:10:05.123Z" 16 | }, 17 | "uniqueId": 1722191, 18 | "manufacturerId": 437, 19 | "deviceClass": 70, 20 | "deviceFunction": 140, 21 | "productID": 1739 22 | }, 23 | "ais":{ 24 | "aisType": 15 25 | } 26 | }, 27 | "manufacturerName": "Digital Yacht", 28 | "productName": "N2NET NMEA 2000 AIS", 29 | "softwareVersion": "040200.08.03.00", 30 | "hardwareVersion": "0", 31 | "serialNumber": "0000000", 32 | "installationNote1": "", 33 | "installationNote2": "" 34 | } 35 | }, 36 | "version": "1.0.0" 37 | } 38 | -------------------------------------------------------------------------------- /test/data/full-invalid/sources-valid_sources_with_no_0183_n2k_or_ais_and_other_items.json: -------------------------------------------------------------------------------- 1 | { 2 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "vessels": { 4 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 5 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 6 | } 7 | }, 8 | "sources": { 9 | "N2K-1": { 10 | "label": "N2K-1", 11 | "type": "NMEA2000", 12 | "36":{ 13 | "other":{ 14 | "id": "anything" 15 | } 16 | }, 17 | "manufacturerName": "Digital Yacht", 18 | "productName": "N2NET NMEA 2000 AIS", 19 | "softwareVersion": "040200.08.03.00", 20 | "hardwareVersion": "0", 21 | "serialNumber": "0000000", 22 | "installationNote1": "", 23 | "installationNote2": "" 24 | } 25 | }, 26 | "version": "1.0.0" 27 | } 28 | -------------------------------------------------------------------------------- /test/data/full-invalid/trip-key_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | 6 | "navigation": { 7 | "trip": { 8 | "log": { 9 | "value": 8.67, 10 | "timestamp": "2017-01-25T00:23:05.201Z", 11 | "$source": "a.suitable.path" 12 | }, 13 | "lastResetTime": { 14 | "value": "2016-04-24T05:14:18.632Z", 15 | "timestamp": "2017-01-25T00:23:05.210Z", 16 | "$source": "a.suitable.path" 17 | } 18 | } 19 | } 20 | } 21 | }, 22 | "version": "1.0.0", 23 | "self": "urn:mrn:imo:mmsi:230099999" 24 | } 25 | -------------------------------------------------------------------------------- /test/data/full-invalid/trip-value_is_a_string.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | 6 | "navigation": { 7 | "trip": { 8 | "log": { 9 | "value": "8.67", 10 | "timestamp": "2017-01-25T00:23:05.201Z", 11 | "$source": "a.suitable.path" 12 | }, 13 | "lastReset": { 14 | "value": "2016-04-24T05:14:18.632Z", 15 | "timestamp": "2017-01-25T00:23:05.210Z", 16 | "$source": "a.suitable.path" 17 | } 18 | } 19 | } 20 | } 21 | }, 22 | "version": "1.0.0", 23 | "self": "urn:mrn:imo:mmsi:230099999" 24 | } 25 | -------------------------------------------------------------------------------- /test/data/full-invalid/vessel-mmsi_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:311982330": { 4 | "mmsi": "111982330", 5 | "navigation": { 6 | "position": { 7 | "timestamp": "2015-03-06T16:57:53.643Z", 8 | "value": { 9 | "longitude": 173.1693, 10 | "latitude": -41.156426, 11 | "altitude": 0 12 | }, 13 | "$source": "sources.gps_0183_RMC" 14 | }, 15 | "courseOverGroundTrue": { 16 | "timestamp": "2015-03-06T16:57:53.643Z", 17 | "$source": "sources.gps_0183_RMC", 18 | "value": 245.69 19 | } 20 | } 21 | } 22 | }, 23 | "version": "1.0.0", 24 | "self": "" 25 | } -------------------------------------------------------------------------------- /test/data/full-valid/aircraft-basic_nav.json: -------------------------------------------------------------------------------- 1 | { 2 | "aircraft": { 3 | "urn:mrn:imo:mmsi:111982330": { 4 | "mmsi": "111982330", 5 | "navigation": { 6 | "position": { 7 | "timestamp": "2015-03-06T16:57:53.643Z", 8 | "value": { 9 | "longitude": 173.1693, 10 | "latitude": -41.156426, 11 | "altitude": 0 12 | }, 13 | "$source": "sources.gps_0183_RMC" 14 | }, 15 | "courseOverGroundTrue": { 16 | "timestamp": "2015-03-06T16:57:53.643Z", 17 | "$source": "sources.gps_0183_RMC", 18 | "value": 245.69 19 | } 20 | } 21 | } 22 | }, 23 | "version": "1.0.0", 24 | "self": "urn:mrn:imo:mmsi:111982330" 25 | } -------------------------------------------------------------------------------- /test/data/full-valid/aircraft-home_base.json: -------------------------------------------------------------------------------- 1 | { 2 | "aircraft": { 3 | "urn:mrn:imo:mmsi:116982330": { 4 | "mmsi": "116982330", 5 | "base": "Nelson", 6 | "navigation": { 7 | "position": { 8 | "timestamp": "2015-03-06T16:57:53.643Z", 9 | "value": { 10 | "longitude": 173.1693, 11 | "latitude": -41.156426, 12 | "altitude": 0 13 | }, 14 | "$source": "sources.gps_0183_RMC" 15 | }, 16 | "courseOverGroundTrue": { 17 | "timestamp": "2015-03-06T16:57:53.643Z", 18 | "$source": "sources.gps_0183_RMC", 19 | "value": 245.69 20 | } 21 | } 22 | } 23 | }, 24 | "version": "1.0.0", 25 | "self": "urn:mrn:imo:mmsi:116982330" 26 | } -------------------------------------------------------------------------------- /test/data/full-valid/ais-aisShipType.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "design": { 6 | "aisShipType": { 7 | "timestamp": "2017-01-25T00:23:05Z", 8 | "$source": "a.suitable.path", 9 | "value": { 10 | "id": 36, 11 | "name": "Sailing" 12 | } 13 | } 14 | } 15 | } 16 | }, 17 | "version": "1.0.0", 18 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 19 | } -------------------------------------------------------------------------------- /test/data/full-valid/ais-full_cpa_tcpa.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "navigation": { 6 | "courseOverGroundTrue": { 7 | "value": 206.9, 8 | "timestamp": "2014-08-15T18:00:00.755Z", 9 | "pgn": 130577, 10 | "$source": "sourceLabel.160" 11 | }, 12 | "closestApproach": { 13 | "value": { 14 | "distance": 250, 15 | "timeTo": 45 16 | }, 17 | "$source": "foo.bar", 18 | "timestamp": "2014-08-15T19:00:15.404Z" 19 | } 20 | } 21 | } 22 | }, 23 | "version": "1.0.0", 24 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 25 | } -------------------------------------------------------------------------------- /test/data/full-valid/ais-source.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 5 | } 6 | }, 7 | "sources": { 8 | "vhf": { 9 | "ais-b":{ 10 | "ais":{ 11 | "aisType": 15 12 | } 13 | }, 14 | "label": "AIS-15.0", 15 | "type": "AIS" 16 | } 17 | }, 18 | "version": "1.0.0", 19 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 20 | } 21 | -------------------------------------------------------------------------------- /test/data/full-valid/ais.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:246326000": { 4 | "mmsi": "246326000", 5 | "name": "UTGERDINA", 6 | "design": { 7 | "length": { 8 | "meta": { 9 | "description": "The various lengths of the vessel" 10 | }, 11 | "value": { 12 | "overall": 641 13 | }, 14 | "$source": "DUMMY_LABEL.AI", 15 | "timestamp": "2020-02-08T09:48:58.219Z", 16 | "sentence": "VDM" 17 | }, 18 | "beam": { 19 | "meta": { 20 | "units": "m", 21 | "description": "Beam length" 22 | }, 23 | "value": 65, 24 | "$source": "DUMMY_LABEL.AI", 25 | "timestamp": "2020-02-08T09:48:58.219Z", 26 | "sentence": "VDM" 27 | }, 28 | "draft": { 29 | "meta": { 30 | "description": "The draft of the vessel" 31 | }, 32 | "value": { 33 | "current": 14.1 34 | }, 35 | "$source": "DUMMY_LABEL.AI", 36 | "timestamp": "2020-02-08T09:48:58.219Z", 37 | "sentence": "VDM" 38 | }, 39 | "aisShipType": { 40 | "meta": { 41 | "description": "The ais ship type see http://www.bosunsmate.org/ais/message5.php" 42 | }, 43 | "value": { 44 | "id": 67, 45 | "name": "Passenger ship" 46 | }, 47 | "$source": "DUMMY_LABEL.AI", 48 | "timestamp": "2020-02-08T09:48:58.219Z", 49 | "sentence": "VDM" 50 | } 51 | }, 52 | "sensors": { 53 | "ais": { 54 | "fromBow": { 55 | "meta": { 56 | "description": "The distance from the bow to the sensor location" 57 | }, 58 | "value": 256, 59 | "$source": "DUMMY_LABEL.AI", 60 | "timestamp": "2020-02-08T09:48:58.219Z", 61 | "sentence": "VDM" 62 | }, 63 | "fromCenter": { 64 | "meta": { 65 | "description": "The distance from the centerline to the sensor location, -ve to starboard, +ve to port" 66 | }, 67 | "value": -27.5, 68 | "$source": "DUMMY_LABEL.AI", 69 | "timestamp": "2020-02-08T09:48:58.219Z", 70 | "sentence": "VDM" 71 | }, 72 | "class": { 73 | "value": "A", 74 | "$source": "DUMMY_LABEL.AI", 75 | "timestamp": "2020-02-08T09:48:58.219Z", 76 | "sentence": "VDM" 77 | } 78 | } 79 | }, 80 | "navigation": { 81 | "destination": { 82 | "commonName": { 83 | "meta": { 84 | "description": "Common name of the Destination, eg 'Fiji', also used in ais messages" 85 | }, 86 | "value": "OOI SILEN", 87 | "$source": "DUMMY_LABEL.AI", 88 | "timestamp": "2020-02-08T09:48:58.219Z", 89 | "sentence": "VDM" 90 | } 91 | } 92 | }, 93 | "communication": { 94 | "callsignVhf": "PH510" 95 | } 96 | } 97 | }, 98 | "self": "urn:mrn:imo:mmsi:246326000", 99 | "version": "0.1.0", 100 | "sources": { 101 | "DUMMY_LABEL": { 102 | "label": "DUMMY_LABEL", 103 | "type": "NMEA0183", 104 | "AI": { 105 | "talker": "AI", 106 | "sentences": { 107 | "VDM": "2020-02-08T09:48:58.219Z" 108 | } 109 | } 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /test/data/full-valid/alarms-branches_with_names_similar_to_mob_etc.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "notifications": { 6 | "moba": { 7 | "amob": { 8 | "X": { 9 | "value": { 10 | "message": "Message", 11 | "state": "alert", 12 | "method": [ 13 | "visual" 14 | ] 15 | }, 16 | "timestamp": "2017-01-23T14:22:17Z", 17 | "$source": "sources.alarm" 18 | } 19 | } 20 | } 21 | } 22 | } 23 | }, 24 | "version": "1.0.0", 25 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 26 | } -------------------------------------------------------------------------------- /test/data/full-valid/alarms-deep_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "vessels": { 4 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 5 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 6 | "notifications": { 7 | "navigation": { 8 | "position": { 9 | "value": { 10 | "message": "Position is more than 5 minutes old", 11 | "state": "alert", 12 | "method": [ 13 | "visual" 14 | ] 15 | }, 16 | "timestamp": "2014-04-10T08:33:53Z", 17 | "$source": "a.suitable.path" 18 | } 19 | } 20 | } 21 | } 22 | }, 23 | "version": "1.0.0" 24 | } -------------------------------------------------------------------------------- /test/data/full-valid/alarms-multiple_at_multiple_levels.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "notifications": { 6 | "mob": { 7 | "value": { 8 | "method": [ 9 | "sound", 10 | "visual" 11 | ], 12 | "state": "emergency", 13 | "message": "Man overboard" 14 | }, 15 | "timestamp": "2017-01-23T14:24:17Z", 16 | "$source": "a.suitable.path" 17 | }, 18 | "navigation": { 19 | "position": { 20 | "value": { 21 | "method": [ 22 | "sound" 23 | ], 24 | "state": "warn", 25 | "message": "Position is more than 5 minutes old" 26 | }, 27 | "timestamp": "2017-01-23T14:24:21Z", 28 | "$source": "a.suitable.path" 29 | }, 30 | "course": { 31 | "crossTrackError": { 32 | "value": { 33 | "method": [ 34 | "visual" 35 | ], 36 | "state": "warn", 37 | "message": "Cross track error is more than 1Nm" 38 | }, 39 | "timestamp": "2017-01-23T14:22:17Z", 40 | "$source": "a.suitable.path" 41 | }, 42 | "nextPoint": { 43 | "distance": { 44 | "value": { 45 | "method": [ 46 | "visual" 47 | ], 48 | "state": "alert", 49 | "message": "Aproaching Waypoint" 50 | }, 51 | "timestamp": "2017-01-23T14:22:17Z", 52 | "$source": "a.suitable.path" 53 | } 54 | } 55 | } 56 | }, 57 | "propulsion": { 58 | "starboardEngine": { 59 | "oilPressure": { 60 | "value": { 61 | "method": [ 62 | "sound", 63 | "visual" 64 | ], 65 | "state": "alarm", 66 | "message": "Oil pressure is low" 67 | }, 68 | "timestamp": "2017-01-23T14:22:17Z", 69 | "$source": "a.suitable.path" 70 | } 71 | } 72 | } 73 | } 74 | } 75 | }, 76 | "version": "1.0.0", 77 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 78 | } -------------------------------------------------------------------------------- /test/data/full-valid/alarms-shallow_keys.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "notifications": { 6 | "steering": { 7 | "value": { 8 | "method": [ 9 | "sound", 10 | "visual" 11 | ], 12 | "state": "alarm", 13 | "message": "Steering system failure" 14 | }, 15 | "timestamp": "2017-01-23T14:24:17Z", 16 | "$source": "a.suitable.path" 17 | } 18 | } 19 | } 20 | }, 21 | "version": "1.0.0", 22 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 23 | } -------------------------------------------------------------------------------- /test/data/full-valid/alarms-simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "vessels": { 4 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 5 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 6 | "notifications": { 7 | "mob": { 8 | "value": { 9 | "message": "MOB", 10 | "state": "emergency", 11 | "method": [ 12 | "visual", 13 | "sound" 14 | ] 15 | }, 16 | "timestamp": "2014-04-10T08:33:53Z", 17 | "$source": "a.suitable.path" 18 | } 19 | } 20 | } 21 | }, 22 | "version": "1.0.0" 23 | } -------------------------------------------------------------------------------- /test/data/full-valid/aton-basic_position.json: -------------------------------------------------------------------------------- 1 | { 2 | "aton": { 3 | "urn:mrn:imo:mmsi:991982330": { 4 | "mmsi": "991982330", 5 | "name": "A random aton", 6 | "atonType": { 7 | "timestamp": "2017-01-25T00:23:05Z", 8 | "$source": "a.suitable.path", 9 | "value": { 10 | "id": 6, 11 | "name": "Light, With Sectors" 12 | } 13 | }, 14 | "navigation": { 15 | "position": { 16 | "timestamp": "2015-03-06T16:57:53.643Z", 17 | "value": { 18 | "longitude": 173.1693, 19 | "latitude": -41.156426, 20 | "altitude": 0 21 | }, 22 | "$source": "sources.gps_0183_RMC" 23 | } 24 | } 25 | } 26 | }, 27 | "version": "1.0.0", 28 | "self": "" 29 | } -------------------------------------------------------------------------------- /test/data/full-valid/aton-dimensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "aton": { 3 | "urn:mrn:imo:mmsi:991982330": { 4 | "mmsi": "991982330", 5 | "name": "A random aton", 6 | "atonType": { 7 | "timestamp": "2017-01-25T00:23:05Z", 8 | "$source": "a.suitable.path", 9 | "value": { 10 | "id": 6, 11 | "name": "Light, With Sectors" 12 | } 13 | }, 14 | "navigation": { 15 | "position": { 16 | "timestamp": "2015-03-06T16:57:53.643Z", 17 | "value": { 18 | "longitude": 173.1693, 19 | "latitude": -41.156426, 20 | "altitude": 0 21 | }, 22 | "$source": "sources.gps_0183_RMC" 23 | } 24 | }, 25 | "design": { 26 | "length": { 27 | "timestamp": "2014-08-15T19:00:15.404Z", 28 | "$source": "foo.bar", 29 | "value": { 30 | "overall": 100.4 31 | } 32 | }, 33 | "beam": { 34 | "value": 75.4, 35 | "timestamp": "2014-08-15T19:00:15.404Z", 36 | "$source": "foo.bar" 37 | } 38 | } 39 | } 40 | }, 41 | "version": "1.0.0", 42 | "self": "" 43 | } -------------------------------------------------------------------------------- /test/data/full-valid/datetime-in_full_tree.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "navigation": { 6 | "datetime": { 7 | "value": "2015-12-05T13:11:59Z", 8 | "gnssTimeSource": "GPS", 9 | "timestamp": "2014-08-15T19:00:15.123456789Z", 10 | "$source": "foo.bar" 11 | } 12 | } 13 | } 14 | }, 15 | "version": "1.0.0", 16 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 17 | } 18 | -------------------------------------------------------------------------------- /test/data/full-valid/datetime-timezone_in_environment.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "environment":{ 6 | "time": { 7 | "timezoneOffset": 300, 8 | "timezoneRegion": "Europe/Helsinki", 9 | "timestamp": "2014-08-15T19:00:15.123456789Z" 10 | } 11 | } 12 | } 13 | }, 14 | "version": "1.0.0", 15 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 16 | } 17 | -------------------------------------------------------------------------------- /test/data/full-valid/environment-mode.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "environment": { 6 | "mode": { 7 | "value": "night", 8 | "timestamp": "2014-04-10T08:33:53Z", 9 | "source": { 10 | "label": "mode", 11 | "type": "self" 12 | } 13 | } 14 | } 15 | } 16 | }, 17 | "version": "1.0.0-Beta", 18 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 19 | } 20 | -------------------------------------------------------------------------------- /test/data/full-valid/gnss-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "navigation": { 6 | "gnss": { 7 | "type": { 8 | "timestamp": "2017-01-25T00:23:05Z", 9 | "$source": "a.suitable.path", 10 | "value":"Integrated navigation system" 11 | }, 12 | "methodQuality":{ 13 | "timestamp": "2017-01-25T00:23:05Z", 14 | "$source": "a.suitable.path", 15 | "value":"Estimated (DR) mode" 16 | }, 17 | "integrity": { 18 | "timestamp": "2017-01-25T00:23:05Z", 19 | "$source": "a.suitable.path", 20 | "value":"Safe" 21 | }, 22 | "satellites": { 23 | "timestamp": "2017-01-25T00:23:05Z", 24 | "$source": "a.suitable.path", 25 | "value":12 26 | }, 27 | "antennaAltitude": { 28 | "timestamp": "2017-01-25T00:23:05Z", 29 | "$source": "a.suitable.path", 30 | "value":0.9 31 | }, 32 | "horizontalDilution": { 33 | "timestamp": "2017-01-25T00:23:05Z", 34 | "$source": "a.suitable.path", 35 | "value":10.234 36 | }, 37 | "positionDilution": { 38 | "timestamp": "2017-01-25T00:23:05Z", 39 | "$source": "a.suitable.path", 40 | "value":20.234 41 | }, 42 | "geoidalSeparation": { 43 | "timestamp": "2017-01-25T00:23:05Z", 44 | "$source": "a.suitable.path", 45 | "value":10 46 | }, 47 | "differentialAge": { 48 | "timestamp": "2017-01-25T00:23:05Z", 49 | "$source": "a.suitable.path", 50 | "value": 30 51 | }, 52 | "differentialReference":{ 53 | "timestamp": "2017-01-25T00:23:05Z", 54 | "$source": "a.suitable.path", 55 | "value": 1 56 | } 57 | } 58 | } 59 | 60 | } 61 | }, 62 | "version": "1.0.0", 63 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 64 | 65 | } 66 | -------------------------------------------------------------------------------- /test/data/full-valid/identities-various_formats.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 5 | }, 6 | 7 | "urn:mrn:imo:mmsi:230099999": { 8 | "mmsi": "230099999" 9 | }, 10 | 11 | "https://vessels.signalk.org/someboatidentity": { 12 | "url": "https://vessels.signalk.org/someboatidentity" 13 | } 14 | }, 15 | "version": "1.0.0", 16 | "self": "urn:mrn:imo:mmsi:230099999" 17 | } 18 | -------------------------------------------------------------------------------- /test/data/full-valid/lux-N2K.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "mmsi": "230099999", 6 | "environment": { 7 | "outside": { 8 | "illuminance": { 9 | "value": 20000, 10 | "$source": ".36.0", 11 | "timestamp": "2017-01-24T23:59:01Z", 12 | "pgn": 130312 13 | } 14 | } 15 | } 16 | } 17 | }, 18 | "sources": { 19 | "N2K-1": { 20 | "36": { 21 | "n2k": { 22 | "src": "36", 23 | "pgns": { 24 | "130312": "2017-01-25T00:03:05.056Z" 25 | } 26 | } 27 | }, 28 | "label": "", 29 | "type": "NMEA2000" 30 | } 31 | }, 32 | "version": "1.0.0", 33 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 34 | } 35 | -------------------------------------------------------------------------------- /test/data/full-valid/lux-i2c.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "mmsi": "230099999", 6 | "environment": { 7 | "outside": { 8 | "illuminance": { 9 | "value": 20000, 10 | "$source": "i2c-0.0x48", 11 | "timestamp": "2017-01-24T23:59:01Z" 12 | } 13 | } 14 | } 15 | } 16 | }, 17 | "sources": { 18 | "i2c-0": { 19 | "label": "", 20 | "type": "signalk" 21 | } 22 | }, 23 | "version": "1.0.0", 24 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 25 | } 26 | -------------------------------------------------------------------------------- /test/data/full-valid/maneuver-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "navigation": { 6 | "maneuver": { 7 | "value":"No special maneuver", 8 | "$source": "a.suitable.path", 9 | "timestamp": "2014-08-15T19:00:15.402Z" 10 | } 11 | } 12 | } 13 | }, 14 | "version": "1.0.0", 15 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 16 | 17 | } 18 | -------------------------------------------------------------------------------- /test/data/full-valid/miscellaneous-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "navigation": { 6 | "courseOverGroundTrue": { 7 | "value": 206.9, 8 | "timestamp": "2014-08-15T18:00:00.755Z", 9 | "pgn": 130577, 10 | "$source": "sourceLabel.160" 11 | } 12 | }, 13 | "environment": { 14 | "current": { 15 | "value": { 16 | "setTrue": 58.9, 17 | "drift": 0.28 18 | }, 19 | "timestamp": "2014-08-15T18:00:00.755Z", 20 | "pgn": 130577, 21 | "$source": "sourceLabel.160" 22 | } 23 | } 24 | } 25 | }, 26 | "version": "1.0.0", 27 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 28 | } -------------------------------------------------------------------------------- /test/data/full-valid/mothership-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | "mothershipMmsi": "230099998" 6 | 7 | } 8 | }, 9 | "version": "1.0.0", 10 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 11 | 12 | } 13 | -------------------------------------------------------------------------------- /test/data/full-valid/nav-destination.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | 6 | "navigation": { 7 | "destination":{ 8 | "commonName": { 9 | "value": "Fiji", 10 | "$source": "a.suitable.path", 11 | "timestamp": "2014-08-15T19:00:15.402Z" 12 | } 13 | } 14 | } 15 | } 16 | }, 17 | "version": "1.0.0", 18 | "self": "vessels.urn:mrn:imo:mmsi:230099999" 19 | } 20 | -------------------------------------------------------------------------------- /test/data/full-valid/racing-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "navigation": { 6 | "racing" : { 7 | "startLineStb" : { 8 | "value": { 9 | "latitude": 49.287333333333336, 10 | "longitude": -123.1595 11 | }, 12 | "timestamp": "2014-08-15T19:00:15.402Z", 13 | "$source": "foo.bar" 14 | }, 15 | "startLinePort" : { 16 | "value": { 17 | "latitude": 49.297333333333336, 18 | "longitude": -123.1595 19 | }, 20 | "timestamp": "2014-08-15T19:00:15.402Z", 21 | "$source": "foo.bar" 22 | }, 23 | "distanceStartline": { 24 | "value": 31, 25 | "timestamp": "2014-08-15T19:00:15.402Z", 26 | "$source": "foo.bar" 27 | }, 28 | "timeToStart": { 29 | "value": 52, 30 | "timestamp": "2014-08-15T19:00:15.402Z", 31 | "$source": "foo.bar" 32 | }, 33 | "timePortDown": { 34 | "value": 55, 35 | "timestamp": "2014-08-15T19:00:15.402Z", 36 | "$source": "foo.bar" 37 | }, 38 | "timePortUp": { 39 | "value": 14, 40 | "timestamp": "2014-08-15T19:00:15.402Z", 41 | "$source": "foo.bar" 42 | }, 43 | "timeStbdDown": { 44 | "value": 45, 45 | "timestamp": "2014-08-15T19:00:15.402Z", 46 | "$source": "foo.bar" 47 | }, 48 | "timeStbdUp": { 49 | "value": 20, 50 | "timestamp": "2014-08-15T19:00:15.402Z", 51 | "$source": "foo.bar" 52 | }, 53 | "layline": { 54 | "distance":{ 55 | "value": 18, 56 | "timestamp": "2014-08-15T19:00:15.402Z", 57 | "$source": "foo.bar" 58 | }, 59 | "time":{ 60 | "value": 15, 61 | "timestamp": "2014-08-15T19:00:15.402Z", 62 | "$source": "foo.bar" 63 | } 64 | }, 65 | "oppositeLayline": { 66 | "distance":{ 67 | "value": 18, 68 | "timestamp": "2014-08-15T19:00:15.402Z", 69 | "$source": "foo.bar" 70 | }, 71 | "time":{ 72 | "value": 15, 73 | "timestamp": "2014-08-15T19:00:15.402Z", 74 | "$source": "foo.bar" 75 | } 76 | } 77 | } 78 | } 79 | } 80 | }, 81 | "version": "1.0.0", 82 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 83 | } 84 | -------------------------------------------------------------------------------- /test/data/full-valid/registrations-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "registrations": { 6 | "imo": "IMO 1234567", 7 | "national":{ 8 | "partA":{ 9 | "country":"NZ", 10 | "registration":"NZ12345", 11 | "description":"New Zealand Part A recreational vessel" 12 | }, 13 | "partB":{ 14 | "country":"NZ", 15 | "registration":"NZ12345", 16 | "description":"New Zealand Part B recreational vessel" 17 | } 18 | }, 19 | "local":{ 20 | "coastGuard":{ 21 | "registration":"NZCG12345", 22 | "description":"New Zealand Coast Guard" 23 | } 24 | }, 25 | "other":{ 26 | "tassieDoc":{ 27 | "registration":"DOC12345", 28 | "description":"Tassie DOC park permit" 29 | } 30 | } 31 | } 32 | } 33 | }, 34 | "version": "1.0.0", 35 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 36 | } 37 | -------------------------------------------------------------------------------- /test/data/full-valid/sar-notifications.json: -------------------------------------------------------------------------------- 1 | { 2 | "sar": { 3 | "urn:mrn:imo:mmsi:971982330": { 4 | "mmsi": "971982330", 5 | "navigation": { 6 | "position": { 7 | "timestamp": "2015-03-06T16:57:53.643Z", 8 | "value": { 9 | "longitude": 173.1693, 10 | "latitude": -41.156426, 11 | "altitude": 0 12 | }, 13 | "$source": "sources.gps_0183_RMC" 14 | } 15 | }, 16 | "notifications": { 17 | "mob": { 18 | "value": { 19 | "method": [ 20 | "sound", 21 | "visual" 22 | ], 23 | "state": "emergency", 24 | "message": "Man overboard" 25 | }, 26 | "timestamp": "2017-01-23T14:24:17Z", 27 | "$source": "sources.gps_0183_RMC" 28 | } 29 | } 30 | } 31 | }, 32 | "version": "1.0.0", 33 | "self": "urn:mrn:imo:mmsi:230099999" 34 | } -------------------------------------------------------------------------------- /test/data/full-valid/sar-position.json: -------------------------------------------------------------------------------- 1 | { 2 | "sar": { 3 | "urn:mrn:imo:mmsi:971982330": { 4 | "mmsi": "971982330", 5 | "navigation": { 6 | "position": { 7 | "timestamp": "2015-03-06T16:57:53.643Z", 8 | "value": { 9 | "longitude": 173.1693, 10 | "latitude": -41.156426, 11 | "altitude": 0 12 | }, 13 | "$source": "sources.gps_0183_RMC" 14 | } 15 | } 16 | } 17 | }, 18 | "version": "1.0.0", 19 | "self": "urn:mrn:imo:mmsi:230099999" 20 | } -------------------------------------------------------------------------------- /test/data/full-valid/sources-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 5 | } 6 | }, 7 | "sources": { 8 | "N2K-1": { 9 | "label": "N2K-1", 10 | "type": "NMEA2000", 11 | "36": { 12 | "n2k": { 13 | "src": "36", 14 | "pgns": { 15 | "130312": "2017-01-25T00:10:05.123Z" 16 | }, 17 | "uniqueId": 1722191, 18 | "manufacturerId": 437, 19 | "deviceClass": 70, 20 | "deviceFunction": 140, 21 | "productID": 1739 22 | } 23 | }, 24 | "manufacturerName": "Digital Yacht", 25 | "productName": "N2NET NMEA 2000 AIS", 26 | "softwareVersion": "040200.08.03.00", 27 | "hardwareVersion": "0", 28 | "serialNumber": "0000000", 29 | "installationNote1": "", 30 | "installationNote2": "" 31 | } 32 | }, 33 | "version": "1.0.0", 34 | "self": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d" 35 | } 36 | -------------------------------------------------------------------------------- /test/data/full-valid/temperatures-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 5 | "mmsi": "230099999", 6 | "environment": { 7 | "water": { 8 | "temperature": { 9 | "value": 0, 10 | "$source": ".36.0", 11 | "timestamp": "2017-01-24T23:59:01Z", 12 | "pgn": 130312 13 | } 14 | }, 15 | "outside": { 16 | "temperature": { 17 | "value": 0, 18 | "$source": ".36.0", 19 | "timestamp": "2017-01-24T23:59:01Z", 20 | "pgn": 130312 21 | }, 22 | "dewPointTemperature": { 23 | "value": 0, 24 | "$source": ".36.0", 25 | "timestamp": "2017-01-24T23:59:01Z", 26 | "pgn": 130312 27 | }, 28 | "apparentWindChillTemperature": { 29 | "value": 0, 30 | "$source": ".36.0", 31 | "timestamp": "2017-01-24T23:59:01Z", 32 | "pgn": 130312 33 | }, 34 | "theoreticalWindChillTemperature": { 35 | "value": 0, 36 | "$source": ".36.0", 37 | "timestamp": "2017-01-24T23:59:01Z", 38 | "pgn": 130312 39 | }, 40 | "heatIndexTemperature": { 41 | "value": 0, 42 | "$source": ".36.0", 43 | "timestamp": "2017-01-24T23:59:01Z", 44 | "pgn": 130312 45 | } 46 | }, 47 | "inside": { 48 | "temperature": { 49 | "value": 0, 50 | "$source": ".36.0", 51 | "timestamp": "2017-01-24T23:59:01Z", 52 | "pgn": 130312 53 | }, 54 | "engineRoom": { 55 | "temperature": { 56 | "value": 0, 57 | "$source": ".36.0", 58 | "timestamp": "2017-01-24T23:59:01Z", 59 | "pgn": 130312 60 | } 61 | }, 62 | "mainCabin": { 63 | "temperature": { 64 | "value": 0, 65 | "$source": ".36.0", 66 | "timestamp": "2017-01-24T23:59:01Z", 67 | "pgn": 130312 68 | } 69 | }, 70 | "refrigerator": { 71 | "temperature": { 72 | "value": 0, 73 | "$source": ".36.0", 74 | "timestamp": "2017-01-24T23:59:01Z", 75 | "pgn": 130312 76 | } 77 | }, 78 | "heating": { 79 | "temperature": { 80 | "value": 0, 81 | "$source": ".36.0", 82 | "timestamp": "2017-01-24T23:59:01Z", 83 | "pgn": 130312 84 | } 85 | }, 86 | "freezer": { 87 | "temperature": { 88 | "value": 0, 89 | "$source": ".36.0", 90 | "timestamp": "2017-01-24T23:59:01Z", 91 | "pgn": 130312 92 | } 93 | } 94 | } 95 | }, 96 | "propulsion": { 97 | "exhaust": { 98 | "temperature": { 99 | "value": 0, 100 | "$source": ".36.0", 101 | "timestamp": "2017-01-24T23:59:01Z", 102 | "pgn": 130312 103 | } 104 | } 105 | } 106 | } 107 | }, 108 | "sources": { 109 | "N2K-1": { 110 | "36": { 111 | "label": "", 112 | "type": "NMEA2000", 113 | "0": {}, 114 | "n2k": { 115 | "src": "36", 116 | "pgns": { 117 | "130312": "2017-01-25T00:03:05.056Z" 118 | } 119 | } 120 | } 121 | } 122 | }, 123 | "version": "1.0.0", 124 | "self": "urn:mrn:imo:mmsi:230099999" 125 | } -------------------------------------------------------------------------------- /test/data/full-valid/trip-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:230099999": { 4 | "mmsi": "230099999", 5 | 6 | "navigation": { 7 | "trip": { 8 | "log": { 9 | "value": 8.67, 10 | "timestamp": "2017-01-25T00:23:05.201Z", 11 | "$source": "a.suitable.path" 12 | }, 13 | "lastReset": { 14 | "value": "2016-04-24T05:14:18.632Z", 15 | "timestamp": "2017-01-25T00:23:05.210Z", 16 | "$source": "a.suitable.path" 17 | } 18 | } 19 | } 20 | } 21 | }, 22 | "version": "1.0.0", 23 | "self": "urn:mrn:imo:mmsi:230099999" 24 | } 25 | -------------------------------------------------------------------------------- /test/data/full-valid/vessel-basic_nav.json: -------------------------------------------------------------------------------- 1 | { 2 | "vessels": { 3 | "urn:mrn:imo:mmsi:311982330": { 4 | "mmsi": "311982330", 5 | "navigation": { 6 | "position": { 7 | "timestamp": "2015-03-06T16:57:53.643Z", 8 | "value": { 9 | "longitude": 173.1693, 10 | "latitude": -41.156426, 11 | "altitude": 0 12 | }, 13 | "$source": "sources.gps_0183_RMC" 14 | }, 15 | "courseOverGroundTrue": { 16 | "timestamp": "2015-03-06T16:57:53.643Z", 17 | "$source": "sources.gps_0183_RMC", 18 | "value": 245.69 19 | } 20 | } 21 | } 22 | }, 23 | "version": "1.0.0", 24 | "self": "urn:mrn:imo:mmsi:311982330" 25 | } -------------------------------------------------------------------------------- /test/data/get-invalid/delta-get-no-context.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "6b0e776f-811a-4b35-980e-b93405371bc5", 3 | "get": { 4 | "path": "a.b.c" 5 | } 6 | } -------------------------------------------------------------------------------- /test/data/get-invalid/delta-get-no-requestId.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "bar", 3 | "get": { 4 | "path": "a.b.c" 5 | } 6 | } -------------------------------------------------------------------------------- /test/data/get-valid/delta-get-array.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5", 3 | "requestId": "6b0e776f-811a-4b35-980e-b93405371bc5", 4 | "get": [{ 5 | "path": "a.b.c" 6 | }] 7 | } -------------------------------------------------------------------------------- /test/data/get-valid/delta-get-no-array.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5", 3 | "requestId": "6b0e776f-811a-4b35-980e-b93405371bc5", 4 | "get": { 5 | "path": "a.b.c" 6 | } 7 | } -------------------------------------------------------------------------------- /test/data/hello-invalid/version_bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "V1.0.2", 3 | "roles": [ 4 | "slave" 5 | ] 6 | } -------------------------------------------------------------------------------- /test/data/hello-valid/master.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "signalk-server", 3 | "version": "1.0.4", 4 | "timestamp": "2018-06-21T15:09:16.704Z", 5 | "self": "vessels.urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 6 | "roles": [ 7 | "master", 8 | "aux" 9 | ] 10 | } -------------------------------------------------------------------------------- /test/data/hello-valid/slave_minimal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "roles": [ 4 | "slave" 5 | ] 6 | } -------------------------------------------------------------------------------- /test/data/put-invalid/delta-put-array-no-requestId.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "bar", 3 | "put": [{ 4 | "path": "a.b.c", 5 | "value": 1234 6 | }] 7 | } -------------------------------------------------------------------------------- /test/data/put-invalid/delta-update-with-put.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "bar", 3 | "requestId": "123345-23232-232323", 4 | "updates": [{ 5 | "source": { 6 | "label": "", 7 | "type": "NMEA0183", 8 | "sentence": "GLL", 9 | "talker": "II" 10 | }, 11 | "timestamp": "2013-10-08T15:47:28.263Z", 12 | "values": [{ 13 | "path": "a.b.c", 14 | "value": 1234 15 | }] 16 | }], 17 | "put": [{ 18 | "path": "a.b.c", 19 | "value": 1234 20 | }] 21 | } -------------------------------------------------------------------------------- /test/data/put-valid/delta-put-array.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5", 3 | "requestId": "6b0e776f-811a-4b35-980e-b93405371bc5", 4 | "put": [{ 5 | "path": "a.b.c", 6 | "value": 1234 7 | }] 8 | } -------------------------------------------------------------------------------- /test/data/put-valid/delta-put-no-array.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.urn:mrn:signalk:uuid:6b0e776f-811a-4b35-980e-b93405371bc5", 3 | "requestId": "6b0e776f-811a-4b35-980e-b93405371bc5", 4 | "put": { 5 | "path": "a.b.c", 6 | "value": 1234 7 | } 8 | } -------------------------------------------------------------------------------- /test/data/put-valid/delta-put-no-context.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "6b0e776f-811a-4b35-980e-b93405371bc5", 3 | "put": { 4 | "path": "a.b.c", 5 | "value": 1234 6 | } 7 | } -------------------------------------------------------------------------------- /test/data/put-valid/put.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "put": { 4 | "path": "electrical.switches.anchorLight.state", 5 | "value": 1 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/data/subscribe-invalid/subscribe-maxPeriod_invalid_property.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.self", 3 | "subscribe": [{ 4 | "path": "navigation.speedThroughWater", 5 | "period": 1000, 6 | "format": "delta", 7 | "policy": "ideal", 8 | "maxPeriod": 200 9 | }, { 10 | "path": "navigation.logTrip", 11 | "period": 10000 12 | }] 13 | } -------------------------------------------------------------------------------- /test/data/subscribe-valid/subscribe-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "vessels.self", 3 | "subscribe": [{ 4 | "path": "navigation.speedThroughWater", 5 | "period": 1000, 6 | "format": "delta", 7 | "policy": "ideal", 8 | "minPeriod": 200 9 | }, { 10 | "path": "navigation.logTrip", 11 | "period": 10000 12 | }] 13 | } -------------------------------------------------------------------------------- /test/data/unsubscribe-invalid/unsubscribe-context_missing.json: -------------------------------------------------------------------------------- 1 | { 2 | "unsubscribe": [{ 3 | "path": "*" 4 | }] 5 | } -------------------------------------------------------------------------------- /test/data/unsubscribe-valid/unsubscribe-from_all.json: -------------------------------------------------------------------------------- 1 | { 2 | "context": "*", 3 | "unsubscribe": [{ 4 | "path": "*" 5 | }] 6 | } -------------------------------------------------------------------------------- /test/data/vessel-invalid/environment-inside_humidity_rather_than_relativeHumidity.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "environment": { 4 | "inside": { 5 | "humidity": { 6 | "value": 0.84, 7 | "timestamp": "2018-02-28T09:46:55.302Z", 8 | "$source": "foo.bar" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/data/vessel-invalid/environment-inside_invalid_property.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "environment": { 4 | "inside": { 5 | "engineRoom2": { 6 | "temperature": { 7 | "value": 288.1, 8 | "timestamp": "2018-02-28T09:46:55.302Z", 9 | "$source": "foo.bar" 10 | }, 11 | "dewPt": { 12 | "value": 278.6, 13 | "timestamp": "2018-02-28T09:46:55.302Z", 14 | "$source": "foo.bar" 15 | } 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/data/vessel-invalid/environment-with_inside_heatIndex.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "environment": { 4 | "inside": { 5 | "mainCabin": { 6 | "temperature": { 7 | "value": 288.1, 8 | "timestamp": "2018-02-28T09:46:55.302Z", 9 | "$source": "foo.bar" 10 | }, 11 | "relativeHumidity": { 12 | "value": 0.86, 13 | "timestamp": "2018-02-28T09:46:55.302Z", 14 | "$source": "foo.bar" 15 | }, 16 | "heatIndex": { 17 | "value": 289.2, 18 | "timestamp": "2018-02-28T09:46:55.5102Z", 19 | "$source": "foo.bar" 20 | } 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /test/data/vessel-invalid/meta-missing-description.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine - The Olde Faithful", 6 | "revolutions": { 7 | "value": 1280, 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "units": "Hz" 12 | } 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/data/vessel-invalid/meta-with-invalid-enum.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine - The Olde Faithful", 6 | "state": { 7 | "value": "started", 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "The current state of the engine", 12 | "enum": "stopped" 13 | } 14 | } 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /test/data/vessel-invalid/meta-with_displayScale_and_additional_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine", 6 | "revolutions": { 7 | "value": 43.5, 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "Engine revolutions (x60 for RPM)", 12 | "units": "Hz", 13 | "displayName": "Tachometer", 14 | "shortName": "RPM", 15 | "displayScale": { 16 | "lower": 0, 17 | "upper": 66.667, 18 | "color": "white" 19 | }, 20 | "zones": [{ 21 | "lower": 60.0, 22 | "state": "alarm", 23 | "message": "Engine exceeds maximum rpm!" 24 | }] 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/data/vessel-invalid/meta-with_displayScale_and_invalid_power.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine", 6 | "revolutions": { 7 | "value": 43.5, 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "Engine revolutions (x60 for RPM)", 12 | "units": "Hz", 13 | "displayName": "Tachometer", 14 | "shortName": "RPM", 15 | "displayScale": { 16 | "lower": 0, 17 | "upper": 66.667, 18 | "type": "logarithmic", 19 | "power": 2 20 | }, 21 | "zones": [{ 22 | "lower": 60.0, 23 | "state": "alarm", 24 | "message": "Engine exceeds maximum rpm!" 25 | }] 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/data/vessel-invalid/meta-with_displayScale_lower_only.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine", 6 | "revolutions": { 7 | "value": 43.5, 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "Engine revolutions (x60 for RPM)", 12 | "units": "Hz", 13 | "displayName": "Tachometer", 14 | "shortName": "RPM", 15 | "displayScale": { 16 | "lower": 0 17 | }, 18 | "zones": [{ 19 | "lower": 60.0, 20 | "state": "alarm", 21 | "message": "Engine exceeds maximum rpm!" 22 | }] 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/data/vessel-invalid/meta-with_displayScale_non_numeric_lower.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine", 6 | "revolutions": { 7 | "value": 43.5, 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "Engine revolutions (x60 for RPM)", 12 | "units": "Hz", 13 | "displayName": "Tachometer", 14 | "shortName": "RPM", 15 | "displayScale": { 16 | "lower": {}, 17 | "upper": 66.667 18 | }, 19 | "zones": [{ 20 | "lower": 60.0, 21 | "state": "alarm", 22 | "message": "Engine exceeds maximum rpm!" 23 | }] 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/data/vessel-invalid/meta-with_displayScale_non_numeric_upper.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine", 6 | "revolutions": { 7 | "value": 43.5, 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "Engine revolutions (x60 for RPM)", 12 | "units": "Hz", 13 | "displayName": "Tachometer", 14 | "shortName": "RPM", 15 | "displayScale": { 16 | "lower": 0, 17 | "upper": "4000 rpm" 18 | }, 19 | "zones": [{ 20 | "lower": 60.0, 21 | "state": "alarm", 22 | "message": "Engine exceeds maximum rpm!" 23 | }] 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/data/vessel-invalid/meta-with_displayScale_upper_only.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine", 6 | "revolutions": { 7 | "value": 43.5, 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "Engine revolutions (x60 for RPM)", 12 | "units": "Hz", 13 | "displayName": "Tachometer", 14 | "shortName": "RPM", 15 | "displayScale": { 16 | "upper": 66.6667 17 | }, 18 | "zones": [{ 19 | "lower": 60.0, 20 | "state": "alarm", 21 | "message": "Engine exceeds maximum rpm!" 22 | }] 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/data/vessel-invalid/sails-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "sails": { 4 | "area": { 5 | "total": { 6 | "value": 82, 7 | "timestamp": "2014-08-15T19:00:15.404Z", 8 | "$source": "foo.bar" 9 | }, 10 | "active": { 11 | "value": 78.4, 12 | "timestamp": "2014-08-15T19:00:15.404Z", 13 | "$source": "foo.bar" 14 | } 15 | }, 16 | "inventory": { 17 | "racingMain": { 18 | "name": "Racing main 2016", 19 | "type": "main", 20 | "material": "mylar", 21 | "brand": "WB Sails", 22 | "area": 34, 23 | "active": false 24 | }, 25 | "cruisingMain": { 26 | "name": "Cruising main", 27 | "type": "main", 28 | "material": "dacron", 29 | "brand": "WB Sails", 30 | "area": 33.8, 31 | "active": true, 32 | "reducedState": false 33 | }, 34 | "genoa": { 35 | "name": "Cruising genoa", 36 | "type": "genoa", 37 | "material": "dacron", 38 | "brand": "North Sails", 39 | "area": 44.6, 40 | "active": true, 41 | "reducedState": { 42 | "reduced": true, 43 | "reefs": 1, 44 | "furledRatio": 0 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/data/vessel-invalid/vessel-no_id.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /test/data/vessel-invalid/vessel-uuid_not_canonical.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:de305d54-75b4-531b-adb2-eb6b9e546014" 3 | } -------------------------------------------------------------------------------- /test/data/vessel-valid/environment-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "environment": { 4 | "inside": { 5 | "temperature": { 6 | "value": 288.1, 7 | "timestamp": "2018-02-28T09:46:55.302Z", 8 | "$source": "foo.bar" 9 | }, 10 | "relativeHumidity": { 11 | "value": 0.84, 12 | "timestamp": "2018-02-28T09:46:55.302Z", 13 | "$source": "foo.bar" 14 | }, 15 | "engineRoom2": { 16 | "temperature": { 17 | "value": 288.1, 18 | "timestamp": "2018-02-28T09:46:55.302Z", 19 | "$source": "foo.bar" 20 | }, 21 | "relativeHumidity": { 22 | "value": 0.86, 23 | "timestamp": "2018-02-28T09:46:55.302Z", 24 | "$source": "foo.bar" 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/data/vessel-valid/environment-with_inside_heatIndexTemperature.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "environment": { 4 | "inside": { 5 | "mainCabin": { 6 | "temperature": { 7 | "value": 288.1, 8 | "timestamp": "2018-02-28T09:46:55.302Z", 9 | "$source": "foo.bar" 10 | }, 11 | "relativeHumidity": { 12 | "value": 0.86, 13 | "timestamp": "2018-02-28T09:46:55.302Z", 14 | "$source": "foo.bar" 15 | }, 16 | "heatIndexTemperature": { 17 | "value": 289.2, 18 | "timestamp": "2018-02-28T09:46:55.5102Z", 19 | "$source": "foo.bar" 20 | } 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /test/data/vessel-valid/meta-missing-units.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine - The Olde Faithful", 6 | "revolutions": { 7 | "value": 1280, 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "Engine revolutions (x60 for RPM)" 12 | } 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/data/vessel-valid/meta-with-enum.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine - The Olde Faithful", 6 | "state": { 7 | "value": "started", 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "The current state of the engine", 12 | "enum": [ 13 | "stopped", 14 | "started", 15 | "unusable" 16 | ] 17 | } 18 | } 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /test/data/vessel-valid/meta-with_displayScale.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine", 6 | "revolutions": { 7 | "value": 43.5, 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "Engine revolutions (x60 for RPM)", 12 | "units": "Hz", 13 | "displayName": "Tachometer", 14 | "shortName": "RPM", 15 | "displayScale": { 16 | "lower": 0, 17 | "upper": 66.667 18 | }, 19 | "zones": [{ 20 | "lower": 60.0, 21 | "state": "alarm", 22 | "message": "Engine exceeds maximum rpm!" 23 | }] 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test/data/vessel-valid/meta-with_displayScale_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "environment": { 4 | "depth": { 5 | "belowKeel": { 6 | "value": 43.5, 7 | "timestamp": "2014-08-15T19:00:15.402Z", 8 | "$source": "foo.bar", 9 | "meta": { 10 | "description": "Depth below keel", 11 | "units": "m", 12 | "displayName": "Depth below Keel", 13 | "shortName": "Depth", 14 | "displayScale": { 15 | "lower": 0.5, 16 | "upper": 200, 17 | "type": "logarithmic" 18 | }, 19 | "zones": [{ 20 | "upper": 2.0, 21 | "state": "alarm", 22 | "message": "Shallow" 23 | },{ 24 | "upper": 5.0, 25 | "state": "warn", 26 | "message": "Shallow" 27 | }] 28 | } 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/data/vessel-valid/meta-with_displayScale_type_power.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "propulsion": { 4 | "instance0": { 5 | "label": "Main Engine", 6 | "revolutions": { 7 | "value": 43.5, 8 | "timestamp": "2014-08-15T19:00:15.402Z", 9 | "$source": "foo.bar", 10 | "meta": { 11 | "description": "Engine revolutions (x60 for RPM)", 12 | "units": "Hz", 13 | "displayName": "Tachometer", 14 | "shortName": "RPM", 15 | "displayScale": { 16 | "lower": 0, 17 | "upper": 66.667, 18 | "type": "power", 19 | "power": 0.5 20 | }, 21 | "zones": [{ 22 | "lower": 60.0, 23 | "state": "alarm", 24 | "message": "Engine exceeds maximum rpm!" 25 | }] 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/data/vessel-valid/polar-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "performance": { 4 | "activePolar": "91c8d96d-4bbd-4c2f-ab36-295b04e83589", 5 | "activePolarData": { 6 | "id": "91c8d96d-4bbd-4c2f-ab36-295b04e83589", 7 | "name": "dynamicPolar", 8 | "description": "Dynamic polar diagram from actual sailing", 9 | "source": { 10 | "label": "signalk-polar" 11 | }, 12 | "windData": [{ 13 | "trueWindSpeed": 8, 14 | "optimalBeats": [ 15 | [0.29644225714765154, 2.5876562110951293], 16 | [-0.23247131479853, 2.71626735478773] 17 | ], 18 | "optimalGybes": [ 19 | [1.7766377696359377, 2.227545008755847], 20 | [-2.1683169232599098, 1.8983004809027886] 21 | ], 22 | "angleData": [ 23 | [-2.2689280275926293, 1.7696893372101876, -1.0244238433266701], 24 | [-0.23247131479853, 2.71626735478773], 25 | [-2.1683169232599098, 1.8983004809027886], 26 | [-2.0943951023931966, 2.05263385333391, -0.9281871475493136], 27 | [0.29644225714765154, 2.5876562110951293], 28 | [1.7766377696359377, 2.227545008755847] 29 | ] 30 | }] 31 | 32 | }, 33 | "polars": { 34 | "91c8d96d-4bbd-4c2f-ab36-295b04e83589": { 35 | "id": "91c8d96d-4bbd-4c2f-ab36-295b04e83589", 36 | "name": "dynamicPolar", 37 | "description": "Dynamic polar diagram from actual sailing", 38 | "source": { 39 | "label": "signalk-polar" 40 | }, 41 | "windData": [{ 42 | "trueWindSpeed": 8, 43 | "optimalBeats": [ 44 | [0.29644225714765154, 2.5876562110951293], 45 | [-0.23247131479853, 2.71626735478773] 46 | ], 47 | "optimalGybes": [ 48 | [1.7766377696359377, 2.227545008755847], 49 | [-2.1683169232599098, 1.8983004809027886] 50 | ], 51 | "angleData": [ 52 | [-2.2689280275926293, 1.7696893372101876, -1.0244238433266701], 53 | [-0.23247131479853, 2.71626735478773], 54 | [-2.1683169232599098, 1.8983004809027886], 55 | [-2.0943951023931966, 2.05263385333391, -0.9281871475493136], 56 | [0.29644225714765154, 2.5876562110951293], 57 | [1.7766377696359377, 2.227545008755847] 58 | ] 59 | }] 60 | }, 61 | "df48485e-6f70-4974-a346-d8907bbfd84c": { 62 | "id": "df48485e-6f70-4974-a346-d8907bbfd84c", 63 | "name": "Light winds wardrobe", 64 | "description": "Spinnaker #1, gib #1, main #2", 65 | "source": { 66 | "label": "signalk-polar" 67 | }, 68 | "windData": [{ 69 | "trueWindSpeed": 4, 70 | "optimalBeats": [ 71 | [0.299, 2.48], 72 | [-0.23, 2.71] 73 | ], 74 | "optimalGybes": [ 75 | [1.77, 2.22], 76 | [-2.16, 1.89] 77 | ], 78 | "angleData": [ 79 | [-2.2689280275926293, 1.7696893372101876, -1.0244238433266701], 80 | [-0.23247131479853, 2.71626735478773], 81 | [-2.1683169232599098, 1.8983004809027886], 82 | [-2.0943951023931966, 2.05263385333391, -0.9281871475493136], 83 | [0.29644225714765154, 2.5876562110951293], 84 | [1.7766377696359377, 2.227545008755847] 85 | ] 86 | }] 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /test/data/vessel-valid/sails-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "sails": { 4 | "area": { 5 | "total": { 6 | "value": 82, 7 | "timestamp": "2014-08-15T19:00:15.404Z", 8 | "$source": "foo.bar" 9 | }, 10 | "active": { 11 | "value": 78.4, 12 | "timestamp": "2014-08-15T19:00:15.404Z", 13 | "$source": "foo.bar" 14 | } 15 | }, 16 | "inventory": { 17 | "racingMain": { 18 | "name": "Racing main 2016", 19 | "type": "main", 20 | "material": "mylar", 21 | "brand": "WB Sails", 22 | "area": 34, 23 | "active": false 24 | }, 25 | "cruisingMain": { 26 | "name": "Cruising main", 27 | "type": "main", 28 | "material": "dacron", 29 | "brand": "WB Sails", 30 | "area": 33.8, 31 | "active": true, 32 | "reducedState": { 33 | "reduced": false 34 | } 35 | }, 36 | "genoa": { 37 | "name": "Cruising genoa", 38 | "type": "genoa", 39 | "material": "dacron", 40 | "brand": "North Sails", 41 | "area": 44.6, 42 | "active": true, 43 | "reducedState": { 44 | "reduced": true, 45 | "reefs": 1, 46 | "furledRatio": 0 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/data/vessel-valid/steering-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "steering": { 4 | "rudderAngle": { 5 | "value": 0.001, 6 | "$source": "foo.bar", 7 | "timestamp": "2014-08-15T19:00:15.402Z" 8 | }, 9 | "rudderAngleTarget": { 10 | "value": 0.001, 11 | "$source": "foo.bar", 12 | "timestamp": "2014-08-15T19:00:15.402Z" 13 | }, 14 | "autopilot": { 15 | "state": { 16 | "value" : "auto", 17 | "$source": "foo.bar", 18 | "timestamp": "2014-08-15T19:00:15.402Z" 19 | }, 20 | "mode": { 21 | "value": "normal", 22 | "$source": "foo.bar", 23 | "timestamp": "2014-08-15T19:00:15.402Z" 24 | }, 25 | "target": { 26 | "windAngleApparent": { 27 | "value": 1.0472, 28 | "$source": "foo.bar", 29 | "timestamp": "2014-08-15T19:00:15.402Z" 30 | }, 31 | "windAngleTrue": { 32 | "value": 1.39626, 33 | "$source": "foo.bar", 34 | "timestamp": "2014-08-15T19:00:15.402Z" 35 | }, 36 | "headingTrue": { 37 | "value": 0.02, 38 | "$source": "foo.bar", 39 | "timestamp": "2014-08-15T19:00:15.402Z" 40 | }, 41 | "headingMagnetic": { 42 | "value": 0.02, 43 | "$source": "foo.bar", 44 | "timestamp": "2014-08-15T19:00:15.402Z" 45 | } 46 | }, 47 | "deadZone":{ 48 | "value": 0.02, 49 | "$source": "foo.bar", 50 | "timestamp": "2014-08-15T19:00:15.402Z" 51 | }, 52 | "gain": { 53 | "value": 0.02, 54 | "$source": "foo.bar", 55 | "timestamp": "2014-08-15T19:00:15.402Z" 56 | }, 57 | "maxDriveCurrent": { 58 | "value": 0.02, 59 | "$source": "foo.bar", 60 | "timestamp": "2014-08-15T19:00:15.402Z" 61 | }, 62 | "maxDriveRate": { 63 | "value": 0.02, 64 | "$source": "foo.bar", 65 | "timestamp": "2014-08-15T19:00:15.402Z" 66 | }, 67 | "portLock": { 68 | "value": 0.02, 69 | "$source": "foo.bar", 70 | "timestamp": "2014-08-15T19:00:15.402Z" 71 | }, 72 | "starboardLock": { 73 | "value": 0.02, 74 | "$source": "foo.bar", 75 | "timestamp": "2014-08-15T19:00:15.402Z" 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/data/vessel-valid/tanks-sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:c0d79334-4e25-4245-8892-54e8ccc8021d", 3 | "tanks": { 4 | "freshWater": { 5 | "main": { 6 | "capacity": { 7 | "value": 31.7, 8 | "$source": "foo.bar", 9 | "timestamp": "2014-08-15T19:00:15.402Z" 10 | }, 11 | "currentVolume": { 12 | "value": 0.0887, 13 | "$source": "foo.bar", 14 | "timestamp": "2014-08-15T19:00:15.402Z" 15 | } 16 | } 17 | }, 18 | "liveWell": { 19 | "live1":{ 20 | "temperature": { 21 | "value": 0, 22 | "$source": ".36.0", 23 | "timestamp": "2017-01-24T23:59:01Z", 24 | "pgn": 130312 25 | } 26 | } 27 | }, 28 | "liveWell": { 29 | "bait1":{ 30 | "temperature": { 31 | "value": 0, 32 | "$source": ".36.0", 33 | "timestamp": "2017-01-24T23:59:01Z", 34 | "pgn": 130312 35 | } 36 | } 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/data/vessel-valid/vessel-just_mmsi_identifier.json: -------------------------------------------------------------------------------- 1 | { 2 | "mmsi": "230099999" 3 | } -------------------------------------------------------------------------------- /test/data/vessel-valid/vessel-just_uuid_identifier.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:b7590868-1d62-47d9-989c-32321b349fb9" 3 | } -------------------------------------------------------------------------------- /test/data/vessel-valid/vessel-uuid_and_mmsi_identifiers.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "urn:mrn:signalk:uuid:b7590868-1d62-47d9-989c-32321b349fb9", 3 | "mmsi": "230099999" 4 | } -------------------------------------------------------------------------------- /test/schema-api.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai') 2 | chai.Should() 3 | const expect = chai.expect 4 | const signalkSchema = require('../dist/') 5 | 6 | describe('metadata:getUnits', function () { 7 | it('Valid simple getUnits works', function () { 8 | signalkSchema.getUnits('vessels.foo.navigation.speedOverGround').should.equal('m/s') 9 | }) 10 | 11 | it('Valid complex path getUnits works', function () { 12 | signalkSchema.getUnits('vessels.foo.propulsion.0.oilTemperature').should.equal('K') 13 | }) 14 | 15 | it('Invalid getUnits returns undefined', function () { 16 | expect(signalkSchema.getUnits('vessels.foo.bar.0.oilTemperature')).to.be.undefined 17 | }) 18 | }) 19 | 20 | describe('metadata:getMetadata', function () { 21 | it('Valid simple getMetadata works', function () { 22 | signalkSchema.getMetadata('vessels.foo.navigation.speedOverGround').should.deep.equal({ 23 | units: 'm/s', 24 | description: "Vessel speed over ground. If converting from AIS 'HIGH' value, set to 102.2 (Ais max value) and add warning in notifications" 25 | }) 26 | }) 27 | 28 | it('Valid complex path getMetadata works', function () { 29 | signalkSchema.getMetadata('vessels.foo.propulsion.0.oilTemperature').should.deep.equal({ 30 | units: 'K', 31 | description: 'Oil temperature' 32 | }) 33 | }) 34 | 35 | it('Invalid getMetadata returns undefined', function () { 36 | expect(signalkSchema.getMetadata('vessels.foo.0.oilTemperature')).to.be.undefined 37 | }) 38 | 39 | it('getMetadata for path with enum works', function () { 40 | expect( 41 | signalkSchema.getMetadata('vessels.foo.navigation.datetime.gnssTimeSource') 42 | ).to.deep.equal({ 43 | description: 'Source of GNSS Date and Time', 44 | enum: [ 45 | 'GPS', 46 | 'GLONASS', 47 | 'Galileo', 48 | 'Beidou', 49 | 'IRNSS', 50 | 'Radio Signal', 51 | 'Internet', 52 | 'Local clock' 53 | ] 54 | }) 55 | }) 56 | }) 57 | 58 | describe('getAISShipTypeName works', function() { 59 | it("ship type 20 is 'Wing In Ground'", function() { 60 | expect( 61 | signalkSchema.getAISShipTypeName(20) 62 | ).to.deep.equal('Wing In Ground') 63 | }); 64 | }); 65 | 66 | 67 | describe('getAtonTypeName works', function() { 68 | it("ship type 11 is 'Beacon, Cardinal S'", function() { 69 | expect( 70 | signalkSchema.getAtonTypeName(11) 71 | ).to.deep.equal('Beacon, Cardinal S') 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /test/schemaReferences.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert 2 | 3 | var signalk = require('../'); 4 | var RefParser = require('@apidevtools/json-schema-ref-parser'); 5 | var path = require('path'); 6 | 7 | describe('Schema references', function() { 8 | it('missing files are not referenced', function() { 9 | var signalkSchema = require('../schemas/signalk.json'); 10 | var tv4 = signalk.getTv4(); 11 | 12 | tv4.validate({}, signalkSchema); 13 | assert.lengthOf(tv4.getMissingUris(), 0, 'There should be no missing schema uris, but found ' + tv4.getMissingUris()); 14 | }) 15 | 16 | it('all references are valid', function(done) { 17 | RefParser.dereference(path.join(__dirname, '../schemas/signalk.json'), function(err, schema) { 18 | if (err) { 19 | done(err); 20 | } else { 21 | done(); 22 | } 23 | }); 24 | }) 25 | }); 26 | -------------------------------------------------------------------------------- /test/sources.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const should = chai.should() 3 | chai.use(require('../dist/').chaiModule); 4 | const FullSignalK = require('../src/fullsignalk') 5 | const debug = require('debug')('test:sources') 6 | 7 | var deltaWithMiscSources = { 8 | "context": "vessels.urn:mrn:imo:mmsi:200000000", 9 | "updates": [{ 10 | "source": { 11 | "sentence": "HDT", 12 | "label": "0183-1", 13 | "talker": "II" 14 | }, 15 | "timestamp": "2016-08-03T07:55:57.000Z", 16 | "values": [{ 17 | "path": "navigation.headingTrue", 18 | "value": 0.2231 19 | }] 20 | }, { 21 | "source": { 22 | "src": "37", 23 | "pgn": 127251, 24 | "label": "N2000-01" 25 | }, 26 | "timestamp": "2016-06-20T10:33:36Z", 27 | "values": [{ 28 | "path": "navigation.rateOfTurn", 29 | "value": 0.108908 30 | }] 31 | }, { 32 | "$source": "1W.0316013faeff", 33 | "timestamp": "2016-07-28T18:18:46.074Z", 34 | "values": [{ 35 | "path": "propulsion.engine1.temperature", 36 | "value": 301.837 37 | }] 38 | }, { 39 | "$source": "i2c-0.0x48.volts", 40 | "timestamp": "2016-07-28T18:18:46.074Z", 41 | "values": [{ 42 | "path": "electrical.batteries.house.voltage", 43 | "value": 12.837 44 | }] 45 | }, { 46 | "$source": "i2c-0.0x48.amps", 47 | "timestamp": "2016-07-28T18:18:46.074Z", 48 | "values": [{ 49 | "path": "electrical.batteries.house.current", 50 | "value": -0.837 51 | }] 52 | }, { 53 | "timestamp": "2016-08-03T07:55:57.000Z", 54 | "values": [{ 55 | "path": "navigation.headingTrue", 56 | "value": 0.2231 57 | }] 58 | }] 59 | } 60 | 61 | describe('Sources in delta', function() { 62 | it("are valid", function() { 63 | var fullSignalK = new FullSignalK('urn:mrn:imo:mmsi:200000000') 64 | fullSignalK.addDelta(deltaWithMiscSources) 65 | var full = fullSignalK.retrieve() 66 | full.sources['0183-1']['II'].talker.should.equal('II') 67 | full.sources['N2000-01']['37']['n2k']['src'].should.equal('37') 68 | should.exist(full.sources['i2c-0']['0x48']) 69 | should.exist(full.sources['1W']['0316013faeff']) 70 | //FIXME for some reason tv4 complains about source's type property being undefined 71 | // renaming the type property of the source fixes the problem 72 | // fix with a better validation tool or dig deeper 73 | // full.should.be.validSignalK 74 | deltaWithMiscSources.should.be.validSignalKDelta; 75 | }); 76 | }); 77 | 78 | describe('Delta with source.instance', function() { 79 | it("produces valid full", function() { 80 | const delta = { 81 | "context": "vessels.urn:mrn:imo:mmsi:200000000", 82 | "updates": [ 83 | { 84 | "source": { 85 | "label": "aLabel", 86 | "type": "NMEA2000", 87 | "pgn": 130312, 88 | "src": "41", 89 | "instance": "5" 90 | }, 91 | "timestamp": "2015-01-15T16:15:18.136Z", 92 | "values": [ 93 | { 94 | "path": "environment.inside.engineRoom.temperature", 95 | "value": 70 96 | } 97 | ] 98 | } 99 | ] 100 | } 101 | delta.should.be.validSignalKDelta 102 | 103 | const fullSignalK = new FullSignalK('urn:mrn:imo:mmsi:200000000'); 104 | fullSignalK.addDelta(delta); 105 | const full = fullSignalK.retrieve(); 106 | debug((JSON.stringify(full, null, 2))) 107 | full.should.be.validSignalK 108 | }) 109 | }); 110 | --------------------------------------------------------------------------------