├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ ├── feature_request.md │ └── general-enhancement.md └── workflows │ ├── asyncapi.yml │ └── openapi.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── core-api ├── README.md ├── rest │ ├── CHANGELOG.md │ ├── README.md │ ├── UCR-core-openapi.yaml │ └── remote-core_rest-api.postman_collection.json └── websocket │ ├── CHANGELOG.md │ ├── README.md │ ├── UCR-core-asyncapi.yaml │ ├── create-html-docker.sh │ └── remote-core_WebSocket API_firecamp.json ├── doc ├── README.md ├── bt │ ├── README.md │ ├── TODO.md │ ├── devices │ │ ├── Android.md │ │ ├── AppleTV.md │ │ ├── Google-Chromecast.md │ │ ├── LG-WebOS.md │ │ ├── Samsung-SmartMonitor.md │ │ ├── _template.md │ │ └── onn-StreamingDevice.md │ ├── hid_consumer.md │ ├── hid_keycodes.md │ ├── hid_system_controls.md │ ├── known_issues.md │ ├── profiles │ │ ├── appletv.json │ │ ├── default.json │ │ ├── generic_android.json │ │ ├── google_chromecast.json │ │ ├── lg_webos.json │ │ ├── onn_streaming_device.json │ │ └── samsung_smartmonitor.json │ └── suspend_behaviour.md ├── discovery.md ├── entities │ ├── README.md │ ├── entity_button.md │ ├── entity_climate.md │ ├── entity_cover.md │ ├── entity_ir_emitter.md │ ├── entity_light.md │ ├── entity_media_player.md │ ├── entity_remote.md │ ├── entity_sensor.md │ ├── entity_switch.md │ └── entity_template.md ├── img │ ├── groups.jpg │ ├── pages.jpg │ ├── profiles.jpg │ ├── ui-overview.jpg │ └── ui-page-media.png ├── integration-driver │ ├── README.md │ ├── driver-advertisement.md │ ├── driver-installation.md │ ├── driver-registration.md │ ├── driver-setup.md │ ├── img │ │ ├── driver-add.png │ │ ├── driver-registered.png │ │ ├── driver-setup-ok.png │ │ └── driver.png │ ├── multi-device-driver.md │ ├── websocket.md │ └── write-integration-driver.md └── remote-ui.md ├── dock-api ├── CHANGELOG.md ├── README.md ├── UCD2-asyncapi.yaml └── create-html-docker.sh └── integration-api ├── README.md ├── UCR-integration-asyncapi.yaml ├── create-html-docker.sh └── create-html.sh /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | ## Description 13 | 14 | 15 | ## How to Reproduce 16 | Steps to reproduce the behavior: 17 | 1. ... 18 | 2. ... 19 | 3. ... 20 | 21 | ## Expected behavior 22 | 23 | 24 | ## Screenshots 25 | 26 | 27 | ## Your Environment 28 | 29 | * Version used: 30 | * Running on: 31 | - [ ] Remote Two 32 | - [ ] Simulator 33 | - [ ] YIO Remote 34 | * Operating System and version if not running on a remote device: 35 | 36 | ## Additional context 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Unfolded Circle Community Forum 4 | url: http://unfolded.community/ 5 | about: Please ask and answer questions here. 6 | - name: Unfolded Circle Discord Channel 7 | url: http://unfolded.chat/ 8 | about: Chat with the community and for asking questions. 9 | - name: Unfolded Circle Contact Form 10 | url: https://unfoldedcircle.com/contact 11 | about: Write us a message on our website. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: feature request 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | 14 | 15 | **Describe the solution you'd like** 16 | 17 | 18 | **Describe alternatives you've considered** 19 | 20 | 21 | **Additional context** 22 | 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/general-enhancement.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: General enhancement 3 | about: API changes and refactoring to improve the project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | ## Expected Behavior or Design 13 | 14 | 15 | 16 | ## Current Behavior or Design 17 | 18 | 19 | 20 | ## Possible Solution 21 | 22 | 23 | 24 | ## Detailed Description and Additional Information 25 | 26 | -------------------------------------------------------------------------------- /.github/workflows/asyncapi.yml: -------------------------------------------------------------------------------- 1 | name: AsyncAPI 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'dock-api/**' 7 | - 'core-api/websocket/**' 8 | - 'integration-api/**' 9 | - '.github/workflows/asyncapi.yml' 10 | pull_request: 11 | types: [ opened, synchronize, reopened ] 12 | paths: 13 | - 'dock-api/**' 14 | - 'core-api/websocket/**' 15 | - 'integration-api/**' 16 | - '.github/workflows/asyncapi.yml' 17 | 18 | jobs: 19 | validate: 20 | name: Validate AsyncAPI 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | 26 | - name: Validate websocket-api 27 | uses: WaleedAshraf/asyncapi-github-action@v0.0.10 28 | with: 29 | filepath: './core-api/websocket/UCR-core-asyncapi.yaml' 30 | 31 | - name: Validate integration-api 32 | uses: WaleedAshraf/asyncapi-github-action@v0.0.10 33 | with: 34 | filepath: './integration-api/UCR-integration-asyncapi.yaml' 35 | 36 | - name: Validate dock-api 37 | uses: WaleedAshraf/asyncapi-github-action@v0.0.10 38 | with: 39 | filepath: './dock-api/UCD2-asyncapi.yaml' 40 | 41 | - name: Prepare AsyncAPI html output 42 | run: | 43 | mkdir -p ./static/api/ws ./static/api/integration ./static/api/dock 44 | 45 | - name: Install AsyncAPI generator 46 | run: | 47 | if [[ ! -x "$(command -v ag)" ]]; then 48 | echo "AsyncAPI generator (ag) not found, installing it with npm" 49 | sudo npm install -g @asyncapi/html-template@0.28.4 50 | sudo npm install -g @asyncapi/generator@1.10.14 51 | fi; 52 | ag --version 53 | 54 | - name: Generate core AsyncAPI HTML doc 55 | run: | 56 | ag ./core-api/websocket/UCR-core-asyncapi.yaml @asyncapi/html-template -o ./static/api/ws --force-write 57 | 58 | - name: Generate integration AsyncAPI HTML doc 59 | run: | 60 | ag ./integration-api/UCR-integration-asyncapi.yaml @asyncapi/html-template -o ./static/api/integration --force-write 61 | 62 | - name: Generate dock AsyncAPI HTML doc 63 | run: | 64 | ag ./dock-api/UCD2-asyncapi.yaml @asyncapi/html-template -o ./static/api/dock --force-write 65 | 66 | - name: Deploy core API GH page 67 | uses: JamesIves/github-pages-deploy-action@v4.5.0 68 | with: 69 | branch: gh-pages 70 | folder: ./static/api/ws 71 | target-folder: ./ws 72 | 73 | - name: Deploy integration API GH page 74 | uses: JamesIves/github-pages-deploy-action@v4.5.0 75 | with: 76 | branch: gh-pages 77 | folder: ./static/api/integration 78 | target-folder: ./integration 79 | 80 | - name: Deploy dock API GH page 81 | uses: JamesIves/github-pages-deploy-action@v4.5.0 82 | with: 83 | branch: gh-pages 84 | folder: ./static/api/dock 85 | target-folder: ./dock 86 | -------------------------------------------------------------------------------- /.github/workflows/openapi.yml: -------------------------------------------------------------------------------- 1 | name: OpenAPI 2 | 3 | on: 4 | push: 5 | paths: 6 | - 'core-api/rest/**' 7 | - '.github/workflows/openapi.yml' 8 | pull_request: 9 | types: [ opened, synchronize, reopened ] 10 | paths: 11 | - 'core-api/rest/**' 12 | - '.github/workflows/openapi.yml' 13 | 14 | jobs: 15 | validate: 16 | name: Validate OpenAPI 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout repository 20 | uses: actions/checkout@v4 21 | - name: Validate 22 | uses: mbowman100/swagger-validator-action@master 23 | with: 24 | files: | 25 | ./core-api/rest/UCR-core-openapi.yaml 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.bak 3 | 4 | /dock-api/html/ 5 | /integration-api/html/ 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Core-API Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## Unreleased 8 | 9 | _Changes in the next release_ 10 | 11 | ### Added 12 | - Bluetooth HID peripheral support. 13 | - Custom integration installation. 14 | - Integration-API: add `get_runtime_info` request message to retrieve driver runtime information from the Remote. 15 | - New IR-emitter entity. 16 | - Core APIs: add Wake on WLAN setting to the network configuration. 17 | - WS Core API: new `active_profile_change` event is emitted whenever the active profile has changed. 18 | - REST Core API: 19 | - Control the [Logdy](https://logdy.dev/) web-app. 20 | - New custom TV icon resource identifier: `ctv:` 21 | - Add query parameter to resource type endpoint to filter items. 22 | - Media-player entity: new attribute `media_position_updated_at`. 23 | 24 | --- 25 | 26 | ## 0.10.0-beta - 2024-04-10 27 | ### Added 28 | - New media-player entity features: context_menu, settings. Support has been included in firmware v1.7.4. 29 | - New remote-entity for sending commands. This allows to write custom integrations for devices which don't fit a media-player entity. 30 | - The first integration supporting remote-entity will be Home Assistant. 31 | 32 | ## 0.9.0-beta - 2024-02-26 33 | ### Added 34 | - Media-player entity features ([#32](https://github.com/unfoldedcircle/core-api/issues/32), [feature-and-bug-tracker#56](https://github.com/unfoldedcircle/feature-and-bug-tracker/issues/56), [feature-and-bug-tracker#92](https://github.com/unfoldedcircle/feature-and-bug-tracker/issues/92)): 35 | - new features: numpad, guide, info, eject, open_close, audio_track, subtitle, record. 36 | - support "simple commands" for any additional commands not covered by a feature. 37 | - Add hostname and MAC address to version information ([#33](https://github.com/unfoldedcircle/core-api/issues/33)). 38 | 39 | ### Changed 40 | - Rename media-player `select_sound_mode` command parameter from `sound_mode` to `mode`. 41 | - Integration API: add `reconfigure` flag in `setup_driver` request message to reconfigure a driver. 42 | - Migrated REST- & WebSocket Core-APIs from the core-simulator repository to the `core-api` directory. 43 | 44 | ## 0.8.0-alpha - 2023-07-15 45 | ### Added 46 | - Media-player entity features: 47 | - new entity states: `STANDBY` and `BUFFERING` 48 | - new device classes: `set_top_box`, `streaming_box`, `tv` 49 | - input source selection & sound mode selection 50 | - new commands for navigation support, channel switching and color button functions 51 | 52 | ## 0.7.0-alpha - 2023-03-14 53 | ### Added 54 | - Initial Dock-API release. 55 | - Driver metadata retrieval with `get_driver_metadata`. 56 | - Integration driver mDNS advertisement documentation. 57 | - Integration driver setup flow API messages and documentation. 58 | 59 | ### Changed 60 | - Replace `localization.unit_system_metric` & `.temperature_celsius` with `measurement_unit` enum (METRIC, US, UK). 61 | - Change `localization.languageCode` separator from dash to underscore. New: `en_UK`, `en_US` etc. 62 | - Removed multi-device setup messages in API and replaced it with the driver setup messages. 63 | They might be added later or be integrated into the driver setup. 64 | 65 | ### Fixed 66 | - `inputTypeLabel` definition. 67 | - The API token header field is called `AUTH-TOKEN`. 68 | 69 | ## 0.6.1-alpha - 2022-06-12 70 | ### Added 71 | - Media player entity has a new state `PAUSED`, new attributes `repeat` & `shuffle` and initial `media_type` definitions. 72 | - Link published [API models in Rust](https://github.com/unfoldedcircle/api-model-rs). 73 | - Clarify sensor state, unit and label. 74 | 75 | ### Changed 76 | - Switching to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format for this changelog. 77 | 78 | ### Fixed 79 | - Integration API: entity discriminator property is `entity_type` 80 | - Fix entity command examples for climate and cover. 81 | 82 | ## 0.6.0-alpha - 2022-05-01 83 | 84 | First public release. 85 | 86 | ### Added 87 | - Refactor and enhance documentation. 88 | - Prepare AsyncAPI html generation with GitHub action. 89 | 90 | ### Changed 91 | - Rename `friendly_name` properties to `name`. 92 | - Refactor entity command message: simple object, instead array of objects. 93 | - Clean up, updated message state emojis to reflect current remote-core status. 94 | 95 | ## 0.5.0-alpha - 2022-03-23 96 | ### Added 97 | - Add authentication messages: `auth_required`, `auth`, `authentication`. 98 | - Add `device_id` property in `device_state` event. 99 | - Add WebSockets specific information. 100 | 101 | ### Changed 102 | - Refactor `driver_version`. 103 | - Refactor documentation structure. 104 | 105 | ## 0.4.0-alpha - 2022-02-27 106 | ### Added 107 | - Add `entity_type` property to messages: `entity_command`, `entity_change`, `entity_state`, `entity_removed`. 108 | 109 | ### Changed 110 | - Rename `entity_change` property `driver_id` to `device_id`. 111 | - Change `device_state` response message to an event message. 112 | - Change `entity` message: 113 | - Property `features` is now optional. 114 | - Cover entity: split `open_close` feature into `open` and `close` features for covers which can only be opened 115 | programmatically but must be closed manually. 116 | - Change `discovered_device` message property `friendly_name` to multi-language text object. 117 | - Improve entity change message descriptions and reflect changes in AsyncAPI 118 | 119 | ### Fixed 120 | - Fix request message example in description. 121 | 122 | ## 0.0.1 - 0.3.0-alpha - 2022-02-18 123 | 124 | Internal releases. 125 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | First off, thanks for taking the time to contribute! 4 | 5 | Found a bug, typo, missing feature or a description that doesn't make sense or needs clarification? 6 | Great, please let us know! 7 | 8 | ### Pull Requests 9 | 10 | **Any pull request needs to be reviewed and approved by the Unfolded Circle API team.** 11 | 12 | ⚠️ Due to the nature of API specifications and their dependencies and impacts in our software, we can't accept every pull 13 | request from the community. Please reach out to us first if you plan to make substantial changes. 14 | 15 | Submitting pull requests for typos, formatting issues etc. are happily accepted and usually approved relatively quick. 16 | For other things, please talk to us first! API changes need to be discussed, reviewed and approved first by the API team. 17 | The preferred way for API changes is to open a feature request or enhancement with your proposed changes, rather than 18 | directly submitting a pull request, which we'll probably have to decline. 19 | 20 | ### Bug Reports :bug: 21 | 22 | - If you find a bug, please search for it first in the [Issues](https://github.com/unfoldedcircle/core-api/issues), 23 | and if it isn't already tracked, [create a new issue](https://github.com/unfoldedcircle/core-api/issues/new). 24 | - Issues that have already been submitted or identified as a bug will be labelled `bug`. 25 | 26 | ### New Features :bulb: 27 | 28 | If you'd like to add new functionality to an API, describe the problem you want to solve in a 29 | [new Issue](https://github.com/unfoldedcircle/core-api/issues/new). 30 | 31 | ### Feedback :speech_balloon: 32 | 33 | There are a few different ways to provide feedback: 34 | 35 | - [Create a new issue](https://github.com/unfoldedcircle/core-api/issues/new) 36 | - [Reach out to us on Twitter](https://twitter.com/unfoldedcircle) 37 | - [Visit our community forum](http://unfolded.community/) 38 | - [Chat with us in our Discord channel](http://unfolded.chat/) 39 | - [Send us a message on our website](https://unfoldedcircle.com/contact) 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![AsyncAPI Validation](https://github.com/unfoldedcircle/core-api/actions/workflows/asyncapi.yml/badge.svg)](https://github.com/unfoldedcircle/core-api/actions/workflows/asyncapi.yml) 2 | [![OpenAPI Validation](https://github.com/unfoldedcircle/core-api/actions/workflows/openapi.yml/badge.svg)](https://github.com/unfoldedcircle/core-api/actions/workflows/asyncapi.yml) 3 | 4 | # Unfolded Circle Core APIs 5 | 6 | This repository contains API specifications of [Unfolded Circle Remote](https://www.unfoldedcircle.com/) products. 7 | 8 | ## Overview 9 | 10 | - [Remote Two user interface](./doc/remote-ui.md) 11 | - [Bluetooth HID peripheral support](doc/bt/README.md) 12 | 13 | API definitions: 14 | 15 | - [WebSocket Integration-API](./integration-api/README.md) defined with [AsyncAPI](https://www.asyncapi.com/). 16 | - [WebSocket Core-API](./core-api/websocket/README.md) defined with [AsyncAPI](https://www.asyncapi.com/) 17 | - [REST Core-API](./core-api/rest/README.md) defined with [OpenAPI](https://www.openapis.org/) 18 | - [WebSocket Dock-API](./dock-api/README.md) defined with [AsyncAPI](https://www.asyncapi.com/) 19 | 20 | Integration driver documentation: 21 | 22 | - [Remote Two entities](doc/entities/README.md). 23 | - [How to write an integration driver](doc/integration-driver/write-integration-driver.md). 24 | - [WebSocket handling](doc/integration-driver/websocket.md): authentication, keep alive, error handling. 25 | 26 | ## Integration API 27 | 28 | The Remote WebSockets integration-API allows writing device integrations for the Unfolded Circle Remote devices. 29 | At the moment only user integrations running on an external host are supported. 30 | 31 | ℹ️ Beta release 1.9.0 allows to install custom integration drivers on the Remote. This is a developer preview feature 32 | to test their integrations. 33 | 34 | The integration driver acts as server and the Remote device as client. The remote connects to the integration when an 35 | integration instance is configured. Whenever the remote enters standby it may choose to disconnect and automatically 36 | reconnect again after wakeup. 37 | 38 | The goal of the integration-API is to cover not only simple static drivers, like controlling GPIOs on a Raspberry Pi, 39 | but also support to integrate existing home automation hubs like Home Assistant, Homey, openHAB etc. 40 | The focus of the integration API is on entity integration, not on controlling or configuring the Remote device. Please 41 | refer to the Core-API for further functionality. 42 | 43 | An integration driver usually doesn't need to use the Core-API as well, unless it also wants to customize certain device 44 | behaviour or automatically add or configure its entities to the users profile. 45 | 46 | ### Develop integration drivers 47 | 48 | Since we are providing an API and not an SDK for a specific programming language, one can develop external integrations 49 | in any language which is capable running a WebSockets server and handling JSON data. 50 | 51 | The downside of an API is that more low-level coding is required. In our case this involves running a WebSocket server, 52 | handling the connections from the Remote device, and parsing the JSON payload in the WebSocket text messages. 53 | However, once this is done, the required API message interactions are rather simple to handle. 54 | 55 | See [how to write an integration driver](doc/integration-driver/write-integration-driver.md) for more information about 56 | how to develop an integration driver for the Remote devices. 57 | 58 | #### Examples 59 | 60 | - [API models in Rust](https://github.com/unfoldedcircle/api-model-rs) 61 | - [Node.js API wrapper for the UC Integration-API](https://github.com/unfoldedcircle/integration-node-library) 62 | Integrations using the Node.js API wrapper: 63 | - [Global Caché IR integration](https://github.com/unfoldedcircle/integration-globalcache) 64 | - [Roon integration](https://github.com/unfoldedcircle/integration-roon) 65 | - [Python API wrapper library for the UC Integration-API](https://github.com/unfoldedcircle/integration-python-library) 66 | Integrations using the Python API wrapper: 67 | - [Android TV integration](https://github.com/unfoldedcircle/integration-androidtv) 68 | - [Apple TV integration](https://github.com/unfoldedcircle/integration-appletv) 69 | - [Denon AVR integration](https://github.com/unfoldedcircle/integration-denonavr) 70 | - [Home Assistant integration](https://github.com/unfoldedcircle/integration-home-assistant) written in Rust 71 | 72 | We plan to release more examples in the future. 73 | 74 | ## Core APIs 75 | 76 | The UC Remote WebSockets & REST Core-APIs allow you to interact with the Unfolded Circle remote-core service and 77 | take full control of its features. 78 | 79 | See [core-api](./core-api) directory for more information. 80 | 81 | ## Other resources 82 | 83 | - [remote-core simulator](https://github.com/unfoldedcircle/core-simulator) to start developing integrations without a UC Remote device 84 | - Open Source [remote-ui application](https://github.com/unfoldedcircle/remote-ui) 85 | 86 | ## API versioning 87 | 88 | The API is versioned according to [SemVer](https://semver.org/). 89 | The initial public release will be `1.0.0` once it is considered stable enough with some initial integration 90 | implementations and developer examples. 91 | 92 | **Any major version zero (`0.y.z`) is for initial development and may change at any time!** 93 | I.e. backward compatibility for minor releases is not yet established, anything MAY change at any time! 94 | 95 | ## Recent changes 96 | 97 | The major changes found in each new release of our API specifications are listed in the [changelog](./CHANGELOG.md) and 98 | under the GitHub [releases](https://github.com/unfoldedcircle/core-api/releases). 99 | 100 | The Dock-API follows an independent release process. The Dock-API changes are listed in the 101 | [Dock-API changelog](./dock-api/CHANGELOG.md). 102 | 103 | ## Contributions 104 | 105 | Please read our [contribution guidelines](./CONTRIBUTING.md) before opening a pull request. 106 | 107 | ## License 108 | 109 | We have published the API specifications and documentations under the [CC-BY-AS-4.0](https://creativecommons.org/licenses/by-sa/4.0/) 110 | (Creative Commons Attribution-ShareAlike 4.0 International) license. Please see [LICENSE file](./LICENSE). 111 | All code examples in this repository are licensed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). 112 | All graphics copyright © Unfolded Circle ApS 2022. 113 | -------------------------------------------------------------------------------- /core-api/README.md: -------------------------------------------------------------------------------- 1 | # Unfolded Circle Remote Core-APIs 2 | 3 | The Unfolded Circle Remote WebSocket & REST Core-APIs allow you to interact with the Unfolded Circle remote-core service 4 | and take full control of its features. 5 | 6 | The focus of the Core-APIs is to provide all functionality for the UI application and the web-configurator. 7 | The APIs are specialized for certain tasks, but otherwise contain the same functionality and data models. 8 | 9 | - The UCR REST Core-API adds: 10 | - Custom resource handling for uploading icons, images etc. 11 | - Installing custom remote-ui and web-configurator components. 12 | - Installing custom integration drivers. 13 | - User management and authentication handling. 14 | - The UCR WS Core-API adds: 15 | - Event subscription with asynchronous notifications. 16 | 17 | The Core-APIs may also be used by other external systems and integration drivers, if specific configuration or 18 | interaction features are required, which are not present in the [UCR Integration-API](../integration-api). 19 | 20 | The API specifications are defined with [OpenAPI](https://swagger.io/specification/) & [AsyncAPI](https://www.asyncapi.com/) 21 | in YAML format. 22 | 23 | - [REST Core-API](rest) 24 | - [WebSocket Core-API](websocket) 25 | 26 | ## API Versions 27 | 28 | | UCR2 Firmware | Core Simulator | REST API | WS API | 29 | |---------------|----------------|----------|--------| 30 | | | 0.58.2 | 0.38.4 | 0.29.2 | 31 | | 2.2.2-beta | 0.53.0 | 0.38.2 | 0.29.1 | 32 | | 2.1.0-beta | 0.51.0 | 0.38.0 | 0.29.0 | 33 | | 2.0.0-beta | 0.49.1 | 0.37.0 | 0.29.0 | 34 | | 1.9.3-beta | 0.48.0 | 0.36.0 | 0.28.0 | 35 | | 1.9.2-beta | 0.47.0 | 0.34.1 | 0.26.0 | 36 | | 1.9.0-beta | | 0.34.0 | 0.26.0 | 37 | | 1.8.0-beta | | 0.33.0 | 0.26.0 | 38 | | 1.7.8 | 0.42.0 | 0.32.0 | 0.25.0 | 39 | | 1.7.7 | 0.41.4 | 0.31.3 | 0.24.6 | 40 | | 1.7.4 | 0.41.2 | 0.31.0 | 0.24.3 | 41 | | 1.6.9 | 0.39.16 | 0.30.3 | 0.24.2 | 42 | 43 | 44 | ## Authentication 45 | 46 | All API endpoints besides `/api/pub` are secured. Available authentication methods are: 47 | 48 | - `Basic Auth` for every request. 49 | This should only be used for simple testing. At the moment there's only a single user account available for the 50 | web-configurator. 51 | - User: `web-configurator` 52 | - Password: generated pin shown in the remote-UI. 53 | - `Bearer Token` for every request. 54 | This is the preferred authentication method for external systems communicating with the UCR REST Core-API. 55 | - See `/auth/api_keys` endpoints on how to create and manage API keys. 56 | - Only the `admin` role is supported at the moment. More roles will be added in the future. 57 | - Example for a curl request: 58 | `curl 'http://$IP/api/system' --header 'Authorization: Bearer $API_KEY'` 59 | - `Cookie` based session login with the `/api/pub/login` endpoint. 60 | This is the preferred method for web frontends like the web-configurator. 61 | 62 | ### Web-configurator Pin 63 | 64 | To access the web-configurator, a pin can be generated in the profile view of the Remote user interface. 65 | 66 | Once generated, the pin can no longer be retrieved from the system anymore. The pin is not stored in plain text. It is 67 | only shown in the UI as long as the device is not restarted. 68 | 69 | Using the web-configurator credentials for external scripting access should be avoided. Furthermore, access is denied if 70 | the web-configurator is disabled in the UI. 71 | 72 | Create an independent API-key instead. 73 | 74 | ### API-key 75 | 76 | 1. Create a new key with the current web-configurator pin in `$PIN`: 77 | 78 | ```shell 79 | curl 'http://$IP/api/auth/api_keys' \ 80 | --header 'Content-Type: application/json' \ 81 | -u "web-configurator:$PIN" \ 82 | --data '{ 83 | "name": "curl access key", 84 | "scopes": [ 85 | "admin" 86 | ] 87 | }' 88 | ``` 89 | 90 | 2. Store the returned API key in `api_key` in a safe place. 91 | It cannot be retrieved anymore and is only shown once in the response message. 92 | Example response: 93 | 94 | ```json 95 | { 96 | "name": "curl access key", 97 | "api_key": "", 98 | "active": true, 99 | "scopes": [ 100 | "admin" 101 | ] 102 | } 103 | ``` 104 | 105 | 3. Use the API key as bearer token in your curl commands. Example: 106 | 107 | ```shell 108 | curl 'http://$IP/api/system' \ 109 | --header 'Authorization: Bearer ' 110 | ``` 111 | -------------------------------------------------------------------------------- /core-api/rest/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # REST Core-API Changelog 2 | All notable changes to the REST Core-API will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## Unreleased 8 | 9 | This section contains unreleased changes which will be part of an upcoming release. 10 | 11 | --- 12 | 13 | ## 0.38.4 14 | ### Added 15 | - Add query parameter to resource type endpoint to filter items. 16 | 17 | ## 0.38.3 18 | ### Changed 19 | - Icon and image identifier validations 20 | - Increase version length to 40. 21 | 22 | ## 0.38.2 23 | ### Changed 24 | - Refactor inline enum definitions in Activity, ActivityGroupOptions, Macro, Remote to simplify data model code generation. 25 | 26 | ## 0.38.0 27 | ### Added 28 | - Control the [Logdy](https://logdy.dev/) web-app through `/system/logs/web`. 29 | 30 | ## 0.37.0 31 | ### Added 32 | - Add Wake on WLAN setting to the network configuration. 33 | 34 | ## 0.36.0 35 | ### Changed 36 | - Changed naming for Remote Two and Remote 3 (same API). 37 | - Add UCR3 specific enhancements and feature flags for charger and button backlight. 38 | - Add IR-emitter entity type 39 | 40 | ## 0.34.1 41 | ### Changed 42 | - Cleanup external token access endpoints for first implementation with Home Assistant. 43 | 44 | ## 0.34.0 45 | ### Added 46 | - Custom integration driver installation. 47 | 48 | ## 0.33.0 49 | ### Added 50 | - BT HID peripheral support with new BT-remote entity ([feature-and-bug-tracker#65](https://github.com/unfoldedcircle/feature-and-bug-tracker/issues/65)). 51 | - New resource type `BtDeviceProfile` for Bluetooth device profiles. 52 | - New endpoints for Bluetooth configuration, HCI logs and BT system control. 53 | 54 | ## 0.32.0 55 | ### Added 56 | - Add hostname and MAC address to version information returned in `/pub/version` endpoint ([#33](https://github.com/unfoldedcircle/core-api/issues/33)). 57 | - Add individual button press endpoints to delete short- and long-press mappings in remotes and activities. 58 | - Add `restart_required` field in the CfgSoftwareUpdate response for the web-configurator to show a restart required notice in the OTA settings screen. 59 | - Add `DELETE /api/cfg/software_update` endpoint to reset software OTA settings. 60 | - Add standby inhibitor management endpoints. 61 | ### Changed 62 | - Software update channel names are uppercase. 63 | 64 | -------------------------------------------------------------------------------- /core-api/rest/README.md: -------------------------------------------------------------------------------- 1 | # Unfolded Circle Remote REST Core-API 2 | 3 | The UCR REST Core-API is defined with [OpenAPI](https://www.openapis.org/). 4 | 5 | You can use the online [Swagger Editor](https://editor.swagger.io/) to load and browse the specification or use your 6 | favorite IDE with OpenAPI support (e.g. IntelliJ or Visual Studio Code). 7 | 8 | - [OpenAPI YAML definition](UCR-core-openapi.yaml). 9 | - ℹ️ This is a bundled YAML file generated from individual definitions. 10 | - Most OpenAPI tools only work with a single file. 11 | - We might publish the original definitions at a later time. 12 | - See [/doc folder](../../doc/README.md) for further API documentation and information. 13 | 14 | ## Postman Collection 15 | 16 | For explorative API testing the `remote-core_rest-api.postman_collection.json` collection can be imported into 17 | [Postman](https://www.postman.com/). It contains pre-defined requests and some helper scripts to propagate generated 18 | identifier keys to simplify common tasks. 19 | 20 | Postman also supports [importing OpenAPI definitions](https://learning.postman.com/docs/integrations/available-integrations/working-with-openAPI/). 21 | 22 | ### Configuration 23 | 24 | The admin user account password needs to be configured in the Postman collection: 25 | 26 | 1. Open the `Remote Two Core-API` collection 27 | 2. Select the `Variables` tab. 28 | 3. Set the `apiPassword` and `apiKeyId` in the **CURRENT VALUE** column. 29 | This will keep the passwords local and won't save them in the collection or Postman cloud. 30 | -------------------------------------------------------------------------------- /core-api/websocket/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # WebSocket Core-API Changelog 2 | All notable changes to the WebSocket Core-API will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## Unreleased 8 | 9 | This section contains unreleased changed which will be part of an upcoming release. 10 | 11 | --- 12 | 13 | ## 0.29.2-beta 14 | ### Changed 15 | - Icon and image identifier validations 16 | 17 | ## 0.29.0-beta 18 | ### Added 19 | - Add Wake on WLAN setting to the network configuration. 20 | - New `active_profile_change` event is emitted whenever the active profile has changed. 21 | 22 | ## 0.28.0-beta 23 | ### Changed 24 | - Changed naming for Remote Two and Remote 3 (same API). 25 | - Add UCR3 specific enhancements and feature flags for charger and button backlight. 26 | - Add IR-emitter entity type 27 | 28 | ## 0.26.0-beta 29 | ### Added 30 | - Bluetooth peripheral pairing events (`bt_pairing_started`, `bt_pairing_auth_request`, `bt_pairing_complete`) and request message (`bt_pairing_response`). 31 | 32 | ## 0.25.0-beta 33 | ### Added 34 | - Add hostname and MAC address to version information returned in `version_info response message ([#33](https://github.com/unfoldedcircle/core-api/issues/33)). 35 | - Add `restart_required` field in the CfgSoftwareUpdate response for the UI to show a restart required notice in the OTA settings screen. 36 | - Add `reset_software_update_cfg` message to reset software OTA settings. 37 | - Add standby inhibitor management messages. 38 | ### Changed 39 | - Software update channel names are uppercase. 40 | -------------------------------------------------------------------------------- /core-api/websocket/README.md: -------------------------------------------------------------------------------- 1 | # Unfolded Circle Remote WebSocket Core-API 2 | 3 | The Remote device acts as server. Whenever the device enters standby, it may choose to disconnect open WebSocket sessions. 4 | It is up to the client to reconnect again. 5 | 6 | The UCR WS Core-API is defined with [AsyncAPI](https://www.asyncapi.com/). 7 | 8 | - [AsyncAPI YAML definition](UCR-core-asyncapi.yaml). 9 | - See [/doc folder](../../doc/README.md) for further API documentation and information. 10 | 11 | ## API HTML documentation 12 | 13 | - View with [AsyncAPI Studio online tool](https://studio.asyncapi.com/?url=https://raw.githubusercontent.com/unfoldedcircle/core-api/main/core-api/websocket/UCR-core-asyncapi.yaml). 14 | The link will directly load the yaml definition file from GitHub and display it together with the HTML documentation 15 | in the browser. 16 | 17 | The [AsyncAPI generator tool](https://github.com/asyncapi/generator) transforms the API definition into other formats. 18 | 19 | The easiest way to generate the HTML documentation from the AsyncAPI definition is to use the official 20 | [asyncapi/generator Docker image](https://hub.docker.com/r/asyncapi/generator). You can also try to use the command line 21 | tool with the instructions provided from the generator tool. 22 | The provided Bash wrapper script [`create-html-docker.sh`](create-html-docker.sh) creates the html documentation in the 23 | `./html` sub folder. 24 | 25 | -------------------------------------------------------------------------------- /core-api/websocket/create-html-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | readonly OUT_DIR=${PWD}/html 4 | 5 | mkdir -p "$OUT_DIR" 6 | docker run --rm -it \ 7 | -v "${OUT_DIR}":/app/example \ 8 | -v "${PWD}/UCR-core-asyncapi.yaml":/app/asyncapi.yaml \ 9 | asyncapi/generator:1.9.2 asyncapi.yaml @asyncapi/html-template@0.25 --force-write -o example 10 | 11 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Remote Two API Documentation 2 | 3 | - [Remote Two user interface](remote-ui.md) 4 | - [Bluetooth HID peripheral support](bt/README.md) 5 | - [Remote Two DNS-SD lookup](discovery.md) 6 | 7 | ## Integration API 8 | 9 | - [Integration AsyncAPI YAML definition](../integration-api/UCR-integration-asyncapi.yaml) 10 | - [Remote Two entities](entities/README.md) 11 | - [How to write an integration driver](integration-driver/write-integration-driver.md) 12 | - [WebSockets handling](integration-driver/websocket.md) 13 | - [Driver registration](integration-driver/driver-registration.md) 14 | - [Driver setup](integration-driver/driver-setup.md) 15 | - [Install integration driver on the device](integration-driver/driver-installation.md) 16 | -------------------------------------------------------------------------------- /doc/bt/README.md: -------------------------------------------------------------------------------- 1 | # Bluetooth HID peripheral 2 | 3 | ‼️ This is currently a beta feature released in the v1.8.0 beta firmware. 4 | 5 | - [Restrictions and missing features](TODO.md) 6 | - [Known issues](known_issues.md) 7 | - [BT suspend / wakeup patterns](suspend_behaviour.md) 8 | 9 | Support articles: 10 | - [How to pair with a Bluetooth LE device](https://support.unfoldedcircle.com/hc/en-us/articles/14695799302940-How-to-pair-with-a-Bluetooth-LE-device) 11 | 12 | ## Virtual Keyboard 13 | 14 | The virtual keyboard defines the following HID report descriptors: 15 | 16 | - [Regular keycodes (Keyboard Keypad Page (0x07))](hid_keycodes.md) 17 | - [Consumer codes (Consumer Page (0x0C))](hid_consumer.md) 18 | - [System Controls (Generic Desktop Page (0x01))](hid_system_controls.md) 19 | 20 | The upper-case `Key name` in the above usage tables is used for the remote-entity commands. 21 | 22 | HID key codes reference: 23 | 24 | ## Devices 25 | 26 | Consumer devices usually only support a small subset of the HID usage definitions. Furthermore, the HID usage tables 27 | leave room for interpretation and contain multiple functions with different commands (for example media and volume control). 28 | 29 | The device test reports are located in the [./devices](devices) folder. They are used to create the device profiles. 30 | 31 | The [template document](devices/_template.md) can be used to submit pull requests for additional devices. 32 | 33 | ## Device Profiles 34 | 35 | Device profiles are tailored device configurations with ready-to-use key-mappings and prepared default UI screens. 36 | The known working key codes are defined as simple commands and directly usable in the web-configurator. 37 | 38 | All other commands can still be used manually with the `send_cmd` command. Only the filtered simple commands in the 39 | profile are visible. 40 | 41 | The remote firmware includes a set of device profiles. New profiles will be included with firmware updates. It's also 42 | possible to load custom profiles without waiting for firmware updates. Custom profiles can be uploaded with the resource 43 | endpoint in the REST Core-API. Support in the web-configurator will be added in the future. 44 | 45 | The existing device profiles are located in the [./profiles](profiles) folder. 46 | 47 | Support articles: 48 | - [Device profiles](https://support.unfoldedcircle.com/hc/en-us/articles/14696263809436-Device-profiles) 49 | - [How to change a device profile for a Bluetooth remote](https://support.unfoldedcircle.com/hc/en-us/articles/14696318441628-How-to-change-device-profiles-for-a-Bluetooth-remote) 50 | 51 | ### Upload custom device profiles 52 | 53 | Custom profiles can be uploaded and managed with the existing `/resources` REST Core-API endpoints. 54 | 55 | - New resource type: `BtDeviceProfile` 56 | - Upload: `POST /api/resources/BtDeviceProfile` 57 | - The filename should be the value of the `id` field and will automatically be renamed. 58 | - Included profiles can be overwritten when using the same `id`. 59 | - Retrieve custom profiles: `GET /api/resources/BtDeviceProfile?page=1&limit=100` 60 | - Delete: `DELETE /api/resources/BtDeviceProfile/:resourceId` 61 | 62 | See [REST Core-API](https://github.com/unfoldedcircle/core-api/tree/main/core-api/rest) for more information. 63 | 64 | ## Send commands with Core-API 65 | 66 | ### send_cmd 67 | 68 | The BT-remote entity supports the same `remote.send_cmd` command as an external remote-entity. 69 | 70 | The `command` parameter supports: 71 | 72 | - UPPER_CASE HID usage key names: [regular key codes](hid_keycodes.md), [consumer key codes](hid_consumer.md), 73 | [system key codes](hid_system_controls.md) depending on prefix. 74 | - Regular key codes support up to 6 simultaneous keys. 75 | Separate key names or codes with `+`, e.g. `KEY_A+KEY_B+KEY_C` 76 | - Regular key codes support the following modifiers: 77 | `LSHIFT`, `LALT`, `LCTRL`, `LGUI`, `RSHIFT`, `RALT`, `RCTRL`, `RGUI`. 78 | For example: `LCTRL+KEY_C` or `LCTRL+LALT+KEY_DELETE` 79 | - Mouse commands if starting with `MOUSE_`: 80 | - Buttons: `MOUSE_BTN_1`, `MOUSE_BTN_2`, `MOUSE_BTN_3` 81 | - X-movement: `MOUSE_X_#` where # is the amount of movement within: `MOUSE_X_-128` .. `MOUSE_X_127` 82 | - Y-movement: `MOUSE_Y_#` where # is the amount of movement within: `MOUSE_Y_-128` .. `MOUSE_Y_127` 83 | - Scroll-wheel: `MOUSE_WHEEL_#` where # is the amount of movement within: `MOUSE_WHEEL_-128` .. `MOUSE_WHEEL_127` 84 | - Hex-codes allow to send an arbitrary usage code, even for keys which are not defined. 85 | This is intended to simplify testing and for proprietary manufacturer commands. 86 | - 2-digit hex-code starting with `0x`: regular key code, for example `0x66` for `KEY_POWER`. 87 | - 4-digit hex-code starting with `0x`: consumer key code, for example `0x0030` for `CONSUMER_POWER`. 88 | - Text: single character or ASCII 7-Bit text (limited to ~ 20 characters). 89 | - Attention: only US layout is supported (as defined in the USB HID specification), there is no support yet for 90 | different character maps. 91 | - ‼️The `repeat`, `delay`, and `hold` parameters are not supported and are ignored. 92 | 93 | Curl example: 94 | ``` 95 | curl --location --request PUT 'http://$IP/api/entities/$ENTITY_ID/command' \ 96 | --header 'Content-Type: application/json' \ 97 | --user 'web-configurator:$PIN' \ 98 | --data '{ 99 | "cmd_id": "remote.send_cmd", 100 | "params": { 101 | "command": "LSHIFT+KEY_A" 102 | } 103 | }' 104 | ``` 105 | 106 | ### send_key 107 | 108 | The BT-remote entity supports an additional command `remote.send_key` for sending a single, predefined key code oder 109 | ASCII character. Optionally, the GUI / meta key modifier can be enabled. 110 | 111 | The `key` parameter supports: 112 | 113 | - A predefined [regular key code](hid_keycodes.md), [consumer key code](hid_consumer.md), 114 | or [system key code](hid_system_controls.md) depending on prefix (`KEY_`, `KEYPAD_`, `CONSUMER_`, `SYSTEM_`). 115 | - Mouse commands if starting with `MOUSE_`: see `send_cmd`. 116 | - Single character (US layout). 117 | 118 | For a regular key code, the GUI / meta key modifier can be set in the `gui` parameter. 119 | 120 | Curl example: 121 | ``` 122 | curl --location --request PUT 'http://$IP/api/entities/$ENTITY_ID/command' \ 123 | --header 'Content-Type: application/json' \ 124 | --user 'web-configurator:$PIN' \ 125 | --data '{ 126 | "cmd_id": "remote.send_key", 127 | "params": { 128 | "key": "KEY_E", 129 | "gui": true 130 | } 131 | }' 132 | ``` 133 | -------------------------------------------------------------------------------- /doc/bt/TODO.md: -------------------------------------------------------------------------------- 1 | ## Restrictions and missing features 2 | 3 | - Number of BT connections / devices is restricted to 1 by default. Multiple Bluetooth LE connections can be enabled under "Settings, Development: Preview features". 4 | - The Bluetooth controller is put to sleep in system standby and re-initialized at wakeup. 5 | - Some devices will show a disconnect & connect message or popup when the remote wakes up. 6 | - Different sleep behaviour can only be configured with the REST Core-API at the moment. Instructions will be published. 7 | - Dock Bluetooth setup is not yet implemented with the new Bluetooth stack. 8 | - Only US-ASCII character map (other languages will likely have KEY_Z / KEY_Y switched and non-letter & digit keys mapped to something else). 9 | - Entity state handling is not yet fully implemented, only UNAVAILABLE (if not connected) and UNKNOWN (when connected) are currently used. 10 | - Power state handling is not yet implemented if the device supports dedicated power on & off commands. Command mapping is prepared in the BT device profile files. 11 | - Native key repeat is not yet implemented. Pressing & holding a button will send individual key presses. 12 | - BT remote-entity send_cmd command parameters repeat, delay, hold are ignored. 13 | - Web-configurator requires manual refresh: 14 | - after the pairing confirmation, otherwise it still shows as not paired. 15 | - after deleting a paired device, otherwise it still shows paired. 16 | - Pairing information is not included in backup archive. 17 | 18 | See [known device issues](known_issues.md) for more information. 19 | 20 | Fixed in v1.9.0 beta: 21 | - Entity command validation issue in `send_cmd` & `send_key` commands with negative mouse movement values. 22 | -------------------------------------------------------------------------------- /doc/bt/hid_system_controls.md: -------------------------------------------------------------------------------- 1 | ## System Controls (Generic Desktop Page (0x01)) 2 | 3 | - System Control. HID usage table: chapter 4 https://usb.org/sites/default/files/hut1_2.pdf 4 | 5 | | Key name | Usage code | Comment | 6 | |---------------------|------------|----------------------------------------------------------------------------------| 7 | | SYSTEM_POWER_DOWN | 0x81 | | 8 | | SYSTEM_SLEEP | 0x82 | | 9 | | SYSTEM_WAKE_UP | 0x83 | | 10 | | SYSTEM_CONTEXT_MENU | 0x84 | | 11 | | SYSTEM_MAIN_MENU | 0x85 | | 12 | | SYSTEM_APP_MENU | 0x86 | | 13 | | SYSTEM_MENU_HELP | 0x87 | | 14 | | SYSTEM_MENU_EXIT | 0x88 | | 15 | | SYSTEM_MENU_SELECT | 0x89 | | 16 | | SYSTEM_MENU_RIGHT | 0x8A | | 17 | | SYSTEM_MENU_LEFT | 0x8B | | 18 | | SYSTEM_MENU_UP | 0x8C | | 19 | | SYSTEM_MENU_DOWN | 0x8D | | 20 | | SYSTEM_COLD_RESTART | 0x8E | | 21 | | SYSTEM_WARM_RESTART | 0x8F | | 22 | | SYSTEM_DPAD_UP | 0x90 | | 23 | | SYSTEM_DPAD_DOWN | 0x91 | | 24 | | SYSTEM_DPAD_RIGHT | 0x92 | | 25 | | SYSTEM_DPAD_LEFT | 0x93 | | 26 | -------------------------------------------------------------------------------- /doc/bt/known_issues.md: -------------------------------------------------------------------------------- 1 | ## Known device issues 2 | 3 | - Playstation 4: does not work, reports "not compatible" after pairing 4 | - Playstation 5: pairing issues, under investigation 5 | - Shield TV: occasional "bonding lost" issues, under investigation 6 | - LG TV, Samsung smart monitor M7: cannot be powered-on with Bluetooth. Most likely this applies to many other devices as well. 7 | -------------------------------------------------------------------------------- /doc/bt/profiles/appletv.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "appletv", 3 | "name": "Apple TV HD & 4K", 4 | "version": 2, 5 | "peripherals": { 6 | "keyboard": true, 7 | "mouse": false 8 | }, 9 | "commands": { 10 | "keyboard": [ 11 | "KEY_A", 12 | "KEY_B", 13 | "KEY_C", 14 | "KEY_D", 15 | "KEY_E", 16 | "KEY_F", 17 | "KEY_G", 18 | "KEY_H", 19 | "KEY_I", 20 | "KEY_J", 21 | "KEY_K", 22 | "KEY_L", 23 | "KEY_M", 24 | "KEY_N", 25 | "KEY_O", 26 | "KEY_P", 27 | "KEY_Q", 28 | "KEY_R", 29 | "KEY_S", 30 | "KEY_T", 31 | "KEY_U", 32 | "KEY_V", 33 | "KEY_W", 34 | "KEY_X", 35 | "KEY_Y", 36 | "KEY_Z", 37 | "KEY_1", 38 | "KEY_2", 39 | "KEY_3", 40 | "KEY_4", 41 | "KEY_5", 42 | "KEY_6", 43 | "KEY_7", 44 | "KEY_8", 45 | "KEY_9", 46 | "KEY_0", 47 | "KEY_RETURN", 48 | "KEY_ESC", 49 | "KEY_BACKSPACE", 50 | "KEY_TAB", 51 | "KEY_SPACE", 52 | "KEY_MINUS", 53 | "KEY_EQUAL", 54 | "KEY_LEFT_BRACE", 55 | "KEY_RIGHT_BRACE", 56 | "KEY_BACKSLASH", 57 | "KEY_HASH_TILDE", 58 | "KEY_SEMICOLON", 59 | "KEY_APOSTROPHE", 60 | "KEY_GRAVE", 61 | "KEY_COMMA", 62 | "KEY_DOT", 63 | "KEY_SLASH", 64 | "KEY_CAPSLOCK", 65 | "KEY_SYSRQ", 66 | "KEY_SCROLL_LOCK", 67 | "KEY_PAUSE", 68 | "KEY_INSERT", 69 | "KEY_HOME", 70 | "KEY_PAGE_UP", 71 | "KEY_DELETE", 72 | "KEY_END", 73 | "KEY_PAGE_DOWN", 74 | "KEY_RIGHT_ARROW", 75 | "KEY_LEFT_ARROW", 76 | "KEY_DOWN_ARROW", 77 | "KEY_UP_ARROW", 78 | "KEYPAD_NUMLOCK", 79 | "KEYPAD_SLASH", 80 | "KEYPAD_ASTERISK", 81 | "KEYPAD_MINUS", 82 | "KEYPAD_PLUS", 83 | "KEYPAD_ENTER", 84 | "KEYPAD_1", 85 | "KEYPAD_2", 86 | "KEYPAD_3", 87 | "KEYPAD_4", 88 | "KEYPAD_5", 89 | "KEYPAD_6", 90 | "KEYPAD_7", 91 | "KEYPAD_8", 92 | "KEYPAD_9", 93 | "KEYPAD_0", 94 | "KEYPAD_DOT", 95 | "KEY_102ND", 96 | "KEYPAD_EQUAL" 97 | ], 98 | "consumer": [ 99 | "CONSUMER_POWER", 100 | "CONSUMER_MENU", 101 | "CONSUMER_MENU_PICK", 102 | "CONSUMER_MENU_UP", 103 | "CONSUMER_MENU_DOWN", 104 | "CONSUMER_MENU_LEFT", 105 | "CONSUMER_MENU_RIGHT", 106 | "CONSUMER_MENU_ESCAPE", 107 | "CONSUMER_DATA_ON_SCREEN", 108 | "CONSUMER_STILL", 109 | "CONSUMER_PLAY", 110 | "CONSUMER_PAUSE", 111 | "CONSUMER_RECORD", 112 | "CONSUMER_FAST_FORWARD", 113 | "CONSUMER_REWIND", 114 | "CONSUMER_SCAN_NEXT_TRACK", 115 | "CONSUMER_SCAN_PREVIOUS_TRACK", 116 | "CONSUMER_STOP", 117 | "CONSUMER_PLAY_PAUSE", 118 | "CONSUMER_AC_HOME", 119 | "CONSUMER_VOLUME_INCREMENT", 120 | "CONSUMER_VOLUME_DECREMENT", 121 | "CONSUMER_MUTE", 122 | "CONSUMER_CHANNEL_INCREMENT", 123 | "CONSUMER_CHANNEL_DECREMENT" 124 | ], 125 | "system": [ 126 | "SYSTEM_WAKE_UP" 127 | ] 128 | }, 129 | "command_mapping": { 130 | "BACK": "CONSUMER_AC_HOME", 131 | "HOME": "CONSUMER_MENU", 132 | "SETTINGS_MENU": "CONSUMER_POWER", 133 | "POWER_ON": "SYSTEM_WAKE_UP", 134 | "VOLUME_UP": "CONSUMER_VOLUME_INCREMENT", 135 | "VOLUME_DOWN": "CONSUMER_VOLUME_DECREMENT", 136 | "MUTE_TOGGLE": "CONSUMER_MUTE", 137 | "CHANNEL_UP": "CONSUMER_CHANNEL_INCREMENT", 138 | "CHANNEL_DOWN": "CONSUMER_CHANNEL_DECREMENT", 139 | "PREVIOUS": "CONSUMER_SCAN_PREVIOUS_TRACK", 140 | "NEXT": "CONSUMER_SCAN_NEXT_TRACK", 141 | "REWIND": "CONSUMER_REWIND", 142 | "FAST_FORWARD": "CONSUMER_FAST_FORWARD", 143 | "PLAY_PAUSE": "CONSUMER_PLAY_PAUSE", 144 | "STOP": "CONSUMER_STOP", 145 | "CURSOR_LEFT": "CONSUMER_MENU_LEFT", 146 | "CURSOR_RIGHT": "CONSUMER_MENU_RIGHT", 147 | "CURSOR_UP": "CONSUMER_MENU_UP", 148 | "CURSOR_DOWN": "CONSUMER_MENU_DOWN", 149 | "CURSOR_ENTER": "CONSUMER_MENU_PICK" 150 | }, 151 | "user_interface": { 152 | "pages": [ 153 | { 154 | "grid": { 155 | "height": 6, 156 | "width": 4 157 | }, 158 | "name": "Numbers", 159 | "page_id": "numbers", 160 | "items": [ 161 | { 162 | "command": { 163 | "cmd_id": "KEY_1" 164 | }, 165 | "location": { 166 | "x": 0, 167 | "y": 0 168 | }, 169 | "text": "1", 170 | "type": "text" 171 | }, 172 | { 173 | "command": { 174 | "cmd_id": "KEY_2" 175 | }, 176 | "location": { 177 | "x": 1, 178 | "y": 0 179 | }, 180 | "text": "2", 181 | "type": "text" 182 | }, 183 | { 184 | "command": { 185 | "cmd_id": "KEY_3" 186 | }, 187 | "location": { 188 | "x": 2, 189 | "y": 0 190 | }, 191 | "text": "3", 192 | "type": "text" 193 | }, 194 | { 195 | "command": { 196 | "cmd_id": "KEY_4" 197 | }, 198 | "location": { 199 | "x": 0, 200 | "y": 1 201 | }, 202 | "text": "4", 203 | "type": "text" 204 | }, 205 | { 206 | "command": { 207 | "cmd_id": "KEY_5" 208 | }, 209 | "location": { 210 | "x": 1, 211 | "y": 1 212 | }, 213 | "text": "5", 214 | "type": "text" 215 | }, 216 | { 217 | "command": { 218 | "cmd_id": "KEY_6" 219 | }, 220 | "location": { 221 | "x": 2, 222 | "y": 1 223 | }, 224 | "text": "6", 225 | "type": "text" 226 | }, 227 | { 228 | "command": { 229 | "cmd_id": "KEY_7" 230 | }, 231 | "location": { 232 | "x": 0, 233 | "y": 2 234 | }, 235 | "text": "7", 236 | "type": "text" 237 | }, 238 | { 239 | "command": { 240 | "cmd_id": "KEY_8" 241 | }, 242 | "location": { 243 | "x": 1, 244 | "y": 2 245 | }, 246 | "text": "8", 247 | "type": "text" 248 | }, 249 | { 250 | "command": { 251 | "cmd_id": "KEY_9" 252 | }, 253 | "location": { 254 | "x": 2, 255 | "y": 2 256 | }, 257 | "text": "9", 258 | "type": "text" 259 | }, 260 | { 261 | "command": { 262 | "cmd_id": "KEY_0" 263 | }, 264 | "location": { 265 | "x": 1, 266 | "y": 3 267 | }, 268 | "text": "0", 269 | "type": "text" 270 | }, 271 | { 272 | "command": { 273 | "cmd_id": "KEY_ESC" 274 | }, 275 | "location": { 276 | "x": 3, 277 | "y": 0 278 | }, 279 | "text": "ESC", 280 | "type": "text" 281 | }, 282 | { 283 | "command": { 284 | "cmd_id": "CONSUMER_AC_EXIT" 285 | }, 286 | "location": { 287 | "x": 3, 288 | "y": 2 289 | }, 290 | "text": "Exit", 291 | "type": "text" 292 | }, 293 | { 294 | "command": { 295 | "cmd_id": "CONSUMER_AC_BACK" 296 | }, 297 | "location": { 298 | "x": 3, 299 | "y": 3 300 | }, 301 | "text": "Back", 302 | "type": "text" 303 | }, 304 | { 305 | "command": { 306 | "cmd_id": "KEYPAD_PLUS" 307 | }, 308 | "location": { 309 | "x": 0, 310 | "y": 4 311 | }, 312 | "text": "+", 313 | "type": "text" 314 | }, 315 | { 316 | "command": { 317 | "cmd_id": "KEYPAD_MINUS" 318 | }, 319 | "location": { 320 | "x": 0, 321 | "y": 5 322 | }, 323 | "text": "-", 324 | "type": "text" 325 | }, 326 | { 327 | "command": { 328 | "cmd_id": "KEY_PAGE_UP" 329 | }, 330 | "location": { 331 | "x": 1, 332 | "y": 4 333 | }, 334 | "text": "Pg up", 335 | "type": "text" 336 | }, 337 | { 338 | "command": { 339 | "cmd_id": "KEY_PAGE_DOWN" 340 | }, 341 | "location": { 342 | "x": 1, 343 | "y": 5 344 | }, 345 | "text": "Pg down", 346 | "type": "text" 347 | }, 348 | { 349 | "command": { 350 | "cmd_id": "KEY_COMMA" 351 | }, 352 | "location": { 353 | "x": 2, 354 | "y": 4 355 | }, 356 | "text": ",", 357 | "type": "text" 358 | }, 359 | { 360 | "command": { 361 | "cmd_id": "KEY_DOT" 362 | }, 363 | "location": { 364 | "x": 3, 365 | "y": 4 366 | }, 367 | "text": ".", 368 | "type": "text" 369 | }, 370 | { 371 | "command": { 372 | "cmd_id": "CONSUMER_POWER" 373 | }, 374 | "location": { 375 | "x": 2, 376 | "y": 5 377 | }, 378 | "text": "Menu", 379 | "type": "text" 380 | } 381 | ] 382 | }, 383 | { 384 | "name": "Media Consumer", 385 | "page_id": "media2", 386 | "grid": { 387 | "height": 6, 388 | "width": 4 389 | }, 390 | "items": [ 391 | { 392 | "location": { 393 | "x": 0, 394 | "y": 0 395 | }, 396 | "size": { 397 | "height": 1, 398 | "width": 4 399 | }, 400 | "text": "Consumer codes", 401 | "type": "text" 402 | }, 403 | { 404 | "command": { 405 | "cmd_id": "CONSUMER_SCAN_PREVIOUS_TRACK" 406 | }, 407 | "icon": "uc:prev", 408 | "location": { 409 | "x": 0, 410 | "y": 5 411 | }, 412 | "type": "icon" 413 | }, 414 | { 415 | "command": { 416 | "cmd_id": "CONSUMER_PLAY" 417 | }, 418 | "icon": "uc:play", 419 | "location": { 420 | "x": 1, 421 | "y": 5 422 | }, 423 | "type": "icon" 424 | }, 425 | { 426 | "command": { 427 | "cmd_id": "CONSUMER_PAUSE" 428 | }, 429 | "icon": "uc:pause", 430 | "location": { 431 | "x": 2, 432 | "y": 5 433 | }, 434 | "type": "icon" 435 | }, 436 | { 437 | "command": { 438 | "cmd_id": "CONSUMER_SCAN_NEXT_TRACK" 439 | }, 440 | "icon": "uc:next", 441 | "location": { 442 | "x": 3, 443 | "y": 5 444 | }, 445 | "type": "icon" 446 | }, 447 | { 448 | "command": { 449 | "cmd_id": "CONSUMER_REWIND" 450 | }, 451 | "icon": "uc:bw", 452 | "location": { 453 | "x": 0, 454 | "y": 4 455 | }, 456 | "type": "icon" 457 | }, 458 | { 459 | "command": { 460 | "cmd_id": "CONSUMER_STOP" 461 | }, 462 | "icon": "uc:stop", 463 | "location": { 464 | "x": 1, 465 | "y": 4 466 | }, 467 | "type": "icon" 468 | }, 469 | { 470 | "command": { 471 | "cmd_id": "CONSUMER_RECORD" 472 | }, 473 | "icon": "uc:rec", 474 | "location": { 475 | "x": 2, 476 | "y": 4 477 | }, 478 | "type": "icon" 479 | }, 480 | { 481 | "command": { 482 | "cmd_id": "CONSUMER_FAST_FORWARD" 483 | }, 484 | "icon": "uc:ff", 485 | "location": { 486 | "x": 3, 487 | "y": 4 488 | }, 489 | "type": "icon" 490 | } 491 | ] 492 | } 493 | ] 494 | } 495 | } -------------------------------------------------------------------------------- /doc/bt/profiles/lg_webos.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "lg_webos", 3 | "name": "LG WebOS TV", 4 | "version": 1, 5 | "peripherals": { 6 | "keyboard": true, 7 | "mouse": true 8 | }, 9 | "commands": { 10 | "keyboard": [ 11 | "KEY_A", 12 | "KEY_B", 13 | "KEY_C", 14 | "KEY_D", 15 | "KEY_E", 16 | "KEY_F", 17 | "KEY_G", 18 | "KEY_H", 19 | "KEY_I", 20 | "KEY_J", 21 | "KEY_K", 22 | "KEY_L", 23 | "KEY_M", 24 | "KEY_N", 25 | "KEY_O", 26 | "KEY_P", 27 | "KEY_Q", 28 | "KEY_R", 29 | "KEY_S", 30 | "KEY_T", 31 | "KEY_U", 32 | "KEY_V", 33 | "KEY_W", 34 | "KEY_X", 35 | "KEY_Y", 36 | "KEY_Z", 37 | "KEY_1", 38 | "KEY_2", 39 | "KEY_3", 40 | "KEY_4", 41 | "KEY_5", 42 | "KEY_6", 43 | "KEY_7", 44 | "KEY_8", 45 | "KEY_9", 46 | "KEY_0", 47 | "KEY_RETURN", 48 | "KEY_ESC", 49 | "KEY_BACKSPACE", 50 | "KEY_SPACE", 51 | "KEY_MINUS", 52 | "KEY_EQUAL", 53 | "KEY_LEFT_BRACE", 54 | "KEY_RIGHT_BRACE", 55 | "KEY_BACKSLASH", 56 | "KEY_HASH_TILDE", 57 | "KEY_SEMICOLON", 58 | "KEY_APOSTROPHE", 59 | "KEY_GRAVE", 60 | "KEY_COMMA", 61 | "KEY_DOT", 62 | "KEY_SLASH", 63 | "KEY_PAUSE", 64 | "KEY_PAGE_UP", 65 | "KEY_PAGE_DOWN", 66 | "KEY_RIGHT_ARROW", 67 | "KEY_LEFT_ARROW", 68 | "KEY_DOWN_ARROW", 69 | "KEY_UP_ARROW", 70 | "KEYPAD_SLASH", 71 | "KEYPAD_ASTERISK", 72 | "KEYPAD_MINUS", 73 | "KEYPAD_PLUS", 74 | "KEYPAD_1", 75 | "KEYPAD_2", 76 | "KEYPAD_3", 77 | "KEYPAD_4", 78 | "KEYPAD_5", 79 | "KEYPAD_6", 80 | "KEYPAD_7", 81 | "KEYPAD_8", 82 | "KEYPAD_9", 83 | "KEYPAD_0", 84 | "KEYPAD_DOT", 85 | "KEY_POWER", 86 | "KEY_STOP", 87 | "KEY_MUTE", 88 | "KEY_VOLUME_UP", 89 | "KEY_VOLUME_DOWN", 90 | "KEY_LEFT_GUI" 91 | ], 92 | "consumer": [ 93 | ], 94 | "system": [ 95 | ] 96 | }, 97 | "command_mapping": { 98 | "BACK": "KEY_ESC", 99 | "HOME": "KEY_LEFT_GUI", 100 | "POWER_OFF": "KEY_POWER", 101 | "VOLUME_UP": "KEY_VOLUME_UP", 102 | "VOLUME_DOWN": "KEY_VOLUME_DOWN", 103 | "MUTE_TOGGLE": "KEY_MUTE", 104 | "CHANNEL_UP": "KEY_PAGE_UP", 105 | "CHANNEL_DOWN": "KEY_PAGE_DOWN", 106 | "PAUSE": "KEY_PAUSE", 107 | "STOP": "KEY_STOP", 108 | "CURSOR_LEFT": "KEY_LEFT_ARROW", 109 | "CURSOR_RIGHT": "KEY_RIGHT_ARROW", 110 | "CURSOR_UP": "KEY_UP_ARROW", 111 | "CURSOR_DOWN": "KEY_DOWN_ARROW", 112 | "CURSOR_ENTER": "KEY_RETURN" 113 | }, 114 | "user_interface": { 115 | "pages": [ 116 | { 117 | "grid": { 118 | "height": 6, 119 | "width": 4 120 | }, 121 | "name": "Numbers", 122 | "page_id": "numbers", 123 | "items": [ 124 | { 125 | "command": { 126 | "cmd_id": "KEY_1" 127 | }, 128 | "location": { 129 | "x": 0, 130 | "y": 0 131 | }, 132 | "text": "1", 133 | "type": "text" 134 | }, 135 | { 136 | "command": { 137 | "cmd_id": "KEY_2" 138 | }, 139 | "location": { 140 | "x": 1, 141 | "y": 0 142 | }, 143 | "text": "2", 144 | "type": "text" 145 | }, 146 | { 147 | "command": { 148 | "cmd_id": "KEY_3" 149 | }, 150 | "location": { 151 | "x": 2, 152 | "y": 0 153 | }, 154 | "text": "3", 155 | "type": "text" 156 | }, 157 | { 158 | "command": { 159 | "cmd_id": "KEY_4" 160 | }, 161 | "location": { 162 | "x": 0, 163 | "y": 1 164 | }, 165 | "text": "4", 166 | "type": "text" 167 | }, 168 | { 169 | "command": { 170 | "cmd_id": "KEY_5" 171 | }, 172 | "location": { 173 | "x": 1, 174 | "y": 1 175 | }, 176 | "text": "5", 177 | "type": "text" 178 | }, 179 | { 180 | "command": { 181 | "cmd_id": "KEY_6" 182 | }, 183 | "location": { 184 | "x": 2, 185 | "y": 1 186 | }, 187 | "text": "6", 188 | "type": "text" 189 | }, 190 | { 191 | "command": { 192 | "cmd_id": "KEY_7" 193 | }, 194 | "location": { 195 | "x": 0, 196 | "y": 2 197 | }, 198 | "text": "7", 199 | "type": "text" 200 | }, 201 | { 202 | "command": { 203 | "cmd_id": "KEY_8" 204 | }, 205 | "location": { 206 | "x": 1, 207 | "y": 2 208 | }, 209 | "text": "8", 210 | "type": "text" 211 | }, 212 | { 213 | "command": { 214 | "cmd_id": "KEY_9" 215 | }, 216 | "location": { 217 | "x": 2, 218 | "y": 2 219 | }, 220 | "text": "9", 221 | "type": "text" 222 | }, 223 | { 224 | "command": { 225 | "cmd_id": "KEY_ESC" 226 | }, 227 | "location": { 228 | "x": 0, 229 | "y": 3 230 | }, 231 | "text": "ESC", 232 | "type": "text" 233 | }, 234 | { 235 | "command": { 236 | "cmd_id": "KEY_0" 237 | }, 238 | "location": { 239 | "x": 1, 240 | "y": 3 241 | }, 242 | "text": "0", 243 | "type": "text" 244 | }, 245 | { 246 | "command": { 247 | "cmd_id": "KEY_RETURN" 248 | }, 249 | "location": { 250 | "x": 2, 251 | "y": 3 252 | }, 253 | "text": "Enter", 254 | "type": "text" 255 | }, 256 | { 257 | "command": { 258 | "cmd_id": "KEY_PAGE_UP" 259 | }, 260 | "location": { 261 | "x": 3, 262 | "y": 0 263 | }, 264 | "text": "CH up", 265 | "type": "text" 266 | }, 267 | { 268 | "command": { 269 | "cmd_id": "KEY_PAGE_DOWN" 270 | }, 271 | "location": { 272 | "x": 3, 273 | "y": 1 274 | }, 275 | "text": "CH down", 276 | "type": "text" 277 | }, 278 | { 279 | "command": { 280 | "cmd_id": "KEY_LEFT_GUI" 281 | }, 282 | "location": { 283 | "x": 2, 284 | "y": 4 285 | }, 286 | "text": "Home", 287 | "type": "text" 288 | }, 289 | { 290 | "command": { 291 | "cmd_id": "KEY_STOP" 292 | }, 293 | "icon": "uc:stop", 294 | "location": { 295 | "x": 1, 296 | "y": 5 297 | }, 298 | "type": "icon" 299 | }, 300 | { 301 | "command": { 302 | "cmd_id": "KEY_PAUSE" 303 | }, 304 | "icon": "uc:pause", 305 | "location": { 306 | "x": 2, 307 | "y": 5 308 | }, 309 | "type": "icon" 310 | } 311 | ] 312 | } 313 | ] 314 | } 315 | } -------------------------------------------------------------------------------- /doc/bt/profiles/samsung_smartmonitor.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "samsung_smartmonitor", 3 | "name": "Samsung Smart Monitor M7", 4 | "version": 1, 5 | "peripherals": { 6 | "keyboard": true, 7 | "mouse": true 8 | }, 9 | "commands": { 10 | "keyboard": [ 11 | "KEY_A", 12 | "KEY_B", 13 | "KEY_C", 14 | "KEY_D", 15 | "KEY_E", 16 | "KEY_F", 17 | "KEY_G", 18 | "KEY_H", 19 | "KEY_I", 20 | "KEY_J", 21 | "KEY_K", 22 | "KEY_L", 23 | "KEY_M", 24 | "KEY_N", 25 | "KEY_O", 26 | "KEY_P", 27 | "KEY_Q", 28 | "KEY_R", 29 | "KEY_S", 30 | "KEY_T", 31 | "KEY_U", 32 | "KEY_V", 33 | "KEY_W", 34 | "KEY_X", 35 | "KEY_Y", 36 | "KEY_Z", 37 | "KEY_1", 38 | "KEY_2", 39 | "KEY_3", 40 | "KEY_4", 41 | "KEY_5", 42 | "KEY_6", 43 | "KEY_7", 44 | "KEY_8", 45 | "KEY_9", 46 | "KEY_0", 47 | "KEY_RETURN", 48 | "KEY_ESC", 49 | "KEY_BACKSPACE", 50 | "KEY_TAB", 51 | "KEY_SPACE", 52 | "KEY_MINUS", 53 | "KEY_EQUAL", 54 | "KEY_LEFT_BRACE", 55 | "KEY_RIGHT_BRACE", 56 | "KEY_BACKSLASH", 57 | "KEY_HASH_TILDE", 58 | "KEY_SEMICOLON", 59 | "KEY_APOSTROPHE", 60 | "KEY_GRAVE", 61 | "KEY_COMMA", 62 | "KEY_DOT", 63 | "KEY_SLASH", 64 | "KEY_CAPSLOCK", 65 | "KEY_F1", 66 | "KEY_F2", 67 | "KEY_F3", 68 | "KEY_F4", 69 | "KEY_F5", 70 | "KEY_F6", 71 | "KEY_F7", 72 | "KEY_F8", 73 | "KEY_F9", 74 | "KEY_F10", 75 | "KEY_F11", 76 | "KEY_F12", 77 | "KEY_PAUSE", 78 | "KEY_INSERT", 79 | "KEY_HOME", 80 | "KEY_PAGE_UP", 81 | "KEY_DELETE", 82 | "KEY_END", 83 | "KEY_PAGE_DOWN", 84 | "KEY_RIGHT_ARROW", 85 | "KEY_LEFT_ARROW", 86 | "KEY_DOWN_ARROW", 87 | "KEY_UP_ARROW", 88 | "KEYPAD_SLASH", 89 | "KEYPAD_ASTERISK", 90 | "KEYPAD_MINUS", 91 | "KEYPAD_PLUS", 92 | "KEYPAD_ENTER", 93 | "KEY_102ND", 94 | "KEY_POWER", 95 | "KEY_HELP", 96 | "KEY_MENU", 97 | "KEY_SELECT", 98 | "KEY_COPY", 99 | "KEY_PASTE", 100 | "KEY_MUTE", 101 | "KEY_VOLUME_UP", 102 | "KEY_VOLUME_DOWN", 103 | "KEYPAD_COMMA", 104 | "KEY_RO", 105 | "KEY_YEN", 106 | "KEYPAD_JPCOMMA", 107 | "KEY_ZENKAKU_HANKAKU", 108 | "KEY_LEFT_GUI", 109 | "KEY_MEDIA_PLAY_PAUSE", 110 | "KEY_MEDIA_STOP_CD", 111 | "KEY_MEDIA_PREVIOUS_SONG", 112 | "KEY_MEDIA_NEXT_SONG", 113 | "KEY_MEDIA_WWW", 114 | "KEY_MEDIA_BACK", 115 | "KEY_MEDIA_FORWARD", 116 | "KEY_MEDIA_SLEEP", 117 | "KEY_MEDIA_REFRESH", 118 | "KEY_MEDIA_CALC" 119 | ], 120 | "consumer": [ 121 | "CONSUMER_POWER", 122 | "CONSUMER_SLEEP_MODE", 123 | "CONSUMER_MENU", 124 | "CONSUMER_MENU_ESCAPE", 125 | "CONSUMER_RED_MENU_BUTTON", 126 | "CONSUMER_GREEN_MENU_BUTTON", 127 | "CONSUMER_BLUE_MENU_BUTTON", 128 | "CONSUMER_YELLOW_MENU_BUTTON", 129 | "CONSUMER_ASPECT", 130 | "CONSUMER_MODE_SELECT_3D", 131 | "CONSUMER_DISPLAY_BRIGHTNESS_INCREMENT", 132 | "CONSUMER_DISPLAY_BRIGHTNESS_DECREMENT", 133 | "CONSUMER_DISPLAY_BACKLIGHT_TOGGLE", 134 | "CONSUMER_DISPLAY_SET_BRIGHTNESS_TO_MINIMUM", 135 | "CONSUMER_DISPLAY_SET_BRIGHTNESS_TO_MAXIMUM", 136 | "CONSUMER_MEDIA_SELECT_WWW", 137 | "CONSUMER_QUIT", 138 | "CONSUMER_CHANNEL_INCREMENT", 139 | "CONSUMER_CHANNEL_DECREMENT", 140 | "CONSUMER_VCR_PLUS", 141 | "CONSUMER_PLAY", 142 | "CONSUMER_PAUSE", 143 | "CONSUMER_RECORD", 144 | "CONSUMER_FAST_FORWARD", 145 | "CONSUMER_REWIND", 146 | "CONSUMER_SCAN_NEXT_TRACK", 147 | "CONSUMER_SCAN_PREVIOUS_TRACK", 148 | "CONSUMER_STOP", 149 | "CONSUMER_MUTE", 150 | "CONSUMER_VOLUME_INCREMENT", 151 | "CONSUMER_VOLUME_DECREMENT", 152 | "CONSUMER_AC_HOME", 153 | "CONSUMER_AC_BACK" 154 | ], 155 | "system": [ 156 | "SYSTEM_SLEEP", 157 | "SYSTEM_MAIN_MENU", 158 | "SYSTEM_COLD_RESTART" 159 | ] 160 | }, 161 | "command_mapping": { 162 | "BACK": "CONSUMER_AC_BACK", 163 | "HOME": "CONSUMER_AC_HOME", 164 | "GUIDE": "KEY_MENU", 165 | "SETTINGS": "CONSUMER_MENU", 166 | "EXIT": "CONSUMER_QUIT", 167 | "POWER_TOGGLE": "KEY_POWER", 168 | "VOLUME_UP": "KEY_VOLUME_UP", 169 | "VOLUME_DOWN": "KEY_VOLUME_DOWN", 170 | "MUTE_TOGGLE": "KEY_MUTE", 171 | "CHANNEL_UP": "CONSUMER_CHANNEL_INCREMENT", 172 | "CHANNEL_DOWN": "CONSUMER_CHANNEL_DECREMENT", 173 | "PREVIOUS": "CONSUMER_SCAN_PREVIOUS_TRACK", 174 | "NEXT": "CONSUMER_SCAN_NEXT_TRACK", 175 | "REWIND": "CONSUMER_REWIND", 176 | "FAST_FORWARD": "CONSUMER_FAST_FORWARD", 177 | "PLAY_PAUSE": "CONSUMER_PLAY", 178 | "STOP": "CONSUMER_STOP", 179 | "RECORD": "CONSUMER_RECORD", 180 | "CURSOR_LEFT": "KEY_LEFT_ARROW", 181 | "CURSOR_RIGHT": "KEY_RIGHT_ARROW", 182 | "CURSOR_UP": "KEY_UP_ARROW", 183 | "CURSOR_DOWN": "KEY_DOWN_ARROW", 184 | "CURSOR_ENTER": "KEY_RETURN", 185 | "FUNCTION_RED": "CONSUMER_RED_MENU_BUTTON", 186 | "FUNCTION_GREEN": "CONSUMER_GREEN_MENU_BUTTON", 187 | "FUNCTION_BLUE": "CONSUMER_BLUE_MENU_BUTTON", 188 | "FUNCTION_YELLOW": "CONSUMER_YELLOW_MENU_BUTTON" 189 | }, 190 | "user_interface": { 191 | "pages": [ 192 | { 193 | "grid": { 194 | "height": 6, 195 | "width": 4 196 | }, 197 | "name": "Numbers", 198 | "page_id": "numbers", 199 | "items": [ 200 | { 201 | "command": { 202 | "cmd_id": "KEY_1" 203 | }, 204 | "location": { 205 | "x": 0, 206 | "y": 0 207 | }, 208 | "text": "1", 209 | "type": "text" 210 | }, 211 | { 212 | "command": { 213 | "cmd_id": "KEY_2" 214 | }, 215 | "location": { 216 | "x": 1, 217 | "y": 0 218 | }, 219 | "text": "2", 220 | "type": "text" 221 | }, 222 | { 223 | "command": { 224 | "cmd_id": "KEY_3" 225 | }, 226 | "location": { 227 | "x": 2, 228 | "y": 0 229 | }, 230 | "text": "3", 231 | "type": "text" 232 | }, 233 | { 234 | "command": { 235 | "cmd_id": "KEY_4" 236 | }, 237 | "location": { 238 | "x": 0, 239 | "y": 1 240 | }, 241 | "text": "4", 242 | "type": "text" 243 | }, 244 | { 245 | "command": { 246 | "cmd_id": "KEY_5" 247 | }, 248 | "location": { 249 | "x": 1, 250 | "y": 1 251 | }, 252 | "text": "5", 253 | "type": "text" 254 | }, 255 | { 256 | "command": { 257 | "cmd_id": "KEY_6" 258 | }, 259 | "location": { 260 | "x": 2, 261 | "y": 1 262 | }, 263 | "text": "6", 264 | "type": "text" 265 | }, 266 | { 267 | "command": { 268 | "cmd_id": "KEY_7" 269 | }, 270 | "location": { 271 | "x": 0, 272 | "y": 2 273 | }, 274 | "text": "7", 275 | "type": "text" 276 | }, 277 | { 278 | "command": { 279 | "cmd_id": "KEY_8" 280 | }, 281 | "location": { 282 | "x": 1, 283 | "y": 2 284 | }, 285 | "text": "8", 286 | "type": "text" 287 | }, 288 | { 289 | "command": { 290 | "cmd_id": "KEY_9" 291 | }, 292 | "location": { 293 | "x": 2, 294 | "y": 2 295 | }, 296 | "text": "9", 297 | "type": "text" 298 | }, 299 | { 300 | "command": { 301 | "cmd_id": "KEY_0" 302 | }, 303 | "location": { 304 | "x": 1, 305 | "y": 3 306 | }, 307 | "text": "0", 308 | "type": "text" 309 | }, 310 | { 311 | "command": { 312 | "cmd_id": "KEY_ESC" 313 | }, 314 | "location": { 315 | "x": 3, 316 | "y": 0 317 | }, 318 | "text": "ESC", 319 | "type": "text" 320 | }, 321 | { 322 | "command": { 323 | "cmd_id": "CONSUMER_AC_EXIT" 324 | }, 325 | "location": { 326 | "x": 3, 327 | "y": 2 328 | }, 329 | "text": "Exit", 330 | "type": "text" 331 | }, 332 | { 333 | "command": { 334 | "cmd_id": "CONSUMER_AC_BACK" 335 | }, 336 | "location": { 337 | "x": 3, 338 | "y": 3 339 | }, 340 | "text": "Back", 341 | "type": "text" 342 | }, 343 | { 344 | "command": { 345 | "cmd_id": "KEYPAD_PLUS" 346 | }, 347 | "location": { 348 | "x": 0, 349 | "y": 4 350 | }, 351 | "text": "+", 352 | "type": "text" 353 | }, 354 | { 355 | "command": { 356 | "cmd_id": "KEYPAD_MINUS" 357 | }, 358 | "location": { 359 | "x": 0, 360 | "y": 5 361 | }, 362 | "text": "-", 363 | "type": "text" 364 | }, 365 | { 366 | "command": { 367 | "cmd_id": "KEY_PAGE_UP" 368 | }, 369 | "location": { 370 | "x": 1, 371 | "y": 4 372 | }, 373 | "text": "Pg up", 374 | "type": "text" 375 | }, 376 | { 377 | "command": { 378 | "cmd_id": "KEY_PAGE_DOWN" 379 | }, 380 | "location": { 381 | "x": 1, 382 | "y": 5 383 | }, 384 | "text": "Pg down", 385 | "type": "text" 386 | }, 387 | { 388 | "command": { 389 | "cmd_id": "KEY_COMMA" 390 | }, 391 | "location": { 392 | "x": 2, 393 | "y": 4 394 | }, 395 | "text": ",", 396 | "type": "text" 397 | }, 398 | { 399 | "command": { 400 | "cmd_id": "KEY_DOT" 401 | }, 402 | "location": { 403 | "x": 3, 404 | "y": 4 405 | }, 406 | "text": ".", 407 | "type": "text" 408 | }, 409 | { 410 | "command": { 411 | "cmd_id": "KEY_MENU" 412 | }, 413 | "location": { 414 | "x": 2, 415 | "y": 5 416 | }, 417 | "text": "Menu", 418 | "type": "text" 419 | }, 420 | { 421 | "command": { 422 | "cmd_id": "CONSUMER_MEDIA_SELECT_PROGRAM_GUIDE" 423 | }, 424 | "location": { 425 | "x": 3, 426 | "y": 5 427 | }, 428 | "text": "Guide", 429 | "type": "text" 430 | } 431 | ] 432 | }, 433 | { 434 | "name": "Media Consumer", 435 | "page_id": "media2", 436 | "grid": { 437 | "height": 6, 438 | "width": 4 439 | }, 440 | "items": [ 441 | { 442 | "location": { 443 | "x": 0, 444 | "y": 0 445 | }, 446 | "size": { 447 | "height": 1, 448 | "width": 4 449 | }, 450 | "text": "Consumer codes", 451 | "type": "text" 452 | }, 453 | { 454 | "command": { 455 | "cmd_id": "CONSUMER_SCAN_PREVIOUS_TRACK" 456 | }, 457 | "icon": "uc:prev", 458 | "location": { 459 | "x": 0, 460 | "y": 5 461 | }, 462 | "type": "icon" 463 | }, 464 | { 465 | "command": { 466 | "cmd_id": "CONSUMER_PLAY" 467 | }, 468 | "icon": "uc:play", 469 | "location": { 470 | "x": 1, 471 | "y": 5 472 | }, 473 | "type": "icon" 474 | }, 475 | { 476 | "command": { 477 | "cmd_id": "CONSUMER_PAUSE" 478 | }, 479 | "icon": "uc:pause", 480 | "location": { 481 | "x": 2, 482 | "y": 5 483 | }, 484 | "type": "icon" 485 | }, 486 | { 487 | "command": { 488 | "cmd_id": "CONSUMER_SCAN_NEXT_TRACK" 489 | }, 490 | "icon": "uc:next", 491 | "location": { 492 | "x": 3, 493 | "y": 5 494 | }, 495 | "type": "icon" 496 | }, 497 | { 498 | "command": { 499 | "cmd_id": "CONSUMER_REWIND" 500 | }, 501 | "icon": "uc:bw", 502 | "location": { 503 | "x": 0, 504 | "y": 4 505 | }, 506 | "type": "icon" 507 | }, 508 | { 509 | "command": { 510 | "cmd_id": "CONSUMER_STOP" 511 | }, 512 | "icon": "uc:stop", 513 | "location": { 514 | "x": 1, 515 | "y": 4 516 | }, 517 | "type": "icon" 518 | }, 519 | { 520 | "command": { 521 | "cmd_id": "CONSUMER_RECORD" 522 | }, 523 | "icon": "uc:rec", 524 | "location": { 525 | "x": 2, 526 | "y": 4 527 | }, 528 | "type": "icon" 529 | }, 530 | { 531 | "command": { 532 | "cmd_id": "CONSUMER_FAST_FORWARD" 533 | }, 534 | "icon": "uc:ff", 535 | "location": { 536 | "x": 3, 537 | "y": 4 538 | }, 539 | "type": "icon" 540 | } 541 | ] 542 | } 543 | ] 544 | } 545 | } -------------------------------------------------------------------------------- /doc/bt/suspend_behaviour.md: -------------------------------------------------------------------------------- 1 | # BT suspend / wakeup patterns 2 | 3 | When the remote goes into deep sleep mode, the Bluetooth controller is put to sleep and re-initialized at wakeup. 4 | - Some devices will show a disconnect & connect message or popup when the remote wakes up. 5 | - Different sleep behaviour can only be configured with the REST Core-API at the moment. 6 | - Default suspend actions if not defined (empty array): disable advertisements, then put BT controller to sleep 7 | - Default wakeup actions if not defined (empty array): restart BT controller 8 | 9 | ```shell 10 | curl --location --request PATCH 'http://$IP/api/cfg/bt' \ 11 | --header 'Content-Type: application/json' \ 12 | --user 'web-configurator:$PIN' \ 13 | --data '{ 14 | "suspend_actions": ["DISABLE_ADVERTISEMENTS", "SLEEP"], 15 | "wakeup_actions": ["RESTART"] 16 | }' 17 | ``` 18 | 19 | ## Do nothing, best effort 20 | 21 | ```json 22 | { 23 | "suspend_actions": ["NONE"], 24 | "wakeup_actions": ["NONE"] 25 | } 26 | ``` 27 | 28 | - keeps connection during sleep 29 | - fastest way to send key commands after wakeup (if connection is still alive) 30 | - least reliable with certain devices 31 | - connection might hang after wakeup: R2 shows connected, no error while sending keys, but central shows disconnected 32 | - does not work if central is restarted while R2 is suspended 33 | 34 | 35 | ## Disconnect connections at wakeup 36 | 37 | ```json 38 | { 39 | "suspend_actions": ["NONE"], 40 | "wakeup_actions": ["DISCONNECT"] 41 | } 42 | ``` 43 | 44 | - trigger central reconnection at wakeup 45 | - more reliable than best effort 46 | - some devices will show "connected" & "disconnected" popups after wakeup 47 | 48 | 49 | ## Restart at wakeup 50 | 51 | ```json 52 | { 53 | "suspend_actions": ["NONE"], 54 | "wakeup_actions": ["RESTART"] 55 | } 56 | ``` 57 | 58 | - keeps connection during sleep 59 | - restart at wakeup to cleanly reconnect centrals 60 | - small chance that central considers connection as non-working 61 | - some devices will show "connected" & "disconnected" popups after wakeup 62 | 63 | 64 | ## HCI sleep mode, restart at wakeup 65 | 66 | ```json 67 | { 68 | "suspend_actions": ["DISABLE_ADVERTISEMENTS", "SLEEP"], 69 | "wakeup_actions": ["RESTART"] 70 | } 71 | ``` 72 | 73 | - disconnect central before HCI sleep mode 74 | - restart at wakeup to cleanly reconnect centrals 75 | - seems to be the most reliable option that centrals reconnect 76 | - some devices will show "connected" popup at sleep & "disconnected" popup after wakeup 77 | 78 | 79 | ## HCI sleep mode & resume at wakeup 80 | 81 | About 1 sec faster than restart (~ 3sec). 82 | 83 | ```json 84 | { 85 | "suspend_actions": ["DISABLE_ADVERTISEMENTS", "SLEEP"], 86 | "wakeup_actions": ["ENABLE", "ENABLE_ADVERTISEMENTS"] 87 | } 88 | ``` 89 | 90 | - disconnect central before HCI sleep mode 91 | - startup BT controller at wakeup and re-enable advertisements for centrals to reconnect 92 | - some devices will show "connected" popup at sleep & "disconnected" popup after wakeup 93 | -------------------------------------------------------------------------------- /doc/discovery.md: -------------------------------------------------------------------------------- 1 | ## Remote Two DNS-SD lookup 2 | 3 | The Remote Two will announce itself as service type `_uc-remote._tcp`. 4 | 5 | The service lookup will return IP address and port number and other information in the TXT record data like model number 6 | and API version of the remote. 7 | 8 | | Key | Description | 9 | |------------|-------------------------------------------------------| 10 | | ver | Version number | 11 | | ver_api | API version number | 12 | | model | Model number | 13 | | https_port | HTTPS port if enabled | 14 | 15 | ### Model numbers 16 | 17 | The `model` key specifies the physical device or the [core-simulator](https://github.com/unfoldedcircle/core-simulator): 18 | 19 | | Model | Description | 20 | |----------------|-----------------------| 21 | | UCR3 | Remote 3 | 22 | | UCR2 | Remote Two | 23 | | UCR2-simulator | Remote Core simulator | 24 | | YIO1 | YIO Remote | 25 | 26 | ### Examples 27 | 28 | macOS: 29 | ```bash 30 | dns-sd -Z _uc-remote._tcp 31 | ``` 32 | 33 | For a UI tool, see [Discovery](https://apps.apple.com/us/app/discovery-dns-sd-browser/id1381004916). 34 | 35 | Linux: 36 | ```bash 37 | avahi-browse -d local --resolve _uc-remote._tcp -t 38 | ``` 39 | -------------------------------------------------------------------------------- /doc/entities/README.md: -------------------------------------------------------------------------------- 1 | # Entities 2 | 3 | Entities represent devices by describing features and exposing controls. An integration can offer available entities for 4 | control. The user might select and configure entities, that will be available for the user interface. 5 | 6 | Based on the entity type, device class and features, a specific user interface is shown. These features and other entity 7 | characteristics are set by the integration. 8 | 9 | An integration driver provides entity definitions to the remote which then can be used in profiles and pages for the 10 | user to interact with the device. 11 | 12 | Whenever the user interacts with an entity from an integration driver, the remote sends command requests to the 13 | integration driver. Once the entity or physical device has been updated the integration driver sends a change event 14 | message back to the remote to notify it about the updated state or attribute(s). 15 | 16 | Supported entities: 17 | 18 | - [Button](entity_button.md) 19 | - [Switch](entity_switch.md) 20 | - [Climate](entity_climate.md) 21 | - [Cover](entity_cover.md) 22 | - [Light](entity_light.md) 23 | - [Media Player](entity_media_player.md) 24 | - [Remote](entity_remote.md) 25 | - [Sensor](entity_sensor.md) 26 | - [IR-Emitter](entity_ir_emitter.md) 27 | 28 | The 🚧 icon within the entity descriptions indicates a planned feature. 29 | 30 | ## Integration API 31 | 32 | To make entities available to the remote, an integration driver needs to provide `entity` definition for each entity 33 | instance, filled with entity type specific data. 34 | 35 | The remote retrieves the available entities from the integration driver with the `get_available_entities` request 36 | message: 37 | 38 | ```json 39 | { 40 | "kind": "req", 41 | "id": 11, 42 | "msg": "get_available_entities" 43 | } 44 | ``` 45 | 46 | The integration driver then responds with the `available_entities` message containing all entity definitions: 47 | 48 | ```json 49 | { 50 | "kind": "resp", 51 | "req_id": 11, 52 | "msg": "available_entities", 53 | "code": 200, 54 | "msg_data": { 55 | "available_entities": [ 56 | { 57 | "entity_id": "button-1", 58 | "entity_type": "button", 59 | "name": { 60 | "en": "Ring dinner bell" 61 | } 62 | }, 63 | { 64 | "entity_id": "blind-1", 65 | "entity_type": "cover", 66 | "device_class": "blind", 67 | "features": [ 68 | "open", 69 | "close", 70 | "stop", 71 | "position" 72 | ], 73 | "name": { 74 | "en": "Living room blinds", 75 | "de": "Wohnzimmer Jalousien" 76 | } 77 | }, 78 | { 79 | "entity_id": "light-1", 80 | "entity_type": "light", 81 | "features": [ 82 | "on_off", 83 | "dim", 84 | "colour" 85 | ], 86 | "name": { 87 | "en": "Living room" 88 | } 89 | } 90 | ] 91 | } 92 | } 93 | ``` 94 | 95 | All entities share a set of common attributes like `name` and the `UNAVAILABLE`, `UNKNOWN` states. 96 | An entity implementation defines additional features, attributes, states and options which need to be handled in the 97 | driver implementation. 98 | 99 | Please see [Integration AsyncAPI definition](../../integration-api/UCR-integration-asyncapi.yaml) for more information and additional options. 100 | 101 | ### Common entity definition 102 | 103 | All entities share the following properties in the `entity` structure: 104 | 105 | | Entity property | Description | 106 | |-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 107 | | entity_id | Unique identifier of the entity for command and event messages. | 108 | | entity_type | Entity device type name: one of the supported entities. | 109 | | device_id | Optional associated device, if the integration driver supports multiple devices. | 110 | | features | Supported features of the entity. See concrete entity definition for available features. | 111 | | name | Human readable name of the entity or device. Default value of the entity name for the entity configuration in the remote. Changeable by the user. | 112 | | area | Optional area name, e.g. `Living room`. This information might be used by the UI in the setup process to automatically create profile pages for all areas returned in the available entities. | 113 | 114 | ### Features 115 | 116 | The entity features define the controllable properties of an entity. They also influence how an entity is shown in the 117 | UI, e.g. if there are dimmer and colour selection controls for a lamp, or blind position controls. 118 | 119 | ### Attributes 120 | 121 | Entity attributes are controlled or influenced by features. A features can act on multiple attributes, 122 | e.g. the open command for a cover entity will set the state and if supported, also the current position of the cover. 123 | 124 | An attribute value should represent the correct state of the physical device being controlled whenever possible. An 125 | integration driver might simulate intermediate states and position values, if the current state is either not readable 126 | from the device or involves expensive operations. E.g. if it's known that opening a window blind takes 30 seconds, the 127 | integration driver can start a timer and send approximated status updates during the opening time frame. 128 | This will show a nice animation in the remote UI instead of immediately jumping from closed to open while the physical 129 | blind is still opening. 130 | 131 | ### States 132 | 133 | The entity `state` attribute holds the following common values which an integration driver can choose to support: 134 | 135 | | Value | Description | 136 | |-------------|-------------------------------------------------------------------------------------------------------------------------| 137 | | UNAVAILABLE | The entity is currently not available. The UI will render the entity as inactive until the entity becomes active again. | 138 | | UNKNOWN | The entity is available but the current state is unknown. | 139 | 140 | ### Device classes 141 | 142 | An entity type can be further classified by an optional device class. This has mainly an influence on the UI 143 | representation how an entity is presented to a user. E.g. a `cover` entity has multiple device classes like `blind` or 144 | `garage`. 145 | 146 | ### Options 147 | 148 | Options are feature modifiers to customize certain behaviour, e.g. the default `on_off` feature for a switch expects the 149 | current state to be retrievable. If a switch can't provide its current state, it becomes a toggle switch with setting 150 | the `readable` option to `false`. 151 | 152 | ### Commands 153 | 154 | For all user or scene interactions with an entity, like switching it on or changing an attribute, the remote sends an 155 | `entity_command` message to the integration driver. This message contains the entity command with optional parameters 156 | to execute by the driver: 157 | 158 | ```json 159 | { 160 | "kind": "req", 161 | "id": 123, 162 | "msg": "entity_command", 163 | "msg_data": { 164 | "entity_type": "$TYPE", 165 | "entity_id": "$ID", 166 | "cmd_id": "$COMMAND", 167 | "params": { 168 | "$ParameterName1": "$ParameterValue1", 169 | "$ParameterName2": "$ParameterValue2", 170 | "$ParameterNameN": "$ParameterValueN" 171 | } 172 | } 173 | } 174 | ``` 175 | 176 | - The command is specified in `msg_data.cmd_id`. 177 | - Optional command parameters are passed as key / value pairs under `msg_data.params`. 178 | - The available commands and parameters are specified in the corresponding entity documentation. 179 | - The command request must be acknowledged with a `result` response message. 180 | - If at the time of receiving the command the driver already knows that it can't execute the command, it can send 181 | back an error to notify that the command execution is not possible. 182 | Otherwise, the remote assumes that the command is executed. 183 | - Once the value has been set or confirmed by the physical device, an `entity_change` event message must be sent by the 184 | integration driver with the new value(s). Of course, this only applies to stateful attributes and not to buttons or 185 | "fire and forget" commands. 186 | 187 | ### Events 188 | 189 | The `entity_change` event must be emitted by the integration driver if the state of the entity changes. Either after an 190 | `entity_command` or if the entity has been updated otherwise, e.g. externally through a user or another system. 191 | This keeps the remote in sync with the real state of the entity without the need of constant polling. 192 | 193 | Event message structure: 194 | 195 | ```json 196 | { 197 | "kind": "event", 198 | "msg": "entity_change", 199 | "cat": "ENTITY", 200 | "msg_data": { 201 | "entity_type": "$TYPE", 202 | "entity_id": "$ID", 203 | "attributes": { 204 | "$Attribute1": "$Value1", 205 | "$Attribute2": "$Value2", 206 | "$AttributeN": "$ValueN" 207 | } 208 | } 209 | } 210 | ``` 211 | 212 | The available `$Attribute(s)` and their `$Value(s)` are specified in the entity documentation. 213 | 214 | Common event attributes available for all entities: 215 | 216 | | Attribute | Description | 217 | |---------------|-------------------| 218 | | state | New entity state. | 219 | 220 | At least one attribute must be specified in the `entity_change` message. If the common entity `state` and an addition 221 | attribute changed at the same time, they may both be included in the same message. It's also valid to always send every 222 | entity attribute. The remote will filter out non-changed attributes and only update changes in the UI. 223 | -------------------------------------------------------------------------------- /doc/entities/entity_button.md: -------------------------------------------------------------------------------- 1 | # Button Entity 2 | 3 | A button entity can fire an event or start an action which cannot be further controlled once started. 4 | 5 | This can be used for "fire and forget" commands, e.g. running a system command, sending an IR code, restart a 6 | device, reset something etc. 7 | 8 | A button is stateless. To represent something that can be turned on and off, the [switch](entity_switch.md) entity 9 | should be used. 10 | 11 | ## Features 12 | 13 | | Name | R | W | Description | 14 | |-------|-----|-----|---------------------------------------------------------------------| 15 | | press | ❌ | ✅ | Default feature of a button. Always present, even if not specified. | 16 | 17 | ### States 18 | 19 | The button entity only supports the `ON` state and the [common entity states](README.md#states). 20 | 21 | | Value | Description | 22 | |-----------|--------------------------| 23 | | AVAILABLE | The button is available. | 24 | 25 | ### Device Classes 26 | 27 | None. 28 | 29 | ### Options 30 | 31 | None: the button entity doesn't support additional options. 32 | 33 | ## Integration API 34 | 35 | ### Commands 36 | 37 | The integration driver has to implement a handler for the `entity_command` message to process the following command 38 | requests in `msg_data.cmd_id`. 39 | 40 | | cmd_id | Parameters | Description | 41 | |--------|------------|------------------| 42 | | push | - | Push the button! | 43 | 44 | ### Events 45 | 46 | None: the button entity is stateless and the remote doesn't need to be notified when the button was pressed externally. 47 | 48 | ### Command examples 49 | 50 | #### push 51 | 52 | ```json 53 | { 54 | "kind": "req", 55 | "id": 123, 56 | "msg": "entity_command", 57 | "msg_data": { 58 | "entity_type": "button", 59 | "entity_id": "button-1", 60 | "command_id": "push" 61 | } 62 | } 63 | ``` 64 | -------------------------------------------------------------------------------- /doc/entities/entity_climate.md: -------------------------------------------------------------------------------- 1 | # Climate Entity 2 | 3 | A climate entity controls heating, ventilation and air conditioning (HVAC) devices. This can range from simple fans to 4 | personal air conditioning units to integrated building devices. 5 | 6 | ## Features 7 | 8 | | Name | R | W | Description | 9 | |-----------------------------|-----|-----|---------------------------------------------------------------------------------------------------------------------------------------------| 10 | | on_off | ✅ | ✅ | The device can be turned on and off. The active HVAC mode after power on is device specific and must be reflected in the `state` attribute. | 11 | | heat | ❌ | ✅ | The device supports heating. | 12 | | cool | ❌ | ✅ | The device supports cooling. | 13 | | current_temperature | ✅ | ❌ | The device can measure the current temperature. | 14 | | target_temperature | ✅ | ✅ | The device supports a target temperature for heating or cooling. | 15 | | 🚧 target_temperature_range | ✅ | ✅ | The device supports a target temperature range. | 16 | | 🚧 fan | ❌ | ✅ | The device has a controllable fan. | 17 | 18 | ### Attributes 19 | 20 | Entity attributes are controlled by features. Multiple features can act on the same attribute. 21 | 22 | | Attribute | Features | Type | Values | Description | 23 | |----------------------------|--------------------------|--------|-------------------|--------------------------------------------------------| 24 | | state | on_off | enum | [States](#states) | State of the climate device, corresponds to HVAC mode. | 25 | | | heat | | | | 26 | | | cool | | | | 27 | | | fan | | | | 28 | | current_temperature | current_temperature | number | | | 29 | | target_temperature | target_temperature | number | | | 30 | | 🚧 target_temperature_high | target_temperature_range | number | | | 31 | | 🚧 target_temperature_low | target_temperature_range | number | | | 32 | | 🚧 fan_mode | fan | enum | | | 33 | 34 | See [entity options](#options) for temperature unit and ranges. 35 | 36 | ### States 37 | 38 | The climate entity provides the following entity `state` values and represents the currently set HVAC mode of the device: 39 | 40 | | Value | Description | 41 | |-----------|-----------------------------------------------------------------------------------------------------------------------| 42 | | OFF | The climate device is switched off. | 43 | | HEAT | The device is set to heating, optionally to a set target temperature. | 44 | | COOL | The device is set to cooling, optionally to a set target temperature. | 45 | | HEAT_COOL | The device is set to heat or cool to a target temperature range. | 46 | | FAN | Fan-only mode without heating or cooling. | 47 | | AUTO | The device is set to automatic mode. This is device dependant, e.g. according to a schedule, presence detection, etc. | 48 | 49 | Note: the current mode may not be the active state of the device. E.g. if the mode is set to `AUTO` the climate unit 50 | may be heating, cooling, idle, etc. at a specific point in time. 51 | 52 | See [common entity states](README.md#states). 53 | 54 | ### Device Classes 55 | 56 | None. 57 | 58 | ### Options 59 | 60 | | Name | Type | Default | Description | 61 | |-------------------------|--------|---------|--------------------------------------------------------------------------------------------------------------------------------| 62 | | temperature_unit | enum | CELSIUS | The unit of temperature measurement: `CELSIUS`, `FAHRENHEIT`. If not specified, the remote settings are used. | 63 | | target_temperature_step | number | 0.5 / 1 | Step value for the UI for setting the target temperature. Defaults: `CELSIUS` = 0.5, 'FAHRENHEIT` = 1. Smallest step size: 0.1 | 64 | | max_temperature | number | 30 | Maximum temperature to show in the UI for the target temperature range. | 65 | | min_temperature | number | 10 | Minimum temperature to show in the UI for the target temperature range. | 66 | | 🚧 fan_modes | enum | | | 67 | 68 | 🚧 Planned feature. 69 | 70 | ## Integration API 71 | 72 | ### Commands 73 | 74 | The integration driver has to implement a handler for the `entity_command` message to process the following command 75 | requests within `msg_data.cmd_id`. 76 | 77 | - The command request must be acknowledged with a `result` response message. 78 | - Once the value has been set or confirmed by the physical device, an `entity_change` event message with the new 79 | value(s) must be sent. 80 | 81 | | cmd_id | Parameters | Description | 82 | |-----------------------------|------------------|----------------------------------------------------------------| 83 | | on | - | Switch on the climate device. | 84 | | off | - | Switch off the climate device. | 85 | | hvac_mode | hvac_mode | Set the device to heating, cooling, etc. See [state](#states). | 86 | | target_temperature | temperature | Change the target temperature | 87 | | 🚧 target_temperature_range | temperature_high | | 88 | | | temperature_low | | 89 | | 🚧 fan_mode | enum | | 90 | 91 | ### Events 92 | 93 | The `entity_change` event must be emitted by the integration driver if the state or an attribute of the climate device 94 | changes. Either after an`entity_command` or if the climate device has been updated externally through a user or another 95 | system. This keeps the remote in sync with the real state of the entity without the need of constant polling. 96 | 97 | The following attributes are supported: 98 | 99 | | Attribute | Description | 100 | |----------------------------|----------------------------------------| 101 | | hvac_mode | New HVAC mode. See [state](#states). | 102 | | current_temperature | Current temperature value. | 103 | | target_temperature | Changed target temperature value. | 104 | | 🚧 target_temperature_high | Changed high target temperature value. | 105 | | 🚧 target_temperature_low | Changed low target temperature value. | 106 | | 🚧 fan_mode | Changed fan mode. | 107 | 108 | At least one attribute must be specified in the `entity_change` message. If the entity `state` and a climate 109 | attribute changed at the same time, they may both be included in the same message. It's also valid to always send every 110 | attribute. 111 | 112 | ### Command examples 113 | 114 | #### on 115 | 116 | ```json 117 | { 118 | "kind": "req", 119 | "id": 123, 120 | "msg": "entity_command", 121 | "msg_data": { 122 | "entity_type": "climate", 123 | "entity_id": "climate-1", 124 | "cmd_id": "on" 125 | } 126 | } 127 | ``` 128 | #### off 129 | 130 | ```json 131 | { 132 | "kind": "req", 133 | "id": 123, 134 | "msg": "entity_command", 135 | "msg_data": { 136 | "entity_type": "climate", 137 | "entity_id": "climate-1", 138 | "cmd_id": "off" 139 | } 140 | } 141 | ``` 142 | 143 | #### hvac_mode 144 | 145 | ```json 146 | { 147 | "kind": "req", 148 | "id": 123, 149 | "msg": "entity_command", 150 | "msg_data": { 151 | "entity_type": "climate", 152 | "entity_id": "climate-1", 153 | "cmd_id": "hvac_mode", 154 | "params": { 155 | "hvac_mode": "COOL" 156 | } 157 | } 158 | } 159 | ``` 160 | 161 | Combined with target temperature 162 | 163 | ```json 164 | { 165 | "kind": "req", 166 | "id": 123, 167 | "msg": "entity_command", 168 | "msg_data": { 169 | "entity_type": "climate", 170 | "entity_id": "climate-1", 171 | "cmd_id": "hvac_mode", 172 | "params": { 173 | "hvac_mode": "COOL", 174 | "temperature": 23 175 | } 176 | } 177 | } 178 | ``` 179 | 180 | #### target_temperature 181 | 182 | ```json 183 | { 184 | "kind": "req", 185 | "id": 123, 186 | "msg": "entity_command", 187 | "msg_data": { 188 | "entity_type": "climate", 189 | "entity_id": "climate-1", 190 | "cmd_id": "target_temperature", 191 | "params": { 192 | "temperature": 23 193 | } 194 | } 195 | } 196 | ``` 197 | 198 | ### Event examples 199 | 200 | #### State change event 201 | 202 | ```json 203 | { 204 | "kind": "event", 205 | "msg": "entity_change", 206 | "cat": "ENTITY", 207 | "msg_data": { 208 | "entity_type": "climate", 209 | "entity_id": "climate-1", 210 | "attributes": { 211 | "hvac_mode": "HEAT", 212 | "current_temperature": 19.5, 213 | "target_temperature": 21.0 214 | } 215 | } 216 | } 217 | ``` 218 | -------------------------------------------------------------------------------- /doc/entities/entity_cover.md: -------------------------------------------------------------------------------- 1 | # Cover Entity 2 | 3 | Entity for covering or opening things like blinds, window covers, curtains, etc. The entity _features_ specify the 4 | abilities of the cover and the controllable properties, whereas the _device class_ specifies the UI representation. 5 | 6 | ## Features 7 | 8 | | Name | R | W | Description | 9 | |------------------|-----|-----|---------------------------------------------------------------| 10 | | open | ✅ | ✅ | The cover can can be opened. | 11 | | close | ✅ | ✅ | The cover can can be closed. | 12 | | stop | ❌ | ✅ | Opening, closing or setting the position can be stopped. | 13 | | position | ✅ | ✅ | The cover can be moved to a specific position, e.g. 30% open. | 14 | | 🚧 tilt | ✅ | ✅ | The cover supports being tilted up and down. | 15 | | 🚧 tilt_stop | ❌ | ✅ | Tilting the cover can be stopped. | 16 | | 🚧 tilt_position | ✅ | ✅ | The cover can be moved to a specific tilt position. | 17 | 18 | 🚧 Planned feature. 19 | 20 | If no `tilt*` features are specified in the cover entity, the remote UI will not show the tilt support and the 21 | integration driver does not need to support the required tilt commands and events. 22 | 23 | ## Attributes 24 | 25 | | Attribute | Features | Type | Values | Description | 26 | |------------------|---------------|------|-------------------|-------------------------------------------------------------------| 27 | | state | open close | enum | [States](#states) | Default entity state attribute. Influenced by the cover commands. | 28 | | | stop | | | | 29 | | position | position | int | 0..100 | Current position of the cover: 0 = closed, 100 = open. | 30 | | | open close | | 0 / 100 | Position is set to min / max if the state reaches CLOSED / OPEN. | 31 | | 🚧 tilt_position | tilt_position | int | 0..100 | Current tilt position of the cover: 0 = no tilt, 100 = max tilt. | 32 | | | tilt_stop | | | | 33 | | | tilt | | 0 / 100 | Tilt position is set to min / max when tilted up or down. | 34 | 35 | ### States 36 | 37 | The cover entity provides the following entity `state` values: 38 | 39 | | Value | Description | 40 | |---------|-----------------------------------------------------------------------------------| 41 | | OPENING | The cover is in the process of opening. Either fully opened or to a set position. | 42 | | OPEN | The cover is in the open state. | 43 | | CLOSING | The cover is in the process of closing. Either fully closed or to a set position. | 44 | | CLOSED | The cover is in the closed state. | 45 | 46 | See [common entity states](README.md#states). 47 | 48 | ### Device classes 49 | 50 | Optional cover type. This can be used by the UI to represent the cover with a different icon, behaviour etc. 51 | 52 | | Name | Description | 53 | |-----------|------------------------------------------------------------------| 54 | | blind | Window blinds or shutters which can be opened, closed or tilted. | 55 | | curtain | Window curtain or drapes which can be opened or closed. | 56 | | garage | Controllable garage door. | 57 | | shade | Sun shades which can be opened to protect an area from the sun. | 58 | | 🚧 door | Controllable door which can be opened and closed. | 59 | | 🚧 gate | Controllable gate which can be opened and closed. | 60 | | 🚧 window | A window which can be opened, closed or tilted. | 61 | 62 | ### Options 63 | 64 | None: the cover entity doesn't support additional options. 65 | 66 | ## Integration API 67 | 68 | ### Commands 69 | 70 | The integration driver has to implement a handler for the `entity_command` message to process the following command 71 | requests in `msg_data.cmd_id`. 72 | 73 | | cmd_id | Parameters | Description | 74 | |--------------|---------------|-----------------------------------------------------------| 75 | | open | - | Open the cover. | 76 | | close | - | Close the cover. | 77 | | stop | - | Stop the current cover open, close or position operation. | 78 | | position | position | Set the cover to the given position. | 79 | | 🚧 tilt | tilt_position | Tilt the cover to the given position. | 80 | | 🚧 tilt_up | - | Tilt the cover fully up. | 81 | | 🚧 tilt_down | - | Tilt the cover fully down. | 82 | | 🚧 tilt_stop | - | Stop current tilt operation. | 83 | 84 | ### Events 85 | 86 | The `entity_change` event must be emitted by the integration driver if the state or an attribute of the cover changes. 87 | 88 | The following attributes are supported: 89 | 90 | | Attribute | Description | 91 | |------------------|-------------------------------| 92 | | state | New entity [state](#states). | 93 | | position | Current cover position value. | 94 | | 🚧 tilt_position | Current tilt position value. | 95 | 96 | At least one attribute must be specified in the `entity_change` message. If the entity `state` and a `position` 97 | attribute changed at the same time, they may both be included in the same message. It's also valid to always send every 98 | attribute. 99 | 100 | ### Command examples 101 | 102 | #### open 103 | 104 | ```json 105 | { 106 | "kind": "req", 107 | "id": 123, 108 | "msg": "entity_command", 109 | "msg_data": { 110 | "entity_type": "cover", 111 | "entity_id": "blind-1", 112 | "cmd_id": "open" 113 | } 114 | } 115 | ``` 116 | 117 | #### close 118 | 119 | ```json 120 | { 121 | "kind": "req", 122 | "id": 123, 123 | "msg": "entity_command", 124 | "msg_data": { 125 | "entity_type": "cover", 126 | "entity_id": "blind-1", 127 | "cmd_id": "close" 128 | } 129 | } 130 | ``` 131 | 132 | #### stop 133 | 134 | ```json 135 | { 136 | "kind": "req", 137 | "id": 123, 138 | "msg": "entity_command", 139 | "msg_data": { 140 | "entity_type": "cover", 141 | "entity_id": "blind-1", 142 | "cmd_id": "stop" 143 | } 144 | } 145 | ``` 146 | 147 | #### position 148 | 149 | ```json 150 | { 151 | "kind": "req", 152 | "id": 123, 153 | "msg": "entity_command", 154 | "msg_data": { 155 | "entity_type": "cover", 156 | "entity_id": "blind-1", 157 | "cmd_id": "position", 158 | "params": { 159 | "position": 70 160 | } 161 | } 162 | } 163 | ``` 164 | 165 | #### tilt 166 | 167 | ```json 168 | { 169 | "kind": "req", 170 | "id": 123, 171 | "msg": "entity_command", 172 | "msg_data": { 173 | "entity_type": "cover", 174 | "entity_id": "blind-1", 175 | "cmd_id": "tilt", 176 | "params": { 177 | "tilt_position": 45 178 | } 179 | } 180 | } 181 | ``` 182 | 183 | #### tilt_down 184 | 185 | ```json 186 | { 187 | "kind": "req", 188 | "id": 123, 189 | "msg": "entity_command", 190 | "msg_data": { 191 | "entity_type": "cover", 192 | "entity_id": "blind-1", 193 | "cmd_id": "tilt_down" 194 | } 195 | } 196 | ``` 197 | 198 | #### tilt_up 199 | 200 | ```json 201 | { 202 | "kind": "req", 203 | "id": 123, 204 | "msg": "entity_command", 205 | "msg_data": { 206 | "entity_type": "cover", 207 | "entity_id": "blind-1", 208 | "cmd_id": "tilt_up" 209 | } 210 | } 211 | ``` 212 | 213 | #### tilt_stop 214 | 215 | ```json 216 | { 217 | "kind": "req", 218 | "id": 123, 219 | "msg": "entity_command", 220 | "msg_data": { 221 | "entity_type": "cover", 222 | "entity_id": "blind-1", 223 | "cmd_id": "tilt_stop" 224 | } 225 | } 226 | ``` 227 | 228 | ### Event examples 229 | 230 | #### State change event 231 | 232 | ```json 233 | { 234 | "kind": "event", 235 | "msg": "entity_change", 236 | "cat": "ENTITY", 237 | "msg_data": { 238 | "entity_type": "cover", 239 | "entity_id": "blind-1", 240 | "attributes": { 241 | "state": "OPENING", 242 | "position": 72 243 | } 244 | } 245 | } 246 | ``` 247 | 248 | #### Cover position change event 249 | 250 | ```json 251 | { 252 | "kind": "event", 253 | "msg": "entity_change", 254 | "cat": "ENTITY", 255 | "msg_data": { 256 | "entity_type": "cover", 257 | "entity_id": "blind-1", 258 | "attributes": { 259 | "position": 72 260 | } 261 | } 262 | } 263 | ``` 264 | 265 | #### Cover tilt position change event 266 | 267 | ```json 268 | { 269 | "kind": "event", 270 | "msg": "entity_change", 271 | "cat": "ENTITY", 272 | "msg_data": { 273 | "entity_type": "cover", 274 | "entity_id": "blind-1", 275 | "attributes": { 276 | "tilt_position": 50 277 | } 278 | } 279 | } 280 | ``` 281 | -------------------------------------------------------------------------------- /doc/entities/entity_ir_emitter.md: -------------------------------------------------------------------------------- 1 | # IR-emitter Entity 2 | 3 | An IR-emitter entity allows to send IR commands in PRONTO hex format. 4 | 5 | This entity allows to integrate external IR blasters and emitters. Once added to a Remote, a new IR-emitter output is 6 | registered which can be used for IR-remote entities. Similar as adding a new dock. 7 | 8 | An IR-emitter device must support the PRONTO hex format. Other IR formats are optional. Currently only the IR protocols 9 | and data values of the [IRremoteESP8266](https://github.com/crankyoldgit/IRremoteESP8266) library are supported as 10 | additional format (see [options](#options) below for more information). 11 | 12 | ℹ️ Supported in UC Remote firmware from version 1.9.3, Core Simulator from version 0.48.0. 13 | 14 | ## Features 15 | 16 | | Name | R | W | Description | 17 | |-------------|----|----|---------------------------------------------------------------------------------| 18 | | send_ir | ❌ | ✅ | Default feature of an IR-emitter entity. Always present, even if not specified. | 19 | | 🚧 learn_ir | ✅ | ✅ | Planned feature: IR-emitter is also capable of learning IR codes. | 20 | 21 | - R: readable 22 | - ✅ Feature has a readable attribute to retrieve the current or available values. 23 | - ❌ Feature value(s) cannot be read. 24 | - W: writeable 25 | - ✅ Feature has one or multiple commands to trigger an action or set a value. 26 | - ❌ No corresponding command(s), only the current value(s) of the feature can be read. 27 | 28 | ### Attributes 29 | 30 | Entity attributes are controlled by features. Multiple features can act on the same attribute. 31 | 32 | | Attribute | Features | Type | Values | Description | 33 | |-----------------|----------|------|-------------------|-------------------------| 34 | | state | | enum | [States](#states) | State of the IR-emitter | 35 | 36 | ### States 37 | 38 | The IR-emitter entity only supports the `ON` state and the [common entity states](README.md#states). 39 | 40 | | Value | Description | 41 | |-------|---------------------------------------------| 42 | | ON | The emitter is available and ready to send. | 43 | 44 | ### Device Classes 45 | 46 | None. 47 | 48 | ### Options 49 | 50 | Optional features of the IR-emitter entity. 51 | 52 | | Name | Type | Values | Default | Description | 53 | |------------|-------|-------------|---------|---------------------------------------------------------------------------------| 54 | | ports | array | EmitterPort | [] | List of individual emitter ports which can be selected for sending IR commands. | 55 | | ir_formats | array | `HEX` | [] | Supported IR formats / protocols besides `PRONTO`. | 56 | 57 | - The `ports` option can be omitted, if the emitter only supports a single output port. 58 | `EmitterPort` format: 59 | 60 | ```json 61 | { 62 | "id": "unique-id", 63 | "name": "Friendly port name" 64 | } 65 | ``` 66 | 67 | - The `PRONTO` hex format is the default and must be supported by the IR-emitter driver. 68 | - The `HEX` IR format refers to the learned IR codes with the Unfolded Circle Dock. 69 | - Format: `;;;` from the [IRremoteESP8266](https://github.com/crankyoldgit/IRremoteESP8266) 70 | library. 71 | - `protocol`: numeric value from supported and enabled protocols. See: [decode_type_t](https://github.com/unfoldedcircle/IRremoteESP8266/blob/v2.8.5-ucd2.2/src/IRremoteESP8266.h#L1011) 72 | - `hex-ir-code`: HEX value prefixed with `0x`. 73 | - `bits`: number of bits in hex value. 74 | - `repeat-count`: number of repeats. 75 | - Specifying additional formats like `HEX` allows the Remote to send these formats when available, e.g. learned codes 76 | from the dock. It does **not** mean, that all codes are transformed into this format! 77 | - If the emitter device doesn't support the `HEX` format, the learned codes from the dock are automatically converted 78 | and sent as `PRONTO` codes. 79 | - ⚠️ The PRONTO-conversion is a best effort process only and certain IR protocols might lose the native auto-repeat 80 | functionality! 81 | - ℹ️ Raw and Global Caché sendir formats are planned in the future. 82 | 83 | ## Integration API 84 | 85 | ### Commands 86 | 87 | The integration driver has to implement a handler for the `entity_command` message to process the following command 88 | requests in `msg_data.cmd_id`. 89 | 90 | | cmd_id | Parameters | Type | Description | 91 | |----------------|------------|--------|---------------------------------------------------------------------------------------------------------------------------| 92 | | send_ir | | | Send an IR code on the specified output port and number of repeats. | 93 | | | code | String | IR code to send. | 94 | | | format | String | Optional IR format of `code` if the emitter supports multiple IR formats. Defaults to `PRONTO` if not specified. | 95 | | | port | String | Optional: output port identifier. Only required if the emitter supports multiple outputs. | 96 | | | repeat | Number | Optional: how many times the command shall be repeated. Defaults to `1` if not specified (single command without repeat). | 97 | | stop_ir | | | Stop sending any active IR transmission on the specified port. | 98 | | | port | String | Optional: output port identifier. Only required if the emitter supports multiple outputs. | 99 | | 🚧 start_learn | | | | 100 | | 🚧 stop_learn | | | | 101 | 102 | #### send_ir 103 | 104 | The `send_ir` command is used to send an IR code on the specified output port. 105 | 106 | Long button presses on the Remote will trigger continuous `send_ir` requests with a `repeat` count set to 3 or higher. 107 | Once the button is released, a `stop_ir` request is sent. This allows to implement a continuous IR repeat feature: 108 | 109 | - If no transmission is active: start sending the IR command. 110 | - If transmission is active: 111 | 1. check if the same IR code is being transmitted. If other code: abort and respond with error code `409`. 112 | 2. reset repeat count of the active transmission to prolong the command. 113 | 114 | Queuing IR send requests should be avoided to prevent "ghost actions" when the user doesn't expect it. New IR commands 115 | should be executed within 1 second. 116 | 117 | ℹ️ The `HEX` format already contains a repeat field. This value is relevant, if the `send_ir.repeat` parameter is not 118 | set. For continuous IR repeat, the `send_ir.repeat` parameter is set and overrides the embedded repeat field in the 119 | IR code. 120 | 121 | #### stop_ir 122 | 123 | The `stop_ir` command signals to stop an active IR transmission on the specified output port. 124 | 125 | The IR transmission should be stopped no later than at the start of the next repeat sequence. 126 | (This also depends on the IR protocol, some have a specific repeat sequence, others need to be fully retransmitted). 127 | 128 | ### Events 129 | 130 | The `entity_change` event must be emitted by the integration driver if the state or an attribute of the IR-emitter changes. 131 | 132 | The following attributes must be included: 133 | 134 | | Attribute | Description | 135 | |-----------|------------------------------| 136 | | state | New entity [state](#states). | 137 | 138 | ### Command examples 139 | 140 | IR-emitter entity examples of received `entity_command` WebSocket messages in an integration driver. 141 | 142 | #### send_ir with PRONTO code on default output 143 | 144 | Sending a single PRONTO code (without repeat) on an IR-emitter with only one output port (`options.ports` not specified 145 | or empty): 146 | 147 | ```json 148 | { 149 | "kind": "req", 150 | "id": 122, 151 | "msg": "entity_command", 152 | "msg_data": { 153 | "entity_type": "ir_emitter", 154 | "entity_id": "emitter-1", 155 | "cmd_id": "send_ir", 156 | "params": { 157 | "code": "0000 006D 0004 0002 0155 00AB 0015 0015 0015 0015 0015 0015 0155 0055 0015 0E4C" 158 | } 159 | } 160 | } 161 | ``` 162 | 163 | #### send_ir with PRONTO code on specific output and repeat 164 | 165 | Sending a PRONTO code three times on a specific output port: 166 | 167 | ```json 168 | { 169 | "kind": "req", 170 | "id": 123, 171 | "msg": "entity_command", 172 | "msg_data": { 173 | "entity_type": "ir_emitter", 174 | "entity_id": "emitter-2", 175 | "cmd_id": "send_ir", 176 | "params": { 177 | "code": "0000 006D 0004 0002 0155 00AB 0015 0015 0015 0015 0015 0015 0155 0055 0015 0E4C", 178 | "port": "port1", 179 | "repeat": 3 180 | } 181 | } 182 | } 183 | ``` 184 | 185 | #### stop_ir 186 | 187 | Stop an active IR transmission on a specific output port: 188 | 189 | ```json 190 | { 191 | "kind": "req", 192 | "id": 124, 193 | "msg": "entity_command", 194 | "msg_data": { 195 | "entity_type": "ir_emitter", 196 | "entity_id": "emitter-2", 197 | "cmd_id": "stop_ir", 198 | "params": { 199 | "port": "port1" 200 | } 201 | } 202 | } 203 | ``` 204 | 205 | ### Event examples 206 | 207 | #### State change event 208 | 209 | ```json 210 | { 211 | "kind": "event", 212 | "msg": "entity_change", 213 | "cat": "ENTITY", 214 | "msg_data": { 215 | "entity_type": "ir_emitter", 216 | "entity_id": "emitter-1", 217 | "attributes": { 218 | "state": "on" 219 | } 220 | } 221 | } 222 | ``` 223 | -------------------------------------------------------------------------------- /doc/entities/entity_light.md: -------------------------------------------------------------------------------- 1 | # Light Entity 2 | 3 | A light entity can be switched on and off and depending on its features, the light source can be further 4 | controlled like setting brightness, hue, color saturation and color temperature. 5 | 6 | The [HSV color model](https://en.wikipedia.org/wiki/HSL_and_HSV) is used for adjusting color and brightness. 7 | 8 | ## Features 9 | 10 | | Name | R | W | Description | 11 | |-------------------|-----|-----|-----------------------------------------------------------------------------------------------------------------------------------------------| 12 | | on_off | ✅ | ✅ | Default feature of a light source. Always present, even if not specified. | 13 | | toggle | ❌ | ✅ | Toggle support. If there's no native support, the remote will use the current state of the light to send the corresponding on or off command. | 14 | | dim | ✅ | ✅ | Light source supports dimming. | 15 | | color | ✅ | ✅ | The color of the light source can be adjusted. | 16 | | color_temperature | ✅ | ✅ | The color temperature of the light source can be adjusted. | 17 | 18 | ### Attributes 19 | 20 | | Attribute | Features | Type | Values | Description | 21 | |-------------------|-------------------|------|-------------------|--------------------------------------------------------------------------------------------------| 22 | | state | on_off | enum | [States](#states) | Default entity state attribute. Influenced by the entity commands. | 23 | | | toggle | | | | 24 | | hue | color | int | 0..360 | Color wheel: 0-360 degree | 25 | | saturation | color | int | 0..255 | | 26 | | brightness | dim | int | 0..255 | | 27 | | color_temperature | color_temperature | int | 0..100 | Color temperature percentage: a higher value means a warmer color. 0% = coldest, 100% = warmest. | 28 | 29 | ### States 30 | 31 | The light entity provides the following entity `state` values: 32 | 33 | | Value | Description | 34 | |-------|----------------------------| 35 | | ON | The light is switched on. | 36 | | OFF | The light is switched off. | 37 | 38 | See [common entity states](README.md#states). 39 | 40 | ### Device Classes 41 | 42 | None. 43 | 44 | ### Options 45 | 46 | | Name | Type | Values | Default | Description | 47 | |-------------------------|--------|--------|---------|------------------------------------------------------------------------------------------------------------------------------| 48 | | color_temperature_steps | number | 2..100 | 100 | Number of color temperature steps of the light source. Some lamps only support 3 modes, where others can be adjusted freely. | 49 | 50 | ## Integration API 51 | 52 | ### Commands 53 | 54 | The integration driver has to implement a handler for the `entity_command` message to process the following command 55 | requests in `msg_data.cmd_id`. 56 | 57 | | cmd_id | Parameters | Description | 58 | |--------|-------------------|----------------------------------------------------------------| 59 | | on | - | Turn the light on. Optionally set brightness and color values: | 60 | | | brightness | - dim the light to the given percentage value. | 61 | | | hue, saturation | - adjust the color of the light. | 62 | | | color_temperature | - adjust the color temperature. | 63 | | off | - | Turn the light off. | 64 | | toggle | - | Toggle the light from on -> off or from off -> on. | 65 | 66 | ### Events 67 | 68 | The `entity_change` event must be emitted by the integration driver if the state or an attribute of the light changes. 69 | 70 | The following attributes are supported: 71 | 72 | | Attribute | Description | 73 | |-------------------|-------------------------------------------------------------------------------------------------------------------------------| 74 | | state | New entity [state](#states). | 75 | | brightness | Current brightness. | 76 | | hue | Current hue value. | 77 | | saturation | Current saturation value. | 78 | | color_temperature | Current color temperature. The possible values are influenced by `color_temperature_steps`. E.g. with 3 steps: \[0, 50, 100\] | 79 | 80 | ### Command examples 81 | 82 | #### on 83 | 84 | Default request: 85 | 86 | ```json 87 | { 88 | "kind": "req", 89 | "id": 123, 90 | "msg": "entity_command", 91 | "msg_data": { 92 | "entity_type": "light", 93 | "entity_id": "light-1", 94 | "cmd_id": "on" 95 | } 96 | } 97 | ``` 98 | 99 | Request with setting brightness value: 100 | ```json 101 | { 102 | "kind": "req", 103 | "id": 123, 104 | "msg": "entity_command", 105 | "msg_data": { 106 | "entity_type": "light", 107 | "entity_id": "light-1", 108 | "cmd_id": "on", 109 | "params": { 110 | "brightness": 140 111 | } 112 | } 113 | } 114 | ``` 115 | 116 | Request with setting color temperature: 117 | ```json 118 | { 119 | "kind": "req", 120 | "id": 123, 121 | "msg": "entity_command", 122 | "msg_data": { 123 | "entity_type": "light", 124 | "entity_id": "light-1", 125 | "cmd_id": "on", 126 | "params": { 127 | "color_temperature": 70 128 | } 129 | } 130 | } 131 | ``` 132 | 133 | Request with setting light color: 134 | ```json 135 | { 136 | "kind": "req", 137 | "id": 123, 138 | "msg": "entity_command", 139 | "msg_data": { 140 | "entity_type": "light", 141 | "entity_id": "light-1", 142 | "cmd_id": "on", 143 | "params": { 144 | "hue": 180, 145 | "saturation": 200 146 | } 147 | } 148 | } 149 | ``` 150 | 151 | #### off 152 | 153 | ```json 154 | { 155 | "kind": "req", 156 | "id": 123, 157 | "msg": "entity_command", 158 | "msg_data": { 159 | "entity_type": "light", 160 | "entity_id": "light-1", 161 | "cmd_id": "off" 162 | } 163 | } 164 | ``` 165 | 166 | #### toggle 167 | 168 | ```json 169 | { 170 | "kind": "req", 171 | "id": 123, 172 | "msg": "entity_command", 173 | "msg_data": { 174 | "entity_type": "light", 175 | "entity_id": "light-1", 176 | "cmd_id": "toggle" 177 | } 178 | } 179 | ``` 180 | 181 | ### Event examples 182 | 183 | #### Switched on 184 | 185 | ```json 186 | { 187 | "kind": "event", 188 | "msg": "entity_change", 189 | "cat": "ENTITY", 190 | "msg_data": { 191 | "entity_type": "light", 192 | "entity_id": "light-1", 193 | "attributes": { 194 | "state": "ON" 195 | } 196 | } 197 | } 198 | ``` 199 | 200 | The additional brightness and color attributes can be added as well: 201 | 202 | ```json 203 | { 204 | "kind": "event", 205 | "msg": "entity_change", 206 | "cat": "ENTITY", 207 | "msg_data": { 208 | "entity_type": "light", 209 | "entity_id": "light-1", 210 | "attributes": { 211 | "state": "ON", 212 | "brightness": 180, 213 | "hue": 180, 214 | "saturation": 150 215 | } 216 | } 217 | } 218 | ``` 219 | 220 | 221 | #### Switched off 222 | 223 | ```json 224 | { 225 | "kind": "event", 226 | "msg": "entity_change", 227 | "cat": "ENTITY", 228 | "msg_data": { 229 | "entity_type": "light", 230 | "entity_id": "light-1", 231 | "attributes": { 232 | "state": "OFF" 233 | } 234 | } 235 | } 236 | ``` 237 | -------------------------------------------------------------------------------- /doc/entities/entity_sensor.md: -------------------------------------------------------------------------------- 1 | # Sensor Entity 2 | 3 | A sensor entity provides measured values from devices or dedicated hardware sensors. 4 | The device class specifies the type of sensor and links it with a default unit of measurement to display in the user 5 | interface. 6 | 7 | - The `custom` device class allows arbitrary text values and unit labels. 8 | - The `temperature` device class performs automatic conversion between °C and °F. 9 | 10 | ## Features 11 | 12 | The sensor entity has no features. 13 | 14 | ### Attributes 15 | 16 | | Attribute | Type | Values | Description | 17 | |-----------|-----------------|-------------------|---------------------------------------------------------| 18 | | state | enum | [States](#states) | Optional state of the sensor. | 19 | | value | number / string | | The native measurement value of the sensor. | 20 | | unit | string | | Optional unit of the `value` if no default unit is set. | 21 | 22 | The `unit` attribute can be specified in the `entity_change` event and takes precedence over the associated default unit 23 | in the `device_class` or the specified `custom_unit` in the options. 24 | 25 | If an integration driver gets the current unit from an external system together with the current value, it's recommended 26 | to pass it on. 27 | If the integration driver is in control of the unit, and it can't change at runtime, the unit can be specified in the 28 | available entity definition as `options.custom_unit` and then omitted in `entity_change` events. 29 | 30 | ### States 31 | 32 | The `state` attribute is optional for a sensor. 33 | 34 | The sensor entity only supports the `ON` state and the [common entity states](README.md#states). 35 | 36 | | Value | Description | 37 | |-------|-----------------------------------------------------| 38 | | ON | The sensor is available and providing measurements. | 39 | 40 | ### Device classes 41 | 42 | The device class specifies the type of sensor. Default if not specified: `custom`. 43 | 44 | | Name | Default unit | Description | 45 | |-------------|--------------|--------------------------------------------------------------------------------------------------------------------------------------------| 46 | | custom | | Generic sensor with custom unit | 47 | | battery | % | Battery charge in % | 48 | | current | A | Electrical current in ampere | 49 | | energy | kWh | Energy in kilowatt-hour | 50 | | humidity | % | Humidity in % | 51 | | power | W | Power in watt or kilowatt | 52 | | temperature | °C | Temperature with automatic °C, °F conversion, depending on remote settings. Use `native_unit` option if the temperature is measured in °F. | 53 | | voltage | V | Voltage in volt | 54 | 55 | ### Options 56 | 57 | | Name | Type | Default | Description | 58 | |--------------|--------|---------|---------------------------------------------------------------------------------------------------------------------------------| 59 | | custom_unit | text | | Unit label for a custom sensor if `device_class` is not specified or to override a default unit. | 60 | | native_unit | text | | The sensor's native unit of measurement to perform automatic conversion. Applicable to device classes: `temperature`. | 61 | | decimals | number | 0 | Number of decimal places to show in the UI if the sensor provides the measurement as a number. Not applicable to string values. | 62 | | 🚧 min_value | number | | Optional minimum value of the sensor output. This can be used in the UI for graphs or gauges. | 63 | | 🚧 max_value | number | | Optional maximum value of the sensor output. This can be used in the UI for graphs or gauges. | 64 | 65 | ## Integration API 66 | 67 | ### Commands 68 | 69 | The sensor entity doesn't support any commands. 70 | 71 | ### Events 72 | 73 | The `entity_change` event must be emitted by the integration driver if the state or an attribute of a sensor changes. 74 | 75 | The following attributes are supported: 76 | 77 | | Attribute | Description | 78 | |-----------|----------------------------------------------------------------------------------------------------------| 79 | | state | Optional entity [state](#states). | 80 | | value | The current sensor value. | 81 | | unit | Optional: the unit of measurement for the given `value`. If omitted, the configured entity unit is used. | 82 | 83 | - At least one attribute must be specified in the `entity_change` message. 84 | - If the sensor `value` and `unit` attributes change at the same time, both must be included in the same message. 85 | - It's also valid to always send every attribute. 86 | - Only sending the `unit` attribute is not valid. 87 | - If the state is never sent, the sensor is considered available (`ON`). 88 | 89 | ### Event examples 90 | 91 | #### State change event 92 | 93 | Sensor with a number value: 94 | 95 | ```json 96 | { 97 | "kind": "event", 98 | "msg": "entity_change", 99 | "cat": "ENTITY", 100 | "msg_data": { 101 | "entity_type": "sensor", 102 | "entity_id": "sensor-1", 103 | "attributes": { 104 | "value": 21.2, 105 | "unit": "°C" 106 | } 107 | } 108 | } 109 | ``` 110 | 111 | Sensor with a string value: 112 | 113 | ```json 114 | { 115 | "kind": "event", 116 | "msg": "entity_change", 117 | "cat": "ENTITY", 118 | "msg_data": { 119 | "entity_type": "sensor", 120 | "entity_id": "sensor-2", 121 | "attributes": { 122 | "value": "231", 123 | "unit": "V" 124 | } 125 | } 126 | } 127 | ``` 128 | 129 | Sensor coming online with the current value: 130 | 131 | ```json 132 | { 133 | "kind": "event", 134 | "msg": "entity_change", 135 | "cat": "ENTITY", 136 | "msg_data": { 137 | "entity_type": "sensor", 138 | "entity_id": "sensor-1", 139 | "attributes": { 140 | "state": "ON", 141 | "value": 21.5, 142 | "unit": "°C" 143 | } 144 | } 145 | } 146 | ``` 147 | -------------------------------------------------------------------------------- /doc/entities/entity_switch.md: -------------------------------------------------------------------------------- 1 | # Switch Entity 2 | 3 | A switch entity can turn something on or off and the current state should be readable by the integration driver. 4 | 5 | If the state can't be read, the `readable` option property can be set to `false`. This should be avoided 6 | whenever possible, because the remote either has to assume the current state, or the UI needs to ask the user 7 | for the current state. 8 | 9 | If the switch controls a light source, then the [light entity](entity_light.md) is usually a better choice. 10 | 11 | ## Features 12 | 13 | | Name | R | W | Description | 14 | |--------|-----|-----|------------------------------------------------------------------------------------------------------------------------------------------------| 15 | | on_off | ✅ | ✅ | Default feature of a switch. Always present, even if not specified. | 16 | | toggle | ❌ | ✅ | Toggle support. If there's no native support, the remote will use the current state of the switch to send the corresponding on or off command. | 17 | 18 | ### Attributes 19 | 20 | Entity attributes are controlled by features. Multiple features can act on the same attribute. See Events on how to 21 | notify the remote about an updated attribute. The attributes have to be listed as properties under `attributes` with 22 | their current value. 23 | 24 | | Attribute | Features | Type | Values | Description | 25 | |-----------|----------|------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------| 26 | | state | on_off | enum | [States](#states) | State of the switch, it's either on or off. | 27 | | | toggle | | | Toggle inverts the current state. If the driver doesn't provide the toggle feature, the remote uses the current value and calls on or off. | 28 | 29 | ### States 30 | 31 | The switch entity provides the following entity `state` values: 32 | 33 | | Value | Description | 34 | |-------|--------------------| 35 | | ON | The switch is on. | 36 | | OFF | The switch is off. | 37 | 38 | See [common entity states](README.md#states). 39 | 40 | ### Device classes 41 | 42 | Optional switch type. This can be used by the UI to represent the entity with a different icon, behaviour etc. 43 | 44 | | Name | Description | 45 | |--------|--------------------------------------------------| 46 | | outlet | The switch represents a switchable power outlet. | 47 | | switch | Generic switch. | 48 | 49 | ### Options 50 | 51 | | Name | Type | Default | Description | 52 | |----------|---------|---------|----------------------------------------------------------------------------------------------------------------------------------------------------------| 53 | | readable | boolean | true | If set to false the current state of the switch cannot be read. This will make the switch stateless and the UI might ask the user for the current state. | 54 | 55 | ## Integration API 56 | 57 | ### Commands 58 | 59 | The integration driver has to implement a handler for the `entity_command` message to process the following command 60 | requests in `msg_data.cmd_id`. 61 | 62 | | cmd_id | Parameters | Description | 63 | |-----------|---------------|---------------------------------------------------------------------------| 64 | | on | - | Put the switch in the on state. | 65 | | off | - | Put the switch in the off state. | 66 | | toggle | - | Toggle the current switch state, either from on -> off or from off -> on. | 67 | 68 | ### Events 69 | 70 | The `entity_change` event must be emitted by the integration driver if the state or an attribute of the switch changes. 71 | 72 | The following attributes must be included: 73 | 74 | | Attribute | Description | 75 | |---------------|-------------------------------| 76 | | state | New entity [state](#states). | 77 | 78 | ### Command examples 79 | 80 | #### on 81 | 82 | ```json 83 | { 84 | "kind": "req", 85 | "id": 124, 86 | "msg": "entity_command", 87 | "msg_data": { 88 | "entity_type": "switch", 89 | "entity_id": "switch-1", 90 | "cmd_id": "on" 91 | } 92 | } 93 | ``` 94 | 95 | #### off 96 | 97 | ```json 98 | { 99 | "kind": "req", 100 | "id": 124, 101 | "msg": "entity_command", 102 | "msg_data": { 103 | "entity_type": "switch", 104 | "entity_id": "switch-1", 105 | "cmd_id": "off" 106 | } 107 | } 108 | ``` 109 | 110 | #### toggle 111 | 112 | ```json 113 | { 114 | "kind": "req", 115 | "id": 124, 116 | "msg": "entity_command", 117 | "msg_data": { 118 | "entity_type": "switch", 119 | "entity_id": "switch-1", 120 | "cmd_id": "toggle" 121 | } 122 | } 123 | ``` 124 | 125 | ### Event examples 126 | 127 | #### Switched on 128 | 129 | ```json 130 | { 131 | "kind": "event", 132 | "msg": "entity_change", 133 | "cat": "ENTITY", 134 | "msg_data": { 135 | "entity_type": "switch", 136 | "entity_id": "switch-1", 137 | "attributes": { 138 | "state": "on" 139 | } 140 | } 141 | } 142 | ``` 143 | 144 | #### Switched off 145 | 146 | ```json 147 | { 148 | "kind": "event", 149 | "msg": "entity_change", 150 | "cat": "ENTITY", 151 | "msg_data": { 152 | "entity_type": "switch", 153 | "entity_id": "switch-1", 154 | "attributes": { 155 | "state": "off" 156 | } 157 | } 158 | } 159 | ``` 160 | -------------------------------------------------------------------------------- /doc/entities/entity_template.md: -------------------------------------------------------------------------------- 1 | # XXX Entity 2 | 3 | ## Features 4 | 5 | 6 | | Name | R | W | Description | 7 | |------|-----|-----|-------------| 8 | | | ✅ | ✅ | | 9 | | | ❌ | ✅ | | 10 | | | ✅ | ❌ | | 11 | 12 | ### Attributes 13 | 14 | Entity attributes are controlled by features. Multiple features can act on the same attribute. 15 | 16 | | Attribute | Features | Type | Values | Description | 17 | |-----------|----------|------|-------------------|------------------| 18 | | state | on_off | enum | [States](#states) | State of the ### | 19 | | | | | | | 20 | | | | | | | 21 | 22 | ### States 23 | 24 | The entity `state` attribute holds the following values: 25 | 26 | | Value | Description | 27 | |-------|-------------------------| 28 | | ON | The ### is switched on | 29 | | OFF | The ### is switched off | 30 | 31 | See [common entity states](README.md#states). 32 | 33 | ### Device Classes 34 | 35 | | Name | Description | 36 | |------|-------------| 37 | | | | 38 | | | | 39 | 40 | ### Options 41 | 42 | 43 | | Name | Type | Default | Description | 44 | |------|------|---------|-------------| 45 | | | | | | 46 | | | | | | 47 | 48 | 49 | ## Integration API 50 | 51 | ### Commands 52 | 53 | The integration driver has to implement a handler for the `entity_command` message to process the following command 54 | requests in `msg_data.cmd_id`. 55 | 56 | | cmd_id | Parameters | Description | 57 | |--------|------------|-------------| 58 | | X | - | | 59 | | Y | foo | | 60 | | Z | bar | | 61 | 62 | ### Events 63 | 64 | The `entity_change` event must be emitted by the integration driver if the state or an attribute of the ### changes. 65 | 66 | The following attributes are supported: 67 | 68 | | Attribute | Description | 69 | |-----------|------------------------------| 70 | | state | New entity [state](#states). | 71 | | X | Current ### value. | 72 | | Y | Current ### value. | 73 | 74 | At least one attribute must be specified in the `entity_change` message. If the entity `state` and a `###` 75 | attribute changed at the same time, they may both be included in the same message. It's also valid to always send every 76 | attribute. 77 | 78 | ### Command examples 79 | 80 | #### foo 81 | 82 | ```json 83 | { 84 | "kind": "req", 85 | "id": 123, 86 | "msg": "entity_command", 87 | "msg_data": { 88 | "entity_type": "###", 89 | "entity_id": "###-1", 90 | "cmd_id": "foo" 91 | } 92 | } 93 | ``` 94 | 95 | ### Event examples 96 | 97 | #### State change event 98 | 99 | ```json 100 | { 101 | "kind": "event", 102 | "msg": "entity_change", 103 | "cat": "ENTITY", 104 | "msg_data": { 105 | "entity_type": "###", 106 | "entity_id": "###-1", 107 | "attributes": { 108 | "state": "###", 109 | "foobar": 72 110 | } 111 | } 112 | } 113 | ``` 114 | -------------------------------------------------------------------------------- /doc/img/groups.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unfoldedcircle/core-api/9d8f8e81db9b8e30fcf231f704810dc30c9fd742/doc/img/groups.jpg -------------------------------------------------------------------------------- /doc/img/pages.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unfoldedcircle/core-api/9d8f8e81db9b8e30fcf231f704810dc30c9fd742/doc/img/pages.jpg -------------------------------------------------------------------------------- /doc/img/profiles.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unfoldedcircle/core-api/9d8f8e81db9b8e30fcf231f704810dc30c9fd742/doc/img/profiles.jpg -------------------------------------------------------------------------------- /doc/img/ui-overview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unfoldedcircle/core-api/9d8f8e81db9b8e30fcf231f704810dc30c9fd742/doc/img/ui-overview.jpg -------------------------------------------------------------------------------- /doc/img/ui-page-media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unfoldedcircle/core-api/9d8f8e81db9b8e30fcf231f704810dc30c9fd742/doc/img/ui-page-media.png -------------------------------------------------------------------------------- /doc/integration-driver/README.md: -------------------------------------------------------------------------------- 1 | # WebSocket Integration API 2 | 3 | - [AsyncAPI definition](../../integration-api/UCR-integration-asyncapi.yaml) 4 | - [Remote Two entities](../entities/README.md) 5 | - [How to write an integration driver](write-integration-driver.md) 6 | - [WebSockets handling](websocket.md) 7 | - [Driver mDNS advertisement](driver-advertisement.md) 8 | - [Driver registration](driver-registration.md) 9 | - [Driver setup](driver-setup.md) 10 | - [Install integration driver on the device](driver-installation.md) 11 | 12 | ## Examples 13 | 14 | - [Node.js API wrapper for the UC Integration API](https://github.com/unfoldedcircle/integration-node-library) 15 | Integrations using the Node.js API wrapper: 16 | - [Global Caché IR integration](https://github.com/unfoldedcircle/integration-globalcache) 17 | - [Roon integration](https://github.com/unfoldedcircle/integration-roon) 18 | - [Python API wrapper library for the UC Integration API](https://github.com/unfoldedcircle/integration-python-library) 19 | Integrations using the Python API wrapper: 20 | - [Android TV integration](https://github.com/unfoldedcircle/integration-androidtv) 21 | - [Apple TV integration](https://github.com/unfoldedcircle/integration-appletv) 22 | - [Denon AVR integration](https://github.com/unfoldedcircle/integration-denonavr) 23 | - [API models in Rust](https://github.com/unfoldedcircle/api-model-rs) 24 | - [Home Assistant integration](https://github.com/unfoldedcircle/integration-home-assistant) written in Rust 25 | -------------------------------------------------------------------------------- /doc/integration-driver/driver-advertisement.md: -------------------------------------------------------------------------------- 1 | # Integration Driver mDNS advertisement 2 | 3 | The Remote Two uses mDNS discovery to look for local integration drivers on the same network. 4 | 5 | If an integration driver shall be discoverable, it needs to advertise the driver service using multicast DNS: 6 | 7 | - Service type: `_uc-integration._tcp` 8 | - Instance name: unique identifier of the integration driver (`driver_id` in the API model). 9 | 10 | ## TXT Record 11 | 12 | Additional information is published in the TXT record. 13 | 14 | | Key | Required | Format | Description | 15 | |-----------|----------|----------|----------------------------------------------------------------------------------------------| 16 | | name | x | _text_ | Integration name | 17 | | developer | x | _text_ | Developer or company of the integration | 18 | | ver | x | _text_ | Integration version number | 19 | | ver_api | | _text_ | Implemented integration API version | 20 | | ws_url | | _text_ | Override the WebSocket url. ws_path, wss, wss_port will be ignored | 21 | | ws_path | | _text_ | WebSocket endpoint path. Default: `/` | 22 | | wss | | _bool_ | Use secure WebSocket connection | 23 | | wss_port | | _number_ | WebSocket SSL port if different from published port, or driver supports ws & wss connections | 24 | | pwd | | _bool_ | Access token required | 25 | 26 | ## Examples 27 | 28 | ### Node.js 29 | 30 | Using [bonjour-service](https://www.npmjs.com/package/bonjour-service): 31 | 32 | ```js 33 | const { Bonjour } = require('bonjour-service'); 34 | const bonjour = new Bonjour(); 35 | 36 | bonjour.publish({ 37 | name: 'my-driver', 38 | type: 'uc-integration', 39 | port: 1234, 40 | txt: { 41 | name: 'My Driver', 42 | ver: '1.0.0', 43 | developer: 'Foobar Inc', 44 | ws_path: '/ws', 45 | pwd: 'true' 46 | } 47 | }); 48 | ``` -------------------------------------------------------------------------------- /doc/integration-driver/driver-installation.md: -------------------------------------------------------------------------------- 1 | # Driver installation on the Remote 2 | 3 | Starting with firmware v1.9.0, custom installation drivers can be installed on the Remote. 4 | 5 | ‼️ Custom integration drivers is a developer preview and not all features are implemented yet. See [missing features](#missing-features). 6 | 7 | ## Installation archive format 8 | 9 | Integration driver archive requirements: 10 | - TAR GZip archive (either .tgz or .tar.gz file suffix) with a maximum size of 100 MB. 11 | - In the root of the archive, there must be a `driver.json` metadata file describing the custom integration driver. 12 | - The driver binary must be in the `./bin` subdirectory. 13 | - Either a statically linked aarch64 executable named `driver`. 14 | - Or a Node.js file named `driver.js`. 15 | - All application files must be in one of the following subdirectories, other locations are not accessible at runtime: 16 | - `./bin`: application binary folder. 17 | - `./config`: optional configuration data. Path is accessible with `UC_CONFIG_HOME` environment variable. 18 | - `./data`: optional application data. Path is accessible with `UC_DATA_HOME` environment variable. 19 | 20 | ### Metadata file 21 | 22 | The `driver.json` metadata file in the root of the archive describes the integration driver. 23 | 24 | ‼️ The data schema is not yet included in the Core-API, the full object is shown below. 25 | It is a reduced version of the `IntegrationDriver` object, without driver connection fields like driver_url, token etc. 26 | 27 | ```json 28 | { 29 | "driver_id": "foobar", 30 | "version": "1.0.0", 31 | "min_core_api": "0.20.0", 32 | "name": { 33 | "en": "Foobar special" 34 | }, 35 | "icon": "custom:foobar.png", 36 | "description": { 37 | "en": "Control Foobar products", 38 | "de": "Steuere Foobar Produkte" 39 | }, 40 | "features": [ 41 | { 42 | "name": "auth.external_tokens", 43 | "required": true 44 | } 45 | ], 46 | "developer": { 47 | "name": "Unfolded Circle ApS", 48 | "email": "hello@unfoldedcircle.com", 49 | "url": "https://www.unfoldedcircle.com" 50 | }, 51 | "home_page": "https://www.unfoldedcircle.com", 52 | "setup_data_schema": { 53 | "title": { 54 | "en": "Integration setup", 55 | "de": "Integrationssetup" 56 | }, 57 | "settings": [ 58 | { 59 | "id": "info", 60 | "label": { 61 | "en": "Setup process", 62 | "de": "Setup Fortschritt" 63 | }, 64 | "field": { 65 | "label": { 66 | "value": { 67 | "en": "The integration will discover your Foobar products on your network. This process is automatic and can take a few minutes.", 68 | "de": "Diese Integration wird Foobar Produkte im Netzwerk finden. Dies ist ein Automatischer Prozess der ein paar Minuten dauern kann." 69 | } 70 | } 71 | } 72 | } 73 | ] 74 | }, 75 | "release_date": "2024-07-21" 76 | } 77 | ``` 78 | 79 | - `driver_id` must be at least 5 characters long and start with a lower-case letter. 80 | - Only lower-case letters, digits and `-`, `_` are allowed. 81 | - Common system identifiers are not allowed and will be rejected (e.g. `daemon`, `backup`, etc.). 82 | - The `uc_` prefix should not be used. This is reserved for pre-installed integrations. Any custom integrations using 83 | this prefix might get removed during a future firmware update. 84 | - Multiple language fields are not required, but recommended. 85 | - If only a single language is provided, it should use the `en` key. 86 | - `setup_data_schema` is optional if the driver provides a setup flow. 87 | - This is the first screen of the setup, all further screens are dynamically provided by the driver. 88 | - ⚠️ `min_core_api` is not yet used. 89 | 90 | ### Driver icon 91 | 92 | A driver icon can either use a predefined icon or a custom icon. Predefined icons are prefixed with `uc:`, followed by 93 | the icon identifier in lowercase. 94 | 95 | A custom driver icon can be installed automatically as custom icon resource. 96 | 97 | 1. In `driver.json` set the icon field to `custom:$ICON_FILENAME` 98 | 2. Include `$ICON_FILENAME` in the root of the archive. 99 | 100 | Example `driver.json` (other fields omitted for simplicity): 101 | ```json 102 | { 103 | "icon": "custom:foobar.png" 104 | } 105 | ``` 106 | 107 | The icon file called `foobar.png` must be added to the root of the archive. 108 | Icons must be of size 90x90 pixels and either in PNG or JPG format. Maximum size is 32 KB. 109 | 110 | ### Installation archive example 111 | 112 | Example of a Node.js based integration driver archive (contents of bin/node_modules are not shown for a clearer overview): 113 | ``` 114 | . 115 | ├── bin 116 | │   ├── driver.js 117 | │   ├── node_modules 118 | │   │   ├── ... 119 | │   │   ├── foobar 120 | │   │   ├── uc-integration-api 121 | │   │   ├── ws 122 | │   │   └── ... 123 | │   └── foobar.js 124 | ├── config 125 | ├── data 126 | ├── driver.json 127 | └── foobar.png 128 | ``` 129 | 130 | - If the driver doesn't require any data or configuration files, the `./data` and `./config` directories can be omitted 131 | from the archive. 132 | - During the installation, the data and config directories are automatically created and can be accessed by the driver 133 | at runtime, no matter if they were provided in the installation archive or not. 134 | - The `driver.json` file in the root folder is automatically copied to the `./bin` folder during installation if it is 135 | missing. 136 | - If the driver requires a different `driver.json` file, simply include a file in the `./bin` folder. 137 | The one in the root folder won't be copied anymore. 138 | 139 | 140 | ## Restrictions 141 | 142 | - Maximum 10 custom integrations can be installed. 143 | - Only Node.js is supported besides a statically linked binary. Other runtimes are not supported at the moment. 144 | - Python integrations must pack the Python runtime into the archive. 145 | - The integration driver runs in a sandbox. Access to devices and the filesystem is restricted. 146 | - No symlinks are allowed. They are automatically removed during the installation. 147 | - Executable files are only allowed in the `./bin` directory. 148 | - No other tools are provided in the runtime environment. E.g. there is no shell available and no other tools 149 | like `cp` or `mv`. 150 | 151 | ### Missing features 152 | 153 | - [ ] Resource restrictions for integration drivers: limit maximum amount of memory and CPU usage. 154 | - [ ] Update an installed integrations. 155 | - Workaround: remove and re-install. 156 | - [ ] Resource usage: provide memory and CPU usage per integration. 157 | - Only the overall resource usage can be monitored with `GET /api/pub/status`. 158 | - [ ] Web-Configurator support: install custom integration drivers. 159 | - Custom integration drivers can already be deleted in the web-configurator. 160 | 161 | ### Runtime environment 162 | 163 | The driver runs in a sandbox with limited access to the host system. 164 | 165 | - Environment variables for the Websocket integration-API server: 166 | - `UC_INTEGRATION_INTERFACE`: network interface to bind to. 167 | - `UC_INTEGRATION_HTTP_PORT`: port number. 168 | - The working directory is set to the binary directory. 169 | - The binary directory is read-only. 170 | - Node.js version: v20.16 (firmware release 1.9.3 and newer). 171 | - There are no pre-installed node modules. 172 | - An integration driver must include all required modules in the installation archive, including `uc-integration-api` (if used). 173 | - File access with relative paths between `bin`, `config`, and `data` is not possible. 174 | - Environment variables must be used to retrieve the full path of these directories. 175 | - `UC_CONFIG_HOME` and `HOME`: configuration directory. 176 | - `UC_DATA_HOME`: data directory. 177 | - The returned path may not be stored, it may change with future software updates. 178 | - Only the `$UC_CONFIG_HOME` and `$UC_DATA_HOME` directories are writeable and persisted between restarts. 179 | - The `/tmp` directory can be used for small temporary files. 180 | - This is a RAM file system and limited by the available free memory. 181 | - Temporary files are not persisted between restarts. 182 | - A dynamic user and group is allocated when the driver is started. 183 | - The user and group IDs may change between driver restarts. 184 | - Write access to the `$UC_CONFIG_HOME`, `$UC_DATA_HOME` and `/tmp` directories is ensured. 185 | 186 | ⚠️ CPU and memory restrictions are not yet in place but will be enforced in a future firmware update! 187 | - A single integration driver should not use more than 100 MB of memory. 188 | 189 | _TODO more details about sandbox environment_ 190 | 191 | #### Network 192 | 193 | The integration runs on the same network environment as the Remote. There is no network bridge or firewall. 194 | 195 | - Binding ports below 1024 is not possible. 196 | 197 | ⚠️ A port-binding filter might be added in the future to prevent integrations to steal away ports of other integrations. 198 | The port range 8000-9200 and port 13333 are not allowed to be used! 199 | 200 | #### Native integration drivers 201 | 202 | _TODO sysroot, available dynamic libraries_ 203 | 204 | The [Remote Two Cross Compile Toolchain](https://github.com/unfoldedcircle/ucr2-toolchain) can be used to cross-compile 205 | a native binary for the Remote. 206 | 207 | - The driver must be compiled as a static binary for libc. 208 | - Most dynamic libraries in the cross-compile sysroot are NOT available in the custom integration runtime environment! 209 | 210 | ## Install driver 211 | 212 | ```shell 213 | curl --location 'http://$IP/api/intg/install' \ 214 | --user 'web-configurator:$PIN' \ 215 | --form 'file=@"custom-intg.tar.gz"' 216 | ``` 217 | 218 | See [REST Core API](../../core-api/rest/README.md) for more details. 219 | 220 | ## Delete driver 221 | 222 | To delete a custom integration, use the regular endpoints to delete an integration instance and driver. 223 | These are the same endpoints as for an external network integration driver: 224 | 225 | - Delete integration instance `DELETE /api/intg/instances/:intgId`. 226 | - Delete driver and installation files: `DELETE /api/intg/drivers/:driverId`. 227 | 228 | See [REST Core API](../../core-api/rest/README.md) for more details. 229 | 230 | The instance and driver can also be deleted in the web-configurator. 231 | 232 | ## Log access 233 | 234 | Output to stdout and stderr are automatically stored with a timestamp and accessible as the other system logs: 235 | 236 | - Get log services: `GET /api/system/logs/services`. 237 | - Query logs: `GET /api/system/logs?...`. 238 | 239 | See [REST Core API](../../core-api/rest/README.md) for more details. 240 | 241 | Log files can also be downloaded in the web-configurator: _Settings, Development: Logs_ 242 | 243 | ### Web-app log viewer 244 | 245 | ⚠️ Available from firmware release v2.1.0 246 | 247 | The [Logdy](https://logdy.dev/) web application is installed on the device to monitor integration driver log events in 248 | near real time. 249 | 250 | - URL: `http://$IP/log/` 251 | - This function is deactivated by default and the endpoint returns 503. 252 | - The log viewer must be started using the [REST Core API](../../core-api/rest/README.md) 253 | with the `/api/system/logs/web` endpoint: 254 | - `GET` request returns the current state. 255 | - `PUT` request allows to start, stop or to permanently enable the log app. A password can be set to restrict access 256 | to the web app. 257 | - Available logs: 258 | - Remote core service 259 | - All pre-installed and custom integrations 260 | - Custom remote-ui service if installed 261 | - ❗️The log processing and logdy daemon require around 170 MB of the custom integration memory pool. 262 | 263 | Using curl to start the app: 264 | ```shell 265 | curl --request PUT 'http://$IP/api/system/logs/web' \ 266 | --header 'Content-Type: application/json' \ 267 | --user web-configurator:$PIN \ 268 | --data '{"enabled": true}' 269 | ``` 270 | 271 | Start and permanently enable the app, even after a reboot: 272 | ```shell 273 | curl --request PUT 'http://$IP/api/system/logs/web' \ 274 | --header 'Content-Type: application/json' \ 275 | --user web-configurator:$PIN \ 276 | --data '{"enabled": true; "autostart": true}' 277 | ``` 278 | 279 | ## Recommendations 280 | 281 | - Node.js should be preferred for writing integration drivers. 282 | - If using Python: 283 | - Use [PyInstaller](https://pyinstaller.org/) to create a binary distribution. 284 | - Use the default one-folder bundle containing an executable (`--onedir`). Avoid the one-file bundled executable, since 285 | it will easily use an additional 100 MB of memory! 286 | - See [Android TV integration](https://github.com/unfoldedcircle/integration-androidtv) or [Apple TV integration](https://github.com/unfoldedcircle/integration-appletv) 287 | as an example. 288 | - An integration driver should be limited to one process and not launch other processes. 289 | - Use ports above 10000 if the integration needs to create an IPv4 or IPv6 server socket (besides the required WebSocket 290 | server for the integration-API). 291 | - Preserve resources, use as little memory and CPU as possible. 292 | - Use stdout & stderr for logging. 293 | - Only use `/tmp` for small, temporary files since it is a RAM file system. 294 | 295 | ## Example integrations 296 | 297 | The following Python based integration drivers create a custom integration installation archive during build with a 298 | GitHub action: 299 | 300 | - [Android TV](https://github.com/unfoldedcircle/integration-androidtv) 301 | - [Apple TV](https://github.com/unfoldedcircle/integration-appletv) 302 | - [Denon AVR](https://github.com/unfoldedcircle/integration-denonavr) 303 | 304 | -------------------------------------------------------------------------------- /doc/integration-driver/driver-registration.md: -------------------------------------------------------------------------------- 1 | ## Driver Registration 2 | 3 | An external integration driver can optionally register itself in a remote if the [driver advertisement](driver-advertisement.md) 4 | for auto-discovery is not sufficient. 5 | This is a convenience feature if a driver would like to provide its access token without the user requiring to manually 6 | enter it, or if the driver cannot be automatically be discovered by the remote due to network setup (non-local servers, 7 | firewalls, VLAN etc.). 8 | 9 | ```mermaid 10 | sequenceDiagram 11 | participant I as Integration 12 | participant mDNS 13 | participant R as Remote 14 | participant U as UI / web-configurator 15 | R-)mDNS: publish service 16 | I-)mDNS: lookup remote 17 | I->>+R: register integration driver 18 | R-->>-I: result 19 | rect rgb(118, 153, 144) 20 | Note right of R: Integration setup 21 | U->>R: get integration drivers 22 | U->>R: create integration instance 23 | U->>R: enable integration instance 24 | end 25 | rect rgb(51, 82, 102) 26 | R-)I: start communication 27 | Note right of I: See integration message flow 28 | end 29 | ``` 30 | 31 | ### Registration REST API 32 | 33 | Driver registration is only possible through the Remote core REST API. 34 | 35 | See: 36 | 37 | #### Example 38 | 39 | Register the [simulated-light driver](https://github.com/unfoldedcircle/integration-node-library/tree/main/examples/simulated-light) 40 | using the [Core-Simulator Docker setup](https://github.com/unfoldedcircle/core-simulator/tree/main/docker). 41 | This also works using the Simulator VM running with bridge networking: 42 | 43 | - The integration driver is accessible at `192.168.1.115` and listens on port `9988`. Adapt to your environment! 44 | - Register driver with `"driver_url:" "ws://192.168.1.115:9988"` 45 | - Use the [predefined Postman collection](https://github.com/unfoldedcircle/core-simulator/tree/main/core-api/rest) 46 | to interact with the Simulator from the host, or use a curl call: 47 | 48 | ```bash 49 | curl --location 'http://localhost:8080/api/intg/drivers' \ 50 | --user "web-configurator:1234" \ 51 | --header 'Content-Type: application/json' \ 52 | --data ' 53 | { 54 | "driver_id": "unique-driver-id or omit for auto generated uuid", 55 | "name": { 56 | "en": "Nodejs Test driver" 57 | }, 58 | "driver_url": "ws://192.168.1.115:9988", 59 | "version": "0.0.1", 60 | "icon": "uc:test", 61 | "enabled": true, 62 | "description": { 63 | "en": "This is the way" 64 | }, 65 | "device_discovery": false, 66 | "setup_data_schema": {}, 67 | "release_date": "2023-04-02" 68 | } 69 | ' 70 | ``` 71 | - Note: using localhost:8080 when running from the host where the Simulator service is running in a container. 72 | 73 | After a successful registration the driver will show up in the web-configurator: 74 | 75 | ![driver-registered](img/driver-registered.png) 76 | 77 | Click on the driver and start the setup: 78 | 79 | ![driver-add.png](img%2Fdriver-add.png) 80 | 81 | The above example has an empty `setup_data_schema`, so there won't be a user interaction and the setup only consists 82 | of connecting to the driver and fetching the driver information. 83 | 84 | ![driver-setup-ok.png](img%2Fdriver-setup-ok.png) 85 | 86 | The driver is now registered and the available entities will show up in the driver configuration: 87 | 88 | ![driver.png](img%2Fdriver.png) 89 | -------------------------------------------------------------------------------- /doc/integration-driver/driver-setup.md: -------------------------------------------------------------------------------- 1 | ## Driver Setup 2 | 3 | An integration driver can support a settings page and an interactive setup flow with the Remote Two app or 4 | web-configurator to configure the driver. 5 | 6 | The integration driver can specify a `setup_data_schema` for the initial setup screen for the user to fill out. 7 | This can be as simple as an information text or a confirmation button without any further interactions. It also allows 8 | to define more sophisticated user interactions where the integration driver sends dynamic setup screens to the user, 9 | e.g. offer choices or asking for more input data. 10 | 11 | ### Driver Registration 12 | 13 | The following sequence diagram shows the happy message flow of a driver registration. 14 | Note that driver registration can be aborted at every stage with an error response or timeout. 15 | 16 | ```mermaid 17 | sequenceDiagram 18 | participant I as Integration X 19 | participant M as mDNS 20 | participant R as Remote 21 | participant C as UI configurator 22 | actor User 23 | 24 | I--)M: advertise service X 25 | 26 | User->>C: Start integration discovery 27 | C-)+R: start_discovery 28 | R--)C: event: integration_discovery (START) 29 | R-)M: lookup service 30 | loop 31 | M--)R: service(n) 32 | opt Not yet registered 33 | R--)C: event: integration_discovery (DISCOVER) [n] 34 | end 35 | end 36 | R--)-C: event: integration_discovery (STOP) 37 | 38 | User->>C: Choose integration X 39 | 40 | loop 41 | opt 42 | User->>+C: Enter access token 43 | end 44 | opt 45 | User->>+C: Change driver URL 46 | end 47 | 48 | 49 | opt 50 | User->>C: Connection test 51 | C-)+R: get_discovered_intg_driver_metadata 52 | rect rgb(51, 82, 102) 53 | R-)+I: [temporary connection] 54 | Note right of I: See Establish Connection 55 | end 56 | R-)I: get_driver_metadata 57 | I--)-R: driver_metadata 58 | R-->I: disconnect 59 | R--)-C: integration_driver 60 | end 61 | end 62 | 63 | User->>+C: Confirm 64 | C-)+R: configure_discovered_integration_driver 65 | rect rgb(51, 82, 102) 66 | R-)+I: [temporary connection] 67 | Note right of I: See Establish Connection 68 | end 69 | R-)I: get_driver_metadata 70 | I--)-R: driver_metadata 71 | R-->I: disconnect 72 | R->>R: create driver 73 | R--)C: integration_driver 74 | R--)C: event: integration_driver_change (NEW) 75 | 76 | rect rgb(51, 82, 102) 77 | R-)+I: [start communication] 78 | Note right of I: See Establish Connection 79 | end 80 | 81 | R--)C: event: integration_state (CONNECTING) 82 | R--)-C: event: integration_state (ACTIVE) 83 | Note over I,R: Configured driver 84 | ``` 85 | 86 | ### Driver Setup Flow 87 | 88 | The following sequence diagram shows the message flow after the driver registration above. 89 | Again, the happy flow is shown. An error response or timeout can occur at any step in the process. 90 | 91 | ```mermaid 92 | sequenceDiagram 93 | participant I as Integration 94 | participant R as Remote 95 | participant C as UI configurator 96 | actor User 97 | 98 | Note over I,R: Configured driver 99 | R-->+I: connection 100 | 101 | User->>C: Configure new integration 102 | 103 | User->>C: Provide settings value 104 | 105 | C-)+R: setup_integration 106 | R->>R: verify setup session not running & driver connected 107 | R--)C: event: integration_setup_change
START (SETUP) 108 | R->>R: create new setup session 109 | R--)C: integration_setup_info (SETUP) 110 | 111 | R-)+I: setup_driver 112 | I--)R: result (OK) 113 | R--)C: event: integration_setup_change
SETUP (SETUP) 114 | Note right of I: setup started, updates with `driver_setup_change` 115 | loop 116 | alt setup progress 117 | I--)R: event: driver_setup_change
(`event_type: SETUP, state: PROGRESS?`) 118 | R--)C: event: integration_setup_change
SETUP (PROGRESS) 119 | else user interaction 120 | I--)R: event: driver_setup_change
(`event_type: SETUP, state: WAIT_USER_ACTION?`) 121 | R--)+C: event: integration_setup_change
SETUP (WAIT_USER_ACTION) 122 | User->>C: Provide input 123 | C-)+R: set_integration_user_data 124 | R-)+I: set_driver_user_data 125 | I--)-R: result (OK) 126 | R--)-C: integration_setup_info 127 | end 128 | end 129 | I--)-R: driver_setup_change
(`event_type: STOP, state: OK` 130 | R-)R: create integration 131 | R--)C: event: integration_change
(NEW) 132 | R--)C: event: integration_state
(CONNECTED) 133 | R--)C: event: integration_setup_change
SETUP (OK) 134 | R--)C: event: integration_setup_change
STOP (OK) 135 | R-)-R: remove setup session 136 | 137 | Note over I,R: Configured integration 138 | R-)R: wait for messages 139 | Note over I,R: Message exchange until disconnected 140 | I-->-R: disconnect 141 | ``` 142 | -------------------------------------------------------------------------------- /doc/integration-driver/img/driver-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unfoldedcircle/core-api/9d8f8e81db9b8e30fcf231f704810dc30c9fd742/doc/integration-driver/img/driver-add.png -------------------------------------------------------------------------------- /doc/integration-driver/img/driver-registered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unfoldedcircle/core-api/9d8f8e81db9b8e30fcf231f704810dc30c9fd742/doc/integration-driver/img/driver-registered.png -------------------------------------------------------------------------------- /doc/integration-driver/img/driver-setup-ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unfoldedcircle/core-api/9d8f8e81db9b8e30fcf231f704810dc30c9fd742/doc/integration-driver/img/driver-setup-ok.png -------------------------------------------------------------------------------- /doc/integration-driver/img/driver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unfoldedcircle/core-api/9d8f8e81db9b8e30fcf231f704810dc30c9fd742/doc/integration-driver/img/driver.png -------------------------------------------------------------------------------- /doc/integration-driver/multi-device-driver.md: -------------------------------------------------------------------------------- 1 | ### Multi Device Instance Driver 2 | 3 | ⚠️ **This feature might not be implemented anymore or be integrated with the new driver setup in the future!** 4 | 5 | _TODO add introduction_ 6 | 7 | The multi device instance driver adds discovery and user interaction to the setup process. 8 | 9 | - The `device_id` property must be used in all messages to identify the specific device. 10 | 11 | ```mermaid 12 | sequenceDiagram 13 | participant D as Device(s) 14 | participant I as Integration 15 | participant R as Remote 16 | participant U as UI 17 | participant C as configurator 18 | actor User 19 | 20 | R-)+I: [connect] 21 | I->>I: authentication & version check etc 22 | opt 23 | I-->>-D: connect / initialize 24 | end 25 | 26 | User->>+C: Setup new device 27 | C->>R: check connection state 28 | C->>-C: setup flow 29 | C-)+R: start_discovery 30 | R-)-I: start_discovery 31 | I--)+R: discovered_device(X) 32 | R--)-C: discovered_device(X) 33 | I--)+R: discovered_device(Y) 34 | R--)-C: discovered_device(Y) 35 | I--)+R: finished_discovery 36 | R--)-C: finished_discovery 37 | 38 | opt Manual re-discovery 39 | User->>+C: Re-discover 40 | C-)+R: start_discovery 41 | R-)-I: start_discovery 42 | I--)+R: discovered_device(X) 43 | R--)-C: discovered_device(X) 44 | User->>C: Stop discovery 45 | C-)+R: stop_discovery 46 | R-)-I: stop_discovery 47 | end 48 | 49 | User->>+C: Choose device X 50 | 51 | C-)+R: setup_device(X) 52 | R-)-I: setup_device(X) 53 | loop 54 | alt setup progress 55 | I-)R: device_setup_progress 56 | R-)C: progress 57 | else user interaction 58 | I-)R: device_setup_interaction 59 | R-)+C: interaction 60 | User->>C: Provide input 61 | C--)-R: user action 62 | R--)I: setup_user_action 63 | end 64 | end 65 | I-)R: device_setup_complete 66 | R-)C: device_setup_complete 67 | ``` 68 | -------------------------------------------------------------------------------- /doc/integration-driver/websocket.md: -------------------------------------------------------------------------------- 1 | ## WebSocket 2 | 3 | [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) is a bidirectional communication 4 | channel to exchange messages. 5 | These messages are all events, which can be sent by the client or server at any time. 6 | There's no support for synchronous calls or transactions. Message relations must be handled in the application level 7 | protocol and are designed with message exchange patterns. 8 | 9 | ### Authentication 10 | 11 | ⚠️ Custom integrations running on the remote do not support authentication. 12 | 13 | An external integration driver may require a token for authentication, or no authentication at all. 14 | 15 | - During driver registration the preferred authentication type and the token can be specified. 16 | - Default authentication method: message based if a token has been set. 17 | 18 | #### Header Authentication 19 | 20 | If header based authentication has been specified for the integration driver in the remote configuration, the remote 21 | will send the token in the `auth-token` header field while establishing the connection. 22 | 23 | The WebSocket connection should then only be established by the driver if the token matches. Otherwise, the integration 24 | driver (which is the WebSocket server) must respond with http status code `401 Unauthorized` and not upgrading the 25 | connection to WebSockets. 26 | 27 | If the integration driver upgrades the WebSocket connection, the remote assumes the connection as authenticated. 28 | 29 | #### Message Authentication 30 | 31 | For integration drivers not able to use header based authentication with WebSockets (due to framework or runtime 32 | restrictions), a fallback option with text messages exists. See AsyncAPI specification for the message definitions. 33 | 34 | 1. Right after the WebSocket connection is established, the integration driver must send the `auth_required` event as 35 | first message: 36 | 37 | ```json 38 | { 39 | "kind": "event", 40 | "msg": "auth_required", 41 | "msg_data": { 42 | "name": "my-integration", 43 | "version": { 44 | "api": "0.5.0", 45 | "driver": "1.0.0" 46 | } 47 | } 48 | } 49 | ``` 50 | 51 | 2. The remote sends an authentication message: 52 | 53 | ```json 54 | { 55 | "kind": "req", 56 | "id": 1, 57 | "msg": "auth", 58 | "msg_data": { 59 | "token": "$TOKEN" 60 | } 61 | } 62 | ``` 63 | 64 | 3. The integration driver replies with the `authentication` event including the result code of the authentication: 65 | 66 | - `200`: authentication succeeded, driver can be used, all API messages are accepted. 67 | - `401`: invalid token and the driver must close the connection. The remote will mark the integration as 68 | unauthorized and stop auto-reconnect. 69 | 70 | ```json 71 | { 72 | "kind": "resp", 73 | "req_id": 1, 74 | "msg": "authentication", 75 | "code": 200 76 | } 77 | ``` 78 | 79 | The remote will close the connection if the integration sends any other code than 200 or 401. 80 | 81 | #### No Authentication 82 | 83 | If the driver doesn't support authentication tokens, it still needs to send the `authentication` message with code 200 84 | right after a connection has been established by a client. 85 | 86 | ### Keep Alive 87 | 88 | The integration should automatically close idle connections. 89 | In order to keep a connection open, the remote sends WebSocket [Ping Pong control frames](https://tools.ietf.org/html/rfc6455#section-5.5.2) 90 | at least every 60 seconds. Furthermore, an integration should also send pings to the remote to detect stale 91 | connections and handle the standby events from the remote. 92 | 93 | ### Error Handling 94 | 95 | - All response messages include a status code property. 96 | This code corresponds to the standard http status codes as main success / error indication. 97 | 98 | **TODO** _Disconnection standard status code in WebSockets: [rfc6455](https://tools.ietf.org/html/rfc6455#section-7.4)_ 99 | 100 | -------------------------------------------------------------------------------- /doc/integration-driver/write-integration-driver.md: -------------------------------------------------------------------------------- 1 | ## How to write an integration driver 2 | 3 | The Integration-API allows to develop external integration drivers in any programming language capable of running 4 | a WebSockets server. Custom integration drivers require Node.js or a statically linked binary. 5 | 6 | - `External integration driver`: a driver running on an external device, reachable by the Remote. 7 | - `Custom integration driver`: a driver running on the Remote in a restricted, sandboxed environment. 8 | 9 | ℹ️ Beta release 1.9.0 allows to install custom integration drivers on the Remote. 10 | This is a developer preview feature to test integrations. See [Driver installation on the Remote](driver-installation.md) 11 | for more information. 12 | 13 | ℹ️ See [NodeJS API wrapper](https://github.com/unfoldedcircle/integration-node-library) for a quick start in JavaScript, 14 | or the [Python API wrapper](https://github.com/unfoldedcircle/integration-python-library) if using Python. 15 | 16 | When writing a driver from scratch without using a wrapper library: 17 | 18 | 1. Choose your programming language. 19 | 2. Choose a WebSockets server library or framework for your language. 20 | 3. Choose a JSON framework for your language. 21 | 4. Choose which [entities and features](../entities/README.md) the driver should expose. 22 | 5. Implement the required WebSockets text messages in the [WebSocket Integration API](../../integration-api/UCR-integration-asyncapi.yaml). 23 | 24 | ❗️ Using a wrapper library or one of the existing open source drivers as a start will greatly simplify driver development. 25 | Node.js should be preferred when developing a custom integration driver. 26 | 27 | ### Integration Driver Types 28 | 29 | The API defines two different driver types: a simple single-device driver or a multi-device instance driver. 30 | 31 | ⚠️ Only the single device instance driver is supported. The multi-device driver type will most likely not be implemented 32 | in the near future! 33 | 34 | #### Single Device Instance Driver 35 | 36 | The single-device instance driver is the default and easiest driver to develop: 37 | 38 | - There is no concept of different or individual devices, just entities that the driver provides. 39 | - The driver offers a list of available entities to the remote. This can be a single media player entity, or a large 40 | list of different entities from a home automation hub. 41 | - The optional `device_id` property must be omitted in all messages. 42 | - The driver may still control multiple physical devices, but from the Remote's point of view, these are just different 43 | entities provided from the same integration driver. 44 | 45 | E.g. if smart WiFi light bulbs can be discovered by the driver and then each bulb can be provided as a 46 | [light entity](../entities/entity_light.md) to the remote. 47 | 48 | ### Authentication 49 | 50 | ⚠️ Custom integrations running on the remote do not support authentication. 51 | 52 | An external integration driver can choose if the Remote Two requires authentication or not to connect to the driver. 53 | Supported are header based token authentication during connection setup or with an application level message after the 54 | connection has been established. 55 | 56 | See [WebSocket authentication](websocket.md#authentication) for more information. 57 | 58 | ### Connection Handling 59 | 60 | - Support multiple Remote sessions 61 | - An external driver should support multiple, independent WebSocket connections at the same time. 62 | - Keep alive 63 | - The driver must support WebSocket [Ping Pong control frames](https://tools.ietf.org/html/rfc6455#section-5.5.2). 64 | Please check your WebSocket library, most libraries support this out of the box. 65 | - The Remote Two continuously sends ping frames and automatically closes the connection if it doesn't receive a pong 66 | within a certain time frame. The driver may also send ping frames to check if the connection is still alive. 67 | 68 | ### Driver Registration 69 | 70 | ⚠️ Custom integrations running on the remote are automatically registered during installation. 71 | 72 | An external integration driver should advertise itself over mDNS for auto-discovery and allow user configuration with 73 | the web-configurator. See [mDNS advertisement](driver-advertisement.md) for more information. 74 | 75 | An external integration driver can optionally register itself at a remote and provide its authentication token. 76 | 77 | See [driver registration](driver-registration.md) for more information. 78 | 79 | ### Discover Remote Two Devices 80 | 81 | The Remote Two can be discovered with DNS-SD. It announces itself with service type `_uc-remote._tcp`. 82 | 83 | See [remote DNS-SD lookup](../discovery.md) for more information. 84 | 85 | ### JSON Messages 86 | 87 | #### Message Format 88 | 89 | - Only WebSockets Text message are supported at the moment. 90 | Binary support might be added in the future. 91 | - All messages are serialized in JSON format. 92 | - Text encoding is UTF-8. 93 | - The application level protocol defines two message interactions with three different message types: 94 | - Request / response messages 95 | - Event messages 96 | 97 | #### Request Messages 98 | 99 | - A request message is of kind `req`. 100 | - Every request message must include a unique `id` value, which must be increased for every new request. 101 | - The request message type is specified in property `msg`. 102 | - An optional `msg_data` object holds the request specific data. 103 | - A request message is usually answered by a response message if not otherwise stated in the documentation. 104 | Exceptions are event responses, e.g. if the event can be triggered independently of the request message. 105 | 106 | Example of a request message: 107 | 108 | ```json 109 | { 110 | "kind": "req", 111 | "id": 123, 112 | "msg": "entity_command", 113 | "msg_data": { 114 | "entity_type": "button", 115 | "entity_id": "button-1", 116 | "cmd_id": "push" 117 | } 118 | } 119 | ``` 120 | 121 | #### Response Messages 122 | 123 | - A response message is of kind `resp`. 124 | - The corresponding `id` of the request message is given in property `req_id`. 125 | - The response message type is specified in property `msg`. 126 | - The status of the request operation is given in `code`. 127 | - The `msg_data` object holds the response specific data. 128 | 129 | Example of a response message: 130 | 131 | ```json 132 | { 133 | "kind": "resp", 134 | "req_id": 11, 135 | "msg": "available_entities", 136 | "code": 200, 137 | "msg_data": { 138 | "available_entities": [ 139 | { 140 | "entity_id": "button-1", 141 | "entity_type": "button", 142 | "name": { 143 | "en": "Ring dinner bell" 144 | } 145 | } 146 | ] 147 | } 148 | } 149 | ``` 150 | 151 | #### Event Messages 152 | 153 | - An event message is of kind `event`. 154 | - The event type is specified in property `msg`. 155 | - The optional `cat` property denotes the event category. 156 | - The optional `ts` property holds the timestamp when the event occurred. 157 | - The `msg_data` object holds the event specific data. 158 | 159 | Example of an event message: 160 | 161 | ```json 162 | { 163 | "kind": "event", 164 | "msg": "entity_change", 165 | "cat": "ENTITY", 166 | "ts": "2021-04-24T14:15:22Z", 167 | "msg_data": { 168 | "entity_type": "cover", 169 | "entity_id": "blind-1", 170 | "attributes": { 171 | "position": 72 172 | } 173 | } 174 | } 175 | ``` 176 | 177 | ### Required Messages 178 | 179 | For a functioning integration driver not all defined messages and optional features in the integration API have to be 180 | implemented. The mandatory messages are tagged with a pizza 🍕 emoji in the [WebSocket Integration AsyncAPI definition](../../integration-api/UCR-integration-asyncapi.yaml). 181 | 182 | Required request messages which must be processed and answered by a driver: 183 | 184 | | Request | Response | Description | 185 | |--------------------------|----------------------|----------------------------------------------------------------------------------------------------------------------------| 186 | | `get_driver_version` | `driver_version` | Get version information about the integration driver. | 187 | | `get_device_state` | `device_state` | Get the current device state.
This informs the Remote Two if the device is ready or not available. | 188 | | `get_available_entities` | `available_entities` | Retrieve the available entities from the integration driver. | 189 | | `subscribe_events` | `result` | Subscribe to entity state change events.
This instructs the driver to send entity state changes for specific entities. | 190 | | `get_entity_states` | `entity_states` | Get the current state of the entities.
This provides the Remote Two the current entity properties. | 191 | | `entity_command` | `result` | Instructs the driver to execute an entity command.
This sets entity properties from user interactions. | 192 | 193 | Required event messages which must be sent by the driver: 194 | 195 | | Event | Description | 196 | |------------------|-------------------------------------------------| 197 | | `entity_change` | Emitted when an attribute of an entity changes. | 198 | | `device_state` | Emitted when the device state changes. | 199 | 200 | 201 | ### Common Message Flow 202 | 203 | The basic message flow between an integration and the remote is as follows: 204 | 205 | - The integration driver acts as server and the Remote Two initiates the connection. 206 | - After the WebSocket connection is established to the integration driver, the remote subscribes to events of all 207 | configured entities. 208 | - Whenever the state of the integration driver changes, the driver sends a device state event. 209 | This is usually the device which the driver represents or connects / communicates with. E.g. a smart device, home 210 | automation hub, cloud service, etc. 211 | - Whenever an entity attribute of a subscribed entity changes, the driver sends an entity state event. 212 | - The remote announces when it goes into and out of standby, so the integration driver can act accordingly. 213 | Note: the WebSocket connection might get disconnected during remote standby! 214 | 215 | #### Establish Connection 216 | 217 | ```mermaid 218 | sequenceDiagram 219 | participant I as Integration 220 | participant R as Remote 221 | 222 | R-)+R: create integration driver session 223 | 224 | critical Establish WS connection 225 | R-)+I: connect 226 | alt header token 227 | I->>I: authentication & version check etc 228 | I--)R: authentication (OK) 229 | else message based auth 230 | I--)R: auth_required 231 | R-)I: auth 232 | I--)R: authentication (OK) 233 | end 234 | Note over I,R: Authenticated WS session 235 | R-)R: wait for messages 236 | Note over I,R: Message exchange until disconnected 237 | R-->-I: disconnect 238 | option Authentication timeout 239 | R-->I: disconnect 240 | option Credentials rejected 241 | I--)R: authentication (401) 242 | R-->I: disconnect 243 | end 244 | 245 | ``` 246 | 247 | 248 | #### Integration Driver Setup 249 | 250 | See [driver setup](driver-setup.md). 251 | 252 | #### Integration Instance Configuration 253 | 254 | After the driver has been registered and configured, the provided entities can be configured in the Remote Two. 255 | 256 | This consists of requesting all available entities from the driver and subscribing to entity events of the chosen 257 | entities by the user. 258 | 259 | - The `subscribe_events` message is only sent for entities which are configured in the Remote. I.e. only for entities 260 | which are used in a profile and placed on a page or group. 261 | - For simplicity reasons the driver may choose to always send events for all entities. The Remote will ignore 262 | unnecessary events. However, it's recommended to limit the message exchange to entities which are actually used to 263 | save network & processing resources. 264 | - The configured entities from the user(s) are stored in the remote-core application. 265 | Whenever the Remote (re)connects to the driver, only those entity identifiers are submitted in the `subscribe_events` 266 | message. 267 | - The driver can use the `get_version` message to retrieve version and model information of the Remote. 268 | - The driver can use the `get_localization_cfg` message to retrieve localization information from the remote if it deals 269 | with localized labels, values etc. 270 | 271 | ```mermaid 272 | sequenceDiagram 273 | participant D as Device(s) 274 | participant I as Integration 275 | participant R as Remote 276 | participant U as UI 277 | participant C as configurator 278 | actor User 279 | 280 | User->>+C: setup new integration 281 | C->>-R: enable integration instance 282 | 283 | critical Establish a connection. See normal operation. 284 | R-)I: connect 285 | end 286 | 287 | R-)+I: get_driver_version 288 | I--)-R: driver_version 289 | 290 | C-)+R: get_available_entities(integration) 291 | R-)-I: get_available_entities 292 | I--)+R: available_entities 293 | R->>R: cache entities 294 | R--)-C: available entities 295 | 296 | loop 297 | User->>+C: configure entity 298 | C-)+R: add entity 299 | R->>R: store entity cfg 300 | R-)-I: subscribe_events 301 | end 302 | ``` 303 | 304 | #### Normal Operation 305 | 306 | During normal operation there are only a few different message exchanged between the Remote and the integration driver: 307 | 308 | - `authentication`: sent by the driver after WebSocket connection is established 309 | - `subscribe_events`: sent by the Remote after the WebSocket connection is authenticated 310 | - `entity_command`: instruct the driver to perform an action on an entity 311 | - `entity_change`: inform the Remote that an entity changed 312 | - `get_device_state`: request the device state from the driver 313 | - `get_entity_states`: request the current entity attributes and states 314 | 315 | ```mermaid 316 | sequenceDiagram 317 | participant D as Device(s) 318 | participant I as Integration 319 | participant R as Remote 320 | participant U as UI 321 | actor User 322 | 323 | R->>R: startup: lookup enabled integration 324 | 325 | R-)+I: [WS connection] 326 | I->>I: authentication & version check etc 327 | I--)-R: authentication 328 | 329 | R-)+I: connect 330 | loop until connected 331 | opt 332 | I-->>D: connect / initialize 333 | end 334 | I--)-R: device_state 335 | end 336 | 337 | opt 338 | I-)+R: get_version 339 | R--)-I: version 340 | 341 | I-)+R: get_localization_cfg 342 | R--)-I: localization_cfg 343 | end 344 | 345 | 346 | R-)I: subscribe_events 347 | 348 | R-)I: get_entity_states 349 | 350 | D-->>I: external state change 351 | I--)+R: entity_change 352 | R->>R: store event 353 | R-)+U: entity change event 354 | opt 355 | U-)-R: get entity 356 | R--)-U: entity 357 | end 358 | 359 | User->>+U: interact with entity 360 | U-)-R: entity action 361 | R-)+I: entity_command 362 | opt 363 | I-->>D: update 364 | end 365 | I--)-R: result 366 | 367 | opt 368 | D-->>+I: state change 369 | end 370 | I-)-R: entity_change 371 | R->>+R: store event 372 | R-)-U: entity change event 373 | 374 | R-)I: enter_standby 375 | R-)I: exit_standby 376 | R-)+I: get_device_state 377 | I--)-R: device_state 378 | R-)+I: get_entity_states 379 | I--)-R: entity_states 380 | R-)I: [disconnect] 381 | ``` 382 | -------------------------------------------------------------------------------- /doc/remote-ui.md: -------------------------------------------------------------------------------- 1 | # Remote Two User Interface 2 | 3 | The user interface was designed to be easy to use by anyone, who can use a smart home app. Similar principles were 4 | applied to the touch interface, enhanced with button navigation and control. 5 | 6 | ## Home/page screen 7 | 8 | ![Overview](img/ui-overview.jpg) 9 | 10 | | | Description | 11 | |-----|---------------------------------------------| 12 | | 1 | Notification indicator | 13 | | 2 | Clock | 14 | | 3 | Indicator when an integration is connecting | 15 | | 4 | Battery level | 16 | | 5 | Current profile - tap opens setting | 17 | | 6 | Microphone in use indicator | 18 | | 7 | Current page - tap opens page selector | 19 | | 8 | List of entities/groups | 20 | 21 | ## Profiles 22 | 23 | A profile can contain customised settings and UI configurations, including pages, groups and entities. 24 | 25 | ![Overview](img/profiles.jpg) 26 | 27 | A profile can be changed by tapping on the current profile in settings. 28 | 29 | ## Pages 30 | 31 | ![Overview](img/pages.jpg) 32 | 33 | A page can contain entities or groups. Pages can be selected by swiping left and right or using the left and right 34 | buttons on the D-pad. Tapping the page's name will bring up the page selector, where pages can be also added, deleted or 35 | renamed. Pages can have an image header and contain entities or groups. Multiple pages can contain the same entity. 36 | 37 | A page can be used to segment rooms, areas, a specific purpose or anything else that makes sense to you. 38 | 39 | ## Groups 40 | 41 | Groups are a set of entities that can be defined by the user. Groups are another way of organising entities. A group can 42 | be configured to have a group switch, that can turn on/off every entity within the group at once. A group can be 43 | collapsed or expanded. 44 | 45 | ![Overview](img/groups.jpg) 46 | 47 | Example of a group containing two lights in expanded mode. 48 | -------------------------------------------------------------------------------- /dock-api/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Dock-API Changelog 2 | All notable changes to the Dock-API will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## Unreleased 8 | 9 | This section contains unreleased changed which will be part of an upcoming release. 10 | 11 | --- 12 | ## 0.6.0-beta - 2023-06-29 13 | ### Added 14 | - Continuous IR repeat feature 15 | ### Changed 16 | - PRONTO code values can also be separated by a space (either a comma or a space, but not mixed) 17 | 18 | ## 0.5.0-beta - 2022-12-24 19 | ### Added 20 | - Initial public release. 21 | -------------------------------------------------------------------------------- /dock-api/README.md: -------------------------------------------------------------------------------- 1 | # WebSocket Dock API 2 | 3 | - [Dock API AsyncAPI YAML definition](UCD2-asyncapi.yaml). 4 | - See [changelog](CHANGELOG.md) for changes in the API specification. 5 | 6 | ## API HTML documentation 7 | 8 | - View with [AsyncAPI Studio online tool](https://studio.asyncapi.com/?url=https://raw.githubusercontent.com/unfoldedcircle/core-api/main/dock-api/UCD2-asyncapi.yaml). 9 | The link will directly load the yaml definition file from GitHub and display it together with the HTML documentation in 10 | the browser. 11 | 12 | The [AsyncAPI generator tool](https://github.com/asyncapi/generator) transforms the API definition into other formats. 13 | 14 | The easiest way to generate the HTML documentation from the AsyncAPI definition is to use the official 15 | [asyncapi/generator Docker image](https://hub.docker.com/r/asyncapi/generator). You can also try to use the command line 16 | tool with the instructions provided from the generator tool. 17 | The provided Bash wrapper script [`create-html-docker.sh`](create-html-docker.sh) creates the html documentation in the 18 | `./html` sub folder. 19 | 20 | ℹ️ We will look into using GitHub Pages for publishing the html documentation. 21 | -------------------------------------------------------------------------------- /dock-api/create-html-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | readonly OUT_DIR=${PWD}/html 4 | 5 | mkdir -p "$OUT_DIR" 6 | docker run --rm -it \ 7 | -v "${OUT_DIR}":/app/example \ 8 | -v "${PWD}/UCD2-asyncapi.yaml":/app/asyncapi.yaml \ 9 | asyncapi/generator:1.9.2 asyncapi.yaml @asyncapi/html-template@0.25 --force-write -o example 10 | -------------------------------------------------------------------------------- /integration-api/README.md: -------------------------------------------------------------------------------- 1 | # WebSocket Integration API 2 | 3 | - [AsyncAPI YAML definition](UCR-integration-asyncapi.yaml). 4 | 5 | - See [/doc folder](../doc/README.md) for the API documentation and further information. 6 | 7 | ## API HTML documentation 8 | 9 | - View with [AsyncAPI Studio online tool](https://studio.asyncapi.com/?url=https://raw.githubusercontent.com/unfoldedcircle/core-api/main/integration-api/UCR-integration-asyncapi.yaml). 10 | The link will directly load the yaml definition file from GitHub and display it together with the HTML documentation in 11 | the browser. 12 | 13 | The [AsyncAPI generator tool](https://github.com/asyncapi/generator) transforms the API definition into other formats. 14 | 15 | The easiest way to generate the HTML documentation from the AsyncAPI definition is to use the official 16 | [asyncapi/generator Docker image](https://hub.docker.com/r/asyncapi/generator). You can also try to use the command line 17 | tool with the instructions provided from the generator tool. 18 | The provided Bash wrapper script [`create-html-docker.sh`](create-html-docker.sh) creates the html documentation in the 19 | `./html` sub folder. 20 | 21 | ℹ️ We will look into using GitHub Pages for publishing the html documentation. 22 | 23 | ## API Versions 24 | 25 | | Integration API | UCR2 Firmware | Core Simulator | 26 | |-----------------|---------------|----------------| 27 | | 0.12.1 | | 0.58.3 | 28 | | 0.12.0 | 1.9.3-beta | 0.48.0 | 29 | | 0.11.0 | 1.9.2-beta | 0.47.0 | 30 | | 0.10.0 | 1.7.12 | 0.43.0 | 31 | | 0.9.0 | 1.7.2 | 0.41.0 | 32 | | 0.8.0 | 1.5.2 | 0.39.7 | 33 | -------------------------------------------------------------------------------- /integration-api/create-html-docker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | readonly OUT_DIR=${PWD}/html 4 | 5 | mkdir -p "$OUT_DIR" 6 | docker run --rm -it \ 7 | -v "${OUT_DIR}":/app/example \ 8 | -v "${PWD}/UCR-integration-asyncapi.yaml":/app/asyncapi.yaml \ 9 | asyncapi/generator:1.9.2 asyncapi.yaml @asyncapi/html-template@0.25 --force-write -o example 10 | -------------------------------------------------------------------------------- /integration-api/create-html.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # AsyncAPI tooling is a fragile snowflake :-( Couldn't get it to work on macOS. 3 | # The `ag` tool seems to require very specific node versions and often simply 4 | # fails with "Something went wrong" without any further information... 5 | # And don't get me started with external references and bundling it back into a 6 | # single file since most tools can't handle split yaml files as OpenAPI does... 7 | # 8 | # If you run into issues, use the Docker version! 9 | 10 | set -e 11 | 12 | command -v ag >/dev/null 2>&1 || { 13 | echo >&2 "AsyncAPI generator not installed. Aborting."; 14 | echo >&2 "Install with: 'sudo npm install -g @asyncapi/generator' (no sudo required on macOS)"; 15 | exit 1; 16 | } 17 | 18 | readonly OUT_DIR=html 19 | mkdir -p $OUT_DIR 20 | ag UCR-integration-asyncapi.yaml @asyncapi/html-template --force-write -o $OUT_DIR 21 | --------------------------------------------------------------------------------