diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7ac04481..711ea14f 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -41,11 +41,14 @@ jobs: - run: | pip install mkdocs-material pip install mkdocs-minify-plugin - pip install mkdocs-redirects pip install mkdocs-monorepo-plugin pip install mkdocs-rss-plugin pip install mkdocs-git-revision-date-localized-plugin pip install "mkdocs-material[imaging]" pip install mkdocs-glightbox + pip install mkdocs-callouts + + - name: Pre-build documentation + run: python3 build_docs.py - run: mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index 66479553..16bab422 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,20 @@ node_modules/ *swp .cache +.DS_Store -# jekyll -_site/ +# Jekyll vendor/ .jekyll-cache/ + +# MkDocs +_site/ +_build/ + +# Auto-generated category index pages +docs/plugins/connectivity/ +docs/plugins/devices/ +docs/plugins/monitoring/ +docs/plugins/development/ +docs/plugins/infrastructure/ +docs/plugins/templates/ diff --git a/Dockerfile b/Dockerfile index 384cd19a..aa62202e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,4 +5,6 @@ RUN pip install \ mkdocs-monorepo-plugin \ mkdocs-rss-plugin \ mkdocs-glightbox \ + mkdocs-minify-plugin \ + mkdocs-callouts \ "mkdocs-material[imaging]" diff --git a/sigfox/LICENSE b/LICENSE similarity index 97% rename from sigfox/LICENSE rename to LICENSE index 3ac71b5d..2144e7a3 100644 --- a/sigfox/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Thinger.io +Copyright (c) Thinger.io Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index fd00eab6..918e5406 100644 --- a/Makefile +++ b/Makefile @@ -2,4 +2,4 @@ build: docker build -t thinger/plugins . serve: - docker run --rm -it -p 8000:8000 -v ${PWD}:/docs --name thinger_plugins thinger/plugins + docker run --rm -p 9000:9000 -v ${PWD}:/docs --entrypoint sh thinger/plugins -c "python3 build_docs.py && mkdocs serve -a 0.0.0.0:9000" diff --git a/README.md b/README.md new file mode 100644 index 00000000..3da21531 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Thinger.io Marketplace + +Official plugin repository for [Thinger.io](https://thinger.io) IoT platform. Plugins extend the platform with integrations for third-party services, device protocols, and data visualization tools. + +Browse the marketplace at [marketplace.thinger.io](https://marketplace.thinger.io). + +## Repository Structure + +``` +plugin-name/ +├── plugin.json # Plugin metadata and configuration +├── README.md # Documentation (rendered in marketplace) +├── CHANGELOG.md # Version history +├── assets/ # Images and resources +└── task/ # Container definition (if applicable) + └── Dockerfile +``` + +## Development + +Requires Docker installed. + +```bash +make build # Build Docker image (first time only) +make serve # Start dev server at http://localhost:9000 +``` + +Changes to `docs/` are hot-reloaded. For plugin changes, restart the server. + +## Contributing + +See the [contributing guide](https://marketplace.thinger.io/contributing/) for detailed documentation on: + +- Plugin structure and required files +- Writing integration plugins +- Developing custom plugins +- Documentation guidelines + +## License + +MIT License - see [LICENSE](LICENSE) for details. diff --git a/alertmanager/README.md b/alertmanager/README.md index af4f475d..4d39fcc8 100644 --- a/alertmanager/README.md +++ b/alertmanager/README.md @@ -2,7 +2,7 @@ # Alertmanager

- Prometheus logo + Prometheus logo

The Alertmanager handles alerts sent by clients applications such as Prometheus server. More information in their [official documentation](https://prometheus.io/docs/alerting/latest/alertmanager/). @@ -13,13 +13,9 @@ This plugins requires the existence of the [Prometheus plugin](https://marketpla In order for the connection to take place, edit the `prometheus.yml` file by introducing your details and selecting your desired configuration. -

- Prometheus alerts showcasing and alert over a metric devices disconnected greater than 5 -

+![Prometheus alerts showcasing and alert over a metric devices disconnected greater than 5](assets/prometheus-alerts.png) -

- Alertmanager dashboard showcasing and alert over a metric devices disconnected greater than 5 -

+![Alertmanager dashboard showcasing and alert over a metric devices disconnected greater than 5](assets/alertmanager.png) ## Official Documentation @@ -29,12 +25,7 @@ You can find how to configure Alermanager at [this link](https://prometheus.io/d More details regarding the configuration of Prometheus at [this link](https://prometheus.io/docs/prometheus/latest/configuration/configuration/). -!!! note - - Any configuration change in `prometheus.yml` or `rules.yml` requires a - restart of the Prometheus plugin, and any change to `alertmanager.yml` - requires a restart of the Alertmanager plugin. - -## License - -Alertmanager, as well as Prometheus are distributed under the [Apache 2.0 License](https://prometheus.io/docs/introduction/faq/#what-license-is-prometheus-released-under). +> [!NOTE] +> Any configuration change in `prometheus.yml` or `rules.yml` requires a +> restart of the Prometheus plugin, and any change to `alertmanager.yml` +> requires a restart of the Alertmanager plugin. diff --git a/alertmanager/assets/alertmanager.png b/alertmanager/assets/alertmanager.png new file mode 100644 index 00000000..473db248 Binary files /dev/null and b/alertmanager/assets/alertmanager.png differ diff --git a/alertmanager/assets/prometheus-alerts.png b/alertmanager/assets/prometheus-alerts.png new file mode 100644 index 00000000..73289f61 Binary files /dev/null and b/alertmanager/assets/prometheus-alerts.png differ diff --git a/alertmanager/assets/prometheus-logo.svg b/alertmanager/assets/prometheus-logo.svg new file mode 100644 index 00000000..48a53b6b --- /dev/null +++ b/alertmanager/assets/prometheus-logo.svg @@ -0,0 +1,3 @@ + + +image/svg+xml \ No newline at end of file diff --git a/alertmanager/docs/changelog.md b/alertmanager/docs/changelog.md deleted file mode 100644 index ee2de089..00000000 --- a/alertmanager/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "alertmanager/CHANGELOG.md" diff --git a/alertmanager/docs/index.md b/alertmanager/docs/index.md deleted file mode 100644 index b24967c2..00000000 --- a/alertmanager/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "alertmanager/README.md" diff --git a/alertmanager/docs/plugin_file.md b/alertmanager/docs/plugin_file.md deleted file mode 100644 index 7c7b425e..00000000 --- a/alertmanager/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -``` json title="Plugin configuration file" ---8<-- "alertmanager/plugin.json" -``` diff --git a/alertmanager/mkdocs.yml b/alertmanager/mkdocs.yml deleted file mode 100644 index a7765f85..00000000 --- a/alertmanager/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/alertmanager - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/alertmanager/plugin.json b/alertmanager/plugin.json index f8219168..665f4194 100644 --- a/alertmanager/plugin.json +++ b/alertmanager/plugin.json @@ -2,7 +2,7 @@ "name" : "alertmanager", "version" : "0.26.0-1", "description" : "Alertmanager alarm handler system", - "author" : "Jaime Bautista", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "Alertmanager", "description" : "Integration of Alertmanager with Thinger.io", - "image" : "docs/assets/prometheus-logo.svg", - "icon" : "docs/assets/prometheus-logo.svg" + "category" : "monitoring", + "image" : "assets/prometheus-logo.svg", + "icon" : "assets/prometheus-logo.svg" }, "tokens" : { "alertmanager_plugin_callback" : { diff --git a/build_docs.py b/build_docs.py new file mode 100644 index 00000000..c7544d0d --- /dev/null +++ b/build_docs.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python3 +""" +Pre-build script for MkDocs documentation. +Generates the docs structure automatically from plugin README.md, CHANGELOG.md and plugin.json files. + +This script generates all documentation in _build/plugins/ directory, keeping the +source plugin directories clean. The generated structure is: + +_build/plugins/ +└── plugin-name/ + ├── index.md (from README.md) + ├── changelog.md (from CHANGELOG.md) + ├── plugin_file.md (from plugin.json) + ├── assets/ (copied from plugin/assets/) + ├── *.md (extra pages from plugin/pages/) + └── mkdocs.yml (with docs_dir: . for flat structure) + +Run this script before `mkdocs build` or `mkdocs gh-deploy`. +""" + +import json +import re +import shutil +from pathlib import Path + +REPO_ROOT = Path(__file__).parent +MAIN_MKDOCS = REPO_ROOT / "mkdocs.yml" +BUILD_DIR = REPO_ROOT / "_build" / "plugins" + +# Directories/files to ignore when scanning for plugins +IGNORE_DIRS = {".github", "docs", "_build", ".cache", "site", "__pycache__"} + +# Files that are auto-generated +GENERATED_FILES = {"index.md", "changelog.md", "plugin_file.md"} + +# Category display names and order +CATEGORY_ORDER = ["devices", "connectivity", "monitoring", "development", "infrastructure", "templates"] +CATEGORY_NAMES = { + "connectivity": "Connectivity", + "devices": "Devices", + "monitoring": "Monitoring", + "development": "Development", + "infrastructure": "Infrastructure", + "templates": "Templates", +} + + +def find_plugins() -> list[Path]: + """Find all plugin directories (those containing a plugin.json file).""" + plugins = [] + for item in REPO_ROOT.iterdir(): + if item.is_dir() and item.name not in IGNORE_DIRS and not item.name.startswith("."): + plugin_json = item / "plugin.json" + if plugin_json.exists(): + plugins.append(item) + return sorted(plugins, key=lambda p: p.name) + + +def load_plugin_metadata(plugin_dir: Path) -> dict: + """Load plugin.json metadata.""" + plugin_json = plugin_dir / "plugin.json" + with open(plugin_json, "r", encoding="utf-8") as f: + return json.load(f) + + +def get_display_name(plugin_data: dict, fallback: str) -> str: + """Get the display name from plugin.json, preferring metadata.name.""" + # Try metadata.name first (the pretty display name) + if "metadata" in plugin_data and "name" in plugin_data["metadata"]: + return plugin_data["metadata"]["name"] + # Fallback to top-level name or directory name + return plugin_data.get("name", fallback) + + +def get_category(plugin_data: dict) -> str: + """Get the category from plugin.json metadata.""" + if "metadata" in plugin_data and "category" in plugin_data["metadata"]: + return plugin_data["metadata"]["category"].strip().lower() + return "other" + + +def generate_deprecation_warning(plugin_data: dict) -> str: + """Generate a GitHub-style deprecation warning if plugin is deprecated.""" + deprecated = plugin_data.get("deprecated") + if not deprecated: + return "" + + message = deprecated.get("message", "This plugin is no longer maintained") + replacement = deprecated.get("replacement", "") + + if replacement: + message = f"{message}. Use [{replacement}](../{replacement}/) instead." + + return f"\n> [!WARNING]\n> {message}\n" + + +def generate_plugin_docs(plugin_dir: Path, category: str) -> list[str]: + """ + Generate documentation structure for a single plugin in _build/plugins//. + Returns list of extra page names found. + """ + plugin_name = plugin_dir.name + plugin_data = load_plugin_metadata(plugin_dir) + + # Output directory in _build/plugins/// + output_dir = BUILD_DIR / category / plugin_name + + # Clean and create output directory + if output_dir.exists(): + shutil.rmtree(output_dir) + output_dir.mkdir(parents=True) + + # Check for extra pages in source plugin's pages/ folder (if exists) + extra_pages = [] + source_pages = plugin_dir / "pages" + if source_pages.exists(): + for md_file in source_pages.glob("*.md"): + # Copy extra page to build dir + shutil.copy(md_file, output_dir / md_file.name) + extra_pages.append(md_file.stem) + + # 1. Generate index.md from README.md + readme = plugin_dir / "README.md" + index_file = output_dir / "index.md" + if readme.exists(): + content = readme.read_text(encoding="utf-8") + # Inject deprecation warning after first heading if deprecated + deprecation_warning = generate_deprecation_warning(plugin_data) + if deprecation_warning: + # Insert after the first line (title) + lines = content.split("\n", 1) + if len(lines) == 2: + content = lines[0] + "\n" + deprecation_warning + lines[1] + else: + content = lines[0] + "\n" + deprecation_warning + index_file.write_text(content, encoding="utf-8") + else: + print(f" WARNING: {plugin_name} has no README.md") + index_file.write_text(f"# {plugin_name}\n\nNo documentation available.\n") + + # 2. Generate changelog.md from CHANGELOG.md + changelog = plugin_dir / "CHANGELOG.md" + changelog_file = output_dir / "changelog.md" + if changelog.exists(): + content = changelog.read_text(encoding="utf-8") + changelog_file.write_text(content, encoding="utf-8") + else: + changelog_file.write_text("# Changelog\n\nNo changelog available.\n") + + # 3. Generate plugin_file.md from plugin.json + plugin_json = plugin_dir / "plugin.json" + plugin_json_content = plugin_json.read_text(encoding="utf-8") + plugin_file_content = f'''--- +search: + exclude: true +--- + +# Plugin file + +```` json title="Plugin configuration file" +{plugin_json_content} +```` +''' + (output_dir / "plugin_file.md").write_text(plugin_file_content, encoding="utf-8") + + # 4. Copy assets from plugin root to build + assets_src = plugin_dir / "assets" + assets_dst = output_dir / "assets" + if assets_src.exists(): + shutil.copytree(assets_src, assets_dst) + + # 5. Generate mkdocs.yml for this plugin (with docs_dir: . for flat structure) + nav_items = ['"index.md"'] + + # Add extra pages to navigation (sorted alphabetically) + for page in sorted(extra_pages): + # Convert filename to title (get_started -> Get Started) + title = page.replace("_", " ").replace("-", " ").title() + nav_items.append(f'{title}: "{page}.md"') + + nav_items.append('Changelog: "changelog.md"') + nav_items.append('Plugin file: "plugin_file.md"') + + mkdocs_content = f"""site_name: plugins/{category}/{plugin_name} +docs_dir: . + +nav: + - {(chr(10) + " - ").join(nav_items)} +""" + (output_dir / "mkdocs.yml").write_text(mkdocs_content, encoding="utf-8") + + return extra_pages + + +def generate_category_index(category: str, plugins_info: list[tuple[str, str, str, str, bool]]) -> None: + """ + Generate an index.md for a category with a grid of plugin cards. + plugins_info is a list of (display_name, plugin_name, description, icon_path, is_deprecated) + Outputs to docs/plugins//index.md so MkDocs can find it. + """ + # Put category index in docs/plugins// so MkDocs can find it + category_dir = REPO_ROOT / "docs" / "plugins" / category + category_dir.mkdir(parents=True, exist_ok=True) + + category_display = CATEGORY_NAMES.get(category, category.title()) + + # Build the cards as custom HTML grid (not using Material cards) + cards = [] + for display_name, plugin_name, description, icon_path, is_deprecated in plugins_info: + # Icon path relative to the category index page + icon_url = f"{plugin_name}/{icon_path}" if icon_path else "" + + if icon_url: + icon_html = f'' + else: + icon_html = '
' + + card = f''' + {icon_html} +
+ {display_name} + {description} +
+
''' + cards.append(card) + + content = f"""# {category_display} + +
+{chr(10).join(cards)} +
+ + +""" + (category_dir / "index.md").write_text(content, encoding="utf-8") + + +def generate_main_nav_section(categories_data: dict) -> str: + """Generate the Integrations section for the main mkdocs.yml nav, with category index pages.""" + lines = [] + + # Generate nav with category index + plugins (flatter structure) + for category in CATEGORY_ORDER: + if category in categories_data: + category_display = CATEGORY_NAMES.get(category, category.title()) + lines.append(f" - {category_display}:") + lines.append(f" - Overview: 'plugins/{category}/index.md'") + for display_name, plugin_name, _, _, _ in categories_data[category]: + lines.append(f" - {display_name}: '!include ./_build/plugins/{category}/{plugin_name}/mkdocs.yml'") + + # Handle any categories not in CATEGORY_ORDER + for category in sorted(categories_data.keys()): + if category not in CATEGORY_ORDER: + category_display = CATEGORY_NAMES.get(category, category.title()) + lines.append(f" - {category_display}:") + lines.append(f" - Overview: 'plugins/{category}/index.md'") + for display_name, plugin_name, _, _, _ in categories_data[category]: + lines.append(f" - {display_name}: '!include ./_build/plugins/{category}/{plugin_name}/mkdocs.yml'") + + return "\n".join(lines) + + +def update_main_mkdocs_nav(categories_data: dict): + """ + Update the main mkdocs.yml with the auto-generated plugin list. + Replaces content between markers. + """ + mkdocs_content = MAIN_MKDOCS.read_text(encoding="utf-8") + + # Define markers for the auto-generated section + start_marker = " # AUTO-GENERATED-PLUGINS-START" + end_marker = " # AUTO-GENERATED-PLUGINS-END" + + nav_content = generate_main_nav_section(categories_data) + new_section = f"{start_marker}\n - Integrations:\n{nav_content}\n{end_marker}" + + if start_marker in mkdocs_content and end_marker in mkdocs_content: + # Replace existing section + pattern = re.compile( + re.escape(start_marker) + r".*?" + re.escape(end_marker), + re.DOTALL + ) + mkdocs_content = pattern.sub(new_section, mkdocs_content) + MAIN_MKDOCS.write_text(mkdocs_content, encoding="utf-8") + print(" Updated main mkdocs.yml with plugin list") + else: + # Print instructions for manual integration + print("\n" + "=" * 60) + print("Add these markers to mkdocs.yml to enable auto-updates:") + print("=" * 60) + print(f""" + - Plugins: + - 'plugins/index.md' + - Managing Plugins: 'plugins/managing.md' +{new_section} +""") + print("=" * 60 + "\n") + + +def main(): + from collections import defaultdict + + print("Pre-building documentation structure...") + print(f" Repository root: {REPO_ROOT}") + + # Find all plugins + plugins = find_plugins() + print(f"\n Found {len(plugins)} plugins:\n") + + # Group plugins by category and generate docs + categories_data = defaultdict(list) + + for plugin_dir in plugins: + plugin_name = plugin_dir.name + plugin_data = load_plugin_metadata(plugin_dir) + display_name = get_display_name(plugin_data, plugin_name) + category = get_category(plugin_data) + description = plugin_data.get("metadata", {}).get("description", plugin_data.get("description", "")) + is_deprecated = "deprecated" in plugin_data + + # Generate plugin docs in category subfolder + extra_pages = generate_plugin_docs(plugin_dir, category) + extras = f" (+{len(extra_pages)} extra)" if extra_pages else "" + print(f" - [{category}] {plugin_name}{extras}") + + # Get icon from metadata.icon with fallback to metadata.image + metadata = plugin_data.get("metadata", {}) + icon_path = metadata.get("icon", "") or metadata.get("image", "") + + # Store for category index and nav + categories_data[category].append((display_name, plugin_name, description, icon_path, is_deprecated)) + + # Sort plugins within each category by display name + for category in categories_data: + categories_data[category].sort(key=lambda x: x[0].lower()) + + # Generate category index pages + print("\n Generating category index pages...") + for category, plugins_info in categories_data.items(): + generate_category_index(category, plugins_info) + print(f" - {category}/ ({len(plugins_info)} plugins)") + + # Update main mkdocs.yml + print("") + update_main_mkdocs_nav(categories_data) + + print("\n Documentation pre-build complete!") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/chirpstack/LICENSE b/chirpstack/LICENSE deleted file mode 100644 index 0631ba07..00000000 --- a/chirpstack/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025-Current Thinger.io (INTERNET OF THINGER S.L.) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/chirpstack/README.md b/chirpstack/README.md index b89e5389..27262a9a 100644 --- a/chirpstack/README.md +++ b/chirpstack/README.md @@ -1,7 +1,7 @@ # ChirpStack

- ChirpStack logo + ChirpStack logo

ChirpStack is a flexible, open-source LoRaWAN® Network Server that allows organisations to deploy and operate their own private or public IoT networks with full control over infrastructure and data. It is widely used by companies, research institutions, and communities looking for reliable large-scale LoRaWAN deployments without vendor lock-in. @@ -35,10 +35,7 @@ Open the plugin settings page. In the **Applications** table click **Add +** an | **Port** | The gRPC service port exposed by ChirpStack (default is `8080`). If you are running ChirpStack behind a reverse proxy, make sure this port is accessible or properly forwarded. | | **Enabled** | Toggles the application integration on or off. | - -

- Add application modal in ChirpStack Thinger.io Plugin -

+![Add application modal in ChirpStack Thinger.io Plugin](assets/add_application.png) Unlike TTN or LORIOT, ChirpStack must be deployed and managed by the user. The plugin backend communicates with ChirpStack using gRPC (HTTP/2), which provides efficient and real-time interaction but also requires that the gRPC service port is reachable by Thinger.io. @@ -50,7 +47,6 @@ In most cases, you will need to either: Since ChirpStack is self-hosted, network accessibility is critical. In our tests we verified connectivity by forwarding the gRPC port from the router directly to the ChirpStack server, ensuring uplinks and downlinks were processed correctly. - ## ChirpStack Webhook Configuration Log in to the ChirpStack Application Server and select the target Application. @@ -59,14 +55,13 @@ Navigate to Integrations → Add Integration and choose the generic HTTP integra Complete the form with the values provided by the Thinger.io plugin: -

ChirpStack HTTP Webhook Settings for Thinger.io integration

+![ChirpStack HTTP Webhook Settings for Thinger.io integration](assets/chirpstack_webhook.png) | Field | Value | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Event endpoint URL(s)** | Copy the value shown in the plugin under **Endpoint Base URL for Uplinks**. ChirpStack will send all application traffic (uplinks, join events, acknowledgements) to this endpoint. | | **Headers** | Add a new header with key **Authorization** and value equal to the **ChirpStack API Token** provided in the plugin settings page. | - After saving, uplink traffic will start appearing in the plugin Logs panel. At this point the devices may still be unrecognised; autoprovision occurs once a matching Device Template is installed or created. @@ -89,35 +84,4 @@ Search the Marketplace for a template that matches your device model. If none ex > **Important** > Ensure the template **autoprovision prefix** matches the **Device ID Prefix** configured in the plugin. - --- - -## License - - - - - -This plugin is released under the **MIT License**: - -``` -Copyright © Thinger.io - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the “Software”), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -``` diff --git a/chirpstack/assets/add_application.png b/chirpstack/assets/add_application.png new file mode 100644 index 00000000..7971a910 Binary files /dev/null and b/chirpstack/assets/add_application.png differ diff --git a/chirpstack/assets/chirpstack-icon.png b/chirpstack/assets/chirpstack-icon.png new file mode 100644 index 00000000..a03a7360 Binary files /dev/null and b/chirpstack/assets/chirpstack-icon.png differ diff --git a/chirpstack/assets/chirpstack-image.png b/chirpstack/assets/chirpstack-image.png new file mode 100644 index 00000000..f35fc160 Binary files /dev/null and b/chirpstack/assets/chirpstack-image.png differ diff --git a/chirpstack/assets/chirpstack-logo.png b/chirpstack/assets/chirpstack-logo.png new file mode 100644 index 00000000..ee6369a1 Binary files /dev/null and b/chirpstack/assets/chirpstack-logo.png differ diff --git a/chirpstack/assets/chirpstack_webhook.png b/chirpstack/assets/chirpstack_webhook.png new file mode 100644 index 00000000..b2dcaa3a Binary files /dev/null and b/chirpstack/assets/chirpstack_webhook.png differ diff --git a/chirpstack/docs/changelog.md b/chirpstack/docs/changelog.md deleted file mode 100644 index 970da168..00000000 --- a/chirpstack/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "ttn/CHANGELOG.md" diff --git a/chirpstack/docs/index.md b/chirpstack/docs/index.md deleted file mode 100644 index aa049468..00000000 --- a/chirpstack/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "chirpstack/README.md" diff --git a/chirpstack/docs/plugin_file.md b/chirpstack/docs/plugin_file.md deleted file mode 100644 index d5329052..00000000 --- a/chirpstack/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "chirpstack/plugin.json" -```` diff --git a/chirpstack/mkdocs.yml b/chirpstack/mkdocs.yml deleted file mode 100644 index 628753c6..00000000 --- a/chirpstack/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/chirpstack - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/chirpstack/plugin.json b/chirpstack/plugin.json index 47d9d825..f0558cd4 100644 --- a/chirpstack/plugin.json +++ b/chirpstack/plugin.json @@ -12,8 +12,9 @@ "metadata": { "name": "ChirpStack", "description": "Chirpstack Integration", - "image": "./docs/assets/Chirpstack_logo_long.png", - "icon": "https://avatars.githubusercontent.com/u/56792266?s=200&v=4" + "category": "connectivity", + "image": "assets/chirpstack-image.png", + "icon": "assets/chirpstack-icon.png" }, "tokens": { "chirpstack_plugin": { diff --git a/comet-tx5xx-tx6xx/LICENSE.md b/comet-tx5xx-tx6xx/LICENSE.md deleted file mode 100644 index f1bacd61..00000000 --- a/comet-tx5xx-tx6xx/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2025 Thinger.io - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/comet-tx5xx-tx6xx/README.md b/comet-tx5xx-tx6xx/README.md index 3d596f50..2a07e350 100644 --- a/comet-tx5xx-tx6xx/README.md +++ b/comet-tx5xx-tx6xx/README.md @@ -1,10 +1,9 @@ # Comet Tx6xx and Tx5xx Series

- + Comet TX5xx/TX6xx

- A Thinger.io plugin that seamlessly ingests SOAP XML data coming from Comet Tx5xx / Tx6xx dataloggers (temperature, humidity, CO₂ and/or pressure) and converts it into structured JSON records stored in a Data Bucket. The plugin provides an HTTPS endpoint, automatic device provisioning, data parsing in Node JS, and ready-to-use dashboards and alarms. ## Thinger.io and Plugin Name Integration @@ -22,7 +21,6 @@ firmware v1.0 & v1.1 XML versions. ## Requirements - | Component | Minimum version | Notes | |-----------|-----------------|-------| | Thinger.io instance | 6.5.4-developer or newer | Must support **Plugins** and **Proxy Forwarders** | @@ -31,7 +29,6 @@ firmware v1.0 & v1.1 XML versions. | Comet Tx5xx / Tx6xx logger | any SOAP-capable firmware | Device must be configured to POST data to `http://your-host:4444/` | | External TCP/UDP reachability | Port **4444** (or chosen) | Forwarded to Node-RED via Thinger **Proxy Forwarder** | - ## Get Started ### Installation @@ -130,7 +127,6 @@ firmware v1.0 & v1.1 XML versions. 5. Finally, enter the node "http-request", select "Use authentication", select "Bearer authentication" and paste `${THINGER_TOKEN_NODE_RED_PLUGIN}` in Token. Then press "Done" 6. Press "Deploy" to start Node RED instance. - ### Usage 1. Configure each Comet logger's **SOAP destination** to `https:///4444/`. @@ -141,4 +137,3 @@ firmware v1.0 & v1.1 XML versions. ## Additional Resources - [Documentation](https://www.cometsystem.es/productos/reg-t6640) - diff --git a/comet-tx5xx-tx6xx/docs/changelog.md b/comet-tx5xx-tx6xx/docs/changelog.md deleted file mode 100644 index 9a7f9289..00000000 --- a/comet-tx5xx-tx6xx/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "comet-tx5xx-tx6xx/CHANGELOG.md" diff --git a/comet-tx5xx-tx6xx/docs/index.md b/comet-tx5xx-tx6xx/docs/index.md deleted file mode 100644 index ab6aec0d..00000000 --- a/comet-tx5xx-tx6xx/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "comet-tx5xx-tx6xx/README.md" diff --git a/comet-tx5xx-tx6xx/docs/plugin_file.md b/comet-tx5xx-tx6xx/docs/plugin_file.md deleted file mode 100644 index 6b93efec..00000000 --- a/comet-tx5xx-tx6xx/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "comet-tx5xx-tx6xx/plugin.json" -```` diff --git a/comet-tx5xx-tx6xx/mkdocs.yml b/comet-tx5xx-tx6xx/mkdocs.yml deleted file mode 100644 index 24a6f1aa..00000000 --- a/comet-tx5xx-tx6xx/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/comet-tx5xx-tx6xx - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/comet-tx5xx-tx6xx/plugin.json b/comet-tx5xx-tx6xx/plugin.json index ef16affb..8af992ec 100644 --- a/comet-tx5xx-tx6xx/plugin.json +++ b/comet-tx5xx-tx6xx/plugin.json @@ -12,6 +12,7 @@ "metadata": { "name": "Comet Tx", "description": "Integration of Comet Tx6xx and Tx5xx Series", + "category": "devices", "image": "assets/comet-tx5xx-tx6xx.png" }, "resources": { diff --git a/docker-registry/README.md b/docker-registry/README.md index df47cb6a..8e3361ea 100644 --- a/docker-registry/README.md +++ b/docker-registry/README.md @@ -2,7 +2,7 @@ # Docker Registry

- Docker Registry logo + Docker Registry logo

Thinger.io plugin for running a Docker Registry in order to be able to upload and persist images of ML Models. diff --git a/docker-registry/assets/docker-registry-logo.svg b/docker-registry/assets/docker-registry-logo.svg new file mode 100644 index 00000000..cc9f4073 --- /dev/null +++ b/docker-registry/assets/docker-registry-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docker-registry/assets/registry-icon.svg b/docker-registry/assets/registry-icon.svg new file mode 100644 index 00000000..41082f79 --- /dev/null +++ b/docker-registry/assets/registry-icon.svg @@ -0,0 +1,542 @@ + + + + diff --git a/docker-registry/assets/registry-logo.svg b/docker-registry/assets/registry-logo.svg new file mode 100644 index 00000000..227fc21c --- /dev/null +++ b/docker-registry/assets/registry-logo.svg @@ -0,0 +1,531 @@ + + + + diff --git a/docker-registry/docs/changelog.md b/docker-registry/docs/changelog.md deleted file mode 100644 index 706e50c5..00000000 --- a/docker-registry/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "docker-registry/CHANGELOG.md" diff --git a/docker-registry/docs/index.md b/docker-registry/docs/index.md deleted file mode 100644 index a452f5f1..00000000 --- a/docker-registry/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "docker-registry/README.md" diff --git a/docker-registry/docs/plugin_file.md b/docker-registry/docs/plugin_file.md deleted file mode 100644 index 86fdd102..00000000 --- a/docker-registry/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "docker-registry/plugin.json" -```` diff --git a/docker-registry/mkdocs.yml b/docker-registry/mkdocs.yml deleted file mode 100644 index 15577af0..00000000 --- a/docker-registry/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/docker-registry - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/docker-registry/plugin.json b/docker-registry/plugin.json index afec0f26..4bb02b59 100644 --- a/docker-registry/plugin.json +++ b/docker-registry/plugin.json @@ -2,7 +2,7 @@ "name" : "docker-registry", "version" : "2.8.1-1", "description" : "Docker Registry", - "author" : "Álvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "Docker Registry", "description" : "A Registry for images build in Thinger.io", - "image" : "docs/assets/registry-logo.svg", - "icon" : "docs/assets/registry-icon.svg" + "category" : "infrastructure", + "image" : "assets/registry-logo.svg", + "icon" : "assets/registry-icon.svg" }, "task": { "type": "docker", diff --git a/docs/announcements/posts/2024-06-20-rstudio-v4.4.0-1/index.md b/docs/announcements/posts/2024-06-20-rstudio-v4.4.0-1/index.md index 9858436b..1fcc5f19 100644 --- a/docs/announcements/posts/2024-06-20-rstudio-v4.4.0-1/index.md +++ b/docs/announcements/posts/2024-06-20-rstudio-v4.4.0-1/index.md @@ -1,6 +1,6 @@ --- title: RStudio Integration into Thinger.io -image: "/announcements/2024-06-20-rstudio-v4.4.0-1/RStudio_logo_flat.svg" +image: "/announcements/2024-06-20-rstudio-v4.4.0-1/rstudio-logo.svg" date: 2024-06-20 authors: [thinger.io] categories: @@ -15,7 +15,7 @@ comments: true # RStudio Integration into Thinger.io

- RStudio logo + RStudio logo

In an effort to provide different development environments within Thinger.io for Time Series Data Analysis, we are pleased to announce the release of the [RStudio Plugin](https://marketplace.thinger.io/plugins/rstudio/) for Thinger.io. diff --git a/docs/announcements/posts/2024-06-20-rstudio-v4.4.0-1/RStudio_logo_flat.svg b/docs/announcements/posts/2024-06-20-rstudio-v4.4.0-1/rstudio-logo.svg similarity index 100% rename from docs/announcements/posts/2024-06-20-rstudio-v4.4.0-1/RStudio_logo_flat.svg rename to docs/announcements/posts/2024-06-20-rstudio-v4.4.0-1/rstudio-logo.svg diff --git a/docs/assets/OSI_Standard_Logo_0.svg b/docs/assets/osi-standard-logo.svg similarity index 100% rename from docs/assets/OSI_Standard_Logo_0.svg rename to docs/assets/osi-standard-logo.svg diff --git a/docs/how_it_works/hardware_manufacturers.md b/docs/how_it_works/hardware_manufacturers.md index ac195e85..426c56de 100644 --- a/docs/how_it_works/hardware_manufacturers.md +++ b/docs/how_it_works/hardware_manufacturers.md @@ -13,13 +13,12 @@ Are you a hardware manufacturer with groundbreaking devices waiting to be introd We provide a platform to showcase and introduce their innovative devices to a global audience. By joining our marketplace, hardware manufacturers can increase the visibility of their products, leading to enhanced device sales and market reach. -!!! note "Benefits" - - Reduce time-to-market & developments costs - - Increase hardware product visibility & sales - - Creates an ecosystem of compatible devices in which they must be +> [!NOTE] Benefits +> Reduce time-to-market & developments costs +> +> Increase hardware product visibility & sales +> +> Creates an ecosystem of compatible devices in which they must be In just four easy steps a simple hardware can be integrated in the platform for thousand of users: diff --git a/docs/how_it_works/iot_consultants.md b/docs/how_it_works/iot_consultants.md index 71e93c84..9f06dc9b 100644 --- a/docs/how_it_works/iot_consultants.md +++ b/docs/how_it_works/iot_consultants.md @@ -10,13 +10,12 @@ Thinger.io Marketplace presents an unparalleled opportunity for consultancies to Thinger.io Marketplace offers a unique opportunity for consultancies to develop last-mile integrations and facilitate seamless device deployment for their clients. By leveraging our platform, consultancies can efficiently connect with potential customers and identify the best-suited devices for their specific solutions, resulting in improved customer satisfaction and project success. -!!! note "Benefits" - - Fast and simpler development processes - - Reduce long-term technologic debt - - Win-win relationship: They find customers growing our SAAS sales +> [!NOTE] Benefits +> Fast and simpler development processes +> +> Reduce long-term technologic debt +> +> Win-win relationship: They find customers growing our SAAS sales Incorporating Thinger.io Marketplace into your consultancy toolkit offers a multitude of benefits, which include: diff --git a/docs/plugins/index.md b/docs/plugins/index.md index bf31d3f2..884b3b23 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -7,18 +7,16 @@ The core of Thinger.io IoT Platform is designed to be lean and lightweight, to m For instructions and information about installing, upgrading, troubleshooting, and managing Thinger.io plugins, see [Managing Plugins](managing) section. For learning how to use any existent plugin, just find on the left a list of each one. -!!! note - - Plugins are only available for premium Thinger.io servers. Check out the pricing - page to create your own instance within minutes. - - [To the pricing](https://thinger.io/pricing/){ .md-button .md-button--primary } - -!!! tip - - Thinger.io plugins source code is now available in our Github repository,so - if you want to contribute do not hesitate on forking to project and send your pull-requests. - Link is in the top right corner of the page. +> [!NOTE] +> Plugins are only available for premium Thinger.io servers. Check out the pricing +> page to create your own instance within minutes. +> +> [To the pricing](https://thinger.io/pricing/){ .md-button .md-button--primary } + +> [!TIP] +> Thinger.io plugins source code is now available in our Github repository, so +> if you want to contribute do not hesitate on forking to project and send your pull-requests. +> Link is in the top right corner of the page. ## Custom Plugins Development diff --git a/dragino-lt22222l/LICENSE.md b/dragino-lt22222l/LICENSE.md deleted file mode 100644 index f1bacd61..00000000 --- a/dragino-lt22222l/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2025 Thinger.io - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/dragino-lt22222l/README.md b/dragino-lt22222l/README.md index efcf540e..553da86f 100644 --- a/dragino-lt22222l/README.md +++ b/dragino-lt22222l/README.md @@ -1,7 +1,7 @@ # LT-22222-L LoRa I/O Controller

- Dragino LT-22222-L logo + Dragino LT-22222-L

The Dragino LT series I/O Modules are Long Range LoRa I/O Controller. It contains different I/O Interfaces such as: analog current Input, analog voltage input, relay output, digital input and digital output etc. The LT I/O Modules are designed to simplify the installation of I/O monitoring. diff --git a/dragino-lt22222l/assets/LT-22222-L.png b/dragino-lt22222l/assets/dragino-lt22222l.png similarity index 100% rename from dragino-lt22222l/assets/LT-22222-L.png rename to dragino-lt22222l/assets/dragino-lt22222l.png diff --git a/dragino-lt22222l/docs/changelog.md b/dragino-lt22222l/docs/changelog.md deleted file mode 100644 index cbc10421..00000000 --- a/dragino-lt22222l/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "dragino-lt22222l/CHANGELOG.md" diff --git a/dragino-lt22222l/docs/index.md b/dragino-lt22222l/docs/index.md deleted file mode 100644 index 0603d57c..00000000 --- a/dragino-lt22222l/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "dragino-lt22222l/README.md" diff --git a/dragino-lt22222l/docs/plugin_file.md b/dragino-lt22222l/docs/plugin_file.md deleted file mode 100644 index 288b7537..00000000 --- a/dragino-lt22222l/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "dragino-lt22222l/plugin.json" -```` diff --git a/dragino-lt22222l/mkdocs.yml b/dragino-lt22222l/mkdocs.yml deleted file mode 100644 index 5c8ef9b7..00000000 --- a/dragino-lt22222l/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/dragino-lt22222l - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/dragino-lt22222l/plugin.json b/dragino-lt22222l/plugin.json index d0f4014c..edd15094 100644 --- a/dragino-lt22222l/plugin.json +++ b/dragino-lt22222l/plugin.json @@ -10,9 +10,10 @@ "directory": "dragino-lt22222l" }, "metadata": { - "name": "Dragino LT-22222-L LoRa I/O Controller", + "name": "Dragino LT-22222-L", "description": "Dragino LT-22222-L integration with Thinger.io", - "image": "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/dragino-lt22222l/img/LT-22222-L.png" + "category": "devices", + "image": "assets/dragino-lt22222l.png" }, "resources": { "products": [ diff --git a/grafana/README.md b/grafana/README.md index 9ad05268..9696704b 100644 --- a/grafana/README.md +++ b/grafana/README.md @@ -1,15 +1,12 @@ - # Grafana

- Grafana logo + Grafana logo

Grafana is a powerful and versatile open-source platform designed to streamline the monitoring and observability of your metrics, regardless of their storage location. With Grafana, you can effortlessly query, visualize, set up alerts, and gain deep insights into your data, fostering a data-driven culture within your team. -

- Grafana dashboard inside thinger.io -

+![Grafana dashboard inside thinger.io](assets/dashboard.png) ## Key Features @@ -39,9 +36,7 @@ With Grafana, you can effortlessly mix different data sources within the same gr ## About thinger.io and Grafana Integration -

- Diagram showing thinger.io and Grafana integration -

+![Diagram showing thinger.io and Grafana integration](assets/integration.png) The integration of thinger.io with Grafana introduces an immensely valuable toolset for thinger.io users, enabling them to elevate their dashboards to a professional level, conduct intricate analytics in a scalable manner, and embark on collaborative visualization projects with fellow developers on their team. This seamless integration leverages the strengths of each component to create a powerful infrastructure, where thinger.io serves as a centralized hub for device administration and management. @@ -58,7 +53,3 @@ Unsure if Grafana is for you? Watch Grafana in action on [play.grafana.org](http ## Official Documentation The Grafana documentation is available at [grafana.com/docs](https://grafana.com/docs/grafana/latest/getting-started/). - -## License - -Grafana is distributed under the [Apache 2.0 License](https://github.com/grafana/grafana/blob/master/LICENSE). diff --git a/grafana/assets/dashboard.png b/grafana/assets/dashboard.png new file mode 100644 index 00000000..14378246 Binary files /dev/null and b/grafana/assets/dashboard.png differ diff --git a/grafana/assets/data_configuration_flux.png b/grafana/assets/data_configuration_flux.png new file mode 100644 index 00000000..eb6f9454 Binary files /dev/null and b/grafana/assets/data_configuration_flux.png differ diff --git a/grafana/assets/data_configuration_influxql.png b/grafana/assets/data_configuration_influxql.png new file mode 100644 index 00000000..a03d7851 Binary files /dev/null and b/grafana/assets/data_configuration_influxql.png differ diff --git a/grafana/assets/grafana-icon.svg b/grafana/assets/grafana-icon.svg new file mode 100644 index 00000000..6b468ca2 --- /dev/null +++ b/grafana/assets/grafana-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/grafana/assets/grafana-logo.svg b/grafana/assets/grafana-logo.svg new file mode 100644 index 00000000..02e25eec --- /dev/null +++ b/grafana/assets/grafana-logo.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/grafana/assets/influxdb-datasource.png b/grafana/assets/influxdb-datasource.png new file mode 100644 index 00000000..5a309a00 Binary files /dev/null and b/grafana/assets/influxdb-datasource.png differ diff --git a/grafana/assets/integration.png b/grafana/assets/integration.png new file mode 100644 index 00000000..e16e90e4 Binary files /dev/null and b/grafana/assets/integration.png differ diff --git a/grafana/assets/login.png b/grafana/assets/login.png new file mode 100644 index 00000000..0644ed83 Binary files /dev/null and b/grafana/assets/login.png differ diff --git a/grafana/assets/panel_configuration.png b/grafana/assets/panel_configuration.png new file mode 100644 index 00000000..e6693c9c Binary files /dev/null and b/grafana/assets/panel_configuration.png differ diff --git a/grafana/assets/permissions.png b/grafana/assets/permissions.png new file mode 100644 index 00000000..0533dff0 Binary files /dev/null and b/grafana/assets/permissions.png differ diff --git a/grafana/docs/changelog.md b/grafana/docs/changelog.md deleted file mode 100644 index 0e3cf550..00000000 --- a/grafana/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "grafana/CHANGELOG.md" diff --git a/grafana/docs/index.md b/grafana/docs/index.md deleted file mode 100644 index 301212f2..00000000 --- a/grafana/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "grafana/README.md" diff --git a/grafana/docs/plugin_file.md b/grafana/docs/plugin_file.md deleted file mode 100644 index 5f3741fb..00000000 --- a/grafana/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "grafana/plugin.json" -```` diff --git a/grafana/mkdocs.yml b/grafana/mkdocs.yml deleted file mode 100644 index 744258bb..00000000 --- a/grafana/mkdocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -site_name: plugins/grafana - -nav: - - "index.md" - - Get Started: "get_started.md" - - Working with Grafana: "working_with_grafana.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/grafana/docs/get_started.md b/grafana/pages/get_started.md similarity index 62% rename from grafana/docs/get_started.md rename to grafana/pages/get_started.md index f1a08024..783b0a01 100644 --- a/grafana/docs/get_started.md +++ b/grafana/pages/get_started.md @@ -6,26 +6,20 @@ First Login ## First Login -!!! info - - Perform initial login using your thinger.io account username and password 'admin' +> [!NOTE] +> Perform initial login using your thinger.io account username and password 'admin' Upon installing the plugin, perform the initial login using your thinger.io account username and the password `admin`. After the first login, the system will prompt you to change the password to a custom one. Once done, you'll gain access to the Grafana workspace, offering a variety of options. -

- Grafana login prompt -

+![Grafana login prompt](../assets/login.png) ## Adding a Datasource The connection between Thinger.io data and the Grafana plugin is established by adding the buckets database as a `new data source` for Grafana. By default, the configuration for the database is set up with names InfluxDB2-Flux and InfluxDB2-InfluxQL, requiring no further action. -!!! note - - For better performance, it is recommended to use the InfluxQL backend unless you require specific features available only with Flux. +> [!NOTE] +> For better performance, it is recommended to use the InfluxQL backend unless you require specific features available only with Flux. -

- Grafana backends configuration -

+![Grafana backends configuration](../assets/influxdb-datasource.png) However, if needed, you can configure additional data sources. For more information, check [this link](https://grafana.com/docs/grafana/latest/datasources/). diff --git a/grafana/docs/working_with_grafana.md b/grafana/pages/working_with_grafana.md similarity index 100% rename from grafana/docs/working_with_grafana.md rename to grafana/pages/working_with_grafana.md diff --git a/grafana/plugin.json b/grafana/plugin.json index f9057e3b..07567bb0 100644 --- a/grafana/plugin.json +++ b/grafana/plugin.json @@ -2,8 +2,11 @@ "name" : "grafana", "version" : "11.3.0-1", "description" : "Plugin for running Grafana inside Thinger.io", - "author" : "Alvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", + "deprecated": { + "message": "This plugin requires InfluxDB as backend which is no longer available in new instances" + }, "repository" : { "type" : "git", "url" : "https://github.com/thinger-io/plugins.git", @@ -12,8 +15,9 @@ "metadata" : { "name" : "Grafana", "description" : "Grafana integration for Thinger.io", - "image" : "docs/assets/grafana-logo.svg", - "icon" : "https://raw.githubusercontent.com/thinger-io/plugins/main/grafana/docs/assets/grafana-icon.svg" + "category" : "monitoring", + "image" : "assets/grafana-logo.svg", + "icon" : "assets/grafana-icon.svg" }, "tokens" : { }, diff --git a/http-device/LICENSE b/http-device/LICENSE deleted file mode 100644 index fc2c8e0a..00000000 --- a/http-device/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-Current Thinger.io (INTERNET OF THINGER S.L.) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/http-device/README.md b/http-device/README.md index 78287d4d..d426e64e 100644 --- a/http-device/README.md +++ b/http-device/README.md @@ -1,7 +1,7 @@ # HTTP Devices

- Thinger.io web console with VS Code plugin and OTA upload + HTTP Device logo

This plugin is specially useful for fleets of generic devices that can make HTTP requests. diff --git a/http-device/assets/http-device-icon.png b/http-device/assets/http-device-icon.png new file mode 100644 index 00000000..25a49a0f Binary files /dev/null and b/http-device/assets/http-device-icon.png differ diff --git a/http-device/assets/http-device-logo.png b/http-device/assets/http-device-logo.png new file mode 100644 index 00000000..25a49a0f Binary files /dev/null and b/http-device/assets/http-device-logo.png differ diff --git a/http-device/docs/changelog.md b/http-device/docs/changelog.md deleted file mode 100644 index 1a345f7f..00000000 --- a/http-device/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "http-device/CHANGELOG.md" diff --git a/http-device/docs/index.md b/http-device/docs/index.md deleted file mode 100644 index 49ef5ed2..00000000 --- a/http-device/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "http-device/README.md" diff --git a/http-device/docs/plugin_file.md b/http-device/docs/plugin_file.md deleted file mode 100644 index 7a8279c9..00000000 --- a/http-device/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "http-device/plugin.json" -```` diff --git a/http-device/mkdocs.yml b/http-device/mkdocs.yml deleted file mode 100644 index 83da865c..00000000 --- a/http-device/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/http-device - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/http-device/plugin.json b/http-device/plugin.json index 0aa648ea..a830e632 100644 --- a/http-device/plugin.json +++ b/http-device/plugin.json @@ -2,7 +2,7 @@ "name" : "http-device", "version" : "1.1.1", "description" : "Plugin for handling generic HTTP devices.", - "author" : "Alvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "HTTP Device", "description" : "HTTP Devices Integration", - "image" : "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/http-device/img/http-device-logo.png", - "icon" : "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/http-device/img/http-device-icon.png" + "category" : "connectivity", + "image" : "assets/http-device-logo.png", + "icon" : "assets/http-device-icon.png" }, "tokens" : { "http_device_plugin" : { diff --git a/influxdb2/README.md b/influxdb2/README.md index 2c18d985..8f6473a4 100644 --- a/influxdb2/README.md +++ b/influxdb2/README.md @@ -2,16 +2,14 @@ # InfluxDB2

- Influxdb logo + InfluxDB logo

InfluxDB is an open source time series platform. This includes APIs for storing and querying data, processing it in the background for ETL or monitoring and alerting purposes, user dashboards, and visualizing and exploring the data and more. InfluxDB is the default database backend for Thinger.io. This plugin allows direct access to the underlying installation and the user buckets for custom queries, dashboards, or alerts. -

- Influxdb login page integrated in Thinger.io Platform -

+![Influxdb login page integrated in Thinger.io Platform](assets/login_page.jpeg) ## Requirements @@ -29,7 +27,3 @@ This plugin initializes the following resources After the plugin is installed, click on the InfluxDB2 plugin and access the interface with you regular Thinger.io username and password. Note: If the username and password does not work, please, change your Thinger.io password account. - -## License - -InfluxDB is distributed under the [MIT License](https://github.com/influxdata/influxdb/blob/master/LICENSE). diff --git a/influxdb2/assets/influxdb.svg b/influxdb2/assets/influxdb.svg new file mode 100644 index 00000000..c38fd251 --- /dev/null +++ b/influxdb2/assets/influxdb.svg @@ -0,0 +1,23 @@ + + + + + + + diff --git a/influxdb2/assets/login_page.jpeg b/influxdb2/assets/login_page.jpeg new file mode 100644 index 00000000..44912f51 Binary files /dev/null and b/influxdb2/assets/login_page.jpeg differ diff --git a/influxdb2/docs/changelog.md b/influxdb2/docs/changelog.md deleted file mode 100644 index 4b7ec581..00000000 --- a/influxdb2/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "influxdb2/CHANGELOG.md" diff --git a/influxdb2/docs/index.md b/influxdb2/docs/index.md deleted file mode 100644 index 5cb68e38..00000000 --- a/influxdb2/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "influxdb2/README.md" diff --git a/influxdb2/docs/plugin_file.md b/influxdb2/docs/plugin_file.md deleted file mode 100644 index 0a6cf48c..00000000 --- a/influxdb2/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "influxdb2/plugin.json" -```` diff --git a/influxdb2/mkdocs.yml b/influxdb2/mkdocs.yml deleted file mode 100644 index 5cec0ee8..00000000 --- a/influxdb2/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/influxdb2 - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/influxdb2/plugin.json b/influxdb2/plugin.json index 434cc5b9..95d1eab8 100644 --- a/influxdb2/plugin.json +++ b/influxdb2/plugin.json @@ -2,8 +2,11 @@ "name" : "influxdb2", "version" : "1.0.0", "description" : "Plugin for InfluxBD2 access", - "author" : "Alvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", + "deprecated": { + "message": "InfluxDB is no longer the default time series database. This plugin may not work with your instance" + }, "repository" : { "type" : "git", "url" : "https://github.com/thinger-io/plugins.git", @@ -12,8 +15,9 @@ "metadata" : { "name" : "InfluxDB2", "description" : "InfluxDB2 Plugin", - "image" : "docs/assets/influxdb.svg", - "icon" : "docs/assets/influxdb.svg" + "category" : "monitoring", + "image" : "assets/influxdb.svg", + "icon" : "assets/influxdb.svg" }, "resources" : { "proxies" : [ diff --git a/jupyter-minimal/README.md b/jupyter-minimal/README.md index 1fd20287..e1ecaaa2 100644 --- a/jupyter-minimal/README.md +++ b/jupyter-minimal/README.md @@ -2,7 +2,7 @@ # Jupyter Minimal

- Jupyter logo + Jupyter logo

Thinger.io plugin for running Jupyter Minimal version on the Cloud, integrating with Thinger.io file storages. @@ -25,6 +25,4 @@ More details in what this plugins includes can be found [here](https://jupyter-d ## Screenshots -

- Mockup of Jupyter Notebook integrated within Thinger.io -

+![Mockup of Jupyter Notebook integrated within Thinger.io](assets/jupyter-thinger.png) diff --git a/jupyter-minimal/assets/jupyter-thinger.png b/jupyter-minimal/assets/jupyter-thinger.png new file mode 100644 index 00000000..ec4b73f0 Binary files /dev/null and b/jupyter-minimal/assets/jupyter-thinger.png differ diff --git a/jupyter-minimal/assets/jupyter_icon.svg b/jupyter-minimal/assets/jupyter_icon.svg new file mode 100644 index 00000000..2cd40ad2 --- /dev/null +++ b/jupyter-minimal/assets/jupyter_icon.svg @@ -0,0 +1,182 @@ + + + + diff --git a/jupyter-minimal/assets/jupyter_image.svg b/jupyter-minimal/assets/jupyter_image.svg new file mode 100644 index 00000000..94cb1e5f --- /dev/null +++ b/jupyter-minimal/assets/jupyter_image.svg @@ -0,0 +1,199 @@ + + + + diff --git a/jupyter-minimal/docs/changelog.md b/jupyter-minimal/docs/changelog.md deleted file mode 100644 index c25929b1..00000000 --- a/jupyter-minimal/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "jupyter-minimal/CHANGELOG.md" diff --git a/jupyter-minimal/docs/index.md b/jupyter-minimal/docs/index.md deleted file mode 100644 index 83f84cc1..00000000 --- a/jupyter-minimal/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "jupyter-minimal/README.md" diff --git a/jupyter-minimal/docs/plugin_file.md b/jupyter-minimal/docs/plugin_file.md deleted file mode 100644 index 26cccb1e..00000000 --- a/jupyter-minimal/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "jupyter-minimal/plugin.json" -```` diff --git a/jupyter-minimal/mkdocs.yml b/jupyter-minimal/mkdocs.yml deleted file mode 100644 index 52ff2f2f..00000000 --- a/jupyter-minimal/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/jupyter-minimal - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/jupyter-minimal/plugin.json b/jupyter-minimal/plugin.json index af3a13b7..5a4dbbe7 100644 --- a/jupyter-minimal/plugin.json +++ b/jupyter-minimal/plugin.json @@ -2,7 +2,7 @@ "name" : "jupyter-minimal", "version" : "3.10.10-1", "description" : "Jupyter Minimal Plugin for Thinger.io", - "author" : "Álvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "Jupyter Minimal", "description" : "Jupyter Notebook Minimal", - "image" : "docs/assets/jupyter_image.svg", - "icon" : "docs/assets/jupyter_icon.svg" + "category" : "development", + "image" : "assets/jupyter_image.svg", + "icon" : "assets/jupyter_icon.svg" }, "task": { "type": "docker", diff --git a/jupyter-r/README.md b/jupyter-r/README.md index fecf33c2..891d7611 100644 --- a/jupyter-r/README.md +++ b/jupyter-r/README.md @@ -2,7 +2,7 @@ # Jupyter R

- Jupyter logo + Jupyter logo

Thinger.io plugin for running Jupyter R version on the Cloud, integrating with Thinger.io file storages. @@ -26,6 +26,4 @@ More details in what this plugins includes can be found [here](https://jupyter-d ## Screenshots -

- Mockup of Jupyter Notebook integrated within Thinger.io -

+![Mockup of Jupyter Notebook integrated within Thinger.io](assets/jupyter-thinger.png) diff --git a/jupyter-r/assets/jupyter-thinger.png b/jupyter-r/assets/jupyter-thinger.png new file mode 100644 index 00000000..ec4b73f0 Binary files /dev/null and b/jupyter-r/assets/jupyter-thinger.png differ diff --git a/jupyter-r/assets/jupyter_icon.svg b/jupyter-r/assets/jupyter_icon.svg new file mode 100644 index 00000000..2cd40ad2 --- /dev/null +++ b/jupyter-r/assets/jupyter_icon.svg @@ -0,0 +1,182 @@ + + + + diff --git a/jupyter-r/assets/jupyter_image.svg b/jupyter-r/assets/jupyter_image.svg new file mode 100644 index 00000000..94cb1e5f --- /dev/null +++ b/jupyter-r/assets/jupyter_image.svg @@ -0,0 +1,199 @@ + + + + diff --git a/jupyter-r/docs/changelog.md b/jupyter-r/docs/changelog.md deleted file mode 100644 index 48cd75ed..00000000 --- a/jupyter-r/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "jupyter-r/CHANGELOG.md" diff --git a/jupyter-r/docs/index.md b/jupyter-r/docs/index.md deleted file mode 100644 index f9fc4f11..00000000 --- a/jupyter-r/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "jupyter-r/README.md" diff --git a/jupyter-r/docs/plugin_file.md b/jupyter-r/docs/plugin_file.md deleted file mode 100644 index 108e0621..00000000 --- a/jupyter-r/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "jupyter-r/plugin.json" -```` diff --git a/jupyter-r/mkdocs.yml b/jupyter-r/mkdocs.yml deleted file mode 100644 index 0c5df81c..00000000 --- a/jupyter-r/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/jupyter-r - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/jupyter-r/plugin.json b/jupyter-r/plugin.json index 48411463..5db38cbf 100644 --- a/jupyter-r/plugin.json +++ b/jupyter-r/plugin.json @@ -2,7 +2,7 @@ "name" : "jupyter-r", "version" : "4.2.3-3", "description" : "Jupyter R Plugin for Thinger.io", - "author" : "Alvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "Jupyter R", "description" : "Jupyter Plugin for Thinger.io", - "image" : "docs/assets/jupyter_image.svg", - "icon" : "docs/assets/jupyter_icon.svg" + "category" : "development", + "image" : "assets/jupyter_image.svg", + "icon" : "assets/jupyter_icon.svg" }, "task": { "type": "docker", diff --git a/jupyter-tensorflow/README.md b/jupyter-tensorflow/README.md index ac1a0662..7614e2c3 100644 --- a/jupyter-tensorflow/README.md +++ b/jupyter-tensorflow/README.md @@ -2,7 +2,7 @@ # Jupyter TensorFlow

- Jupyter logo + Jupyter logo

Thinger.io plugin for running Jupyter TensorFlow version on the Cloud, integrating with Thinger.io file storages. @@ -27,6 +27,4 @@ More details in what this plugins includes can be found [here](https://jupyter-d ## Screenshots -

- Mockup of Jupyter Notebook integrated within Thinger.io -

+![Mockup of Jupyter Notebook integrated within Thinger.io](assets/jupyter-thinger.png) diff --git a/jupyter-tensorflow/assets/jupyter-thinger.png b/jupyter-tensorflow/assets/jupyter-thinger.png new file mode 100644 index 00000000..ec4b73f0 Binary files /dev/null and b/jupyter-tensorflow/assets/jupyter-thinger.png differ diff --git a/jupyter-tensorflow/assets/jupyter_icon.svg b/jupyter-tensorflow/assets/jupyter_icon.svg new file mode 100644 index 00000000..2cd40ad2 --- /dev/null +++ b/jupyter-tensorflow/assets/jupyter_icon.svg @@ -0,0 +1,182 @@ + + + + diff --git a/jupyter-tensorflow/assets/jupyter_image.svg b/jupyter-tensorflow/assets/jupyter_image.svg new file mode 100644 index 00000000..94cb1e5f --- /dev/null +++ b/jupyter-tensorflow/assets/jupyter_image.svg @@ -0,0 +1,199 @@ + + + + diff --git a/jupyter-tensorflow/docs/changelog.md b/jupyter-tensorflow/docs/changelog.md deleted file mode 100644 index d793058d..00000000 --- a/jupyter-tensorflow/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "jupyter-tensorflow/CHANGELOG.md" diff --git a/jupyter-tensorflow/docs/index.md b/jupyter-tensorflow/docs/index.md deleted file mode 100644 index a0e486c3..00000000 --- a/jupyter-tensorflow/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "jupyter-tensorflow/README.md" diff --git a/jupyter-tensorflow/docs/plugin_file.md b/jupyter-tensorflow/docs/plugin_file.md deleted file mode 100644 index 57e4d925..00000000 --- a/jupyter-tensorflow/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "jupyter-tensorflow/plugin.json" -```` diff --git a/jupyter-tensorflow/mkdocs.yml b/jupyter-tensorflow/mkdocs.yml deleted file mode 100644 index f3d86fe7..00000000 --- a/jupyter-tensorflow/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/jupyter-tensorflow - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/jupyter-tensorflow/plugin.json b/jupyter-tensorflow/plugin.json index 82798723..c0eed5f5 100644 --- a/jupyter-tensorflow/plugin.json +++ b/jupyter-tensorflow/plugin.json @@ -2,7 +2,7 @@ "name" : "jupyter-tensorflow", "version" : "2.11.0-5", "description" : "Jupyter Tensorflow Plugin for Thinger.io", - "author" : "Alvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "Jupyter TensorFlow", "description" : "Jupyter Plugin for Thinger.io", - "image" : "docs/assets/jupyter_image.svg", - "icon" : "docs/assets/jupyter_icon.svg" + "category" : "development", + "image" : "assets/jupyter_image.svg", + "icon" : "assets/jupyter_icon.svg" }, "task": { "type": "docker", diff --git a/lorawan-product-template/LICENSE.md b/lorawan-product-template/LICENSE.md deleted file mode 100644 index f1bacd61..00000000 --- a/lorawan-product-template/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2025 Thinger.io - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lorawan-product-template/README.md b/lorawan-product-template/README.md index 09e0b346..77eb1ad6 100644 --- a/lorawan-product-template/README.md +++ b/lorawan-product-template/README.md @@ -1,7 +1,7 @@ # LoRaWAN Thinger.io Product Template

- LoRaWAN Thinger.io Product + LoRaWAN logo

The LoRaWAN Product Template is a foundational framework designed to accelerate the development of LoRaWAN-based IoT products within the Thinger.io ecosystem. It provides a standardized approach to configuring LoRaWAN devices, ensuring seamless integration with any of the supported LoRaWAN Network Servers (LNS) while maintaining complete LNS-agnostic product logic. diff --git a/lorawan-product-template/docs/changelog.md b/lorawan-product-template/docs/changelog.md deleted file mode 100644 index d3c5b0bd..00000000 --- a/lorawan-product-template/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "lorawan-product-template/CHANGELOG.md" diff --git a/lorawan-product-template/docs/index.md b/lorawan-product-template/docs/index.md deleted file mode 100644 index 6db5b08f..00000000 --- a/lorawan-product-template/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "lorawan-product-template/README.md" diff --git a/lorawan-product-template/docs/plugin_file.md b/lorawan-product-template/docs/plugin_file.md deleted file mode 100644 index 9260e69a..00000000 --- a/lorawan-product-template/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "lorawan-product-template/plugin.json" -```` diff --git a/lorawan-product-template/mkdocs.yml b/lorawan-product-template/mkdocs.yml deleted file mode 100644 index 6f569d04..00000000 --- a/lorawan-product-template/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/lorawan-product-template - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/lorawan-product-template/plugin.json b/lorawan-product-template/plugin.json index 096233f5..f6317cca 100644 --- a/lorawan-product-template/plugin.json +++ b/lorawan-product-template/plugin.json @@ -12,7 +12,8 @@ "metadata": { "name": "LoRaWAN Device Template", "description": "Product template for LoRaWAN-driven devices. Preconfigured to work with any LNS Thinger plugin.", - "image": "https://www.weble.ch/wp-content/uploads/2019/12/lw.png" + "category": "templates", + "image": "assets/lorawan.png" }, "resources": { "products": [ diff --git a/loriot/CHANGELOG.md b/loriot/CHANGELOG.md index a99ab4ce..b328fdff 100644 --- a/loriot/CHANGELOG.md +++ b/loriot/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [2.1.0] - 2025-11-18 + +### Add + +- Changed visual style of plugin sections in order to improve readability +- Significantly improved error handling and logging for uplink parsing issues + ## [2.0.0] - 2025-08-11 ### Fix diff --git a/loriot/LICENSE b/loriot/LICENSE deleted file mode 100644 index 0631ba07..00000000 --- a/loriot/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025-Current Thinger.io (INTERNET OF THINGER S.L.) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/loriot/README.md b/loriot/README.md index d4759443..7a7b599c 100644 --- a/loriot/README.md +++ b/loriot/README.md @@ -2,7 +2,7 @@ # LORIOT

- LORIOT logo + LORIOT logo

LORIOT is a LoRaWAN Network solution that simplifies the deployment of large IoT applications over a collaborative Internet of Things network that spans many countries around the world. @@ -17,9 +17,7 @@ This plugin requires that devices in LORIOT applications to be of homogeneous ty The first step is to install the plugin in your Thinger.io account ([How to install a plugin](https://marketplace.thinger.io/plugins/managing/#install-and-deploy-an-existent-plugin)). -

- LORIOT in Thinger.io Marketplace -

+![LORIOT in Thinger.io Marketplace](assets/loriot_plugin_marketplace.png) ### Plugin Configuration @@ -29,15 +27,11 @@ After installing this plugin, access the plugin configuration page and on the Ap - **Device Id Prefix**: A prefix to be used for the device identifiers. The plugin will automatically append the device EUI to this prefix to create the device identifier. This prefix is necessary for the device autoprovision features. - **LORIOT Access Token**: The LORIOT Application Access Token. Please refer to the [LORIOT Access Token documentation](https://docs.loriot.io/space/NMS/6031583/Access+Tokens) to learn how to obtain this token. -

- Add application modal in LORIOT Thinger.io Plugin -

+![Add application modal in LORIOT Thinger.io Plugin](assets/add_application.png) Don't close the plugin configuration page yet, you will need to configure the LORIOT Webhook to send the device data to Thinger.io. -

- LORIOT Thinger.io Plugin Settings -

+![LORIOT Thinger.io Plugin Settings](assets/loriot_plugin.png) ### LORIOT Webhook Configuration @@ -48,9 +42,7 @@ To integrate LORIOT with Thinger.io, you need to configure a Webhook in LORIOT t 2. In the application configuration, go to `Output` and click on `Add new output`. 3. Select `HTTP Push` as the output type and fill out the configuration with the seetings found in the plugin configuration page: -

- LORIOT Webhook Settings for Thinger.io integration -

+![LORIOT Webhook Settings for Thinger.io integration](assets/loriot_webhook_settings.png) If you [check the logs](https://marketplace.thinger.io/plugins/managing/#analyzing-the-logs) in the LORIOT Thinger.io Plugin, you should see the messages being received from LORIOT. However, the devices are not yet provisioned in Thinger.io, so the messages are being discarded. @@ -66,15 +58,11 @@ Device templates define the device data structure and the processing functions f In Thinger.io device templates are also plugins, checkout the Plugins Marketplace to find the device template that fits your device. In case you cannot find a device template for your device, you can create your own device template, please refer to section [Create Your Own Device Template](#create-your-own-device-template). -

- AM103 Marketplace plugin -

+![AM103 Marketplace plugin](assets/am103_plugin.png) Once installed, in Products you'll see your new installed device templates. By default the device templates have a predefined prefix, make sure to change it to match the prefix you used when creating the LORIOT application in the plugin. -

- AM103 Product profile -

+![AM103 Product profile](assets/am103_product_profile.png) #### Create your own Device Template @@ -83,19 +71,3 @@ You can create your own device template through the Products feature, refer to t ## LORIOT Documentation Please refer to the [LORIOT documentation](https://docs.loriot.io/) for more information about the LORIOT platform. - -## License - - - - - -The plugin is licensed under the [MIT License](http://opensource.org/licenses/MIT): - -Copyright © [Thinger.io](http://thinger.io) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/loriot/assets/add_application.png b/loriot/assets/add_application.png new file mode 100644 index 00000000..342da4af Binary files /dev/null and b/loriot/assets/add_application.png differ diff --git a/loriot/assets/am103_plugin.png b/loriot/assets/am103_plugin.png new file mode 100644 index 00000000..1bb98c0f Binary files /dev/null and b/loriot/assets/am103_plugin.png differ diff --git a/loriot/assets/am103_product_profile.png b/loriot/assets/am103_product_profile.png new file mode 100644 index 00000000..a4b92d59 Binary files /dev/null and b/loriot/assets/am103_product_profile.png differ diff --git a/loriot/assets/loriot-icon.png b/loriot/assets/loriot-icon.png new file mode 100644 index 00000000..a43387db Binary files /dev/null and b/loriot/assets/loriot-icon.png differ diff --git a/loriot/assets/loriot-logo-square.png b/loriot/assets/loriot-logo-square.png new file mode 100644 index 00000000..fa85f3b8 Binary files /dev/null and b/loriot/assets/loriot-logo-square.png differ diff --git a/loriot/assets/loriot-logo.png b/loriot/assets/loriot-logo.png new file mode 100644 index 00000000..f8ca67e7 Binary files /dev/null and b/loriot/assets/loriot-logo.png differ diff --git a/loriot/assets/loriot_plugin.png b/loriot/assets/loriot_plugin.png new file mode 100644 index 00000000..743a1a36 Binary files /dev/null and b/loriot/assets/loriot_plugin.png differ diff --git a/loriot/assets/loriot_plugin_marketplace.png b/loriot/assets/loriot_plugin_marketplace.png new file mode 100644 index 00000000..06ef84db Binary files /dev/null and b/loriot/assets/loriot_plugin_marketplace.png differ diff --git a/loriot/assets/loriot_webhook_settings.png b/loriot/assets/loriot_webhook_settings.png new file mode 100644 index 00000000..6d56cfa6 Binary files /dev/null and b/loriot/assets/loriot_webhook_settings.png differ diff --git a/loriot/docs/changelog.md b/loriot/docs/changelog.md deleted file mode 100644 index bcdadd64..00000000 --- a/loriot/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "loriot/CHANGELOG.md" diff --git a/loriot/docs/index.md b/loriot/docs/index.md deleted file mode 100644 index 5f3cd88d..00000000 --- a/loriot/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "loriot/README.md" diff --git a/loriot/docs/plugin_file.md b/loriot/docs/plugin_file.md deleted file mode 100644 index b9b70585..00000000 --- a/loriot/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "loriot/plugin.json" -```` diff --git a/loriot/mkdocs.yml b/loriot/mkdocs.yml deleted file mode 100644 index ed3b7165..00000000 --- a/loriot/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/loriot - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/loriot/plugin.json b/loriot/plugin.json index 2f0d6fab..2438b0d5 100644 --- a/loriot/plugin.json +++ b/loriot/plugin.json @@ -1,8 +1,8 @@ { "name" : "loriot", - "version" : "2.0.0", + "version" : "2.1.0", "description" : "Plugin for integrating Thinger.io with LORIOT", - "author" : "Thinger.io", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "LORIOT", "description" : "LORIOT Integration", - "image" : "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/loriot/img/loriot-logo-square.png", - "icon" : "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/loriot/img/loriot-icon.png" + "category" : "connectivity", + "image" : "assets/loriot-logo-square.png", + "icon" : "assets/loriot-icon.png" }, "tokens" : { "loriot_plugin" : { diff --git a/loriot/task/backend/package-lock.json b/loriot/task/backend/package-lock.json index 62060c02..3ff10f0f 100644 --- a/loriot/task/backend/package-lock.json +++ b/loriot/task/backend/package-lock.json @@ -1,6 +1,6 @@ { "name": "loriot-backend", - "version": "1.2.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { @@ -10,7 +10,8 @@ "license": "MIT", "dependencies": { "@thinger-io/thinger-node": "0.1.0-alpha.4", - "express": "^4.21.1" + "express": "^4.21.1", + "socket.io": "^4.8.1" }, "devDependencies": { "@eslint/js": "^9.15.0", @@ -24,9 +25,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", - "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -41,9 +42,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", - "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -58,9 +59,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", - "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -75,9 +76,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", - "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -92,9 +93,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", - "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -109,9 +110,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", - "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -126,9 +127,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", - "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -143,9 +144,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", - "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -160,9 +161,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", - "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -177,9 +178,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", - "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -194,9 +195,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", - "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -211,9 +212,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", - "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -228,9 +229,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", - "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -245,9 +246,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", - "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -262,9 +263,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", - "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -279,9 +280,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", - "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -296,9 +297,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", - "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -312,10 +313,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", - "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -330,9 +348,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", - "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], @@ -347,9 +365,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", - "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -363,10 +381,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", - "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -381,9 +416,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", - "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -398,9 +433,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", - "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -415,9 +450,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", - "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -432,9 +467,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -474,13 +509,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.5", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -488,10 +523,23 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", - "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -502,9 +550,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -539,19 +587,22 @@ } }, "node_modules/@eslint/js": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", - "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -559,12 +610,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", - "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -624,9 +676,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -675,6 +727,12 @@ "node": ">= 8" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@thinger-io/thinger-node": { "version": "0.1.0-alpha.4", "resolved": "https://registry.npmjs.org/@thinger-io/thinger-node/-/thinger-node-0.1.0-alpha.4.tgz", @@ -709,6 +767,15 @@ "@types/node": "*" } }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -948,9 +1015,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1029,9 +1096,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -1110,6 +1177,15 @@ "dev": true, "license": "MIT" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/body-parser": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", @@ -1150,9 +1226,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -1313,6 +1389,19 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "license": "MIT" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1410,6 +1499,61 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -1440,6 +1584,21 @@ "node": ">= 0.4" } }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es6-promise": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", @@ -1447,9 +1606,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.23.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", - "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -1460,30 +1619,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.1", - "@esbuild/android-arm": "0.23.1", - "@esbuild/android-arm64": "0.23.1", - "@esbuild/android-x64": "0.23.1", - "@esbuild/darwin-arm64": "0.23.1", - "@esbuild/darwin-x64": "0.23.1", - "@esbuild/freebsd-arm64": "0.23.1", - "@esbuild/freebsd-x64": "0.23.1", - "@esbuild/linux-arm": "0.23.1", - "@esbuild/linux-arm64": "0.23.1", - "@esbuild/linux-ia32": "0.23.1", - "@esbuild/linux-loong64": "0.23.1", - "@esbuild/linux-mips64el": "0.23.1", - "@esbuild/linux-ppc64": "0.23.1", - "@esbuild/linux-riscv64": "0.23.1", - "@esbuild/linux-s390x": "0.23.1", - "@esbuild/linux-x64": "0.23.1", - "@esbuild/netbsd-x64": "0.23.1", - "@esbuild/openbsd-arm64": "0.23.1", - "@esbuild/openbsd-x64": "0.23.1", - "@esbuild/sunos-x64": "0.23.1", - "@esbuild/win32-arm64": "0.23.1", - "@esbuild/win32-ia32": "0.23.1", - "@esbuild/win32-x64": "0.23.1" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, "node_modules/escape-html": { @@ -1506,32 +1667,32 @@ } }, "node_modules/eslint": { - "version": "9.17.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", - "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.17.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -1566,9 +1727,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -1583,9 +1744,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1596,15 +1757,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1888,14 +2049,16 @@ "license": "ISC" }, "node_modules/form-data": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", - "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz", + "integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" }, "engines": { @@ -2061,6 +2224,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2112,9 +2290,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2194,9 +2372,9 @@ "license": "ISC" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -2402,6 +2580,15 @@ "node": ">= 0.6" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", @@ -2900,6 +3087,98 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -2971,13 +3250,13 @@ } }, "node_modules/tsx": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", - "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", + "version": "4.20.6", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", + "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "~0.23.0", + "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "bin": { @@ -3054,9 +3333,9 @@ } }, "node_modules/undici": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.2.0.tgz", - "integrity": "sha512-klt+0S55GBViA9nsq48/NSCo4YX5mjydjypxD7UmHh/brMu8h/Mhd/F7qAeoH2NOO8SDTk6kjnTFc4WpzmfYpQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz", + "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -3141,6 +3420,27 @@ "node": ">=0.10.0" } }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/loriot/task/backend/package.json b/loriot/task/backend/package.json index daeab27e..8a6d64b5 100644 --- a/loriot/task/backend/package.json +++ b/loriot/task/backend/package.json @@ -15,7 +15,9 @@ }, "keywords": [ "loriot", - "thinger" + "thinger", + "LNS", + "LoRaWAN" ], "author": "Thinger.io", "license": "MIT", @@ -35,6 +37,7 @@ }, "dependencies": { "@thinger-io/thinger-node": "0.1.0-alpha.4", - "express": "^4.21.1" + "express": "^4.21.1", + "socket.io": "^4.8.1" } } diff --git a/loriot/task/backend/src/lib/user-events.config.ts b/loriot/task/backend/src/lib/user-events.config.ts new file mode 100644 index 00000000..f1048d09 --- /dev/null +++ b/loriot/task/backend/src/lib/user-events.config.ts @@ -0,0 +1,73 @@ +import { EventCategory, EventSeverity } from './user-events.types.js'; + +export interface EventCategoryConfig { + color: string; + label: string; +} + +export interface UserEventsConfig { + maxQueueSize: number; + defaultLimit: number; + categories: Record; + severities: Record; +} + +/** + * Default configuration + * Can be overridden when instantiating UserEvents class + */ +export const DEFAULT_USER_EVENTS_CONFIG: UserEventsConfig = { + // Maximum number of events to keep in memory + maxQueueSize: 50, + + // Default number of events returned by getRecent() + defaultLimit: 10, + + // Visual configuration for each category + categories: { + uplink: { + color: '#10b981', + label: 'Uplink' + }, + downlink: { + color: '#3b82f6', + label: 'Downlink' + }, + device: { + color: '#8b5cf6', + label: 'Device' + }, + config: { + color: '#6b7280', + label: 'Configuration' + }, + warning: { + color: '#f59e0b', + label: 'Warning' + }, + error: { + color: '#ef4444', + label: 'Error' + } + }, + + // Visual configuration for each severity level + severities: { + success: { + color: '#10b981', + label: 'Success' + }, + info: { + color: '#3b82f6', + label: 'Info' + }, + warning: { + color: '#f59e0b', + label: 'Warning' + }, + error: { + color: '#ef4444', + label: 'Error' + } + } +}; diff --git a/loriot/task/backend/src/lib/user-events.ts b/loriot/task/backend/src/lib/user-events.ts new file mode 100644 index 00000000..03d31f5a --- /dev/null +++ b/loriot/task/backend/src/lib/user-events.ts @@ -0,0 +1,189 @@ +import { randomUUID } from 'crypto'; +import { EventEmitter } from 'events'; +import { + UserEvent, + CreateEventInput, + EventFilters +} from './user-events.types.js'; +import { + DEFAULT_USER_EVENTS_CONFIG, + UserEventsConfig +} from './user-events.config.js'; + +export class UserEvents extends EventEmitter { + private queue: UserEvent[] = []; + private config: UserEventsConfig; + + /** + * Creates a new UserEvents instance + * @param config Optional custom configuration + */ + constructor(config?: Partial) { + super(); + this.config = { + ...DEFAULT_USER_EVENTS_CONFIG, + ...config + }; + } + + /** + * Adds a new event to the queue + * Automatically handles FIFO behavior when max size is reached + * + * @param input Event data + * @returns The created event with generated ID and timestamp + */ + push(input: CreateEventInput): UserEvent { + const event: UserEvent = { + id: randomUUID(), + timestamp: new Date().toISOString(), + ...input + }; + + // Add to the end of the queue + this.queue.push(event); + + // Enforce max queue size (FIFO: remove oldest) + if (this.queue.length > this.config.maxQueueSize) { + this.queue.shift(); // Remove first (oldest) element + } + + this.emit('new-event', event); + + return event; + } + + getRecent(filters?: EventFilters): UserEvent[] { + let filtered = [...this.queue]; + + // Apply filters if provided + if (filters) { + if (filters.category) { + filtered = filtered.filter(e => e.category === filters.category); + } + if (filters.severity) { + filtered = filtered.filter(e => e.severity === filters.severity); + } + if (filters.device) { + filtered = filtered.filter(e => e.device === filters.device); + } + if (filters.application) { + filtered = filtered.filter(e => e.application === filters.application); + } + } + + // Sort by timestamp (newest first) and limit + const limit = filters?.limit ?? this.config.defaultLimit; + return filtered + .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()) + .slice(0, limit); + } + + /** + * Gets a specific event by ID + * + * @param id Event UUID + * @returns The event or undefined if not found + */ + getById(id: string): UserEvent | undefined { + return this.queue.find(e => e.id === id); + } + + /** + * Gets all events for a specific device + * + * @param deviceEui Device EUI + * @param limit Optional limit + * @returns Array of events for the device + */ + getByDevice(deviceEui: string, limit?: number): UserEvent[] { + return this.getRecent({ device: deviceEui, limit }); + } + + /** + * Gets all events for a specific application + * + * @param applicationId Application ID + * @param limit Optional limit + * @returns Array of events for the application + */ + getByApplication(applicationId: string, limit?: number): UserEvent[] { + return this.getRecent({ application: applicationId, limit }); + } + + /** + * Gets events by category + * + * @param category Event category + * @param limit Optional limit + * @returns Array of events in the category + */ + getByCategory(category: EventFilters['category'], limit?: number): UserEvent[] { + return this.getRecent({ category, limit }); + } + + /** + * Gets events by severity + * + * @param severity Event severity + * @param limit Optional limit + * @returns Array of events with the severity + */ + getBySeverity(severity: EventFilters['severity'], limit?: number): UserEvent[] { + return this.getRecent({ severity, limit }); + } + + /** + * Gets the current size of the queue + * + * @returns Number of events in queue + */ + getSize(): number { + return this.queue.length; + } + + /** + * Clears all events from the queue + * Useful for testing or manual cleanup + */ + clear(): void { + this.queue = []; + this.emit('events-cleared'); + } + + /** + * Gets the current configuration + * Useful for frontend to know icons, colors, etc. + * + * @returns Current configuration + */ + getConfig(): UserEventsConfig { + return { ...this.config }; + } + + /** + * Gets statistics about current events + * Useful for monitoring and debugging + * + * @returns Object with event counts by category and severity + */ + getStats() { + const stats = { + total: this.queue.length, + byCategory: {} as Record, + bySeverity: {} as Record + }; + + this.queue.forEach(event => { + // Count by category + stats.byCategory[event.category] = + (stats.byCategory[event.category] || 0) + 1; + + // Count by severity + stats.bySeverity[event.severity] = + (stats.bySeverity[event.severity] || 0) + 1; + }); + + return stats; + } +} diff --git a/loriot/task/backend/src/lib/user-events.types.ts b/loriot/task/backend/src/lib/user-events.types.ts new file mode 100644 index 00000000..b21c9bc1 --- /dev/null +++ b/loriot/task/backend/src/lib/user-events.types.ts @@ -0,0 +1,63 @@ + +export type EventCategory = + | 'uplink' + | 'downlink' + | 'device' + | 'config' + | 'error' + | 'warning'; + +export type EventSeverity = + | 'success' + | 'info' + | 'warning' + | 'error'; + +/** + * Main event structure stored in the queue + */ +export interface UserEvent { + id: string; // Unique identifier (UUID) + timestamp: string; // ISO 8601 format + category: EventCategory; // Event type + severity: EventSeverity; // Importance level + title: string; // Short message for list view (max ~100 chars) + device?: string; // Device EUI if applicable + application?: string; // Application ID if applicable + details: Record; // Complete data for popup/details view + metadata?: EventMetadata; // Optional additional info +} + +/** + * Optional metadata for events + */ +export interface EventMetadata { + duration?: number; // Processing time in ms + size?: number; // Payload size in bytes + retries?: number; // Number of retry attempts + [key: string]: any; // Extensible for future use +} + +/** + * Configuration for creating a new event + */ +export interface CreateEventInput { + category: EventCategory; + severity: EventSeverity; + title: string; + device?: string; + application?: string; + details: Record; + metadata?: EventMetadata; +} + +/** + * Query parameters for filtering events + */ +export interface EventFilters { + limit?: number; + category?: EventCategory; + severity?: EventSeverity; + device?: string; + application?: string; +} diff --git a/loriot/task/backend/src/server.ts b/loriot/task/backend/src/server.ts index 113c2d14..53c09412 100644 --- a/loriot/task/backend/src/server.ts +++ b/loriot/task/backend/src/server.ts @@ -3,10 +3,14 @@ import { FrontEndRouter } from './frontend/routes.js'; import process from "node:process"; import { DevicesApi, PluginsApi, ApiException, PropertyCreate } from "@thinger-io/thinger-node"; import { request } from 'undici' +import { createServer } from 'http'; +import { Server as SocketIOServer } from 'socket.io'; + import { thingerApiConfig } from "./lib/api.js"; import { DownlinkMessage, parseEncodedToken } from "./lib/loriot.js"; import { Log } from "./lib/log.js"; +import { UserEvents } from './lib/user-events.js'; const _user: string = process.env.THINGER_USER || ""; const _plugin = process.env.THINGER_PLUGIN || ""; @@ -24,17 +28,57 @@ export type Application = { let settings: {applications: Application[]} = { applications: [] }; +// Set up user events logger. This logger is used to give the user feedback about +// the plugin operations. It souldnt be used for debugging purposes. +const userEvents = new UserEvents(); + const app: Express = express(); app.enable('trust proxy'); -app.use(express.json({strict: false, limit: '8mb'})) +app.use(express.json({ strict: false, limit: '8mb' })) + +const httpServer = createServer(app); + +const io = new SocketIOServer(httpServer, { + cors: { + origin: "*", + methods: ["GET", "POST"] + }, + path: '/socket.io' +}); // Serve the API app.post(`/downlink`, async (req: Request, res: Response) => { Log.log("Received downlink message:\n", JSON.stringify(req.body, null, 2)); + const { data, port, confirmed, uplink } = req.body; + + if (!data || !uplink) { + userEvents.push({ + category: 'downlink', + severity: 'error', + title: 'Downlink rejected: missing required fields', + details: { + error: 'Missing data or uplink information', + received: { data, uplink } + } + }); + res.status(400).send({ message: "Missing required fields: data or uplink" }); + return; + } + // Check if the downlink message is valid - if ( req.body.data === '' || req.body.data === null || req.body.data === 'null' ) { + if ( data === '' || data === null || data === 'null' ) { + userEvents.push({ + category: 'downlink', + severity: 'warning', + title: 'Downlink rejected: invalid data', + device: uplink.deviceEui, + details: { + error: 'Empty or null data provided', + deviceId: uplink.deviceId + } + }); res.status(200).send( { cmd: "", @@ -50,14 +94,36 @@ app.post(`/downlink`, async (req: Request, res: Response) => { } // find data by token - const application: Application | undefined = settings.applications.find((app: {applicationId: string}) => app.applicationId === req.body.uplink.appId); + const application: Application | undefined = settings.applications.find((app: {applicationId: string}) => app.applicationId === uplink.appId); if ( typeof application === 'undefined' ) { - Log.error(`Application ${req.body.uplink.appId} not found`); + Log.error(`Application ${uplink.appId} not found`); + userEvents.push({ + category: 'downlink', + severity: 'error', + title: 'Downlink failed: application not configured', + device: uplink.deviceEui, + details: { + error: 'Application not found in plugin settings', + deviceId: uplink.deviceId, + applicationId: uplink.appId + } + }); res.status(404).send({message: "Application not found"}); return; } if ( typeof application.accessToken === 'undefined' ) { - Log.error(`Access token not found for application ${req.body.uplink.appId}`); + Log.error(`Access token not found for application ${uplink.appId}`); + userEvents.push({ + category: 'downlink', + severity: 'error', + title: 'Downlink failed: missing access token', + device: uplink.deviceEui, + details: { + error: 'Application access token not configured', + deviceId: uplink.deviceId, + applicationId: uplink.appId + } + }); res.status(400).send({message: "Application access token not found"}); return; } @@ -65,16 +131,27 @@ app.post(`/downlink`, async (req: Request, res: Response) => { // Build downlink message to send to the device const msg: DownlinkMessage = { cmd: "tx", - EUI: req.body.uplink.deviceEui, - port: req.body.port, - confirmed: req.body.confirmed || false, - data: req.body.data, - appid: req.body.uplink.appId, + EUI: uplink.deviceEui, + port: port, + confirmed: confirmed || false, + data: data, + appid: uplink.appId, } const allDefined = Object.values(msg).every(value => value !== undefined); if ( !allDefined ) { Log.error("Downlink message is not well defined:\n", JSON.stringify(msg, null, 2)); + userEvents.push({ + category: 'downlink', + severity: 'error', + title: 'Downlink failed: invalid message format', + device: uplink.deviceEui, + details: { + error: 'Downlink message is not well defined', + deviceId: uplink.deviceId, + message: msg + } + }); res.status(400).send({message: "Downlink message is not well defined"}); return; } @@ -84,28 +161,115 @@ app.post(`/downlink`, async (req: Request, res: Response) => { "Authorization": `Bearer ${application.accessToken}` } - const token = parseEncodedToken(application.accessToken, null); - const server = token.serverId; - Log.log("Sending downlink message:\n", JSON.stringify(msg, null, 2) ); + try { + const token = parseEncodedToken(application.accessToken, null); + const server = token.serverId; + Log.log("Sending downlink message:\n", JSON.stringify(msg, null, 2) ); + + userEvents.push({ + category: 'downlink', + severity: 'info', + title: `Downlink initiated to ${uplink.deviceEui}`, + device: uplink.deviceEui, + application: uplink.appId, + details: { + deviceId: uplink.deviceId, + port: port, + confirmed: confirmed || false, + dataHex: data, + server: server + }, + metadata: { + size: Buffer.from(data, 'hex').length + } + }); + + const startTime = Date.now(); + const { + statusCode, + //headers, + body + } = await request(`https://${server}/1/rest`, { method: 'POST', headers: msg_headers , body: JSON.stringify(msg) }) + const duration = Date.now() - startTime; + + // Collect stream chunks into a string + let responseBody = ''; + for await (const chunk of body) { + responseBody += chunk.toString(); // Assuming chunks are Buffer or string + } - const { - statusCode, - //headers, - body - } = await request(`https://${server}/1/rest`, { method: 'POST', headers: msg_headers , body: JSON.stringify(msg) }) + // Now parse if it's JSON + let parsedBody; + try { + parsedBody = JSON.parse(responseBody); + } catch { + parsedBody = responseBody; + } + console.log(parsedBody); + + Log.debug(`Downlink response:\n`, statusCode, parsedBody); + + if (statusCode >= 200 && statusCode < 300) { + userEvents.push({ + category: 'downlink', + severity: 'success', + title: `Downlink sent to ${uplink.deviceEui}`, + device: uplink.deviceEui, + application: uplink.appId, + details: { + deviceId: uplink.deviceId, + port: port, + confirmed: confirmed || false, + statusCode: statusCode, + response: parsedBody + }, + metadata: { + duration: duration, + size: Buffer.from(data, 'hex').length + } + }); + } else { + userEvents.push({ + category: 'downlink', + severity: 'error', + title: `Downlink failed for ${uplink.deviceEui} (HTTP ${statusCode})`, + device: uplink.deviceEui, + application: uplink.appId, + details: { + deviceId: uplink.deviceId, + statusCode: statusCode, + error: parsedBody, + request: { + port: port, + confirmed: confirmed || false, + dataHex: data + } + }, + metadata: { + duration: duration + } + }); + } - // Collect stream chunks into a string - let responseBody = ''; - for await (const chunk of body) { - responseBody += chunk.toString(); // Assuming chunks are Buffer or string - } + res.status(statusCode).send(parsedBody); + + } catch (err: any) { + Log.error("Error while sending downlink:", err.message || err); - // Now parse if it's JSON - const parsedBody = JSON.parse(responseBody); - console.log(parsedBody); + userEvents.push({ + category: 'downlink', + severity: 'error', + title: `Downlink exception for ${uplink.deviceEui}`, + device: uplink.deviceEui, + details: { + deviceId: uplink.deviceId, + error: err.message || err, + stack: err.stack + } + }); - Log.debug(`Downlink response:\n`, statusCode, body); - res.status(statusCode).send(parsedBody); + res.status(500).send({ message: "Error while sending downlink", error: err.message || err }); + } }); @@ -173,26 +337,134 @@ app.post(`/:applicationId/uplink`, (req: Request, res: Response) => { // find data by id const application: Application | undefined = settings.applications.find((app: {applicationId: string}) => app.applicationId === applicationId); + const deviceEui = req.body.EUI; if ( typeof application !== 'undefined' ) { - - const device = `${ application.deviceIdPrefix }${ req.body.EUI}`; + const device = `${ application.deviceIdPrefix }${deviceEui}`; const thingerUplink = loriotToThinger(req.body, applicationId, device); devicesApi.accessInputResources(_user, device, 'uplink', thingerUplink).then((response: object) => { Log.log(`handling uplink callback for device ${device} and 'uplink ${response}'`); + userEvents.push({ + category: 'uplink', + severity: 'success', + title: `Uplink forwarded to Thinger (${device})`, + device: deviceEui, + application: applicationId, + details: { + deviceId: device, + action: 'forwarded_to_thinger', + fPort: thingerUplink.fPort, + fCnt: thingerUplink.fCnt + } + }); res.status(200).send(); }).catch((error: ApiException) => { - Log.error(`Error while handling uplink for device ${device}: ${error.message}`); - res.status(500).send(); + Log.log("Error while handling uplink", error); + userEvents.push({ + category: 'error', + severity: 'error', + title: `Failed to forward uplink from ${deviceEui}`, + device: deviceEui, + application: applicationId, + details: { + deviceId: device, + httpErrorCode: error.code || 'N/A', + error: error.message, + description: 'This plugin couldn\'t forward the uplink message to Thinger.io platform. Check you product id prefix', + fPort: thingerUplink.fPort, + fCnt: thingerUplink.fCnt + } + }); + res.status(500).send(); + }).catch((error: any) => { + Log.log("Unexpected error while handling uplink", error); + userEvents.push({ + category: 'error', + severity: 'error', + title: `Unexpected error forwarding uplink from ${deviceEui}`, + device: deviceEui, + application: applicationId, + details: { + deviceId: device, + httpErrorCode: error.code || 'N/A', + error: error.message || 'Unknown unexpected error', + uplinkRecieved: req.body + } + }); + res.status(500).send(); }); } else { Log.error(`Application ${applicationId} not found`); + userEvents.push({ + category: 'uplink', + severity: 'warning', + title: `Uplink rejected: unknown application ${applicationId}`, + device: deviceEui, + application: applicationId, + details: { + error: 'Application not configured in plugin settings', + applicationId: applicationId, + availableApplications: settings.applications.map(a => a.applicationId) + } + }); res.status(404).send(); } +}); + +io.on('connection', (socket) => { + Log.info('Client connected to events stream:', socket.id); + + // Send initial data when client connects + socket.emit('initial-events', { + events: userEvents.getRecent({ limit: 20 }), + config: userEvents.getConfig(), + stats: userEvents.getStats() + }); + + // Handle client requests for filtered events + socket.on('get-events', (filters) => { + try { + const events = userEvents.getRecent(filters); + socket.emit('events-response', { events, filters }); + } catch (error: any) { + socket.emit('error', { message: 'Error fetching events', error: error.message }); + } + }); + + // Handle clear events request + socket.on('clear-events', () => { + try { + userEvents.clear(); + socket.emit('events-cleared'); + } catch (error: any) { + socket.emit('error', { message: 'Error clearing events', error: error.message }); + } + }); + + // Handle get stats request + socket.on('get-stats', () => { + try { + socket.emit('stats-response', userEvents.getStats()); + } catch (error: any) { + socket.emit('error', { message: 'Error fetching stats', error: error.message }); + } + }); + + socket.on('disconnect', () => { + Log.info('Client disconnected from events stream:', socket.id); + }); +}); +userEvents.on('new-event', (event) => { + io.emit('new-event', event); +}); + +// When events are cleared, notify all clients +userEvents.on('events-cleared', () => { + io.emit('events-cleared'); }); // Endpoint to return the os environment variables that start with THINGER @@ -236,7 +508,7 @@ function saveSettings(value: object = {}){ return pluginsApi.createProperty(_user, _plugin, prop); } -function readSettings(){ +async function readSettings(){ pluginsApi.readProperty(_user, _plugin, "settings").then((response: { value: {applications: Application[]} }) => { @@ -262,8 +534,9 @@ function readSettings(){ }); } // Read settings on startup -readSettings(); +await readSettings(); -app.listen(3000, () => { - Log.log("Server running on port 3000"); +httpServer.listen(3000, () => { + Log.log("Server running on port 3000 with WebSocket support"); }); + diff --git a/loriot/task/frontend/package-lock.json b/loriot/task/frontend/package-lock.json index ad551c09..8cf4bc2a 100644 --- a/loriot/task/frontend/package-lock.json +++ b/loriot/task/frontend/package-lock.json @@ -23,6 +23,7 @@ "buffer": "^6.0.3", "ng-zorro-antd": "^19.0.0", "rxjs": "~7.8.0", + "socket.io-client": "^4.8.1", "tslib": "^2.3.0", "zone.js": "~0.15.0" }, @@ -4830,7 +4831,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "dev": true, "license": "MIT" }, "node_modules/@trysound/sax": { @@ -7386,11 +7386,40 @@ "node": ">=10.2.0" } }, + "node_modules/engine.io-client": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz", + "integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -10719,7 +10748,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/msgpackr": { @@ -13926,11 +13954,42 @@ } } }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, "license": "MIT", "dependencies": { "@socket.io/component-emitter": "~3.1.0", @@ -13944,7 +14003,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -15857,7 +15915,6 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -15875,6 +15932,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/loriot/task/frontend/package.json b/loriot/task/frontend/package.json index 59e4676a..ae348382 100644 --- a/loriot/task/frontend/package.json +++ b/loriot/task/frontend/package.json @@ -25,6 +25,7 @@ "buffer": "^6.0.3", "ng-zorro-antd": "^19.0.0", "rxjs": "~7.8.0", + "socket.io-client": "^4.8.1", "tslib": "^2.3.0", "zone.js": "~0.15.0" }, diff --git a/loriot/task/frontend/src/app/app.component.html b/loriot/task/frontend/src/app/app.component.html index 97ab845f..77b2a950 100644 --- a/loriot/task/frontend/src/app/app.component.html +++ b/loriot/task/frontend/src/app/app.component.html @@ -9,10 +9,17 @@
- - - - + + + + + + + + + + +
diff --git a/loriot/task/frontend/src/app/app.component.ts b/loriot/task/frontend/src/app/app.component.ts index a4555b30..27b9d563 100644 --- a/loriot/task/frontend/src/app/app.component.ts +++ b/loriot/task/frontend/src/app/app.component.ts @@ -3,23 +3,29 @@ import { CommonModule } from '@angular/common'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzLayoutModule } from 'ng-zorro-antd/layout'; import { NzMenuModule } from 'ng-zorro-antd/menu'; +import { NzSpaceModule } from "ng-zorro-antd/space"; +import { NzTabsModule } from 'ng-zorro-antd/tabs'; + +// Components import { SettingsComponent } from "./features/settings/settings.component"; import { ApplicationsComponent } from "./features/applications/applications.component"; -import { NzSpaceModule } from "ng-zorro-antd/space"; +import { InspectorComponent } from "./features/inspector/inspector.component"; @Component({ - selector: 'app-root', - imports: [ - CommonModule, - NzIconModule, - NzLayoutModule, - NzMenuModule, - SettingsComponent, - ApplicationsComponent, - NzSpaceModule - ], - templateUrl: './app.component.html', - styleUrls: ['./app.component.css'] + selector: 'app-root', + imports: [ + CommonModule, + NzIconModule, + NzLayoutModule, + NzMenuModule, + SettingsComponent, + ApplicationsComponent, + InspectorComponent, + NzSpaceModule, + NzTabsModule + ], + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] }) export class AppComponent { isCollapsed = false; diff --git a/loriot/task/frontend/src/app/core/services/events-socket.service.ts b/loriot/task/frontend/src/app/core/services/events-socket.service.ts new file mode 100644 index 00000000..4aa4ec59 --- /dev/null +++ b/loriot/task/frontend/src/app/core/services/events-socket.service.ts @@ -0,0 +1,195 @@ +import { Injectable } from '@angular/core'; +import { io, Socket } from 'socket.io-client'; +import { BehaviorSubject, Observable } from 'rxjs'; +import {HttpClient} from "@angular/common/http"; +import {AppConfigService} from "./app-config.service"; + +/** + * User event interface matching backend structure + */ +export interface UserEvent { + id: string; + timestamp: string; + category: 'uplink' | 'downlink' | 'device' | 'config' | 'error' | 'warning'; + severity: 'success' | 'info' | 'warning' | 'error'; + title: string; + device?: string; + application?: string; + details: Record; + metadata?: Record; +} + +/** + * Service to handle WebSocket connection for real-time events + */ +@Injectable({ + providedIn: 'root' +}) +export class EventsSocketService { + // @ts-ignore + private socket: Socket; + private events$ = new BehaviorSubject([]); + private connected$ = new BehaviorSubject(false); + private config$ = new BehaviorSubject(null); + + constructor() {} + + async initialize(socketEndpoint: string = '/socket.io', maxRetries: number = 5): Promise { + + const socketPath = window.location.pathname.replace(/\/$/, '') + socketEndpoint; + console.log(`Socket.IO path: ${socketPath}`); + + // Initialize Socket.IO client + this.socket = io({ + path: socketPath, + transports: ['websocket', 'polling'], + reconnection: true, + reconnectionDelay: 1000, + reconnectionDelayMax: 5000, + reconnectionAttempts: 10, + timeout: 20000, + autoConnect: true, + withCredentials: true + }); + + this.setupListeners(); + } + + /** + * Set up all Socket.IO event listeners + */ + private setupListeners(): void { + // Connection established + this.socket.on('connect', () => { + console.log('Connected to events WebSocket - ID:', this.socket.id); + this.connected$.next(true); + }); + + // Connection lost + this.socket.on('disconnect', (reason: string) => { + console.log('Disconnected from events WebSocket - Reason:', reason); + this.connected$.next(false); + }); + + // Reconnection attempts + this.socket.on('reconnect_attempt', (attempt: number) => { + console.log(`Reconnection attempt #${attempt}`); + }); + + // Successfully reconnected + this.socket.on('reconnect', (attempt: number) => { + console.log(`Reconnected after ${attempt} attempts`); + }); + + // Initial data when connecting + this.socket.on('initial-events', (data: { events: UserEvent[], config: any, stats: any }) => { + console.log('Received initial data:', data); + console.log(` - Events: ${data.events.length}`); + console.log(` - Config:`, data.config); + console.log(` - Stats:`, data.stats); + + this.events$.next(data.events); + this.config$.next(data.config); + }); + + // New event in real-time + this.socket.on('new-event', (event: UserEvent) => { + console.log('NEW EVENT:', event); + console.log(` Category: ${event.category}`); + console.log(` Severity: ${event.severity}`); + console.log(` Title: ${event.title}`); + console.log(` Device: ${event.device || 'N/A'}`); + console.log(` Details:`, event.details); + + const current = this.events$.value; + // Add new event at the beginning and keep only last 50 + this.events$.next([event, ...current].slice(0, 50)); + }); + + // Events cleared + this.socket.on('events-cleared', () => { + console.log('🗑Events cleared'); + this.events$.next([]); + }); + + // Response to filtered request + this.socket.on('events-response', (data: { events: UserEvent[], filters: any }) => { + console.log('Filtered events received:', data.events.length, 'events with filters:', data.filters); + this.events$.next(data.events); + }); + + // Stats response + this.socket.on('stats-response', (stats: any) => { + console.log('Stats received:', stats); + }); + + // Error handling + this.socket.on('error', (data: { message: string, error: string }) => { + console.error('Socket error:', data.message, '-', data.error); + }); + + // Connection error + this.socket.on('connect_error', (error: Error) => { + console.error('Connection error:', error.message); + }); + } + + /** + * Get events as Observable + */ + getEvents$(): Observable { + return this.events$.asObservable(); + } + + /** + * Get connection status as Observable + */ + isConnected$(): Observable { + return this.connected$.asObservable(); + } + + /** + * Get config as Observable + */ + getConfig$(): Observable { + return this.config$.asObservable(); + } + + /** + * Request events with optional filters + */ + requestEvents(filters?: { + limit?: number; + category?: string; + severity?: string; + device?: string; + application?: string; + }): void { + console.log('Requesting events with filters:', filters); + this.socket.emit('get-events', filters); + } + + /** + * Clear all events + */ + clearEvents(): void { + console.log('Requesting to clear events'); + this.socket.emit('clear-events'); + } + + /** + * Get statistics + */ + getStats(): void { + console.log('Requesting stats'); + this.socket.emit('get-stats'); + } + + /** + * Disconnect from WebSocket + */ + disconnect(): void { + console.log('Disconnecting from WebSocket'); + this.socket.disconnect(); + } +} diff --git a/loriot/task/frontend/src/app/features/inspector/inspector.component.html b/loriot/task/frontend/src/app/features/inspector/inspector.component.html new file mode 100644 index 00000000..f24493f7 --- /dev/null +++ b/loriot/task/frontend/src/app/features/inspector/inspector.component.html @@ -0,0 +1,126 @@ +
+ +
+
+

Recent Events

+ + + + {{ isConnected ? 'Live' : 'Disconnected' }} + +
+
+ {{ events.length }} total + +
+
+ + +
+
+

No events yet

+

Events will appear here in real-time

+
+ +
+ + +
+ +
+
+ + +
+
+ {{ event.title }} +
+
+ + {{ event.category }} + + + {{ event.device }} + + + {{ event.application }} + +
+
+ + +
+ + {{ event.metadata['duration'] }}ms + + + {{ event.metadata['size'] }}B + +
+ + +
+ {{ formatTime(event.timestamp) }} +
+ + +
+ {{ isEventExpanded(event.id) ? '▲' : '▼' }} +
+
+ + +
+ + +
+
+ ID: + {{ event.id }} +
+
+ Timestamp: + {{ event.timestamp | date :'medium' }} +
+
+ Severity: + + {{ event.severity }} + +
+
+ + +
+
Details
+
{{ event.details | json }}
+
+ + +
+
Metadata
+
{{ event.metadata | json }}
+
+
+
+
+
diff --git a/loriot/task/frontend/src/app/features/inspector/inspector.component.ts b/loriot/task/frontend/src/app/features/inspector/inspector.component.ts new file mode 100644 index 00000000..31e1700b --- /dev/null +++ b/loriot/task/frontend/src/app/features/inspector/inspector.component.ts @@ -0,0 +1,139 @@ +import {Component} from "@angular/core"; +import { NzModalService } from 'ng-zorro-antd/modal'; +import { CommonModule } from '@angular/common'; +import { EventsSocketService, UserEvent } from '../../core/services/events-socket.service'; +import { Subscription } from 'rxjs'; + +@Component({ + selector: 'app-inspector', + imports: [CommonModule], + templateUrl: './inspector.component.html', +}) +export class InspectorComponent { + // Events data + events: UserEvent[] = []; + isConnected = false; + expandedEventIds = new Set(); + + private subscription = new Subscription(); + + constructor(private eventsSocketService: EventsSocketService, + private modal: NzModalService) { + console.log('App Component initialized'); + } + + async ngOnInit() { + + try { + // Initialize WebSocket with health check and retries + await this.eventsSocketService.initialize('/socket.io', 5); + + + // Subscribe to events stream + this.subscription.add( + this.eventsSocketService.getEvents$().subscribe(events => { + this.events = events; + }) + ); + + // Subscribe to connection status + this.subscription.add( + this.eventsSocketService.isConnected$().subscribe(connected => { + this.isConnected = connected; + }) + ); + + } catch (error) { + console.error('Failed to initialize EventsSocketService:', error); + } + } + + /** + * Toggle event details expansion + */ + toggleEventDetails(eventId: string): void { + if (this.expandedEventIds.has(eventId)) { + this.expandedEventIds.delete(eventId); + } else { + this.expandedEventIds.add(eventId); + } + } + + /** + * Check if event is expanded + */ + isEventExpanded(eventId: string): boolean { + return this.expandedEventIds.has(eventId); + } + + /** + * Clear all events + */ + clearEvents(): void { + this.modal.confirm({ + nzTitle: 'Clear All Events', + nzContent: 'Are you sure you want to clear all events? This action cannot be undone.', + nzOkText: 'Clear', + nzOkType: 'primary', + nzOkDanger: true, + nzCancelText: 'Cancel', + nzOnOk: () => { + this.eventsSocketService.clearEvents(); + this.expandedEventIds.clear(); + } + }); + } + + /** + * Get color based on severity + */ + getSeverityColor(severity: string): string { + const colors: Record = { + 'success': '#52c41a', + 'info': '#1890ff', + 'warning': '#faad14', + 'error': '#ff4d4f' + }; + return colors[severity] || '#d9d9d9'; + } + + /** + * Get background color based on severity (for badges) + */ + getSeverityBg(severity: string): string { + const colors: Record = { + 'success': '#f6ffed', + 'info': '#e6f7ff', + 'warning': '#fffbe6', + 'error': '#fff2f0' + }; + return colors[severity] || '#fafafa'; + } + + /** + * Check if metadata has any content + */ + hasMetadata(metadata: any): boolean { + return metadata && Object.keys(metadata).length > 0; + } + + /** + * Format timestamp to relative time + */ + formatTime(timestamp: string): string { + const date = new Date(timestamp); + const now = new Date(); + const diff = Math.floor((now.getTime() - date.getTime()) / 1000); + + if (diff < 60) return `${diff}s ago`; + if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; + if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; + return date.toLocaleString(); + } + + ngOnDestroy() { + console.log('App Component destroyed - Cleaning up subscriptions'); + this.subscription.unsubscribe(); + this.eventsSocketService.disconnect(); + } +} diff --git a/loriot/task/frontend/src/styles.css b/loriot/task/frontend/src/styles.css index 90d4ee00..471de7b5 100644 --- a/loriot/task/frontend/src/styles.css +++ b/loriot/task/frontend/src/styles.css @@ -1 +1,22 @@ /* You can add global styles to this file, and also import other style files */ + +.app-layout { + height: 100vh; + display: flex; + flex-direction: column; +} + +nz-layout { + height: 100%; + display: flex; + flex-direction: column; +} + +nz-header { + flex-shrink: 0; +} + +nz-content { + flex: 1; + min-height: 0; +} diff --git a/milesight-am103/LICENSE.md b/milesight-am103/LICENSE.md deleted file mode 100644 index f1bacd61..00000000 --- a/milesight-am103/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2025 Thinger.io - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/milesight-am103/README.md b/milesight-am103/README.md index 9ba5c4a8..dd33ab27 100644 --- a/milesight-am103/README.md +++ b/milesight-am103/README.md @@ -18,9 +18,7 @@ Alongside, the corresponding plugin for the selected LoRaWAN server needs to be ### Installation -

- Milesight AM103 Plugin Installation into Thinger.io -

+![Milesight AM103 Plugin Installation into Thinger.io](assets/plugin-install.png) Once the plugin is installed a new Product will be created for this device. diff --git a/milesight-am103/assets/plugin-install.png b/milesight-am103/assets/plugin-install.png new file mode 100644 index 00000000..1bb98c0f Binary files /dev/null and b/milesight-am103/assets/plugin-install.png differ diff --git a/milesight-am103/docs/changelog.md b/milesight-am103/docs/changelog.md deleted file mode 100644 index fb8a602e..00000000 --- a/milesight-am103/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "milesight-am103/CHANGELOG.md" diff --git a/milesight-am103/docs/index.md b/milesight-am103/docs/index.md deleted file mode 100644 index 333ac5bc..00000000 --- a/milesight-am103/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "milesight-am103/README.md" diff --git a/milesight-am103/docs/plugin_file.md b/milesight-am103/docs/plugin_file.md deleted file mode 100644 index 3fdc2fe6..00000000 --- a/milesight-am103/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "milesight-am103/plugin.json" -```` diff --git a/milesight-am103/mkdocs.yml b/milesight-am103/mkdocs.yml deleted file mode 100644 index 77e84064..00000000 --- a/milesight-am103/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/milesight-am103 - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/milesight-am103/plugin.json b/milesight-am103/plugin.json index fcb84700..3751a5e3 100644 --- a/milesight-am103/plugin.json +++ b/milesight-am103/plugin.json @@ -12,6 +12,7 @@ "metadata": { "name": "Milesight AM103", "description": "Milesight AM103 device template for integrating into Thinger.io", + "category": "devices", "image": "assets/milesight-am103.png" }, "resources": { diff --git a/mkdocs.yml b/mkdocs.yml index 1b4ad867..2fb9de02 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,6 +2,7 @@ # Project information site_name: Thinger.io Marketplace site_url: https://marketplace.thinger.io/ +site_dir: _site site_author: Thinger.io site_description: >- Experience IoT innovation with powerful integrations, updates, and refined capabilities. Join us and embark on a data-driven journey @@ -12,7 +13,7 @@ repo_url: https://github.com/thinger-io/plugins # Copyright copyright: > - Copyright © 2016 - 2024 Thinger.io
+ Copyright © Thinger.io
# Change cookie settings # Configuration @@ -60,6 +61,7 @@ theme: # Plugins plugins: + - callouts - monorepo - social - blog: @@ -162,39 +164,53 @@ nav: - Plugins: - 'plugins/index.md' - Managing Plugins: 'plugins/managing.md' - #- Plugins: '*include ./*/mkdocs.yml' + # AUTO-GENERATED-PLUGINS-START - Integrations: - - Alertmanager: '!include ./alertmanager/mkdocs.yml' - - Comet-Tx5xx-Tx6xx: '!include ./comet-tx5xx-tx6xx/mkdocs.yml' - - ChirpStack: '!include ./chirpstack/mkdocs.yml' - - Docker Registry: '!include ./docker-registry/mkdocs.yml' - - Dragino LT22222L: '!include ./dragino-lt22222l/mkdocs.yml' - - Grafana: '!include ./grafana/mkdocs.yml' - - HTTP Devices: '!include ./http-device/mkdocs.yml' - - InfluxDB2: '!include ./influxdb2/mkdocs.yml' - - Jupyter Minimal: '!include ./jupyter-minimal/mkdocs.yml' - - Jupyter R: '!include ./jupyter-r/mkdocs.yml' - - Jupyter TensorFlow: '!include ./jupyter-tensorflow/mkdocs.yml' - - LoRaWAN Product Template: '!include ./lorawan-product-template/mkdocs.yml' - - LORIOT: '!include ./loriot/mkdocs.yml' - - Milesight AM103: '!include ./milesight-am103/mkdocs.yml' - - RStudio: '!include ./rstudio/mkdocs.yml' - - Node-RED: '!include ./node-red/mkdocs.yml' - - Prometheus Exporter: '!include ./prometheus-exporter/mkdocs.yml' - - Prometheus: '!include ./prometheus/mkdocs.yml' - - SFTPGo: '!include ./sftpgo/mkdocs.yml' - - Shelly 1L: '!include ./shelly-1l/mkdocs.yml' - - Shelly EM: '!include ./shelly-em/mkdocs.yml' - - Shelly Plug S: '!include ./shelly-plug-s/mkdocs.yml' - - Shelly Plus 1PM: '!include ./shelly-plus-1pm/mkdocs.yml' - - Sigfox: '!include ./sigfox/mkdocs.yml' - - Teltonika Eye: '!include ./teltonika-eye/mkdocs.yml' - - Teltonika Telematics: '!include ./teltonika-telematics/mkdocs.yml' - - TTN: '!include ./ttn/mkdocs.yml' - - Siemens LOGO!: '!include ./siemens-logo/mkdocs.yml' - - VS Code: '!include ./vscode/mkdocs.yml' - - Wika PGW23-100: '!include ./wika-pgw23-100/mkdocs.yml' - - Wika PEW1000: '!include ./wika-pew1000/mkdocs.yml' + - Devices: + - Overview: 'plugins/devices/index.md' + - Comet Tx: '!include ./_build/plugins/devices/comet-tx5xx-tx6xx/mkdocs.yml' + - Dragino LT-22222-L: '!include ./_build/plugins/devices/dragino-lt22222l/mkdocs.yml' + - Milesight AM103: '!include ./_build/plugins/devices/milesight-am103/mkdocs.yml' + - Shelly 1L: '!include ./_build/plugins/devices/shelly-1l/mkdocs.yml' + - Shelly EM: '!include ./_build/plugins/devices/shelly-em/mkdocs.yml' + - Shelly Plug S: '!include ./_build/plugins/devices/shelly-plug-s/mkdocs.yml' + - Shelly Plus 1PM: '!include ./_build/plugins/devices/shelly-plus-1pm/mkdocs.yml' + - Siemens LOGO!: '!include ./_build/plugins/devices/siemens-logo/mkdocs.yml' + - Teltonika Eye: '!include ./_build/plugins/devices/teltonika-eye/mkdocs.yml' + - Teltonika Telematics: '!include ./_build/plugins/devices/teltonika-telematics/mkdocs.yml' + - WIKA PEW-1000: '!include ./_build/plugins/devices/wika-pew1000/mkdocs.yml' + - Wika PGW23-100 Pressure Sensor: '!include ./_build/plugins/devices/wika-pgw23-100/mkdocs.yml' + - Connectivity: + - Overview: 'plugins/connectivity/index.md' + - ChirpStack: '!include ./_build/plugins/connectivity/chirpstack/mkdocs.yml' + - HTTP Device: '!include ./_build/plugins/connectivity/http-device/mkdocs.yml' + - LORIOT: '!include ./_build/plugins/connectivity/loriot/mkdocs.yml' + - Sigfox: '!include ./_build/plugins/connectivity/sigfox/mkdocs.yml' + - The Things Stack: '!include ./_build/plugins/connectivity/ttn/mkdocs.yml' + - The Things Stack: '!include ./_build/plugins/connectivity/ttn-stack/mkdocs.yml' + - Monitoring: + - Overview: 'plugins/monitoring/index.md' + - Alertmanager: '!include ./_build/plugins/monitoring/alertmanager/mkdocs.yml' + - Grafana: '!include ./_build/plugins/monitoring/grafana/mkdocs.yml' + - InfluxDB2: '!include ./_build/plugins/monitoring/influxdb2/mkdocs.yml' + - Prometheus: '!include ./_build/plugins/monitoring/prometheus/mkdocs.yml' + - Prometheus Exporter: '!include ./_build/plugins/monitoring/prometheus-exporter/mkdocs.yml' + - Development: + - Overview: 'plugins/development/index.md' + - Jupyter Minimal: '!include ./_build/plugins/development/jupyter-minimal/mkdocs.yml' + - Jupyter R: '!include ./_build/plugins/development/jupyter-r/mkdocs.yml' + - Jupyter TensorFlow: '!include ./_build/plugins/development/jupyter-tensorflow/mkdocs.yml' + - Node-RED: '!include ./_build/plugins/development/node-red/mkdocs.yml' + - RStudio: '!include ./_build/plugins/development/rstudio/mkdocs.yml' + - VS Code: '!include ./_build/plugins/development/vscode/mkdocs.yml' + - Infrastructure: + - Overview: 'plugins/infrastructure/index.md' + - Docker Registry: '!include ./_build/plugins/infrastructure/docker-registry/mkdocs.yml' + - SFTPGo: '!include ./_build/plugins/infrastructure/sftpgo/mkdocs.yml' + - Templates: + - Overview: 'plugins/templates/index.md' + - LoRaWAN Device Template: '!include ./_build/plugins/templates/lorawan-product-template/mkdocs.yml' + # AUTO-GENERATED-PLUGINS-END - Contributing: - 'contributing/index.md' - Plugin System Infrastructure: 'contributing/plugin-system-infrastructure.md' diff --git a/node-red/README.md b/node-red/README.md index 7516da11..ca4d4e18 100644 --- a/node-red/README.md +++ b/node-red/README.md @@ -5,9 +5,7 @@ Node-RED is an Open Source project created by IBM to provide the Rule Engines ma This technology begins especially useful for IoT projects, to process and analyze data or create rules to automate behaviors in response to events produced by the IoT devices measures. It can also be used as an MQTT broker or to show data in customizable dashboards. However, its greatest potential is obtained when used in combination with an IoT platform like [Thinger.io](https://thinger.io/), leaving one in charge of data acquisition, storage, and device management and the other one for processing, automation, etc. -

- Thinger.io web console with Node-RED plugin and ad-hoc nodes -

+![Thinger.io web console with Node-RED plugin and ad-hoc nodes](assets/laptop.png) ## Thinger.io and Node-RED integration @@ -44,19 +42,19 @@ The "Nodes" are the basic building blocks for creating flows. Each node contains #### **Inject Nodes** -Inject node image of Node-RED +![Inject node image of Node-RED](assets/node-inject.png) This node allows to automatically start a flow when an event is triggered, at regular intervals defined in the properties form or manually over the web editor. The message sent by these nodes has the payload and topic properties set. **Function, Change, Switch Nodes** -Function node image of Node-RED +![Function node image of Node-RED](assets/node-function.png) Node-RED counts with some nodes that allows working with the flow's payload using pre-configured tools or coding their behavior in JavaScript functions that can be created within the editor using a rich text editor. With this proposal, these nodes are provided with both input and output connectors. **Output and Debug Nodes** -Debug node image of Node-RED +![Debug node image of Node-RED](assets/node-debug.png) These nodes only have an input connector that allows extracting data from the flow to third parties, files, or debugs console in the web editor. @@ -70,9 +68,7 @@ A flow is represented as a tab within the editor workspace and is the main way t The flow editor makes it easy to wire together flows using the wide range of nodes in the palette. Flows can be then deployed to the runtime just clicking into "debloy" button: -

- Deploy flow button in Node-RED -

+![Deploy flow button in Node-RED](assets/button-deploy.png) ### Debug Console @@ -80,9 +76,8 @@ Is a section of the right slide bar that provides a structured view of the messa Alongside each message, the debug sidebar includes information about the time the message was received and which Debug node sent it. Clicking on the source node id will reveal that node within the workspace. -!!! note "Node-RED documentation" - - You can find additional documentation about the use of this tool in Node-RED's official website: [https://nodered.org/docs/user-guide/editor/workspace/](https://nodered.org/docs/user-guide/editor/workspace/) +> [!NOTE] Node-RED documentation +> You can find additional documentation about the use of this tool in Node-RED's official website: [https://nodered.org/docs/user-guide/editor/workspace/](https://nodered.org/docs/user-guide/editor/workspace/) ## Starting with Thinger.io Nodes @@ -90,21 +85,16 @@ In this section, it is described **how to configure Node-RED** "Thinger.io Nodes To make this configuration, just drag any Thinger.io Node to the canvas and open its properties form, then go to the last input, called "Server " and click into the edition button, which will open the Thinger-Server configuration menu. -

- Node-RED node configuration form -

+![Node-RED node configuration form](assets/node-form.png) This form allows you to introduce the credentials of the specific Thinger.io instance address and authorization that is going to receive your Node-RED requests. However, it is important to take care of the next considerations in order to make a proper configuration: -!!! tip - - * If you are using Node-RED in a Thinger.io Plugin and you want to work with the same Thinger.io server that is hosting the plugin, it is only necessary to include `$(THINGER_HOST)` into "Host" box and `$(THINGER_TOKEN_NODE_RED_PLUGIN)` in the "Token" box, then you can disable SSL communication, as all messages are going to run into the same computer. +> [!TIP] +> * If you are using Node-RED in a Thinger.io Plugin and you want to work with the same Thinger.io server that is hosting the plugin, it is only necessary to include `$(THINGER_HOST)` into "Host" box and `$(THINGER_TOKEN_NODE_RED_PLUGIN)` in the "Token" box, then you can disable SSL communication, as all messages are going to run into the same computer. * if you want to use a different Thinger.io Server or you aren't running Node-RED from a plugin, it will be necessary to **include its URL or IP Address** in the "Host" box and also a Thinger.io **Access Token with Admin-Access** privileges in the "Token" box, finally, SSL would be preferable in this case. -

- Node-RED server node configuration form -

+![Node-RED server node configuration form](assets/node-server-configuration.png) ## Thinger.io Nodes @@ -112,7 +102,7 @@ As it was explained in the beginning, this plugin has two purposes: Host a Node- ### Asset Iterator -Asset iterator node from Thinger.io image of Node-RED +![Asset iterator node from Thinger.io image of Node-RED](assets/node-asset-iterator.png) It is a **Function** Node that iterates over all the desired assets avaible on Thinger.io Platform given a filter. It is able to receive a JSON from Node-RED flow and automatically query the backend. This node is useful to execute operations over multiple assets at the same time. @@ -122,7 +112,7 @@ There will be as many output messages as assets retrieved, each containing the i ### Bucket Create -Bucket create node from Thinger.io image of Node-RED +![Bucket create node from Thinger.io image of Node-RED](assets/node-bucket-create.png) It is a **Function** Node that creates a Data Bucket into a Thinger.io Platform. It is able to receive a JSON from Node-RED flow and automatically create the Bucket, so results quite useful to implement scalable and on demand data storage to any project. @@ -132,7 +122,7 @@ The output message will contain the result and details of the operation. ### Bucket Export -Bucket export node from Thinger.io image of Node-RED +![Bucket export node from Thinger.io image of Node-RED](assets/node-bucket-export.png) It is a **Function** Node that executes a Thinger.io Data Bucket export operation and waits until the data is ready to be downloaded, returning the download file URL or the contents as desired. It is useful to extract data from Thinger.io for further analysis, storage or forwarding to other services. @@ -140,7 +130,7 @@ The configuration of this Node requires introducing of at least the `Bucket ID`, ### Bucket Read -Bucket read node from Thinger.io image of Node-RED +![Bucket read node from Thinger.io image of Node-RED](assets/node-bucket-read.png) It is a **Function** Node that retrieves data from a specific Thinger.io Data Bucket when an injection Node requires it. It is useful to get data from buckets with a Node-RED programmed sampling interval. @@ -148,7 +138,7 @@ To configure this node just include the `Bucket ID`, `Filter` and `Sorting` into ### Bucket Write -Bucket write node from Thinger.io image of Node-RED +![Bucket write node from Thinger.io image of Node-RED](assets/node-bucket-write.png) It is an **Output** Node that allows to store data into the Thinger.io Data Bucket. It is able to receive a JSON from Node-RED flow and automatically create an entry into the Bucket, so results quite useful to implement scalable data storage to any project. @@ -156,7 +146,7 @@ The configuration of this Node just requires introducing the `Bucket ID`, howeve ### Device Callback -Device callback node from Thinger.io image of Node-RED +![Device callback node from Thinger.io image of Node-RED](assets/node-device-callback.png) It is a **Function** Node that calls an HTTP device callback in order to send a message or retrieve what the device has configured in its callback. If the device does not exist it will manage the auto provisioning of a new device and data bucket. It is useful to get auto provision new devices based of different events save its data from the beginning. @@ -164,7 +154,7 @@ To configure this node just include the `Device ID` into the node parameters for ### Device Create -Device create node from Thinger.io image of Node-RED +![Device create node from Thinger.io image of Node-RED](assets/node-device-create.png) It is a **Function** Node that creates a Device into the Thinger.io Platform. It is able to receive a JSON from Node-RED flow and automatically create the Device, so results quite useful to implement scalable and on demand device creation to any project. @@ -174,7 +164,7 @@ The output message will contain the result and details of the operation. ### Device Read -Device read node from Thinger.io image of Node-RED +![Device read node from Thinger.io image of Node-RED](assets/node-device-read.png) It is a **Function** Node that retrieves data from a specific Thinger.io device resource when an injection Node requires it. It is useful to get data from devices with a Node-RED programmed sampling interval. @@ -182,7 +172,7 @@ To configure this node just include the `Device ID`and the `Resource Name` into ### Device Stream -Device stream node from Thinger.io image of Node-RED +![Device stream node from Thinger.io image of Node-RED](assets/node-device-stream.png) It is an **Injection** Node that retrieves data from a specific Thinger.io Device Resource on regular defined interval expressed in seconds into the properties form. @@ -190,7 +180,7 @@ To configure this node just include the `Device ID`and the `Resource Name` into ### Device Write -Device write node from Thinger.io image of Node-RED +![Device write node from Thinger.io image of Node-RED](assets/node-device-write.png) It is a **Function** Node that allows sending data to a Thinger.io connected Device. It is able to receive a JSON from Node-RED flow and automatically send it to other devices in real-time. So results quite useful to implement scalable device communication for any project. @@ -198,7 +188,7 @@ The configuration of this Node just requires introducing the `Device ID` and the ### Endpoint Call -Endpoint call node from Thinger.io image of Node-RED +![Endpoint call node from Thinger.io image of Node-RED](assets/node-endpoint-call.png) It is a **Function** Node that allows calling the execution of a Thinger.io endpoint profile. It is able to receive a JSON that can be introduced in the endpoint in order to use that data in an e-mail or send it to third parties, so it is quite useful to create notifications or to integrating an IoT project with other systems. @@ -206,7 +196,7 @@ The configuration of this Node just requires introducing the `Endpoint ID`, howe ### Property Read -Property read node from Thinger.io image of Node-RED +![Property read node from Thinger.io image of Node-RED](assets/node-property-read.png) It is a **Function** Node that retrieves data from a specific Thinger.io device, type or group property when an injection Node requires it. It is useful to get data from assets with a Node-RED programmed sampling interval. @@ -214,7 +204,7 @@ To configure this node just include the `Asset Type`, `Asset ID`and `Property Na ### Property Write -Property write node from Thinger.io image of Node-RED +![Property write node from Thinger.io image of Node-RED](assets/node-property-write.png) It is a **Function** Node that writes data into a specific Thinger.io device, type or group property when an injection Node requires it. It is useful to save data into assets in order to manage different configurations and act accordingly. @@ -222,7 +212,7 @@ To configure this node just include the `Asset Type`, `Asset ID`and `Property Na ### Storage Read -Storage read node from Thinger.io image of Node-RED +![Storage read node from Thinger.io image of Node-RED](assets/node-storage-read.png) It is a **Function** Node that reads any file saved in a Thinger.io storage and returns it to Node-RED. It is able to read any file, returning the string representation for text files (plain, csv, json, ...) or a Buffer otherwise. Useful for retrieving and treating information or being able to pass the info to third party nodes. @@ -230,7 +220,7 @@ The configuration of the node requires only the `Storage ID`, as it is able to r ### Storage Write -Storage write node from Thinger.io image of Node-RED +![Storage write node from Thinger.io image of Node-RED](assets/node-storage-write.png) It is an **Output** Node that allows to store data into the Thinger.io File Storage. It is able to receive a payload from Node-RED flow and automatically save or append into a file in the Storage, so results quite useful to implement scalable file storage to any project. @@ -238,7 +228,7 @@ The configuration of this Node just requires introducing the `Storage ID`, howev ### Server Events -Server events node from Thinger.io image of Node-RED +![Server events node from Thinger.io image of Node-RED](assets/node-server-events.png) It is an **Inject** Node that allows triggering in real-time any event that takes places in Thinger.io IoT Server over all existing assets, like device, types, groups or buckets. Some events contain also additional filter fields to filter from. @@ -246,9 +236,8 @@ The configuration of each type requires to include the ID of the element that wa When any of these events is triggered, this Node is able to inject a JSON in the flow with the identifier of the Bucket, Device, or Endpoint that has produced the event along with the relevant data. -!!! tip - - If you need further technical information, you can find all the details for each node in Node-RED help dialog. +> [!TIP] +> If you need further technical information, you can find all the details for each node in Node-RED help dialog. ## Useful Example Flows @@ -258,15 +247,12 @@ In this section, you can find our own cookbook with some useful flows that you c Using the "Device Status Change" property of the Server Event Node, it is possible to detect the disconnection of any device of your IoT network and execute a flow in Node-RED to notify the incidence using an endpoint sending an email for example. - !!! warning - - [Learn how to create an email endpoint here. ](https://docs.thinger.io/features/endpoints-1#email-endpoint) + > [!WARNING] + > [Learn how to create an email endpoint here. ](https://docs.thinger.io/features/endpoints-1#email-endpoint) The next flow uses two Thinger.io Nodes, the first one is triggering the Device Disconnection Server Event that will throw a JSON formatted message with the device ID, the status and the timestamp of the change. The second Node allows calling the Endpoint profile to send the alert with the device information JSON attached so it is possible to custom the message to easily identify the problem. -

- On device state change call DisconnectionAlert endpoint -

+ ![On device state change call DisconnectionAlert endpoint](assets/flow-device-disconnection-alert.png) This flow can be easily imported into your Node-RED workspace using the next JSON: @@ -280,12 +266,10 @@ In this section, you can find our own cookbook with some useful flows that you c MQTT is an extended communication protocol in IoT that works on top of the TCP/IP protocol suite. It is designed for connections with remote locations where a "small code footprint" is required or the network bandwidth is limited. The next flow allows sending data from a device hosted by NodeRED MQTT Server to the REST API Callback of a Thinger.io HTTP device, in order to store, analyze an show that information with Thinger.io features. - !!! info - [Learn how to work with Thinger.io HTTP device callback here](https://docs.thinger.io/http-devices) + > [!NOTE] + > [Learn how to work with Thinger.io HTTP device callback here](https://docs.thinger.io/http-devices) -

- From MQTT Topic to device callback -

+ ![From MQTT Topic to device callback](assets/flow-mqtt-to-thinger.png) This flow can be easily imported into your Node-RED workspace using the next JSON: @@ -299,15 +283,12 @@ In this section, you can find our own cookbook with some useful flows that you c Geofencing is an interesting IoT use case, with many applications in asset management, fleets or package tracking. The next flow shows how to monitor the location of any device to create an alert when it leaves an area specified with a Geofence Node. -

- When device outside geofence call alert endpoint -

+ ![When device outside geofence call alert endpoint](assets/flow-gps-geofences.png) Creating this integration with the "device\_location" property, it is possible to integrate any kind of device including Thinger.io Software Clients, Sigfox, TTN or even HTTP devices in a very simple way. - !!! info - - [Learn how to create an email endpoint here. ](https://docs.thinger.io/console#email-endpoint) + > [!NOTE] + > [Learn how to create an email endpoint here. ](https://docs.thinger.io/console#email-endpoint) This flow can be easily imported into your Node-RED workspace using the next JSON: @@ -315,14 +296,11 @@ In this section, you can find our own cookbook with some useful flows that you c [{"id":"5f747490.ef8edc","type":"tab","label":"GPS Geofences","disabled":false,"info":""},{"id":"207937f8.10dc48","type":"server-events","z":"5f747490.ef8edc","name":"","asset":"","event":"device_callback_call","filter":"","filters":{},"bucket":"","device":"","endpoint":"","state":"","server":"ec0dd4b1ef5aa9a8","x":158.60000228881836,"y":264.00000381469727,"wires":[["8f3dd8cd.71e4e8","56adf62.4aa1108"]]},{"id":"553ff10.2e60c1","type":"geofence","z":"5f747490.ef8edc","name":"","mode":"polyline","inside":"false","rad":0,"points":[{"latitude":39.89203705190782,"longitude":-3.8814695924520493},{"latitude":40.06041580712444,"longitude":-3.507934436202049},{"latitude":40.09404176311921,"longitude":-3.0025633424520493},{"latitude":41.14474248673421,"longitude":-3.5299070924520493},{"latitude":40.253538217286675,"longitude":-4.628539904952049},{"latitude":40.102445657515226,"longitude":-3.771606311202049}],"centre":{},"floor":"","ceiling":"","worldmap":false,"outputs":1,"x":573.0000076293945,"y":265.0000276565552,"wires":[["be251c21.6d6e3","233f843b.94472c"]]},{"id":"be251c21.6d6e3","type":"endpoint-call","z":"5f747490.ef8edc","name":"","endpoint":"alert","server":"ec0dd4b1ef5aa9a8","x":771.6000595092773,"y":266.0000286102295,"wires":[[]]},{"id":"8f3dd8cd.71e4e8","type":"debug","z":"5f747490.ef8edc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":399.6000061035156,"y":346.00000762939453,"wires":[]},{"id":"233f843b.94472c","type":"debug","z":"5f747490.ef8edc","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":790.6000366210938,"y":347.0000057220459,"wires":[]},{"id":"56adf62.4aa1108","type":"change","z":"5f747490.ef8edc","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":389.6000061035156,"y":265.00000381469727,"wires":[["553ff10.2e60c1"]]},{"id":"ec0dd4b1ef5aa9a8","type":"thinger-server","host":"$(THINGER_HOST)","name":"local","ssl":false}] ``` - === "Modify Incoming data" In some situations it is required to change the unit or metric of any variable, it is now possible to make this transformations before store data in a data bucket by use this Node-RED flow: -

- Receive device callback, convert units and store -

+ ![Receive device callback, convert units and store](assets/flow-modify-incoming-data.png) The configuration is quite simple, fist node retrieves the measurement of any device to be modified by the "function" node, that contains the codification that has been included below, and finally the "bucket write" node allows storing the transformed data. @@ -344,9 +322,7 @@ In this section, you can find our own cookbook with some useful flows that you c With the "Device create" and "Bucket create" nodes it is possible to create multiple devices, with the same or different credentials, types or groups; and create buckets associated to the recently created devices. -

- Creation of devices and assignment of buckets -

+ ![Creation of devices and assignment of buckets](assets/flow-device-creation.png) This flow can be easily imported into your Node-RED workspace using the next JSON: @@ -354,6 +330,5 @@ In this section, you can find our own cookbook with some useful flows that you c [{"id":"a4b9f2e08c15ad79","type":"tab","label":"Device creation","disabled":false,"info":"","env":[]},{"id":"507f1a24287e53e2","type":"function","z":"a4b9f2e08c15ad79","name":"loop - 10 devices","func":"let device = msg.device;\n\nfor (let i= 0; i < 10; i++) {\n msg.device = `${device}_${i}`;\n msg.name = `${device} ${i}`;\n msg.description = \"Device auto generated from Node-RED flow\";\n node.send(msg);\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":490,"y":220,"wires":[["752ee26eb02bed51"]]},{"id":"415e0ad528c64866","type":"inject","z":"a4b9f2e08c15ad79","name":"inject device name","props":[{"p":"device","v":"climastick","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":270,"y":220,"wires":[["507f1a24287e53e2"]]},{"id":"752ee26eb02bed51","type":"device-create","z":"a4b9f2e08c15ad79","name":"","deviceType":"Generic","deviceId":"","deviceCredentials":"&Xzi3LlG&lyaoyO2","deviceName":"","description":"","assetType":"temperature","assetGroup":"house","server":"ec0dd4b1ef5aa9a8","x":690,"y":220,"wires":[["e1da854b042efd77"]]},{"id":"69d16e383e46acd9","type":"debug","z":"a4b9f2e08c15ad79","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":715,"y":300,"wires":[],"l":false},{"id":"3be83c6bcc65f1cb","type":"comment","z":"a4b9f2e08c15ad79","name":"Create 10 device and assign a bucket to all of them","info":"This node creates 10 devices in Thinger.io Platform, in this case climasticks, from 0 to 9; all of them with the same credentials, type and group. Also, 10 buckets are created with the date source being the devices created.\n\n### Device create node\nThe device id, name and description are passed as input to the device create node, while the credentials, type and group is configured through the device create node dialog.\n\n### Bucket create node\nThe id, bucket name and description are passed as input to the bucket create node, as well as the extra source (the device in this case) for the data source.\nThe source (device), resource, update interval and asset type and group are configured through the bucket create node dialog.","x":350,"y":160,"wires":[]},{"id":"05af9a7c2958e6c6","type":"bucket-create","z":"a4b9f2e08c15ad79","name":"","bucketId":"","bucket":"","description":"","enabled":true,"source":"device","extraSource":"","resource":"temperature","update":"interval","interval":"1m","assetType":"temperature","assetGroup":"house","server":"ec0dd4b1ef5aa9a8","x":570,"y":300,"wires":[["69d16e383e46acd9"]]},{"id":"e1da854b042efd77","type":"change","z":"a4b9f2e08c15ad79","name":"Reorder message to create bucket","rules":[{"t":"delete","p":"payload","pt":"msg"},{"t":"set","p":"id","pt":"msg","to":"device","tot":"msg"},{"t":"move","p":"name","pt":"msg","to":"bucket","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":300,"wires":[["05af9a7c2958e6c6"]]},{"id":"ec0dd4b1ef5aa9a8","type":"thinger-server","host":"$(THINGER_HOST)","name":"local","ssl":false}] ``` - !!! info - - Learn how to create devices [here](https://docs.thinger.io/features/devices-administration), and data buckets [here](https://docs.thinger.io/features/buckets) + > [!NOTE] + > Learn how to create devices [here](https://docs.thinger.io/features/devices-administration), and data buckets [here](https://docs.thinger.io/features/buckets) diff --git a/node-red/assets/button-deploy.png b/node-red/assets/button-deploy.png new file mode 100644 index 00000000..12bd6b1e Binary files /dev/null and b/node-red/assets/button-deploy.png differ diff --git a/node-red/assets/flow-device-creation.png b/node-red/assets/flow-device-creation.png new file mode 100644 index 00000000..10bb9c82 Binary files /dev/null and b/node-red/assets/flow-device-creation.png differ diff --git a/node-red/assets/flow-device-disconnection-alert.png b/node-red/assets/flow-device-disconnection-alert.png new file mode 100644 index 00000000..d3c62923 Binary files /dev/null and b/node-red/assets/flow-device-disconnection-alert.png differ diff --git a/node-red/assets/flow-gps-geofences.png b/node-red/assets/flow-gps-geofences.png new file mode 100644 index 00000000..05c2e40d Binary files /dev/null and b/node-red/assets/flow-gps-geofences.png differ diff --git a/node-red/assets/flow-modify-incoming-data.png b/node-red/assets/flow-modify-incoming-data.png new file mode 100644 index 00000000..9693a0d7 Binary files /dev/null and b/node-red/assets/flow-modify-incoming-data.png differ diff --git a/node-red/assets/flow-mqtt-to-thinger.png b/node-red/assets/flow-mqtt-to-thinger.png new file mode 100644 index 00000000..6f3dd153 Binary files /dev/null and b/node-red/assets/flow-mqtt-to-thinger.png differ diff --git a/node-red/assets/laptop.png b/node-red/assets/laptop.png new file mode 100644 index 00000000..fa4c0bbe Binary files /dev/null and b/node-red/assets/laptop.png differ diff --git a/node-red/assets/node-asset-iterator.png b/node-red/assets/node-asset-iterator.png new file mode 100644 index 00000000..8d60095a Binary files /dev/null and b/node-red/assets/node-asset-iterator.png differ diff --git a/node-red/assets/node-bucket-create.png b/node-red/assets/node-bucket-create.png new file mode 100644 index 00000000..269cff01 Binary files /dev/null and b/node-red/assets/node-bucket-create.png differ diff --git a/node-red/assets/node-bucket-export.png b/node-red/assets/node-bucket-export.png new file mode 100644 index 00000000..8af2fdb1 Binary files /dev/null and b/node-red/assets/node-bucket-export.png differ diff --git a/node-red/assets/node-bucket-read.png b/node-red/assets/node-bucket-read.png new file mode 100644 index 00000000..fd28ce24 Binary files /dev/null and b/node-red/assets/node-bucket-read.png differ diff --git a/node-red/assets/node-bucket-write.png b/node-red/assets/node-bucket-write.png new file mode 100644 index 00000000..c4c2345a Binary files /dev/null and b/node-red/assets/node-bucket-write.png differ diff --git a/node-red/assets/node-debug.png b/node-red/assets/node-debug.png new file mode 100644 index 00000000..1e6e7b0e Binary files /dev/null and b/node-red/assets/node-debug.png differ diff --git a/node-red/assets/node-device-callback.png b/node-red/assets/node-device-callback.png new file mode 100644 index 00000000..f91cf776 Binary files /dev/null and b/node-red/assets/node-device-callback.png differ diff --git a/node-red/assets/node-device-create.png b/node-red/assets/node-device-create.png new file mode 100644 index 00000000..d11a1a92 Binary files /dev/null and b/node-red/assets/node-device-create.png differ diff --git a/node-red/assets/node-device-read.png b/node-red/assets/node-device-read.png new file mode 100644 index 00000000..cfc819d7 Binary files /dev/null and b/node-red/assets/node-device-read.png differ diff --git a/node-red/assets/node-device-stream.png b/node-red/assets/node-device-stream.png new file mode 100644 index 00000000..1232d257 Binary files /dev/null and b/node-red/assets/node-device-stream.png differ diff --git a/node-red/assets/node-device-write.png b/node-red/assets/node-device-write.png new file mode 100644 index 00000000..022ad580 Binary files /dev/null and b/node-red/assets/node-device-write.png differ diff --git a/node-red/assets/node-endpoint-call.png b/node-red/assets/node-endpoint-call.png new file mode 100644 index 00000000..7248497b Binary files /dev/null and b/node-red/assets/node-endpoint-call.png differ diff --git a/node-red/assets/node-form.png b/node-red/assets/node-form.png new file mode 100644 index 00000000..aa984490 Binary files /dev/null and b/node-red/assets/node-form.png differ diff --git a/node-red/assets/node-function.png b/node-red/assets/node-function.png new file mode 100644 index 00000000..8d0b1af2 Binary files /dev/null and b/node-red/assets/node-function.png differ diff --git a/node-red/assets/node-inject.png b/node-red/assets/node-inject.png new file mode 100644 index 00000000..943cc84e Binary files /dev/null and b/node-red/assets/node-inject.png differ diff --git a/node-red/assets/node-property-read.png b/node-red/assets/node-property-read.png new file mode 100644 index 00000000..72ead530 Binary files /dev/null and b/node-red/assets/node-property-read.png differ diff --git a/node-red/assets/node-property-write.png b/node-red/assets/node-property-write.png new file mode 100644 index 00000000..7242db75 Binary files /dev/null and b/node-red/assets/node-property-write.png differ diff --git a/node-red/assets/node-red-icon.svg b/node-red/assets/node-red-icon.svg new file mode 100644 index 00000000..e412dce6 --- /dev/null +++ b/node-red/assets/node-red-icon.svg @@ -0,0 +1,33 @@ + + + + Node-RED Icon + + + + image/svg+xml + + Node-RED Icon + + + + Nick O'Leary + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/node-red/assets/node-red-logo.svg b/node-red/assets/node-red-logo.svg new file mode 100644 index 00000000..45b8108d --- /dev/null +++ b/node-red/assets/node-red-logo.svg @@ -0,0 +1,30 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/node-red/assets/node-server-configuration.png b/node-red/assets/node-server-configuration.png new file mode 100644 index 00000000..f1c90f3a Binary files /dev/null and b/node-red/assets/node-server-configuration.png differ diff --git a/node-red/assets/node-server-events.png b/node-red/assets/node-server-events.png new file mode 100644 index 00000000..9f6c2611 Binary files /dev/null and b/node-red/assets/node-server-events.png differ diff --git a/node-red/assets/node-storage-read.png b/node-red/assets/node-storage-read.png new file mode 100644 index 00000000..ae61a00d Binary files /dev/null and b/node-red/assets/node-storage-read.png differ diff --git a/node-red/assets/node-storage-write.png b/node-red/assets/node-storage-write.png new file mode 100644 index 00000000..52c054ee Binary files /dev/null and b/node-red/assets/node-storage-write.png differ diff --git a/node-red/docs/changelog.md b/node-red/docs/changelog.md deleted file mode 100644 index 09aa1962..00000000 --- a/node-red/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "node-red/CHANGELOG.md" diff --git a/node-red/docs/index.md b/node-red/docs/index.md deleted file mode 100644 index 4885d4c5..00000000 --- a/node-red/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "node-red/README.md" diff --git a/node-red/docs/plugin_file.md b/node-red/docs/plugin_file.md deleted file mode 100644 index 4356a47b..00000000 --- a/node-red/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "node-red/plugin.json" -```` diff --git a/node-red/mkdocs.yml b/node-red/mkdocs.yml deleted file mode 100644 index 5c1a29c8..00000000 --- a/node-red/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/node-red - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/node-red/plugin.json b/node-red/plugin.json index 72bfb345..d3566a1e 100644 --- a/node-red/plugin.json +++ b/node-red/plugin.json @@ -2,7 +2,7 @@ "name" : "node-red", "version" : "1.9.2-1", "description" : "Plugin for Node-RED integration", - "author" : "Alvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "Node-RED", "description" : "Node-RED integration for Thinger.io", - "image" : "docs/assets/node-red-logo.svg", - "icon" : "https://raw.githubusercontent.com/thinger-io/plugins/main/node-red/docs/assets/node-red-icon.svg" + "category" : "development", + "image" : "assets/node-red-logo.svg", + "icon" : "assets/node-red-icon.svg" }, "tokens" : { "node_red_plugin" : { diff --git a/prometheus-exporter/LICENSE b/prometheus-exporter/LICENSE deleted file mode 100644 index 7bb3be2e..00000000 --- a/prometheus-exporter/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024-Current Thinger.io (INTERNET OF THINGER S.L.) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/prometheus-exporter/README.md b/prometheus-exporter/README.md index 9f7b0224..81028b9d 100644 --- a/prometheus-exporter/README.md +++ b/prometheus-exporter/README.md @@ -2,7 +2,7 @@ # Prometheus Exporter

- Prometheus logo + Prometheus logo

The Thinger.io Prometheus Exporter plugin is designed to facilitate the monitoring of your Thinger.io resources. It allows you to expose metrics in Prometheus formats through a dedicated endpoint. @@ -43,7 +43,7 @@ Here you can find an example for a metric that tracks the total of devices with Each metric can be tested out individually, and once validated and saved, the full application endpoint can be queried, through the `Endpoint Settings` tab, where all needed configuration to create a Prometheus scrape job can be found. - +![](assets/prometheus-exporter.png) For each application there are some settings that can be configured: @@ -51,7 +51,7 @@ For each application there are some settings that can be configured: - **Asynchronous Queries**: Enable or disable the Asynchronous queries for the metrics. - **Enable**: Enable or disable the metrics endpoint for the application. - +![](assets/settings.png) ## Additional Resources @@ -60,20 +60,3 @@ Given that the plugin is based and developed with Prometheus in mind, you may re Metrics are configured using the unofficial Prometheus client for Node.js, coded through the gui of the plugin. Also, the [Prometheus Server Plugin](https://marketplace.thinger.io/plugins/prometheus) is available within Thinger.io. - -## License - - - - - -The class is licensed under the [MIT License](http://opensource.org/licenses/MIT): - -Copyright © [Thinger.io](http://thinger.io) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/prometheus-exporter/assets/prometheus-exporter.png b/prometheus-exporter/assets/prometheus-exporter.png new file mode 100644 index 00000000..71676d6e Binary files /dev/null and b/prometheus-exporter/assets/prometheus-exporter.png differ diff --git a/prometheus-exporter/assets/prometheus-logo.svg b/prometheus-exporter/assets/prometheus-logo.svg new file mode 100644 index 00000000..48a53b6b --- /dev/null +++ b/prometheus-exporter/assets/prometheus-logo.svg @@ -0,0 +1,3 @@ + + +image/svg+xml \ No newline at end of file diff --git a/prometheus-exporter/assets/settings.png b/prometheus-exporter/assets/settings.png new file mode 100644 index 00000000..7684a61c Binary files /dev/null and b/prometheus-exporter/assets/settings.png differ diff --git a/prometheus-exporter/docs/changelog.md b/prometheus-exporter/docs/changelog.md deleted file mode 100644 index e45cdb97..00000000 --- a/prometheus-exporter/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "prometheus-exporter/CHANGELOG.md" diff --git a/prometheus-exporter/docs/index.md b/prometheus-exporter/docs/index.md deleted file mode 100644 index 30a4ae5b..00000000 --- a/prometheus-exporter/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "prometheus-exporter/README.md" diff --git a/prometheus-exporter/docs/plugin_file.md b/prometheus-exporter/docs/plugin_file.md deleted file mode 100644 index 9aa1db25..00000000 --- a/prometheus-exporter/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "prometheus-exporter/plugin.json" -```` diff --git a/prometheus-exporter/mkdocs.yml b/prometheus-exporter/mkdocs.yml deleted file mode 100644 index b7bdc545..00000000 --- a/prometheus-exporter/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/prometheus-exporter - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/prometheus-exporter/plugin.json b/prometheus-exporter/plugin.json index 30b9a824..004b73ee 100644 --- a/prometheus-exporter/plugin.json +++ b/prometheus-exporter/plugin.json @@ -2,7 +2,7 @@ "name" : "prometheus-exporter", "version" : "1.2.1", "description" : "Plugin for querying the status of Thinger resources and exporting them in a Prometheus format", - "author" : "Álvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "Prometheus Exporter", "description" : "Export Thinger's resource status in a Prometheus format", - "image" : "https://upload.wikimedia.org/wikipedia/commons/3/38/Prometheus_software_logo.svg", - "icon" : "https://upload.wikimedia.org/wikipedia/commons/3/38/Prometheus_software_logo.svg" + "category" : "monitoring", + "image" : "assets/prometheus-logo.svg", + "icon" : "assets/prometheus-logo.svg" }, "tokens" : { "prometheus_exporter_plugin" : { diff --git a/prometheus/README.md b/prometheus/README.md index cb299c66..21fd477f 100644 --- a/prometheus/README.md +++ b/prometheus/README.md @@ -2,7 +2,7 @@ # Prometheus

- Prometheus logo + Prometheus logo

Prometheus is an open-source monitoring system and time series database that allows you to collect and store metrics about your infrastructure and applications, and provides a powerful query language called PromQL that you can use to analyze and graph these metrics. @@ -21,21 +21,17 @@ In order for the connection to take place, edit the file by introducing your det This file can also be used to configure external systems scraping. -!!! note +> [!NOTE] +> Check out also the [Alertmanager plugin](https://marketplace.thinger.io/plugins/alertmanager) - Check out also the [Alertmanager plugin](https://marketplace.thinger.io/plugins/alertmanager) - -

- Prometheus integration showing a query over devices_total in Thinger.io -

+![Prometheus integration showing a query over devices_total in Thinger.io](assets/query.png) ## Integration with Grafana Grafana provides native support for Prometheus, therefore, we can configure a connection between the two plugins. -!!! note - - Refer to the [Grafana plugin page](https://marketplace.thinger.io/plugins/grafana) if you need more details on how to get started +> [!NOTE] +> Refer to the [Grafana plugin page](https://marketplace.thinger.io/plugins/grafana) if you need more details on how to get started Once grafana in installed and logged in, you may go to 'Settings'->'Data sources'. Click on `Add data source` and click on 'Prometheus'. The data to configure the Prometheus as data source you'll need: @@ -43,9 +39,7 @@ The data to configure the Prometheus as data source you'll need: And thats it, you may start now creating dashboard in grafana with prometheus as the backend. -

- Prometheus integration showing a devices_total dashboard in Grafana -

+![Prometheus integration showing a devices_total dashboard in Grafana](assets/grafana-dashboard.png) ## Official Documentation @@ -53,10 +47,5 @@ The Prometheus documentation is available at [prometheus.io/docs](https://promet More details regarding the configuration of Prometheus at [this link](https://prometheus.io/docs/prometheus/latest/configuration/configuration/). -!!! note - - Any configuration change in `prometheus.yml` requires a restart of the Prometheus plugin. - -## License - -Prometheus is distributed under the [Apache 2.0 License](https://prometheus.io/docs/introduction/faq/#what-license-is-prometheus-released-under). +> [!NOTE] +> Any configuration change in `prometheus.yml` requires a restart of the Prometheus plugin. diff --git a/prometheus/assets/grafana-dashboard.png b/prometheus/assets/grafana-dashboard.png new file mode 100644 index 00000000..6a414996 Binary files /dev/null and b/prometheus/assets/grafana-dashboard.png differ diff --git a/prometheus/assets/prometheus-logo.svg b/prometheus/assets/prometheus-logo.svg new file mode 100644 index 00000000..48a53b6b --- /dev/null +++ b/prometheus/assets/prometheus-logo.svg @@ -0,0 +1,3 @@ + + +image/svg+xml \ No newline at end of file diff --git a/prometheus/assets/query.png b/prometheus/assets/query.png new file mode 100644 index 00000000..7af150cf Binary files /dev/null and b/prometheus/assets/query.png differ diff --git a/prometheus/docs/changelog.md b/prometheus/docs/changelog.md deleted file mode 100644 index f80fa094..00000000 --- a/prometheus/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "prometheus/CHANGELOG.md" diff --git a/prometheus/docs/index.md b/prometheus/docs/index.md deleted file mode 100644 index 5a31f3ce..00000000 --- a/prometheus/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "prometheus/README.md" diff --git a/prometheus/docs/plugin_file.md b/prometheus/docs/plugin_file.md deleted file mode 100644 index 2fc4d557..00000000 --- a/prometheus/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "prometheus/plugin.json" -```` diff --git a/prometheus/mkdocs.yml b/prometheus/mkdocs.yml deleted file mode 100644 index 87b18d72..00000000 --- a/prometheus/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/prometheus - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/prometheus/plugin.json b/prometheus/plugin.json index a47b7b06..d0946daf 100644 --- a/prometheus/plugin.json +++ b/prometheus/plugin.json @@ -2,7 +2,7 @@ "name" : "prometheus", "version" : "2.46.0-1", "description" : "Prometheus monitoring system", - "author" : "Jaime Bautista", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "Prometheus", "description" : "Integration of Prometheus with Thinger.io", - "image" : "docs/assets/prometheus-logo.svg", - "icon" : "docs/assets/prometheus-logo.svg" + "category" : "monitoring", + "image" : "assets/prometheus-logo.svg", + "icon" : "assets/prometheus-logo.svg" }, "tokens" : { "prometheus_plugin_callback" : { diff --git a/rstudio/README.md b/rstudio/README.md index 71326f20..e4394eb4 100644 --- a/rstudio/README.md +++ b/rstudio/README.md @@ -2,16 +2,14 @@ # RStudio Web IDE

- RStudio logo + RStudio logo

RStudio® is an integrated development environment (IDE) for R, a programming language for statistical computing and graphics. The RStudio Web IDE plugin provides a web-based RStudio Server instance that can be accessed from the Thinger.io console. Integrating RStudio with Thinger.io can greatly benefit data scientists, IoT developers, and researchers who need to analyze and visualize IoT data effectively. This integration empowers professionals across various domains to make data-driven decisions and optimize their IoT solutions. -

- Thinger.io web console with Node-RED plugin and ad-hoc nodes -

+![Thinger.io web console with Node-RED plugin and ad-hoc nodes](assets/screen-front.png) ## Thinger.io and RStudio integration @@ -52,9 +50,3 @@ resp ## Additional Resources You may refer to the official website of R for additional resources and documentation: [R](https://www.r-project.org/), as well as the official website of RStudio for more information on RStudio IDE: [RStudio](https://www.rstudio.com/). - -## License - -R is distributed under the GNU GPL v2 license. RStudio is distributed under the AGPL v3 license. For more information, please refer to the official websites of [R](https://www.r-project.org/), the Rocker Dockerfiles used by the plugin are licensed under the GPL 2 or later. [RStudio](https://www.rstudio.com/). RStudio® is a registered trademark of RStudio, Inc. - -No affiliation with R, Rocker or RStudio is implied or intended by this plugin. \ No newline at end of file diff --git a/rstudio/assets/rstudio-icon.png b/rstudio/assets/rstudio-icon.png new file mode 100644 index 00000000..fe7e9070 Binary files /dev/null and b/rstudio/assets/rstudio-icon.png differ diff --git a/rstudio/assets/rstudio-image.png b/rstudio/assets/rstudio-image.png new file mode 100644 index 00000000..9f0754e8 Binary files /dev/null and b/rstudio/assets/rstudio-image.png differ diff --git a/rstudio/assets/rstudio-logo.svg b/rstudio/assets/rstudio-logo.svg new file mode 100644 index 00000000..15a1d2a3 --- /dev/null +++ b/rstudio/assets/rstudio-logo.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rstudio/assets/screen-front.png b/rstudio/assets/screen-front.png new file mode 100644 index 00000000..a4a282ab Binary files /dev/null and b/rstudio/assets/screen-front.png differ diff --git a/rstudio/docs/changelog.md b/rstudio/docs/changelog.md deleted file mode 100644 index 15a91a45..00000000 --- a/rstudio/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "rstudio/CHANGELOG.md" diff --git a/rstudio/docs/index.md b/rstudio/docs/index.md deleted file mode 100644 index 35a32af0..00000000 --- a/rstudio/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "rstudio/README.md" diff --git a/rstudio/docs/plugin_file.md b/rstudio/docs/plugin_file.md deleted file mode 100644 index dbdbeef8..00000000 --- a/rstudio/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "rstudio/plugin.json" -```` diff --git a/rstudio/mkdocs.yml b/rstudio/mkdocs.yml deleted file mode 100644 index c2a7d0b2..00000000 --- a/rstudio/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/rstudio - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/rstudio/plugin.json b/rstudio/plugin.json index 0b88595e..a18a9b35 100644 --- a/rstudio/plugin.json +++ b/rstudio/plugin.json @@ -2,7 +2,7 @@ "name" : "rstudio", "version" : "4.4.0-1", "description" : "rstudio", - "author" : "Jaime Bautista", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "RStudio", "description" : "RStudio Plugin for Thinger.io", - "image" : "https://www.rstudio.com/apple-touch-icon.png", - "icon" : "https://www.rstudio.com/favicon-32x32.png" + "category" : "development", + "image" : "assets/rstudio-image.png", + "icon" : "assets/rstudio-icon.png" }, "tokens" : { "rstudio_plugin" : { diff --git a/sftpgo/LICENSE b/sftpgo/LICENSE deleted file mode 100644 index f1e54eac..00000000 --- a/sftpgo/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-Current Thinger.io (INTERNET OF THINGER SL) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/sftpgo/README.md b/sftpgo/README.md index a4a920a5..c9d9312b 100644 --- a/sftpgo/README.md +++ b/sftpgo/README.md @@ -2,7 +2,7 @@ # SFTPGo

- Prometheus logo + SFTPGo logo

[SFTPGo](https://sftpgo.com/) extends capabilities of File Storages, adding support for SFTP, FTP and FTPS with individual user management. With this plugin you can leverage Thinger.io file storage backends for exchanging and storing files on the platform. @@ -13,19 +13,14 @@ SFTPGo has two different Web UIs, separated by admin and client users. To access When loaded, the WebClient will appear, but before being able to login with a user, we should login with the admin. -

- SFTPGo WebClient marking how to access the WebAdmin -

+![SFTPGo WebClient marking how to access the WebAdmin](assets/webclient-login.png) Click on the WebAdmin link and enter as username and password your Thinger.io username. -

- SFTPGo WebAdmin login -

+![SFTPGo WebAdmin login](assets/webadmin-login.png) -!!! important - - It is highly recommended to change the admin password to the same of the Thinger.io account or to one of your choice. +> [!IMPORTANT] +> It is highly recommended to change the admin password to the same of the Thinger.io account or to one of your choice. ## Creating Users @@ -37,9 +32,7 @@ When installing this plugin, a new file storage was created in Thinger.io with t We can then upload an example file and if we go to said file storage we will see the file structure. -

- File storage screen when using a common storage for data in SFTPGo -

+![File storage screen when using a common storage for data in SFTPGo](assets/sftpgo-common-storage.png) ### User an already created file storage @@ -47,9 +40,7 @@ When creating a user to share the files of an already existing file storage, the All storages will be mounted in `/srv/sftpgo/`, and to that path we would need to append the id of our storage. Example in the below screen. -

- SFTPGo Add user with dedicated storage -

+![SFTPGo Add user with dedicated storage](assets/sftpgo-dedicated-storage.png) ## Establish a Connection @@ -65,15 +56,13 @@ In order to connect with this protocol, the connection parameters are: ### FTP and FTPS -!!! warning - - It is recommended to always use SFTP protocol due to the design challenges of FTP/S +> [!WARNING] +> It is recommended to always use SFTP protocol due to the design challenges of FTP/S For FTP and FTP over TLS (FTPS) the supported connection is in Passive Mode. The upgrade to TLS of the control and data ports may be handled automatically by the FTP client using explicit FTP. -!!! tip - - Currently, only one data connection may be opened simultaneously +> [!TIP] +> Currently, only one data connection may be opened simultaneously The connection parameters are: @@ -85,14 +74,9 @@ On connection with an explicit FTP over TLS, the secure connection will be upgra This plugin only supports the TLS connection over the base domain name of the instance. -!!! warning - - If the TLS connection is not longer able to be established, restart the plugin manually in order for the certificates to reload +> [!WARNING] +> If the TLS connection is not longer able to be established, restart the plugin manually in order for the certificates to reload ## Official Documentation This plugin is based on the software SFTPGo. It's official documentation can be found under the [docs folder](https://github.com/drakkan/sftpgo/tree/main/docs) in its [official repository](https://github.com/drakkan/sftpgo). - -## License - -SFTPGo is distributed under the [AGPL-3.0 License](https://github.com/drakkan/sftpgo/blob/main/LICENSE) diff --git a/sftpgo/assets/sftpgo-common-storage.png b/sftpgo/assets/sftpgo-common-storage.png new file mode 100644 index 00000000..6c83401b Binary files /dev/null and b/sftpgo/assets/sftpgo-common-storage.png differ diff --git a/sftpgo/assets/sftpgo-dedicated-storage.png b/sftpgo/assets/sftpgo-dedicated-storage.png new file mode 100644 index 00000000..497d7a12 Binary files /dev/null and b/sftpgo/assets/sftpgo-dedicated-storage.png differ diff --git a/sftpgo/assets/sftpgo-icon.png b/sftpgo/assets/sftpgo-icon.png new file mode 100644 index 00000000..84a7bbc7 Binary files /dev/null and b/sftpgo/assets/sftpgo-icon.png differ diff --git a/sftpgo/assets/sftpgo-logo.png b/sftpgo/assets/sftpgo-logo.png new file mode 100644 index 00000000..2ada2e6b Binary files /dev/null and b/sftpgo/assets/sftpgo-logo.png differ diff --git a/sftpgo/assets/webadmin-login.png b/sftpgo/assets/webadmin-login.png new file mode 100644 index 00000000..6d8640de Binary files /dev/null and b/sftpgo/assets/webadmin-login.png differ diff --git a/sftpgo/assets/webclient-login.png b/sftpgo/assets/webclient-login.png new file mode 100644 index 00000000..5929c8ca Binary files /dev/null and b/sftpgo/assets/webclient-login.png differ diff --git a/sftpgo/docs/changelog.md b/sftpgo/docs/changelog.md deleted file mode 100644 index 81344fda..00000000 --- a/sftpgo/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "sftpgo/CHANGELOG.md" diff --git a/sftpgo/docs/index.md b/sftpgo/docs/index.md deleted file mode 100644 index f06a2f16..00000000 --- a/sftpgo/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "sftpgo/README.md" diff --git a/sftpgo/docs/plugin_file.md b/sftpgo/docs/plugin_file.md deleted file mode 100644 index 669240bf..00000000 --- a/sftpgo/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "sftpgo/plugin.json" -```` diff --git a/sftpgo/mkdocs.yml b/sftpgo/mkdocs.yml deleted file mode 100644 index bdb0fcef..00000000 --- a/sftpgo/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/sftpgo - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/sftpgo/plugin.json b/sftpgo/plugin.json index 228e7436..d52f48d1 100644 --- a/sftpgo/plugin.json +++ b/sftpgo/plugin.json @@ -2,7 +2,7 @@ "name" : "sftpgo", "version" : "2.5.4-1", "description" : "Plugin for SFTP access to file storages", - "author" : "Jaime Bautista", + "author": "Thinger.io", "repository" : { "type" : "git", "url" : "https://github.com/thinger-io/plugins.git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "SFTPGo", "description" : "SFTPGo Plugin", - "image" : "docs/assets/sftpgo-logo.png", - "icon" : "https://raw.githubusercontent.com/thinger-io/plugins/main/sftpgo/docs/assets/sftpgo-icon.png" + "category" : "infrastructure", + "image" : "assets/sftpgo-logo.png", + "icon" : "assets/sftpgo-icon.png" }, "resources" : { "storages": [ diff --git a/shelly-1l/README.md b/shelly-1l/README.md index dfd21aac..2d45e345 100644 --- a/shelly-1l/README.md +++ b/shelly-1l/README.md @@ -22,5 +22,5 @@ The Shelly 1L is a small and compact Wi-Fi smart switch that enables you to cont - Certification: CE, RoHS

- Shelly 1L picture + Shelly 1L

diff --git a/shelly-1l/assets/shelly-1l.png b/shelly-1l/assets/shelly-1l.png new file mode 100644 index 00000000..2530ac47 Binary files /dev/null and b/shelly-1l/assets/shelly-1l.png differ diff --git a/shelly-1l/docs/changelog.md b/shelly-1l/docs/changelog.md deleted file mode 100644 index d948ac05..00000000 --- a/shelly-1l/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "shelly-1l/CHANGELOG.md" diff --git a/shelly-1l/docs/index.md b/shelly-1l/docs/index.md deleted file mode 100644 index 0d44a7fb..00000000 --- a/shelly-1l/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "shelly-1l/README.md" diff --git a/shelly-1l/docs/plugin_file.md b/shelly-1l/docs/plugin_file.md deleted file mode 100644 index d7f1556d..00000000 --- a/shelly-1l/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "shelly-1l/plugin.json" -```` diff --git a/shelly-1l/mkdocs.yml b/shelly-1l/mkdocs.yml deleted file mode 100644 index 1d98697e..00000000 --- a/shelly-1l/mkdocs.yml +++ /dev/null @@ -1,7 +0,0 @@ - -site_name: plugins/shelly-1l - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/shelly-1l/plugin.json b/shelly-1l/plugin.json index 11e889b8..c725998e 100644 --- a/shelly-1l/plugin.json +++ b/shelly-1l/plugin.json @@ -12,7 +12,8 @@ "metadata": { "name": "Shelly 1L", "description": "Shelly 1L with Thinger.io", - "image": "docs/assets/shelly-1l.png" + "category": "devices", + "image": "assets/shelly-1l.png" }, "resources": { "products" : [ diff --git a/shelly-em/README.md b/shelly-em/README.md index 515043ae..4cab8446 100644 --- a/shelly-em/README.md +++ b/shelly-em/README.md @@ -23,6 +23,5 @@ Checkout the official documentation of the Shelly EM at this [link](https://shel - Operating temperature: -0 to + 40 °C

- Shelly EM picture + Shelly EM

- diff --git a/shelly-em/assets/shelly-em-clamp-50a.png b/shelly-em/assets/shelly-em-clamp-50a.png new file mode 100644 index 00000000..a2a6f3f5 Binary files /dev/null and b/shelly-em/assets/shelly-em-clamp-50a.png differ diff --git a/shelly-em/docs/changelog.md b/shelly-em/docs/changelog.md deleted file mode 100644 index b79f0925..00000000 --- a/shelly-em/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "shelly-em/CHANGELOG.md" diff --git a/shelly-em/docs/index.md b/shelly-em/docs/index.md deleted file mode 100644 index dbb96a06..00000000 --- a/shelly-em/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "shelly-em/README.md" diff --git a/shelly-em/docs/plugin_file.md b/shelly-em/docs/plugin_file.md deleted file mode 100644 index e394464e..00000000 --- a/shelly-em/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "shelly-em/plugin.json" -```` diff --git a/shelly-em/mkdocs.yml b/shelly-em/mkdocs.yml deleted file mode 100644 index 9dbecc59..00000000 --- a/shelly-em/mkdocs.yml +++ /dev/null @@ -1,7 +0,0 @@ - -site_name: plugins/shelly-em - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/shelly-em/plugin.json b/shelly-em/plugin.json index f30b2c77..8c72da68 100644 --- a/shelly-em/plugin.json +++ b/shelly-em/plugin.json @@ -12,7 +12,8 @@ "metadata": { "name": "Shelly EM", "description": "Shelly EM with Thinger.io", - "image": "docs/assets/shelly-em-clamp-50a.png" + "category": "devices", + "image": "assets/shelly-em-clamp-50a.png" }, "resources": { "products": [ diff --git a/shelly-plug-s/README.md b/shelly-plug-s/README.md index e3e03012..50042587 100644 --- a/shelly-plug-s/README.md +++ b/shelly-plug-s/README.md @@ -22,5 +22,5 @@ Shelly Plug can automatically monitor and control lighting, heating and any othe - Certification: CE

- Shelly Plug S picture + Shelly Plug S

diff --git a/shelly-plug-s/assets/shelly-plug-s.jpg b/shelly-plug-s/assets/shelly-plug-s.jpg new file mode 100644 index 00000000..c4946797 Binary files /dev/null and b/shelly-plug-s/assets/shelly-plug-s.jpg differ diff --git a/shelly-plug-s/docs/changelog.md b/shelly-plug-s/docs/changelog.md deleted file mode 100644 index dff69aaf..00000000 --- a/shelly-plug-s/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "shelly-plug-s/CHANGELOG.md" diff --git a/shelly-plug-s/docs/index.md b/shelly-plug-s/docs/index.md deleted file mode 100644 index 49aa0870..00000000 --- a/shelly-plug-s/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "shelly-plug-s/README.md" diff --git a/shelly-plug-s/docs/plugin_file.md b/shelly-plug-s/docs/plugin_file.md deleted file mode 100644 index 0ba454d1..00000000 --- a/shelly-plug-s/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "shelly-plug-s/plugin.json" -```` diff --git a/shelly-plug-s/mkdocs.yml b/shelly-plug-s/mkdocs.yml deleted file mode 100644 index 22b0218b..00000000 --- a/shelly-plug-s/mkdocs.yml +++ /dev/null @@ -1,7 +0,0 @@ - -site_name: plugins/shelly-plug-s - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/shelly-plug-s/plugin.json b/shelly-plug-s/plugin.json index 504e7955..cd05e1af 100644 --- a/shelly-plug-s/plugin.json +++ b/shelly-plug-s/plugin.json @@ -2,7 +2,7 @@ "name": "shelly-plug-s", "version": "1.0.1", "description": "Shelly Plug S", - "author": "Alvaro Luis Bustamante", + "author": "Thinger.io", "license": "MIT", "repository": { "type": "git", @@ -12,8 +12,9 @@ "metadata": { "name": "Shelly Plug S", "description": "Shelly Plug S", - "image": "docs/assets/shelly-plug-s.jpg", - "icon": "docs/assets/shelly-plug-s.jpg" + "category": "devices", + "image": "assets/shelly-plug-s.jpg", + "icon": "assets/shelly-plug-s.jpg" }, "resources": { "products": [ diff --git a/shelly-plus-1pm/README.md b/shelly-plus-1pm/README.md index a97776ec..12a736c1 100644 --- a/shelly-plus-1pm/README.md +++ b/shelly-plus-1pm/README.md @@ -22,6 +22,5 @@ Shelly Plus 1PM is a Wi-Fi enabled smart switch designed for efficient control o - Enclosure: UL94V-0 flame retardant polymer

- Shelly Plus 1PM + Shelly Plus 1PM

- diff --git a/shelly-plus-1pm/assets/shelly-plus-1pm.png b/shelly-plus-1pm/assets/shelly-plus-1pm.png new file mode 100644 index 00000000..ffe1914b Binary files /dev/null and b/shelly-plus-1pm/assets/shelly-plus-1pm.png differ diff --git a/shelly-plus-1pm/docs/changelog.md b/shelly-plus-1pm/docs/changelog.md deleted file mode 100644 index 413bc163..00000000 --- a/shelly-plus-1pm/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "shelly-plus-1pm/CHANGELOG.md" diff --git a/shelly-plus-1pm/docs/index.md b/shelly-plus-1pm/docs/index.md deleted file mode 100644 index 1acb4c41..00000000 --- a/shelly-plus-1pm/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "shelly-plus-1pm/README.md" diff --git a/shelly-plus-1pm/docs/plugin_file.md b/shelly-plus-1pm/docs/plugin_file.md deleted file mode 100644 index 2e5ce933..00000000 --- a/shelly-plus-1pm/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "shelly-plus-1pm/plugin.json" -```` diff --git a/shelly-plus-1pm/mkdocs.yml b/shelly-plus-1pm/mkdocs.yml deleted file mode 100644 index a3b9311a..00000000 --- a/shelly-plus-1pm/mkdocs.yml +++ /dev/null @@ -1,7 +0,0 @@ - -site_name: plugins/shelly-plus-1pm - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/shelly-plus-1pm/plugin.json b/shelly-plus-1pm/plugin.json index f22f2132..b79447bd 100644 --- a/shelly-plus-1pm/plugin.json +++ b/shelly-plus-1pm/plugin.json @@ -12,7 +12,8 @@ "metadata": { "name": "Shelly Plus 1PM", "description": "Shelly Plus 1PM with Thinger.io", - "image": "docs/assets/shelly-plus-1pm.png" + "category": "devices", + "image": "assets/shelly-plus-1pm.png" }, "resources": { "products": [ diff --git a/siemens-logo/README.md b/siemens-logo/README.md index 7d0a3b1d..ab7857f1 100644 --- a/siemens-logo/README.md +++ b/siemens-logo/README.md @@ -2,7 +2,7 @@ # Siemens LOGO!

- Siemens LOGO! + Siemens LOGO!

Siemens LOGO! is a highly versatile logic module known for its simplicity, flexibility, and reliability. Widely used in industrial automation, building control, and various other applications, Siemens LOGO! allows for easy implementation of control tasks without requiring extensive programming knowledge. @@ -11,10 +11,9 @@ Siemens LOGO! is a highly versatile logic module known for its simplicity, flexi Integrating Siemens LOGO! with Thinger.io can significantly enhance its capabilities by enabling remote monitoring, control, and automation of LOGO!-based systems. This integration empowers users to access, visualize, and manage LOGO! data remotely through Thinger.io platform, enabling real-time monitoring, data analysis, and automation of LOGO!-based applications. -!!! note "Siemens LOGO! minimum requirements" - - Siemens LOGO! 8.4 or later is required to use this plugin, as it supports open MQTT communication. [More info](https://support.industry.siemens.com/cs/document/109826554/sales-release-for-logo!-8-4-basic-devices-and-logo!-soft-comfort-v8-4-?dti=0) - Siemens LOGO! Soft Comfort V8.4 or later is required to configure the MQTT communication. [More info](https://support.industry.siemens.com/cs/document/109826553/download-and-installation-instructions-for-logo!-soft-comfort-upgrade-v8-4?dti=0) +> [!NOTE] Siemens LOGO! minimum requirements +> Siemens LOGO! 8.4 or later is required to use this plugin, as it supports open MQTT communication. [More info](https://support.industry.siemens.com/cs/document/109826554/sales-release-for-logo!-8-4-basic-devices-and-logo!-soft-comfort-v8-4-?dti=0) +> Siemens LOGO! Soft Comfort V8.4 or later is required to configure the MQTT communication. [More info](https://support.industry.siemens.com/cs/document/109826553/download-and-installation-instructions-for-logo!-soft-comfort-upgrade-v8-4?dti=0) The plugin provides a first step to be able to integrate a fleet of Siemens LOGO! controllers, while allowing the ad-hoc configuration that best suits the user's needs. @@ -28,10 +27,8 @@ It provides the following settings: - Basic **product functions** to parse the shadow of the device and handling sending commands to the Siemens LOGO! controller. - A preconfigured **dashboard** to visualize relevant data of each Siemens LOGO! controller. -

- Time Settings in Soft Comfort software for Siemens LOGO! - Time Settings in Soft Comfort software for Siemens LOGO! -

+![Time Settings in Soft Comfort software for Siemens LOGO!](assets/12_product_profile_1.png) +![Time Settings in Soft Comfort software for Siemens LOGO!](assets/13_product_profile_2.png) ## Get Started @@ -45,23 +42,17 @@ Ensure that your Siemens LOGO! device is connected to the Internet, either throu Set the time and date on the Siemens LOGO! through the menu `Tools -> Transfer -> Set Time...`. -

- Time Settings in Soft Comfort software for Siemens LOGO! -

+![Time Settings in Soft Comfort software for Siemens LOGO!](assets/1_time_settings.png) #### Configure MQTT Communication Configure the Siemens LOGO! device to communicate with Thinger.io platform using MQTT protocol. You will need to provide the MQTT broker address, port, username, and password. Go to the menu option `Tools -> Transfer -> Cloud settings -> Cloud connection settings` and configure the settings as shown below: 1. In the new dialog that appears on screen, check the `Active Cloud Access`. Then, click on `Register object`. -

- Time Settings in Soft Comfort software for Siemens LOGO! -

+ ![Time Settings in Soft Comfort software for Siemens LOGO!](assets/3_cloud_settings_2.png) 2. Select the `Type of cloud` as `MQTT` and click on `Next`. -

- Time Settings in Soft Comfort software for Siemens LOGO! -

+ ![Time Settings in Soft Comfort software for Siemens LOGO!](assets/4_cloud_settings_3.png) 3. Now set the broker URL, port, device id, username, and device password. * Broker URL: domain name of the instance of Thinger.io. If you use the Community Console, the URL should be `backend.thinger.io`. If you use a private instance use your own URL. @@ -69,28 +60,20 @@ Configure the Siemens LOGO! device to communicate with Thinger.io platform using * Device ID: Identifier that we will give to the device in the Thinger.io platform and which must be unique. The current autoprovision rule is to start with 'logo-', which can be followed by the serial number of the device for example. * Username: Username of the Thinger.io platform that will own the device resource in the system. * Device Password: Password of the device the Thinger.io platform will use to authenticate the device. It will be configured on the platform on autoprovision. -

- Time Settings in Soft Comfort software for Siemens LOGO! -

+ ![Time Settings in Soft Comfort software for Siemens LOGO!](assets/5_cloud_settings_4.png) 4. In this next step, we will select for `Authenticacion Type` the option `Unilateral TLS`. The certificate CA de broker uses can be download from this [link](https://letsencrypt.org/certificates/), specifically the pem certificate `ISRG Root X1`, valid until 2023-06-04. -

- Time Settings in Soft Comfort software for Siemens LOGO! - Time Settings in Soft Comfort software for Siemens LOGO! -

+ ![Time Settings in Soft Comfort software for Siemens LOGO!](assets/6_certificate.png) + ![Time Settings in Soft Comfort software for Siemens LOGO!](assets/7_cloud_settings_5.png) 5. In the next dialog screen we will configure the MQTT topics in which the Siemens LOGO! publishes and is subscribed. * Publish topic: topic where the LOGO! will send data to be read form the Thinger.io Cloud platform. We suggest the following structure: `logo//tx` * Subscribe to topic: topic where the LOGO! will receive data sent by the Thinger.io Cloud platform. We suggest the following structure: `logo//rx` -

- Time Settings in Soft Comfort software for Siemens LOGO! -

+ ![Time Settings in Soft Comfort software for Siemens LOGO!](assets/8_cloud_settings_6.png) 6. After a few seconds, the device will autoprovision itself on Thinger.io Platform. You can check the device status on the Thinger.io Console. -

- Time Settings in Soft Comfort software for Siemens LOGO! - Time Settings in Soft Comfort software for Siemens LOGO! -

+ ![Time Settings in Soft Comfort software for Siemens LOGO!](assets/9_cloud_settings_7.png) + ![Time Settings in Soft Comfort software for Siemens LOGO!](assets/10_thinger_settings_1.png) ### Configure Data Exchange @@ -99,9 +82,7 @@ Configure the data registers in the Siemens LOGO! device that you want to exchan * In the following image, the two registers will be sent to the publish topic (`logo//tx`) configured in the previous step, when their value changes. * To send data from the platform to the controller, the register must be set with `write permissions`. In this case the register M1. -

- Time Settings in Soft Comfort software for Siemens LOGO! -

+![Time Settings in Soft Comfort software for Siemens LOGO!](assets/11_cloud_settings_8.png) By following all these steps the plugin will be fully configured, and it is time to begin the configuration of the Product for your own project requirements. @@ -119,21 +100,3 @@ There are several resources available with both the Siemens LOGO! and Thinger.io * [Siemens LOGO! System Manual](https://support.industry.siemens.com/cs/document/109826499/logo!?dti=0) * [Siemens LOGO! Support Forum](https://support.industry.siemens.com/forum/es/en/conf/65/) * [Thinger.io Products Documentation](https://docs.thinger.io/business-features/products) - -## License - -No affiliation with Siemens is implied or intended by this plugin. - - - - - -The plugin is licensed under the [MIT License](http://opensource.org/licenses/MIT): - -Copyright © [Thinger.io](http://thinger.io) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/siemens-logo/assets/10_thinger_settings_1.png b/siemens-logo/assets/10_thinger_settings_1.png new file mode 100644 index 00000000..e4ed8c84 Binary files /dev/null and b/siemens-logo/assets/10_thinger_settings_1.png differ diff --git a/siemens-logo/assets/11_cloud_settings_8.png b/siemens-logo/assets/11_cloud_settings_8.png new file mode 100644 index 00000000..cfd39e0b Binary files /dev/null and b/siemens-logo/assets/11_cloud_settings_8.png differ diff --git a/siemens-logo/assets/12_product_profile_1.png b/siemens-logo/assets/12_product_profile_1.png new file mode 100644 index 00000000..ec95c582 Binary files /dev/null and b/siemens-logo/assets/12_product_profile_1.png differ diff --git a/siemens-logo/assets/13_product_profile_2.png b/siemens-logo/assets/13_product_profile_2.png new file mode 100644 index 00000000..09f7884c Binary files /dev/null and b/siemens-logo/assets/13_product_profile_2.png differ diff --git a/siemens-logo/assets/1_time_settings.png b/siemens-logo/assets/1_time_settings.png new file mode 100644 index 00000000..77b578ac Binary files /dev/null and b/siemens-logo/assets/1_time_settings.png differ diff --git a/siemens-logo/assets/3_cloud_settings_2.png b/siemens-logo/assets/3_cloud_settings_2.png new file mode 100644 index 00000000..20bfb236 Binary files /dev/null and b/siemens-logo/assets/3_cloud_settings_2.png differ diff --git a/siemens-logo/assets/4_cloud_settings_3.png b/siemens-logo/assets/4_cloud_settings_3.png new file mode 100644 index 00000000..dfc6e59e Binary files /dev/null and b/siemens-logo/assets/4_cloud_settings_3.png differ diff --git a/siemens-logo/assets/5_cloud_settings_4.png b/siemens-logo/assets/5_cloud_settings_4.png new file mode 100644 index 00000000..6048500c Binary files /dev/null and b/siemens-logo/assets/5_cloud_settings_4.png differ diff --git a/siemens-logo/assets/6_certificate.png b/siemens-logo/assets/6_certificate.png new file mode 100644 index 00000000..5847f8a9 Binary files /dev/null and b/siemens-logo/assets/6_certificate.png differ diff --git a/siemens-logo/assets/7_cloud_settings_5.png b/siemens-logo/assets/7_cloud_settings_5.png new file mode 100644 index 00000000..a31b77fb Binary files /dev/null and b/siemens-logo/assets/7_cloud_settings_5.png differ diff --git a/siemens-logo/assets/8_cloud_settings_6.png b/siemens-logo/assets/8_cloud_settings_6.png new file mode 100644 index 00000000..8431bf50 Binary files /dev/null and b/siemens-logo/assets/8_cloud_settings_6.png differ diff --git a/siemens-logo/assets/9_cloud_settings_7.png b/siemens-logo/assets/9_cloud_settings_7.png new file mode 100644 index 00000000..5194cc3e Binary files /dev/null and b/siemens-logo/assets/9_cloud_settings_7.png differ diff --git a/siemens-logo/assets/logo-plc.svg b/siemens-logo/assets/logo-plc.svg new file mode 100644 index 00000000..a0ad7b22 --- /dev/null +++ b/siemens-logo/assets/logo-plc.svg @@ -0,0 +1,382 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/siemens-logo/docs/changelog.md b/siemens-logo/docs/changelog.md deleted file mode 100644 index e7c17a2e..00000000 --- a/siemens-logo/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "siemens-logo/CHANGELOG.md" diff --git a/siemens-logo/docs/index.md b/siemens-logo/docs/index.md deleted file mode 100644 index 9e7edd08..00000000 --- a/siemens-logo/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "siemens-logo/README.md" diff --git a/siemens-logo/docs/plugin_file.md b/siemens-logo/docs/plugin_file.md deleted file mode 100644 index b62902aa..00000000 --- a/siemens-logo/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "siemens-logo/plugin.json" -```` diff --git a/siemens-logo/mkdocs.yml b/siemens-logo/mkdocs.yml deleted file mode 100644 index 73564178..00000000 --- a/siemens-logo/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/siemens-logo - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/siemens-logo/plugin.json b/siemens-logo/plugin.json index b0775baf..cf739e10 100644 --- a/siemens-logo/plugin.json +++ b/siemens-logo/plugin.json @@ -12,7 +12,8 @@ "metadata": { "name": "Siemens LOGO!", "description": "Siemens LOGO! integration with Thinger.io", - "image": "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/siemens_logo/img/logo_plc.svg" + "category": "devices", + "image": "assets/logo-plc.svg" }, "resources": { "products": [ diff --git a/sigfox/README.md b/sigfox/README.md index 42ecb6e6..e1d69cc2 100644 --- a/sigfox/README.md +++ b/sigfox/README.md @@ -2,12 +2,10 @@ # Sigfox

- Sigfox logo + Sigfox logo

-

- Sigfox diagram with Thinger.io integration -

+![Sigfox diagram with Thinger.io integration](assets/sigfox-diagram.png) This plugin is an interface for using Sigfox HTTP Callback feature in an optimized way, providing features to easily integrate these devices with Thinger.io Platform, such as automatic device and storage provisioning and both uplink and downlink cloud processing. @@ -29,9 +27,8 @@ For a better understanding of the following sections, here is described some bas * Downlink: It is a data flow which represents messages sent from the Sigfox cloud to a device. * Device Type: It is a concept that defines a group of devices of the same type, normally sending the same kind of data both in uplink and downlink). -!!! info - - Learn how to configure Sigfox Callback to send data to Thinger.io platform [**here**](https://docs.thinger.io/lpwan/sigfox#creating-sigfox-callback) +> [!NOTE] +> Learn how to configure Sigfox Callback to send data to Thinger.io platform [**here**](https://docs.thinger.io/lpwan/sigfox#creating-sigfox-callback) ## Plugin Configuration @@ -41,23 +38,18 @@ In this section it is described the different interfaces that can be used to con Every Sigfox "Device Type" that is integrated over this plugin, should define a new profile in Thinger.io plugin, with the same identifier as defined in Sigfox Platform. Note that each Sigfox Device Type defined in this way will allow to customize the plugin behaviour for that kind of devices. -

- Sigfox GUI application settings -

+![Sigfox GUI application settings](assets/application-settings.png) It is possible to create as many Device Types profiles as required. To configure a new profile, just select the id from the Device Types dropdown, and then navigate to the other plugin sections. -!!! warning - - Always create the Device Type with the same identifier as defined in Sigfox cloud. +> [!WARNING] +> Always create the Device Type with the same identifier as defined in Sigfox cloud. ### Callback Config The uplink behaviour allows to configure how the plugin will react on new information received from Sigfox. -

- Sigfox GUI callback configuration -

+![Sigfox GUI callback configuration](assets/callback-configuration.png) The configurable parameters are the following: @@ -74,9 +66,7 @@ In this section it is possible to configure the payload processors that will tra The interface provides a code editor for NodeJS, where it is possible to define the `uplink` and `downlink`processors. It is also possible to test the code by providing a sample input data both for `uplink` and `downlink`. -

- Sigfox GUI payload processing -

+![Sigfox GUI payload processing](assets/payload-processing.png) In the following, there is information about the uplink and downlink methods. @@ -98,9 +88,8 @@ In the following, there is information about the uplink and downlink methods. }; ``` - !!! info - - The uplink method must always return a JSON object. + > [!NOTE] + > The uplink method must always return a JSON object. === "Downlink" @@ -113,9 +102,8 @@ In the following, there is information about the uplink and downlink methods. The output of this method will be a **Base64 String** with the binary information that is going to be sent to Sigfox network. - !!! warning - - When working with Downlink processes the`Initialize Downlink Data`parameter must be initialized with any data otherwise the processing of the downlink payload will fail + > [!WARNING] + > When working with Downlink processes the`Initialize Downlink Data`parameter must be initialized with any data otherwise the processing of the downlink payload will fail Example of a downlink method converting a JSON device configuration into base64 as required by Sigfox: @@ -134,17 +122,14 @@ In the following, there is information about the uplink and downlink methods. }; ``` -!!! info - - Use the interface tester to see if your code is correctly procesing the payloads. +> [!NOTE] +> Use the interface tester to see if your code is correctly procesing the payloads. ## Sigfox Cloud Configuration After getting Thinger.io Plugin ready for receiving data, the next step is to configure the Sigfox Backend for pushing messages on it. This process has been simplified by the "Sigfox Integration" section of the plugin settings menu, that automatically builds the **HTTP query** and the **Authorization token** that needs to be included into Sigfox Callbacks Manager: -

- Sigfox GUI webhook settings -

+![Sigfox GUI webhook settings](assets/webhook-settings.png) Note that Sigfox Cloud provides three different API, depending on the kind of interaction that it's going to be created, Thinger.io Platform allows integrating with all of them: Uplink, Advanced Data and Downlink callback type. Each callback configuration can be created to work with a single device or over all the devices that belongs to the same Device Type. @@ -161,35 +146,26 @@ These are all the parameters that needs to be configured when creating a new cal This callback configuration is the most common one, it just send devices data to Thinger.io in an unidirectional way to be stored, aggregate and show. -

- Sigfox Cloud callbacks settings -

+![Sigfox Cloud callbacks settings](assets/sigfox-cloud-callbacks.png) ### Uplink with computed location (Data Advanced) This callback configuration allows to retrieve additional information, such as geolocation, and infrastructure metadata like the computed over the different base stations which received the messages. However, working with this callback requires a delay of approximately 30s. -

- Sigfox Cloud callbacks settings -

+![Sigfox Cloud callbacks settings](assets/sigfox-cloud-callbacks_2.png) ### Uplink & Downlink Finally, selecting DATA/BIDIR Callback, it is possible to send uplink messages to configure Sigfox Cloud to send data to Thinger.io and waits for a downlink message that can be sent to the device in order to make any configuration or remote controlling processes. downlink payload processing -

- Sigfox Cloud callbacks settings -

+![Sigfox Cloud callbacks settings](assets/sigfox-cloud-callbacks_3.png) The configuration of the Downlink message payload can be made in Thinger.io Platform, using the Callback Config section of Sigfox Plugin or modifying each individual Downlink device property in the device dashboard: -

- Sigfox GUI callbacks configuration downlink -

+![Sigfox GUI callbacks configuration downlink](assets/callback-configuration-downlink.png) -!!! tip - - It is mandatory to send the device in the callback payload. Other optional values are signal or location information extracted from Sigfox. +> [!TIP] +> It is mandatory to send the device in the callback payload. Other optional values are signal or location information extracted from Sigfox. ```json { @@ -212,9 +188,7 @@ congratulations now you can retrieve data from all your Sigfox devices into Thin As these devices are not real-time connected to the platform, it is hard to know if everything is running well in the long term, specially in large devices networks. But thanks to the timeout feature that is configurable using the "device connection timeout" of the plugin callback configuration menu, it is possible to know if a device is sending data in proper time frame or if otherwise it has stopped working due to any problem by checking the "device status" that is available at its dashboard. -

- Checking Sigfox device status and location with Thinger.io -

+![Checking Sigfox device status and location with Thinger.io](assets/device-dashboard.png) It is also possible to check the status of a large amount of devices from the device list, but note that this page is not showing real-time data so it is necessary to refresh it to know the current status. @@ -222,15 +196,11 @@ It is also possible to check the status of a large amount of devices from the de One of the most used features of Thigner.io is the data buckets, which allows storing thousands of data in a scalable and easy way. The integration with sigfox through this new plugin allows to store data after the payload has been processed instead of storing it in raw or constraint format. -

- Storing Sigfox data in scalable Buckets -

+![Storing Sigfox data in scalable Buckets](assets/sigfox-bucket.png) Once stored in a data bucket, it is possible to work with these data points by downloading them in CSV and creating widgets to show data in customizable dashboards that can be easily shared with colleagues or customers. -

- Showing IoT data with Thinger.io Dashboards -

+![Showing IoT data with Thinger.io Dashboards](assets/thinger-dashboard.png) ### Sending Data to Third Parties with Endpoints @@ -240,9 +210,7 @@ Thinger.io is part of a large ecosystem of technologies and platforms that enabl at thinger.io we continue to work to extend the platform's integration capabilities with technologies that can help create more advanced IoT projects in the easiest way. We have other plugins such as NodeRED that allow working with IoT data in almost unlimited ways, such as creating alerts on the data, geofencing, reporting, etc. -

- Sigfox device callback endpoint -

+![Sigfox device callback endpoint](assets/device-callback.png) ## Plugin Development Details @@ -250,9 +218,7 @@ at thinger.io we continue to work to extend the platform's integration capabilit In this section it is described how the uplink data flow works, from its source in the Sigfox network, to its final destination in Thinger.io. -

- Sigfox uplink dataflow with Thinger.io integration -

+![Sigfox uplink dataflow with Thinger.io integration](assets/uplink-dataflow.png) In the following subsections are described the elements shown in the figure. @@ -293,9 +259,7 @@ The last step of this plugin is to call the device callback in Thinger.io. This In this case, the plugin interacts with the platform over such REST interface, pushing data received from Sigfox, and processed by the custom uplink method. By default, the plugin initializes an HTTP device to write to a data bucket that is also automatically created. So, every message sent by a Sigfox device, will write finally write to a specific data bucket. As shown in the following picture: -

- Sigfox device callback settings in Thinger.io -

+![Sigfox device callback settings in Thinger.io](assets/device-callback-settings.png) After the device callback is done, it will appear as a connected device, showing also its location if it was configured in the plugin options. diff --git a/sigfox/assets/application-settings.png b/sigfox/assets/application-settings.png new file mode 100644 index 00000000..c79a968b Binary files /dev/null and b/sigfox/assets/application-settings.png differ diff --git a/sigfox/assets/callback-configuration-downlink.png b/sigfox/assets/callback-configuration-downlink.png new file mode 100644 index 00000000..0c044b8c Binary files /dev/null and b/sigfox/assets/callback-configuration-downlink.png differ diff --git a/sigfox/assets/callback-configuration.png b/sigfox/assets/callback-configuration.png new file mode 100644 index 00000000..cb3f5819 Binary files /dev/null and b/sigfox/assets/callback-configuration.png differ diff --git a/sigfox/assets/device-callback-settings.png b/sigfox/assets/device-callback-settings.png new file mode 100644 index 00000000..9ccc7efc Binary files /dev/null and b/sigfox/assets/device-callback-settings.png differ diff --git a/sigfox/assets/device-callback.png b/sigfox/assets/device-callback.png new file mode 100644 index 00000000..5ca79ff1 Binary files /dev/null and b/sigfox/assets/device-callback.png differ diff --git a/sigfox/assets/device-dashboard.png b/sigfox/assets/device-dashboard.png new file mode 100644 index 00000000..7cc4c91e Binary files /dev/null and b/sigfox/assets/device-dashboard.png differ diff --git a/sigfox/assets/payload-processing.png b/sigfox/assets/payload-processing.png new file mode 100644 index 00000000..d7d6508f Binary files /dev/null and b/sigfox/assets/payload-processing.png differ diff --git a/sigfox/assets/sigfox-bucket.png b/sigfox/assets/sigfox-bucket.png new file mode 100644 index 00000000..86b47f4e Binary files /dev/null and b/sigfox/assets/sigfox-bucket.png differ diff --git a/sigfox/assets/sigfox-cloud-callbacks.png b/sigfox/assets/sigfox-cloud-callbacks.png new file mode 100644 index 00000000..91bc95b3 Binary files /dev/null and b/sigfox/assets/sigfox-cloud-callbacks.png differ diff --git a/sigfox/assets/sigfox-cloud-callbacks_2.png b/sigfox/assets/sigfox-cloud-callbacks_2.png new file mode 100644 index 00000000..1fe394ed Binary files /dev/null and b/sigfox/assets/sigfox-cloud-callbacks_2.png differ diff --git a/sigfox/assets/sigfox-cloud-callbacks_3.png b/sigfox/assets/sigfox-cloud-callbacks_3.png new file mode 100644 index 00000000..772ce1c9 Binary files /dev/null and b/sigfox/assets/sigfox-cloud-callbacks_3.png differ diff --git a/sigfox/assets/sigfox-diagram.png b/sigfox/assets/sigfox-diagram.png new file mode 100644 index 00000000..67ee81a5 Binary files /dev/null and b/sigfox/assets/sigfox-diagram.png differ diff --git a/sigfox/assets/sigfox-icon.png b/sigfox/assets/sigfox-icon.png new file mode 100644 index 00000000..fe97840a Binary files /dev/null and b/sigfox/assets/sigfox-icon.png differ diff --git a/sigfox/assets/sigfox-image.jpg b/sigfox/assets/sigfox-image.jpg new file mode 100644 index 00000000..d719747f Binary files /dev/null and b/sigfox/assets/sigfox-image.jpg differ diff --git a/sigfox/assets/sigfox-logo.png b/sigfox/assets/sigfox-logo.png new file mode 100644 index 00000000..1835c22b Binary files /dev/null and b/sigfox/assets/sigfox-logo.png differ diff --git a/sigfox/assets/thinger-dashboard.png b/sigfox/assets/thinger-dashboard.png new file mode 100644 index 00000000..17418efa Binary files /dev/null and b/sigfox/assets/thinger-dashboard.png differ diff --git a/sigfox/assets/uplink-dataflow.png b/sigfox/assets/uplink-dataflow.png new file mode 100644 index 00000000..199cf98f Binary files /dev/null and b/sigfox/assets/uplink-dataflow.png differ diff --git a/sigfox/assets/webhook-settings.png b/sigfox/assets/webhook-settings.png new file mode 100644 index 00000000..46442579 Binary files /dev/null and b/sigfox/assets/webhook-settings.png differ diff --git a/sigfox/docs/changelog.md b/sigfox/docs/changelog.md deleted file mode 100644 index f9c3d615..00000000 --- a/sigfox/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "sigfox/CHANGELOG.md" diff --git a/sigfox/docs/index.md b/sigfox/docs/index.md deleted file mode 100644 index 2b561074..00000000 --- a/sigfox/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "sigfox/README.md" diff --git a/sigfox/docs/plugin_file.md b/sigfox/docs/plugin_file.md deleted file mode 100644 index 884d19ce..00000000 --- a/sigfox/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "sigfox/plugin.json" -```` diff --git a/sigfox/mkdocs.yml b/sigfox/mkdocs.yml deleted file mode 100644 index 40f1b0d4..00000000 --- a/sigfox/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/sigfox - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/sigfox/plugin.json b/sigfox/plugin.json index 635aa54c..e38b72e8 100644 --- a/sigfox/plugin.json +++ b/sigfox/plugin.json @@ -2,7 +2,7 @@ "name" : "sigfox", "version" : "1.4.1", "description" : "Plugin for handling Sigfox integration", - "author" : "Alvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "Sigfox", "description" : "Sigfox Integration", - "image" : "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/sigfox/sigfox-image.jpg", - "icon" : "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/sigfox/sigfox-icon.png" + "category" : "connectivity", + "image" : "assets/sigfox-image.jpg", + "icon" : "assets/sigfox-icon.png" }, "tokens" : { "sigfox_plugin" : { diff --git a/teltonika-eye/LICENSE.md b/teltonika-eye/LICENSE.md deleted file mode 100644 index f1bacd61..00000000 --- a/teltonika-eye/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2025 Thinger.io - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/teltonika-eye/README.md b/teltonika-eye/README.md index 517cb402..5f782c2b 100644 --- a/teltonika-eye/README.md +++ b/teltonika-eye/README.md @@ -40,7 +40,6 @@ Provide any additional resources, documents, or links to external sites that can - [Teltonika Support Forum](https://community.teltonika.lt/) - [Thinger.io Community](https://community.thinger.io/) - ## FAQ -Include a section for frequently asked questions to address common issues or queries. \ No newline at end of file +Include a section for frequently asked questions to address common issues or queries. diff --git a/teltonika-eye/docs/changelog.md b/teltonika-eye/docs/changelog.md deleted file mode 100644 index a0b978d7..00000000 --- a/teltonika-eye/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "teltonika-eye/CHANGELOG.md" diff --git a/teltonika-eye/docs/index.md b/teltonika-eye/docs/index.md deleted file mode 100644 index 191d5b55..00000000 --- a/teltonika-eye/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "teltonika-eye/README.md" diff --git a/teltonika-eye/docs/plugin_file.md b/teltonika-eye/docs/plugin_file.md deleted file mode 100644 index 63424e9e..00000000 --- a/teltonika-eye/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "teltonika-eye/plugin.json" -```` diff --git a/teltonika-eye/mkdocs.yml b/teltonika-eye/mkdocs.yml deleted file mode 100644 index 746b884f..00000000 --- a/teltonika-eye/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/teltonika-eye - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/teltonika-eye/plugin.json b/teltonika-eye/plugin.json index 46f570c8..1cc05624 100644 --- a/teltonika-eye/plugin.json +++ b/teltonika-eye/plugin.json @@ -12,6 +12,7 @@ "metadata": { "name": "Teltonika Eye", "description": "Decoder and preconfiguration for Teltonika Eye devices", + "category": "devices", "image": "assets/teltonika-eye.png" }, "resources": { diff --git a/teltonika-telematics/LICENSE.md b/teltonika-telematics/LICENSE.md deleted file mode 100644 index f1bacd61..00000000 --- a/teltonika-telematics/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2025 Thinger.io - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/teltonika-telematics/README.md b/teltonika-telematics/README.md index c5cdb486..bb84ad90 100644 --- a/teltonika-telematics/README.md +++ b/teltonika-telematics/README.md @@ -1,6 +1,8 @@ # Teltonika Telematics -!['teltonika_telematics.png'](/plugins/teltonika-telematics/assets/teltonika_telematics.png) +

+ Teltonika Telematics +

The Teltonika Telematics Plugin for Thinger.io enables the integration of Teltonika's GPS tracking devices with the Thinger.io Platform. This plugin facilitates real-time monitoring, data visualization, and management of your Teltonika devices within the Thinger.io ecosystem. @@ -32,7 +34,7 @@ Afterwards create some generic certificates with the following commands, configu openssl req -x509 -newkey rsa:4096 -keyout private.pem.key -out cert.pem.crt -sha256 -days 9125 -nodes ``` -!['certificate_creation.png'](/plugins/teltonika-telematics/assets/certificate_creation.png) +!['certificate_creation.png'](assets/certificate_creation.png) ### Configuration @@ -46,15 +48,15 @@ Once the device is connected, the following changes need to be done: - In the `Security Tab`, upload the device certificates. -!['teltonika_configurator_security.png'](/plugins/teltonika-telematics/assets/teltonika_configurator_security.png) +!['teltonika_configurator_security.png'](assets/teltonika_configurator_security.png) - In the `GPRS Tab`, set the Server Settings box with your Thinger.io instance domain, the Port `8883`, Protocol `MQTT` and TLS Encryption `TLS/DTLS`. Then in the MQTT Settings box, select `AWS IoT Custom` and leave the default settings as shown in the image. -!['teltonika_configurator_gprs.png'](/plugins/teltonika-telematics/assets/teltonika_configurator_gprs.png) +!['teltonika_configurator_gprs.png'](assets/teltonika_configurator_gprs.png) After that, copy the device IMEI from the status page, as it will be the device Id in Thinger.io. -!['teltonika_configurator_status.png'](/plugins/teltonika-telematics/assets/teltonika_configurator_status.png) +!['teltonika_configurator_status.png'](assets/teltonika_configurator_status.png) After the configuration is done, make sure to reboot the device. @@ -62,7 +64,7 @@ After the configuration is done, make sure to reboot the device. Create a new device through the `Devices` menu, of type HTTP, the Id the IMEI of the device and assign it to the product `Teltonika Telematics`. At this point assign it random credentials, they will not be used in favor of the certificates. -!['teltonika_mqtt_device.png'](/plugins/teltonika-telematics/assets/teltonika_mqtt_device.png) +!['teltonika_mqtt_device.png'](assets/teltonika_mqtt_device.png) ### Usage diff --git a/teltonika-telematics/assets/certificate_creation.png b/teltonika-telematics/assets/certificate_creation.png new file mode 100644 index 00000000..6441bf47 Binary files /dev/null and b/teltonika-telematics/assets/certificate_creation.png differ diff --git a/teltonika-telematics/assets/teltonika_configurator_gprs.png b/teltonika-telematics/assets/teltonika_configurator_gprs.png new file mode 100644 index 00000000..c16c26ae Binary files /dev/null and b/teltonika-telematics/assets/teltonika_configurator_gprs.png differ diff --git a/teltonika-telematics/assets/teltonika_configurator_security.png b/teltonika-telematics/assets/teltonika_configurator_security.png new file mode 100644 index 00000000..74ae6a50 Binary files /dev/null and b/teltonika-telematics/assets/teltonika_configurator_security.png differ diff --git a/teltonika-telematics/assets/teltonika_configurator_status.png b/teltonika-telematics/assets/teltonika_configurator_status.png new file mode 100644 index 00000000..2228ed04 Binary files /dev/null and b/teltonika-telematics/assets/teltonika_configurator_status.png differ diff --git a/teltonika-telematics/assets/teltonika_mqtt_device.png b/teltonika-telematics/assets/teltonika_mqtt_device.png new file mode 100644 index 00000000..f545402d Binary files /dev/null and b/teltonika-telematics/assets/teltonika_mqtt_device.png differ diff --git a/teltonika-telematics/assets/teltonika_telematics.png b/teltonika-telematics/assets/teltonika_telematics.png new file mode 100644 index 00000000..8caa5470 Binary files /dev/null and b/teltonika-telematics/assets/teltonika_telematics.png differ diff --git a/teltonika-telematics/docs/changelog.md b/teltonika-telematics/docs/changelog.md deleted file mode 100644 index b69e13bf..00000000 --- a/teltonika-telematics/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "teltonika-telematics/CHANGELOG.md" diff --git a/teltonika-telematics/docs/index.md b/teltonika-telematics/docs/index.md deleted file mode 100644 index e64359c8..00000000 --- a/teltonika-telematics/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "teltonika-telematics/README.md" diff --git a/teltonika-telematics/docs/plugin_file.md b/teltonika-telematics/docs/plugin_file.md deleted file mode 100644 index ee26d5c8..00000000 --- a/teltonika-telematics/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "teltonika-telematics/plugin.json" -```` diff --git a/teltonika-telematics/mkdocs.yml b/teltonika-telematics/mkdocs.yml deleted file mode 100644 index cffd2d6e..00000000 --- a/teltonika-telematics/mkdocs.yml +++ /dev/null @@ -1,7 +0,0 @@ - -site_name: plugins/teltonika-telematics - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/teltonika-telematics/plugin.json b/teltonika-telematics/plugin.json index b6dd9daf..c0d40121 100644 --- a/teltonika-telematics/plugin.json +++ b/teltonika-telematics/plugin.json @@ -12,6 +12,7 @@ "metadata": { "name": "Teltonika Telematics", "description": "Decoder and preconfiguration for Teltonika GPS Telematics devices", + "category": "devices", "image": "assets/teltonika-telematics.png" }, "resources": { diff --git a/ttn-stack/LICENSE b/ttn-stack/LICENSE deleted file mode 100644 index fc2c8e0a..00000000 --- a/ttn-stack/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-Current Thinger.io (INTERNET OF THINGER S.L.) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/ttn-stack/README.md b/ttn-stack/README.md index 478d3fe4..ef45533f 100644 --- a/ttn-stack/README.md +++ b/ttn-stack/README.md @@ -1,9 +1,7 @@ # The Things Stack -

- TTS diagrams and integration with Thinger.io -

+![TTS diagrams and integration with Thinger.io](assets/tts-diagram.png) The Things Network is a LoRaWAN Network solution that simplifies the deployment of large IoT applications over a collaborative Internet of Things network that spans many countries around the world. From thinger.io we wanted to offer an improved integration to The Things Stack users by providing easy-to-configure tools for storing, analyzing, and showing device data in a simple way. This plugin allows retrieving The Things Stack webhook messages to enhance the integration with some interesting features such as: @@ -38,21 +36,16 @@ This section describes the different interfaces that can be used to configure Th The first step to perform the integration is to create a new plugin configuration. It is possible to create multiple configuration profiles with custom behavior for each application deployed in The Things Stack. To create a new application profile, just type the application ID and press the green Add Application button. Note that the ID must be exactly the same identifier defined in The Things Stack application. -

- TTS plugin web ui application configuration -

+![TTS plugin web ui application configuration](assets/application-configuration.png) The `Application Id` dropdown allows to select and configure a particular application profile, but if the "default" profile is selected, the configuration will be applied to all the applications integrated with the plugin. -!!! warning - - Always create applications with the same application identifier as defined in The Things Stack. +> [!WARNING] +> Always create applications with the same application identifier as defined in The Things Stack. ### Uplink Settings -

- TTS plugin web ui uplink settings -

+![TTS plugin web ui uplink settings](assets/uplink-settings.png) As shown in the image above, the parameters can be used to configure the plugin's behavior: @@ -69,9 +62,7 @@ As shown in the image above, the parameters can be used to configure the plugin' The Things Stack Downlink processes can be configured from Thinger.io in order to select the behavior in some parameters as shown below: -

- tts plugin web ui downlink settings -

+![tts plugin web ui downlink settings](assets/downlink-settings.png) * **Confirmed Downlink:** Set to enabled if downlink messages must be confirmed by the device. * **Push To Downlink Queue:** Enable to push downlink messages instead of replace previous ones. @@ -83,9 +74,7 @@ This tab is used to configure the payload data treatment in order to transform f The interface provides a code editor for Node.js scripts, where it is possible to define the codification / decodification processes and also provides a testing tool that allows to verify the behavior of both `uplink` and `downlink` processes. -

- tts plugin web ui payload processing -

+![tts plugin web ui payload processing](assets/payload-processing.png) The following sections provide additional information about how to configure the uplink and downlink methods. @@ -93,7 +82,6 @@ The following sections provide additional information about how to configure the The uplink method will be called after a gateway sends a new message over The Things Stack network. Depending on the configuration done in The Things Stackapplication, this function will receive different inputs: - * **Base64 String**: If The Things Stack application defines `Custom Javascript Formatter` for the payload but does not provide a decoder function, this method will receive the raw payload encoded in base64. In this case, it will be necessary to write a function to transform this base64 data to a JSON object. * **JSON Object from Cayene LPP**: If The Things Stack application defines a `Cayene LPP` payload formatter, The Things Stack will automatically convert the binary data to a JSON object that can be used directly by the platform. In this case, it is not necessary to define a custom uplink method unless you want to do some extra processing like incorporating calculated fields. * **JSON Object from Custom Decoder:** If The Things Stack application defines `Custom Javascript Formatter`for the payload and provides a decoder function, this function will receive the output from The Things Stack function. In this case, creating a custom uplink method will be redundant, so create the function in The Things Stack, or in the plugin. @@ -112,9 +100,8 @@ The following sections provide additional information about how to configure the }; ``` - !!! info - - The uplink method must always return a JSON object. + > [!NOTE] + > The uplink method must always return a JSON object. === "Downlink" @@ -144,9 +131,8 @@ The following sections provide additional information about how to configure the }; ``` - !!! info - - The downlink method should return a base64 string if The Things Stack application does not define a converter. + > [!NOTE] + > The downlink method should return a base64 string if The Things Stack application does not define a converter. ## The Things Stack Console Configuration @@ -154,31 +140,23 @@ The following sections provide additional information about how to configure the The last tab of the plugin configuration interface is called "Webhook settings", it has been created to help the developers to complete the integration in The Things Stack Console, by providing all the information required to set up the webhook profile. -

- tts plugin web ui webhook settings -

+![tts plugin web ui webhook settings](assets/webhook-settings.png) -!!! info - - Note that the REST API does not define the application ID, this parameter will be checked by the plugin software to manage the payload according to the configuration. +> [!NOTE] +> Note that the REST API does not define the application ID, this parameter will be checked by the plugin software to manage the payload according to the configuration. To create a new webhook integration follow the next steps in The Things Stack web console: 1. Select the Application to be integrated. 2. In the main menu open the "Integrations" section and click the "Webhooks" option. The webhooks list will be shown. -

- tts plugin web ui webhook settings -

+![tts plugin web ui webhook settings](assets/webhook-integration.png) 3\. Clicking the `+Add webhook` blue button in the right top corner of the interface allows choosing between different webhooks integration templates. Select Thinger.io template. Then, configure the webhook only requires filling the form with the information provided by Thinger.io "webhook settings" tab and selecting JSON webhook format. -

- the Things Stack webhook template for Thinger.io -

- -!!! info +![the Things Stack webhook template for Thinger.io](assets/webhook-integration-settings.png) - Note that the Authorization header must be set up using the access token including the "Bearer" command +> [!NOTE] +> Note that the Authorization header must be set up using the access token including the "Bearer" command ### Downlink Configuration @@ -202,13 +180,8 @@ There are multiple ways to view the data sent by the device in [Thinger.io](http As long as the uplink messages are being sent by The Things Stack, the raw data will be available in the [Thinger.io](https://thinger.io/) **Data Bucket** configured or auto-provisioned by the [The Things Stack Plugin](/plugins/ttn-stack), without any further configuration. -

- Thinger.io data bucket showing raw data sent by The Things Stack device -

+![Thinger.io data bucket showing raw data sent by The Things Stack device](assets/data_bucket.png) Nevertheless, it is recommended to create a [Dashboard](https://docs.thinger.io/features/dashboards) in [Thinger.io](https://thinger.io/) to be able to view the data in a more comprehensive way. -

- Thinger.io dashobard showing data form The Things Stack device -

- +![Thinger.io dashobard showing data form The Things Stack device](assets/data-dashboard.png) diff --git a/ttn-stack/assets/application-configuration.png b/ttn-stack/assets/application-configuration.png new file mode 100644 index 00000000..67757aa1 Binary files /dev/null and b/ttn-stack/assets/application-configuration.png differ diff --git a/ttn-stack/assets/data-dashboard.png b/ttn-stack/assets/data-dashboard.png new file mode 100644 index 00000000..8dfdc821 Binary files /dev/null and b/ttn-stack/assets/data-dashboard.png differ diff --git a/ttn-stack/assets/data_bucket.png b/ttn-stack/assets/data_bucket.png new file mode 100644 index 00000000..143363cb Binary files /dev/null and b/ttn-stack/assets/data_bucket.png differ diff --git a/ttn-stack/assets/downlink-settings.png b/ttn-stack/assets/downlink-settings.png new file mode 100644 index 00000000..2a9e9d4f Binary files /dev/null and b/ttn-stack/assets/downlink-settings.png differ diff --git a/ttn-stack/assets/payload-processing.png b/ttn-stack/assets/payload-processing.png new file mode 100644 index 00000000..8c110d5b Binary files /dev/null and b/ttn-stack/assets/payload-processing.png differ diff --git a/ttn-stack/assets/ttn-stack-icon.png b/ttn-stack/assets/ttn-stack-icon.png new file mode 100644 index 00000000..3e9323cb Binary files /dev/null and b/ttn-stack/assets/ttn-stack-icon.png differ diff --git a/ttn-stack/assets/ttn-stack-logo.png b/ttn-stack/assets/ttn-stack-logo.png new file mode 100644 index 00000000..561dbe2a Binary files /dev/null and b/ttn-stack/assets/ttn-stack-logo.png differ diff --git a/ttn-stack/assets/tts-diagram.png b/ttn-stack/assets/tts-diagram.png new file mode 100644 index 00000000..fb33279a Binary files /dev/null and b/ttn-stack/assets/tts-diagram.png differ diff --git a/ttn-stack/assets/uplink-settings.png b/ttn-stack/assets/uplink-settings.png new file mode 100644 index 00000000..f5fcaa30 Binary files /dev/null and b/ttn-stack/assets/uplink-settings.png differ diff --git a/ttn-stack/assets/webhook-integration-settings.png b/ttn-stack/assets/webhook-integration-settings.png new file mode 100644 index 00000000..985c6853 Binary files /dev/null and b/ttn-stack/assets/webhook-integration-settings.png differ diff --git a/ttn-stack/assets/webhook-integration.png b/ttn-stack/assets/webhook-integration.png new file mode 100644 index 00000000..a684963f Binary files /dev/null and b/ttn-stack/assets/webhook-integration.png differ diff --git a/ttn-stack/assets/webhook-settings.png b/ttn-stack/assets/webhook-settings.png new file mode 100644 index 00000000..1e6ded79 Binary files /dev/null and b/ttn-stack/assets/webhook-settings.png differ diff --git a/ttn-stack/docs/changelog.md b/ttn-stack/docs/changelog.md deleted file mode 100644 index 87a1e9c8..00000000 --- a/ttn-stack/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "ttn-stack/CHANGELOG.md" diff --git a/ttn-stack/docs/index.md b/ttn-stack/docs/index.md deleted file mode 100644 index 276e4d56..00000000 --- a/ttn-stack/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "ttn-stack/README.md" diff --git a/ttn-stack/docs/plugin_file.md b/ttn-stack/docs/plugin_file.md deleted file mode 100644 index 5c16dc99..00000000 --- a/ttn-stack/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "ttn-stack/plugin.json" -```` diff --git a/ttn-stack/mkdocs.yml b/ttn-stack/mkdocs.yml deleted file mode 100644 index 54805199..00000000 --- a/ttn-stack/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/ttn-stack - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/ttn-stack/plugin.json b/ttn-stack/plugin.json index e62b4f63..4c18da4b 100644 --- a/ttn-stack/plugin.json +++ b/ttn-stack/plugin.json @@ -2,8 +2,12 @@ "name" : "ttn-stack", "version" : "1.2.1", "description" : "Plugin for handling The Things Stack integration", - "author" : "Alvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", + "deprecated": { + "message": "This plugin is no longer maintained", + "replacement": "ttn" + }, "repository" : { "type" : "git", "url": "https://github.com/thinger-io/plugins.git", @@ -12,8 +16,9 @@ "metadata" : { "name" : "The Things Stack", "description" : "The Things Stack Integration", - "image" : "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/ttn-stack/img/ttn-stack-logo.png", - "icon" : "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/ttn-stack/img/ttn-stack-icon.png" + "category" : "connectivity", + "image" : "assets/ttn-stack-logo.png", + "icon" : "assets/ttn-stack-icon.png" }, "tokens" : { "ttn_stack_plugin" : { diff --git a/ttn/CHANGELOG.md b/ttn/CHANGELOG.md index d8224f7d..7ddc08fe 100644 --- a/ttn/CHANGELOG.md +++ b/ttn/CHANGELOG.md @@ -1,5 +1,25 @@ # Changelog +## [2.1.2] - 2025-11-27 + +### Add + +- Added support for enabling/disabling individual applications in the backend +- Improved Inspector messages +- Auto completion for application edition in the plugin configuration page + +### Fix + +- Fixed multiple application selection bug in the plugin configuration page +- Fixed edition of existing plugin applications + +## [2.1.1] - 2025-11-13 + +### Add + +- Changed visual style of plugin sections in order to improve readability +- Significantly improved error handling and logging for uplink parsing issues + ## [2.1.0] - 2025-11-12 ### Add diff --git a/ttn/LICENSE b/ttn/LICENSE deleted file mode 100644 index 0631ba07..00000000 --- a/ttn/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025-Current Thinger.io (INTERNET OF THINGER S.L.) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/ttn/README.md b/ttn/README.md index d701715d..54a8e65b 100644 --- a/ttn/README.md +++ b/ttn/README.md @@ -1,7 +1,7 @@ # TTN Stack

- The Things Network logo + The Things Network logo

The Things Network (TTN) is an open, community‑driven LoRaWAN® network that simplifies the deployment of large‑scale IoT solutions across the globe. @@ -32,9 +32,7 @@ Open the plugin settings page. In the **Applications** table click **Add +** an | **Application Name** | Must match **exactly** the Application ID defined in the TTN Console. The plugin uses this value to route outgoing requests to the correct TTN application. | | **Device ID Prefix** | Prefix used when generating the device identifier; the Device EUI is appended automatically. Required for autoprovision. | -

- Add application modal in TTN Thinger.io Plugin -

+![Add application modal in TTN Thinger.io Plugin](assets/add_application.png) Keep this page open—you will need the **Webhook URL** and the **TTN TOKEN** generated by the plugin in the next step. @@ -46,9 +44,7 @@ Keep this page open—you will need the **Webhook URL** and the **TTN TOKEN** ge 2. Navigate to **Integrations → Webhooks** and click **Add Webhooks**. 3. Choose **Thinger.io Template** and complete the form with the values shown in your plugin: -

- TTN Webhook Settings for Thinger.io integration -

+![TTN Webhook Settings for Thinger.io integration](assets/thinger_webhook.png) | Template Field | Value | |-----------|-----------------| @@ -62,9 +58,7 @@ Keep this page open—you will need the **Webhook URL** and the **TTN TOKEN** ge > You can find your **TTN API TOKEN** in the plugin settings page, under the **TTN API TOKEN** field, > just click the "copy" button and paste it into the **Authoritation Header** field in the TTN Console. -

- TTN Webhook Settings for Thinger.io integration -

+![TTN Webhook Settings for Thinger.io integration](assets/ttn_api_token.png) After saving, uplink traffic will appear in the plugin **Logs** panel. At this point the devices may still be unrecognised; autoprovision occurs once a matching **Device Template** is installed or created. @@ -90,7 +84,6 @@ Search the Marketplace for a template that matches your device model. If none ex > **Important** > Ensure the template **autoprovision prefix** matches the **Device ID Prefix** configured in the plugin. - --- ## TTN Documentation @@ -98,33 +91,3 @@ Search the Marketplace for a template that matches your device model. If none ex For more information about The Things Network and The Things Stack, please refer to the [official documentation](https://www.thethingsindustries.com/docs/). --- - -## License - - - - - -This plugin is released under the **MIT License**: - -``` -Copyright © Thinger.io - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the “Software”), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -``` \ No newline at end of file diff --git a/ttn/assets/add_application.png b/ttn/assets/add_application.png new file mode 100644 index 00000000..c0038587 Binary files /dev/null and b/ttn/assets/add_application.png differ diff --git a/ttn/assets/thinger_webhook.png b/ttn/assets/thinger_webhook.png new file mode 100644 index 00000000..4ae0307d Binary files /dev/null and b/ttn/assets/thinger_webhook.png differ diff --git a/ttn/assets/ttn-logo.png b/ttn/assets/ttn-logo.png new file mode 100644 index 00000000..561dbe2a Binary files /dev/null and b/ttn/assets/ttn-logo.png differ diff --git a/ttn/assets/ttn_api_token.png b/ttn/assets/ttn_api_token.png new file mode 100644 index 00000000..666b9c66 Binary files /dev/null and b/ttn/assets/ttn_api_token.png differ diff --git a/ttn/docs/changelog.md b/ttn/docs/changelog.md deleted file mode 100644 index 970da168..00000000 --- a/ttn/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "ttn/CHANGELOG.md" diff --git a/ttn/docs/index.md b/ttn/docs/index.md deleted file mode 100644 index 849ac62e..00000000 --- a/ttn/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "ttn/README.md" diff --git a/ttn/docs/plugin_file.md b/ttn/docs/plugin_file.md deleted file mode 100644 index b392636d..00000000 --- a/ttn/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "ttn/plugin.json" -```` diff --git a/ttn/mkdocs.yml b/ttn/mkdocs.yml deleted file mode 100644 index 4e1f819c..00000000 --- a/ttn/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/ttn - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/ttn/plugin.json b/ttn/plugin.json index beefeb53..99c6a39b 100644 --- a/ttn/plugin.json +++ b/ttn/plugin.json @@ -1,6 +1,6 @@ { "name": "ttn", - "version": "2.1.0", + "version": "2.1.2", "description": "Plugin for integrating Thinger.io with TTN", "author": "Thinger.io", "license": "MIT", @@ -12,8 +12,9 @@ "metadata": { "name": "The Things Stack", "description": "The Things Stack Integration", - "image": "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/ttn-stack/img/ttn-stack-logo.png", - "icon": "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/ttn-stack/img/ttn-stack-icon.png" + "category": "connectivity", + "image": "assets/ttn-logo.png", + "icon": "assets/ttn-logo.png" }, "tokens": { "ttn_plugin": { diff --git a/ttn/task/backend/src/server.ts b/ttn/task/backend/src/server.ts index dbd5d5a0..d4880e0f 100644 --- a/ttn/task/backend/src/server.ts +++ b/ttn/task/backend/src/server.ts @@ -106,7 +106,7 @@ app.post("/downlink", async (req: Request, res: Response) => { if (!data || !uplink) { userEvents.push({ - category: 'error', + category: 'downlink', severity: 'error', title: 'Downlink rejected: missing required fields', details: { @@ -120,7 +120,7 @@ app.post("/downlink", async (req: Request, res: Response) => { if (data === '' || data === null || data === 'null') { userEvents.push({ - category: 'warning', + category: 'downlink', severity: 'warning', title: 'Downlink rejected: invalid data', device: uplink.deviceEui, @@ -142,7 +142,7 @@ app.post("/downlink", async (req: Request, res: Response) => { if (typeof application === 'undefined') { Log.error(`Application not found`); userEvents.push({ - category: 'error', + category: 'downlink', severity: 'error', title: 'Downlink failed: application not configured', device: uplink.deviceEui, @@ -167,7 +167,7 @@ app.post("/downlink", async (req: Request, res: Response) => { if (!downlinkUrl || !apiKey) { Log.error("Downlink URL or API key not found in device properties"); userEvents.push({ - category: 'error', + category: 'downlink', severity: 'error', title: 'Downlink failed: missing configuration', device: uplink.deviceEui, @@ -277,7 +277,7 @@ app.post("/downlink", async (req: Request, res: Response) => { }); } else { userEvents.push({ - category: 'error', + category: 'downlink', severity: 'error', title: `Downlink failed for ${uplink.deviceEui} (HTTP ${statusCode})`, device: uplink.deviceEui, @@ -303,7 +303,7 @@ app.post("/downlink", async (req: Request, res: Response) => { Log.error("Error while sending downlink:", err.message || err); userEvents.push({ - category: 'error', + category: 'downlink', severity: 'error', title: `Downlink exception for ${uplink.deviceEui}`, device: uplink.deviceEui, @@ -324,20 +324,38 @@ app.post(`/uplink`, (req: Request, res: Response) => { Log.debug("Received message from device:\n", JSON.stringify(req.body, null, 2)); + let applicationId: string; + let deviceEui: string; + let application: ttnApplication | undefined; + // Application id is recieved in payload from TTN according to // TTN-Data-Format specifications: // https://www.thethingsindustries.com/docs/integrations/data-formats/ - const applicationId = req.body.end_device_ids.application_ids.application_id; - const deviceEui = req.body.end_device_ids.dev_eui; - - const application: ttnApplication | undefined = settings.applications.find((app: { applicationName: string }) => app.applicationName === applicationId); + try { + applicationId = req.body.end_device_ids.application_ids.application_id; + deviceEui = req.body.end_device_ids.dev_eui; + application = settings.applications.find((app: { applicationName: string }) => app.applicationName === applicationId); + } catch (error: any) { + Log.error("Error parsing uplink message:", error.message || error); + userEvents.push({ + category: 'uplink', + severity: 'error', + title: 'Uplink rejected: invalid message format', + details: { + error: error.message || 'Unknown error parsing uplink message', + receivedBody: req.body + } + }); + res.status(400).send({ message: "Invalid message format" }); + return; + } if (typeof application === 'undefined') { Log.error(`Application ${applicationId} not found`); userEvents.push({ - category: 'error', - severity: 'error', + category: 'uplink', + severity: 'warning', title: `Uplink rejected: unknown application ${applicationId}`, device: deviceEui, application: applicationId, @@ -352,6 +370,24 @@ app.post(`/uplink`, (req: Request, res: Response) => { return; } + if (!application.enabled) { + Log.log(`Application ${applicationId} is disabled, ignoring uplink`); + + userEvents.push({ + category: 'uplink', + severity: 'warning', + title: `Uplink ignored: application ${applicationId} is disabled`, + device: deviceEui, + application: applicationId, + details: { + deviceId: deviceEui, + applicationId: applicationId + } + }); + res.status(200).send({ message: "Application is disabled, uplink ignored" }); + return; + } + const device = `${application.deviceIdPrefix}${deviceEui}`; console.log("Device:", device); @@ -363,7 +399,7 @@ app.post(`/uplink`, (req: Request, res: Response) => { userEvents.push({ category: 'uplink', - severity: 'success', + severity: 'info', title: hasDecodedPayload ? `Uplink from ${deviceEui}` : `Uplink from ${deviceEui} (no decoded payload)`, @@ -415,26 +451,44 @@ app.post(`/uplink`, (req: Request, res: Response) => { devicesApi.createProperty(_user, device, prop) .then(() => { Log.info("Downlink info updated for device", device); - userEvents.push({ - category: 'device', - severity: 'info', - title: `Downlink config updated for ${deviceEui}`, - device: deviceEui, - application: applicationId, - details: { - deviceId: device, - hasApiKey: !!downlinkInfo.api_key, - hasPushUrl: !!downlinkInfo.push_url, - hasReplaceUrl: !!downlinkInfo.replace_url - } - }); + // If downlink info is not present, warn the user + if (!downlinkInfo.api_key || !downlinkInfo.push_url || !downlinkInfo.replace_url) { + userEvents.push({ + category: 'device', + severity: 'warning', + title: `Incomplete downlink config for ${deviceEui}`, + device: deviceEui, + application: applicationId, + details: { + deviceId: device, + hasApiKey: !!downlinkInfo.api_key, + hasPushUrl: !!downlinkInfo.push_url, + hasReplaceUrl: !!downlinkInfo.replace_url, + description: 'Essential downlink configuration parameters are missing from TTN request headers. Uplink messages will be forwarded, but downlink functionality is disabled.' + } + }); + } else { + userEvents.push({ + category: 'device', + severity: 'success', + title: `Downlink config updated for ${deviceEui}`, + device: deviceEui, + application: applicationId, + details: { + deviceId: device, + hasApiKey: !!downlinkInfo.api_key, + hasPushUrl: !!downlinkInfo.push_url, + hasReplaceUrl: !!downlinkInfo.replace_url + } + }); + } res.status(200).send(); }) .catch((err: ApiException) => { Log.error("Error saving downlink info", err); userEvents.push({ - category: 'warning', + category: 'uplink', severity: 'warning', title: `Failed to save downlink config for ${deviceEui}`, device: deviceEui, @@ -457,12 +511,30 @@ app.post(`/uplink`, (req: Request, res: Response) => { application: applicationId, details: { deviceId: device, - error: error.message || 'Unknown error forwarding to Thinger', + httpErrorCode: error.code || 'N/A', + error: error.message, + description: 'This plugin couldn\'t forward the uplink message to Thinger.io platform. Check you product id prefix', fPort: ttnMessage.fPort, fCnt: ttnMessage.fCnt } }); res.status(500).send(); + }).catch((error: any) => { + Log.log("Unexpected error while handling uplink", error); + userEvents.push({ + category: 'error', + severity: 'error', + title: `Unexpected error forwarding uplink from ${deviceEui}`, + device: deviceEui, + application: applicationId, + details: { + deviceId: device, + httpErrorCode: error.code || 'N/A', + error: error.message || 'Unknown unexpected error', + uplinkRecieved: req.body + } + }); + res.status(500).send(); }); }); diff --git a/ttn/task/frontend/package-lock.json b/ttn/task/frontend/package-lock.json index 94c21af5..e46dd37a 100644 --- a/ttn/task/frontend/package-lock.json +++ b/ttn/task/frontend/package-lock.json @@ -622,15 +622,15 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -961,9 +961,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { @@ -971,9 +971,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -1006,27 +1006,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", - "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.5" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -2171,15 +2171,15 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -2222,14 +2222,14 @@ } }, "node_modules/@babel/types": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", - "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2759,6 +2759,16 @@ "node": ">=6" } }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@inquirer/checkbox": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.0.6.tgz", @@ -2797,42 +2807,54 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.4", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.4.tgz", - "integrity": "sha512-5y4/PUJVnRb4bwWY67KLdebWOhOc7xj5IP2J80oWXa64mVag24rwQ1VAdnj7/eDY/odhguW0zQ1Mp1pj6fO/2w==", + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.9", - "@inquirer/type": "^3.0.2", - "ansi-escapes": "^4.3.2", + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", - "strip-ansi": "^6.0.1", "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/@inquirer/editor": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.3.tgz", - "integrity": "sha512-S9KnIOJuTZpb9upeRSBBhoDZv7aSV3pG9TECrBj0f+ZsFwccz886hzKBrChGrXMJwd4NKY+pOA9Vy72uqnd6Eg==", + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.4", - "@inquirer/type": "^3.0.2", - "external-editor": "^3.1.0" + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" }, "peerDependencies": { "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/@inquirer/expand": { @@ -2853,10 +2875,49 @@ "@types/node": ">=18" } }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/@inquirer/figures": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.9.tgz", - "integrity": "sha512-BXvGj0ehzrngHTPTDqUoDT3NXL8U0RxUk2zJm2A66RhCEIWdtU1v6GuUqNAgArW4PQ9CinqIWyHdQgdwOj06zQ==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", "dev": true, "license": "MIT", "engines": { @@ -2998,9 +3059,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.2.tgz", - "integrity": "sha512-ZhQ4TvhwHZF+lGhQ2O/rsjo80XoZR5/5qhOY3t6FJuX5XBg5Be8YzYTvaUGJnc12AUGI2nr4QSUE4PhKSigx7g==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", "dev": true, "license": "MIT", "engines": { @@ -3008,6 +3069,11 @@ }, "peerDependencies": { "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/@isaacs/cliui": { @@ -3969,9 +4035,9 @@ } }, "node_modules/@npmcli/package-json/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4870,9 +4936,9 @@ } }, "node_modules/@tufjs/models/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5896,9 +5962,9 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -6034,9 +6100,9 @@ } }, "node_modules/cacache/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6246,9 +6312,9 @@ } }, "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", "dev": true, "license": "MIT" }, @@ -6514,9 +6580,9 @@ } }, "node_modules/compression": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.5.tgz", - "integrity": "sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, "license": "MIT", "dependencies": { @@ -6524,7 +6590,7 @@ "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", - "on-headers": "~1.0.2", + "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" }, @@ -7862,21 +7928,6 @@ "dev": true, "license": "MIT" }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -8760,9 +8811,9 @@ } }, "node_modules/ignore-walk/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9647,16 +9698,6 @@ "node": ">=8" } }, - "node_modules/karma/node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.14" - } - }, "node_modules/karma/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -10659,9 +10700,9 @@ } }, "node_modules/minizlib/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -10969,9 +11010,9 @@ } }, "node_modules/node-gyp/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11321,9 +11362,9 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, "license": "MIT", "engines": { @@ -11457,16 +11498,6 @@ "license": "MIT", "optional": true }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", @@ -14440,9 +14471,9 @@ } }, "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -14901,16 +14932,13 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, "engines": { - "node": ">=0.6.0" + "node": ">=14.14" } }, "node_modules/to-regex-range": { @@ -15582,9 +15610,9 @@ } }, "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", - "integrity": "sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", + "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -16045,9 +16073,9 @@ } }, "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", "dev": true, "license": "MIT", "engines": { diff --git a/ttn/task/frontend/src/app/app.component.html b/ttn/task/frontend/src/app/app.component.html index 4f1b739d..98de9242 100644 --- a/ttn/task/frontend/src/app/app.component.html +++ b/ttn/task/frontend/src/app/app.component.html @@ -7,166 +7,19 @@ - - -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-

Recent Events

- - - - {{ isConnected ? 'Live' : 'Disconnected' }} - -
-
- {{ events.length }} total - -
-
- -
-
-

No events yet

-

Events will appear here in real-time

-
- -
- - -
- -
-
- - -
-
- {{ event.title }} -
-
- - {{ event.category }} - - - {{ event.device }} - - - {{ event.application }} - -
-
- - -
- - {{ event.metadata['duration'] }}ms - - - {{ event.metadata['size'] }}B - -
- - -
- {{ formatTime(event.timestamp) }} -
- - -
- {{ isEventExpanded(event.id) ? '▲' : '▼' }} -
-
- - -
- - -
-
- ID: - {{ event.id }} -
-
- Timestamp: - {{ event.timestamp | date:'medium' }} -
-
- Severity: - - {{ event.severity }} - -
-
- - -
-
Details
-
{{ event.details | json }}
-
- - -
-
Metadata
-
{{ event.metadata | json }}
-
-
-
-
-
-
- -
- -
-
- -
- - - - {{ isConnected ? 'Live Events' : 'Disconnected' }} - - - {{ events.length }} - + +
+ + + + + + + + + + +
diff --git a/ttn/task/frontend/src/app/app.component.ts b/ttn/task/frontend/src/app/app.component.ts index ac8fa988..27b9d563 100644 --- a/ttn/task/frontend/src/app/app.component.ts +++ b/ttn/task/frontend/src/app/app.component.ts @@ -1,157 +1,33 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { RouterOutlet } from '@angular/router'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzLayoutModule } from 'ng-zorro-antd/layout'; import { NzMenuModule } from 'ng-zorro-antd/menu'; -import { NzModalService } from 'ng-zorro-antd/modal'; -import { EventsSocketService, UserEvent } from './core/services/events-socket.service'; -import { Subscription } from 'rxjs'; -import { ApplicationsComponent } from './features/applications/applications.component'; -import { SettingsComponent } from './features/settings/settings.component'; +import { NzSpaceModule } from "ng-zorro-antd/space"; +import { NzTabsModule } from 'ng-zorro-antd/tabs'; + +// Components +import { SettingsComponent } from "./features/settings/settings.component"; +import { ApplicationsComponent } from "./features/applications/applications.component"; +import { InspectorComponent } from "./features/inspector/inspector.component"; @Component({ selector: 'app-root', - standalone: true, imports: [ CommonModule, - RouterOutlet, NzIconModule, NzLayoutModule, NzMenuModule, + SettingsComponent, ApplicationsComponent, - SettingsComponent + InspectorComponent, + NzSpaceModule, + NzTabsModule ], templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) -export class AppComponent implements OnInit, OnDestroy { +export class AppComponent { isCollapsed = false; - // Events data - events: UserEvent[] = []; - isConnected = false; - expandedEventIds = new Set(); - - private subscription = new Subscription(); - - constructor(private eventsSocketService: EventsSocketService, - private modal: NzModalService) { - console.log('App Component initialized'); - } - - async ngOnInit() { - - try { - // Initialize WebSocket with health check and retries - await this.eventsSocketService.initialize('/socket.io', 5); - - - // Subscribe to events stream - this.subscription.add( - this.eventsSocketService.getEvents$().subscribe(events => { - this.events = events; - }) - ); - - // Subscribe to connection status - this.subscription.add( - this.eventsSocketService.isConnected$().subscribe(connected => { - this.isConnected = connected; - }) - ); - - } catch (error) { - console.error('Failed to initialize EventsSocketService:', error); - } - } - - /** - * Toggle event details expansion - */ - toggleEventDetails(eventId: string): void { - if (this.expandedEventIds.has(eventId)) { - this.expandedEventIds.delete(eventId); - } else { - this.expandedEventIds.add(eventId); - } - } - - /** - * Check if event is expanded - */ - isEventExpanded(eventId: string): boolean { - return this.expandedEventIds.has(eventId); - } - - /** - * Clear all events - */ - clearEvents(): void { - this.modal.confirm({ - nzTitle: 'Clear All Events', - nzContent: 'Are you sure you want to clear all events? This action cannot be undone.', - nzOkText: 'Clear', - nzOkType: 'primary', - nzOkDanger: true, - nzCancelText: 'Cancel', - nzOnOk: () => { - this.eventsSocketService.clearEvents(); - this.expandedEventIds.clear(); - } - }); - } - - /** - * Get color based on severity - */ - getSeverityColor(severity: string): string { - const colors: Record = { - 'success': '#52c41a', - 'info': '#1890ff', - 'warning': '#faad14', - 'error': '#ff4d4f' - }; - return colors[severity] || '#d9d9d9'; - } - - /** - * Get background color based on severity (for badges) - */ - getSeverityBg(severity: string): string { - const colors: Record = { - 'success': '#f6ffed', - 'info': '#e6f7ff', - 'warning': '#fffbe6', - 'error': '#fff2f0' - }; - return colors[severity] || '#fafafa'; - } - - /** - * Check if metadata has any content - */ - hasMetadata(metadata: any): boolean { - return metadata && Object.keys(metadata).length > 0; - } - - /** - * Format timestamp to relative time - */ - formatTime(timestamp: string): string { - const date = new Date(timestamp); - const now = new Date(); - const diff = Math.floor((now.getTime() - date.getTime()) / 1000); - - if (diff < 60) return `${diff}s ago`; - if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; - if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; - return date.toLocaleString(); - } - - ngOnDestroy() { - console.log('App Component destroyed - Cleaning up subscriptions'); - this.subscription.unsubscribe(); - this.eventsSocketService.disconnect(); - } } diff --git a/ttn/task/frontend/src/app/features/applications/applications.component.html b/ttn/task/frontend/src/app/features/applications/applications.component.html index f52f5402..1c2a3874 100644 --- a/ttn/task/frontend/src/app/features/applications/applications.component.html +++ b/ttn/task/frontend/src/app/features/applications/applications.component.html @@ -1,46 +1,41 @@ - - - - - - + + + + - - - - Application - Device Id Prefix - Enabled - + + + + Application + Device Id Prefix + Enabled + - @for (application of applicationsTable.data; track application) { + @for (application of applicationsTable.data; track application.applicationName) { - + - + - @if ( application.applicationName ) { - {{ application.applicationName }} - } @else { - {{ application.applicationId }} - } + {{ application.applicationName || application.applicationId }} - @if ( application.applicationName ) { + @if (application.applicationId && application.applicationName !== application.applicationId) {
{{ application.applicationId }}
} @@ -48,25 +43,28 @@ + [ngModel]="application.enabled" + (ngModelChange)="onSwitchChange(application.applicationName || '', $event)" + [disabled]="disabled"> } - +
+ - - - + + + Applications - + + @if (checkedApplications.size > 0) { } @if (checkedApplications.size === 0) { - } - + - + + diff --git a/ttn/task/frontend/src/app/features/applications/applications.component.ts b/ttn/task/frontend/src/app/features/applications/applications.component.ts index 09c97bbc..185288ab 100644 --- a/ttn/task/frontend/src/app/features/applications/applications.component.ts +++ b/ttn/task/frontend/src/app/features/applications/applications.component.ts @@ -11,7 +11,6 @@ import { NzButtonComponent } from "ng-zorro-antd/button"; import { NzCardModule } from 'ng-zorro-antd/card'; import { NzIconModule } from "ng-zorro-antd/icon"; import { NzModalModule } from 'ng-zorro-antd/modal'; -import { NzSkeletonComponent } from "ng-zorro-antd/skeleton"; import { NzSwitchModule } from 'ng-zorro-antd/switch'; import { NzPopconfirmModule } from 'ng-zorro-antd/popconfirm'; @@ -27,33 +26,30 @@ export interface Application { } @Component({ - selector: 'app-applications', - imports: [ - NzTableModule, - NzTooltipDirective, - NzButtonComponent, - NzGridModule, - NzCardModule, - NzIconModule, - FontAwesomeModule, - NzModalModule, - ApplicationComponent, - NzSkeletonComponent, - NzSwitchModule, - NzPopconfirmModule, - FormsModule, - CommonModule - ], - templateUrl: './applications.component.html', + selector: 'app-applications', + imports: [ + NzTableModule, + NzTooltipDirective, + NzButtonComponent, + NzGridModule, + NzCardModule, + NzIconModule, + FontAwesomeModule, + NzModalModule, + ApplicationComponent, + NzSwitchModule, + NzPopconfirmModule, + FormsModule, + CommonModule + ], + templateUrl: './applications.component.html', }) export class ApplicationsComponent { @ViewChild('applicationForm') applicationFormComponent!: ApplicationComponent; protected modalVisible = false; - protected applications: Application[] = []; - protected selectedApplication: Application | undefined;// = {}; - + protected selectedApplication: Application | undefined; protected tokenVisible: {[key: string]: boolean} = {}; public disabled: boolean = false; @@ -65,18 +61,23 @@ export class ApplicationsComponent { protected faLayerGroup = faLayerGroup; protected faQuestionCircle = faQuestionCircle; - constructor( private settingsService: SettingsService ) { - this.settingsService.loadSettings().then( () => { - + constructor(private settingsService: SettingsService) { + this.settingsService.loadSettings().then(() => { this.applications = settingsService.getApplications(); - }); + } + // Helper to get unique identifier for an application + private getApplicationKey(app: Application): string { + return app.applicationName || app.applicationId || ''; } - showModal(id: string = "") { - if ( id.length > 0 ) - this.selectedApplication = this.applications.find(app => app.applicationId === id );// || null; + showModal(name: string = "") { + if (name.length > 0) { + this.selectedApplication = this.applications.find( + app => this.getApplicationKey(app) === name + ); + } this.modalVisible = true; } @@ -90,90 +91,115 @@ export class ApplicationsComponent { } removeItems() { + this.applications = this.applications.filter( + application => !this.checkedApplications.has(this.getApplicationKey(application)) + ); - // remove the applications from the list - this.applications = this.applications.filter(application => !this.checkedApplications.has(application.applicationId)); - - // remove the application Id from this.tokenVisible - this.checkedApplications.forEach(applicationId => { - delete this.tokenVisible[applicationId]; - }) + this.checkedApplications.forEach(appName => { + delete this.tokenVisible[appName]; + }); this.checkedApplications.clear(); + this.allChecked = false; this.settingsService.saveApplications(this.applications).then(() => { + console.log('Applications removed successfully'); + }).catch(error => { + console.error('Failed to save applications', error); }); } - updateCheckedSet(applicationId: string, checked: boolean) { - if(checked){ - this.checkedApplications.add(applicationId); - }else{ - this.checkedApplications.delete(applicationId); + updateCheckedSet(applicationName: string, checked: boolean) { + if (checked) { + this.checkedApplications.add(applicationName); + } else { + this.checkedApplications.delete(applicationName); } + + // Update allChecked status + this.allChecked = this.applications.length > 0 && + this.checkedApplications.size === this.applications.length; } onAllChecked(checked: boolean) { - if(checked){ - for (let index = 0; index < this.applications.length; index++) { - this.checkedApplications.add(this.applications[index].applicationId); - } - }else{ + this.allChecked = checked; + if (checked) { + this.applications.forEach(app => { + this.checkedApplications.add(this.getApplicationKey(app)); + }); + } else { this.checkedApplications.clear(); } } - onItemChecked(applicationId: string, checked: boolean) { - this.updateCheckedSet(applicationId, checked); + onItemChecked(applicationName: string, checked: boolean) { + this.updateCheckedSet(applicationName, checked); } - // Change the enable status of the application processing based on the switch in the applications table - onSwitchChange(appId: string, checked: any) { + onSwitchChange(appName: string, checked: boolean) { + console.log('onSwitchChange called with:', { appName, checked }); + + const index = this.applications.findIndex( + app => this.getApplicationKey(app) === appName + ); + + console.log('Found index:', index); - // set the application to enabled or disabled - const index = this.applications.findIndex(app => app.applicationId === appId); if (index === -1) { - console.error("Application not found"); + console.error("Application not found:", appName); + return; } - this.applications[index].enabled = checked; - - this.settingsService.saveApplications(this.applications).catch(() => { - console.error("Failed to save applications"); - }); + console.log('Application before update:', this.applications[index]); + + // Update the specific application + this.applications[index] = { + ...this.applications[index], + enabled: checked + }; + + console.log('Application after update:', this.applications[index]); + + // Save changes + this.settingsService.saveApplications(this.applications) + .then(() => { + console.log(`Application ${appName} enabled status updated to ${checked}`); + }) + .catch(error => { + console.error("Failed to save applications:", error); + // Revert the change if save fails + this.applications[index].enabled = !checked; + }); } onFormSubmit(application?: Application) { + if (typeof application === 'undefined') return; - // return if application is undefined - if ( typeof application === 'undefined' ) return; - - // Validity has already been checked in form - const index = this.applications.findIndex(app => app.applicationId === this.selectedApplication?.applicationId); - - if ( typeof this.selectedApplication !== 'undefined' ) { + if (typeof this.selectedApplication !== 'undefined') { // Update existing application - const index = this.applications.findIndex(app => app.applicationId === this.selectedApplication?.applicationId); + const index = this.applications.findIndex( + app => this.getApplicationKey(app) === this.getApplicationKey(this.selectedApplication!) + ); + if (index === -1) { console.error("Application not found"); + return; } - this.applications[index] = application; - this.applications = [ - ...this.applications, - ] + this.applications[index] = application; + // Force change detection + this.applications = [...this.applications]; } else { - this.applications = [ - ...this.applications, - application - ] + // Add new application + this.applications = [...this.applications, application]; } this.settingsService.saveApplications(this.applications).then(() => { this.modalVisible = false; this.selectedApplication = undefined; + }).catch(error => { + console.error("Failed to save application:", error); }); - } protected readonly faEye = faEye; diff --git a/ttn/task/frontend/src/app/features/inspector/inspector.component.html b/ttn/task/frontend/src/app/features/inspector/inspector.component.html new file mode 100644 index 00000000..f24493f7 --- /dev/null +++ b/ttn/task/frontend/src/app/features/inspector/inspector.component.html @@ -0,0 +1,126 @@ +
+ +
+
+

Recent Events

+ + + + {{ isConnected ? 'Live' : 'Disconnected' }} + +
+
+ {{ events.length }} total + +
+
+ + +
+
+

No events yet

+

Events will appear here in real-time

+
+ +
+ + +
+ +
+
+ + +
+
+ {{ event.title }} +
+
+ + {{ event.category }} + + + {{ event.device }} + + + {{ event.application }} + +
+
+ + +
+ + {{ event.metadata['duration'] }}ms + + + {{ event.metadata['size'] }}B + +
+ + +
+ {{ formatTime(event.timestamp) }} +
+ + +
+ {{ isEventExpanded(event.id) ? '▲' : '▼' }} +
+
+ + +
+ + +
+
+ ID: + {{ event.id }} +
+
+ Timestamp: + {{ event.timestamp | date :'medium' }} +
+
+ Severity: + + {{ event.severity }} + +
+
+ + +
+
Details
+
{{ event.details | json }}
+
+ + +
+
Metadata
+
{{ event.metadata | json }}
+
+
+
+
+
diff --git a/ttn/task/frontend/src/app/features/inspector/inspector.component.ts b/ttn/task/frontend/src/app/features/inspector/inspector.component.ts new file mode 100644 index 00000000..31e1700b --- /dev/null +++ b/ttn/task/frontend/src/app/features/inspector/inspector.component.ts @@ -0,0 +1,139 @@ +import {Component} from "@angular/core"; +import { NzModalService } from 'ng-zorro-antd/modal'; +import { CommonModule } from '@angular/common'; +import { EventsSocketService, UserEvent } from '../../core/services/events-socket.service'; +import { Subscription } from 'rxjs'; + +@Component({ + selector: 'app-inspector', + imports: [CommonModule], + templateUrl: './inspector.component.html', +}) +export class InspectorComponent { + // Events data + events: UserEvent[] = []; + isConnected = false; + expandedEventIds = new Set(); + + private subscription = new Subscription(); + + constructor(private eventsSocketService: EventsSocketService, + private modal: NzModalService) { + console.log('App Component initialized'); + } + + async ngOnInit() { + + try { + // Initialize WebSocket with health check and retries + await this.eventsSocketService.initialize('/socket.io', 5); + + + // Subscribe to events stream + this.subscription.add( + this.eventsSocketService.getEvents$().subscribe(events => { + this.events = events; + }) + ); + + // Subscribe to connection status + this.subscription.add( + this.eventsSocketService.isConnected$().subscribe(connected => { + this.isConnected = connected; + }) + ); + + } catch (error) { + console.error('Failed to initialize EventsSocketService:', error); + } + } + + /** + * Toggle event details expansion + */ + toggleEventDetails(eventId: string): void { + if (this.expandedEventIds.has(eventId)) { + this.expandedEventIds.delete(eventId); + } else { + this.expandedEventIds.add(eventId); + } + } + + /** + * Check if event is expanded + */ + isEventExpanded(eventId: string): boolean { + return this.expandedEventIds.has(eventId); + } + + /** + * Clear all events + */ + clearEvents(): void { + this.modal.confirm({ + nzTitle: 'Clear All Events', + nzContent: 'Are you sure you want to clear all events? This action cannot be undone.', + nzOkText: 'Clear', + nzOkType: 'primary', + nzOkDanger: true, + nzCancelText: 'Cancel', + nzOnOk: () => { + this.eventsSocketService.clearEvents(); + this.expandedEventIds.clear(); + } + }); + } + + /** + * Get color based on severity + */ + getSeverityColor(severity: string): string { + const colors: Record = { + 'success': '#52c41a', + 'info': '#1890ff', + 'warning': '#faad14', + 'error': '#ff4d4f' + }; + return colors[severity] || '#d9d9d9'; + } + + /** + * Get background color based on severity (for badges) + */ + getSeverityBg(severity: string): string { + const colors: Record = { + 'success': '#f6ffed', + 'info': '#e6f7ff', + 'warning': '#fffbe6', + 'error': '#fff2f0' + }; + return colors[severity] || '#fafafa'; + } + + /** + * Check if metadata has any content + */ + hasMetadata(metadata: any): boolean { + return metadata && Object.keys(metadata).length > 0; + } + + /** + * Format timestamp to relative time + */ + formatTime(timestamp: string): string { + const date = new Date(timestamp); + const now = new Date(); + const diff = Math.floor((now.getTime() - date.getTime()) / 1000); + + if (diff < 60) return `${diff}s ago`; + if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; + if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; + return date.toLocaleString(); + } + + ngOnDestroy() { + console.log('App Component destroyed - Cleaning up subscriptions'); + this.subscription.unsubscribe(); + this.eventsSocketService.disconnect(); + } +} diff --git a/vscode/LICENSE b/vscode/LICENSE deleted file mode 100644 index f1e54eac..00000000 --- a/vscode/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021-Current Thinger.io (INTERNET OF THINGER SL) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vscode/README.md b/vscode/README.md index f93615f0..0beb9afb 100644 --- a/vscode/README.md +++ b/vscode/README.md @@ -1,9 +1,7 @@ # VS Code -

- Thinger.io web console with VS Code plugin and OTA upload -

+![Thinger.io web console with VS Code plugin and OTA upload](assets/laptop-mockup.png) The Thinger.io VS Code Plugin enables users to edit software files on the cloud using a fully-featured Integrated Development Environment (IDE) powered by Visual Studio Code (VS Code). This plugin also facilitates the coding of complete software and allows Over-The-Air (OTA) updates for microcontrollers directly from the cloud. @@ -13,21 +11,16 @@ Thinger.io cloud platform offers a VS Code plugin based on Code-Server. This int The plugin comes pre-installed with the complete setup of PlatformIO and the [Thinger.io extension](https://marketplace.visualstudio.com/items?itemName=thinger-io.thinger-io), making it effortless to get started with your projects. -

- VS Code OTA update for ESP32 -

+![VS Code OTA update for ESP32](assets/iot-ota.gif) Furthermore, when accessing the [File Storages](https://docs.thinger.io/file-system) in Thinger.io's web console, the platform detects the presence of VS Code and adds a convenient shortcut to open the storage inside the VS Code Plugin. -

- VS Code file storage shortcut -

+![VS Code file storage shortcut](assets/storage-shortcut.png) -!!! note - - Please note that the Thinger.io VS Code Plugin is available only for premium - Thinger.io servers, from Medium subscription and upwards. You can check - [**this link**](https://thinger.io/pricing) to create your own instance within minutes. +> [!NOTE] +> Please note that the Thinger.io VS Code Plugin is available only for premium +> Thinger.io servers, from Medium subscription and upwards. You can check +> [**this link**](https://thinger.io/pricing) to create your own instance within minutes. ## OTA Support diff --git a/vscode/assets/iot-ota.gif b/vscode/assets/iot-ota.gif new file mode 100644 index 00000000..e81b39c2 Binary files /dev/null and b/vscode/assets/iot-ota.gif differ diff --git a/vscode/assets/laptop-mockup.png b/vscode/assets/laptop-mockup.png new file mode 100644 index 00000000..81e41922 Binary files /dev/null and b/vscode/assets/laptop-mockup.png differ diff --git a/vscode/assets/storage-shortcut.png b/vscode/assets/storage-shortcut.png new file mode 100644 index 00000000..dc1cae73 Binary files /dev/null and b/vscode/assets/storage-shortcut.png differ diff --git a/vscode/assets/vscode-icon.png b/vscode/assets/vscode-icon.png new file mode 100644 index 00000000..37a08476 Binary files /dev/null and b/vscode/assets/vscode-icon.png differ diff --git a/vscode/docs/changelog.md b/vscode/docs/changelog.md deleted file mode 100644 index 9651cb1a..00000000 --- a/vscode/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "vscode/CHANGELOG.md" diff --git a/vscode/docs/index.md b/vscode/docs/index.md deleted file mode 100644 index 42c74f7e..00000000 --- a/vscode/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "vscode/README.md" diff --git a/vscode/docs/plugin_file.md b/vscode/docs/plugin_file.md deleted file mode 100644 index 3985471f..00000000 --- a/vscode/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "vscode/plugin.json" -```` diff --git a/vscode/mkdocs.yml b/vscode/mkdocs.yml deleted file mode 100644 index 363c2757..00000000 --- a/vscode/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/vscode - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/vscode/plugin.json b/vscode/plugin.json index 345c3c50..d968c0b2 100644 --- a/vscode/plugin.json +++ b/vscode/plugin.json @@ -2,7 +2,7 @@ "name" : "vscode", "version" : "4.89.1-1", "description" : "VS Code Plugin for Thinger.io", - "author" : "Alvaro Luis Bustamante", + "author": "Thinger.io", "license" : "MIT", "repository" : { "type" : "git", @@ -12,8 +12,9 @@ "metadata" : { "name" : "VS Code", "description" : "VS Code Plugin for Thinger.io", - "image" : "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/vscode/img/vscode-icon.png", - "icon" : "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/vscode/img/vscode-icon.png" + "category" : "development", + "image" : "assets/vscode-icon.png", + "icon" : "assets/vscode-icon.png" }, "tokens" : { "vscode_plugin" : { diff --git a/wika-pew1000/LICENSE.md b/wika-pew1000/LICENSE.md deleted file mode 100644 index f1bacd61..00000000 --- a/wika-pew1000/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2025 Thinger.io - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/wika-pew1000/docs/changelog.md b/wika-pew1000/docs/changelog.md deleted file mode 100644 index c6bde86c..00000000 --- a/wika-pew1000/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "wika-pew1000/CHANGELOG.md" diff --git a/wika-pew1000/docs/index.md b/wika-pew1000/docs/index.md deleted file mode 100644 index 1fe478f0..00000000 --- a/wika-pew1000/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "wika-pew1000/README.md" diff --git a/wika-pew1000/docs/plugin_file.md b/wika-pew1000/docs/plugin_file.md deleted file mode 100644 index 082107f0..00000000 --- a/wika-pew1000/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "wika-pew1000/plugin.json" -```` diff --git a/wika-pew1000/mkdocs.yml b/wika-pew1000/mkdocs.yml deleted file mode 100644 index 8a942acf..00000000 --- a/wika-pew1000/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/wika-pew1000 - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/wika-pew1000/plugin.json b/wika-pew1000/plugin.json index 21d8dc95..728c0e5c 100644 --- a/wika-pew1000/plugin.json +++ b/wika-pew1000/plugin.json @@ -12,7 +12,8 @@ "metadata": { "name": "WIKA PEW-1000", "description": "Wireless pressure sensor", - "image": "https://s3.eu-west-1.amazonaws.com/thinger.io.files/plugins/wika-pew1000/img/wikapew1000.png" + "category": "devices", + "image": "assets/wikapew1000.png" }, "resources": { "products": [ diff --git a/wika-pgw23-100/LICENSE.md b/wika-pgw23-100/LICENSE.md deleted file mode 100644 index f1bacd61..00000000 --- a/wika-pgw23-100/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2025 Thinger.io - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/wika-pgw23-100/README.md b/wika-pgw23-100/README.md index 3f9dcb8a..9d5973da 100644 --- a/wika-pgw23-100/README.md +++ b/wika-pgw23-100/README.md @@ -1,8 +1,7 @@ # WIKA PGW-23-100 -

- wika logo + WIKA PGW-23-100

The WIKA PGW23-100 is a hybrid IIoT pressure gauge: it combines a mechanical on-site Bourdon tube display with wireless LoRaWAN® communication for remote monitoring. It is designed for process-industry applications where you want both local indication and centralized data collection. diff --git a/wika-pgw23-100/assets/wika-pgw23-100.jpg b/wika-pgw23-100/assets/wika-pgw23-100.jpg index 67591c06..8a3cc95c 100644 Binary files a/wika-pgw23-100/assets/wika-pgw23-100.jpg and b/wika-pgw23-100/assets/wika-pgw23-100.jpg differ diff --git a/wika-pgw23-100/docs/changelog.md b/wika-pgw23-100/docs/changelog.md deleted file mode 100644 index 047b1124..00000000 --- a/wika-pgw23-100/docs/changelog.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "wika-pgw23-100/CHANGELOG.md" diff --git a/wika-pgw23-100/docs/index.md b/wika-pgw23-100/docs/index.md deleted file mode 100644 index a4480567..00000000 --- a/wika-pgw23-100/docs/index.md +++ /dev/null @@ -1,2 +0,0 @@ - ---8<-- "wika-pgw23-100/README.md" diff --git a/wika-pgw23-100/docs/plugin_file.md b/wika-pgw23-100/docs/plugin_file.md deleted file mode 100644 index ea2db388..00000000 --- a/wika-pgw23-100/docs/plugin_file.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -search: - exclude: true ---- - -# Plugin file - -```` json title="Plugin configuration file" ---8<-- "wika-pgw23-100/plugin.json" -```` diff --git a/wika-pgw23-100/mkdocs.yml b/wika-pgw23-100/mkdocs.yml deleted file mode 100644 index 1314c163..00000000 --- a/wika-pgw23-100/mkdocs.yml +++ /dev/null @@ -1,6 +0,0 @@ -site_name: plugins/wika-pgw23-100 - -nav: - - "index.md" - - Changelog: "changelog.md" - - Plugin file: "plugin_file.md" diff --git a/wika-pgw23-100/plugin.json b/wika-pgw23-100/plugin.json index b343b797..9e75f5ad 100644 --- a/wika-pgw23-100/plugin.json +++ b/wika-pgw23-100/plugin.json @@ -12,7 +12,8 @@ "metadata": { "name": "Wika PGW23-100 Pressure Sensor", "description": "Bourdon tube pressure gauge with wireless transmission", - "image": "https://www.wika.com/media/Images/Product_700x700/Pressure/pgw23.100_en_co_rs_w410_h410_image.jpg" + "category": "devices", + "image": "assets/wika-pgw23-100.jpg" }, "resources": { "products": [