{"id":22895548,"url":"https://github.com/bw-hro/webthing-cpp","last_synced_at":"2025-03-31T22:47:55.871Z","repository":{"id":181723263,"uuid":"667169471","full_name":"bw-hro/webthing-cpp","owner":"bw-hro","description":"Webthing-CPP is a modern CPP/C++17 implementation of the WebThings API.","archived":false,"fork":false,"pushed_at":"2024-10-17T07:58:14.000Z","size":107,"stargazers_count":1,"open_issues_count":3,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-19T10:16:27.806Z","etag":null,"topics":["cpp","iot","webofthings","webthings","wot"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bw-hro.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-16T21:39:45.000Z","updated_at":"2024-10-17T07:33:13.000Z","dependencies_parsed_at":"2023-12-02T17:25:58.338Z","dependency_job_id":"e1868b8c-0a6f-4e2e-9828-ab52683d851a","html_url":"https://github.com/bw-hro/webthing-cpp","commit_stats":null,"previous_names":["bw-hro/webthing-cpp"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bw-hro%2Fwebthing-cpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bw-hro%2Fwebthing-cpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bw-hro%2Fwebthing-cpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bw-hro%2Fwebthing-cpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bw-hro","download_url":"https://codeload.github.com/bw-hro/webthing-cpp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246552885,"owners_count":20795837,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","iot","webofthings","webthings","wot"],"created_at":"2024-12-13T23:30:05.507Z","updated_at":"2025-03-31T22:47:55.864Z","avatar_url":"https://github.com/bw-hro.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"[project_name]: Webthing-CPP\n\n# Webthing-CPP\n\n[![CI Ubuntu](https://github.com/bw-hro/webthing-cpp/actions/workflows/ubuntu.yml/badge.svg?branch=master)](https://github.com/bw-hro/webthing-cpp/actions/workflows/ubuntu.yml)\n[![CI Windows](https://github.com/bw-hro/webthing-cpp/actions/workflows/windows.yml/badge.svg?branch=master)](https://github.com/bw-hro/webthing-cpp/actions/workflows/windows.yml)\n[![CI macOS](https://github.com/bw-hro/webthing-cpp/actions/workflows/macos.yml/badge.svg?branch=master)](https://github.com/bw-hro/webthing-cpp/actions/workflows/macos.yml)\n[![code coverage](https://bw-hro.github.io/webthing-cpp/coverage-report/badge.svg)](https://bw-hro.github.io/webthing-cpp/coverage-report)\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/bw-hro/webthing-cpp/master/LICENSE.txt)\n[![GitHub Releases](https://img.shields.io/github/release/bw-hro/webthing-cpp.svg)](https://github.com/bw-hro/webthing-cpp/releases)\n[![Vcpkg Version](https://img.shields.io/vcpkg/v/webthing-cpp)](https://vcpkg.link/ports/webthing-cpp)\n\nWebthing-CPP is a modern CPP/C++17 implementation of the [WebThings API](https://webthings.io/api). The goal of the project is to provide an easy way to set up digital twins with a web interface for various \"things\" by simply specifying their properties, actions, and events. This project focuses on an easy-to-use API, heavily inspired by similar projects in the [Java](https://github.com/WebThingsIO/webthing-java) and [Python](https://github.com/WebThingsIO/webthing-python) ecosystems.\n\nWebthing-CPP is distributed under the MIT license, without any warranty.\n\n## Project structure\n\nThis project follows a header-only approach to simplify integration into other projects. However, it relies on several dependencies to implement its features:\n\n- [µWebSockets](https://github.com/uNetworking/uWebSockets) serves as the underlying HTTP/WebSocket server.\n- [nlohmann::json](https://github.com/nlohmann/json) and [json-schema-validator](https://github.com/pboettch/json-schema-validator) are used to facilitate JSON handling.\n- [mdns](https://github.com/mjansson/mdns) enables easy service discovery.\n- [OpenSSL](https://github.com/openssl/openssl) is used to provide SSL support when required.\n\nThe project’s source files are located in the _include_ folder. In addition to the library source files, the project includes several examples for demonstration purposes, which are located in the _examples_ folder. Additionally, unit tests based on the [Catch2](https://github.com/catchorg/Catch2) framework can be found in the _test/catch2_ folder, along with other test scripts. The tools folder contains various helpers related to _vcpkg_, code generation, and certificates.\n\n## Defines \n\n__WT_USE_JSON_SCHEMA_VALIDATION__  \n\nWhen ```WT_USE_JSON_SCHEMA_VALIDATION``` is defined Webthing-CPP will validate JSON input for thing properties and thing actions against JSON scheme defined by the thing description. Otherwise validation will be omitted.  \n\n__WT_UNORDERED_JSON_OBJECT_LAYOUT__  \n\nWebthing-CPP uses ```nlohmann::ordered_json``` as default json implementation. This specialization maintains the insertion order of object keys. By defining ```WT_UNORDERED_JSON_OBJECT_LAYOUT``` Webthing-CPP will use ```nlohmann::json``` as json implementation.\n\n__WT_WITH_SSL__  \n\nBy defining ```WT_WITH_SSL``` Webthing-CPP will use the ```uWS::SSLApp``` as backing webserver. When definition is missing it will use ```uWS::App```.\n\n## Build system\n\nWebthing-CPP uses _cmake_ in conjunction with _vcpkg_ as default build system. By default, the build system is configured to statically link all dependencies to build simple self-contained executables.\n\nThe __build.sh__ script is a little helper to ensure cmake is called with correct parameters and vcpkg installs all required dependencies. For Windows users there is an alternative __build.bat__ script available. Following arguments are supported:\n\n__clean__  \n\nDeletes the build folder that cmake creates to ensure a fresh rebuild.\n\n__release__  \n\nUses _Release_ as cmake build type. _Debug_ will be used as default. In _Debug_ mode code coverage flags will be set when GNU or Clang compiler is detected.\n\n__without_tests__\n\nBuilds the project without tests\n\n__skip_tests__\n\nSkips the execution of project tests\n\n__without_examples__\n\nBuilds the project without examples\n\n__with_ssl__  \n\nConfigures the project to support SSL for WebThingServer and installs additional required dependencies.\n\n__win32__\n\nWindows only: Use _Win32_ as target architecture. _x64_ will be used as default.\n\n## SSL support\n\nBuild project with SSL support.\n\n```sh\n./build.sh clean release with_ssl\n```\n\nA self signed certificate for test purposes can be created by using the __create-pems.sh__ script located in the __tools__ folder. This will create a _key.pem_ as well as a _cert.pem_. Make sure to configure the ```WebThingServer``` with correct ```SSLOptions``` e.g.:\n\n```C++\nSSLOptions ssl_options;\nssl_options.key_file_name = \"key.pem\";\nssl_options.cert_file_name = \"cert.pem\";\nssl_options.passphrase = \"1234\";\n\nauto server = WebThingServer::host(things)\n    .port(8888)\n    .ssl_options(ssl_options)\n    .build();\n```\n\n## Examples\n\nAt the moment three example applications are available.\n\n- [single-thing.cpp](examples/single-thing.cpp) Shows how to set up a simple WebThing with two properties and an action.\n- [multiple-thing.cpp](examples/multiple-things.cpp) Shows how to host more then one WebThing in a single application. Things are a fake light and a fake humidity sensor.\n- [gui-thing.cpp](examples/gui-thing.cpp) Demonstrates how to embed  a HTML GUI of a fake slot machine and how to interact with it.\n\n### Example implementation\n\nIn this code-walkthrough we will set up a dimmable light and a humidity sensor (both using fake data, of course). All working examples can be found in the [examples](/examples/) directory.\n\n#### Dimmable light\n\nImagine you have a dimmable light that you want to expose via the WebThings API. The light can be turned on/off and the brightness can be set from 0% to 100%. Besides the name, description, and type, a _Light_ is required to expose two properties:\n\n* ```on```: the state of the light, whether it is turned on or off\n  - Setting this property via a ```PUT {\"on\": true/false}``` call to the REST API toggles the light.\n\n* ```brightness```: the brightness level of the light from 0-100%\n  - Setting this property via a PUT call to the REST API sets the brightness level of this light.\n\nFirst we create a new Thing:\n\n```C++\nauto thing = make_thing(\"urn:dev:ops:my-lamp-1234\", \"My Lamp\",\n    std::vector\u003cstd::string\u003e({\"OnOffSwitch\", \"Light\"}),\n    \"A web connected lamp\");\n```\nNow we can add the required properties.\n\nThe ```on``` property reports and sets the on/off state of the light. For this, we need to have a ```Value``` object which holds the actual state and also a method to turn the light on/off. For our purposes, we just want to log the new state if the light is switched on/off.\n\n```C++\nauto on_value = make_value(true, [](auto v){\n        logger::info(\"On-State is now \" + std::string( v ? \"on\" : \"off\"));\n});\nlink_property(thing, \"on\", on_value, {\n    {\"@type\", \"OnOffProperty\"},\n    {\"title\", \"On/Off\"},\n    {\"type\", \"boolean\"},\n    {\"description\", \"Whether the lamp is turned on\"}});\n```\n\nThe ``brightness`` property reports the brightness level of the light and sets the level. Like before, instead of actually setting the level of a light, we just log the level.\n\n```C++\nauto brightness_value = make_value(50, [](auto v){\n    logger::info(\"Brightness is now \" + std::to_string(v));\n});\nlink_property(thing, \"brightness\", brightness_value, {\n    {\"@type\", \"BrightnessProperty\"},\n    {\"title\", \"Brightness\"},\n    {\"type\", \"integer\"},\n    {\"description\", \"The level of light from 0-100\"},\n    {\"minimum\", 0},\n    {\"maximum\", 100},\n    {\"unit\", \"percent\"}});\n```\n\nNow we can add our newly created thing to the server and start it:\n\n```C++\n// If adding more than one thing, use MultipleThings() with a name.\n// In the single thing case, the thing's name will be broadcast.\nWebThingServer::host(SingleThing(thing.get())).port(8888).start();\n```\n\nThis will start the server, making the light available via the WebThings REST API and announcing it as a discoverable resource on your local network via mDNS.\n\n#### Sensor\n\nLet's now also connect a humidity sensor to the server we set up for our light.\n\nA _MultiLevelSensor_ (a sensor that returns a level instead of just on/off) has one required property (besides the name, type, and optional description): ```level```. We want to monitor this property and get notified if the value changes.\n\nFirst we create a new Thing with a ```level``` property (Here we specify the property within the constructor of our custom Thing object):\n\n* ``level``: tells us what the sensor is actually reading\n\n  - Contrary to the light, the value cannot be set via an API call, as it wouldn't make much sense, to SET what a sensor is reading. Therefore, we are creating a **readOnly** property.\n\n```C++\nstruct FakeGpioHumiditySensor : public Thing\n{\n    FakeGpioHumiditySensor() : Thing(\n        \"urn:dev:ops:my-humidity-sensor-1234\", \"My Humidity Sensor\",\n        \"MultiLevelSensor\", \"A web connected humidity sensor\")\n    {\n        level = make_value(0.0);\n        link_property(this, \"level\", level,  {\n            {\"@type\", \"LevelProperty\"},\n            {\"title\", \"Humidity\"},\n            {\"type\", \"number\"},\n            {\"description\", \"The current humidity in %\"},\n            {\"minimum\", 0},\n            {\"maximum\", 100},\n            {\"unit\", \"percent\"},\n            {\"readOnly\", true}});\n    }\n\n    std::shared_ptr\u003cValue\u003cdouble\u003e\u003e level;\n};\n```\n\nNow we have a sensor that constantly reports 0%. To make it usable, we need a thread or some kind of input when the sensor has a new reading available. For this purpose we start a thread that queries the physical sensor every few seconds. For our purposes, it just calls a fake method.\n\n```C++\nstruct FakeGpioHumiditySensor : public Thing\n{\n    FakeGpioHumiditySensor() : Thing(\n        \"urn:dev:ops:my-humidity-sensor-1234\", \"My Humidity Sensor\",\n        \"MultiLevelSensor\", \"A web connected humidity sensor\")\n    {\n        level = make_value(0.0);\n        link_property(this, \"level\", level,  {\n            {\"@type\", \"LevelProperty\"},\n            {\"title\", \"Humidity\"},\n            {\"type\", \"number\"},\n            {\"description\", \"The current humidity in %\"},\n            {\"minimum\", 0},\n            {\"maximum\", 100},\n            {\"unit\", \"percent\"},\n            {\"readOnly\", true}});\n\n        // Start a thread that polls the sensor reading every 3 seconds\n        std::thread([this]{\n            while(true)\n            {\n                std::this_thread::sleep_for(std::chrono::seconds(3));\n\n                // Update the underlying value, which in turn notifies\n                // all listeners\n                level-\u003enotify_of_external_update(read_from_gpio());\n            }\n        }).detach();\n    }\n\n\n    // Mimic an actual sensor updating its reading every couple seconds.\n    double read_from_gpio()\n    {\n        std::random_device rd; \n        std::mt19937 gen(rd());\n        std::uniform_real_distribution\u003cdouble\u003e dist(0.0, 100.0);\n        return dist(gen);\n    }\n\n    std::shared_ptr\u003cValue\u003cdouble\u003e\u003e level;\n};\n```\n\nThis will update our ```Value``` object with the sensor readings via the ```level-\u003enotify_of_external_update(read_from_gpio())``` call. The ```Value``` object now notifies the property and the thing that the value has changed, which in turn notifies all websocket listeners.\n\n## Adding to Gateway\n\nTo add your web thing to the WebThings Gateway, install the \"Web Thing\" add-on and follow the instructions [here](https://github.com/WebThingsIO/thing-url-adapter#readme).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbw-hro%2Fwebthing-cpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbw-hro%2Fwebthing-cpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbw-hro%2Fwebthing-cpp/lists"}